The
RubyCocoa project makes Ruby an incredibly powerful scripting language in Mac OS X.
As an example, here's a script that I used to rearrange windows when switching between various monitors. Based on the width of the main screen (something which I couldn't find a robust way to query outside of the NSScreen Cocoa API), it applies my preferred size and positioning to specific windows I care about. If you run it with '-q', it instead dumps a structure with those windows' current sizes and positions, for feeding back into the script as configuration.
Enjoy:
#!/usr/bin/env ruby -w
require 'optparse'
require 'osx/cocoa' # http://rubycocoa.sourceforge.net
require 'pp'
options = { :query => false }
OptionParser.new do |opts|
opts.banner = 'Usage: repos.rb [options]'
opts.on( '-q', '--query', 'Query rather than set positioning' ) do |q|
options[:query] = q
end
end.parse!
def first_window_of( s ) %Q{the first window of process "#{s}"} end
WindowsOfInterest = {
:adium_chat => first_window_of('Adium') + ' whose name is not "Contacts"',
:adium_contacts => first_window_of('Adium') + ' whose name is "Contacts"',
:firefox => first_window_of('Firefox') + ' whose name is not "Downloads"',
:ical => first_window_of('iCal'),
:iterm => first_window_of('iTerm'),
:itunes => first_window_of('iTunes'),
:mail => first_window_of('Mail'),
:terminal => first_window_of('Terminal'),
:tweetie => first_window_of('Tweetie') + ' whose name is "Tweetie"',
}
PropertiesOfInterest = [ :position, :size ]
ConfigurationForWidth = {
2560 => {
:adium_chat => { :position => [2058, 1241], :size => [501, 357] },
:adium_contacts => { :position => [2419, 22], :size => [141, 357] },
:firefox => { :position => [632, 223], :size => [1459, 1096] },
:ical => { :position => [3199, 800], :size => [640, 715] },
:iterm => { :position => [0, 740], :size => [786, 860] },
:itunes => { :position => [1080, 22], :size => [1336, 946] },
:mail => { :position => [0, 22], :size => [1079, 717] },
:terminal => { :position => [2560, 800], :size => [641, 795] },
:tweetie => { :position => [2058, 549], :size => [500, 690] },
},
1920 => {
:adium_chat => { :position => [1419, 844], :size => [501, 357] },
:adium_contacts => { :position => [1785, 22], :size => [135, 319] },
:firefox => { :position => [397, 72], :size => [1208, 1034] },
:ical => { :position => [949, 1203], :size => [640, 715] },
:iterm => { :position => [0, 355], :size => [810, 844] },
:itunes => { :position => [494, 22], :size => [1280, 715] },
:mail => { :position => [0, 22], :size => [1079, 717] },
:terminal => { :position => [312, 1202], :size => [641, 723] },
:tweetie => { :position => [1418, 293], :size => [501, 550] },
},
}
def do_apple_script(s)
result = OSX::NSAppleScript.alloc.initWithSource(s).executeAndReturnError(nil)
# Return an array of the values (AppleScript uses 1-based indexing)
(1..result.numberOfItems).map do |i|
result.descriptorAtIndex( i ).int32Value
end
end
main_display_width = Integer( OSX::NSScreen.mainScreen.frame.width )
window_properties = {}
if options[:query]
WindowsOfInterest.each do |key,spec|
window_properties[key] = {}
PropertiesOfInterest.each do |prop|
window_properties[key][prop] = do_apple_script(
%Q{tell application "System Events" to get the #{prop} of #{spec}}
)
end
end
puts "#{main_display_width} =>"
pp window_properties
else
config = ConfigurationForWidth[main_display_width] or
raise "No configuration for main display width #{main_display_width}"
config.each do |window,props|
props.each do |prop,rubyval|
value = '{' + rubyval.join(',') + '}'
do_apple_script(
%Q{tell application "System Events" to set the #{prop} of #{WindowsOfInterest[window]} to #{value}}
)
end
end
system %Q{/Users/ryan/bin/emacsclient -e '(rdj-smartsize-frame-for #{main_display_width}))' > /dev/null}
end