Sunday, July 21, 2019
  • Index
  •  » Code Exchange
  •  » Script to memorize and restore windows to specific (MC) desktops

#1 2019-07-09 12:48:46 pm

robleach72
Member
Registered: 2013-01-01
Posts: 26

Script to memorize and restore windows to specific (MC) desktops

One reason I don't reboot very often is because I use the desktops in mission control as project spaces.  I typically have 3 to 9 projects in active use each week.  Each project typically have a few windows from: Safari, Text Edit, Stickies, and Terminal, plus possibly another app or 2.  I use Stickies in large font tucked under the dock to label each desktop (so I can identify the project easily in MC - because it doesn't show the dock). If I have to reboot, that's a lot of windows all getting merged onto desktop 1 and it takes awhile to sort them out.

If I could just figure out a way to automatically re-distribute Stickies, Text Edit, and Terminal windows after reboot, that would solve 90% of my problem.  (I think somehow Safari remembers which desktop each window was on.)

Does anyone have any pointers for a script that could:

1. Trigger upon shutdown to memorize which desktop each window of a list of specific apps is on
2. Trigger after reboot (or even manually trigger) to restore the memorized windows to their pre-reboot desktop

Just thinking off the top of my head, I can imagine a script that, to memorize desktop assignments of each window, cycles through the desktops and switches to each app and cycles through the windows to record what desktop they're on in a lookup (text) file.

The problem is, I don't know the Applescript elements or any key commands to send a window to a specific desktop.  Does anyone know how to do this?

Browser: Safari 605.1.15
Operating System: macOS 10.14

Offline

 

#2 2019-07-10 09:59:28 am

robleach72
Member
Registered: 2013-01-01
Posts: 26

Re: Script to memorize and restore windows to specific (MC) desktops

I managed to create a script that can memorize what windows are open on which desktops.  As a proof of concept, it's OK, albeit hacky.  It needs however to save the windows and save them in a way I can manipulate them later using a companion script instead of just as a set of window names displayed in a dialog.  It has a couple issues:

1. Stickies window names are truncated to not include hard returns (possibly making the window name unusable)
2. All 3 of my Fluid apps are identified inaccurately as FluidApp.app
3. My method of determining the number of desktops present is not likely to work on all versions of macOS
4. I don't have a way of omitting apps assigned to all desktops

Any help in figuring out how to move windows to their original desktops (as recorded by some improved version of this script) would be appreciated.  I had had an idea to simply quit an app and then launch it using a shell command like `open -a app -F` and then just switching desktops using keystrokes (like the below script does) and opening the associated files when I'm on that desktop.  That could work in a limited fashion, however the `-F argument doesn't work for untitled/unsaved documents.  Those always open regardless of `-F`.  That would mean however that I would have to get the file paths associated with the open windows in the script below.  I would settle for what is possible this way and just let the untitled docs end up wherever.  That would be better than nothing.  Though I could launch the app from the desktop where the first untitled doc was open...

Applescript:


--This applescript reports a list of application windows present on each desktop
--This is only a sample script intended to eventually be used to restore all application windows to their pre-reboot desktops

tell application "System Events"
   
   set windows_string to ""
   set numDesktops to (first paragraph of (do shell script "strings ~/Library/Preferences/com.apple.spaces.plist | grep -c ^\\\\$")) + 1
   --the following tcsh command can determine the number of desktops:
   -- @ x = ( `strings ~/Library/Preferences/com.apple.spaces.plist | grep -c '^\$'` + 1 ); echo $x
   
   -- switch to the first desktop:
   repeat with aDesktop from 1 to numDesktops
       key code 123 using {control down}
   end repeat
   
   repeat with aDesktop from 1 to numDesktops
       
       set windows_string to windows_string & return & return & "Desktop " & (aDesktop as string) & return
       delay 1
       get (the name of every application process whose class of windows contains window)
       
       repeat with P in the result
           
           set windows_string to windows_string & return & return & P & return
           
           get (every window of process (contents of P) whose value of attribute "AXMinimized" is false)
           
           repeat with W in the result
               
               set window_name to ((name of W) as string)
               
               --Stickies window names can be multi-line, so this trims from the first hard return onward
               set better_window_name to (my replacePattern:"[\\n].*" inString:window_name usingThis:"")
               if window_name is not equal to "" then
                   try
                       set windows_string to windows_string & better_window_name & return
                   on error
                       set windows_string to windows_string & "couldn't get window name" & return
                   end try
               end if
               
           end repeat
           
       end repeat
       
       --switch to the next desktop
       key code 124 using {control down}
       
   end repeat
   
   display dialog "List of windows on this desktop: " & return & return & windows_string
   
