2011年1月31日月曜日

職場のPC更新

職場のパソコンがレンタル期間が過ぎるということで一新された。児童用のパソコン室も、職員室の職員用のも。これについては書きたいことが山ほどあるが自重する。(^^;)

仕事で使うPCが新しくなってたとえばExcelでの作業が楽になるのはうれしいことだけど(今までは起動してまともに動かせるまで10分、出席簿のExcelファイルを開いたら入力できるまで3分、みたいな感じだった)、それでも税金の無駄遣いはまだまだたくさんある。公務員の深い闇。

私たち教育公務員はあまりその闇の中に住んでいないのが救いか。与えられたものをただありがたがるしかないのかなあ。

とりあえずWin7になって、周りはみんな悲鳴を上げている。「使い方がわかんねー」という。私もわからないことが多くて苦労の連続。

あらゆる怒りを全てMSへの不満に変換して(^^;)、明日もがんばろう。

MacRuby・Snatcher・タスクセレクタ

現在起動中のアプリを探して、一覧表示する。で、選択したアプリをアクティブにする機能を考える。

さすがxcatsan師匠、私のためにちゃんとサンプルを作ってくれていた。(^^;)

このページの通り作業してみる。

まずInterfaceBuilderでmenuを一つプロジェクトに追加する。それをとりあえずメインの透明ウィンドウ上のViewにOutletでつないでおく。attr_accessor :my_menu、ですな。

書いたメソッドはmouseDownを含めて4つだけ。

def mouseDown(event)
  task_set
  menu_set
  NSMenu.popUpContextMenu(my_menu,withEvent: event ,forView: self)

 end
 def task_set
  @icons.clear
  ws=NSWorkspace.sharedWorkspace
  ra=ws.launchedApplications
  ra.each do |app|
   icon=ws.iconForFile(app['NSApplicationPath'])
   name=app['NSApplicationName']
   icon.setSize(NSMakeSize(32,32))
   @icons[name]=icon
  
  end
 
 end
 def menu_set
  my_menu.removeAllItems
  if @icons then
   @icons.each do |name,icon|
    app_name=name
    image=icon
    item=NSMenuItem.alloc.init
    item.setTitle(name)
    item.setImage(image)
    item.setTarget(self)
    item.setAction("select:")
    my_menu.addItem(item)
    
   end
   
  end
    
 end
 def select(sender)
  #puts "select"+sender.title.to_s
  ws=NSWorkspace.sharedWorkspace
  ws.launchApplication(sender.title)
 end


task_setで起動中のアプリを取得してHashにアプリ名とアイコンを(NSImageで)収め、menu_setでInterfaceBuilderで作っておいたmenuにアイコンとアプリ名をセット、selectでlaunchApplication。非常に簡単。今日の自由時間、2時間程度でできた。

popupするメニューにはこうなる。


上が16×16、下が32×32。


ただ、launchApplicationだと期待通りの動作をしないことあると予想される。複数のインスタンスを起動できるappとか。現実にFirefoxだと「一つしか起動できません」という警告ダイアログが表示された。

他のアプリを、ただ単にアクティブにするにはどうするか。明日以降の課題。

今日の収穫は、senderの書き方。

item.setAction("select")

と書くと引数なしの「def select」しか許してもらえない。しかし

item.setAction("select:")

なら引数「sender」があってもOK。「select(sender)」と書けるわけ。ふむ、なるほどなあ。こういうのは実際に書いてみて、いろいろ失敗しないとわかんないよな。

それともうひとつ。rubyの書き方がよくわかっていないことがばれちゃうけど、Hashについてかなり調べました。(^^;)

@icons.each do |name,icon|

というふうにkeyとvalueをいっぺんに取り出すのってすごく便利だ。いいぞruby。(^^;)

2011年1月30日日曜日

MacRuby・Snatcher・追加機能

SnatcherというかSpinというか、とにかく自分用ツールの追加機能を考える。

デスクトップにアクセスできないとんでもない仕様なので、その代わりになるような追加機能をつけたい。

現在の妄想は


こんな感じですかねー。マウスクリックでworkspaceの切替はすぐにでもできる。

あとはタスク選択(⌘+TAB、みたいな)とWindowセレクタがあれば便利だよな、と思うけど、果たして実現できるかどうか。

XcodeとInterfaceBuilderを起動していると、デスクトップ上にやたらウィンドウがたくさん開いてしまって、うっとおしいことがある。私の場合はマウスをDockまでもっていって、HyperDockの「アプリケーションで開いているウィンドウ一覧」表示の中から必要なウィンドウを選択することが多い。

これに近い機能をマウスクリックした場所で可能にできないか。ウィンドウのイメージ表示までできなくていいので、メニュー表示のようにしてできないかな。

タスク選択は、やっぱりDockまでマウスを動かすことが多いから、これもマウスクリックした場所でできたら便利かも。

開いているウィンドウの一覧を得る、というのはxcatsan師匠のblogにその手の記事があったのでただいまじっくり読んでいる最中。ただ師匠は「開いているウィンドウを画像として得る」ためかなり高度のAPIを叩いているので、素人はまずはNSWorkspaceあたりから攻めてみることにする。

def mouseDown(event)
  ws=NSWorkspace.sharedWorkspace
  #ws.launchApplication("Finder")
  ra=ws.launchedApplications
  puts ra[0].to_s
 end
とりあえず左クリックで起動中のアプリの一覧を取得して、その1番目をデバッガコンソールに出力してみる。(MacRubyはこれが手軽にできるからいい。ちなみにコメントアウトしてあるのは左クリックでFinder起動、という部分)

で、デバッガコンソールにはこう出力された。

