Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Magnify.cp
/* |
File: Magnify.cp |
Contains: Sample application which implements a magnifying lens tool. |
Version: Mac OS X |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Copyright © 1998-2001 Apple Computer, Inc., All Rights Reserved |
*/ |
#ifdef __APPLE_CC__ |
#include <Carbon/Carbon.h> |
#else |
#include <Carbon.h> |
#endif |
enum |
{ |
kCmdMagnificationChanged = 'MChg', |
kCmdShowMouseLocation = 'MLoc', |
kCmdFloat = 'Floa' |
}; |
struct PrefInfo |
{ |
UInt8 origMagFactor; |
Boolean origShowInfo; |
Boolean origFloats; |
WindowRef window; |
}; |
typedef struct PrefInfo PrefInfo; |
static pascal OSStatus WindowHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ); |
static pascal OSStatus AppCommandHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ); |
static pascal OSStatus PrefHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ); |
pascal void MagnifyEventLoopTimer(EventLoopTimerRef /*inTimer*/, void *inUserData); |
static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon); |
static void DoPreferences(); |
static void AdjustWindowFloating(); |
static GrafPtr sGlobalPort = NULL; |
static UInt8 sMagFactor = 8; |
static Boolean sShowInfo = true; |
static Boolean sFloats = false; |
static Boolean sForceUpdate = false; |
static WindowRef sMagnifyWindow; |
static const ControlID kMagSlider = { 'PREF', 1 }; |
static const ControlID kShowInfoCheck = { 'PREF', 2 }; |
static const ControlID kFloatCheck = { 'PREF', 3 }; |
#define kPreferencesKey CFSTR( "com.apple.carbon.examples.magnify" ) |
//---------------------------------------------------------------------------- |
// ¥ MagnifyEventLoopTimer |
// |
// Our magnification timer. Ideally we should be event based, but we can't |
// detect every event that might cause a screen update (window move, etc.) |
// so we resort to this. |
//---------------------------------------------------------------------------- |
pascal void |
MagnifyEventLoopTimer(EventLoopTimerRef /*inTimer*/, void *inUserData) |
{ |
WindowRef window = (WindowRef)inUserData; |
Point mouse; |
GrafPtr windowPort; |
GrafPtr savePort; |
Rect sourceRect; |
Rect destRect; |
Rect portBounds; |
SInt16 distance; |
static Point sLastMouse = { -1, -1}; |
GetPort( &savePort ); |
windowPort = GetWindowPort(window); |
GetPortBounds( windowPort, &portBounds ); |
GetGlobalMouse( &mouse ); |
if ( !EqualPt( mouse, sLastMouse ) || sForceUpdate ) |
{ |
Rect monitorBounds; |
SInt32 temp; |
sLastMouse = mouse; |
SetPort(windowPort); |
distance = SInt16((portBounds.right - portBounds.left) / (sMagFactor * 2)); |
// Pin the mouse position to the main monitor |
monitorBounds = (**GetMainDevice()).gdRect; |
InsetRect( &monitorBounds, distance, distance ); |
temp = PinRect( &monitorBounds, mouse ); |
mouse = *(Point*)&temp; |
sourceRect.left = mouse.h - distance; |
sourceRect.right = mouse.h + distance; |
sourceRect.top = mouse.v - distance; |
sourceRect.bottom = mouse.v + distance; |
destRect = portBounds; |
ForeColor(blackColor); |
BackColor(whiteColor); |
PenMode( srcCopy ); |
CopyBits( *(BitMap **)GetPortPixMap(sGlobalPort), *(BitMap **)GetPortPixMap(windowPort), &sourceRect, &destRect, srcCopy, NULL ); |
if ( sShowInfo ) |
{ |
CFStringRef string; |
string = CFStringCreateWithFormat( NULL, NULL, CFSTR( "(%d,%d), %dx" ), sLastMouse.h, sLastMouse.v, sMagFactor ); |
if ( string ) |
{ |
Rect box; |
Point size; |
SInt16 baseLine; |
const RGBColor kWhite = { 0xFFFF, 0xFFFF, 0xFFFF }; |
const RGBColor kBlack = { 0, 0, 0 }; |
TextFont( 0 ); |
TextSize( 12 ); |
GetThemeTextDimensions( string, kThemeCurrentPortFont, kThemeStateActive, |
false, &size, &baseLine ); |
box.left = portBounds.left; |
box.bottom = portBounds.bottom; |
box.right = box.left + size.h; |
box.top = box.bottom - size.v; |
RGBForeColor( &kBlack ); |
PaintRect( &box ); |
RGBForeColor( &kWhite ); |
DrawThemeTextBox( string, kThemeCurrentPortFont, kThemeStateActive, |
false, &box, teCenter, NULL ); |
CFRelease( string ); |
} |
} |
ForeColor( blackColor ); |
BackColor( whiteColor ); |
PenMode( patXor ); |
MoveTo( ( portBounds.right / 2 ) - 4, portBounds.bottom / 2 ); |
Line( 8, 0 ); |
MoveTo( portBounds.right / 2, ( portBounds.bottom / 2 ) - 4 ); |
Line( 0, 8 ); |
SetPort( savePort ); |
} |
sForceUpdate = false; |
} |
//---------------------------------------------------------------------------- |
// ¥ CreateMagnifyWindow |
// |
// Create our magnification window. |
//---------------------------------------------------------------------------- |
static OSStatus |
CreateMagnifyWindow( IBNibRef nibRef ) |
{ |
WindowRef window; |
EventLoopTimerUPP eventLoopTimer; |
EventHandlerRef eventHandlerRef; |
EventHandlerUPP handlerProc; |
EventTypeSpec events[] = { { kEventClassWindow, kEventWindowBoundsChanging } }; |
OSStatus err; |
err = CreateWindowFromNib( nibRef, CFSTR( "MainWindow" ), &window ); |
require_noerr( err, CantCreateWindow ); |
// Now install our own handler so we can intercept window size changes |
// For this handler, plus our timer below, we create a UPP, but we never |
// dispose of it. This is because our window stays around until we quit. |
// If we had a window that came and went, we would need to clean up |
// properly and Dispose the EventHandlerUPP and the EventLoopTimerUPP for |
// the window. |
handlerProc = NewEventHandlerUPP( WindowHandler ); |
verify_noerr( InstallWindowEventHandler( window, handlerProc, GetEventTypeCount( events ), |
events, window, &eventHandlerRef ) ); |
// Install our timer, which we use to update the window. |
eventLoopTimer = NewEventLoopTimerUPP(MagnifyEventLoopTimer); |
verify_noerr( InstallEventLoopTimer( GetCurrentEventLoop(), kEventDurationSecond, |
kEventDurationSecond / 20, eventLoopTimer, (void *)window, NULL ) ); |
// Make this window such that it does not hide when we are suspended. |
// Normally, floating windows have the hide-on-suspend attribute set |
// so that they hide when switching out and show when resumed later. |
ChangeWindowAttributes( window, 0, kWindowHideOnSuspendAttribute ); |
sMagnifyWindow = window; |
// Adjust our float-ness as per our current settings |
AdjustWindowFloating(); |
// And it's always nice to see the windows we create. |
ShowWindow(window); |
CantCreateWindow: |
return err; |
} |
//---------------------------------------------------------------------------- |
// ¥ WindowHandler |
// |
// Constrain our resizing to a perfect square. |
//---------------------------------------------------------------------------- |
static pascal OSStatus |
WindowHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ) |
{ |
Rect bounds; |
SInt16 height, width; |
UInt32 attributes; |
OSStatus result = eventNotHandledErr; |
GetEventParameter( inEvent, kEventParamAttributes, typeUInt32, NULL, sizeof( UInt32 ), NULL, &attributes ); |
// We only constrain on size changes. There is no point constraining if |
// the position (origin) is changing. |
if ( (attributes & kWindowBoundsChangeSizeChanged) != 0 ) |
{ |
// Extract the current bounds. This is the paramter you get to modify to |
// alter the window position or size during a window resizing. |
GetEventParameter( inEvent, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof( bounds ), NULL, &bounds ); |
// Make the bounds square |
height = bounds.bottom - bounds.top; |
width = bounds.right - bounds.left; |
if ( width > height ) |
{ |
bounds.bottom = bounds.top + width; |
} |
else |
{ |
bounds.right = bounds.left + height; |
} |
// Set the current bounds parameter to our adjusted bounds. Return |
// noErr to indicate we handled this event. |
SetEventParameter( inEvent, kEventParamCurrentBounds, typeQDRectangle, sizeof( bounds ), &bounds ); |
result = noErr; |
} |
return result; |
} |
static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) |
{ |
#pragma unused (appleEvt, reply, refcon) |
QuitApplicationEventLoop(); |
return(noErr); |
} |
//---------------------------------------------------------------------------- |
// ¥ main |
// |
// Where it all begins... |
//---------------------------------------------------------------------------- |
int main( int argc, char* argv[] ) |
{ |
long response; |
IBNibRef nibRef; |
Boolean test; |
OSStatus err = noErr; |
EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess }; |
// Make sure the arrow cursor is displayed |
InitCursor(); |
err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false); |
if (err != noErr) |
ExitToShell(); |
// Open our nib and set up the menu bar from it |
err = CreateNibReference( CFSTR( "Magnify" ), &nibRef ); |
require_noerr( err, CantOpenNib ); |
err = Gestalt(gestaltMenuMgrAttr, &response); |
if ((err == noErr) && (response & gestaltMenuMgrAquaLayoutMask)) |
err = SetMenuBarFromNib( nibRef, CFSTR( "MainMenuX" ) ); |
else |
err = SetMenuBarFromNib( nibRef, CFSTR( "MainMenu9" ) ); |
require_noerr( err, CantSetMenuBar ); |
// We have a preference dialog, so we need to activate |
// the Preferences menu item in our Application menu. |
// The way to do that is by command ID. Normally, this |
// menu item is disabled. |
EnableMenuCommand( NULL, kHICommandPreferences ); |
// Create a global port so we can copy bits from the screen |
sGlobalPort = CreateNewPort(); |
require_noerr( QDError(), NewPortFailed ); |
// Let's see the menu bar, please. |
DrawMenuBar(); |
// Load any prefs. We use CFPreferences to store our preferences. |
// When doing this, you need to create a key for your application. |
// Normally this is the bundle ID of your app. |
sShowInfo = CFPreferencesGetAppBooleanValue( CFSTR( "ShowInfo" ), kPreferencesKey, &test ); |
if ( !test ) sShowInfo = true; |
sMagFactor = CFPreferencesGetAppIntegerValue( CFSTR( "MagFactor" ), kPreferencesKey, &test ); |
if ( !test ) sMagFactor = 8; |
sFloats = CFPreferencesGetAppBooleanValue( CFSTR( "Float" ), kPreferencesKey, &test ); |
if ( !test ) sFloats = false; |
// Now create our window and dispose the nib ref - we're done with it. |
CreateMagnifyWindow( nibRef ); |
DisposeNibReference( nibRef ); |
// Because we want to respond to the preferences menu item, we need to |
// install a command handler to handle the menu selection. Here we are |
// creating the UPP on the fly and not disposing it, since it will get |
// destroyed when the app terminates. |
InstallApplicationEventHandler( NewEventHandlerUPP( AppCommandHandler ), 1, &cmdEvent, 0, NULL ); |
// Run Lola, Run! |
RunApplicationEventLoop(); |
// Clean up and exit. |
DisposePort( sGlobalPort ); |
return noErr; |
NewPortFailed: |
CantSetMenuBar: |
DisposeNibReference( nibRef ); |
CantOpenNib: |
return err; |
} |
//---------------------------------------------------------------------------- |
// ¥ AppCommandHandler |
// |
// Look for the preferences menu item and call DoPreferences when we |
// receive it. |
//---------------------------------------------------------------------------- |
static pascal OSStatus |
AppCommandHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData ) |
{ |
#pragma unused( inHandler, inUserData ) |
HICommand cmd; |
OSStatus result = eventNotHandledErr; |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd ); |
if ( cmd.commandID == kHICommandPreferences ) |
{ |
DoPreferences(); |
result = noErr; |
} |
return result; |
} |
//---------------------------------------------------------------------------- |
// ¥ DoPreferences |
// |
// Open our preferences dialog and run it modally. |
//---------------------------------------------------------------------------- |
static void |
DoPreferences() |
{ |
IBNibRef nibRef; |
EventTypeSpec cmdEvent = { kEventClassCommand, kEventCommandProcess }; |
WindowRef window; |
OSStatus err; |
ControlRef control; |
PrefInfo info; |
EventHandlerUPP handler; |
// Open our nib, create the window, and close the nib. |
err = CreateNibReference( CFSTR( "Magnify" ), &nibRef ); |
require_noerr( err, CantOpenNib ); |
err = CreateWindowFromNib( nibRef, CFSTR( "Preferences" ), &window ); |
require_noerr( err, CantCreateWindow ); |
DisposeNibReference( nibRef ); |
// For each control of interest, set its value as appropriate |
// for the settings we currently have. |
GetControlByID( window, &kMagSlider, &control ); |
SetControlValue( control, (sMagFactor >> 2) + 1 ); |
GetControlByID( window, &kShowInfoCheck, &control ); |
SetControlValue( control, sShowInfo ); |
GetControlByID( window, &kFloatCheck, &control ); |
SetControlValue( control, sFloats ); |
// Build up a structure to pass to the window handler we are about |
// to install. We store the window itself, as well as the original |
// states of our settings. We use this to revert if the user clicks |
// the cancel button. |
info.window = window; |
info.origMagFactor = sMagFactor; |
info.origShowInfo = sShowInfo; |
info.origFloats = sFloats; |
// Now create our UPP and install the handler. |
handler = NewEventHandlerUPP( PrefHandler ); |
InstallWindowEventHandler( window, handler, 1, &cmdEvent, &info, NULL ); |
// Position and show the window |
RepositionWindow( window, NULL, kWindowAlertPositionOnMainScreen ); |
ShowWindow( window ); |
// Now we run modally. We will remain here until the PrefHandler |
// calls QuitAppModalLoopForWindow if the user clicks OK or |
// Cancel. |
RunAppModalLoopForWindow( window ); |
// OK, we're done. Dispose of our window and our UPP. |
// We do the UPP last because DisposeWindow can send out |
// CarbonEvents, and we haven't explicitly removed our |
// handler. If we disposed the UPP, the Toolbox might try |
// to call it. That would be bad. |
DisposeWindow( window ); |
DisposeEventHandlerUPP( handler ); |
return; |
CantCreateWindow: |
DisposeNibReference( nibRef ); |
CantOpenNib: |
return; |
} |
//---------------------------------------------------------------------------- |
// ¥ PrefHandler |
// |
// Our command handler for the Preferences dialog. |
//---------------------------------------------------------------------------- |
static pascal OSStatus |
PrefHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* inUserData ) |
{ |
#pragma unused( inHandler ) |
HICommand cmd; |
OSStatus result = eventNotHandledErr; |
PrefInfo* info = (PrefInfo*)inUserData; |
ControlRef control; |
CFNumberRef number; |
WindowRef window = info->window; |
// The direct object for a 'process commmand' event is the HICommand. |
// Extract it here and switch off the command ID. |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd ); |
switch ( cmd.commandID ) |
{ |
case kHICommandOK: |
number = CFNumberCreate( NULL, kCFNumberSInt8Type, &sMagFactor ); |
CFPreferencesSetAppValue( CFSTR( "MagFactor" ), number, kPreferencesKey ); |
CFPreferencesSetAppValue( CFSTR( "ShowInfo" ), sShowInfo ? kCFBooleanTrue : kCFBooleanFalse, kPreferencesKey ); |
CFPreferencesSetAppValue( CFSTR( "Float" ), sFloats ? kCFBooleanTrue : kCFBooleanFalse, kPreferencesKey ); |
CFPreferencesAppSynchronize( kPreferencesKey ); |
CFRelease( number ); |
QuitAppModalLoopForWindow( window ); |
result = noErr; |
break; |
case kHICommandCancel: |
sMagFactor = info->origMagFactor; |
sShowInfo = info->origShowInfo; |
sFloats = info->origFloats; |
AdjustWindowFloating(); |
sForceUpdate = true; |
QuitAppModalLoopForWindow( window ); |
result = noErr; |
break; |
case kCmdMagnificationChanged: |
GetControlByID( window, &kMagSlider, &control ); |
sMagFactor = 2 << (GetControlValue( control ) - 1); |
sForceUpdate = true; |
result = noErr; |
break; |
case kCmdShowMouseLocation: |
GetControlByID( window, &kShowInfoCheck, &control ); |
sShowInfo = GetControlValue( control ); |
sForceUpdate = true; |
result = noErr; |
break; |
case kCmdFloat: |
GetControlByID( window, &kFloatCheck, &control ); |
sFloats = GetControlValue( control ); |
AdjustWindowFloating(); |
result = noErr; |
break; |
} |
return result; |
} |
//---------------------------------------------------------------------------- |
// ¥ AdjustWindowFloating |
// |
// Adjust our window's floating state based on the current setting. We can |
// either float in our application's layer, or above all applications. We |
// accomplish this magic by moving the window between the appropriate window |
// groups. |
//---------------------------------------------------------------------------- |
static void |
AdjustWindowFloating() |
{ |
check( sMagnifyWindow != NULL ); |
if ( sFloats ) |
SetWindowGroup( sMagnifyWindow, GetWindowGroupOfClass( kUtilityWindowClass ) ); |
else |
SetWindowGroup( sMagnifyWindow, GetWindowGroupOfClass( kFloatingWindowClass ) ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-10