end tell

--Call like this: set res to my replacePattern:"\\s+" inString:"1 subtratcing-these: -2 3 4" usingThis:"-"
use framework "Foundation"
use scripting additions

on replacePattern:thePattern inString:theString usingThis:theTemplate
   set theRegEx to current application's NSRegularExpression's regularExpressionWithPattern:thePattern options:0 |error|:(missing value)
   set theResult to theRegEx's stringByReplacingMatchesInString:theString options:0 range:{location:0, |length|:length of theString} withTemplate:theTemplate
   return theResult as text
end replacePattern:inString:usingThis:

Offline

 

#3 2019-07-10 10:39:02 am

robleach72
Member
Registered: 2013-01-01
Posts: 26

Re: Script to memorize and restore windows to specific (MC) desktops

I'm another step closer...

This is another proof of concept.  And it works on untitled documents too.  Despite this example, I wouldn't use it for Safari windows because:

1. It loses the window history
2. Safari already restores windows to their original desktops

But regardless, I should be able to use this code to update the first script to get & save document file paths and then to process the saved paths and close & reopen windows on the target desktop by switching to them using control-arrow keystrokes...

Applescript:


--This is a proof of concept that docs open in an app can be closed on another desktop and be reopened on another desktop. It works with untitled documents as well as saved documents. When an app launches (even with open ... -F) all reopened windows open on the current desktop instead of the desktop where they were before a reboot. This simply proves that you can close & re-open the docs on whichever desktop you are currently on. Combined with my "ReportWindowsOnEachDesktop" script, this could allow all windows to be re-opened on their original desktop...

--switch to a different desktop and run...
tell application "Safari"
   set p to get URL of document 1 of application "Safari"
   close document 1
   --display dialog p
   do shell script "open '" & p & "'"
end tell

tell application "TextEdit"
   set doclist to every document
   set pathlist to {}
   repeat with adoc in doclist
       --display dialog path of adoc as text
       set p to path of adoc as text
       set end of pathlist to p
       --display dialog p
       close adoc saving yes
   end repeat
   repeat with apath in pathlist
       --display dialog path of adoc as text
       --display dialog p
       do shell script "open -a 'TextEdit' '" & apath & "'"
   end repeat
   return doclist
end tell

Offline

 

#4 2019-07-10 02:33:20 pm

robleach72
Member
Registered: 2013-01-01
Posts: 26

Re: Script to memorize and restore windows to specific (MC) desktops

I'm still making progress.  This is a modification of the first script to save the desktop, app, and path of each open document.  It unfortunately doesn't work with Stickies, but I have an idea on how to possibly circumvent that issue.  This script skips any app that doesn't have open "document" windows.

Applescript:


--This applescript reports a list of application windows present on each desktop
--This is only a sample script intended to eventually be used to restore all application windows to their pre-reboot desktops

