Guest
2011-01-27, 09:01 AM
FWIW a version which should work with single or multiple display systems, and can be run from the command line with four arguments: OriginX, OriginY, Width, Depth [and, optionally Index - see next post].
(Useful for assigning keystrokes to a variety of screen positions with something like Keyboard Maestro).
Code:
property pVer : "1.2"
property pTitle : "Resize and reposition window by % of screen" & tab & pVer
-- SET SIZE AND POSITION OF SELECTED WINDOW (ANY APPLICATION)
-- (WORKS WITH SINGLE OR DUAL DISPLAY SYSTEMS)
-- Can be also used with arguments from the command line.
-- e.g. if saved as ~/Library/Scripts/PlaceWin.scpt then,
-- to resize the selected window to the left third of the screen:
-- osascript ~/Library/Scripts/PlaceWin.scpt 0 0 0.333 1
-- where the parameters are OriginX OriginY Width Depth
-- THUS, TO RESTORE A WORKSPACE, A COMMAND LINE BATCH SCRIPT LIKE
-- open -a 'omnioutliner professional'
-- osascript ~/Library/Scripts/PlaceWin.scpt 0 0 0.667 1 2
-- open -a 'DEVONthink Pro'
-- osascript ~/Library/Scripts/PlaceWin.scpt 0 0 0.667 1 1
-- (Which can be assigned to a keystroke with something like KeyBoard Maestro)
-- Version history
-- Ver 0.4 Waits, up to pTimeOut seconds, for the frontmost process to present a window (useful if an app is loading)
-- Ver 0.5 Should support more than two monitors. Code has been slightly simplified.
-- Ver 0.7 Cycles between one-corner rectangles, and their left-right and top-down complements
-- Ver 0.8 Accepts a fifth argument: an index which suppresses toggling and selects a particular transform of the basic coordinates (usually 1)
-- (Useful when calling the script after an open -b or open -a to launch an application)
-- Ver 0.9 if the height or width is 0.5 toggles between vertical and horizontal splits
-- (Economises on the allocation of keyboard shortcuts through something like Keyboard Maestro)
-- Ver 1.0 if the height or width is < 0.5 and expressible as 1/N where N is a small integer (≤ 6), then cycles between the N fitting rectangles
-- Ver 1.1 Restores compensation for menu bar space (though Microsoft Word still recalcitrant with landscape splits)
-- Ver 1.2 Defaults to the primary rectangle on the first call with a particular set of parameters
-- (Begins to cycle through any screen-tiling transforms on the first repeated call).
-- DEFAULT: A full-height window, rightmost third of the screen:
-- NB (Not all windows can be arbitrarily resized)
-- UNITS - proportion of whichever screen is displaying the selected window
-- POSITION - (origin: top left)
property pPosnAcross : 0
property pPosnDown : 0
-- SIZE
property pSizeAcross : 1 / 3
property pSizeDown : 1
property pblnMenuWindow : false
property pMenuHeight : 22
property pstrWinPrefsPath : "/Library/Preferences/com.apple.windowserver.plist"
property pTimeout : 10 -- Maximum number of seconds to wait for a loading app to present a window
property plstDisplays : {}
property piBox : 0
property piSpecific : missing value
property pblnRepeat : false
on run argv
-- PROCESS ANY ARGUMENTS PASSED BY AN OSASCRIPT SHELL COMMAND
try
if class of argv is list then
-- FIRST FOUR ARGUMENTS REQUIRED: OriginX, OriginY, Width, Height
set refPosnSize to a reference to items 1 thru 4 of argv
repeat with i from 1 to 4
set item i of refPosnSize to (item i of refPosnSize) as real
end repeat
-- RECORD WHETHER THESE ARGUMENTS MATCH THOSE OF THE PREVIOUS CALL
-- (WE MAY NEED TO CYCLE TO THE NEXT COMPLEMENTARY TRANSFORM)
set pblnRepeat to (contents of refPosnSize) = {pPosnAcross, pPosnDown, pSizeAcross, pSizeDown}
-- AND UPDATE THE PERSISTENT PARAMETERS IF THEY HAVE CHANGED
if not pblnRepeat then set {pPosnAcross, pPosnDown, pSizeAcross, pSizeDown} to refPosnSize
-- HANDLE THE OPTIONAL FIFTH ARGUMENT
-- (AN INDEX TO A SPECIFIC COMPLEMENTARY TRANSFORM (LEFT/RIGHT OR TOP/DOWN) OF THE BASE RECTANGLE)
-- USEFUL IF THE OSASCRIPT CALL FOLLOWS AN OPEN -A CALL
-- WHICH AIMS TO OPEN A NAMED APP TO A SPECIFIC WINDOW SIZE/POSN
if length of argv > 4 then
set piSpecific to (item 5 of argv)
else
set piSpecific to missing value
end if
-- IF THIS IS NOT A REPEATED CALL WITH THE SAME ARGUMENTS,
-- (AND NO TRANSFORM HAS BEEN SPECIFIED),
-- THEN DEFAULT TO THE BASE RECTANGLE (TRANSFORM 1)
if (not pblnRepeat) and (piSpecific is missing value) then set piSpecific to 1
end if
on error strError
return strError
end try
-- NOW REPOSITION AND RESIZE THE FRONT WINDOW,
-- USING THE GIVEN PROPORTIONS OF THE CURRENT SCREEN SIZE
ReSizePosn()
end run
-- GET THE CURRENT SCREEN SIZE,
-- AND MOVE AND RESIZE THE FRONT WINDOW USING PROPORTIONAL DIMENSIONS
on ReSizePosn()
-- GET THE PROPERTIES OF THE REMEMBERED DISPLAYS
tell application id "com.apple.systemevents"
tell contents of property list file pstrWinPrefsPath
set plstDisplays to (first item of ((value of (property list item "DisplaySets")) as list))
end tell
-- GET THE FRONT WINDOW AND ITS POSITION
set refProc to a reference to (first application process where frontmost = true)
set procFront to (first application process where frontmost = true)
set oWin to missing value
repeat with i from 1 to pTimeout * 2
if (count of windows of procFront) > 0 then exit repeat
do shell script "sleep 0.5"
end repeat
set blnMSWord to (name of procFront) contains "Microsoft Word"
set oWin to front window of procFront
if oWin is missing value then return
set {lngWinX, lngWinY} to position of oWin
-- FIND THE DISPLAY CONTAINING THE TOP LEFT CORNER OF THIS WINDOW
set blnFound to false
repeat with recDisplay in plstDisplays
set {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown} to {OriginX, OriginY, Width, Height} of recDisplay
if ((lngWinX ≥ lngScreenX) and (lngWinX < (lngScreenX + lngScreenAcross)) and ¬
((lngWinY ≥ lngScreenY) and (lngWinY < (lngScreenY + lngScreenDown)))) then
set pblnMenuWindow to {lngScreenX, lngScreenY} = {0, 0}
set blnFound to true
exit repeat
end if
end repeat
if not blnFound then return
set lstBoxes to my GetTransforms({pPosnAcross, pPosnDown, pSizeAcross, pSizeDown}, {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown})
if piSpecific is missing value then
set piBox to piBox + 1
if piBox > length of lstBoxes then set piBox to 1
else
try
set piBox to (piSpecific as integer) -- specified as fifth argument of the command line
on error
set piBox to 1
end try
end if
set {lngWinX, lngWinY, lngWinWidth, lngWinHeight} to item piBox of lstBoxes
if pblnMenuWindow then set lngWinY to lngWinY + pMenuHeight
tell oWin
if blnMSWord then
tell application id "com.Microsoft.Word"
tell front window of active document
set position to {lngWinX, lngWinY + (pMenuHeight)}
set width to lngWinWidth
set height to lngWinHeight
end tell
end tell
else
set {position, size} to {{lngWinX, lngWinY}, {lngWinWidth, lngWinHeight}}
end if
end tell
end tell
return
end ReSizePosn
-- FOR RECTANGLES WHICH INCLUDE ONE OR TWO CORNERS OF THE SCREEN,
-- OR TOUCH OPPOSITE EDGES OF THE SCREEN, AND HAVE A HEIGHT OR WIDTH
-- WHICH IS A SIMPLE FRACTION OF THE CORRESPONDING SCREEN DIMENSION
-- GENERATE THE SCREEN-TILING SET OF COMPLEMENTARY RECTANGLES.
-- (REPEATED CALLS OF THE SCRIPT WITH THE SAME PARAMETERS CYCLE THROUGH THESE COMPLEMENTARY TRANSFORMS,
-- ALLOWING SIMPLE TILING OF THE SCREEN WITH A SMALL NUMBER OF ASSIGNED KEYSTROKES)
on GetTransforms({pPosnAcross, pPosnDown, pSizeAcross, pSizeDown}, {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown})
if pblnMenuWindow then
set lngScreenDown to lngScreenDown - pMenuHeight
--set lngScreenY to lngScreenY + pMenuHeight
end if
set {lngX, lngY, lngwidth, lngHeight} to {(pPosnAcross * lngScreenAcross) + lngScreenX, (pPosnDown * lngScreenDown) + lngScreenY, pSizeAcross * lngScreenAcross, pSizeDown * lngScreenDown}
set {pSizeAcross, pSizeDown} to {pSizeAcross as real, pSizeDown as real}
set {lngDeltaX, lngDeltaY} to {0, 0}
if lngX = 0 then set lngDeltaX to lngwidth
if lngY = 0 then set lngDeltaY to lngHeight
if (pSizeAcross = 1.0) or (pSizeDown = 1.0) then -- Single split left/right or up/down
if pSizeAcross = 1.0 then
-- UP DOWN
if pSizeDown = 0.5 then -- (vertical and horizontal splits)
set {lngAltwidth, lngAltHeight} to {pSizeDown * lngScreenAcross, pSizeAcross * lngScreenDown}
{{lngX, lngY, lngwidth, lngHeight}, {lngX, lngDeltaY, lngwidth, lngScreenDown - lngHeight}, ¬
{0, 0, lngAltwidth, lngAltHeight}, {lngAltwidth, 0, lngScreenAcross - lngAltwidth, lngAltHeight}}
else if pSizeDown ≥ (1 / 6) and IsSimpleFraction(pSizeDown) then
set lst to {}
set lngDeltaY to 0
repeat with i from 1 to (1 / pSizeDown) as integer
set end of lst to {lngX, lngDeltaY, lngwidth, lngHeight}
set lngDeltaY to lngDeltaY + lngHeight
end repeat
lst
else
{{lngX, lngY, lngwidth, lngHeight}, {lngX, lngDeltaY, lngwidth, lngScreenDown - lngHeight}}
end if
else
-- LEFT RIGHT
if pSizeAcross = 0.5 then
set {lngAltwidth, lngAltHeight} to {pSizeDown * lngScreenAcross, pSizeAcross * lngScreenDown}
{{lngX, lngY, lngwidth, lngHeight}, {lngDeltaX, lngY, lngScreenAcross - lngwidth, lngHeight}, ¬
{0, 0, lngAltwidth, lngAltHeight}, {0, lngAltHeight, lngAltwidth, lngScreenDown - lngAltHeight}}
else if pSizeAcross ≥ (1 / 6) and IsSimpleFraction(pSizeAcross) then
set lst to {}
set lngDeltaX to 0
repeat with i from 1 to (1 / pSizeAcross) as integer
set end of lst to {lngDeltaX, lngY, lngwidth, lngHeight}
set lngDeltaX to lngDeltaX + lngwidth
end repeat
lst
else
{{lngX, lngY, lngwidth, lngHeight}, {lngDeltaX, lngY, lngScreenAcross - lngwidth, lngHeight}}
end if
end if
else -- does this have at least one corner ? (at {0,0} or (height+y)=1) or ((width+x)=1)
if ({lngX, lngY} = {0, 0}) or AlmostOne(pSizeDown + pPosnDown) or AlmostOne(pSizeAcross + pPosnAcross) then
{{lngX, lngY, lngwidth, lngHeight}, ¬
{lngX, lngDeltaY, lngwidth, lngScreenDown - lngHeight}, ¬
{lngDeltaX, lngDeltaY, lngScreenAcross - lngwidth, lngScreenDown - lngHeight}, ¬
{lngDeltaX, lngY, lngScreenAcross - lngwidth, lngHeight}}
else
{{lngX, lngY, lngwidth, lngHeight}}
end if
end if
end GetTransforms
on AlmostOne(rNumber)
(1 - rNumber) < 0.01
end AlmostOne
on IsSimpleFraction(nNumber)
if nNumber > 0.5 then
return false
else
set n to 1 / nNumber
Abs((n as real) - (n as integer)) < 0.01
end if
end IsSimpleFraction
on Abs(n)
if n < 0 then
-n
else
n
end if
end Abs
--
Last edited by RobTrew; 2011-01-30 at 03:58 AM..
Reason: ver 1.2 Defaults to the primary tile on first call with given parameters
|