FloatingScale


アプリケーション

 ものさしです。72dpi と実寸を切り替えて表示します。
 目盛りは cm、inch、100 pixel 単位です。また、関連するデータとして、ディスプレイ、マシン、モデル、OSのバージョンを表示します。

72dpi.png

real.png


プロジェクト

 新規に Carbon Application を作成します。

nib

 main.nib を次のように手を入れます。

MenuBar

 Submenu で "Scale" を付け加えます。Title に "Change the Scale" と入力します。Command に 'chSc' と入力します。キーはお好みに応じて。
 About にアプリケーションの名称を入力します。何でも良いのですが "FloatingScale" としています。

MainWindow

 そのまま使います。

ソース

 main.c は次のコードに差し替えます。
 イベント・ハンドラを登録する関数が 2 つ、ものさしを描く関数、実寸と72dpiの係数を設定する関数を含みます。

プリプロセッサ・宣言

 コマンド 'chSc'、目盛りの種類を表すグローバル変数 gFlagScale の値である kScaleDPI72 と kScaleReal を定義します。

#include <Carbon/Carbon.h> #include <sys/types.h> #include <sys/sysctl.h> #define kFlScaleCommand 'chSc' #define kScaleDPI72 0 #define kScaleReal 1 pascal OSStatus MainWindow_EventHandler (EventHandlerCallRef myHandler, EventRef event, void *userData); OSStatus App_EventHandler (EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ); void DrawScale (WindowRef pWindowRef); float DetermineScalingFactor (char *pStringModel); int gFlagScale;

main 関数

 nib ファイルからメニューとウインドウを作ります。ウインドウとコマンドのイベント・ハンドラを登録します。
 ウインドウの大きさはディスプレイに合わせて設定します。ウインドウの内部を描画してから制御を渡します。

int main(int argc, char* argv[]) { WindowRef aWindowRef; IBNibRef nibRef; OSStatus err; EventTypeSpec aEventWindow = {kEventClassWindow, kEventWindowClose}; EventTypeSpec aEventCommand = {kEventClassCommand, kEventCommandProcess}; int aDisplayPixelsWide; // Create a Nib reference err = CreateNibReference(CFSTR("main"), &nibRef); require_noerr( err, CantGetNibRef ); // Set the menu bar err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); require_noerr( err, CantSetMenuBar ); err = CreateWindowFromNib (nibRef, CFSTR("MainWindow"), &aWindowRef); require_noerr (err, CantCreateWindow); DisposeNibReference (nibRef); // Install Event Handlers err = InstallWindowEventHandler (aWindowRef, NewEventHandlerUPP(MainWindow_EventHandler), 1, &aEventWindow, (void *)aWindowRef, NULL); InstallApplicationEventHandler( NewEventHandlerUPP( App_EventHandler ), GetEventTypeCount(aEventCommand), &aEventCommand, 0, NULL ); // Making Window SetWindowTitleWithCFString (aWindowRef, CFSTR("FloatingScale")); aDisplayPixelsWide = CGDisplayPixelsWide(kCGDirectMainDisplay); SizeWindow (aWindowRef, aDisplayPixelsWide * 0.9, 4 + 12 * 4 + 2 * 3, false); MoveWindow (aWindowRef, (int) aDisplayPixelsWide * 0.05, (int) aDisplayPixelsWide * 0.05, false); ShowWindow (aWindowRef); SelectWindow (aWindowRef); DrawScale (aWindowRef); RunApplicationEventLoop (); HideWindow (aWindowRef); DisposeWindow (aWindowRef); CantCreateWindow: CantSetMenuBar: CantGetNibRef: return err; }

ウインドウ・イベント・ハンドラ 関数

 ウインドウが閉じられるイベントを登録します。

pascal OSStatus MainWindow_EventHandler (EventHandlerCallRef myHandler, EventRef event, void *userData) { short ret = eventNotHandledErr; WindowRef aWRef; UInt32 eventKind; aWRef = (WindowRef)userData; SetPortWindowPort(aWRef); if (GetEventClass (event) == kEventClassWindow) { eventKind = GetEventKind (event); if (eventKind == kEventWindowClose) { QuitApplicationEventLoop (); ret = noErr; } } return(ret); }

コマンド・イベント・ハンドラ 関数

 コマンドが発生したら DrawScale を呼び目盛りを書き換えます。

OSStatus App_EventHandler (EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) { OSStatus result = eventNotHandledErr; HICommandExtended cmd; switch ( GetEventClass( inEvent ) ) { case kEventClassCommand: { verify_noerr( GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd ) ); switch ( GetEventKind( inEvent ) ) { case kEventCommandProcess: switch ( cmd.commandID ) { case kFlScaleCommand: DrawScale (ActiveNonFloatingWindow ()); break; default: break; } break; } break; } default: break; } return result; }

目盛りを描く関数

 CGContext に対して描画します。
 マシンとモデルの名称は sysctl 関数を呼んで、文字列を得ます。OS のバージョンは Gestalt を利用して取得します。
 変数 gFlagScale の値を書き換えます。

