Skip to content

Instantly share code, notes, and snippets.

@johnnyt
Created May 5, 2009 18:39
Show Gist options
  • Save johnnyt/107116 to your computer and use it in GitHub Desktop.
Save johnnyt/107116 to your computer and use it in GitHub Desktop.
property apps_to_maximize : {"MacVim", "iTunes", "Dragon's Lair"}
property menubar_height : 22
property fuzzy_limit : 10 -- number of pixels that the top and bottom could be off by
property unmaximized_window_bounds : {200, 100, 1200, 700} -- size that the unmaximized window should be (it will be placed on its current monitor)
property apps_with_wacky_coords : {"Dragon's Lair", "PivotalTracker"}
property apps_without_title_bars : {"Dragon's Lair", "PivotalTracker"}
property apps_bounds_to_secondary_monitor : {"Dragon's Lair"}
-- We now move ALL apps to primary monitor (except what is listed above)
-- property apps_bounds_to_primary_monitor : {"Finder", "Firefox", "MacVim", "Terminal", "iTunes"} -- "Sequel Pro", "Thunderbird", "Script Editor", "TextMate"
property apps_with_double_title_height : {"Finder", "Thunderbird"}
property apps_needed_to_activate_for_windows : {"Mail", "MacVim", "PivotalTracker", "Dragon's Lair"}
global window_is_on_primary_monitor
set window_is_on_primary_monitor to true
global monitor_properties
set monitor_properties to my monitorProperties()
global primary_monitor_bounds
set primary_monitor_bounds to my bounds_from_properties(monitor1 of monitor_properties)
global secondary_monitor_bounds
global multiple_monitors
if monitorCount of monitor_properties is equal to 2 then
set multiple_monitors to true
set secondary_monitor_bounds to my bounds_from_properties(monitor2 of monitor_properties)
else
set multiple_monitors to false
set secondary_monitor_bounds to {0, 0, 0, 0}
end if
global active_apps_with_windows
set active_apps_with_windows to my get_active_apps_with_windows()
--#####################################################################################
--#####################################################################################
--#####################################################################################
-- First move all windows (other than Dragon's Lair) to main monitor
my move_all_windows()
-- Maximize all windows of specified apps
my maximize_specified_apps()
-- Make Dragon's Lair active on all spaces
--tell application "Dragon's Lair" to activate
my remove_spaces_application_binding("com.fluidapp.FluidInstance.Dragon's Lair")
my set_spaces_application_binding_for_application("com.fluidapp.FluidInstance.Dragon's Lair", "All")
--#####################################################################################
--#####################################################################################
--#####################################################################################
on move_all_windows()
set dbg to {}
-- repeat with an in (apps_bounds_to_primary_monitor & apps_bounds_to_secondary_monitor)
repeat with an in my active_apps_with_windows
set app_name to name of application an
tell application an
try
--activate
repeat with x from 1 to (count windows)
set current_window to window x
if visible of current_window is true then
set current_window_needs_to_be_moved to true
set returned_bounds_list to bounds of current_window
if (count returned_bounds_list) = 4 then
set current_bounds to returned_bounds_list
else
set current_bounds to item 1 of returned_bounds_list
end if
set current_bounds to bounds of current_window
set new_bounds to current_bounds
if (app_name is in apps_bounds_to_secondary_monitor) then
set new_bounds to my convert_bounds(secondary_monitor_bounds)
else
set possible_new_bounds to my relative_bounds_on_current_monitor(current_bounds)
if my bounds_match(current_bounds, possible_new_bounds) then
set current_window_needs_to_be_moved to false
else
set new_bounds to possible_new_bounds
end if
end if
if current_window_needs_to_be_moved then
set bounds of current_window to new_bounds
end if
end if
end repeat
on error error_string
-- return {error_string, app_name, returned_bounds_list}
end try
end tell
end repeat
return dbg
end move_all_windows
on maximize_specified_apps()
repeat with app_name in apps_to_maximize
if active_apps_with_windows contains app_name then
tell application app_name
-- activate
repeat with x from 1 to (count windows)
-- try
my toggle_window(app_name, window x, true)
-- on error error_string
-- end try
end repeat
end tell
end if
end repeat
end maximize_specified_apps
on get_active_apps_with_windows()
set app_names to {}
tell application "System Events" to set all_procs to application processes
repeat with i from 1 to count all_procs
tell application "System Events" to tell process i to set app_name to short name
if apps_needed_to_activate_for_windows contains app_name then
activate application app_name
delay 0.5
end if
tell application "System Events" to tell process i
if (count windows) > 0 then
set end of app_names to app_name
end if
end tell
end repeat
--return {"Safari", "PivotalTracker"}
return app_names
end get_active_apps_with_windows
on bounds_match(current_bounds, possible_new_bounds)
set lefts_match to (item 1 of current_bounds) = (item 1 of possible_new_bounds)
set tops_match to ((item 2 of current_bounds) = (item 2 of possible_new_bounds)) or ((item 2 of current_bounds) = ((item 2 of possible_new_bounds) + menubar_height)) or ((item 2 of current_bounds) = ((item 2 of possible_new_bounds) - menubar_height))
return lefts_match and tops_match
end bounds_match
on process_for_app_name(app_name)
tell application "System Events" to get the first process whose short name is app_name
end process_for_app_name
on convert_bounds(old_bounds)
set pm_height to (item 4 of primary_monitor_bounds)
set sm_height to (item 4 of secondary_monitor_bounds)
set screen_height to item 4 of primary_monitor_bounds
set window_height to (item 4 of old_bounds) - (item 2 of old_bounds)
if multiple_monitors then
set new_top to -(item 2 of old_bounds) + (pm_height - sm_height)
set new_bottom to -(item 4 of old_bounds) + (pm_height + sm_height)
else
set new_bottom to screen_height - (item 4 of old_bounds) + window_height
set new_top to screen_height - (item 2 of old_bounds) - window_height
end if
return {item 1 of old_bounds, new_top, item 3 of old_bounds, new_bottom}
end convert_bounds
on monitorProperties()
--system_profiler parsing
set SPDisplaysData to (do shell script "system_profiler SPDisplaysDataType")
set text item delimiters to "Displays:"
set SPDisplaysData to text item 3 of SPDisplaysData
set text item delimiters to (word 1 of SPDisplaysData)
copy text item 1 of SPDisplaysData to text item delimiters
set SPDisplaysData to text items 2 thru -1 of SPDisplaysData
set text item delimiters to ""
repeat with i from 2 to length of SPDisplaysData
if character 1 of item i of SPDisplaysData is not " " then
set display1 to items 2 thru (i - 1) of SPDisplaysData
set display2 to items (i + 1) thru -1 of SPDisplaysData
exit repeat
end if
end repeat
--END OF system_profiler parsing
set layout_values to (do shell script "displays_layout")
set {displayCount, output} to {1, {}}
repeat with curList in {display1, display2}
set mainDisplay to false
-- We have already parsed the primary monitor and are looking at the non-existent secondary monitor
if item 1 of curList contains "Status: No display connected" then
set monitorCount to 1
set display2 to {width:missing value, height:missing value, OriginX:missing value, OriginY:missing value}
else
repeat with i from 1 to length of curList
set curItem to item i of curList
if curItem contains "Resolution:" then
set {y, x} to {word 2 of curItem, word 4 of curItem}
end if
if curItem contains "Main Display: Yes" then
set mainDisplay to true
end if
end repeat
if mainDisplay then
set display1 to {width:y as integer, height:x as integer, OriginX:(word 4 of layout_values) as integer, OriginY:(word 5 of layout_values) as integer}
else
set monitorCount to 2
set display2 to {width:y as integer, height:x as integer, OriginX:(word 9 of layout_values) as integer, OriginY:(word 10 of layout_values) as integer}
end if
end if
end repeat
return {monitor1:display1, monitor2:display2, monitorCount:monitorCount}
end monitorProperties
on relative_bounds_on_current_monitor(current_bounds)
set mon_bounds to my get_monitor_bounds(current_bounds)
set rel_bounds to {0, 0, 0, 0}
repeat with i from 1 to 4
set item i of rel_bounds to (item i of current_bounds) - (item (((i - 1) mod 2) + 1) of mon_bounds)
end repeat
return rel_bounds
end relative_bounds_on_current_monitor
--#####################################################################################
--#####################################################################################
--#####################################################################################
--#####################################################################################
--#####################################################################################
-- For maximizing / minimizing windows
--#####################################################################################
--#####################################################################################
on toggle_window(app_name, win_to_toggle, always_maximize)
try
if apps_with_wacky_coords contains app_name then
set window_bounds to my convert_bounds(bounds of win_to_toggle)
else
set window_bounds to bounds of win_to_toggle
end if
set monitor_bounds to my get_monitor_bounds(window_bounds)
-- Already maximized
if fuzzy_match_bounds(window_bounds, monitor_bounds, (apps_with_double_title_height contains app_name)) then
if not always_maximize then unmaximize_window(win_to_toggle, monitor_bounds)
else
if (apps_without_title_bars contains app_name) and window_is_on_primary_monitor then
set item 2 of monitor_bounds to ((item 2 of monitor_bounds) - menubar_height)
set item 4 of monitor_bounds to ((item 4 of monitor_bounds) - menubar_height)
end if
set bounds of win_to_toggle to monitor_bounds
-- Some apps (e.g. MacVim) have a grid for bounds, and may need to be adjusted up a little
set set_bounds to bounds of win_to_toggle
if (item 2 of set_bounds) is not equal to (item 2 of monitor_bounds) then
set item 4 of monitor_bounds to ((item 4 of monitor_bounds) - 4)
set bounds of win_to_toggle to monitor_bounds
end if
end if
on error
set invalidNum to true
end try
end toggle_window
on unmaximize_window(win_to_change, monitor_bounds)
set new_bounds to {0, 0, 0, 0}
repeat with i from 1 to 4
set item i of new_bounds to (item (((i - 1) mod 2) + 1) of monitor_bounds) + (item i of unmaximized_window_bounds)
end repeat
set bounds of win_to_change to new_bounds
end unmaximize_window
on fuzzy_match_bounds(win_bounds, mon_bounds, window_has_double_title_height)
set win_top to item 2 of win_bounds
if window_has_double_title_height then set win_top to win_top - menubar_height
set left_matches to (item 1 of win_bounds is equal to item 1 of mon_bounds)
set right_matches to (is_within(item 3 of win_bounds, {((item 3 of mon_bounds) - fuzzy_limit), ((item 3 of mon_bounds) + fuzzy_limit)}))
set top_matches to is_within(win_top, {((item 2 of mon_bounds) - fuzzy_limit), ((item 2 of mon_bounds) + fuzzy_limit)})
--set bottom_matches to is_within(item 4 of win_bounds, {((item 4 of mon_bounds) - fuzzy_limit), ((item 4 of mon_bounds) + fuzzy_limit)})
return left_matches and right_matches and top_matches -- and bottom_matches
end fuzzy_match_bounds
on get_monitor_bounds(window_bounds)
set is_in_primary_width to my is_within(item 1 of window_bounds, {item 1 of primary_monitor_bounds, item 3 of primary_monitor_bounds})
set is_in_primary_height to my is_within(item 2 of window_bounds, {item 2 of primary_monitor_bounds, item 4 of primary_monitor_bounds})
-- Is on primary monitor
if (not multiple_monitors) or (is_in_primary_width and is_in_primary_height) then
return {item 1 of primary_monitor_bounds, menubar_height, item 3 of primary_monitor_bounds, item 4 of primary_monitor_bounds}
else
set window_is_on_primary_monitor to false
return secondary_monitor_bounds
end if
end get_monitor_bounds
on bounds_from_properties(monitor_props)
set _left to OriginX of monitor_props
set _top to OriginY of monitor_props
return {_left, _top, (_left + (width of monitor_props)), (_top + (height of monitor_props))}
end bounds_from_properties
on is_within(var, range)
return (var ≥ item 1 of range and var < item 2 of range)
end is_within
--#####################################################################################
--#####################################################################################
--#####################################################################################
--#####################################################################################
--#####################################################################################
-- For changing spaces settings
--#####################################################################################
--#####################################################################################
on make_lowercase(the_string)
return do shell script "echo " & quoted form of the_string & " | /usr/bin/perl -pe 'use encoding utf8; s/(\\w)/\\L$1/gi'"
end make_lowercase
on list_to_string(l, d)
tell (a reference to my text item delimiters)
set {o, contents} to {contents, d}
set {l, contents} to {"" & l, o}
end tell
return l as Unicode text
end list_to_string
on string_to_list(s, d)
tell (a reference to my text item delimiters)
set {o, contents} to {contents, d}
set {s, contents} to {s's text items, o}
end tell
return s
end string_to_list
on set_spaces_application_binding_for_application(application_bundle_id, chosen_space)
set application_bundle_id to my make_lowercase(application_bundle_id)
if chosen_space is in {0, "None"} then
my remove_spaces_application_binding(application_bundle_id)
else
if chosen_space = "All" then set chosen_space to 65544
my set_spaces_application_bindings((run script "{|" & application_bundle_id & "|: " & chosen_space & "}") & (my get_spaces_application_bindings()))
end if
end set_spaces_application_binding_for_application
on remove_spaces_application_binding(application_bundle_id)
set application_bundle_id to my make_lowercase(application_bundle_id)
set app_bindings to my get_spaces_application_bindings()
try
get app_bindings as string
on error error_string
set app_bindings to my string_to_list(text 13 thru -20 of error_string, ", ")
end try
set new_bindings to {}
repeat with i in app_bindings
set i to contents of i
if i does not start with "|" & application_bundle_id & "|:" then set end of new_bindings to i
end repeat
my set_spaces_application_bindings(run script "{" & (my list_to_string(new_bindings, ", ")) & "}")
end remove_spaces_application_binding
on get_spaces_application_bindings()
tell application "System Events" to tell spaces preferences of expose preferences to return (get application bindings)
end get_spaces_application_bindings
on set_spaces_application_bindings(new_bindings)
tell application "System Events" to tell spaces preferences of expose preferences to set application bindings to new_bindings
end set_spaces_application_bindings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment