Positioning a window with AppleScript using dual monitors - macos

I have two monitors set up and I am trying to position the window of an application in the second monitor but nothing I do seems to work. For example I am using my laptop and the terminal window is maximized on the screen. Then I plug in an external monitor. I then want to run the applescript and have the terminal maximize on the larger second monitor.
Here is what I have right now:
set monitorTwoPos to {1050, -600}
set monitorTwoSze to {1200, 1920}
tell application "Microsoft Outlook"
set position of window 1 to monitorTwoPos
set size of window 1 to monitorTwoSze
end tell
Here is the error I get:
/Users/vcutten/AppleScripts/SpacesWork.scpt:1291:1332: execution error:
Microsoft Outlook got an error: Can’t make position of window 1 into type specifier. (-1700)
I'm pretty sure I'm just using set position and set size completely wrong :( When I used bounds it kind of works...
Bonus Question:
How can I loop through the open windows and get their size? Thanks!

What have you tried?
I think to solve this you need to calculate the screen size and coordinates of the second monitor. For example, your main monitor starts at position {0,0}. So the starting position of the second monitor has to be something different and you need to find that. Luckily I have written a tool that will give you both the starting coordinates and screen size of your monitors. Once you have the size and position then it's simple. System events can set the size and position of a window so you could do something like this...
set monitorSize to {800, 600}
set monitorPosition to {-800, 0}
tell application "System Events"
tell process "Terminal"
set frontWindow to first window
set position of frontWindow to monitorPosition
set size of frontWindow to monitorSize
end tell
end tell
So from the above script you just need the size and position variables. You can get my tool here called hmscreens which will give you those. You may need to do some adjusting of the coordinates depending on if the screen is measured from the lower left corner or upper left, but that's just simple math.
I hope that helps...

Use bounds instead of position, it works. You can get bounds of the window like this:
tell application "Microsoft Outlook"
get bounds of first window
end tell
Answer to the bonus question:
tell application "Microsoft Outlook"
repeat with nextWindow in (get every window)
get bounds of nextWindow
end repeat
end tell
If you open Replies tab at bottom part of Applescript editor, you will see all get results.
Hope it helps.

Here is a script that handles saving and restoring size and postion for multiple display configurations. It may have some issues with fullscreen apps but it seems to work ok.
-- allSettings is a list of records containing {width:? height:? apps:{{name:? pos:? size:?},...}
-- for each display setup store the apps and their associated position and size
property allSettings : {}
-- create a variable for the current settings
set currentSettings to {}
display dialog "Restore or save window settings?" buttons {"Restore", "Save"} default button "Restore"
set dialogResult to result
tell application "Finder"
-- use the desktop bounds to determine display config
set desktopBounds to bounds of window of desktop
set desktopWidth to item 3 of desktopBounds
set desktopHeight to item 4 of desktopBounds
set desktopResolution to desktopWidth & "x" & desktopHeight
-- find the saved settings for the display config
repeat with i from 1 to (count of allSettings)
if (w of item i of allSettings is desktopWidth) and (h of item i of allSettings is desktopHeight) then
set currentSettings to item i of allSettings
end if
end repeat
if (count of currentSettings) is 0 then
-- add the current display settings to the stored settings
set currentSettings to {w:desktopWidth, h:desktopHeight, apps:{}}
set end of allSettings to currentSettings
--say "creating new config for " & desktopResolution
else
--say "found config for " & desktopResolution
end if
end tell
tell application "System Events"
if (button returned of dialogResult is "Save") then
say "saving"
repeat with p in every process
if background only of p is false then
tell application "System Events" to tell application process (name of p as string)
set appName to name of p
if (count of windows) > 0 then
set appSize to size of window 1
set appPosition to position of window 1
else
set appSize to 0
set appPosition to 0
end if
set appSettings to {}
repeat with i from 1 to (count of apps of currentSettings)
if name of item i of apps of currentSettings is name of p then
set appSettings to item i of apps of currentSettings
end if
end repeat
if (count of appSettings) is 0 then
set appSettings to {name:appName, position:appPosition, size:appSize}
set end of apps of currentSettings to appSettings
else
set position of appSettings to appPosition
set size of appSettings to appSize
end if
end tell
end if
end repeat
end if
if (button returned of dialogResult is "Restore") then
if (count of apps of currentSettings) is 0 then
say "no window settings were found"
else
say "restoring"
repeat with i from 1 to (count of apps of currentSettings)
set appSettings to item i of apps of currentSettings
set appName to (name of appSettings as string)
try
tell application "System Events" to tell application process appName
if (count of windows) > 0 then
set position of window 1 to position of appSettings
set size of window 1 to size of appSettings
end if
end tell
end try
end repeat
end if
end if
end tell
https://gist.github.com/cmackay/5863257

Related

Print result from two functions to console

I've created a small script to get the current size and position of a window. Unfortunately, I only can get one result at a time and need to comment/uncomment the other one.
tell application "System Events" to tell process "Toggl Track"
# get position of window 1
get size of window 1
end tell
If I uncomment both, I will only get the size.
So, my I tried to write the results in a variable and then log these instead.
tell application "System Events" to tell process "Toggl Track"
set position to get position of window 1
set size to get size of window 1
log position & size
end tell
With this, I get no result at all.
What did I do wrong?
position and size are part of the terminology of System Events. You cannot use them as variable names. Rename the variables
tell application "System Events" to tell process "Toggl Track"
set windowPosition to position of window 1
set windowSize to size of window 1
log windowPosition & windowSize
end tell
Regarding the first script, get saves the result of the line in the AppleScript property result and overwrites previous values.

How to get Application Name To Call from PID in Mac AppleScript

There is a script that lets you resize any app in mac. This is the code:
set theApp to "Application Name"
set appHeight to 1080
set appWidth to 1920
tell application "Finder"
set screenResolution to bounds of window of desktop
end tell
set screenWidth to item 3 of screenResolution
set screenHeight to item 4 of screenResolution
tell application theApp
activate
reopen
set yAxis to (screenHeight - appHeight) / 2 as integer
set xAxis to (screenWidth - appWidth) / 2 as integer
set the bounds of the first window to {xAxis, yAxis, appWidth + xAxis, appHeight + yAxis}
end tell
I want to change the size of a java application opened by a launcher. When I insert the name of any app, it works. However when I insert the name of the app that I want to resize it doesn't work. I know the process id of the app that I want to resize. Is there a way I can change this line set theApp to "Application Name" to use PID instead of Application name?
Thanks.
Not all apps are AppleScript scriptable and some that are do not support the bounds property, they use position property and size property. Also, sometimes you'll need System Events to position and size an app's window.
I use a keyboard shortcut assigned in FastScripts with the following example AppleScript code to automatically adjust the frontmost app's window. You can adjust the code to suite your needs.
If the frontmost app can't use the bounds property it silently errors, and then System Events does it.
tell application "System Events"
set frontmostProcess to name of process 1 whose frontmost is true
end tell
try
tell application frontmostProcess
set bounds of window 1 to {0, 22, 1136, 844}
end tell
on error
tell application "System Events" to tell application process frontmostProcess
set position of window 1 to {0, 22}
set size of window 1 to {1136, 822}
end tell
end try
Note: I am not affiliated with the developer of FastScript, just a satisfied user. It's also free for the first ten keyboard shortcuts.

Change Finder folder background color AppleScript

I'm trying to change the background color of Finder within a repeat loop.
tell application "Finder"
set windowCount to (count windows)
if windowCount > 0 then #check if windows are available
set bgcolor to {65535, 0, 32896}
repeat 10 times
tell the icon view options of window 1
set the background color to {some item of bgcolor, some item of bgcolor, some item of bgcolor}
delay 0.1
end tell
end repeat
end if
end tell
I know I'm missing something simple here. I got it to work in other contexts (outside the loop)...
If you close and reopen your Finder window manually the background changes! According to this question from yesterday the solution is to re-open the window (aka open a new window and close the old) to "refresh" the view:
tell application "Finder"
set windowCount to (count windows)
if windowCount > 0 then #check if windows are available
set bgcolor to {65535, 0, 32896}
repeat 10 times
tell the icon view options of window 1
set the background color to {some item of bgcolor, some item of bgcolor, some item of bgcolor}
end tell
try
set w to front window
tell (make new Finder window)
try
set target to (get target of w)
end try
set bounds to (bounds of w)
set current view to (current view of w)
end tell
tell w to close
end try
delay 0.1
end repeat
end if
end tell
Enjoy, Michael / Hamburg

Resizing all windows of all running applications

I am attempting to write an applescript script that will allow me to resize all windows of all running applications whenever I choose (I currently use Stay, but find that it is glitchy at times, so I want to "re-invent" it)
I have been following some applescripting tutorials and have come up with the following code to do so, but it is buggy:
tell application "Finder"
set rect to bounds of window of desktop
end tell
property excludedApplicationNames : {"Finder"}
tell application "System Events"
say "a"
repeat with theProcess in processes
say "b"
if background only of theProcess is false then
say "c"
set theProcessName to name of theProcess as string
if theProcessName is not in excludedApplicationNames then
say theProcessName
tell application theProcess
set bounds of windows of process theProcess to rect
end tell
end if
end if
end repeat
end tell
say "done"
The problem is that when this code encounters my only terminal window (with several open tabs), it error: System Events got an error: Can’t set application (item 2 of every process) to {0, 0, 1280, 900}.System Events got an error: Can’t set application (item 2 of every process) to {0, 0, 1280, 900}.
Changing tell application theProcess to tell application theProcessName doesn't help (same error), and neither does changing it to tell application "System Events" (Error: System Events got an error: Can’t make item 2 of every process into type integer.)
Interestingly, this works as expected:
tell application "Finder"
set rect to bounds of window of desktop
end tell
tell application "Terminal"
repeat with theWindow in windows
set bounds of theWindow to rect
end repeat
end tell
So I'm very confused.
What am I doing wrong? How can I fix this?
tell application "Finder"
set {0, 0, dtw, dth} to bounds of window of desktop
end tell
tell application "System Events"
repeat with p in (processes where background only is false)
tell p
if name is not in {"Finder"} then
set position of windows to {0, 0}
set size of windows to {dtw, dth}
end if
end tell
end repeat
end tell
Took about 3 seconds on my Mac
Maximizes Terminal windows to fill the screen (except for the 4px area taken up by Dock)
tell application "Finder"
set dtb to bounds of window of desktop
end tell
tell application "System Events"
bundle identifier of processes where background only is false
end tell
repeat with bid in result
tell application id bid
try
if name is not in {"Finder"} then
set (bounds of windows where visible is true) to dtb
end if
end try
end tell
end repeat
Took about 0.3 seconds on my Mac
Doesn't work with all applications like Preview or Reeder
Uses bundle identifiers because a few applications have different process and application names
Resizes Terminal windows so that they have a few pixels empty space above and below them
I use this script to maximize windows:
try
tell application "Finder" to set dtb to bounds of window of desktop
tell application (path to frontmost application as text)
if name is in {"Terminal"} then
error
else
set bounds of window 1 to dtb
end if
end tell
on error
tell application "System Events" to tell (process 1 where it is frontmost)
try
click (button 1 of window 1 where subrole is "AXZoomButton")
end try
end tell
end try
In many applications that don't have basic AppleScript support the zoom button also maximizes windows to fill the screen.
This one take the size of the dock into account. I have mine on the right side of the monitor, but it should be easy to modify to accommodate the dock being on the bottom.
tell application "Finder"
set dtb to bounds of window of desktop
end tell
tell application "System Events" to tell process "Dock"
set dockDimentions to size in list 1
set dockWidth to item 1 of dockDimentions
end tell
tell application "System Events"
bundle identifier of processes where background only is false
end tell
repeat with bid in result
tell application id bid
try
if name is not in {"Finder", "System Preferences", "Notepad", "Terminal", "Activity Monitor"} then
set x to item 1 of dtb
set y to item 2 of dtb
set w to (item 3 of dtb) - dockWidth
set h to item 4 of dtb
set (bounds of windows) to {x, y, w, h}
end if
end try
end tell
end repeat
This eventually did the trick for me:
property blacklist : {"Finder", "Preview", "Console", "AppleScript Editor", "Spotify", "TaskCoach"}
property buttonApps : {"LyX", "Eclipse"}
property buttonMaps : {{name:"LyX", Button:1, pname:"lyx"}, {name:"Eclipse", Button:2, pname:"eclipse"}}
tell application "Finder" to set theBounds to bounds of window of desktop
tell application "System Events"
set bids to bundle identifier of processes where background only is false
end tell
repeat with bid in bids
tell application id bid
if name is not in blacklist then
set appName to name as string
if name is "Terminal" then
set newBounds to {0, 0, (item 3 of theBounds) - 10, item 4 of theBounds}
repeat with theWindow in windows
if visible of theWindow is true then
say appName
set bounds of theWindow to newBounds
end if
end repeat
else if name is not in buttonApps then
repeat with theWindow in windows
if visible of theWindow is true then
set bounds of theWindow to theBounds
end if
end repeat
else if name is in buttonApps then
-- get the buttonNumber
repeat with buttonApp in buttonMaps
if (name of buttonApp as string) is appName then
set theButton to Button of buttonApp
end if
end repeat
tell application "System Events"
repeat with theProcess in (processes where bundle identifier is bid)
try
tell theProcess to tell window 1 to click button theButton
end try
end repeat
end tell
end if
end if
end tell
end repeat
Note that "Spotify" and "Task Coach" are blacklisted because I am not able to resize them by:
setting the window bounds
clicking on the green button
clicking on "Window">"Zoom" in the menu bar
using the ⌘F10 shortcut that I had mapped it to.
If anyone is able to come up with a better solution, I'm all ears

Resizing Windows of Unscriptable Applications in AppleScript

I am working on an application that moves and resizes windows of other applications on OSX.
The main application is written in Cocoa but the resizing part is done in AppleScript as Cocoa does not seem to have this kind of feature.
Here is the result of a regular call to ActionScript (passed as a string to NSAppleScript):
tell application "TextEdit"
set currentWindow to the window id 5184
set bounds of the currentWindow to {2855, 218, 3790, 578}
end tell
The Window ID is obtained using the CGWindowListCopyWindowInfo API introduced in OSX 10.6.
This approach works fine for most windows but fails for applications that are unscriptable.
The most prominent example is OSX's own Preview.
I have tried "telling" System Events instead of Preview using variations of this code
tell application "System Events"
set bounds of window 5184 to {1920, -502, 2855, 578}
end tell
However, OSX gives me an error message:
"System Events got an error: Can’t set bounds of window 5184 to {1920, -502, 2855, 578}."
The same happens when just trying to get a reference to the window:
tell application "System Events"
get window 5184
end tell
I have doublechecked that the window exists and the window ID is correct.
What is the proper way to programmatically resize unscriptable windows on OSX?
I can see it is possible from applications such as moom.
Any advice - be it Cocoa- or AppleScript-Based or something else entirely - is more than welcome.
Unfortunately, the AppleScripting the windows of an app is up to the developer of the app -- there's no clean way to do it in general. Look at this script I wrote some years ago for all the special cases:
-- Get screen bounds and origins
set f to (path to preferences from local domain as Unicode text) & "com.apple.windowserver.plist"
tell application "System Events" to set {{|Width|:w1, |Height|:h1, |OriginX|:OX1, |OriginY|:OY1}, {|Width|:w2, |Height|:h2, |OriginX|:OX2, |OriginY|:OY2}} to value of property list items of property list item 1 of property list item "DisplaySets" of property list file f
set SecondaryScreenBounds to {OX2, OY2, OX2 + w2, OY2 + h2}
set RHedge to OX1
set BOTedge to OY1
tell application "Finder"
-- Get the running apps (excluding those with special windows)
set |running| to name of processes whose visible is true and name is not "Finder" and name is not "QuickSilver" and name is not "CopyPaste" and name is not "DropCopy" and name is not "iPulse"
repeat with anApp in |running|
try -- for a scriptable App with window bounds property
tell application anApp
set allWindows to (every window)
repeat with aWindow in allWindows
set Wbounds to (get bounds of aWindow)
if item 1 of Wbounds > RHE or item 2 of Wbounds > BoE then my moveWindows(contents of anApp)
end repeat
end tell
on error -- for an App with window position & size properties
tell application "System Events"
tell application process anApp
set allWindows to (every window)
repeat with aWindow in allWindows
set {p1, p2} to aWindow's position
if p1 ≥ RHedge or p2 ≥ BOTedge then my moveWindows(contents of anApp)
end repeat
end tell
end tell
end try
end repeat
-- for the Finder
set allWindows to (every window whose visible is true)
repeat with aWindow in allWindows
set Wbounds to bounds of aWindow
if (item 1 of Wbounds) > RHedge or (item 2 of Wbounds) > BOTedge then
set bounds of aWindow to {200, 200, 1200, 800}
end if
end repeat
end tell
-- for Safari
if "Safari" is in |running| then tell application "Safari"
set Wind to name of windows
set Wbounds to bounds of windows
repeat with k from 1 to count Wind
set W to item k of Wind
set B to item k of Wbounds
if (item 1 of B) ≥ RHedge or (item 2 of B) ≥ BOTedge then
set bounds of window W to {200, 200, 1200, 800}
end if
end repeat
end tell
-- for HoudahSpot
if "HoudahSpot" is in |running| then tell application "System Events" to tell process "HoudahSpot"
set W to name of windows
set B to position of windows
repeat with k from 1 to count W
if item k of W is not missing value and (item 1 of item k of B) ≥ RHedge then set position of window (item k of W) to {100, 100}
end repeat
end tell
-- for Activity Monitor
if "Activity Monitor" is in |running| then tell application "System Events" to tell process "Activity Monitor"
set W to name of windows
set B to position of windows
repeat with k from 1 to count W
if item k of W is not missing value and (item 1 of item k of B) ≥ RHedge then set position of window (item k of W) to {100, 100}
end repeat
end tell
-- for 1Password
if "1Password" is in |running| then tell application "System Events" to tell process "1Password"
set W to name of windows
set B to position of windows
repeat with k from 1 to count W
if item k of W is not missing value and (item 1 of item k of B) ≥ RHedge then set position of window (item k of W) to {100, 100}
end repeat
end tell
-- for iCal
if "iCal" is in |running| then tell application "iCal"
set iCB to bounds of window "iCal"
if item 1 of iCB ≥ RHedge or item 2 of iCB ≥ BOTedge then
set bounds of window "iCal" to {100, 100, 1200, 1000}
end if
end tell
-- for a Help Window
tell application "System Events"
if exists process "Help Viewer" then tell process "Help Viewer"
set W to windows
repeat with w1 in W
set position of w1 to {200, 200}
end repeat
end tell
end tell
to moveWindows(anApp)
tell application "System Events"
if anApp is "ScriptLight" then
tell process "ScriptLight" to set position of window 1 to {200, 200}
else if anApp is "PowerKey" then
tell process "PowerKey" to set position of window "PowerKey" to {200, 200}
else if anApp is "Script Debugger 4" then
tell application process "Script Debugger 4"
set allWindows to (every window)
repeat with aWindow in allWindows
set {p1, p2} to aWindow's position
if p1 ≥ 1680 or p2 > 1050 then set aWindow's position to {100, 100}
end repeat
end tell
end if
end tell
end moveWindows
You mentioned Preview, so open a window in Preview and run this script.
tell application "System Events"
tell process "Preview"
set theWindows to windows
return properties of (item 1 of theWindows)
end tell
end tell
Look at the properties that are returned. There is no "id" property so you can't access windows that way. There is no "bounds" property so you can't set the bounds. Also notice that you have to get the windows from a "process" in the system events call. So if you want to use system events your code is pretty far off.
Your best bet would be to find the name of the window you want to target. Then in applescript you get theWindows as I have above, then loop through them checking their name. When you find the proper name you have found the appropriate window. Then you could set the "size" and "position" properties of that window.
Good luck because your task is a large one!
I was having the same issue trying to resize an application using similar logic to what you were using.
In my case I needed to resize an application to match the screen size and I found that you could use "System Events" if you grant script editor assistive access. If you save the script as an app you will need to grant that app access instead.
The following script simply grabs the screen size, reopens the app to make sure it is open, then uses "System Events" to set the size of the front window.
NOTE: I'm targeting an application called "cefclient" below
set theApp to "cefclient"
tell application "Finder"
set screenResolution to bounds of window of desktop
end tell
set screenWidth to item 3 of screenResolution
set screenHeight to item 4 of screenResolution
tell application theApp
activate
reopen
end tell
tell application "System Events"
tell process theApp
set the size of front window to {screenWidth, screenHeight}
end tell
end tell

Resources