tell application "System Events"
   
   set windows_string to ""
   set docs_string to ""
   set numDesktops to (first paragraph of (do shell script "strings ~/Library/Preferences/com.apple.spaces.plist | grep -c ^\\\\$")) + 1
   
   -- switch to the first desktop:
   repeat with aDesktop from 1 to numDesktops
       key code 123 using {control down}
   end repeat
   
   repeat with aDesktop from 1 to numDesktops
       
       set windows_string to windows_string & return & return & "Desktop " & (aDesktop as string) & return
       delay 1
       get (the name of every application process whose class of windows contains window)
       
       repeat with P in the result
           
           set windows_string to windows_string & return & return & P & return
           set alldocs to {}
           set doclist to my getAppDocs(P)
           set docnames to first item in doclist
           set docpaths to second item in doclist
           set adoc to null
           
           --For some reason, this stopped working for me: 'whose value of attribute "AXMinimized" is false' so I commented it out
           get (every window of process (contents of P)) -- whose value of attribute "AXMinimized" is false)
           
           repeat with W in the result
               
               set window_name to ((name of W) as string)
               try
                   set attrs to attributes of W
                   display dialog attrs
               end try
               --Stickies window names can be multi-line, so this trims from the first hard return onward
               --Stickies isn't applescript-able. I may however be able to use my "StickiesBackup2" script to circumvent that issue
               set better_window_name to (my replacePattern:"[\\n].*" inString:window_name usingThis:"")
               
               if window_name is not equal to "" then
                   try
                       set i to 0
                       repeat with listdoc in docnames
                           set i to i + 1
                           if (listdoc as string) is window_name then
                               --set adoc to item i of alldocs
                               set adoc to item i of docpaths
                           end if
                       end repeat
                       set adocpath to adoc
                       if adocpath is not null then
                           set docs_string to docs_string & "Desktop " & (aDesktop as string) & tab & P & tab & adocpath & return
                       end if
                       --on error errstr
                       --    set docs_string to docs_string & "Desktop " & (aDesktop as string) & tab & P & tab & "ERROR for " & better_window_name & ": " & errstr & return
                   end try
                   
                   try
                       set windows_string to windows_string & better_window_name & return
                       --on error
                       --    set windows_string to windows_string & "couldn't get window name" & return
                   end try
               else
                   --set docs_string to docs_string & "Desktop " & (aDesktop as string) & tab & P & tab & "no docs" & return
               end if
               
           end repeat
           
       end repeat
       
       --switch to the next desktop
       key code 124 using {control down}
       
   end repeat
   
   display dialog "List of documents on each desktop: " & return & return & docs_string
   
end tell

on getAppDocs(appName)
   set doclist to {}
   
   set myscript to "tell application \"" & appName & "\"" & return
   set myscript to myscript & tab & "set docnames to {}" & return
   set myscript to myscript & tab & "set docpaths to {}" & return
   set myscript to myscript & tab & "try" & return
   set myscript to myscript & tab & tab & "set docnames to name of every document" & return
   set myscript to myscript & tab & tab & "set docpaths to path of every document" & return
   set myscript to myscript & tab & "end try" & return
   set myscript to myscript & tab & "return {docnames,docpaths}" & return
   set myscript to myscript & "end tell" & return
   
   try
       run script myscript
       set doclist to result as list
   on error errstr
       display dialog "Error running script:" & return & return & myscript & return & return & "Error: " & errstr
   end try
   
   return doclist
end getAppDocs

--Call like this: set res to my replacePattern:"\\s+" inString:"1 subtratcing-these: -2 3 4" usingThis:"-"
use framework "Foundation"
use scripting additions
on replacePattern:thePattern inString:theString usingThis:theTemplate
   set theRegEx to current application's NSRegularExpression's regularExpressionWithPattern:thePattern options:0 |error|:(missing value)
   set theResult to theRegEx's stringByReplacingMatchesInString:theString options:0 range:{location:0, |length|:length of theString} withTemplate:theTemplate
   return theResult as text
end replacePattern:inString:usingThis:

It also assumes that window names are the same as the file names (which is not necessarily true).

Last edited by robleach72 (2019-07-10 02:34:39 pm)

Offline

 
  • Index
  •  » Code Exchange
  •  » Script to memorize and restore windows to specific (MC) desktops

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)