{"NSApplicationPath"=>"/System/Library/CoreServices/Finder.app", "NSWorkspaceApplicationKey"=>#, "NSApplicationBundleIdentifier"=>"com.apple.finder", "NSApplicationProcessSerialNumberLow"=>40970, "NSApplicationProcessIdentifier"=>181, "NSApplicationProcessSerialNumberHigh"=>0, "NSApplicationName"=>"Finder"}

ハッシュで取得できるんだな。起動中のアプリを選んでアクティブにする、というだけならこの程度も十分なような気がする。せめてアイコンくらいは表示できるようにしたいけど、それはNSWorkspaceでもできるのかな。明日以降試してみよう。

MacRuby・環境設定ウィンドウその6

現在作っている自分用「デスクトップでマウスホイールをくるくる回すとSpaces.appのworkspaceが切り替わる」アプリ(長いな(^^;))仮称Snatcher、プロジェクト名はSpin(こっちのほうが自分としてはしっくりくる名前かも)で、環境設定ウィンドウのデフォルト値を読み取って実際の動作に反映させる。

ホイールのイベントを受け取って実際にworkspaceを切り替えるのはViewのお仕事。

def set_defaults
  defaults=NSUserDefaults.standardUserDefaults
  @use_visual=defaults.boolForKey("useVisual")
  @wheel=defaults.integerForKey("wheel")
  @trans=defaults.integerForKey("trans")
  @trans_opt=defaults.integerForKey("opt")
 end

こんなメソッドを書いて、scrollWheelイベントが来るたびに呼び出せば、それだけで所期の目的が完了。しかしこれだと、環境設定が何も変わっていない時でもいちいちデフォルト値を読み込みに行くのでなんだかもったいない。

環境設定が変更された時だけ、このメソッドが呼ばれるようにしたい。というわけでNotificationで実現することにした。

まず applicationDidFinishLaunching に以下の1行を追加。
NSDistributedNotificationCenter.defaultCenter.addObserver(self, selector:"defaultsChanged" ,name:"SpinDefaultChanged" ,object:nil)


これはアプリ終了時には必ず「もうオブザーブしません」と宣言するのが決まりらしい。

def applicationWillTerminate(note)
  NSNotificationCenter.defaultCenter.removeObserver(self, name:"SpinDefaultChanged" ,object:nil)
 end  
これも追加する。

で、あとは実際に環境設定が変更されたらNotificationを投げればいいはず。

PopupButton 関連は自分で変更を保存しているので、そのメソッドの中で
NSDistributedNotificationCenter.defaultCenter.postNotificationName "SpinDefaultChanged",object:nil

とする。しかし自前でやっていない、checkboxとかスライダーとかはどうするか。

def chabge_other(sender)
  NSDistributedNotificationCenter.defaultCenter.postNotificationName "SpinDefaultChanged",object:nil
 end

こんなメソッドを用意して、あとはInterfaceBuilderでactionを結びつけるだけでよかった。簡単だなあ。自動でUserDefaultに保存されるコントロールも、actionで結び付けられたメソッドのsenderになってくれるとわかったよかった。

こう、いかにもさっさと書きました、みたいな感じで書いているけれど、なんせ素人なので右往左往しましたよ。Notificationの書き方で、
selector:"defaultsChanged"

の最後に「:」(コロン)がいらない、とわかるまでにしばらくかかった。selectorってそのまま書けばいいの?って感じで驚いた。先日のToolbarの時は・・・selectorだけ書いて怒られたような気がするんだけど。

ここまでで本当にいちおう、所期の目的を果たした自分用ツールができた。視覚効果もつけてworkspaceを切り替えることができるし、スライダーで設定した「マウス感度」で切替のタイミングもある程度ユーザ設定できるようになった。

ふむ、次は・・・。

2011年1月29日土曜日

Linus Torvalds、ずいぶん・・・

Our exclusive interview with Linus Torvalds

Linusの近影が見られます。ずいぶん・・・胴回りが太くなってますなあ。ビール好きなのかな。

MacRuby・環境設定ウィンドウその5

PopupButtonとController側のアクションを結びつけると、PopupButtonで選択アイテムが変更されたあとにメソッドが呼ばれる。

そのタイミングでselectedItemを保存してしまえばとりあえずOKだろう、ということで試してみる。

@defaults.setInteger(sender.indexOfSelectedItem,forKey:key)

これでいける。@defaultsはawakeFormNibの中で
@defaults=NSUserDefaults.standardUserDefaults

としたもの。あとは保存するときのkeyをactionを送ってくるsenderによって変えてやればいいんだけれど、senderって何よ?状態のためしばらく調べる。

いちいちコントロールに名前つけないでもいいからInterfaceBuilderって楽ね、と思ったけどこのケースだとちょいと困る。どうやって6つあるPopupButtonを見分けばいいんだ。

とりあえず動けばいい、という姿勢でいかざるを得ないので、PopupButtonごとにtagをつけてそれで見分けることにした。そんなんでこういうちょっと情けない感じのメソッドでコントロールの現在値を保存。

def changedPop(sender)
  
  case sender.tag
   when 0
    key="trans"
   when 1
    key="opt"
   when 2
    key="L_butoon_click"
   when 3
    key="L_button_D_click"
   when 4
    key="R_button_click"
   when 5
    key="R_button_D_click"
    
   else key=""
  end
  
  @defaults.setInteger(sender.indexOfSelectedItem,forKey:key) if key!=""
 
 end

なんかVBAみたいだな、と自嘲しつつ。
あとは保存した値をawakeFormNibからこんなメソッドを呼び出すことで読み取る、と。

def set_popup_botton_index
  trans_pop.selectItemAtIndex(@defaults.integerForKey("trans"))
  option_pop.selectItemAtIndex(@defaults.integerForKey("opt"))
  l_click.selectItemAtIndex(@defaults.integerForKey("L_button_click"))
  l_d_click.selectItemAtIndex(@defaults.integerForKey("L_button_D_click"))
  r_click.selectItemAtIndex(@defaults.integerForKey("R_button_click"))
  r_d_click.selectItemAtIndex(@defaults.integerForKey("R_button_D_click"))
 end

無事これでbinding相当の動作ができるので、いいとする。

次はこれをどうやってメインのコントローラーで読み込むか、ですな。
@defaults=NSUserDefaults.standardUserDefaultsって同じようにインスタンス変数作ったほうが、ことあるごとにUserDefaults読み込むより効率的だけど、UserDefaultsが変更された時にどうするか。Notification使うのがいいのかな。WarpはNotificationだったなあ。

MacRuby・環境設定ウィンドウその4

本日はMacRubyのPopup Buttonの不具合に悩まされた。というか、MacRubyのせいらしい、とわかるまでに時間かかりすぎた。基本的にうまくいかないときは「自分が何か悪いことをしている」と考えざるを得ない状態なので、まさかMacRuby側の整備不良とは思わなんだ。

こんな環境設定パネルを作っている。


これはInterfaceBuilderで作成中。実行するとToolbarがついてこうなる。


まだデザインをいじる可能性があるのでTabは表示したまま。ToolbarをつけずにTabのままでもいい気がする。(^^;)

で、コントロールの現在の状態をUserDefaultsを通して保存したり読み込んだり、はxcatsan師匠によればInterfaceBuilderのインスペクタでbindingを使えばいい、ということなので試してみる。

チェックボックスやTabViewの選択Tab、スライダの現在値などは全く問題なく処理できる。しかし6個もつけたPopup Button(2番目のTabにあと4個)が全く保存できていない。


bindingできるContentもContent Valuesも(この2つにbindingするとコントロールがまともに動かなくなることがわかった)、Selected IndexもSelected ValuesもSelected TagもSelected Objectも全部試してみたけど全部動いていない。

いくらやってもだめなので、試しに普通のObjCのプロジェクトを作って試してみたら、そっちはInterfaceBuilderだけの作業で見通しどおりに動く。がっくり。

MacRubyをnightlyに変えてみたりもしたけど、全然だめでした。

ここで考えたのはObjCで書きなおすことと、泥臭くMacRubyで処理しちゃうこと。乗りかかった船(という表現をここ最近何回くらい使ったかなあ)なのでMacRubyでやってしまうことにする。

昼過ぎから夕食後まで、ずっとこの問題をいじっていて、やっと夜になってこう決めた、と。

2011年1月28日金曜日

NetBeansでRailsサポート終了

NetBeans Community News

Netbeans7.0でrailsサポートがなくなる、と。
うーん、最近はもうお世話にならなくてもよくなったので、そんなもんかいな、という感想しかない。Winでrails使うならかなりいいIDEでしたね。

でもUbuntuやOSXなら需要が少ないかも。OSXでは海外ならTextMateみたいだし。(国内はどーなっているんだろう)

このNewsに接して、んじゃあ削除しておくか、とApplicationsをみたらすでに影も形もなかった。(^^;)いつ削除したっけな。

MacRuby・環境設定ウィンドウその3

Toolbarができたところで、TabViewのindexとむすびつけて、Toolbarのクリックでタブを切り替えるようにする。xcatsan師匠の解説の通りにやっている。

で、1枚目のTabにスライダーをpopupを置いてみる。


各コントロールの値をUserDefaultで保存、読み込み、とやる予定だったけれど、「視覚効果を有効にする」チェックボックスがチェックされたら2つのPopUpを有効化する方法を調べているうちに時間切れとなった。

チェックボックス、PopUpをOutletでコントローラーに結びつけて、コードを書いてどうにかする、というのはなんとかできそうだけれど、この程度のことをはInterfaceBuilderだけで実現できそうだよなあ、と思って調べたければまだわからない。

とりあえずスライダーの値を読み取ってホイールの動作を変更できるようにしてみるか・・・。InterfaceBuilderとかXcodeとか、基本的なことがわかっていないからまだつらい。

2011年1月27日木曜日

MacRubyの本、もう1冊

やっぱり電子書籍で、まだ出版されていない超初期版。


MacRuby in Action

今年の夏に完成予定、全13章のうち現在完成しているのが4章分。EARLY ACCESS+完成版の電子書籍、のお値段が34.99ドル。google様によれば

34.99米ドル = 2 897.24269 円

だそうです。先日買ったrspec本(全然読んでないや)の請求がきて、2000円くらいでした。(24ドル)

円高のうちに買っておくかなあ。手元において検索するのに便利かも。
ちなみにこの本の章立てはこうなっている。

Part I - Starting off with MacRuby
1 Introducing MacRuby - FREE
2 Exploring MacRuby using Macirb and Apple's development tools - AVAILABLE

Part II - Take it for a spin
3 Using Interface Builder to create more advanced MacRuby Cocoa applications - AVAILABLE

Part III - MacRuby and Cocoa
4 Using delegates - AVAILABLE
5 Notifications
6 Key value coding and observing
7 Core data basics
8 Core animation basics
9 Building UI with HotCocoa

Part IV - MacRuby Extras
10 Scripting the Mac with MacRuby
11 Testing MacRuby applications with RSpec
12 Mac App Store
13 MacRuby and iOS development
AVAILABLEが現在読める部分ですな。目次を見るとすごく魅力的なんだなあ。11 Testing MacRuby applications with RSpecとかね。

MacRuby・Snatcher(^^;)・大弱点

自分用のツールだから不都合や不具合はなんでもあり、なんだけど、いささか「これはなあ」と思わされた弱点発見。

透明な巨大ウィンドウがデスクトップ全体を覆い尽くしているわけで、事実上デスクトップが隠されている状態。(透明ウィンドウだからデスクトップそのものはみえるけど)


だからデスクトップ上のアイコンは選択できない。

orz。どんなOSでもデスクトップ上にごちゃごちゃアイコンを置くのはきらいだから、iMacでもデスクトップ上にあるのはMacintosh HDのアイコンだけなんだけど、SimpleCapでキャプチャした画像だけはどんどんデスクトップにためている。blogにアップしたら削除しているわけ。最近はキャプチャした画像すなわちblogのネタ帳、という感じなので、これにワンクリックでアクセス出来ないのは痛い。

これを解消するには、ざっと妄想すれば全部のイベントを取得して、マウスホイールが回ったのを感知したらその場所を調べて、どのウィンドウ上でもない時だけSpacesを切り替える・・・うー、頭がいたい。

今のところそんなtoolは自分の手に負えそうにないので、現在の仕様であきらめるしかないな。

現実的に考えるなら、デスクトップ上の特定の領域に透明ウィンドウを作って、そこの上だけでマウスホイールのイベントを拾う、というのがいいでしょうね。場所を決める時だけウィンドウを半透明にして、とやればいいのでしょう。私の場合はたいていデスクトップの右を空けていることが多いので、そこに縦長の透明ウィンドウを作ればいい。

ふむ、そういうのも作ってみよう、とりあえず今のSnatcherを作ってから。

MacRuby・環境設定ウィンドウその2・Toolbar

引き続き環境設定パネルにToolbarをつける話。しかしよくよく考えてみれば、わざわざToolbarつけてTabView切り替えるほど環境設定する項目がないのね。(^^;)TabViewだけでもいいわけだし。

しかし乗りかかった船、いちおうできるところまでやってみないと気持ちが悪い。

そこでxcatsan師匠ページにあるとおり作業してみる。

結果がこれ。


無事、Toolbarをつけることができた。MacRuby版のソースはこうなる。

class PreferenceController < NSObject
attr_accessor :win
 def awakeFromNib
  toolbar=NSToolbar.alloc.initWithIdentifier("TOOLBAR")
  toolbar.setDelegate(self)
  win.setToolbar(toolbar)  
 end
 def toolbarDefaultItemIdentifiers(toolbar)
  ["Hello","Hi!"]
 end
 def toolbarAllowedItemIdentifiers(toolbar)
  ["Hello","Hi!"]
 end
 def toolbar(toolbar,itemForItemIdentifier:itemIdentifier,willBeInsertedIntoToolbar:flag)
 
  toolbarItem=NSToolbarItem.alloc.initWithItemIdentifier(itemIdentifier)
 
  if itemIdentifier.isEqual("Hello")
   toolbarItem.setLabel("Hello")
   toolbarItem.setImage(NSImage.imageNamed(NSImageNamePreferencesGeneral))
   toolbarItem.setTarget(self)
   toolbarItem.setAction(NSSelectorFromString("hello:"))
  elsif itemIdentifier.isEqual("Hi!")
   toolbarItem.setLabel("Hi!")
   toolbarItem.setImage(NSImage.imageNamed(NSImageNameAdvanced))
   toolbarItem.setTarget(self)
   toolbarItem.setAction(NSSelectorFromString("hi:"))

  else
   toolbarItem=nil
  end
  toolbarItem
 end
 def toolbarSelectableItemIdentifiers(toolbar)
  ["Hello","Hi!"]
 end
 def hello(sender)
  puts "hello"
 end
 def hi(sender)
  puts "hi"
 end
 def open_window(sender)
 
  win.makeKeyAndOrderFront(self)
 end

end

ObjCのコードをMacRubyに置き換えていくのもかなり慣れてはきたけれど、例えば引数のラベルを間違えたりしてまだまだ時間がかかる。 今回も
toolbar(toolbar,itemForItemIdentifier:itemIdentifier,willBeInsertedIntoToolbar:flag)
を書くときに、「itemForItemIdentifier」を「itemForIdentifier」とtypoしてそれをなかなか発見できず、「デリゲートに必要なメソッドが足りない」と言われて頭を抱えた。 もう一つ引っかかったのがObjCで言うところの
[toolbarItem setAction:@selector(hello:)];
この「@selector」ってどうすんの?と悩んだ。幸い
ダイナミックObjective-C 3 Cocoa実現の肝 - クラスとそのメソッドの調査方法をチェック

に「@selector」は「NSSelectorFromString」と同じ、と書かれていて、なんとか書き換えることができた。いやまったく、GoogleというかWebがなけりゃプログラミングできないですね。

さて、次はいよいよオプション選択かな。

2011年1月26日水曜日

MacRuby・環境設定ウィンドウその1

xcatsan師匠のサイトを読む。

環境設定ウィンドウを表示して、いろいろカスタマイズできるようにしたい。マウスホイールがどのくらい回転したらworkspaceを切り替えるか、とか。

新しいNSWindowのウィンドウを1枚作って、さらにそのコントローラのpreferenceControllerも作ってnibに収める、と。

outletをつないで、メニューの「preference」でウィンドウを表示、というところまでは簡単。で、toolbarをつける段階でxcatsan師匠はawakeFormNibの中でtoolbarクラスを動的に作っているんだけど、InterfaceBuilder3.2では10.6オンリーでNSToolbarが部品としてちゃんと用意されているのでそっちを使ってみることにする。

しかし、どうやったらtoolbarItemに画像を設定できるかのよくわからない。(^^;)InterfaceBuilderのインスペクタにはImage Nameってcomboboxがあって、そこに画像のファイル名を入れればいいとおもうのだけれど、なぜかうまくいかない。orz

ついでにNSImageNamePreferencesGeneralってーシステムに用意された画像を使いたいとも考えてるのだけど、どこでsetImageすればいいのやら。

toolbarだけInterfaceBuilderでウィンドウにくっつけて、後は動的にtoolbarItemを生成させるべきなのか・・・。うーん、平日はなかなか前に進まないなあ。今日なんかはグラウンドでスキーをしているから疲れ果てている感じ。

2011年1月25日火曜日

MacRubyが楽しい理由

正直なところ、わざわざMacRubyに書き換えないで、ObjCで書く勉強をしたほうがいいのでは?と思うこともある。

(仮称)Snatcherも、もし公開する、ということを考えるならObjCで最初から書いていると思う。

でもMacRubyは「cocoaフレームワークを簡単に使えるRuby」なわけで、使っていて非常に楽しい。楽だとも思う。

いわゆるprintfデバッグにしても puts deltaY.to_sですみますからねー。ヘッダファイルがいらないのもなんか楽。

もう少しrubyをよく知っていたらもっともっと楽しんだろうなあ。そうか、もともとrubyの勉強のために始めたんだ、MacRuby。(^^;)

なんかこのごろ、すごく生活が充実している。家に帰って、MacRuby触るぞ、と考えるとわくわくしている自分を発見している。楽しいなあ。

MacRuby・Snatcher(^^;)

というわけで仮称Snatcher、初期段階の一区切り。デスクトップでマウスホイールをころころするとSpaces.appのworkspaceを切り替えられるようになった。快適。

イベント処理を含めたCustomViewのコードはたったこれだけ。

class CustomView < NSView
 def awakeFromNib
  super
  @my_space=Spaces.new
  @max_spaces=get_max
  @tmp_space_no=0
  
 end
 def get_max
  CFPreferencesAppSynchronize("com.apple.dock")
  row=CFPreferencesGetAppIntegerValue("workspaces-rows", "com.apple.dock", nil)
  col=CFPreferencesGetAppIntegerValue("workspaces-cols", "com.apple.dock", nil);
  row * col
 end
 def set_space_no
  number=Pointer.new('i')
  number[0]=@my_space.spaceNumber
  number[0]
 end
 def move(muki)
  
  @tmp_space_no=set_space_no
  if muki=="left" then
   @tmp_space_no -= 1
   @tmp_space_no=@max_spaces if @tmp_space_no  == 0
  else
   @tmp_space_no += 1
   @tmp_space_no=1 if @tmp_space_no  == @max_spaces+1
  end
  @my_space.postNotification(@tmp_space_no-1)
  
 end
 def scrollWheel(event)
  if event.deltaY > 0 then
   move("left")
  else
   move("right")
  end
 end
 
 def mouseDown(event)
  ws=NSWorkspace.sharedWorkspace
  ws.launchApplication("Finder")
 
 end
 
end
SpacesっていうObjCで書いている(というかコピーしただけだけど)クラスがあって、そちらでCoreGraphicsの非公開APIを使う処理をしている。ただしそっちも素人が理解できる範囲なのでたいしたことをやっているわけではない。

今のところ使っている非公開APIはSpaces.appが現在表示しているworkspaceの番号を取得するCGSGetWorkspaceだけ。workspaceの切替そのものはSwitchSpacesNotificationをNSDistributedNotificationCenterに投げて実現している。つまりSpaces.appを使っているわけ。そのため動作が非常に安定している。

これをCGSSetWorkspaceを使って自前の切替にすると・・・Spaces.appとの兼ね合いで若干不審な点がある。要調査ということで。

ただし、視覚効果が全くない状態で自前の切替にするとすこぶる速い。一瞬で替わる。これも魅力的。

現在、使っていて感じる問題点は、

1,マウスホイールがちょっとでも動くとworkspaceが切り替わるので、その辺の感度調整が必要
2,視覚効果なしで自前で切り替えると何番目のworkspaceか一瞬迷う

その辺をどうにかするのを兼ねて、環境設定(プリファレンス)作りに今後挑戦する予定。
Viewのコーディング(って言うほどのものでもないけど)がすぐ終わったので、あとはxcatsan師匠のblogを読んで環境設定パネル作りの研究をした。いやー、しかし宝の山だな、xcatsan師匠のblogは。

2011年1月24日月曜日

MacRuby・Spaces.appその7・発想の転換あるいは素人の悪あがき

昨日は大嘘を書いてしまった。orz

setIgnoresMouseEvents(false)としている限り、 自分のアプリをどのレベルで起動しようが

デスクトップをクリックしてもFinderがアクティブにならない

ことが判明。昨日は希望的観測の誤解でFinderが起動したと思い込んだんですな。もちろん右クリックも効きません。

ま、自分だけが使う前提のアプリなんでそれでもいいわけですが、悔しいのでいろいろ調べる。しかしsetIgnoresMouseEvents(false)は白黒どちらかの強烈なメソッドらしく、抜け道はないようです。そんなんで今日の自由時間は終了。

ただ、どーせデスクトップのクリックを奪ってしまうなら、タスクスイッチャーとかランチャーを実現すればいいんじゃない、と思いつく。マウスホイールでSpacesを切り替え、クリックでランチャ、タスクスイッチャー。おお、なかなかいいじゃない、マウス派の自分には。実際Findeに切り替えてもあんまりいいことはないわけだし。

というわけで当面はマウスホイールでのSpaces切替を実現するための努力をする。その後タスクスイッチャーとかの勉強をする、ことに決定。なんか前向きだな>自分。

アプリの名前は唐突にSNATCHERでどうだ、と思いついた。当然映画「ボディスナッチャー」からの連想です。デスクトップスナッチャー・・・。

2011年1月23日日曜日

Gimpインストール

OSX用のGimpをインストールした。注意点としては
XQuartz

を先にインストールしなければいけないことか。簡単に言って新しいXserver。GIMPそのものは

GIMP on OS X

からdmgをダウンロードできるわけですが、ダウンロードする前にサイトのNewを読んでびっくり。
上記サイトより全文引用。

Hi folks,


since more than one year I'm offering GIMP packages for Mac OS X. All of the work is done by only one person, by me. This is okay as long as there's only few to do and bugs are more or less easily to squeeze out.


But now there's one major bug in GTK+, which will prevent GIMP 2.8 to work properly with tablets. This bug is in all GTK+ version 2.17 or higher. Since GIMP 2.8 depends on at least GTK+ 2.18, GIMP 2.8 will lack tablet support.


So, if you are a developer, capable of GTK+, GLIB2, GIMP, MacPorts and bash scripting, it would be nice if you'll join this project and help to make GIMP 2.8 work properly with graphic tablets on Macs.


Without your help, it's likely that GIMP 2.8 won't run on Mac OS X. Don't let GIMP on OS X die !


So, join the project, fix the bug and help offering one of the best image manipulating programs to Mac users: GIMP.


このままだとGIMP on OS Xは使えなくなる・・・と。なにかお手伝いできたら、とは思うけれど、でもタブレットは持ってないしなあ。残念。このアーティクルは昨年の11月1日付け。うーん、どうなるのやら。

MacRuby・Spaces.appその6・デスクトップいっぱいの透明ウィンドウを作る

MacRubyを使用して自分で使いたいアプリを作っている。目的はただひとつ、

デスクトップでマウスホイールをころころしてSpacesのworkspaceを切り替える

こと。

これまでに
1,Spacesの切替を自作アプリの中から行う
2,workspaceが切り替わったことをSpaces.appに通知する

ところまでやってみた。初心者にしては上等じゃねえか、と自分で言ってみる。残りは

3,デスクトップのマウスイベントを取得する
4,ホイールの回転でworkspaceを切り替える

そこで本日は3、のデスクトップのマウスイベント。脳内方針としては

a,透明なデスクトップ大のウィンドウを非アクティブで表示する
b,そいつでマウスイベントをもらう

てな感じ。当然、ステータスバーにメニューを表示するアプリとなる。とは言え、ばかでかいウィンドウを1枚持っているだけのアプリなので、「常駐もの」で連想するような「システムに負荷をかける」ことにはならないはず。

本日はステータスバーにメニューを表示させ、透明な巨大ウィンドウ(iMac27inchなもんで)を作成するところまで進んだ。

「cocoa 透明ウィンドウ」でググルと

Cocoaはやっぱり! 出張版 3. ウィンドウの形状変更と透明化

このサイトが一番初めに出てくるのでとりあえずここに書いてある通りにやってみる。デベロッパードキュメントのサンプルの解説とリファクタリング、という感じで大変わかりやすいけれど、やはり情報が古いのは否めない。(2002年作成のページ)

一般的に透明ウィンドウの作成はNSWindowクラスのサブクラスを作ってinitWithContentRectでウィンドウのスタイルマスクを指定する、となっているけれど、The Invisible Designerさんのこのアーティクルによると、OSX10.6以降は

[window setStyleMask:0];

という便利なメソッドができたということで、サブクラスをつくらずControllerから簡単に透明化ができる。
def applicationDidFinishLaunching(note)
#ウィンドウの透明化
 my_window.setStyleMask(0 & NSNonactivatingPanelMask)
 my_window.setAlphaValue(1.0)
 my_window.setOpaque(false)
 my_window.setBackgroundColor(NSColor.clearColor)
 
#フルスクリーンにする
 main_screen = NSScreen.mainScreen
 fullscreen_frame =NSRect.new(main_screen.frame.origin,main_screen.frame.size)
 my_window.setFrame(fullscreen_frame,display:false)
        
#マウスイベントは拾えるようにする
 my_window.setIgnoresMouseEvents(false) 
        
#全部のworkspaceで表示する
 my_window.setCanBeVisibleOnAllSpaces(true)
        
#ここで初めてウィンドウを表示する 
 my_window.orderBack(self)

#これ以降はステータスバー関係
 status_bar = NSStatusBar.systemStatusBar
 status_item = status_bar.statusItemWithLength(NSVariableStatusItemLength)
 
 status_item.setMenu sbMenu
 status_item.setTitle "Spin"
 status_item.setToolTip "Spin"
 
end
たったこれだけ書くのに、今日の丸1日かかりましたよ。(^^;)楽しんだけど。InterfaceBuilderでWindowのVisible at Launchオプションを外して、あれこれ準備が整ってからorderBack(self)とする方法は前記The Invisible Designerさんのこのアーティクルによる。(それまでは黒い四角が表示されて首をひねりまくった)

しかし実はこのまま実行すると、「透明なウィンドウがデスクトップを覆い尽くして他のアプリが選択できない」状態となる。setIgnoresMouseEvents(false) としているため。でもこれを書かないとマウスイベントが拾えない。info.plistにBGOnlyを記入してもだめ。

夕方からずーっとこれに悩んで、解決したのがもう寝酒を飲もうかという9時でございました。素人はねー。

結局、いつものxcatsan師匠のこのページで問題解決のヒントをもらった。

画面キャプチャその1 - 画面全体を黒くする

解説されているソースの中に、

[_fullscreen_window setLevel:NSScreenSaverWindowLevel + 1];
とあって、ははあ、ウィンドウの表示にもレベルってのがあるのか、と知る。で、さっそく「my_window.setlevel(NSScreenSaverWindowLevel + 1)」と書いて実行してみてびっくり。後知恵で考えれば、これはほとんど一番上のレベルに表示しているんですね。その辺のところは

ザリガニが見ていた...。 マウスイベントを処理するCocoaアプリケーションにしてみる

を読んでやっと気づいた。じゃあ、普通のウィンドウより下のレベルに表示したらいいのかな、ということでデベロッパドキュメントを調べてみたら、こんな記述があった。

enum _CGCommonWindowLevelKey {
   kCGBaseWindowLevelKey = 0,
   kCGMinimumWindowLevelKey,
   kCGDesktopWindowLevelKey,
   kCGBackstopMenuLevelKey,
   kCGNormalWindowLevelKey,
   kCGFloatingWindowLevelKey,
   kCGTornOffMenuWindowLevelKey,
   kCGDockWindowLevelKey,
   kCGMainMenuWindowLevelKey,
   kCGStatusWindowLevelKey,
   kCGModalPanelWindowLevelKey,
   kCGPopUpMenuWindowLevelKey,
   kCGDraggingWindowLevelKey,
   kCGScreenSaverWindowLevelKey,
   kCGMaximumWindowLevelKey,
   kCGOverlayWindowLevelKey,
   kCGHelpWindowLevelKey,
   kCGUtilityWindowLevelKey,
   kCGDesktopIconWindowLevelKey,
   kCGCursorWindowLevelKey,
   kCGNumberOfWindowLevelKeys
};

NSNormalWindowLevelはkCGNormalWindowLevelなので、-2くらいしても大丈夫そう。というわけで試しに

my_window.setLevel(NSNormalWindowLevel - 1)
としたところ、考えていたとおりの動きとなった。つまり
1,自アプリ起動後に直前までアクティブだったウィンドウがアクティブになる
2,どのアプリをクリックしてもすぐアクティになる
3,デスクトップをクリックするとFinderがアクティブになる
4,デスクトップでマウスホイールを回すと、自アプリがそのイベントを受取る

よし、これであとはViewにSpacesを切り替えるメソッドを書くだけだ。
workspaceが切り替わった、というNotificationをスローするだけでworkspaceを切り替えることができる、ということが、昨日の一連の実験で判明したのでまずはその辺りから。視覚効果付きのworkspace切替は、Prefarenceを付けてからの実装となりそう。

2011年1月22日土曜日

MacRuby・Viewでマウスのイベントを得る

次は計画通り今度はマウスイベントを拾ってみる。

ViewをカスタムViewにしてそこに「mouseDown」とかと書くのだ、とわかるまでしばらくかかる。他の方法もあるのかな。

cocoaフレームワークにはいろんなContorollerがあって、ViewContorollerやらViewそのものやら、どこに何を書くべきなのかまだ把握できていない。勉強することが多いなあ。

まず、とにかくだだっとやってみたいことをとにかく書いてみるのも大切かな、と考えつつ
def scrollWheel(event)
  puts " Wheel!"
  puts "deltaX :"+event.deltaX.to_s
  puts "deltaY :"+event.deltaY.to_s
  puts "deltaZ :"+event.deltaZ.to_s
 end

と書いたら自分のWindowの上のマウスホイールを簡単に捉えることができた。ほうほう、deltaYがホイールの前後の動きを表しているのか。ってーことは、deltaXは横の動き?じゃあZは何?

ま、おいおい研究していくことにする。近い将来の自分のためにやったことを順番に書くと、
1,新しいファイルを作って「class MyEventView < NSView」というclassを書く。
2,InterfaceBuilderで今までNSViewだったviewのclassをMyEventViewにする。

あとはViewのclassにあれこれ追加して実験していけばいい、と。

・・・でもこのViewからどうやってMyControllerに書いてあるメソッドを呼び出せばいいんだろうorz。

MyControllerはNSWindowControllerだから、ウィンドウを読み込んで表示する役割だけさせて、Eventの応対はこの際Viewに詰め込んでいいんだろうか。

というわけで、新しいプロジェクトを作って実験してみることにする。どーせメインウィンドウを透明にしてデスクトップいっぱいに広げて、という方法を試してみないといけなかったし。

MacRuby・Spacesその5・Notification

本日はNotificationの勉強をする。
詳解 Objective-C 2.0 改訂版
荻原 剛志
ソフトバンククリエイティブ
売り上げランキング: 3701

この本でおおまかに勉強。さらにWeb上のあちこちをさまよい、Postするだけなら簡単そう、ということがわかって最終的にはいつもの

MacRuby: The Definitive Guide

でMacRubyでのお作法を確かめる。この本によると、
center = NSNotificationCenter.defaultCenter
notification_handler = NotificationHandler.new

center.addObserver( notification_handler,
                    selector: "tea_time:",
                    name: 'tea_time_reminder',
                    object: nil )

center.postNotificationName("tea_time_reminder", object:self)

って感じでいいらしい。そこで、Spaces.appにworkspaceが切り替えられたことを通知するのに、

def post_notification(space_no)
  
  center = NSNotificationCenter.defaultCenter
  center.postNotificationName("com.apple.switchSpaces",object:space_no.to_s)
  
 end
と書いてみたが、これが全然働いていない。なぜだろー。"com.apple.switchSpaces"って文字列、NSStringのポインタが必要みたいなんだけど、あれこれやってみたかぎりどうもうまくいかない。

試しにCoreGraphics関連のメソッドを書いているObjCのclassで、
NSString *SwitchSpacesNotification = @"com.apple.switchSpaces";
- (void)postNotification:(NSInteger)object{
 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:SwitchSpacesNotification object:[NSString stringWithFormat:@"%d", object]];
}

