実際の作業
コンパイラ(CodeWarrior ver7)の設定
ここでは、OS 9でもOSXでも起動するCFM形式のアプリを作る。したがって、プロジェクトとしてはMac OS CarbonのMac OS ToolBoxのC ToolBox Carbonを選択する。
作成するアプリのヒープは推賞も最小も1024k以上に設定しておく。
アプリの基本部分
アプリの基本部分のお約束のAPIで不要になったものがある。
例をあげると、
void InitToolbox()
{
UInt32 ic=128;
//Ptr limit;//不要
//limit=GetApplLimit();//不要
//SetApplLimit(limit-64000L);//不要
//MaxApplZone();//不要
#ifndef TARGET_API_MAC_CARBON
{
InitGraf(&qd.thePort);//Carbonで不要
InitWindows();//Carbonで不要
InitFonts();//Carbonで不要
}
#endif
InitCursor();
#ifndef TARGET_API_MAC_CARBON
TEInit();//Carbonで不要
#endif
FlushEvents(everyEvent,nullEvent);
#ifndef TARGET_API_MAC_CARBON
{
InitMenus();//Carbonで不要
InitDialogs(0);//Carbonで不要
MaxApplZone();//Carbonで不要
MoreMasters();//Carbonで不要
MoreMasters();//Carbonで不要
MoreMasters();//Carbonで不要
}
#endif
MoreMasterPointers(ic);
MoreMasterPointers(ic);
MoreMasterPointers(ic);
}
qdグローバルが参照できない
qdグローバルが直接参照できないので、専用のアクセッサー関数を使うことになる。
例えば、
void Drag(EventRecord event,WindowPtr window)
{
Rect dragRect;
BitMap theScreenBits;
//dragRect=qd.screenBits.bounds;//以前はqdグローバルから参照した
GetQDGlobalsScreenBits(&theScreenBits);//Carbonでは
dragRect=theScreenBits.bounds;//Carbonでは
DragWindow(window,event.where,&dragRect);
}
その他のqdグローバルもアクセッサー関数を使って得る。例をいくつかあげると、
qd.grayは、
Pattern thegray;
GetQDGlobalsGray(&thegray);
として得る。
qd.thePortは、
CGrafPtr GetQDGlobalsThePort()
として得る。
モーダルダイアログの変更点
NewModalFilterProc は NewModalFilterUPPに、
DisposeRoutineDescriptor は DisposeModalFilterUPPに変更する。
例えば、
myF=NewModalFilterProc(myFilter3);
DisposeRoutineDescriptor(myF);
であれば、Carbonでは、
ModalFilterUPP myF;
myF=NewModalFilterUPP(myFilter3);
DisposeModalFilterUPP(myF);
となる。
ダイアログアイテム描画の注意
リアルタイムでダイアログアイテムを描画するためには、描画のあとで
QDFlushPortBuffer(GetQDGlobalsThePort(),NULL);
が必要。(さもないと次のイベントがくるまで描画されない。)
これは、MacOSXではウィンドウの内容をいったんバッファにためこんで定期的に画面更新しているからで、自分が書き直した状態をただちに画面に反映させたい時にはこの手続きが必要である。
なおこの問題は、ダイアログアイテムの描画に限らず、ウィンドウ上の描画すべてにあてはまることなので、結構注意が必要である。
メニューをClassicとOS Xの両方に適応できるようにする
終了がファイルメニューの最後にくるのがClassicではお約束だが、MacOS Xではアプリケーションメニューの最後に強制的に発生する。(アップルイベントが組み込まれている必要がある。)また、アバウトダイアログを出す...についてのメニューはClassicではアップルメニューの最初にくるのがお約束だが、MacOS Xではアプリケーションメニューの最初につくらなければいけない。また、OS Xではそもそもアップルメニューが従来のものとは別のものである。
そのアプリが起動した際、OS 9.2の上やClassic環境上で起動したのか、あるいはOS X上で起動したか判断して、その場に応じたメニューを表示しなければいけない。
まず、例えば以下のような鑑別用の関数を作っておく。
Boolean isOSX()
{
long response;
OSStatus err=noErr;
err=Gestalt(gestaltMenuMgrAttr,&response);
if((err==noErr)&&(response & gestaltMenuMgrAquaLayoutMask))
{
return(true);
}
else return(false);
}
この関数の戻り値によってメニュー構成を変える。
例えば以下のようにメニューを作る。
void SetUpMenu()
{
MenuRef menu;
------
if(isOSX()){
menu=GetMenuHandle(129);//file menu
DeleteMenuItem(menu,2);//quit
}
#ifndef TARGET_API_MAC_CARBON
{
appleMenu=GetMenuHandle(128);
AppendResMenu(appleMenu,'DRVR');
InsertMenu(appleMenu,0);
}
#endif
DrawMenuBar();
}
アップルイベント関係の書き換え
OS Xでは、たとえどんな単純なソフトであっても、終了処理のアップルイベントは組み込んでおく必要がある。(システム終了の邪魔をしないためもあるが、アプリケーションメニューの最後に終了メニューが強制的に発生することにも対応する必要がある。)
まず、
void InitAE()
{
OSErr er;
AEEventHandlerUPP procOAPP,procQUIT,procODOC,procPDOC;
//procOAPP=NewAEEventHandlerProc(MyHandleOAPP);
procQUIT=NewAEEventHandlerProc(MyHandleQUIT);
//procODOC=NewAEEventHandlerProc(MyHandleODOC);
//procPDOC=NewAEEventHandlerProc(MyHandlePDOC);
//er=AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,procOAPP,0,false);
//er=AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,procODOC,0,false);
//er=AEInstallEventHandler(kCoreEventClass,kAEPrintDocuments,procPDOC,0,false);
er=AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,procQUIT,0,false);
}
では、
NewAEEventHandlerProc を NewAEEventHandlerUPP に変更する。
すなわち、
void InitAE()
{
OSErr er;
AEEventHandlerUPP procOAPP,procQUIT,procODOC,procPDOC;
//procOAPP=NewAEEventHandlerUPP(MyHandleOAPP);
procQUIT=NewAEEventHandlerUPP(MyHandleQUIT);//carbon
//procODOC=NewAEEventHandlerUPP(MyHandleODOC);
//procPDOC=NewAEEventHandlerUPP(MyHandlePDOC);
//er=AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,procOAPP,0,false);
//er=AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,procODOC,0,false);
//er=AEInstallEventHandler(kCoreEventClass,kAEPrintDocuments,procPDOC,0,false);
er=AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,procQUIT,0,false);
}
QUITの処理としては、
pascal OSErr MyHandleQUIT(AppleEvent *theAppleEvent,AppleEvent *reply,long handlerRefcon)
{
MyEnd();//アプリ終了のための自分の関数
return(noErr);
}
を
static pascal OSErr MyHandleQUIT(const AppleEvent *theAppleEvent,AppleEvent *reply,long handlerRefcon)
{
MyEnd();
return(noErr);
}
に変更する。
後ろのウィンドウのクローズボタン
後ろにあるウィンドウのクローズボタンなどもクリックできてしまうのはClassicではなかったことである。この処理を作り忘れるとエラーをおこす。
Dockに入っている状態
これまで自作したソフトでコラプスボックスをサポートしたものが一つもなかったため、Dockに入っているかいないかを知る方法が長いこと分からなかった。少なくとも表面上は、Dockに入った状態はコラプスした状態と同じであるらしい。IsWindowCollapsedで判断すればよいようだ。
構造体メンバーへのアクセス
先述したように、qdに限らず、windowなど主要な構造体のメンバーに直接アクセスできず、専用のアクセッサー関数を使わなくてはならないが、これが実に不便で、
例えば、
pt.h=(((CGrafPtr)window)->portRect).left;
pt.v=(((CGrafPtr)window)->portRect).top;
としていたものは、
CGrafPtr mygp;
Rect gprect;
mygp=GetWindowPort(window);
GetPortBounds(mygp,&gprect);
pt.h=gprect.left;
pt.v=gprect.top;
と、まわりくどいことをしなければならない。
以下に少し例を挙げると、
if(((CWindowPeek)window)->visible==false)ShowWindow(window);というのは
if(IsWindowVisible(window)==false)ShowWindow(window);
といったふうにする必要がある。
theDlg->portRectを得るためには、
CGrafPtr cgp;
Rect drect;
cgp=GetDialogPort(theDlg);
GetPortBounds(cgp,&drect);
といった感じにする。
newwindowRect=((CGrafPtr)window)->portRect;などは
GetPortBounds(GetWindowPort(window),&newwindowRect);とするか。
((CGrafPtr)window)->clipRgnにdrawregionを代入したい時は、
SetPortClipRegion(GetWindowPort(window),drawregion);とするか。
CopyRgn(qd.thePort->clipRgn,curClipR);としたい時は、
CGrafPtr qdtheport;
RgnHandle myclipRgn;
qdtheport=GetQDGlobalsThePort();
GetPortClipRegion(qdtheport,myclipRgn);
CopyRgn(myclipRgn,curClipR);
といった感じにするか。
CopyBits((BitMap*)*pixMapHandle,&window->portBits,&windowRect,&newwindowRect,ditherCopy,0);も
CopyBits((BitMap*)*pixMapHandle,GetPortBitMapForCopyBits(GetWindowPort(window)),&windowRect,&newwindowRect,ditherCopy,0);となる。
こういうのがいっぱいあって、書き換えるのにけっこう手間がかかる。
各構造体のヘッダファイルのコメントを参考にしてそのメンバーのアクセッサー関数を調べる。
変わりウィンドウ
猫型ウィンドウなどの変わりウィンドウは、ClassicではWDEFをつくってそれをWindowやDialogに組み込むことで実現していた。 Carbonでは変わりウィンドウは原則的にはWDEFを使わずに、CreateCustomWindowという関数を使ってコードの中から作る。しかし、RegisterWindowDefinitionという関数を使えば旧来のWDEFをCarbonアプリの中でも使えるらしい。しかし、当然WDEFの中のコードはCarbon対応になっている必要はあるだろうと思われる。
(工事中)
Carbonへの道-An Opaque Road To Carbon-