あまつぶ

9.10【初期設定はXMLで・その2】

 まず、前回のソースの補足から。gPrefsは、初期設定の内容を保存するグローバル変数で、RGBColor型のlastUsedColor、short(いや、正確にはSInt16)型のtagKind、そしてBoolean型のtagCapitalをメンバに持つ。dictは、CFMutableDictionaryRef型、numはCFNumberRef型のローカル変数だ。
 RGBColorの保存では、それぞれのメンバがUInt16なので、SInt16として保存すると0x7FFFより大きな数値の場合はマイナスになってしまう(たぶん)。そこで、SInt32型のtempに一度代入してから保存することにした。

 さて、今回は読み込み。実は、この読み込みルーチンを作る時にしょうもない勘違いでしばらく悩んでしまった。前回の書き込みについては、勘でソースを書いたら一発で成功したのだが、読み込みルーチンを作ったら、なぜかうまく動いていた書き込みルーチンでエラーが発生するようになってしまったのだ。原因は単純なことだったが、ひょっとしたら同じことで悩む人がいるかも知れないので、そのあたりに触れながら書いてみようと思う。
 初期設定ファイルから読み込むには、CFPreferencesCopyAppValue()というAPIを使う。数値やブール値を読み込むためのものとしてCFPreferencesGetAppIntegerValue()やCFPreferencesGetAppBooleanValue()というものも用意されているが、ここではCFPreferencesCopyAppValue()のみを使うことにする。読み込んだデータは、数値であればCFNumberGetValue()で、ブール値であればCFBooleanGetValue()でもとの値に戻す。Dictionaryではもう少し複雑で、まずCFDictionaryGetValueで値を得てから、CFNumberGetValue()などを使うという流れになる。Dictionaryの中にDictionaryが入っている場合には、必要なだけ順次実行することになる。
 さて、ソースの掲載。前回書き込んだデータを読み込むためのもの。

/* last used color */
dict = CFPreferencesCopyAppValue(CFSTR(
"Last used color"),
    kCFPreferencesCurrentApplication);
if (dict)
{
  num = CFDictionaryGetValue(dict,CFSTR(
"Red"));
  
if (num)
  {
    CFNumberGetValue(num,kCFNumberSInt32Type,&temp);
    
gPrefs.lastUsedColor.red = temp;
  }
  num = CFDictionaryGetValue(dict,CFSTR(
"Green"));
  
if (num)
  {
    CFNumberGetValue(num,kCFNumberSInt32Type,&temp);
    
gPrefs.lastUsedColor.green = temp;
  }
  num = CFDictionaryGetValue(dict,CFSTR(
"Blue"));
  
if (num)
  {
    CFNumberGetValue(num,kCFNumberSInt32Type,&temp);
    
gPrefs.lastUsedColor.blue = temp;
  }
  CFRelease(dict);
}

/* tag type */
num = CFPreferencesCopyAppValue(CFSTR(
"Tag type"),
    kCFPreferencesCurrentApplication);
if (num)
{
  CFNumberGetValue(num,kCFNumberSInt16Type,&
gPrefs.tagKind);
  CFRelease(num);
}

/* tag use capitals */
bool = CFPreferencesCopyAppValue(CFSTR(
"Tag use capitals"),
    kCFPreferencesCurrentApplication);
if (bool)
{
  
gPrefs.tagCapital = CFBooleanGetValue(bool);
  CFRelease(bool);
}


dictはCFDictionaryRef型、numはCFNumberRef型、boolはCFBooleanRef型のローカル変数。tempは、保存の時と同様、SInt32型で、UInt16に保存するために一時的に使う。
 まず、Last used colorはDictionaryなので、一度読み込んだ後に中の値を取り出していく。具体的には見てのとおりなのだが、ここで気をつけないといけないのは、CFPreferencesCopyAppValue()で得たdictはCFRelease()で解放する必要があるが、CFNumberGetValue()で得たnumは解放する必要がないということ。解放し忘れてもメモリリークが起こるくらいですむが、間違って解放してしまうと、後のルーチンでエラーが起こる場合がある(実際、それでしばらく悩んだ)。なんだかわかりにくいが、CreateやCopyは解放が必要、Getは不要ということだろうか。
 数値やブール値はいたって簡単だ。
 一応、初期設定ファイルが変更されている可能性を考慮して、得られた値がNULLでないことを確認するようにしてみた。NULLの場合は無視して、保存の時に書き込むことにしておけば問題ないと思う。関係ないキーが追加されている可能性もあるが、そこまでは考えなくていいかなと。

 最後に、ホットキーの登録の話。RegisterEventHotKey()で登録したホットキーは、UnregisterEventHotKey()で登録解除すれば使用できなくなるはずなのだが、登録解除した状態でホットキーを押すと、なぜかエラーで終了してしまった。イベントハンドラをはずしてみてもだめで、結局苦肉の策で、新しいホットキーを登録する時に以前のホットキーを登録解除することにした。そのせいで、ホットキーの「変更」ボタンを押すと、以前のホットキーを押しても何も反応せず、もとに戻すことができない(汗)。その場合は、「中止」ボタンを押してキャンセルしてほしい。
 ホットキーの入力待ちでは、通常のショートカットが有効なため、例えば、Cmd+Qを押すとアプリケーションが終了してしまう(笑)。これを防ぐ手立てはいろいろあると思うが、あまりしぼる必要はないと思い、そのままにしてある。また、ダイアログの「OK」や「Cancel」をクリックすることもできる。これらが押されると、ホットキーの変更は自動的にキャンセルされる仕組みだ。アプリケーションの切り替えも問題なく行われる。バックグラウンドにいる間は、キー入力には反応しない。と、まあ、こんなところ。

 次回の予定は未定。WebColorXの開発でしばらくX環境にいたことで、Phutの問題点がいろいろと浮かび上がってきた。そのあたりについて対処していきたいと思う。

September 9, 2001 ↑ September index → September 16, 2001