と書いたらこれはちゃんと働く。なんか悔しい。
ObjCのclassのメソッドを呼び出せ、無事workspaceの切替を通知できているみたいなのでいちおうこれでよし、とするけど、どうやればMacRubyからSpace関連の通知ができるのか、もう少し研究だなー。

addObserverのほうも本当はやっておきたい(Spaces.appの設定が変わったら通知を受ける、という風に)けれど、Spaces.app関連はすべてプライベート関数を使わねばならず、そうするとMacRuby側で直接呼び出せないようなのであきらめる。
MacRubyのControllerの中で
CGSRegisterConnectionNotifyProc(_CGSDefaultConnection(), spacesSwitchCallback, CGSWorkspaceChangedEvent, (void *)self);
とかって書くのは無理、と。

以上、偉そうにかいているけれど知識のほとんどはWarpのソースから得たものです。

2011年1月21日金曜日

memo

Cocoaアプリケーションを拡張するSIMBLプラグインの作り方: "

Reeder for MacからTwitterへのポストをスピーディーにするReeder Extension


うーん、おもしろさそう。じっくりと読むことにする。

Creating Growl Notifications From A MacRuby App - new ThoughtStream("Derick Bailey"); - Los Techies : Blogs about software and anything tech!

Creating Growl Notifications From A MacRuby App - new ThoughtStream("Derick Bailey"); - Los Techies : Blogs about software and anything tech!

