MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

プログラマーに興味がある方なら誰でも入会いただけます。
MOSA Multi-OS Software Artists
===SINCE 1995===

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSADenバックナンバー 2008年5月発行分

  • MOSA Developer News[MOSADeN=モサ伝]第299号

    2008-05-27

    目次

    • りんご味Ruby         第25回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #138
    • 高橋真人の「プログラミング指南」  第136回
    • 開発ツールよもやま話      Sharkの概要

    りんご味Ruby   第25回  藤本 尚邦

    前回、プログラムが期待通りに動作しているか確認するのためのテストを書きました。ということで、今回はYahooHonyaku#translateの実装にとりかかることにします。人がYahoo!翻訳を使うときには:

    (1) WebブラウザでYahoo!翻訳のページを開く
    (2) 何語から何語へ変換するか指定する
    (3) 翻訳したいテキストを入力する
    (4) 翻訳ボタンをクリック
    (5) 結果が表示される
    (6) 翻訳されたテキストを読む
    (7) 続けて使う場合は(2)から繰り返す

    というような手順を繰り返すことになるでしょう。Webブラウザは、(1)でHTTPのGETメソッドでYahoo!翻訳のトップページを取得します。(4)でHTTP のPOSTメソッドで翻訳を実行します。(5)でHTMLをパースして翻訳されたテキストを含めたデータ構造(HTML構文木)を作っているはずです。

    YahooHonyaku#translateメソッドの実装もこれと同じような流れで書くことにします(注:前回までに示していたtranslateの実装から変更しました)。

     def translate(mode, text)
       open_top_page unless top_page_opened?
       html = post_text(mode, text)
       parse_result(html)
     end
    


    ・ページが開かれていなければ、開く (1行目)
    ・変換モードを指定して翻訳したいテキストをポストする (2行目)
    ・結果のHTMLをパースして翻訳されたテキストを取り出す (3行目)

    1行目では、unless の後置記法を使って書いています。これについては後述します。

    YahooHonyaku#translateの中で使っている4つのプライベートメソッド、open_top_page, top_page_opened?, post_text, parse_result の中身はあとで実装するとして、現時点での yahoo-honyaku.rb のコードは以下のようになります:

    --------------------------------------------- yahoo-honyaku.rb
    class YahooHonyaku
     def self.translate(mode, text)
       new.translate(mode, text)
     end
    
     def translate(mode, text)
       open_top_page unless top_page_opened?
       html = post_text(mode, text)
       parse_result(html)
     end
    
     private
    
     def open_top_page
       raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
     end
    
     def top_page_opened?
       raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
     end
    
     def post_text(mode, text)
       raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
     end
    
     def parse_result(html)
       raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
     end
    end
    ---------------------------------------------
    


    private 宣言以降の部分はプライベートメソッドの定義になります。ここで前回書いたテストを実行すると:

    $ ruby test-yahoo-honyaku.rb
    (中略)
     1) Error:
    test_s_translate(TestYahooHonyaku):
    NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
       ./yahoo-honyaku.rb:19:in `top_page_opened?'
    (中略)
    2 tests, 0 assertions, 0 failures, 2 errors
    


    となりました。top_page_opened? を実装してくれということなので、次回はそこから取りかかることにします。

    ■ (おまけ) if と unless について

    今回示したYahooHonyaku#translate の1行目は、unless の後置記法を使って書いています。Rubyでは、ifやunlessの(Perlにあるような)後置記法を使うことができます。

     AAA if BBB        #  if     BBB then AAA end  と同じ意味
     AAA unless BBB    #  unless BBB then AAA end  と同じ意味
     AAA if not BBB    #  if not BBB then AAA end  と同じ意味

    好みの問題もありますが、AAA や BBB の部分がシンプルで短いときには読みやすくなります。AAA や BBB の部分が長めだったり複雑な場合には、むしろ読みにくくなるので、普通に if式を使って:

     

    if BBB
       AAA
     end
    


    と書いた方が良いでしょう。if式で、条件節とTHEN節の間を改行する場合は、両者を挟むthenを省略可能です。ifやunlessの後置記法、もしくはif式やunless式において、else節が省略されて居る場合、条件にマッチしなければ結果は nil になります。

    irb(main):001:0> 123 if true
    123
    irb(main):002:0> 123 if false
    nil
    irb(main):003:0> 123 unless true
    nil
    irb(main):004:0> 123 unless false
    123
    


    CやJavaのif文が文であって値を返さないのに対して、Rubyのifやunlessは式として、THEN節またはELSE節のどちらかを評価した値を返します。CやJavaでいうと三項演算子の?に近いかもしれません。

    藤本裕之のプログラミング夜話 #138

     承前……というか、またちょっとモノの見方を変えてみたい。前回「かつてワタシは(いやワタシだけでなく多くのプログラマは)、ユーザがソフトウエアをハードウエアのおまけだと思っていることについて憤っていた」と書いた。いや、このままの表現ではなかったかも知れないがそういう意味のことを書いたんですよ。で、今回はユーザだけでなく誰が見ても「ハードウエアのおまけ」である「バンドルソフト」というものについて考えてみたい。

     およそパッケージ・ソフトを制作、販売しているものにとって、それがターゲットとするマシンに自分のソフトがバンドルされるというのは実にうれしくおいしい話である。……あ、ちょっとこの辺の事情というかカラクリについて知らない人がいるといけないので解説しておくか。あれ、バンドルしてもハードメーカーから入る金は微々たるもんすよ。オレの見聞した話だと、だいたい小売りで1万円くらいのソフトをバンドルして、1台につき数百円の利益があればおいしいくらいのもんだったと思う。……数十円かな? いやとにかく聞いたときびっくり仰天したことは覚えているのだ。

     そんなんでナニが実利やねんロックンロールショーと思うかも知れないが、つうかオレも当時はそう思ったのだが、そういう人はソフトウエア・パッケージ商売についての理解がまだまだ浅いのね。

     そもそもソフトウエアというのはバイナリ商品だから、メーカー出荷時にハードディスクにコピーして貰うだけなら原価(開発費は別ね)はゼロ、メディアと印刷したマニュアルをつけたところで知れている。少々旧い数字で申し訳ないが、オレが以前いた会社で制作・販売してたパッケージ、メディアとマニュアルだけなら300円はかからなかった。メディアはフロッピーで歩留まり悪かったから、CDを使える今はもっと安く済むだろうし…だいたい、今のバンドルソフトってマニュアルもpdfでいいし自前のメディアも要らないよね。つまりバンドルしてもらうための「持ち出し」というのはほぼゼロ。そしておいしいのは「とにかくユーザの目に触れる、一度くらいは使ってみてもらえるかも知れない」ことなのだ。

     なんだかどんどん筆が……キーがかな? あらぬ方向に滑っていくような気がするが、アプリケーション・パッケージ商売で最も難しいのはソフトの開発でもバグフィックスでもない。そのソフトの存在をユーザに知ってもらうことなのである。もちろん、資金潤沢で莫大な広告費をかけられるならコトは簡単だ。だけど普通そんな金はないし、ワタシを含めてプログラマという論理的な
    種族はそもそもそんな金があったら働かない。しかも困ったことにソフトウエアって、斬新で画期的なものほどコトバだけでナニをするものか、ナニが便利か、ナニが楽しいかを理解してもらうのが難しいモンなのね。

     「こう升目に数字を入れてですね、自動的に合計とか平均とかが出せるわけです」
     「そりゃできるでしょ、計算機なんだから。……電卓でだってできるよ」
     「いやその、つまり表を作ってですね」
     「ああ、表組みができるワードプロセッサなの?」
     「そうではなくて……」

     80年代始めに「Visicalc」(AppleII 向けに開発された最初の表計算ソフト)がどんなソフトであるかコトバだけで人に伝えるのはとんでもなく難しかったはずだ。そう言えば当時石田晴久センセが書いた本に「これからはBASICよりもVisicalcだ」てな文言があって、しばらくの間オレはVisicalcのことをプログラミング言語だと思っていたんだよな。

     閑話休題。とにかくハードにバンドルされれば多くの人に「一度は」使ってみてもらえる。そして彼らの……例えば数%の人がそれを日常的に使うようになってくれれば、そこに「バージョンアップ」という大きなチャンスが得られるのだ。これまた知らない人には意外な話かも知れないが、アプリケーション・パッケージ商売で最も楽で効率がいいのはバージョンアップなのである。
    流通マージンが無いうえ、外装も簡略でいい。しかも料金回収を振り込みやクレジットカードにすれば、ほとんど現金商売なのである。バンドルソフトってのはバージョンアップのための「撒き餌」だと言ってもいいくらいなのである。

     と、斯様にいいことずくめにみえた「バンドルソフト」だが、広い目、長い目で見るとそう喜んでばかりもいられないシクミだった。次回はその「弊害」……てのは言い過ぎか。「功罪」の「罪」のほうの話をしてみたい。
                           (以下次回 2008_05_24)

    高橋真人の「プログラミング指南」第136回

    プログラマのためのオブジェクト指向再入門(42)

    〜XcodeによるPowerPlant X入門(25)〜

     こんにちは、高橋真人です。
     さて早速続きですが、「なぜビューのアクティブ状態によって、枠とコントロールの前後関係が入れ替わってしまうのか?」という疑問についてでした。最初に言ってしまうと、この「前後関係が入れ替わる」という私の認識は誤りでした。その辺も含めて、今回は私の考えていった過程をお話しします。

     前回の最後で、私が「コントロールはどこで描かれているのでしょう?」と書いた時に頭の中で考えていたのは、MyViewがPushButtonを経由して生成するボタンコントロールは、便宜上はHIViewとして扱われているが、もともとはControl Managerにおけるコントロールとして実装されていたものなので、プログラムのどこか(Carbonイベントの処理の流れとは)別のところで描かれているのではないか、ということです。
     Control ManagerがControlを描く場合、Draw1Control()というAPIが使われます。このAPIの引数に描画したいコントロールを渡すことでコントロールが描かれるというわけです。そこで、MyViewが内部で利用しているボタンコントロールの場合、HIViewのCarbonイベントによる描画の仕組みが内部的にどこかでこのControl Managerの仕組みを呼び出しているのではないかと私は推理したわけです。
     それでは、この“内部的に”呼び出す部分はいつ呼び出されるのでしょうか? “別のところ”とは言え、呼び出しがMyViewの描画と別のタイミングで行われたらMyViewとの連携が取れません。MyViewに対して描画要求が来たら(当然、これはCarbonイベントの流れでやってきます)、それと連動してボタンコントロールの描画が行われる必要があるわけです。
     そうなると、次に考えるのは、「DoControlDraw()の呼ばれる前なのか、後なのか?」ということです。これは、描画における“前後関係”の話になってきます。後から描かれるものが手前に来るのは自明の理なので、MyViewがアクティブな時にはボタンコントロールは装飾部(MyViewのDoControlDraw()内で描画されている内容を指します)よりも後に描かれるはずですし、逆にアクティブでない時にはボタンコントロールは先に描画されるために、装飾部が手前に来るという理屈になるわけです。
     ボタンコントロールが先(つまり、装飾部よりも下)に描かれる場合、前々回で触れた「イベントの流れを止める」ことを意図してDoControlDraw()でnoErrを返しても、それ以前に描画が完了していれば影響は出ないでしょうから、「MyViewがアクティブでない時には、noErrを返してもボタンは見えるはず」と推理して、早速実験をしてみたわけです。
     実験の結果は私の予想を覆すものでした。MyViewがアクティブでない時にもボタンコントロールは全く表れなかったのです。
     さて、これは一体どうしたものかとしばし考えてみて、ある仮説を思い付きました。それは、そもそも前後関係が入れ替わっているわけではなく、単にボタンが透き通っているために、後ろにある装飾部分が見えているだけなのではないのか? ということです。
     そこで早速実験をしてみました。具体的には、DoControlDraw()の

      

     ::CGContextStrokeRect(inContext, frame);

    の前に以下のコードを追加してみたのです。

      

     ::CGContextSetRGBStrokeColor(inContext, .95, .85, .3, 1.0);
       ::CGContextSetLineWidth(inContext, 5.0);
    


     この状態でプログラムを走らせてみると予想通りでした。MyViewがアクティブでなくなったときにも、ボタンコントロールの中央にあるタイトル部の“Button”という文字は黄色い斜線に隠されることなく見えています。さらに、黄色い部分のうちボタンコントロールと重なっている部分が、そうでない部分に比べて薄く見えることも、よぉーく観察してみると分かります。黄色い線の透明度はゼロにしてありますから、黄色が見えるということはボタンコントロールが透き通っていることを示しているわけです。
     というわけで、MyViewのアクティブ状態によってボタンコントロール部と装飾部の前後関係が入れ替わっているように見えたのは錯覚で、単にボタンの透明度が上がっていただけのことだったのです。
     もっとも、私が予想していたように「アクティブ状態によって描画順が変わってしまう」などということになっていたら、もっと複雑な表示をしたい場合には相当厄介な話になってしまいそうです。ですから、今回判明した事実の方がずっと処理もラクですし、何より筋も通ります。
     ボタンがアクティブでない時に透明になるということを既にご存知だった方にとっては、「何を今さら」という内容だったと思いますが、実は私はこの描画順序が変わるという“事実”に基づいて、今後の連載の進めて行く内容を考えていたために、正直、少なからぬショックを受けております(笑)

     次回からは、気を取り直して、今回得た結果を元にボタンの周囲だけでなく前面にも装飾をする方法を考えて行きたいと思います。

    開発ツールよもやま話   Sharkの概要   高橋 政明

     開発に欠かせないツールはXcodeだけではありません。前回ご紹介したMallocDebugのようなパフォーマンスツールも重要です。
     Mac OS Xはパブリックベータから10.5まで起動速度など性能(パフォーマンス)が目に見えて改善されてきました。もちろんMac本体のハードウェアも高性能になっていますが、少し古い機種でもMac OSをバージョンアップすると起動が早くなったことを体験されたことと思います。

     処理速度の改善ではボトルネックを見つけて、そこを集中的に改善するのが最も効果的です。ボトルネックは計測して見つけ出す事が肝心と言われています。今回ご紹介するsharkはコードのパフォーマンス問題を発見するために利用可能な最も強力なツールのひとつです。

     sharkの公式ドキュメントは次の4つありました。

    Optimizing Your Application with System Trace in Shark 4 2006-01-30
    http://developer.apple.com/tools/performance/optimizingwithsystemtrace.html

    Optimizing Your Application with Shark 4 2004-11-08
    http://developer.apple.com/tools/sharkoptimize.html

    Optimizing with Shark: Big Payoff, Small Effort 2004-01-19
    http://developer.apple.com/tools/shark_optimize.html

    Performance Overview/Doing an Initial Performance Evaluation/
    Using Shark 2006-10-03
    http://developer.apple.com/documentation/Performance/Conceptual/PerformanceOverview/InitialEvaluation/chapter_5_section_5.html

    また、日本語の資料としてはCocoa勉強会の「Cocoa Life」Vol.2に関根延篤氏の「Sharkを使ってみよう」があります。

    どの資料からもsharkが強力で多機能な最適化ツールである事がわかります。

    ■sharkのインストール
     かつてはCHUD Toolsと分類されていましたが10.5のXcode ToolsのインストーラではDeveloper Toolsシステムコンポーネントにチェックをするとインストールされます。
     インストール先は/Developer/Applications/Performance Tools/Shark.appです。サメアイコンなのですぐにわかります。

    ■なぜ「サメ」?
     sharkを起動しHelpメニューからShark Helpを開くと304ページもあるpdf書類「Shark User Guide」が開きます。(ページ数に圧倒されますね)
     最初に『なぜサメなのか?』が書かれていました。

     sharkはパフォーマンスを理解と最適化のためのツールである。
     なぜサメと呼ばれるのか?
     パフォーマンスの最適化はハンターの精神を必要とし、サメ程純粋に獲物を探索する動物はほかにない。
     サメは目的のためにすべての潜在的資源を使う専門家でもある。
     サメという名前は、あなたがコードをチューニングする時に持たなければならない精神と感情を表す。…だそうです。

     sharkの解析機能を使うとコードのどこにパフォーマンスの問題があるのかを明らかにすることができます。結果的に開発者は最も効率よくパフォーマンスを改善できるのです。下手な訳で恐縮ですがOverviewの続きもご紹介します。(Yahoo!翻訳に手伝ってもらいました〔^_^;〕)

     あなたのコードのパフォーマンスを分析するのを助けるため、sharkはシステム全体(アプリケーションと同様にカーネルとドライバ)のプロファイルを可能にする。
     最もシンプルな水準ではsharkはあなたのコードが動作する間、時間がどこで費やされているかプロファイルする。
     ハードウェアとソフトウェアのパフォーマンスイベント例えばキャッシュのミス、仮想メモリの活動、メモリアロケーション、ファンクションコール、インストラクション依存の失速などのプロファイルをつくる。
     この情報はあなたのコードやシステムのどの部分がボトルネックであるかを見るパフォーマンス調整努力の貴重な一歩だ。加えてどこで時間が費やされているかをあなたに示すことに加えて、
     sharkはどうやってコードを改善するかをアドバイスできる。
     sharkは多くの一般的なパフォーマンスの落とし穴を確認でき、視覚的に問題のコストを示す。

     プログラムのどの部分で時間を費やしているのかを明確に示してくれる重要なツールで高性能ですが、コマンドラインでの操作ではなく通常のMacのアプリケーションである点が助かります。
     CPUのキャッシュやバーチャルメモリや機械語レベルの話になるとかなり高度となります。使い手のレベルでより効果を発揮するツールであることもまたまちがいないですね。
     sharkは普段目にする事のないような高度な内容を含む膨大な情報を収集してくれます。その情報をもとに、どこに改善の余地がひそんでいるのかを見つけるのは他でもないプログラマです。

     都合により開発ツールよもやま話は今後不定期の掲載とさせていただきます。

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

     MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
    はそれぞれ各社の商標ならびに登録商標です。
    このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.

  • MOSA Developer News[MOSADeN=モサ伝]第298号

    2008-05-20

    目次

    • 「「Wonderful Server Life」    第70回   田畑 英和
    • 小池邦人のCarbon視点でCocoa探求
    • ターミナルの向こうから      第25回  海上 忍 

    「Wonderful Server Life」  第70回  田畑 英和

      〜Open Directory編〜

     前回は「ディレクトリユーティリティ」を使ってクライアントコンピュータをOpen Directoryのサーバへ接続する方法と、ローカルユーザとネットワークユーザの関連付けについて解説しました。今回はOpen Directoryサーバへの接続時に行われる自動設定およびネットワークユーザとの関連付けについて解説します。

    ◇自動設定
     まず自動設定ですが、Open Directoryのサーバに接続するとクライアントコンピュータのメール/iChat/iCal/VPNに関する自動設定が可能です。このため、あらかじめサーバ側の準備が整っていれば各サービスをすぐに利用できるようになります。具体的にはそれぞれ次のような設定が行われます。

    [メール]
     接続先のOpen Directoryサーバ上でメールサービスが稼働している場合、クライアント上ではメールのアカウント設定が自動的に行われます。このときアカウントの種類はIMAPとして設定され、IMAPの認証にはKerberosを用いることになります(SMTPは認証なし)。アカウントはローカルアカウントと関連付けたサーバ上のネットワークユーザが使用されます。
     なおメールアドレスですが、username@hostname.example.comのようにホスト名も含んだアドレスが設定されます。自動設定されるといっても、運用上問題がないか、どのようなパラメータが設定されているかを事前に一通り確認するようにしましょう。

    ・メールのアカウント設定画面(自動設定)
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#28

    [iChat]
     次にiChatですが、こちらもメールと同様にローカルアカウントと関連付けたネットワークユーザがJabberアカウントとして自動登録され、Kerberos認証が有効になります。つまり、同じサーバに接続しているユーザ同士であればすぐにiChatでのコミュニケーションが可能になります。

    ・iChatのアカウント設定画面(自動設定)
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#24

    [iCal]
     iCalサービスはLeopard Serverの新機能ですが、これもネットワークユーザを使ってアカウントが自動設定されます。認証にはやはりKerberosが利用できるように設定され、LeopardではKerberosを積極的に利用するようになってきたことが分かります。

    ・iCalのアカウント設定画面(自動設定)
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#21

    [VPN]
     VPNはTigerのころは「インターネット接続」を使って設定していましたが、Leopardでは「インターネット接続」が廃止され「システム環境設定」の「ネットワーク」に統合されました。自動設定ではVPNの設定も行われます。ネットワーク関連の設定は基本的にはシステム全体の設定になりますが、VPNはユーザ認証に関する設定をともない、Open Directoryへの接続時にログインしていたユーザでしか設定は有効になりません。他のユーザでもVPN接続を行うには個別に設定する必要があります。VPN接続の認証に使用するユーザはやはり関連付けを行ったネットワークユーザになります。

    ・VPNのアカウント設定画面(自動設定)
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#32

     以上が自動設定の内容になりますが、自動設定を行うかどうかは設定時に選択することができます。また自動設定を行った場合、クライアントコンピュータのネットワーク設定も自動更新され、接続先のサーバがDNSサーバとして追加されます。
    ※サーバが詳細構成の場合、自動設定はできません。

    ◇ネットワークユーザとの関連付け
     ローカルユーザとネットワークユーザの関連付けは「ディレクトリユーティリティ」を使ったOpen Directoryへの接続時に設定できますが、「システム環境設定」を使って後から設定することもできます。
     Open Directoryへ接続中のクライアントコンピュータで「システム環境設定」の「アカウント」を表示すると「サーバアカウント」という設定項目が追加されているのを確認できます。「設定」ボタンをクリックしてローカルユーザのパスワードと関連付けるネットワークユーザのアカウント名およびパスワードを入力するとローカルユーザとネットワークユーザを関連付けることができます。
     「ディレクトリユーティリティ」での関連付けではログイン中のユーザに対する設定しかできませんが「システム環境設定」で関連付ければ任意のローカルユーザに対する設定ができます。さらに「システム環境設定」を使えば関連付けているネットワークユーザを確認したり、ネットワークユーザとの関連付けを解除することもできます。ただしネットワークユーザとの関連付けはサーバが詳細構成の場合は設定できませんのでご注意ください。
     「ディレクトリユーティリティ」を使ってOpen Directoryとの接続を解除した場合もネットワークユーザとの関連付けは解除されます。接続を解除した場合ですが、自動設定された内容はそのまま残るものもありますので注意してください。

    ・サーバアカウントの設定画面
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#2

     それでは次回はネットワークユーザと関連付けたときのユーザ認証や、設定時にアカウント情報がどのように処理されているのかを解説します。
    次回へつづく                             

    小池邦人のCarbon視点でCocoa探求(2008/05/16)

    〜 NSTableViewクラスを試してみる 〜

    前回は、ドキュメントや画像ファイルについて実装すべき処理を列挙してみました。ただし、先んじてウィンドウに画像を表示する仕組みを作らないと、こうしたファイル処理の動作確認は困難です。今回は、登録された画像ファイルの内容をウィンドウに表示する方法について色々と考察してみたいと思います。

    以前の「しんぶんし 2」では、こうした画像表示をCarbon FrameworkのDataBrowserコントロールを利用することで実現していました。用意したカラムの種類は、ファイル名とファイルの種類(OSType)の2種類のみですが、ファイル名の左側に画像アイコン表示を加えていました。しかし、このアイコン表示があまりにも小さいために(笑)一覧をざっと見て必要な画像を見極めるという役割にはあまり適していませんでした。

    CocoaのAppKit FrameworkでCarbonのDataBrowserに準拠するものと言えば、そ
    れはNSTableViewクラスとなります。このクラスから作成されるTableViewコントロールのカラムには、ファイル名(パス名)やファイルタイプだけでなく、画像ファイルから得られたNSImageも表示できますので、CarbonでDataBrowserを実装した時とほとんど同じ状況を再現できます。カラムに表示する画像を通常よりも大きめ(カラムの高さを変更する)にしてやれば、選択の見極めにも有効かもしれません。

    NSTableViewクラスを利用することは、そんなに難しくありません。InterfaceBuilderでウィンドウ上にTableViewを配置して各種アトリビュート設定を行う点は、CarbonのDataBrowserの場合とほとんど同じです。DataBrowserの場合には、コールバックルーチン経由でカラムに表示するデータ(文字列など)を渡します。また、選択カラムが変更された情報や、カラムがダブルクリックされた時のイベントなどもコールバックルーチンで受け取る仕組みが用意されています。例えば、以下の様なソースコードを記述することで、DataBrowserにそうしたコールバックルーチンを設定してやります。

    OSErr setupDataBrowser( ControlRef browser )
    {
       OSErr                    err=1;
       DataBrowserCallbacks    call;
    
       call.version=kDataBrowserLatestCallbacks;
       if( ! InitDataBrowserCallbacks( &call ) )
       {
           call.u.v1.itemDataCallback=setgetDataBrowser; // データの入出力
           call.u.v1.itemNotificationCallback=notifiDataBrowser; // イベント受信
           SetDataBrowserCallbacks( browser,&call ); // コールバックルーチン設定
           err=noErr;
       }
       return( err );
    }
    


    setgetDataBrowserの方が、DataBrowserに表示用のデータを渡す(もしくはBrowser側で編集されたデータを受け取る)コールバックルーチンです。
    notifiDataBrowserの方は、DataBrowser上で発生したイベント情報(マウスクリックによるカラム選択など)を受け取るコールバックルーチンです。DataBrowserのカラムに、パス名やファイルタイプを表示するだけであれば(内容の編集しない)、setgetDataBrowserコールバックルーチンは以下の様な感じとなります。ここでのgetMyData()ルーチンは、指定行番号のデータを得るための自作ルーチンです。

    OSStatus setgetDataBrowser( ControlRef browser,DataBrowserItemID itemID,
             DataBrowserPropertyID property,DataBrowserItemDataRef itemData,
             Boolean changeValue )
    {
       CFStringRef    path,type;
       short            err=noErr;
    
       if( ! changeValue )
       {
           if( itemID )
               getMyData( itemID-1,&path,&type ); // 指定行番号のデータを得る
           switch( property )
           {
               case 'path': //  パス名の表示
    
                   SetDataBrowserItemDataText( itemData,path );
                   break;
    
               case 'type': //  タイプ名の表示
    
                   SetDataBrowserItemDataText( itemData,type );
                   break;
           }
       }
       return( err );
    }
    


    表示対象の切り分けに使われるproperty値(DataBrowserPropertyID)は、Interface Builderで各カラムに対し設定しておいたOSType値です。ちなみに、Interface Builder 3では、DataBrowserの各種設定が非常に設定しづらく改悪されています(涙)何とかして欲しいと思うのですが…今回はとりあえず横に置いといて、notifiDataBrowserコールバックルーチンの例も見てみることにします。

    void noteDataBrowserMosa( ControlRef browser,
               DataBrowserItemID itemID,DataBrowserItemNotification message )
    {
       DataBrowserItemID    item;
       WindowRef            wptr;
    
       wptr=GetControlOwner( browser ); //  ブラウザを配置したウィンドウ
       switch( message )
       {
           case kDataBrowserSelectionSetChanged:
    
               //  ここにカラムが選択された時の処理を記述する
               break;
    
           case kDataBrowserItemDoubleClicked
    
               //  ここにカラムがダブルクリックされた時の処理を記述する
               break;
       }
    }
    


    TableViewを使う時も、これらのコールバックルーチンと同様の役割(完全に同等ではない)を持つメソッドを実装する必要があります。TableViewへのデータ入出力については、dataSource(データソース)として選んだクラスに指定メソッドを実装しておきます。TableViewに対するイベントに対しては、delegate(デリゲート)として選んだクラスに必要なメソッドを実装します。例えば「しんぶんし 3」用の「Cocoa Document-based」プロジェクトであれば、とりえあず、NSDocumentを継承したMyDocumentクラスをdataSourceやdelegateとして指定すれば大丈夫です。

    TableViewのOutlet(アウトレット)として用意されているdataSourceとdelegateのリンク先は、他のアウトレット同様、Interface Builderにより線で接続することで設定します。TableViewを選択し、Inspector(インスペクタ)の「Table View Connection」の「Outlet」に表示されている、dataSourceとdelegateを好みのオブジェクト(青い立方体)へと接続します。
    先ほど言及したMyDocumentであれば「File’s Owner」に接続することになります。どんな目的に対して、どのような種類のdataSourceやdelegateメソッドがあるかについては、技術ドキュメント「Table View Programming Guide」や、ヘッダファイルの「NSTableView.h」を参照してください。まずは、最小限必要なふたつのdataSourceメソッドを紹介しておきます。これらは、表示すべき行の総数を返すメソッドと、その行番号へ表示する内容(オブジェクト)を返すメソッドです。

    - (int)numberOfRowsInTableView:(NSTableView *)aTableView
    {
       return [array count];   // テーブルが表示する全行数を返す
    }
    
    (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTbableColumn row:(int)rowIndex
    {
       ImageFile    *image;
       NSString    *ident;
    
       ident=[aTbableColumn identifier];    // 表示対象となるカラムのキーを得る
       image=[list objectAtIndex:rowIndex]; //  配列から対象行のオブジェクトを得る
       return [image valueForKey:ident];    //  キーから得た表示用 プロパティを渡す
    }
    


    続いて、delegateメソッドであるtableViewSelectionIsChanging:を紹介しておきます。このメソッドは、ユーザにより選択カラムが変更された時に呼び出されます。delegateメソッドの方は、そのアプリケーションがどんな機能を実現したいかにより、実装すべき種類が大きく変わります。

    - (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
    {
       long    row;
    
       row=[table selectedRow];  //  選択された行番号(ゼロ以上)
       if( row>=0 )
       {
           //  ここには選択カラムの内容を利用する処理を記述する
           //  例えば別のTextFieldに選択内容を代入するなど...
       }
    }
    


    今回は、データ表示にdataSourceメソッドを使う方法を解説しましたが、TableViewでデータ表示する場合には、Mac OS X 10.3から導入された「Cocoa Binding」の仕組みを使うと便利です。この場合には、上記のようなdataSourceメソッドを用意することなく、データ(オブジェクト)のプロパティ値(インスタンス変数)をカラムに表示できます。 ただし、こちらの仕組みについては説明すべき技術項目が膨大ですので、機会があれば、別途「Cocoa Binding」に特化した解説を行いたいと思います。

    さて、試しにTableViewを利用して領域を広げたカラムに画像(NSImage)を表示してみると、それなりに使えることが分かりました。しかし、画像に付随したパス名などが横の矩形枠の中に小さく表示され、間の抜けた感じが拭えません。やはり、TableViewは画像中心の一覧表示には向いているとは言い難いようです。画像と、それに付随する情報を美しく一覧表示するのには、何か別の「ブラウザ機能」を採用した方が良さそうです。

    そこで次回は、TableViewの代わりに、Mac OS X 10.5から採用された「ImageKit」のIKImageBrowserView(画像ブラウザ機能)クラスを利用してみます。ImageBrowserと名称が付いているわけですから、今回の様な役割にはもってこいだと予想されますが、さてどうなるでしょうか?
    つづく
                                    

    ターミナルの向こうから      第25回  海上 忍

    〜  番外編:LeopardのRubyを入れ替える(irbで日本語を!)〜

     前回は、Rubyを利用した日本語文字コードの変換について説明しました。今回は、RubyCocoaを利用して多少見栄えのいいものを……と考えていましたが、途中から脱線気味の内容となっています。とはいえ、irbで日本語入力できない問題が解決しますので、Leopardユーザには有用かと思います。

    ・まずは「CocoaRepl」の準備
     今回の作業には、Developer Tools付属の「CocoaRepl」を使用します。RubyCocoaを活用したプログラミングの体系的な話については、藤本尚邦さんの連載「りんご味Ruby」のバックナンバー、またはマイコミジャーナルの特集(http://journal.mycom.co.jp/articles/2007/07/27/WelcomeToRubyCocoa/index.html)が参考になることでしょう。
     CocoaReplはソースコードのみ収録されているため、利用に際してはコンパイルが必要です。Developer→Examples→Ruby→RubyCocoa以下にある「CocoaRepl」フォルダを開き、Xcodeのプロジェクトファイル(CocoaRepl.xcodeproj)をオープン、ツールバー上の「ビルドして進行」ボタンをクリックすれば、ビルドからCocoaReplの起動までを処理できます。
     このCocoaReplは、RubyCocoaの機能を対話形式で利用できる、藤本さんの手によるRubyCocoaのインタープリタです。コンパイル・リンクなしに実行結果をその場で確認できるため、プロトタイプにも至らない”叩き台”のようなコードを試すには最適なツールといえます。文字媒体のMOSA伝ではお伝えしにくいUIデザインのような要素を省いた、文字変換ツールのコア部分をこのCocoaReplで作成してみよう、というわけです。

    ・CocoaReplの基本的な使い方
     CocoaReplの画面は、上段(スクラッチビュー)がコード入力用、下段が結果出力用という設定が基本です。スクラッチビューにRubyのコードを入力し、下段で確認しつつ作業を進めることになりますが、コードを実行するときには行末で[command]-[enter]を押すこと(またはメニューバーから[Ruby]→[Eval Line]を実行)に注意してください。単純に[enter]を押すだけでは、コードは実行されません。
     それでは、以下のコードを1行ずつ(各行末で[command]-[enter])実行してください。最後の行を実行したとき、変数mojiに格納した文字列の文字コード(UTF-8)を意味する「6」が、画面下段に戻り値として表示されるはずです。

    - - - - -
    require 'kconv'
    moji = "日本語"
    Kconv.guess(moji)
    - - - - -
    


    ・Rubyのインストールでirbが日本語レディに
     前回から気になっていたのですが、Leopardに付属のirbは、日本語入力に対応しない困った仕様です。その原因はRuby / irbサイドにあるのではなく、文字入力用ライブラリ「GNU readline」が適切に使用されていないことが原因です。CocoaReplもよくできていますが、この際Ruby / irbを入れ替えてしまったほうがいいのでは、と最新のRubyをインストールすることにしました。以下のコマンドラインをターミナルにコピー&ペーストするだけで作業は完了するはずですので、よろしければお使いください。

    - - - - -
    ・GNU Readline 5.2
    $ curl -O http://ftp.iij.ad.jp/pub/GNU/readline/readline-5.2.tar.gz
    $ tar xzf readline-5.2.tar.gz
    $ cd readline-5.2
    $ ./configure --disable-static
    $ perl -i -pe "s/darwin [78 ]/darwin [789 ]/g" support/shobj-conf
    $ ./configure --disable-static
    $ make
    $ sudo make install
    - - - - -
    


    - - - - -
    ・Ruby 1.8.6-p114
    $ curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p114.tar.bz2
    $ tar xjf ruby-1.8.6-p114.tar.bz2
    $ cd ruby-1.8.6-p114
    $ ./configure --with-readline-dir=/usr/local --enable-shared
    $ make
    $ sudo make install
    - - - - -

     上記の手順に従った場合、Rubyは/usr/localディレクトリ以下にインストールされるので、それに対応したRubyCocoaも必要です。RubyCocoaのソースコードは、こちら(http://rubycocoa.sourceforge.net/)から入手してください。脱線ついでに、次回もreadlineおよびirb周辺のトピックを取り上げたいと思います。

    - - - - -
    $ tar xzf RubyCocoa-0.13.2.tgz
    $ cd RubyCocoa-0.13.2
    $ /usr/local/bin/ruby install.rb config --prefix=/usr/local
    $ /usr/local/bin/ruby install.rb setup
    $ sudo /usr/local/bin/ruby install.rb install
    - - - - -
    

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

     MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
    はそれぞれ各社の商標ならびに登録商標です。
    このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.

  • MOSA Developer News[MOSADeN=モサ伝]第297号

    2008-05-13

    目次

    • りんご味Ruby         第24回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #137
    • 高橋真人の「プログラミング指南」  第135回

    りんご味Ruby   第24回  藤本 尚邦

    前回は、実装すべきことを思い出すための手段として、組み込みメソッドのraiseを使って例外を発生させました。自分用のちょっとしたツールを作るくらいならこれで十分かもしれないのですが、プログラムをライブラリ的に使い回すつもりがある場合には、実装すべき機能が実装されていて、それが期待通りに動いているかどうかをチェックするためのテストを書いておくとより良いでしょう。

    YahooHonyakuで実装すべき機能は、クラスメソッドのYahooHonyaku.translateおよびインスタンスメソッドのYahooHonyaku#translate です。どちらも、テキストを入力して得られた結果が、Yahoo翻訳で実行したときの結果と一致していれば、期待通りに動いているといえます。

    ■ Test::Unitを使う

    Rubyで使うことのできるテスト用のライブラリはいくつかあるのですが、ここではRubyに標準添付されている単体テスト用ライブラリTest::Unitを使ってテストを書いてみます。

    --------------------------------------------- test-yahoo-honyaku.rb
    require 'test/unit'
    require 'yahoo-honyaku'
    
    class TestYahooHonyaku < Test::Unit::TestCase
     # インスタンスメソッド YahooHonyaku#translate のテスト
     def test_translate
       translator = YahooHonyaku.new
       assert_equal("Good morning",
                    translator.translate(:je, "おはよう"),
                    "和英翻訳の動作確認")
       assert_equal("おはよう",
                    translator.translate(:ej, "Good morning"),
                    "英和翻訳の動作確認")
     end
    
     # クラスメソッド YahooHonyaku.translate のテスト
     def test_s_translate
       assert_equal("Good morning",
                    YahooHonyaku.translate(:je, "おはよう"),
                    "和英翻訳の動作確認")
       assert_equal("おはよう",
                    YahooHonyaku.translate(:ej, "Good morning"),
                    "英和翻訳の動作確認")
     end
    end
    ---------------------------------------------
    


    Test::Unit では、Test::Unit::TestCaseの派生クラスを作り、その中にテストを記述していきます。テストは「test_」で始まる名前のインスタンスメソッドとして定義します。テストの中に、「assert」で始まる名前の各種assertメソッドを使って、テストの詳細を記述します。上のテストで使っているassert_equal の場合、引数は:

    第1引数: 期待される結果
    第2引数: 実行結果
    第3引数: メッセージ文字列(省力可能)

    となります。ではテストを実行してみましょう。上のテストプログラムをtest-yahoo-honyaku.rb という名前で yahoo-honyaku.rb と同じディレクトリにファイルに保存して以下のように実行してください。

     $ ruby test-yahoo-honyaku.rb
     (中略)
       1) Error:
     test_s_translate(TestYahooHonyaku):
     NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
         ./yahoo-honyaku.rb:14:in `_eid_'
         ./yahoo-honyaku.rb:7:in `translate'
         ./yahoo-honyaku.rb:3:in `translate'
         test-yahoo-honyaku.rb:17:in `test_s_translate'
    
       2) Error:
     test_translate(TestYahooHonyaku):
     NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
         ./yahoo-honyaku.rb:14:in `_eid_'
         ./yahoo-honyaku.rb:7:in `translate'
         test-yahoo-honyaku.rb:8:in `test_translate'
    
     2 tests, 0 assertions, 0 failures, 2 errors
    


    このような結果になりました。テストの実行中にエラー(例外)が発生した場合には、errorとしてカウントされます。前回までに書いたtranslateメソッドは、呼び出すとエラーが発生する(raiseを使って自ら発生させている)ので、2つのテスト(test_translateおよびtest_s_translate)のそれぞれがerrorとしてカウントされたのです。

    ここで、Test::Unitの動作を示すために、YahooHonyaku#translate をちょっと変更してみましょう。例外を発生させるかわりに、実装はされたもののバグがあるという意味で、間違った文字列を返すようにします。

     def translate(mode, text)
       "「#{text}」が翻訳できませんでした!"
     end
    


    この変更をくわえた上で test-yahoo-honyaku.rb を実行すると以下のようにな
    ります:

     

    $ ruby -Ku test-yahoo-honyaku.rb
     (中略)
       1) Failure:
     test_s_translate(TestYahooHonyaku) [test-yahoo-honyaku.rb:18]:
     和英翻訳の動作確認.
     <"Good morning"> expected but was
     <"「おはよう」が翻訳できませんでした!">.
    
       2) Failure:
     test_translate(TestYahooHonyaku) [test-yahoo-honyaku.rb:8]:
     和英翻訳の動作確認.
     <"Good morning"> expected but was
     <"「おはよう」が翻訳できませんでした!">.
    
     2 tests, 2 assertions, 2 failures, 0 errors

    今度は、2つのerrorではなくて、2つのfailureがカウントされました。それぞれのテストでは、(assert_equalによって)期待と違う結果だったことがわかるメッセージが出力されているので、何が問題なのか・何を直すべきなのかが見つけやすくなります。

    (注) YahooHonyaku#translate は元に戻しておいてください。

    今回は、assert_equalしか紹介できませんでしたが、他にもいろいろなassertメソッドがあります。例えば、assert_raiseというメソッドは:

     assert_raise(NotImplementedError) { ... }
    


    のようにブロックを渡すことができます。ブロック実行中に指定されたエラー(この場合NotImplementedError)が発生すればOKという意味になります。assertメソッドなどを含めて、Test::Unitの詳細については、Rubyリファレンスマニュアル(日本語)をご覧ください。

    ■ [おまけ] RSpec

    Test::Unitは、RubyプログラミングにおいてDSL的手法がまだあまり浸透していないころに作られたライブラリで、大ざっぱに言うとJavaのJUnitをそのままRubyに持ち込んだという感じでした。その後、RubyプログラミングのDSL手法を効果的に使ったRSpecというテスト用ライブラリが登場しています。

    --------------------------------------------- Rspecによる仕様の記述例
     # bowling_spec.rb
     require 'bowling'
    
     describe Bowling do
       before(:each) do
         @bowling = Bowling.new
       end
    
       it "should score 0 for gutter game" do
         20.times { @bowling.hit(0) }
         @bowling.score.should == 0
       end
     end
    ---------------------------------------------
    


    残念ながら私はまだ使ったことがないのですが、Rubyの特徴を活かしたおもしろそうなライブラリです。

    ■ 参考URL

    Rubyリファレンスマニュアル(日本語)のTest::Unit
    http://www.ruby-lang.org/ja/man/html/Test_Unit.html

    RSpec
    http://rspec.info/

    藤本裕之のプログラミング夜話 #137

     ずいぶん間が空いてしまったが、元気でしたかとかGWはいかがお過ごしでしたかとかじつはワタシは数年ぶりに松本へ遊びに行ったんですよとかそういう一方的おあいそは省いて話を続ける。……どこまで行ったっけ? そうそう。「コンピュータ・ソフトウエア」とそれ以外の「ソフトウエア」すなわち文学とか音楽とかとの決定的な違いは「ハードウエアから独立していない」ってことだ、ということころまでだった。

     ワタシがこの仕事を始めた1980年代の始め頃、業界ではよく「ユーザはソフトウエアをハードウエアのおまけだと思っている」てなことが、ある種のフンガイを伴って言われていた。いや、他人のせいにしてはいけませんね、ワタシ自身、訳も分からずヒトの受け売りでそういうことを言ってたような気がする。

     当時はまだパソコンというものが一般的ではなく(オレだって持ってなかった)、ここで言うハードウエアというのはもっと大げさなミニコンクラスの(若いヒトは「?」と思うかも知れぬが、ミニコンというパソコンより大っきなコンピュータがあったのだ)機械のことで、それを買うのはつまりそれで動作する業務用ソフトウエアをコミで買うことであったから、別にユーザの認識が間違ってたわけではない。

     それどころか営業に言わせれば、ユーザの多く(今でもそういうヒトはいるんだけど)にとって、自分が金を払うのはこの、でっかくて中に部品がぎっしり詰まり、うんうん唸りながら動くので特別に冷房をつけっぱなしにしておく「マシンルーム」なるものまで要求する贅沢な機械なんであって、営業に連れられてきた頭ぼさぼさでネクタイもちゃんと締められないような若いの(というのは別に若き日のオレのことではありません)が機械に差し込んでカチャカチャ言わせてる封筒入りのソノシートみたいなものに記録された「ナニカ」ではなかった。

     早い話、仕入れ値300万のハードウエアに、400万かけて業務アプリを載せ、利益100万乗っけて800万で売る場合(乱暴な話だがタトエだからね)、ユーザへの見積書にハード350万、ソフト450万と書いちゃうとペケで、ハード700万、ソフト100万なら納得してもらえるみたいな状況だったんですわ。こないだ観た映画「ゲゲゲの鬼太郎」の中で井上真央ちゃんがウェンツ瑛士演じる鬼太郎に「私、目で見て触れるものしか信じないの」と言うシーンがあったけど、あんな感じかしら。

     で、その井上真央ちゃん……つうか役の上では実花ちゃんとか言うんだが、にちょっとホの字である鬼太郎は自分だけでなく自分の属する「妖怪」というものの存在を彼女に認めてもらおうと奮闘する。当時の我々も、自分たちが作るソフトウエアというものを、ハードウエアから独立した一個の商品たらしめんと、粉骨砕身……とまではいかないかも知れないがそれなりに努力もし、また時代の趨勢がそっちに向かっていくのは水が高いほうから低いほうへ流れるがごとき歴史の必然である、と思ってしまったんである。今思えば。

     しかるに、だ。前回まで観てきたように、コンピュータ・ソフトウエアというものは最終的にハードウエアから自由になれない。経済的な問題に立ち戻った表現をすれば、ハードウエアの更新に順応しなければソフトウエアは商品価値を失ってしまうのである。つまりね、悔しいけどある意味においてソフトウエアはやっぱりハードウエアの「おまけ」なんであってそこにはけして乗り越えられない壁があったのだ。しかもそれはそこにずっとあったのにオレたちはそれをまるで妖怪のごとく「見たくないから見てこなかった」んぢゃないかと。

     かつて、マルクス=エンゲルスを読んで社会主義から共産主義への移項こそが歴史の必然だと信じた人々が味わったのと同じ蹉跌を、ここに来て我々も目の当たりにしているわけだ……と言ったらすげぇ大げさに聞こえるだろうけれども、この……夢も希望もないというか身も蓋もないというか事実の上にしっかりと視座を据えないと、ソフトウエア産業の今後を展望することは難しい、とまず思うのである。
                           (以下次回 2008_05_10)

    高橋真人の「プログラミング指南」第135回

    プログラマのためのオブジェクト指向再入門(41)

    〜XcodeによるPowerPlant X入門(24)〜

     こんにちは、高橋真人です。
     連休の関係で少し間が開いてしまいましたが、早速続きを始めます。
     さて、前回の最後で投げかけておいた質問の答えですが、お分かりになったでしょうか? 正解をお教えする前に、プログラムにさらにちょっとだけ手を加えてみます。MyView.cpのDoControlDraw()関数の、今回のプログラムで加えた

     

      ::CGContextStrokeRect(inContext, frame);
    


    のあとに以下を加えてください。

      

     ::CGContextMoveToPoint(inContext, frame.origin.x, frame.origin.y);
       ::CGContextAddLineToPoint(inContext,
                                 frame.origin.x + frame.size.width,
                                 frame.origin.y + frame.size.height);
       ::CGContextStrokePath(inContext);
    


     詳細の説明は省きますが、要はCGContextStrokeRect()で描いた矩形枠に単に斜線を追加しているだけです。
     それでは早速動かしてみてください。加えた斜線は見えましたか?
     「うーん、よく見えない。よぉーく見てみると、ビューの隅にチラっと...」。もしくはLeopard上で動かしている方は、ボタンが真四角なために加えた斜線が全く見えないかもしれません。*注
     それでは、開いたウインドウをディアクティベート、つまりアクティブでない状態にしてみてください。アプリケーションを別のものに切り替えてしまっても構わないのですが、そうすると他のアプリのウインドウに覆われてしまって肝心のビューがよく見えない場合もあるかもしれませんので、ウインドウをさらにもう一つ新規に作ってみるのがいいでしょう。
     表示されている2つのウインドウのうち、アクティブでない方にあるボタンの上に斜線が引かれているのが今度はご覧になれると思います。これが、今回のプログラムの狙いです。
     斜めの線を引いたのは、あくまでも前回作った矩形枠がボタン(コントロール)に対して前後関係がどうなっているのかを分かりやすくするためです。それを踏まえて改めて観察してみると、矩形枠自体もビューのアクティブ状態によってボタンのコントロールとの前後関係が入れ替わることが分かると思います。

     ちなみに、ここで問われている“アクティブ状態”は、ウインドウのものではなくて、このビュー(MyView)側の問題です。その証拠に、ウインドウがアクティブなままで、このボタンだけをディアクティベートしても状況は同じになります。
     試してみたい方は、どこか適切な場所でMyViewに対してSetEnabled(PPx::enabled_No)を呼んでやれば確認できます。
    (PPx::enabled_Noは、falseの別名)
     入れる場所は、MyApplication.cpのDoSpecificCommand()の中で、ビューを生成した後であればどこでも構わないでしょう。試しに、ウインドウを表示する直前に入れてみると以下のような感じになります。

      

     view->SetEnabled(PPx::enabled_No);  // ここが追加したコード
    
       ::RepositionWindow(...);            // 引数は省略
       theWindow->Show();
    


     さて、前回の最後に提示した問題点である“ちょっと困ったこと”とは、このことを言っていたのです。つまり、PPx::PushButtonにサブクラスで視覚的な装飾を加えてみたくても、ビューのアクティブ状態によって見え方が変わってしまう(前後関係をコントロールできない)ということです。
     それでは、何でビューのアクティブ状態によって、枠とコントロールの前後関係が入れ替わってしまうのでしょうか?
     MyViewの実装コードを観察していただけば分かりますが、このビューの描画処理部分(DoControlDraw())には、コントロールの描画処理は記述されていません。それなら、コントロールはどこで描かれているのでしょう?
     ところで、MyViewの継承元であるPushButtonには、DoControlDraw()そのものがないことは、前回も触れた通りです。だとしたら、一体どうやってボタンのコントロール(のイメージ)が見えているのでしょうか?
     この辺は、PPxにおけるViewの実装の仕組みに大きく関わってくることですので、回を改めて、次回に少し突っ込んで調べてみたいと思います。

    注:前回、「実行環境によってボタンの見た目が違う場合がある」と申しました。実は、Leopardの特定の環境ではボタンが通常の角丸のものではなく、真四角の、いわゆるベベルボタンになってしまう現象が起きていたのです。ところが、今回試したところまたボタンは従来の角丸に戻っていました。少し調べてみたところ、ボタンの高さを22ピクセル以内に納めていれば角丸のボタンになり、それを23ピクセル以上だとベベルボタンになります。ただ、以前試した時にはこのようなことにはなりませんでした。Mac OS Xのバージョンのみならず、Xcodeなどとの組み合わせに関しても細かく実験してみないと、真相は見えてこないかもしれません。

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

     MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
    はそれぞれ各社の商標ならびに登録商標です。
    このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.