void DrawScale (WindowRef pWindowRef) { CGContextRef aContext; int aDisplayPixelsWide, aDisplayPixelsHigh; Rect aBounds; CGRect aFillArea = { {10, 10}, {50, 50}}; float x, aFactor; char aStr255[256] = "Hello, Quartz"; /* For retrieving machine model */ int mib[2]; size_t aLen; char *aStringMachine, *aStringModel; /* For retrieving system version */ long aResponse; SetPortWindowPort(pWindowRef); QDBeginCGContext(GetWindowPort(pWindowRef), &aContext); aDisplayPixelsWide = CGDisplayPixelsWide(kCGDirectMainDisplay); aDisplayPixelsHigh = CGDisplayPixelsHigh(kCGDirectMainDisplay); GetWindowBounds (pWindowRef, kWindowContentRgn, &aBounds); CGContextSetRGBFillColor (aContext, 1.0, 1.0, 1.0, 1.0); aFillArea.origin.x = aFillArea.origin.y = 0; aFillArea.size.width = aBounds.right - aBounds.left; aFillArea.size.height = aBounds.bottom - aBounds.top; CGContextFillRect (aContext, aFillArea); CGContextSelectFont (aContext, "Courier", 12, kCGEncodingMacRoman); CGContextSetTextDrawingMode (aContext, kCGTextFill); mib[0] = CTL_HW; mib[1] = HW_MACHINE; sysctl(mib, 2, NULL, &aLen, NULL, 0); aStringMachine = malloc(aLen); sysctl(mib, 2, aStringMachine, &aLen, NULL, 0); mib[1] = HW_MODEL; sysctl(mib, 2, NULL, &aLen, NULL, 0); aStringModel = malloc(aLen); sysctl(mib, 2, aStringModel, &aLen, NULL, 0); if (gFlagScale == kScaleDPI72) { aFactor = 1.0; CGContextSetRGBFillColor (aContext, 0.5, 0.5, 0.0, 1.0); gFlagScale = kScaleReal; sprintf(aStr255,"Scale = 72dpi, "); } else { aFactor = DetermineScalingFactor(aStringModel); CGContextSetRGBFillColor (aContext, 0.0, 0.5, 0.5, 1.0); gFlagScale = kScaleDPI72; sprintf(aStr255,"Scale = Real, "); } CGContextShowTextAtPoint(aContext, 4, 4, aStr255, strlen(aStr255)); Gestalt (gestaltSystemVersion, &aResponse); sprintf(aStr255,"Display = %d x %d, Machine = %s, Model = %s, System = %#x", aDisplayPixelsWide, aDisplayPixelsHigh, aStringMachine, aStringModel, (int)aResponse); CGContextShowText(aContext, aStr255, strlen(aStr255)); sprintf(aStr255,"100 Pixels:"); CGContextShowTextAtPoint(aContext, 4, 16, aStr255, strlen(aStr255)); sprintf(aStr255,"Inch:"); CGContextShowTextAtPoint(aContext, 4, 30, aStr255, strlen(aStr255)); sprintf(aStr255,"Centimeter:"); CGContextShowTextAtPoint(aContext, 4, 44, aStr255, strlen(aStr255)); CGContextSetLineCap (aContext, kCGLineCapButt); // Drawing Pixel scale CGContextMoveToPoint(aContext, 96, 16); CGContextAddLineToPoint(aContext, aBounds.right - aBounds.left - 8, 16); CGContextStrokePath(aContext); for (x = 96; x < aBounds.right - aBounds.left - 8; x += 100) { CGContextMoveToPoint(aContext, x, 16); CGContextAddLineToPoint(aContext, x, 16 + 8); CGContextStrokePath(aContext); } // Drawing Inch scale CGContextMoveToPoint(aContext, 96, 30); CGContextAddLineToPoint(aContext, aBounds.right - aBounds.left - 8, 30); CGContextStrokePath(aContext); for (x = 96; x < aBounds.right - aBounds.left - 8; x += 72 / aFactor) { CGContextMoveToPoint(aContext, x, 30); CGContextAddLineToPoint(aContext, x, 30 + 8); CGContextStrokePath(aContext); } // Drawing Centimeter scale CGContextMoveToPoint(aContext, 96, 44); CGContextAddLineToPoint(aContext, aBounds.right - aBounds.left - 8, 44); CGContextStrokePath(aContext); for (x = 96; x < aBounds.right - aBounds.left - 8; x += 72 / 2.54 / aFactor) { CGContextMoveToPoint(aContext, x, 44); CGContextAddLineToPoint(aContext, x, 44 + 8); CGContextStrokePath(aContext); } CGContextFlush(aContext); QDEndCGContext(GetWindowPort(pWindowRef), &aContext); return; }

目盛りの係数を設定する関数

 ディスプレイの寸法は機種により異なる場合がありますから、機種に応じて係数を設定します。ここでは、いくつかのモデルを想定して係数をあらかじめ埋め込んでおき、モデルの名称を所定の文字列と比較して係数を選びます。

float DetermineScalingFactor (char *pStringModel) { float aFactor = 1.0; if (0 == strcmp("PowerBook1,1", pStringModel)){ aFactor = 0.791; // PowerBook G3, 1,024 pixel } else { if (0 == strcmp("iMac", pStringModel)) aFactor = 0.735; // iMac, 1,024 pixel else { if (0 == strcmp("c4,1", pStringModel)) aFactor = 0.721; // iMac Intel, 1,440 pixel } } return aFactor; }

プログラミングの環境

 筆者の環境は次に示す通りです。プログラムは同環境での動作を確認しています。
・PowerBook G3 333MHz, 384 MB
・Mac OS X 10.3.9
・Xcode 1.5


busaho
November 27, 2008
Rev., December 10, 2008