Cocoaでいこう! Macらしく 第27回
Yoshiki(DreamField)
この記事は、MOSAが発行するデベロッパ向けのデジタルマガジンMOSADeN 第88号(2003年11月11日発行)に掲載された記事です。2〜3ヶ月遅れで、ここに掲載して行きます。

TinyViewの動作確認をしていて、うっかり「File」メニューから「Save」を選んでしまいそうになったことは無いでしょうか。今まで、必ず「Save As ...」を選ぶようにと注意して来ましたが、それは、「Save」を選ぶと開いているファイルに上書きしてしまうので、JPEGの場合は毎回圧縮をしてしまい、どんどん画質が劣化するからです。そもそも編集が出来ないソフトですから、「Save」というメニュー自体意味がありません。その他にも、選んでも何も実行されないメニューもあります。今までは開発途中のこともあり、手を付けて来ませんでしたが、Macのソフトたるもの、意味が無いメニューが選択可能ではいけないでしょう。そこで今回は、使用出来ないメニューは選択出来ないようにします。

いらないメニューを外してしまおう

まずは、nibファイルを編集してしまう方法です。この方法はコードを書く必要がありませんので簡単です。いつも通りにTinyView.pbproj(もしくはTinyView.xcode)を開いて下さい。そして、MainMenu.nibをダブルクリックして、Interface Builderを開いて下さい。開いたらnibファイルウィンドウの中にあるMainMenuをダブルクリックして、MainMenuを表示させて下さい(fig.01)。

[fig.01] MainMenuを表示させる

メニューを選んで行くと、ちゃんとプルダウンしますし、サブメニューも表示されます。それぞれのメニューを選択してからダブルクリックすると、表記されている文字を書き換えることも出来ます。その他色々なことが出来ますが、それを説明しているとそれだけでボリュームが膨大になってしまいますので、今回は削除だけを行うことにします。

まずは「Edit」メニューを選んでプルダウンメニューを表示させてみて下さい(fig.02)。

[fig.02] 初期状態のEditメニュー

TinyViewは画像を表示するソフトですから、少なくとも「Find」や「Spelling」は必要無いでしょう。そこでこの二つの項目を削除することにします。「Find」を選んで、Interface Builderの「Edit」メニューから「Delete」を選択して下さい(fig.03)。単に「delete」キーを押してもかまいません。

[fig.03] 不要なメニューを選択して削除する

これで「Find」メニューは消えたはずです。同様の操作で「Spelling」と、「Select All」のすぐ下のセパレータ(メニューの区切りです)も削除してしまって下さい。これで「Edit」メニューは、「Copy」や「Paste」等、編集に必要なものだけになりました(fig.04)。

[fig.04] 不要なものを削った後のEditメニュー

もっとも、編集が出来ないTinyViewにとっては、これも必要ありません。と言うより、「Edit」メニューそのものが必要無いでしょう。ですが将来、環境設定を作った時に、そこで必要になるかもしれません。ですから、とりあえずはこれは残しておきましょう。

実を言えば、「Edit」メニューを削らなかった理由はもう一つあります。昔からMacを使っている私にとって、「Edit」メニューを削るという行為は、抵抗があるからです。その昔、まだMacintoshが複数のアプリを同時に使用出来なかった頃、アプリと同時に使えるちょっとしたプログラムがありました。これをデスクアクセサリと言います。その頃のアプリは、たとえ自分では使うことが無くても、デスクアクセサリのために「Edit」メニューを用意しておくという規約がありました。もちろん、現在はそんな物必要ありません。ですが、「File」メニューの隣に「Edit」メニューが無いと、どうも違和感を感じてしまいます。このため、どうしてもこれを削ることに抵抗を覚えてしまうのです。

さて、次に「File」メニューを見てみましょう(fig.05)。

[fig.05] 初期状態のFileメニュー

編集が出来ないTinyViewにとって、「Save」と「Revert」は必要ありません。現在は印刷も出来ませんから、「Page Setup...」と「Print...」も必要ありません。ですが、今回はこれは残しておいて、プログラムで選択出来ないようにすることにします。

プログラムでメニューを選択出来ないようにしよう

TinyViewを実行して、「File」メニューを選んでみて下さい。何も画像を開いて無い状態では、「New」、「Open...」、「Open Recent」以外は選べなくなっています(fig.06)。

[fig.06] ドキュメントを開いていない時のFileメニュー

画像を開いていなければ、閉じることも保存も印刷も出来ませんから、これは妥当な動作であると言えます。画像を開くと、その他のメニューも選べるようになります(fig.07)。つまり、ここまではフレームワークで制御されています。

[fig.07] ドキュメントを開いている時のFileメニュー

ドキュメントを開いた後に選択出来るようになった保存や印刷等の機能。これが可能かどうかを知っているのはそのドキュメント自身です。従いまして、このメニューの制御は、MyDocumentクラスに組み込むことになります。MyDocumentクラスのスーパークラスである、NSDocumentのリファレンスを見て下さい。validateMenuItem:というメソッドがあります。この説明を読むと、渡されたメニューアイテムが使えるかどうかをYES、NOで返せば良いことが分かります。そこで、これをオーバライドします。MyDocument.mに、次のメソッドを加えて下さい。

- (BOOL)validateMenuItem:(NSMenuItem *)anItem
{
    if( [[anItem title] isEqualToString:NSLocalizedString(@"Save", @"")] == YES){
        return NO;
    }
    if( [[anItem title] isEqualToString:NSLocalizedString(@"Revert", @"")] == YES){
        return NO;
    }
    if( [[anItem title] isEqualToString:NSLocalizedString(@"Page Setup...", @"")] == YES){
        return NO;
    }
    if( [[anItem title] isEqualToString:NSLocalizedString(@"Print...", @"")] == YES){
        return NO;
    }
    return  [super validateMenuItem:anItem];
}

一つずつ見て行きましょう。

    [anItem title] 

これは、渡されたメニューアイテムからタイトルを取り出しています。

    [[anItem title] isEqualToString:NSLocalizedString(@"Save", @"")]  

そして、取り出したタイトルとNSLocalizedString(@"Save", @"")を比較しています。NSLocalizedString()は、ここでは詳しく説明をしませんが、将来ローカライズをするのなら必要な関数です。TinyViewは英語メニューしか持っていませんから、この関数は単純に@"Save"を返しています(今はそういうものだと思ってください)。つまり、渡って来たメニューアイテムのタイトルと@"Save"が等しいかどうかを比べています。

    if( [[anItem title] isEqualToString:NSLocalizedString(@"Save", @"")] == YES){
        return NO;
    } 

そして等しければNOを返しています。もし、その時々の条件によって「Save」メニューが使えたり使えなかったりする場合は、ここで判断してYESかNOを返すことになりますが、今回は常に使用できないので、単純にNOを返します。

この後、順次「Revert」、「Page Setup...」、「Print...」と比べて行き、等しい時にはNOを返し、どれにも該当しなければ、スーパークラスに処理を引き継いで判断をしてもらっています。この様にして、各メニューの選択可能、不可能を制御することができます。

それでは、ビルドして実行し、適当な画像を開いてから、「File」メニューを開いてみて下さい。使用出来ないメニューは選択できなくなっているはずです(fig.08)。

[fig.08] 使用できない項目は選択できなくなったFileメニュー

前頁目次次頁