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.
HandySample.c
/* |
File: HandySample.c |
Description: |
This file contains the main application program for the HandySample. |
Routines in this file are responsible for handling events directed |
at the application. |
Copyright: |
© Copyright 1999 Apple Computer, Inc. All rights reserved. |
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. |
Change History (most recent first): |
Tue, Jan 18, 2000 -- created |
*/ |
#include "HandySample.h" |
#include "HandyWindow.h" |
#include "SampleUtils.h" |
#ifdef __APPLE_CC__ |
#include <Carbon/Carbon.h> |
#else |
#include <Carbon.h> |
#endif |
/* our globals */ |
Boolean gRunning = true; /* true while the app is running */ |
RgnHandle gMouseRgn = NULL; /* the mouse region passed to WaitNextEvent */ |
Boolean gIsFrontApp = true; /* true while we're in the forground. */ |
/* ResetMenus is called immediately before all calls to |
MenuSelect or MenuKey. In this routine, we re-build |
or enable the menus as appropriate depending on the |
current environment */ |
static void ResetMenus(void) { |
Boolean drawnSlowly; |
MenuHandle fileMenu; |
fileMenu = GetMenuHandle(mFile); |
/* here, if the frontmost window is a handy window, then |
enable and check the slow item according to it's slow drawing |
state. Otherwise, disable the item. */ |
if ( HandyWindowGetSlow(FrontWindow(), &drawnSlowly) == noErr ) { |
EnableMenuItem(fileMenu, iSlow); |
CheckMenuItem(GetMenuHandle(mFile), iSlow, drawnSlowly); |
} else DisableMenuItem(fileMenu, iSlow); |
} |
/* DoMenuCommand is called in response to MenuKey |
or MenuSelect. Here, we dispatch the menu command |
to its appropriate handler, or if it's a small action |
we do it here. */ |
static void DoMenuCommand(long rawMenuSelectResult) { |
short menu, item; |
/* decode the MenuSelect result */ |
menu = (rawMenuSelectResult >> 16); |
if (menu == 0) return; |
item = (rawMenuSelectResult & 0x0000FFFF); |
/* dispatch on result */ |
switch (menu) { |
/* apple menu commands */ |
case mApple: |
if (item == iAbout) { |
ParamAlert(kAboutBoxAlertID, NULL, NULL); |
} |
break; |
/* file menu commands */ |
case mFile: |
if (item == iSlow) { |
Boolean drawnSlowly; |
WindowPtr target; |
/* if the frontmost window is a handy window, then |
invert it's slow drawing state. */ |
if ( HandyWindowGetSlow((target = FrontWindow()), &drawnSlowly) == noErr ) { |
HandyWindowSetSlow(target, ! drawnSlowly); |
} |
} else if (item == iQuit) { |
gRunning = false; |
} |
break; |
} |
/* unhilite the menu bar */ |
HiliteMenu(0); |
} |
/* QuitAppleEventHandler is our quit Apple event handler. this routine |
is called when a quit Apple event is sent to our application. Here, |
we set the gRunning flag to false. NOTE: it is not appropriate to |
call ExitToShell here. Instead, by setting the flag to false we |
fall through the bottom of our main loop the next time we're called. */ |
static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) { |
gRunning = false; |
return noErr; |
} |
/* HandleMouseDown is called for mouse down events. The main difference |
over 'the usual' mouse event handler, is we use the grabbing hand |
cursor inside of window drag and resize operations. Other clicks |
are dispatched to routines defined in HandyWindows.h as appropriate. */ |
static void HandleMouseDown(EventRecord *ev) { |
WindowPtr theWindow; |
short partcode; |
switch ((partcode = FindWindow(ev->where, &theWindow))) { |
/* inside the window's content area */ |
case inContent: |
if (theWindow != FrontWindow()) { |
/* if it's not the frontmost window, |
then make it the frontmost window. */ |
SelectWindow(theWindow); |
} else { |
/* otherwise, if it's a rendering window, |
pass the click along to the window. */ |
Point where; |
SetPort(GetWindowPort(theWindow)); |
where = ev->where; |
GlobalToLocal(&where); |
if (IsHandyWindow(theWindow)) |
HandyWindowMouse(theWindow, where, ev->modifiers); |
} |
break; |
/* menu bar clicks */ |
case inMenuBar: |
SetThemeCursor(kThemePointingHandCursor); |
ResetMenus(); |
DoMenuCommand(MenuSelect(ev->where)); |
break; |
/* track clicks in the close box */ |
case inGoAway: |
if (TrackGoAway(theWindow, ev->where)) { |
if (IsHandyWindow(theWindow)) { |
HandyWindowCloseWindow(theWindow); |
gRunning = false; |
} |
} |
break; |
/* allow window drags */ |
case inDrag: |
{ Rect boundsRect = {0,0, 32000, 32000}; |
SetThemeCursor(kThemeClosedHandCursor); |
DragWindow(theWindow, ev->where, &boundsRect); |
} |
break; |
/* allow window drags */ |
case inGrow: |
{ Rect sizerect; |
long grow_result; |
SetThemeCursor(kThemeClosedHandCursor); |
if (GetHandyWindowGrowLimits(theWindow, &sizerect) != noErr) |
SetRect(&sizerect, 300, 150, 32767, 32767); |
grow_result = GrowWindow(theWindow, ev->where, &sizerect); |
if (grow_result != 0) { |
SizeWindow(theWindow, LoWord(grow_result), HiWord(grow_result), true); |
if (IsHandyWindow(theWindow)) |
HandyWindowSizeChanged(theWindow); |
} |
} |
break; |
/* zoom box clicks. NOTE: since the rendering window |
always sets the standard rectangle to the 'best size' for |
displaying the current HTML window, the inZoomOut partcode |
will zoom the window to that size rather than the entire screen.*/ |
case inZoomIn: |
case inZoomOut: |
if (TrackBox(theWindow, ev->where, partcode)) { |
Rect r; |
CGrafPtr gp; |
gp = GetWindowPort(theWindow); |
GetPortBounds(gp, &r); |
SetPort(gp); |
EraseRect(&r); |
ZoomWindow(theWindow, partcode, true); |
if (IsHandyWindow(theWindow)) |
HandyWindowSizeChanged(theWindow); |
} |
break; |
} |
} |
/* ResetCursor sets the cursor according to the location of the mouse on |
the screen. If the mouse is inside of a handy window, then the task of |
setting the cursor is passed along to the HandyWindowCursor routine |
defined in HandyWindows.c. */ |
static void ResetCursor(EventRecord *ev) { |
WindowPtr target; |
Point where; |
short partcode; |
/* discover the mouse's location */ |
switch ((partcode = FindWindow(ev->where, &target))) { |
/* if it's in the content area of a window, then we either |
set the cursor to an arrow (if it's not the front window) or |
we ask HandyWindowCursor to set the cursor. */ |
case inContent: |
if (target != FrontWindow()) { |
SetThemeCursor(kThemeArrowCursor); |
SetEmptyRgn(gMouseRgn); |
} else { |
SetPort(GetWindowPort(target)); |
where = ev->where; |
GlobalToLocal(&where); |
if ( ! HandyWindowCursor( target, where, ev->modifiers, gMouseRgn) ) { |
SetThemeCursor(kThemeArrowCursor); |
SetEmptyRgn(gMouseRgn); |
} |
} |
break; |
/* the following sets the cursor according to other locations |
in the gui, setting the mouse region to reflect the region where |
the cursor should remain as set. */ |
case inMenuBar: |
SetThemeCursor(kThemePointingHandCursor); |
GetMenuBarRegion(gMouseRgn); |
break; |
case inCollapseBox: |
SetThemeCursor(kThemePointingHandCursor); |
GetWindowRegion(target, kWindowCollapseBoxRgn, gMouseRgn); |
break; |
case inZoomIn: |
case inZoomOut: |
SetThemeCursor(kThemePointingHandCursor); |
GetWindowRegion(target, kWindowZoomBoxRgn, gMouseRgn); |
break; |
case inGoAway: |
SetThemeCursor(kThemePointingHandCursor); |
GetWindowRegion(target, kWindowCloseBoxRgn, gMouseRgn); |
break; |
case inGrow: |
SetThemeCursor(kThemeOpenHandCursor); |
GetWindowRegion(target, kWindowGrowRgn, gMouseRgn); |
break; |
case inDrag: |
SetThemeCursor(kThemeOpenHandCursor); |
GetWindowRegion(target, kWindowDragRgn, gMouseRgn); |
break; |
default: |
SetThemeCursor(kThemeArrowCursor); |
SetEmptyRgn(gMouseRgn); |
break; |
} |
} |
/* HandleEvent is the main event handling routine for the |
application. ev points to an event record returned by |
WaitNextEvent. */ |
void HandleEvent(EventRecord *ev) { |
WindowPtr target; |
/* process other event types */ |
switch (ev->what) { |
case keyDown: |
case autoKey: |
if ((ev->modifiers & cmdKey) != 0) { |
ResetMenus(); |
DoMenuCommand(MenuKey((char) (ev->message & charCodeMask))); |
ev->what = nullEvent; |
} else { |
target = FrontWindow(); |
if (IsHandyWindow(target)) |
HandyWindowKey(target, (char) (ev->message & charCodeMask), ev->modifiers); |
} |
break; |
/* for null events, track the cursor. */ |
case nullEvent: |
if (gIsFrontApp) |
ResetCursor(ev); |
break; |
case osEvt: |
if (((ev->message >> 24) & 255) == mouseMovedMessage) { |
if (gIsFrontApp) |
ResetCursor(ev); |
} else if (((ev->message >> 24) & 255) == suspendResumeMessage) { |
Boolean switchingIn; |
switchingIn = ((ev->message & resumeFlag) != 0); |
/* reset the cursor to its last known state */ |
gIsFrontApp = switchingIn; |
/* send an activate event to the frontmost window */ |
target = FrontWindow(); |
if (IsHandyWindow(target)) |
HandyWindowActivate(target, gIsFrontApp); |
} |
break; |
/* for activate events we call the window's activate event |
handler. */ |
case activateEvt: |
target = (WindowPtr) ev->message; |
if (IsHandyWindow(target)) |
HandyWindowActivate(target, ((ev->modifiers&1) != 0)); |
break; |
/* for update events we call the window's update event |
handler. As we may be called from inside of a dialog filter |
routine, if the window not a handy window then we ignore the |
update event. */ |
case updateEvt: |
target = (WindowPtr) ev->message; |
if (IsHandyWindow(target)) |
HandyWindowUpdate(target); |
break; |
/* for mouse events we call the the HandleMouseDown routine |
defined above. */ |
case mouseDown: |
HandleMouseDown(ev); |
break; |
/* Apple events. */ |
case kHighLevelEvent: |
AEProcessAppleEvent(ev); |
break; |
} |
} |
/* MyIdleInteractProc is the idle procedure called by AEInteractWithUser while we are waiting |
for the application to be pulled into the forground. It simply passes the event along |
to HandleNextEvent */ |
static pascal Boolean MyIdleInteractProc(EventRecord *theEvent, long *sleepTime, RgnHandle *mouseRgn) { |
HandleEvent(theEvent); |
return ( ! gRunning ); /* quit waiting if we're not running */ |
} |
/* ascii codes we respond to in our filter routine */ |
enum { |
ETX = 3, /* ENTER KEY */ |
CR = 13 /* RETURN KEY */ |
}; |
/* MyModalFilterProc is the modal filter routine we call while alerts are |
being displayed. Its main purpose is to ensure our activation and update |
routines are called appropriately for other windows displayed in our |
application. */ |
static pascal Boolean MyModalFilterProc(DialogPtr theDialog, EventRecord *theEvent, DialogItemIndex *itemHit) { |
switch (theEvent->what) { |
case keyDown: |
case autoKey: |
{ char key; |
key = (char) (theEvent->message & charCodeMask); |
if (key == ETX || key == CR) { |
*itemHit = ok; |
return true; |
} |
} |
break; |
case nullEvent: |
case osEvt: |
case activateEvt: |
case kHighLevelEvent: |
case updateEvt: |
HandleEvent(theEvent); |
break; |
} |
return false; |
} |
/* ParamAlert is a general alert handling routine. If Apple events exist, then it |
calls AEInteractWithUser to ensure the application is in the forground, and then |
it displays an alert after passing the s1 and s2 parameters to ParamText. */ |
short ParamAlert(short alertID, StringPtr s1, StringPtr s2) { |
AEIdleUPP aeIdleProc; |
ModalFilterUPP filterProc; |
OSStatus err; |
filterProc = NULL; |
aeIdleProc = NULL; |
aeIdleProc = NewAEIdleUPP(MyIdleInteractProc); |
if (aeIdleProc == NULL) { err = memFullErr; goto bail; } |
filterProc = NewModalFilterUPP(MyModalFilterProc); |
if (filterProc == NULL) { err = memFullErr; goto bail; } |
err = AEInteractWithUser(kNoTimeOut, NULL, aeIdleProc); |
if (err != noErr) goto bail; |
ParamText(s1, s2, NULL, NULL); |
err = Alert(alertID, filterProc); |
DisposeAEIdleUPP(aeIdleProc); |
DisposeModalFilterUPP(filterProc); |
return err; |
bail: |
if (aeIdleProc != NULL) DisposeAEIdleUPP(aeIdleProc); |
if (filterProc != NULL) DisposeModalFilterUPP(filterProc); |
return err; |
} |
/* the main program */ |
int main(void) { |
/* allocate the mouse region. We pass this region to |
WaitNextEvent after setting the cursor so we can |
receive mouse moved events. */ |
gMouseRgn = NewRgn(); |
/* set the cursor to a handy one... */ |
SetThemeCursor(kThemePointingHandCursor); |
/* install our event handlers */ |
AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false); |
/* set up the menu bar */ |
SetMenuBar(GetNewMBar(kMacOS9MenuBarID)); |
DrawMenuBar(); |
/* open some handy windows */ |
OpenHandyWindow(kHandyWindowTwo, true); |
OpenHandyWindow(kHandyWindowOne, false); |
/* run the app */ |
while (gRunning) { |
EventRecord ev; |
RgnHandle theMouseRgn; |
/* if the mouse region is empty, then we'll pass NULL to WaitNextEvent. */ |
if (EmptyRgn(gMouseRgn)) theMouseRgn = NULL; else theMouseRgn = gMouseRgn; |
/* get the next event */ |
if ( ! WaitNextEvent(everyEvent, &ev, GetCaretTime(), theMouseRgn) ) |
ev.what = nullEvent; |
/* call our handler to deal with it. */ |
HandleEvent(&ev); |
} |
/* close all of our windows. */ |
CloseAllHandyWindows(); |
/* close down and leave. */ |
DisposeRgn(gMouseRgn); |
ExitToShell(); |
return 0; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30