Twitter経由で知ったチュートリアル。MacRubyからGrowl通知を作成する。
見たところ実質的なコードは
    def awakeFromNib()

      GrowlApplicationBridge.setGrowlDelegate(self)


      GrowlApplicationBridge.notifyWithTitle("Our Growling Title",

        description: "this is a really big description of really cool things! now you can take over the world with Growl from MacRuby!",

        notificationName: "Test",

        iconData: nil,

        priority: 0,

        isSticky: false,

        clickContext: nil)

    end
これだけで、こんなふうにGrowl notificationができあがるみたい。


うむ、明日やってみるか。

MacRuby・CoreGraphicsその4・Spaces切替の視覚効果


こんなショボいUIを作っていろいろやってみる。CoreGraphicsで定義されている視覚効果のemunは以下のとおり。

typedef enum {
 CGSNone = 0,    // No transition effect.
 CGSFade,                // Cross-fade.
 CGSZoom,                // Zoom/fade towards us.
 CGSReveal,              // Reveal new desktop under old.
 CGSSlide,               // Slide old out and new in.
 CGSWarpFade,    // Warp old and fade out revealing new.
 CGSSwap,                // Swap desktops over graphically.
 CGSCube,                // The well-known cube effect.
 CGSWarpSwitch,   // Warp old, switch and un-warp.
 CGSFlip   // Flip over
} CGSTransitionType;

こいつと一緒に渡すCGSTransitionOptionについては省略。CGSTransitionTypeとCGSTransitionOptionをそれぞれcombboxのアイテムに仕込んで、trans_combo.indexOfSelectedItemといった感じで選択アイテムのindexを取得する。

で、ObjCのclassのメソッドを書きなおす。
- (void)setSpaceNumberWithTransition:(NSInteger)space trans:(NSInteger)a_trans option:(NSInteger)a_option{
 CGSConnection cid;
 int transitionHandle;
 int transitionSpec[5];
 
 cid = _CGSDefaultConnection();
 
 /* Set up the transition specification */
 
 memset(transitionSpec, 0, 20); /* 20 = 0x14 */
 transitionSpec[1] = a_trans;
 transitionSpec[2] = a_option;
 /* Set up the transition itself */
 CGSNewTransition(cid, transitionSpec, &transitionHandle);
 /* Switch desktop */
 CGSSetWorkspace(cid, space);
 /* A little pause to let the Window Server sort itself out ... */
 usleep(6000); 
 /* ... and fire off the transition */
 CGSInvokeTransition(cid, transitionHandle, 4);
}


実は引数を3つに増やしたところでハマった。引数にラベルをつける、というObjCの書法を知らなかったため、ずいぶんとあちこち探しまわったりあれこれ書き直したりして時間をくってしまった。素人だから仕方ない。

先日、木下誠氏のセミナー資料をMacRubyで書きなおしたとき、ちゃんとerror:nilみたいにしてラベル付きで引数渡しをちゃんとやっていたのに。意味がわかってなかったんだなあ。わかってよかった。
MacRuby側のソースは今のところこうなった。下手な書き方ご容赦。

  def get_max
  CFPreferencesAppSynchronize("com.apple.dock")
  row=CFPreferencesGetAppIntegerValue("workspaces-rows", "com.apple.dock", nil)
  col=CFPreferencesGetAppIntegerValue("workspaces-cols", "com.apple.dock", nil);
  row * col
 end
 
 def prev_space(sender)

  trans=trans_combo.indexOfSelectedItem
  option=option_combo.indexOfSelectedItem
  
  @tmp_space_no=set_space_no
  
  @max_spaces=get_max
  @tmp_space_no = @tmp_space_no -1
  if @tmp_space_no  == 0 then @tmp_space_no=@max_spaces end
  
    @my_space.setSpaceNumberWithTransition(@tmp_space_no,trans:trans,option:option)  
  
  
 end
 def next_space(sender)
 
 end
 def set_space_no
  number=Pointer.new('i')
  number[0]=@my_space.spaceNumber
  number[0]
 end

ちなみに視覚効果を「WarpFade」にするとこんなふうにSpacesが切り替わる。



視覚効果を切り替えて遊んでいると大変たのしい。次はSpaces.appへのNotificationの勉強をしよう。それからマウスのイベント。

土日でどれくらい勉強できるかなあ。

2011年1月20日木曜日

MacRuby・CoreGraphicsその3・CGSSetWorkspaceWithTransition(workspaceのCube風切替)

CoreGraphicsのプライベート関数を使ってSpaces.appのworkspaceを切り替えるシリーズ、3回目です。

1回目 MacRuby・いきなりダークサイドに突入・CGSGetWorkspace
2回目 Spaces.app・現在のworkspaceの総数を知る

CGSPrivate.hを読んでいくと、CGSSetWorkspaceWithTransitionというメソッド(・・・今まで関数って書いてましたが、メソッドの方が正確ですね。)があるのは当初から知っていた。このメソッドの前後に、視覚効果を列挙したenumが2つあって、その中にはCGSCube、なんてのもあって、ひょっとしてcompizの「デスクトップをキューブ化」みたいな効果を操作できるんだろうか、とは考えていた。

本日はそれを試した。結果から言えばCGSSetWorkspaceWithTransitionではできない。まだ単なるCGSSetWorkspaceと同じ動きしかしないようだ。しかし方法はある。CGSNewTransitionで視覚効果を有効にしてから、CGSSetWorkspaceでworkspaceを切り替えるとCube風の動きをする。

いちおう証拠のスクリーンキャスト。



workspaceの切替の速度が遅いのはわざとです。実用的な速さにするとスクリーンキャストしても動きがさっぱりわからないので。
iMacのドでかいデスクトップ全体はキャストできなかったが、なんとなくそれっぽい動きをしているのがわかるかと思う。

何も自分で考え出した方法ではなく、CGSSetWorkspaceWithTransitionでググったら一番最初に出てくるサイトに書いてあったんですけどね。(^^;)

A Brief Tutorial on Reverse Engineering OS X

DesktopManager
という、Spacesが標準添付となる前のOSXでよく使われていたvirtual desktop managerの作者さんのblogです。CGSSetWorkspaceWithTransitionはまだ使えない、だけどリバースエンジニアリングしてこんなことがわかったよ、というDEEPな話。結果的にこのお方、こうやればCube風効果ができる、でもおれはTigerだからだめだった、友達のLeopardなら動いたよ、と書いているようです。(訂正:よくよく前記の記事を読んだら、この機能をDesktopManagerに組み込んだらちゃんと動いているよ、という内容でした)

CGSConnectionRef cid;
int transitionHandle;
int transitionSpec[5];

cid = _CGSDefaultConnection();

/* Set up the transition specification */
memset(transitionSpec, 0, 20); /* 20 = 0x14 */
transitionSpec[1] = transition;
transitionSpec[2] = direction;

/* Set up the transition itself */
CGSNewTransition(cid, transitionSpec, &transitionHandle);
/* Switch desktop */
CGSSetWorkspace(cid, workspaceNumber);
/* A little pause to let the Window Server sort itself out ... */
usleep(10000); 
/* ... and fire off the transition */
CGSInvokeTransition(cid, transitionHandle, seconds);

このコードを多少改変してObjCのSpaces classに実装した。こんな感じ。
-(void)setSpaceNumberWithTransition:(NSInteger)space {
 CGSConnection cid;
 //CGSTransitionSpec spec;
 int transitionHandle;
 int transitionSpec[5];
 
 cid = _CGSDefaultConnection();
 
 /* Set up the transition specification */
 
 memset(transitionSpec, 0, 20); /* 20 = 0x14 */
 transitionSpec[1] = CGSCube;
 transitionSpec[2] = CGSBottomLeft;
 /* Set up the transition itself */
 CGSNewTransition(cid, transitionSpec, &transitionHandle);
 /* Switch desktop */
 CGSSetWorkspace(cid, space);
 /* A little pause to let the Window Server sort itself out ... */
 usleep(6000); 
 /* ... and fire off the transition */
 CGSInvokeTransition(cid, transitionHandle, 6);
}
CGSConnectionがわからない、と怒られるので、CGSPrivate.hをimportしている。


transitionSpec[1] = CGSCube;
 transitionSpec[2] = CGSBottomLeft;

ここで実際の視覚効果を設定しているが、CGSCubeを指定するとCGSTransitionOptionは何を設定しても同じ動きしかしないようだ。

そのかわりCGSWarpSwitchとかCGSFlipを指定すると非常に楽しい動きになる。明日辺りはこれらの設定をGUIで選べるようにして遊んでみよう。

先程のRich Wareham氏のコードが2005年、同じようなことをやっていて、1枚のWindowをCube化してグリングリン動かしている、

