I'd like my macros to be "clipboard preserving", that is, whatever was on the clipboard before running a macro should still be there afterwards. And so when a macro action puts a new entry on the clipboard I remove that entry before exiting the macro. This can be tricky, since KM silently uses the clipboard in such actions as "Insert text by pasting", but that's not hard to deal with once you know that's what an action does. It seems, though, that in some cases KM's use of the clipboard depends on the contents of the clipboard, and not just the action. So, in the example below, "Search and Replace" (clipboard-to-clipboard) creates a new clipboard entry only if the contents change. Note that both "Search and Replace" actions succeed, so that's not the difference. To put it another way, if you wanted to use "Search and Replace" to force the first character of the clipboard to be "X", KM would produce a new clipboard entry only if the first character was not already an "X". Having discovered this, I'm altering my use of the clipboard (not hard, given KM's amazing set of options), but I am curious. Is this a bug or a feature or perhaps an artifact of the macOS clipboard? Is there some obvious way to checkpoint/restore the clipboard I haven't run across?
The trick here is that Keyboard Maestro won't allow duplicate entries in its clipboard history, and there is no way to turn that off.
I find this exceedingly aggravating at times, but I've never been able to get @peternlewis to provide a preference for it.
You can use the
CLIPBOARDSEED() function to see if the clipboard did indeed change, and only delete the last entry if it did.
It's a trifle cumbersome, but it works.
Thanks, Chris. Yes, after a variety of tests I discovered that the anomaly goes deeper than I thought and, as you say, if there's a clipboard entry of "xyz", no matter how far down the stack, adding a new "xyz" entry deletes the old one in favor of the new one or, depending on how you look at it, moves the old one to the top of the stack. As far as I can see, it is therefore impossible to preserve the clipboard around a macro call, since you can't tell whether some random clipboard text generated during the macro might coincidentally match an older entry.
As to CLIPBOARDSEED(), I did try some experiments prior to my initial post, and I just re-checked. That value changes whether or not the new clipboard entry duplicates an old one and so, as far as I can see, can't be used to differentiate the two cases. It sounds like you're successfully doing this, so I wonder what we're doing differently. Below is one of my test cases.
[Added clarification: When I say "preserve the clipboard", I'm referring to the entire clipboard stack, not just the top entry. This test case, though, shows that CLIPBOARDSEED() doesn't help even with the top entry.]
And then at the very end of your Macro, do the reverse.
Thanks, Profile - JMichaelTX - Keyboard Maestro Discourse. Does the "Copy Clipboard to Clipboard" action copy the entire clipboard or just the top entry? I was hoping to preserve the clipboard stack.
And yes, bookending the macro with preserve/restore (of any flavor), is a good general option, but can get tricky if there are multiple macro exits, whether due to macro logic or error aborts.
[OK, I'll admit ignorance: How do I get someone's "handle" into a post, like I failed to do with JMichaelTX above?]
I don't understand your question.
Did you read the wiki article?
That is a design issue.
All you need is one Action whenever you exit a Macro:
If you mean the wiki article on the Copy Clipboard to Clipboard action, yes.
More relevant, though, might be Clipboards [Keyboard Maestro Wiki]. What I'm trying to preserve is the System Clipboard History, not just the most recent item in that history. As noted in my discussion above with Chris Stone, copying a single item to the System Clipboard has the potential to alter a random item in the History. Copying the System Clipboard to a Named Clipboard only copies the current item in the System Clipboard History and so does not serve to preserve the entire History.
As to bookending with preserve/restore, yes, each of those actions is simple, but if the macro has multiple possible exits, then the Restore operation must be included at each exit, and you must remember to add Restore whenever adding a new exit. Further, any possible error termination of the macro must be anticipated and tested in order to perform the Restore before termination. The alternative is to restore each modification as soon as possible after the modification happens. If there are few modifications and many exits, restoring the modifications as each one is made might be preferable. With many modifications and few exits, the reverse might apply.
While we have some control over the KM Clipboard History, it is not absolute, as Chris @ccstone noted.
You really only have two choices:
- Keep track of when your Macro ADDs to the Clipboard History, and perform a corresponding delete of the current top of the history, which is the same thing as the macOS System Clipboard (which does NOT have a history).
- Use KM Named Clipboard to save and then restore the System Clipboard.
It would be a fool's errand to try to maintain the exact KM Clipboard History, including order.
That was my point about design.
Having multiple "exits" in a KM Macro is much like having multiple "returns" in any programming language. My software design methodology is to have only ONE "return", or in the case of KM, one "Exit". This has served me very well.
IMO, if you find yourself needing many exits, then most likely your macro is too long and convoluted. @peternlewis has mentioned many times that it is best to keep your macros short.
Better to have multiple, short Macros, perhaps with the same hot key, than one long Macro with a Switch and/or bunch of IF/THEN Actions.
My design suggestions:
- Avoid use of Clipboard as much as possible -- use KM Variables.
- When you must use the Clipboard, immediately set a variable to the Clipboard, and delete that Clipboard.
- This keeps the KM Clipboard History clean as you go. So even if the Macro crashes or you do have multiple exits, it won't be a problem.
- Make good use of Sub-Macros.
This has worked very well for me for many years, many macros.
An option for preserving the clipboard history would be to save then first N spots to Named Clipboards, and then restore those at the end - but it still requires you to have either a single exit point, or to be diligent in restoring your macros at all points.
If I was going to do this (which clearly I am not, since if I was I'd probably think this was an important issue and have implemented a bunch of things around it, but unfortunately for folks who do, in this case I do not), I would have two sub macros, one that saved the first N clipboard history entries to N Named Clipboards, and then another that would restore them. And then I would execute the first on entry to the macro, and the second on any exit point of the macro (as @JMichaelTX says, generally I would ensure a single exit point). I would also have to take care that the first store history macro was only called at the very start of all execution, and not at the start of any sub-macro I called during execution. And I would have to ensure two macros did not run at the same time.
Many of these issues would apply to Keyboard Maestro itself if I tried to implement anything along these lines.
And clipboards cannot safely be immediately restored for reasons described in the wiki.
Yes, pretty much what I came up with, and immediately decided it wasn't worth the trouble (not to mention the other problems you cite).
Really not trying to quibble here, but this doesn't work in all cases. In particular, if the line added by the Macro to the System Clipboard happens to be the same as the line on top of the history when the Macro was called, that original line will be deleted. (Or, actually, if that line was anywhere in the History, it will be deleted.) This is exactly the case that led me down this rabbit hole in the first place.
But if a macro is used where you frequently are finding that you need the system clipboard preserved, then Keyboard Maestro provides the tools for this. After your clipboard mangling action, add an appropriately long Pause followed by a Delete Current Clipboard action. The length of the Pause will depend on the target application and the speed of your Mac at that moment.
As I keep coming back to, the use of Delete Current Clipboard as a means of "un-mangling" (nice choice of word) a clipboard action must be conditional on whether the action actually changed the clipboard. That is, Mangle/Loooooooong Pause/Delete will itself mangle the clipboard if the mangled text happened to match what was already the current clipboard entry. I'm not referring to a case where the Mangle operation fails (CLIPBOARDSEED() can be used to detect this case), but where the pre-mangle and post-mangle content just happen to match (for certain applications, more common than one might at first imagine), and so the Delete must be conditional on a before/after comparison. I'm not asking for this behavior to be changed or flagged or justified or explained, just want to be sure I really do understand what's going on (although if I am wrong, please do explain ).
You are correct. I can confirm this behavior. I finally get your point.
@peternlewis , here is a macro and video which demonstrate this issue.
Bottom line: I think you need to ADD a KM Preference that would allow duplicate entires in the Clipboard History. Otherwise, valuable data can be permanently lost.
-~~~ VER: 1.0 2021-06-08 ~~~
Requires: KM 8.2.4+ macOS 10.11 (El Capitan)+
(Macro was written & tested using KM 9.0+ on macOS 10.14.5 (Mojave))
Clipboard History @TEST 1.kmmacros
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.
The clipboard history is a list of the most recent clipboards, without duplicates which would inevitably end up with lots of wasted entries.
Keyboard Maestro is not designed for you to preserve the clipboard history around uses of the system clipboard - it is not a good thing to do, and will inevitably lead to problems and inconsistencies.
So the solution is quite simply to not do this.
I am unlikely to add any feature that encourages people to do this since there is simply no safe way to attempt it.
Thanks. Perhaps worth removing (or adding a caveat to) the FAQ text I quoted? Just a thought.