Xcode Animations and Transitions

このサイトの記述が2006年。うーん、もっと新しい研究成果がどこかにあるような気がする。調べ方が足りないか。それともアンタッチャブルな領域なのかな。

Tigerの時点で準備されていたメソッド群だから、近いうちにOSXで「派手な視覚効果のSpaces切替」が標準で用意されるのか。

2011年1月19日水曜日

MacRubyとエディタ

最近になって生意気にもmacruby-devのメーリングリストを購読している。けっこう気さくなメーリングリストらしく、技術的な話だけなく「どんな開発環境使ってる?」といったフランクな話題も行き交っている。いいな、MacRuby。

で、気になってTextMateのMacRuby用のBundleがあるかググったらすぐに出てきた。

MacRuby.tmbundle - TextMate bundle for MacRuby

redcarのウリの一つはこのTextMateのBundleがそのまま使えること。いや、実際便利ですね、この機能。ちゃんとObjCのBundleもあって、うちの環境にも組み込んでおります。エディタとしてだけでなく、ソースコードビューワとしてもかなり使えます、redcar。これでもう少し起動を速くすれば・・・。

Bundleはたいていgithubのリポジトリなんで、~HOME/.redcar/Bundleで
git clone git://github.com/drnic/macruby-tmbundle.git "MacRuby.tmbundle"
とかすればインストール終了。ただ、現在のところ、~HOME/.redcar/cache/textmate_bundles.cacheをインストール後に一度削除しないと、インストールが反映されないようです。(うーん、この辺、もう変わっているかも)

こんな感じで、いい具合にシンタックスハイライトしてくれます。


あとは日本語(入力)の扱いだけちゃんとしてくれれば、と祈るのみ。

Spaces.app・現在のworkspaceの総数を知る

Spaces.appの行数・列数は~HOME/Library/Prefarences/com.apple.dock.plistに保存されている。・・・って一体どうやったらその情報に行き着くんだろ。(^^;)

Warpのコードを読んでわかったわけですが。WarpのMainController.m、223行目あたり。
  CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
 
  NSInteger columnCount = CFPreferencesGetAppIntegerValue(CFSTR("workspaces-cols"), CFSTR("com.apple.dock"), nil);

MacRubyでcolumnCount = CFPreferencesGetAppIntegerValue(CFSTR("workspaces-cols"), CFSTR("com.apple.dock"), nil);をそのまま入力すると「CFSTR」なんて知らない、と怒られる。こういう場合はおそらくCFSTRという関数は必要なからだろう、と推測して
row=CFPreferencesGetAppIntegerValue("workspaces-rows", "com.apple.dock", nil);
としてみたところ問題なく動いた。

TextViewに改行を入力できていないのはご愛嬌。

この試験アプリを立ち上げたまま、Spacesの設定を4列×2行の8面にしてみると、残念ながらその変更を読み取れない。やはりWarpのコードにあるとおり、いちいち
CFPreferencesAppSynchronize "com.apple.dock"
とする必要があるようだ。これを書くと、起動中にSpacesの設定を変更してもちゃんと読み取れる。
行×列をすればworkspaceの最大数を得ることができる、と。

しかしWarpのコードの中ではなんだかいろいろやっていて、現在のworkspaceの番号が「65538」だったら、みたいな処理があちこちにある。これはもう少しあれこれ自分で書いてみないとわかりませんな。

2011年1月18日火曜日

Warp

ところで私は、WarpというSpaces.appを便利にするユーティリティを使っています。デスクトップの左右の端にカーソルを持って行くとworkspaceが切り替わる、というものです。

これがマウスゼスチャとか、ホィールボタンとかに対応してくれたらわざわざ自分でtoolを作ってみようかと考えることもないわけですが、そこまで便利ではない。あ、そうか、作者さんに要望してみるという手はありますな。

それはともかく、作者さんのblogに何かその手の情報はないかな、と思って本日行ってみたら、なんてこったい、githubにソースをおいてあるじゃん!

で、さっそく git cloneしてソースを眺める。

頭の中で勝手に想像していたのは、Spaces.appにイベントを発行して動かしているのではないか、ということだったんですが、全然違いました。

workspaceの移動は基本的にCGSSetWorkspaceを使っていました。で、移動したあと、spacesSwitchCallbackという関数で
[[NSNotificationCenter defaultCenter] postNotificationName:@"ActiveSpaceDidSwitchNotification" object:nil userInfo:info];
というふうにSpacesに通知してるんですな。なるほどねえ。

さらにworkspaceの最大数は、
CFPreferencesGetAppIntegerValue(CFSTR("workspaces-rows"), CFSTR("com.apple.dock"), nil);

で行と列の最大数を取得して計算しているみたい。

これは勉強になります。しばらく「荻原本」とWarpのソースを読むことになりそう。

よし、と。新しく、「cocoa」というtagも作ってみた。

MacRuby・少しいじる

Window上にcomboboxを配置して、選択された番号のworkspaceに移動できるようにしてみた。


comboboxの選択されている項目を値をどうやって取得するのかわからなかったので調べたら、my_combo.stringValueってするだけだった。簡単だなあ。

ということでworkspaceの移動はできるわけだけど、移動したことをSpaces.appがわかってないからその後のSpaces.appの挙動が少しおかしくなる。Spaces.appが保持している「現在のworkspace」が変わってないわけだから、それも当然。

そんなんで今のところの課題は

1,workspaceの数(最大数)をどうやって取得するのかわからない
2,workspaceが変更されたことをSpaces.appに教えることができない

というところ。まだまだ勉強することがたくさんあるなあ。でも本当にもっと勉強するのか>自分。

これを買っちゃったのでもう少しがんばる。

詳解 Objective-C 2.0 改訂版
荻原 剛志
ソフトバンククリエイティブ
売り上げランキング: 2877

本当は師匠が推薦している

Mac OS X Cocoa プログラミング 第3版
Aaron Hillegass アーロン ヒレガス
ピアソンエデュケーション
売り上げランキング: 98291
これを買うつもりだったんだけど、売っていなかった。がっくり。で、どうせ買うだろうからということで通称「荻原本」を買うことにした。

一応、市内で唯一オライリーの書籍を置いている、「一番たくさん本がある」と定評のある某大型書店に行ったんですけどねー。田舎に住むってのはこういうことです。

redcar0.10

redcarがバージョンアップして0.10になった。0.9の次は1.0かと思ってわくわくして待ってたけどまだまだアルファ版だそうです。

今回の改良点は
  1. better_search_and_replace
  2. Mirah syntax checking and a Mirah REPL!(Mirahってどんな言語なんでしょ)
  3. 見た目の向上
その他
* the REPL now has a cute block cursor (Delisa)
* there is a configurable test runner, which can run the test associated with the current file (new contributor Chris Nelson)
* reveal file in directory tree is now optional (Delisa)
* the fuzzy file finder ignores spaces (new contributor Tomasz Wegrzanowski)
* “close others” and “close all” commands (Clinton Nixon)
* cursor movement from selection more standard (Frederik Fix)

ということだそうです。日本語を入力したとたん、シンタックスハイライトが消えてしまう現象は直ったか試してみました。

日本語の表示そのものは全く問題ないです。


cucumberもちゃんとシンタックスハイライトしてます。TextMateのBundleがそのまま使えるというのはすごい強みなんですな。

では日本語入力は・・・


hamlもばっちりハイライトしてます。いいですなあ。しかしこの状態のファイルの末尾に「あ」と入力すると


orz。

ま、日本語入力する場面ってあんまりないから、これでも十分実用に耐えるわけですが。
次に期待しよう。(ちなみにbug報告はもう上がってます。日本語とロシア語だけこの現象が起きるそうです)

2011年1月17日月曜日

MacRuby・いきなりダークサイドに突入・CGSGetWorkspace

MacRubyから、cocoaのプライベート関数であるCGSGetWorkspaceを使ってみた。

午後からお休みをとって帰宅。家庭の用事をあれこれ済ませてもやはり時間の余裕があってMacRubyでできることを調べる。

(旧)cocoaの日々さんの「Spaces - アクティブなspaceの番号を得る(プライベート関数使用)」というアーティクルでCGSGetWorkspace という関数を使えばSpacesをいじれることを学習しました。これは昨晩すでに酔っ払ってから調べがついたため(それでもじゃあCGSGetWorkspaceのGetをSetにすればSpacesを動かせるんじゃないの?え?googleさんよ、とやっていたらやっぱりそのようだ、ということまではわかった)、本日改めて調べてみました。なんのことはない、「MacRuby CGSGetWorkspace」で検索しただけなんですが。

叩けよさらば開かれん、そうすると蛇の道は蛇でしてやっぱりこういうページに行き当たる。

Mouvement dans les spaces avec MacRuby (et Cocoa)

これ、何語?という状態ですが、ありがたいことにコードは読める。なるほど、プライベートな関数は一旦ObjCのclassにして、それをMacRubyから使うのか。

参考のために上記サイトのヘッダとインプリメントをコピーしておきます。
ヘッダ、Spaces.h。
//
//  Spaces.h
//  RConsole
//
//  Created by greg on 12/03/10.
//  Copyright 2010 Grégoire Lejeune. All rights reserved.
//

#import 
#import 
 
@interface Spaces : NSObject {

}
 
 - (NSInteger)spaceNumber;
 - (void)moveWindowToCurrentSpace:(NSWindow *)win;
 - (void)moveWindow:(NSWindow *)win toSpaceNumber:(NSInteger)space;
 - (NSInteger)getSpaceNumberOfWindow:(NSWindow *)win;
 @end

Spaces.m。
//
//  Spaces.m
//  RConsole
//
//  Created by greg on 12/03/10.
//  Copyright 2010 Grégoire Lejeune. All rights reserved.
//
 
 #import "Spaces.h"
 
 @implementation Spaces
 
 - (NSInteger)spaceNumber {
    NSInteger space = -1;
    CGSGetWorkspace(_CGSDefaultConnection(), &space);
    return space;
  }  

 - (NSInteger)getSpaceNumberOfWindow:(NSWindow *)win {
    NSInteger windowId = [win windowNumber];
    NSInteger space = -1;
    CGSGetWindowWorkspace(_CGSDefaultConnection(), windowId, &space);
    return space;
  }
 
 - (void)moveWindowToCurrentSpace:(NSWindow *)win {
    [self moveWindow:win toSpaceNumber:[self spaceNumber]];
  }
 
 - (void)moveWindow:(NSWindow *)win toSpaceNumber:(NSInteger)space {
    NSInteger windowId = [win windowNumber];
    if(windowId != -1) {
       CGSMoveWorkspaceWindowList(_CGSDefaultConnection(), &windowId, 1, space);
     }
  }
 @end

で、MacRubyでプロジェクトをつくり、WindowにラベルとボタンだけはりつけてControllerを作る。

class MyController < NSWindowController
attr_accessor :my_label
 def init
  super
  @my_space=Spaces.new
  self
 end
 
 def button_action(sender)
  number=@my_space.spaceNumber
  my_label.setStringValue number.to_s
 end
 
end

ボタンを押すとSpacesの番号が出るはず・・・なんですが-4976594とか表示されます。 はて・・・。intだから整数のはずなんだけど・・・。特にエラーが出るわけでもないので、とりあえずMacRubyからプライベート関数が使えることがわかっただけでも万々歳としよう。 ・・・と考えるわけですが、どう見てもintじゃない数値が返ってくるのは変です。いろいろなサイトで CGSGetWorkspaceについて読んでいくと、返ってくるのはどうやらポインタらしい。ということはnumber.to_sとかしてもだめだ。numberにはアドレスが入っている(という表現でいいのかな)なら、そのアドレスに入っているint型の数値を読まなければ。 で、MacRubyにはPointer型がある、という記事をどこかで読んだ記憶があったので検索。おお、MacRubyのコミッタさんの@watson1978さんのはてなだった。
MacRubyのPointerクラスについて
そこでPointer型を使ってコードを書き換えてみる。
def button_action(sender)
  number=Pointer.new('i')
  number[0]=@my_space.spaceNumber
  my_label.setStringValue number[0].to_s
end
わーい、これでちゃんと1とか2とか表示できるようになった。
ボタンを押すと現在のワークスペースを表示するという。 で、画像の中のボタン2は何かというと、
- (void)setSpaceNumber {
 NSInteger space = -1;
 CGSGetWorkspace(_CGSDefaultConnection(), &space);
 CGSSetWorkspace(_CGSDefaultConnection(),space-1);
}
こういう関数を書いておいて、
def button2_action(sender)
  @my_space.setSpaceNumber
end        
こうやってみる。見事、Spacesが瞬時に動くようになりました。ただし、1引いてるだけなのでSpacesの「1」で実行すると恐ろしいことになるに違いない(^^;)。また、設定しているSpaces変更のキーボードショートカット(ctrl-2とか)が一時的に効かなくなります。なんせど素人のやっていることなので危険。

ただ、全く不可能、ということではない、とわかったのが楽しかったです。これで次はマウスのキーイベントか。

OSX・ブラウザでhtmlからPDFを作成する・ブラウザによってちがうのね

初めは当然firefoxで試したわけです。でもなんかサイトの表示通りになってくれない。あれれ、変だな、やっぱ専用のソフトを探してみようか、と思ったところ、他のSpaceで起動していたchromeでやってみようと思いついた。

うーん、なるほど、同じようにPDF化するにもブラウザによってけっこう差がある、ということがわかりました。

yahooのトップページで試してみた結果です。

まずfirefox。


どうもあれこれ試したところ、firefoxはヘッダの部分を表紙と解釈してしまうようです。だから先頭のページが情けないことになる。
続いてsafari。


これは素晴らしい、さすがapple謹製。ほぼそのまま再現できています。
続けてchrome。


右上の広告が抜けているほかは気にならないレベルです。
最後はOpera。


OperaはPDFに関しては一番残念な結果となりました。見た目だけでなく、処理が重いですね。「印刷」ボタンをおしてからかなり時間がかかる感じです。

最後に、ググるなかで行き着いたApplescriptでPDF化したもの。

htmlのPDF化はけっこう難しい作業のようです。safariだけずば抜けている感じがするのは、apple、なにかやらかしているのかな。昔MicroSoftが一太郎に意地悪したように、(^^;)

というわけで、PDF化すると各ページがA4に割りつけられて、けっこう読みづらくなることも判明したので結局「cocoaの日々」さんもPDF化は止めた、という(^^;)。なんのための調査なんだか。

(旧)Cocoaの日々が神すぎる件

(旧)Cocoaの日々


私が騒いでも何をいまさら、なんでしょうけど、@xcatsanさんすごすぎ。毎度便利に使わせてもらっているSimpleCapの作者さんのサイトなんですが、もう情報ありまくり。これだけ素敵なプログラムを作っていて、さらにこれだけの情報を詳しくわかりやすく発信してくれているなんてサイコー!

同じbloggerを使っているんだから(って理由にならないか)、@xcatsanを目標にしてがんばろう。これで木下誠氏と、私淑する人が二人目になりました。迷惑でしょうが。

あまりにも有益な情報がありすぎて、evernoteにつめこむより、PDF化して手元において事あるごとに読ませてもらおう、と考えました。ブラウザで十分かな、とも思うんですが、私は好みで「リンクは新しいタブで開く」としているので、すぐにタブでいっぱいになってしまうんですね。読んでいた記事を見失いやすいので、じっくり読みたいときはブラウザじゃないほうがありがたい。

最初はいわゆる「サイトぶっこ抜き」アプリでサイトごとダウンロードして、htmlをPDF化しようと思ったわけですが、OSXだとわざわざそんなことをしなくてもブラウザから簡単にPDFが作れてしまう、ということを初めて知りました。

Winなら専用ソフトか、PDF用のプリンタドライバをインストールをしなければいけないところです。(・・・本当かなあ。明日職場で試してみよう)OSXって面白いなあ。幸い、月毎のアーカイブを開いていけば、ある程度まとめてPDF化できそう。

で、あれこれ試したのが次のアーティクルになる、と。

2011年1月16日日曜日

iPad

次期iPadは4倍解像度か──iBooks 1.1アプリの解析から推測出回る | iPad | iPad*iPhone Fan:

ふむふむ、なるほどー。

iPadは自分には全然縁のないデバイスだと思っていました。でもiMac買ってからPDFを閲覧する機会が増えて(字を大きくしても余裕でA4で2枚分表示できる)電子書籍なんぞも買うようになったので気が変わった。ベッドで電子書籍を読めるようになりたい。

どーせなら噂が出回り始めたiPad2がいいなあ。上の記事によると

今年春のデビューが噂される次世代iPad、通称「iPad 2」について、スクリーン解像度が一気に1,536×2,048ドットの4倍になるのではないかという憶測が持ち上がっている。報じているのはAppleInsiderとMacRumorsで、iBooksアプリのv1.1を解析した開発者らのTwitterでのコメントを紹介している。

っていうんだけど、それじゃ逆に老眼には見えづらくね?という感想。字をでかくすればいいだけか(^^;)。

恥ずかしながら告白すると、本日、初めてiPadの実物を見た。appleは最近地方のフツーの大型電器店にコンピュータ置いてないようで、ど田舎にすんでるものでiPadを今まで見たことなかったんです、はい。本日、たまたま寄った大型電器店の、ソフトバンクコーナーで初めて実機を見かけて思わず手に取りました。

印象は、まず「意外と小さい」こと。そして「重い」、ですね。こりゃベッドの上でもずーっと手にもっていられる重さじゃないようで。寝転がって、膝をたててそこに立てかけて、という使い方になるでしょうねえ。

しかし「非常に軽快」。こりゃiPad2が楽しみだな。

Apple iPad Wi-Fiモデル 32GB MB293J/A
Apple Computer
売り上げランキング: 9872

でもこの値段なら、やっぱりAirのほうがよくね?と思うおやぢ・・・。

そろそろ

そろそろ人のコードをそのままMacRubyで書きなおすのにも飽きてきた(^^;)。明日からはちがうことをやってみよう。もう少しMacRubyを使って。もうちょっとrubyっぽく書けるようになりたい。

ObjCを勉強してみるのもいいかもしれない。ただ一番の問題は作ってみたいアプリってのが思いつかないことでしょうねえ。なんでもいいから、と思っても、実際に使わないアプリを作っても面白くないわけで。

自分でぜひほしい、と思うのは「デスクトップでマウスのホィールをくるくる回したら、Spacesが切り替わる」という機能ですが・・・。compizでできていたみたいな。有料のマウスユーティリティならできるみたいなんですけどねー。Applescriptとかでできないか調べてみたりはしております。でも簡単な方法はない、と。(無料の)

プログラムするとなるとどう考えてもこれは難関。だって

1,マウスのイベントを拾ってそれがデスクトップ上で起きたものか判定
2,Spacesに「スペース切り替えろ」と命令する

っていう非常にシステムよりの処理をしないといけない。

ま、だからといって絶対不可能とはいえないわけで(現実にできるてるアプリがあるんだから)少し調べてみよう。まずはステータスバーからですかねえ。

この手の知識は、やっぱり最終的にはデベロッパドキュメントの中をあさるしかないんだろうなあ。

XcodeでMacRuby・6日目その2 NSTableView

いやーNSTableViewはきつかった。

- (int)numberOfRowsInTableView:(NSTableView*)tableView
 if (!xmlDocument) { 
  return 0;
 }
 // '/rss/channel/item/'の数を返します
 NSArray* nodes; 
 nodes = [xmlDocument nodesForXPath:@"/rss/channel/item" error:NULL]; 
 return [nodes count];
}
// NSTableViewデータソース - 
- (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)tableColumn row:(int)row
{
 if (!xmlDocument) { 
  return nil;
 }
 // テーブルカラムの識別子を取得します 
 id identifier;
 identifier = [tableColumn identifier];
 // 指定された行の、'/rss/channel/item'を取得します
 NSArray* nodes;
 NSXMLNode* node; 
 nodes = [xmlDocument nodesForXPath:@"/rss/channel/item" error:NULL]; 
 node = [nodes objectAtIndex:row];
 if ([identifier isEqual:@"title"]) { // 'title'の文字列を取得します
  nodes = [node nodesForXPath:@"title" error:NULL]; 
  if ([nodes count] == 1) {
   node = [nodes objectAtIndex:0]; 
   return [node stringValue];
  }
 }
 return nil;
}

// NSTableViewデリゲート
- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
 // 選択された行を取得します
 int row; 
 row = [tableView selectedRow];
 // 選択された行の、'/rss/channel/item'を取得します
 NSArray* nodes;
 NSXMLNode* node; 
 nodes = [xmlDocument nodesForXPath:@"/rss/channel/item" error:NULL];
  node = [nodes objectAtIndex:row];
 // 'description'の文字列を取得します
 nodes = [node nodesForXPath:@"description" error:NULL]; 
 if ([nodes count] == 1) {
  NSString* description; 
  node = [nodes objectAtIndex:0]; 
  description = [node stringValue];
     // テキストビューに記事の内容を設定します 
     [textView setString:description];
 }
}
(void)windowControllerDidLoadNib:(NSWindowController *) aController
{
 // テーブルビューにデータを読み込みます 
 [_tableView reloadData];
 
}
実質3つの関数を書き換えるだけ、なんですが、わからないことだらけでした。
今回お世話になったのがデベロッパドキュメント中の
Developing Cocoa Applications Using MacRuby
この文書でした。NSTableViewを実際にMacRubyから使っているチュートリアル。

結局MacRuby版はこうなった。
def numberOfRowsInTableView(view)
  return 0 unless @xmlDocument
  
  nodes=@xmlDocument.nodesForXPath("/rss/channel/item",error:nil)
  nodes.count
 end
 
 def tableView(view,objectValueForTableColumn:column, row:index)
  return unless @xmlDocument
                    
  identifier=column.identifier
  
  nodes=@xmlDocument.nodesForXPath("/rss/channel/item",error:nil)
  node=nodes.objectAtIndex(index)
  
  if identifier.isEqual("title") then
   nodes=node.nodesForXPath("title",error:nil)
   if nodes.count==1 then
    node=nodes.objectAtIndex(0)
    return node.stringValue
   end
  end
  nil
 end

 def tableViewSelectionDidChange(notification)
  row=atableView.selectedRow
  nodes=@xmlDocument.nodesForXPath("/rss/channel/item",error:nil)
  node=nodes.objectAtIndex(row)
  
  nodes=node.nodesForXPath("description",error:nil)
  if nodes.count==1 then
   node=nodes.objectAtIndex(0)
   str=node.stringValue
   textView.setString str
  end
 
 end

セミナー用にわかりやすさを優先して書かれたソースだとは思うんですが、同じようなコードが並んでやや冗長か、と生意気に考えるおやぢ。
さっきamazonでcocoaの解説書を調べたら
たのしいCocoaプログラミング[Leopard対応版]
木下 誠
ビー・エヌ・エヌ新社
売り上げランキング: 20020

この本のレビューによるとやっぱりrssリーダーがお題になっているらしい。なるほど。

それはともかく、デリゲートってのは、Viewとかの更新を他のクラスにお任せしちゃうための機能、ということでいいのかな。

XcodeでMacRuby・6日目 NSXMLDocument

rssリーダーの続き。
- (void)windowControllerDidLoadNib:(NSWindowController *) aController {
 [super windowControllerDidLoadNib:aController];
 // 書類のURLを取得します
 NSURL* fileURL; fileURL = [self fileURL]; if (!fileURL) {
  return;
 }
 // テキストファイルを読み込みます
 NSData* data; data = [NSData dataWithContentsOfURL:fileURL]; 
 // XMLドキュメントを作成します
 xmlDocument = [[NSXMLDocument alloc] initWithContentsOfURL:fileURL options:0 error:NULL];
 if (!xmlDocument) {
   return;
 }
 // '/rss/channle/title'のノードを取得します
 NSArray* nodes; nodes = [xmlDocument nodesForXPath:@"/rss/channel/title" error:NULL]; 
 if ([nodes count] == 1) {
  NSXMLNode* titleNode; titleNode = [nodes objectAtIndex:0];
  // テキストフィールドにタイトルを設定します
  NSString* title; title = [titleNode stringValue]; [titleTextField setStringValue:title];
 }
 // '/rss/channle/link'のノードを取得します
 nodes = [xmlDocument nodesForXPath:@"/rss/channel/link" error:NULL]; if ([nodes count] == 1) {
  NSXMLNode* linkNode; linkNode = [nodes objectAtIndex:0];
 // テキストフィールドにリンクを設定します
  NSString* link; link = [linkNode stringValue]; [linkTextField setStringValue:link];
 }
}

これをこう書きなおしたら意図通り動きました。

def windowControllerDidLoadNib(aController)
    super
    fileURL ? super : return
  
    data=NSData.dataWithContentsOfURL(fileURL)
    @xmlDocument = NSXMLDocument.alloc.initWithData(data, options:NSXMLDocumentTidyXML, error:nil)

    return unless @xmlDocument 
  
    nodes=[]
    nodes=@xmlDocument.nodesForXPath("/rss/channel/title", error:nil)
    if nodes.count==1 then
      titleNode=nodes.objectAtIndex(0)   
      title=titleNode.stringValue
      titleTextField.setStringValue title
    end
    nodes=@xmlDocument.nodesForXPath("/rss/channel/link", error:nil)
    if nodes.count==1 then
      linkNode=nodes.objectAtIndex(0)
      link=linkNode.stringValue
      linkTextField.setStringValue link
    end
end

cocoaのプログラムの作る、というだけなら素直にObjC使ったほうが簡単でしょうね。(^^;)わざわざrubyに書き換えているのはrubyの勉強だから、です。ついでにcocoaの勉強もできるんだから一挙両得。

今回なかなかわからなかったのは
xmlDocument = [[NSXMLDocument alloc] initWithContentsOfURL:fileURL options:0 error:NULL];

これをどうするか、でした。初めは
@xmlDocument = NSXMLDocument.alloc.initWithData(data, "xml", nil)
と書いてみたんですが、こうやるとNSXMLDocumentにそんなメソッドはない、と怒られてしまう。そんでいつもの
MacRuby: The Definitive Guide

のなかを検索したら、やっぱりありました。うーん、この本は買いだな。まだアルファ版くらいだけど。

document = NSXMLDocument.alloc.initWithData(data, options:NSXMLDocumentTidyHTML, error:nil)

そうか、こうやってオプションとかを書くのか・・・。TidyHTMLをTidyXMLにしたらすっきり動くようになりました。完全な自己満足の世界ではありますが、とってもおもしろいですねえ。

2011年1月15日土曜日

Rubyの本

改めてMacRubyのサンプルプロジェクトを読んでみると、へー、こういう書き方をするのか、と感心します。rubyって本当にシンプルできれいなコードを書くことができますね。

残念ながら私はまだまだ学習中なので、ObjCから書き換えようとしてもあんまりうまくいかない。全然わかってないんだから仕方ないわけですが、とりあえずたくさん書いて覚えるしかありません。

ちょっとわからないことがあると、文法的なことなら(自己代入演算子は許されるんだったかな、とか)ググるよりまず手元の本を参照することが多い。最近はもっぱら

プログラミング言語 Ruby
まつもと ゆきひろ David Flanagan
オライリージャパン
売り上げランキング: 67631

この本の索引を開くことが多いです。できれば電子書籍版を手に入れてささっと検索できたらいいな、と思ってオライリーのページで探してみる。


あんまり安くなりませんね、紙の本から。もう少しお値打ち感があるとすぐにポチるんだけど。紙の本をもっているだけにためらいますな、ケチな私は。Pragmaticさんだと、

この本のベータ版、電子書籍だけなら24ドル。印刷した紙の本で32ドル。両方合わせて53ドルに値引き。紙の本に比べて3割くらい安くしてます、電子版。「プログラム言語Ruby」なら2800円くらいだと「許せるかな」ということに・・って400円くらいの違いか。単にケチなだけだよこれじゃ。(^^;)

プログラミング関係の書籍なら、今後は電子書籍があるならそっちを買いたい。だから「ちょびっと立ち読み」できるようにして欲しいですね。

私の机の周りには

たのしいRuby 第3版
たのしいRuby 第3版
posted with amazlet at 11.01.15
高橋 征義 後藤 裕蔵
ソフトバンククリエイティブ
売り上げランキング: 22262

Rubyレシピブック 第3版 303の技
青木 峰郎 後藤 裕蔵 高橋 征義
ソフトバンククリエイティブ
売り上げランキング: 205897

もおいてあって、かなり頻繁に使ってます。「たのしいRuby」は赤い表紙の2004年2月発行の初版第3刷。「レシピ」に至っては2004年6月の初版第1刷(本当の初版本ですな)、赤いピーマンが踊ってるやつ。

いかに「Rubyが書けるようになりたい」と考えていたか、自分でも驚いちゃいますね。

今のところ電子書籍の欠点はパソコンに向かって座って読むしかないところ。iPad2は、おそらく買うと思うなあ。

MacRuby: The Definitive Guide



MacRuby: The Definitive Guide

昨日からがらっと目次が変わっていてびっくり。大きく2部構成にするみたい。前半が「MacRuby Overview」、後半が「MacRuby in Practice」。後半はexampleで実際にアプリを組みながら解説、という実践的な内容になるようです。ますます期待大。後半の章立ては本日のところこうなっています。

Chapter 9. Address Book Example

This first example shows how to integrate the Twitter webservice with the Address Book application.

Chapter 10. Geolocation

This example shows how to use the location of a user thanks to the CoreLocation framework.

Chapter 11. Video Game using CoreAnimation

This example shows how to use CoreAnimation to create a simple 2D game.

Chapter 12. Video Game using OpenGL

This example shows how to use OpenGL from within MacRuby.

Chapter 13. Use MacRuby in an Objective-C Cocoa app

This example shows how to mix MacRuby code and Objective code in the same app.

なるほどー、こういうアプリをrubyで書ける、ということならMacRubyってすごく魅力的ですね。すでに30ドルちょっとで買える、ということがわかった。PDFなら全体の内容から検索するのに楽だから、もう少し様子を見て買うことにしようと思います。

XcodeでMacRuby・5日目 Document Base

というわけで5日目は木下誠氏のcocoa中級セミナーに挑戦。(リンクは直接ダウンロードに行っちゃうのでご注意を)

わ、いきなりRSSリーダーですか。大丈夫かなあ。と不安を抱えつつ作業をしてみる。今回は「Document Base」のアプリケーション。うむ、勉強のいい機会だ。

まずはプロジェクトを作る。

この段階で自動生成される「MyDocument.rb」の中身はこうなっている。
class MyDocument < NSDocument
 
 # Name of nib containing document window
 def windowNibName
  'MyDocument'
 end
 
 # Document data representation for saving (return NSData)
 def dataOfType(type, error:outError)
  outError.assign(NSError.errorWithDomain(NSOSStatusErrorDomain, code:-4, userInfo:nil))
  nil
 end

 # Read document from data (return non-nil on success)
 def readFromData(data, ofType:type, error:outError)
  outError.assign(NSError.errorWithDomain(NSOSStatusErrorDomain, code:-4, userInfo:nil))
  nil
 end

 # Return lowercase 'untitled', to comply with HIG
 def displayName
  fileURL ? super : super.sub(/^[[:upper:]]/) {|s| s.downcase}
 end

end

昨日はこのファイルを見ただけでorzってなったんですが、これに書き加えながら勉強、と。 まず読み込めるファイルの種類を設定する、と。「ターゲット」からやればいいんですね。知らなかった。昨日はinfo.plistを直接いじってみたのでした。(^^;)
この規模のIDEとなると、やっぱり闇雲な猿いぢりだけではダメですね。ちゃんと1冊本を買うほうがいいか・・・。 それから「attr_accessor :textView」だけMyDocument.rbに記述しておいてOutletをつなぐ。
MyDocument.rbをセーブしないでこの作業をしようとしたら、どうしてもTextViewに行き着かず悩みました。 さてここから、いよいよコーディング。本日は実質、次の関数を書き換えて終わりました。
- (void)windowControllerDidLoadNib:(NSWindowController *) aController 
    [super windowControllerDidLoadNib:aController];
 // 書類のURLを取得します
 NSURL* fileURL; fileURL = [self fileURL]; if (!fileURL) {
  return;
 }
 // テキストファイルを読み込みます
 NSData* data; NSString* string; 
    data = [NSData dataWithContentsOfURL:fileURL]; 
        string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        if (!string) {
  return;
 }
 // テキストビューにテキストを設定します 
 [textView setString:string];
dataOfType、readFromData、windowControllerDidLoadNibといった関数がどんな働きをするのか調べるところから始まるので、時間がかかりますねー。Document Baseのアプリがどのようにデータをもらって起動するか、といった辺りの説明はちょいとググれば山ほどの記述に行き当たります。みなさん同じことを苦労して調べてるのね。それをちゃんと書き残してくれるから、私のような「遅れてきた」人間もなんとか頑張れるんだなあ。 とりあえず
def readFromData(data, ofType:type, error:outError)
    outError.assign(NSError.errorWithDomain(NSOSStatusErrorDomain, code:-4, userInfo:nil))
    true
end
この関数のnilがあるとどんなドキュメントも読み込んでくれないので適当に「true」としておいたけど大丈夫だろうか。いや大丈夫みたいなんですけどね。 一番困ったのが
string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
この部分。ググったりXcode付属のドキュメントを探したりいろいろしましたが、どうやって書くのかわからない。もうやめようか、と思うくらい。 NSStringで「string」を初期化して、initWithDataで「data」を文字列化したものを渡す、という処理だというのはわかるんだけどMacRubyでどう書いたらいいか、そんなのどこにも書いてませんわねえ。 ところが
MacRuby: The Definitive Guide
にはちゃんと書いてありましたよ。(MacRubyの作者の一人が書きかけの本を公開してるものです)第5章「Foundation」の「Data」にちゃんとこう書いてある。
NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding) # => "Some classes gets initiated using data, (i.e NSXMLDocument)"
そうか、NSString.alloc.って書いていくのか。 というわけで現在のところのMyDocument.rbはこんな感じです。
class MyDocument < NSDocument
attr_accessor :textView
 # Name of nib containing document window
 def windowNibName
  'MyDocument'
 end
 
 # Document data representation for saving (return NSData)
 def dataOfType(type, error:outError)
  #outError.assign(NSError.errorWithDomain(NSOSStatusErrorDomain, code:-4, userInfo:nil))
  #nil
  str=textView.string
  data=str.dataUsingEncoding(NSUTF8StringEncoding)
  data
 end

 # Read document from data (return non-nil on success)
 def readFromData(data, ofType:type, error:outError)
  outError.assign(NSError.errorWithDomain(NSOSStatusErrorDomain, code:-4, userInfo:nil))
  true
 end

 # Return lowercase 'untitled', to comply with HIG
 def displayName
  fileURL ? super : super.sub(/^[[:upper:]]/) {|s| s.downcase}
 end
 def windowControllerDidLoadNib(aController)
  super
  fileURL ? super : return
  
  data=NSData.dataWithContentsOfURL(fileURL)
  str=NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
  return unless str 
  textView.setString(str)
 end

end

中級セミナーの資料は40ページほどなんですが、ここまで10ページ。さすがに先が長いなあ。

ど下手なコードでも、一応人目にさらすことを考えると一生懸命勉強して、少しでも「ruby」っぽくなるよう努力できるものですね。そんなわけで、これを書きながら、私は「ruby」と「cocoa」、両方の学習をできていて幸せ。

2011年1月14日金曜日

Xcode DebuggerConsoleを知った

なーんだ、デバッガコンソールを表示させればいいのか。わざわざターミナルに移って作業していた自分が恥ずかしい。

知らない、というのは恐ろしい。けれど知ることができてよかった。


だんだんMacRubyがおもしろくなってきたのでcocoaの勉強をぽちぽちしています。Web上の情報が豊富なこと、PDFファイルでまとまった資料があること、が特徴的ですね。

デバッガコンソールの存在は、githubの
MacRubyWebKitPlugInExample

上にあったスクリーンキャストを見ていて、あれ、このウィンドウはなに?と気づいた、と。
まあ勉強し始めてまだ1週間も経ってないし、まともに本も読んでないので無知は仕方がない。

タブインターフェイス

今やブラウザでは普通になったタブインターフェイス、WindowsやGnomeやKDEでもいろいろなアプリで採用されてます。たくさんのコンテンツの表示をさっさと切り替えることができるので便利ですね。私は好きですな。

OSX上のアプリだと、意外と採用されていない。ブラウザはともかく、ぱっと思いつくのが最近知った「Kod」、iTerm、ターミナル.app、くらいですかねえ。

Windowsのアプリには「SDI」と「MDI」という区別があって、というか昔はあって・・・今はどうなんだろう、触ってないからよくわかんないや、とにかくSDIはひとつのアプリの起動プロセスにつきひとつのドキュメントしか表示できない、というやつ。だからシングルドキュメントインターフェイス。MDIはもともとのMacの仕組みに近いんだけど、一つの親ウィンドウの中に複数の子ウィンドウが表示されるというやつ。これもやっぱりMacでは見かけない方式かも。

タブインターフェイスが生まれてからはMDIは廃れてきているかなあ。Excelも基本的にMDIなんだけどBookを2つも3つも開くことあんまりないしな。

アプリの起動プロセスは一つ、でも複数のドキュメントを開くことができる、というのがOSXのDocument Baseということらしい、と本日初めてわかった。この伝統があるから、わざわざタブを一つのウィンドウの中にたくさん表示する必要がないのか。じゃあなぜターミナル.appだけはこのインターフェイスなのかしらん?

そんなんでタブインターフェイスを初めて採用したアプリってなにか、ちょっと調べてみた。当然ブラウザだろう、という先入観で「タブブラウザ 世界初」でググったら、行き当たったがGIGAZINEさんのこのページ。

世界で初めてタブを採用したブラウザの名は「NetCaptor」

はい?NetCaptorとな?1997年?Operaじゃなかったの?上記ページの写真を見ると、タブは右横に表示されていますねー。このインターフェイスで思い出したのが、OS/2Warpのプレファレンスですね。画像検索したらこんなのが出てきた。


むむ、そういえばWindows95も設定系のダイアログにタブもったコントロールがあったじゃん、というわけでまたもや検索。あったあった。


そういえば自作のアプリにこのインターフェイスをつけようとしてあれこれやった記憶がよみがえってきた。なーんだ、そうするとタブインターフェイスはMicroSoft発祥か。いや待てよ、fluxboxはいくつかのウィンドウをタブにまとめる機能があったぞ・・・。


うーむ、しかしfluxboxは2001年からのプロジェクトのようですね。

結局、日本版WikipediaからTabbed Document Interfaceという言葉に行き着き、英語版Wikipediaのこのページにたどり着きました。なんと、NeWS(はSonyが発売した廉価なワークステーション、かと思ったら、日本語版Wikipediaによると

NeWS(Network extensible Window System)は、1980年代中ごろにサン・マイクロシステムズが開発したウィンドウシステム[1]。古くは "SunDew" と呼ばれ[2]、後にJavaを設計したジェームズ・ゴスリンが主任アーキテクトとして設計に当たった。NeWS インタプリタは PostScript に基づいている(後の Display PostScript に似ているが、2つのプロジェクトに関係はない)。

ということです)上の「Gosling Emacs text editor」が世界初、だそうです。ちなみにWindowManagerレベルでタブインターフェイスに対応したのはPWMが初めてだそうです。

さて、ここで話題はまたまたブラウザに戻ります。(わ、狙っていたみたいな展開ですな)
おそらく、世界で一番新しいブラウザがただいま開発中。あのなつかしい「シイラ」がHMDTの木下誠氏によって再開発中。

私はMacのCPUがインテル製になる、と発表されてからPPC版のmac miniを買った物好きなんですが、シイラの初登場はそのころでした。firefoxとは全く違う使い心地でとっても好きなアプリだったのですが、現在のiMacを買ってからインストールしようとしたらすでに過去の遺物になっていて悲しかった。

cocoaの勉強を始めたら木下氏の文章にすぐに行き当たって、再開発を知ってとてもうれしかったです。とりあえず最後はシイラの話になるように、と思いながら書いてきたこのアーティクル、あれこれ調べながら書いて、意外な発見もあっておもしろかった。

XcodeでMacRuby・4日目 電卓を作る

やはりちょっと面白くなってcocoaについていろいろ調べる。

Butinekoの世界 iPhone SDKの開発に入る前に読んでおきたい日本語の資料(開発ツール編)

経由で知りました。HDMTの木下誠氏によるアップルのcocoaセミナーの資料。ダウンロードはここから。

Documentation 日本語に翻訳されたドキュメントです。

とりあえず今回は「初級編」の資料をじっくり読んでみた。まあObjective-Cのソースは見慣れない記号が多くて([]とか-とか@とかがどうしてここにある?という感じで)私には読みづらいわけですが基本はCなので、簡単なものならなんとかついていける。

で・・・この電卓のサンプルをMacRubyで書きなおせばいいんじゃないの?と考えました。それができるなら、楽しいだろう、と。

しかしソースを読むと、のっけから
- (id)init {
    Calculator/CalcController.m
    self = [super init]; if (self) {
        value = 0; operation = OpNone; numberEntering = YES;
    } return self;
}
ってrubyのclassのinitなんか書いたことないぜ、というレベルなので非常に不安になる。(はいはい、その程度ですよ私は)ただ、MacRubyにはサンプルがたくさん用意されているので、そちらで多少調べてみると、こんなふうに書きなおせばいいらしい。

def init
 super
 @value=0
 @operation=OpNone
 @numberEntering = true
 
 self
end

「super」って何?(ボソッ)
ま、とりあえずこれで大丈夫みたい。(^^;)わからないことは後でゆっくり調べよう。思いついたことを実行にうつすときはスピードも大事なので、やれそうだ、という時はさっさと作業したほうがいい。

で、リソースもセミナー資料の通りに作って、アウトレットとアクションの接続をするために骨組みだけソースを作ったのが下の画像。


ちょっと資料に比べると電卓君がかっちょ悪い感じがする。センス無いからなあ。で、あとはアクションにあたるdefを地道に移し替えていく。
できあがったソースがこうなりました。

class CalcController < NSWindowController
attr_accessor :textField
OpNone=0
OpPlus=1
OpMinus=2
OpTIme=3
OpDivid=4

def init
 super
 @value=0
 @operation=OpNone
 @numberEntering = true
 
 self
end

 def clear(sender)
  textField.setStringValue("0")
  @value=0
  @numberEntering = false
 end

 def divide(sender)
  self.calculate
  @operation=OpDivid
  @numberEntering=false
 end

 def equal(sender)
  self.calculate
  @operation=OpNone
  @numberEntering=false
 end

 def minus(sender)
  self.calculate
  @operation=OpMinus
  @numberEntering=false
 end

 def number(sender)
  s_number=sender.title
  old_number=textField.stringValue
  if @numberEntering
   new_number=old_number+s_number
  else
   new_number=s_number
  end
  textField.setStringValue(new_number)
  
  @numberEntering=true
 end

 def plus(sender)
  self.calculate
  @operation=OpPlus
  @numberEntering=false
 end
 
 def time(sender)
  self.calculate
  @operation=OpTIme
  @numberEntering=false
 end

 def calculate
  enterValue=textField.stringValue.to_i
  
  case @operation
  when OpNone then
   @value= enterValue
  
  when OpPlus then
   @value += enterValue
  
  when OpMinus then
   @value -=enterValue
  
  when OpTIme then
   @value *= enterValue
  
  when OpDivid then
   @value /= enterValue
   
  end
  textField.setStringValue @value.to_s
 end
end

うーん、Objective-Cより短く書けてるかも。 ちゃんと意図したとおりに動きます。かなり打ち間違えたところがあって、相変わらず時間がかかっております。 一番自分でも笑っちゃったのが
class CalcController < NSWindowController
の「<」を「>」って入力してたことですね。呆れました、さすがに。こんなひどい間違いをしてもXcodeの「ビルド」では文句を言われません。実行したら「コード1で終了」という見慣れたエラーがでるので、あとはターミナルから直接実行してみて、MacRubyが何と言って怒っているかを確かめる、というなかなか泣けるdebug環境です。こういう時こそtestなんでしょうねえ。でもいっぺんに勉強する時間がないぞ。

こうやって、Objective-CのソースをMacRubyで書き換えていくという作業、自分にはとっても勉強になります。しばらくこれでいけそうかも。なにより、rubyの書き方の勉強にすごーくなってます。実際に動くコードを作れるのもうれしいし。XcodeやInterfaceBuilderにも慣れてきたし。InterfaceBuilderの挙動はMacらしく、洗練されてますね。VisualStudioの似たような機能とは大分ちがうような気がする。(いや、かなり昔ですからね、VS使ってたの)

5日目もあるかもしれません。

2011年1月13日木曜日

SourceForge.JPの新機能・github対抗?

SF.jpからこんなメールがきた。

SourceForge.JP 上で最近行われたアップデートのお知らせです。

* 個人向けオープンソース開発支援機能 PersonalForge 提供開始
* コンパイルファームサービス提供終了
* プロジェクトレビュー機能評価方式の変更
* SourceForge.JP Blog 機能(Wordpressホスティング機能)のベータ公開(再掲)

久しぶりにログインして、PersonalForge っていうのを試してみた。

オープンソース開発をより手軽に行なっていただけるように、個人向けオープン
ソース開発支援機能 PersonalForge の提供を開始しました。

PersonalForgeを利用することで、SourceForge.JPアカウントに結びついた
個人用のGitレポジトリおよびファイル配布用の領域などが用意された作業
領域を複数作成し、自分のための開発エリアを簡単に用意することができます。

従来SourceForge.JP上での開発のためには、SourceForge.JPへのプロジェクト
登録を行い、その上で開発をすすめるという手順が必要でしたが より手軽に
オープンソース開発を開始することができます。

PersonalForgeで提供される機能は、現在はGitレポジトリ/ファイル配布サービス/
Blog連携のみですが、利用状況やユーザーの要望を参考にして、Git 以外のレポジ
トリやその他の機能のサポートを随時行うことを予定しています。

実際に「作業部屋」というのをつくると、そこはgitのリポジトリ。昨日、KUROIGAMENさんで勉強した通りにファイルをひとつ git pushしたらちゃんとできました。(正確には
git push origin master
と入力しないとだめでしたが・・・なぜだ)
ふむ、こそこそと練習するにはいいかも。

さらにファイル置き場にも使えるので、何かの時に利用させてもらおう。
Wordpressのblogまで設置できるようになっている。がんばっているなあ。github全盛で、ちょっと利用が減っているのかしらん。

しかし、相変わらずダウンロード順位はCabosが1位。まだP2Pやってる人いるのか。やばくねえ?っていう感じでありますな。

ずるっこ!がスゲー件

ずるっこ!

何日前からはてブが壮大に集まっていて、いったいなんだろうと思って覗いたらさすがにはてブが集まるだけあった。

私のようになんちゃって、で英語サイトを読む人間には最高じゃないか。いやー、ぜひともローカルのPDFを(以下略)


firefoxでは単語の上に表示、とは(デフォルトでは)ならないようです。プラグイン入れるだったかな。

かなりネット上の英語サイトを読むが楽になります、私は。単語の意味がわからん!ということが一番多いので。

XcodeでMacRuby・3日目

class Controller < NSWindowController
attr_accessor :textView
  def file_open(sender)
    dialog = NSOpenPanel.openPanel
    dialog.allowsMultipleSelection = false
    dialog.canChooseFiles = true
    if dialog.runModal == NSOKButton
      open_file_string = dialog.filename
    end
    data = File.read(open_file_string)
    textView.insertText data 
  end

end

変更したのは実質最後から2行目だけ。
data = File.read(open_file_string)
これでだけ。なんでもテキストとしてOpneしちゃうビューワの完成(^^;)。rubyはすごいなあ。
htmlはもちろん、pdfも開けます。
テキストとして。(^^;)。 とりあえずこのプロジェクトはここまでで置いておいて、Xcodeで作れる他のMacRubyのテンプレートも試してみる。 他に作れるのは
MacRuby Core Data ApplicationとMacRuby Document Based Application、そしてpref。

いちおう全部プロジェクトを作って、実行したりソースを眺めたりしたけれど、ここから先はcocoaの知識がないとかなり難しい、という判断。(MacRubyのサンプルも眺めてみたけど、残念ながら理解不能でございました、私には)

というわけでこの先どうするか思案中。
道楽なんで何をしてもいいわけですが、cocoaの勉強はちょっと苦しい道のりになりそうだしなあ。
rubyの勉強という点では、実際に動くGUI付きのプログラムを作れるのはかなり魅力的ではある、と。