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.
HandyWindow.c
/* |
File: HandyWindow.c |
Description: |
HandyWindow.c implements the routines used for drawing the windows |
displayed by this application. |
Copyright: |
© Copyright 2000 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, Feb 8, 2000 -- created |
*/ |
#include "HandyWindow.h" |
#include "SampleUtils.h" |
#ifdef __APPLE_CC__ |
#include <Carbon/Carbon.h> |
#else |
#include <Carbon.h> |
#endif |
/* constants used to define the image size. */ |
#define kBoxSize 80 /* size of color swatches */ |
#define kLineSize 2 /* pensize used to draw the boxes */ |
#define kPatchSize (kBoxSize + 2) /* color swatch size with border */ |
#define kColumnCount 16 /* number of columns of color swatches */ |
#define kRowCount 16 /* number of rows of color swatches */ |
#define kImageWidth (kPatchSize * kColumnCount) /* total width of the image */ |
#define kImageHeight (kPatchSize * kRowCount) /* total height of the image */ |
#define kTimerDisplayWidth 100 /* width of timer display area */ |
/* resource ID numbers for the STR# resource used for |
storing window title strings.*/ |
enum { |
kHandyWindowStrings = 128, |
kHandyWindowTitle = 1, |
kHandyWindowSlowTitle = 2 |
}; |
/* some ascii codes for special keys on the US keyboard. we use these codes |
in the HandyWindowKey for dispatching keydown events to the scrolling |
routines. */ |
enum { |
SOH = 1, /* HOME KEY*/ |
EOT = 4, /* END KEY*/ |
VT = 11, /* PAGEUP KEY*/ |
FF = 12, /* PAGEDOWN KEY*/ |
FS = 28, /* LEFT ARROW KEY */ |
GS = 29, /* RIGHT ARROW KEY */ |
RS = 30, /* UP ARROW KEY */ |
US = 31 /* DOWN ARROW KEY */ |
}; |
/* the HandyWindowRecord type is used for storing information related |
to each HandyWindow. We use these records to store references |
to the scroll bars, and the window's activation state. Also, we store |
a pointer to the window in this structure. In this sample, we use this |
as the key value for finding a given window's HandyWindowRecord record. */ |
typedef struct HandyWindowStruct HandyWindowRecord; |
typedef HandyWindowRecord *HandyWindowPtr; |
struct HandyWindowStruct { |
HandyWindowPtr prev, next; /* pointers for our list of handy window records */ |
WindowPtr theWindow; /* a pointer to our handy window */ |
ControlHandle hScroll, vScroll; /* the horizontal and vertical scroll bars */ |
Boolean isActive; /* the window's activation state */ |
Boolean useSlowDrawing; /* if true, then add delays while drawing */ |
}; |
/* gHWFirst and gHWLast are the first and last pointers in a |
linked list of HandyWindowRecord records. */ |
static HandyWindowPtr gHWFirst = NULL, gHWLast = NULL; |
/* ScrollRectInBlack since the background for our image is black, it would |
be desirable that during scrolling operations the scrolled in area should |
be drawn 'black' instead of the default white colour. To make this happen, |
se set the background colour to black before calling scroll rect. ScrollRectInBlack |
is the same as calling ScrollRect except it scrolls in a black area rather than |
a white one. This helps avoid unnecessary white to black flickering during scrolling |
operations. */ |
static void ScrollRectInBlack(const Rect* r, short dh, short dv, RgnHandle updateRgn) { |
RGBColor rgbSave, rgbBlack = { 0x0000, 0x0000, 0x0000 }; |
/* save the pen state and background colour */ |
GetBackColor(&rgbSave); |
/* set the background colour to black */ |
RGBBackColor(&rgbBlack); |
/* scroll the bits */ |
ScrollRect(r, dh, dv, updateRgn); |
/* restore the background colour */ |
RGBBackColor(&rgbSave); |
} |
/* FindHandyWindow iterates through the list of open handy windows |
looking for a HandyWindowRecord containing a window pointer |
matching the theWindow. If one is found, a pointer to the |
HandyWindowRecord is returned. If there is no HandyWindowRecord |
record for the window in the list of open handy windows, then |
NULL is returned. */ |
static HandyWindowPtr FindHandyWindow(WindowPtr theWindow) { |
HandyWindowPtr rover; |
for (rover = gHWFirst; rover != NULL; rover = rover->next) |
if (rover->theWindow == theWindow) |
return rover; |
return NULL; |
} |
/* NewHandyWindowRec allocates a new HandyWindowRecord record, |
adds it to the list of open handy windows, and returns a pointer |
to it. If there is not enough memory to complete the allocation |
request, this routine will return NULL. */ |
static HandyWindowPtr NewHandyWindowRec(WindowPtr theWindow) { |
HandyWindowPtr local; |
local = (HandyWindowPtr) NewPtr(sizeof(HandyWindowRecord)); |
if (local == NULL) |
return NULL; |
else { |
local->theWindow = theWindow; |
local->prev = local->next = NULL; |
if (gHWFirst == NULL) { |
gHWFirst = gHWLast = local; |
} else { |
local->next = gHWFirst; |
gHWFirst->prev = local; |
gHWFirst = local; |
} |
} |
return local; |
} |
/* DisposeHandyWindowRec removes the HandyWindowRecord |
from the linked list of open handy windows and deallocates |
the storage it occupies. */ |
static void DisposeHandyWindowRec(HandyWindowPtr hwRec) { |
if (hwRec->next != NULL) |
hwRec->next->prev = hwRec->prev; |
else gHWLast = hwRec->prev; |
if (hwRec->prev != NULL) |
hwRec->prev->next = hwRec->next; |
else gHWFirst = hwRec->next; |
DisposePtr((Ptr) hwRec); |
} |
/* DrawTimer draws the time tickcount in the bottom left corner of the window. |
This little timer display allows you to monitor redraw times during |
scrolling and update operations. As you will notice, the strategy of |
only redrawing squares that will actually appear on the screen greatly |
reduces the amount of processor time spent in the drawing routine during |
scrolling and update operations. |
To draw the entire image contains 256 squares that would require |
a total of 256 ticks (4:16 Seconds) to draw when slow drawing |
is turned on. As you will notice, because of the 'draw only what needs |
to be drawn strategy', timings are much than 4:16 Seconds. */ |
static void DrawTimer(CGrafPtr wPort, HandyWindowPtr handp, unsigned long tickcount) { |
Rect windowBounds, timerRect; |
RgnHandle clipsave; |
FontInfo fin; |
unsigned long seconds, sixtieths; |
Str255 title, temp; |
/* set up for drawing */ |
GetClip((clipsave = NewRgn())); |
SetPort(wPort); |
UseThemeFont(kThemeSmallSystemFont, smSystemScript); |
GetFontInfo(&fin); |
GetPortBounds( wPort, &windowBounds); |
SetRect( &timerRect, windowBounds.left, windowBounds.bottom-15, windowBounds.left+kTimerDisplayWidth, windowBounds.bottom+1); |
timerRect.top += 1; |
/* set the clip region */ |
ClipRect(&timerRect); |
EraseRect(&timerRect); |
/* calculate the string to display */ |
seconds = tickcount/60; |
sixtieths = tickcount%60; |
NumToString(seconds, title); |
PLstrcat(title, "\p:"); |
NumToString(sixtieths, temp); |
if (temp[0] == 1) PLstrcat(title, "\p0"); |
PLstrcat(title, temp); |
PLstrcat(title, "\p Seconds"); |
/* display the string, centered. */ |
MoveTo((timerRect.right + timerRect.left - StringWidth(title)) / 2, (timerRect.bottom + timerRect.top - (fin.ascent + fin.descent))/2 + fin.ascent); |
DrawString(title); |
/* if we're not active, gray it out. */ |
if ( ! handp->isActive) |
GrayOutBox(&timerRect); |
/* restore and return. */ |
SetClip(clipsave); |
DisposeRgn(clipsave); |
} |
/* DrawHandyWindowImage is called to re-draw the contents of the window. To optimize |
drawing performance this routine limits the drawing routines it calls to only those |
ones that will have a visual result appearing on the screen. To do this, it calculates |
the intersection of the grafport's visRgn, clipRgn, and the *bounds parameter and |
tests to see if what it is about to draw resides in this region before performing |
the actual drawing operations. Since the scrolling routines set the clip region to |
the region returned by ScrollRect, this improves scrolling performance dramatically |
by limiting the drawing operation to only the part of the image scrolled into view.*/ |
static void DrawHandyWindowImage(CGrafPtr wPort, Rect *bounds, HandyWindowPtr handp) { |
RgnHandle clipsave, temp, exposedBits; |
short x, y, index, hposition, vposition; |
Rect box, steps, nameplate; |
RGBColor theColor, savedColor; |
RGBColor rgbWhite = { 0xFFFF, 0xFFFF, 0xFFFF }, rgbBlack = { 0x0000, 0x0000, 0x0000 }; |
PenState ps; |
Str255 titleStr, tempStr; |
FontInfo fin; |
CTabHandle clut; |
Pattern blackPen; |
unsigned long startTicks, stopTicks; |
/* start the timer */ |
startTicks = TickCount(); |
/* set up locals */ |
hposition = GetControlValue(handp->hScroll); |
vposition = GetControlValue(handp->vScroll); |
index = 0; |
temp = NewRgn(); |
clipsave = NewRgn(); |
exposedBits = NewRgn(); |
clut = GetCTable(72); |
/* calculate content region */ |
SetPort(wPort); |
UseThemeFont(kThemeSystemFont, smSystemScript); |
GetFontInfo(&fin); |
GetForeColor(&savedColor); |
SetOrigin(0, 0); |
GetClip(clipsave); |
/* calculate the region of the content that needs to |
be redrawn. Here, this is the intersection of the content |
area, the current clipping region, and the window's visRgn. */ |
RectRgn(exposedBits, bounds); |
/* intersect with the clip region */ |
SectRgn(clipsave, exposedBits, exposedBits); |
/* intersect with the visible region */ |
GetPortVisibleRegion(wPort, temp); |
SectRgn(temp, exposedBits, exposedBits); |
/* only draw, if there's actually something to draw... */ |
if ( ! EmptyRgn(exposedBits) ) { |
/* offset the region to it's location in the contents. |
We do this explicitly because although SetOrigin will move |
the portRect and visRgn, it will not move the clipRgn. */ |
OffsetRgn(exposedBits, hposition, vposition); |
/* set the origin and clipping region */ |
SetOrigin(hposition, vposition); |
SetClip(exposedBits); |
/* set up for drawing */ |
GetPenState(&ps); |
PenSize(kLineSize, kLineSize); |
PenMode(patCopy); |
PenPat(GetQDGlobalsBlack(&blackPen)); |
RGBForeColor(&rgbBlack); |
RGBBackColor(&rgbWhite); |
/* erase the background *black* */ |
PaintRgn(exposedBits); |
/* iterate through rows and columns */ |
for (y=0; y<kColumnCount; y++) |
for (x=0; x<kRowCount; x++, index++) { |
/* calculate the location for this color swatch */ |
SetRect(&box, 0, 0, kBoxSize, kBoxSize); |
OffsetRect(&box, x*kPatchSize + 1, y*kPatchSize + 1); |
/* only draw this item if the drawing operation will have |
some noticable effect on the screen. Overall, this will save |
some time as we're not doing any of the following drawing |
or calculation operations. */ |
RectRgn(temp, &box); |
SectRgn(exposedBits, temp, temp); |
if ( EmptyRgn(temp) ) |
continue; |
/* set the colour */ |
theColor = (**clut).ctTable[ index ].rgb; |
RGBForeColor(&theColor); |
/* draw an x in the box */ |
MoveTo(box.left, box.top); |
LineTo(box.right - kLineSize, box.bottom - kLineSize); |
MoveTo(box.right - kLineSize, box.top); |
LineTo(box.left, box.bottom - kLineSize); |
/* for an illustration of how drawing only the exposed |
area can affect timing, uncomment the following line. */ |
/* the following line is used to slow down drawing |
to simulate a complex drawing task. given that the default |
size of a window will contain 6 X 4 squares, it will |
take at the very least 24 ticks (nearly half a second) |
to redraw the entire window. But, since during scrolling |
operations only the part of the image that needs to be |
redrawn is being drawn, this doesn't really matter. */ |
if (handp->useSlowDrawing) { |
unsigned long finalTicks; |
Delay(1, &finalTicks); |
} |
/* draw some rescinding frames */ |
steps = box; |
while ( ! EmptyRect(&steps)) { |
FrameRect(&steps); |
InsetRect(&steps, kLineSize + 1, kLineSize + 1); |
} |
/* calculate the cell title */ |
NumToString(x, titleStr); |
PLstrcat(titleStr, "\p, "); |
NumToString(y, tempStr); |
PLstrcat(titleStr, tempStr); |
/* calculate a box for the title */ |
SetRect(&nameplate, 0, 0, |
StringWidth(titleStr) + kLineSize*6, |
fin.ascent + fin.descent + kLineSize*2); |
/* center it in the color swatch */ |
OffsetRect(&nameplate, |
(box.right + box.left - nameplate.right) / 2, |
(box.bottom + box.top - nameplate.bottom) / 2); |
/* erase it and draw a frame */ |
EraseRect(&nameplate); |
FrameRect(&nameplate); |
/* draw the text in black and white */ |
RGBForeColor(&rgbBlack); |
MoveTo(nameplate.left + kLineSize*3, nameplate.top + fin.ascent + kLineSize); |
DrawString(titleStr); |
} |
/* reset the origin and clipping region */ |
SetPort(wPort); |
SetOrigin(0, 0); |
SetClip(clipsave); |
} |
/* clean up */ |
RGBForeColor(&savedColor); |
DisposeRgn(clipsave); |
DisposeRgn(exposedBits); |
DisposeRgn(temp); |
SetPenState(&ps); |
DisposeCTable(clut); |
/* stop the timer */ |
stopTicks = TickCount(); |
DrawTimer(wPort, handp, stopTicks - startTicks); |
} |
/* IsHandyWindow returns true if the window pointer |
in the HandyWindow parameter is not NULL and |
points to the about box. */ |
Boolean IsHandyWindow(WindowPtr theWindow) { |
return (FindHandyWindow(theWindow) != NULL); |
} |
/* GetHandyWindowGrowLimits sets the sizerect rectangle parameter |
to values appropriate for passing to the GrowWindow routine. */ |
OSErr GetHandyWindowGrowLimits(WindowPtr theWindow, Rect *sizerect) { |
HandyWindowPtr handp; |
/* get our window globals */ |
handp = FindHandyWindow(theWindow); |
if (handp == NULL) return paramErr; |
SetRect(sizerect, 128, 64, kImageWidth+15, kImageWidth+15); |
return noErr; |
} |
/* HandyWindowSizeChanged should be called whenever the size of a window |
created by OpenHandyWindow has been changed by either a call to |
ZoomWindow or SizeWindow. */ |
OSErr HandyWindowSizeChanged(WindowPtr theWindow) { |
HandyWindowPtr handp; |
Rect wRect, hsRect, vsRect, contentRect, timerRect; |
CGrafPtr wPort; |
short hmax, vmax; |
RgnHandle invalidRegion; |
/* get our window globals */ |
handp = FindHandyWindow(theWindow); |
if (handp == NULL) return paramErr; |
/* set up locals */ |
invalidRegion = NewRgn(); |
SetPort((wPort = GetWindowPort(theWindow))); |
GetPortBounds( wPort, &wRect); |
contentRect = wRect; |
contentRect.right -= 15; |
contentRect.bottom -= 15; |
/* accumulate the old scroll bar and timer rectangles into the update region */ |
RgnUnionRect(invalidRegion, GetControlBounds(handp->hScroll, &hsRect)); |
SetRect( &timerRect, 0, hsRect.top, kTimerDisplayWidth, hsRect.bottom); |
RgnUnionRect(invalidRegion, &timerRect); |
RgnUnionRect(invalidRegion, GetControlBounds(handp->vScroll, &vsRect)); |
/* calculate the boxes */ |
SetRect( &timerRect, wRect.left, wRect.bottom-15, wRect.left+kTimerDisplayWidth, wRect.bottom+1); |
SetRect( &hsRect, wRect.left+kTimerDisplayWidth, wRect.bottom-15, wRect.right-14, wRect.bottom+1); |
SetRect( &vsRect, wRect.right-15, wRect.top-1, wRect.right+1, wRect.bottom-14); |
/* reposition the scroll bars */ |
SetControlBounds( handp->hScroll, &hsRect); |
SetControlBounds( handp->vScroll, &vsRect); |
vsRect.bottom += 15; /* include the grow box */ |
RgnUnionRect(invalidRegion, &timerRect); |
RgnUnionRect(invalidRegion, &hsRect); |
RgnUnionRect(invalidRegion, &vsRect); |
/* set the horizontal scroll bar maximum and value */ |
hmax = kImageWidth - contentRect.right; |
if (hmax < 0) hmax = 0; |
if (GetControlValue(handp->hScroll) > hmax) { |
SetControlValue(handp->hScroll, hmax); |
RgnUnionRect(invalidRegion, &contentRect); |
} |
SetControlMaximum(handp->hScroll, hmax); |
SetControlViewSize(handp->hScroll, kImageWidth); |
/* set the vertical scroll bar maximum and value */ |
vmax = kImageWidth - contentRect.bottom; |
if (vmax < 0) vmax = 0; |
if (GetControlValue(handp->vScroll) > vmax) { |
SetControlValue( handp->vScroll, vmax); |
RgnUnionRect(invalidRegion, &contentRect); |
} |
SetControlMaximum(handp->vScroll, vmax); |
SetControlViewSize(handp->vScroll, kImageWidth); |
/* announce the accumulated update region */ |
InvalWindowRgn(theWindow, invalidRegion); |
DisposeRgn(invalidRegion); |
return noErr; |
} |
/* OpenHandyWindow opens a handy window using the WIND resource |
with the windowID ID. If successful, OpenHandyWindow returns a |
pointer to the newly created window. */ |
WindowPtr OpenHandyWindow(short windowID, Boolean drawSlow) { |
Rect wRect, hsRect, vsRect; |
HandyWindowPtr handp; |
WindowPtr theWindow; |
ControlHandle hscroll, vscroll; |
Str255 title; |
/* set up locals */ |
handp = NULL; |
theWindow = NULL; |
hscroll = vscroll = NULL; |
/* get the window */ |
theWindow = GetNewCWindow(windowID, NULL, (WindowPtr)(-1)); |
if (theWindow == NULL) goto bail; |
handp = NewHandyWindowRec(theWindow); |
if (handp == NULL) goto bail; |
/* set up the zoom size */ |
SetWindowStandardStateSize(theWindow, kImageWidth+15, kImageWidth+15); |
/* create the scroll bars */ |
GetPortBounds(GetWindowPort(theWindow), &wRect); |
SetRect(&hsRect, wRect.left, wRect.bottom-15, wRect.right-14, wRect.bottom+1); |
SetRect(&vsRect, wRect.right-15, wRect.top-1, wRect.right+1, wRect.bottom-14); |
hscroll = NewControl(theWindow, &hsRect, "\p", true, 0, 0, 100, kControlScrollBarLiveProc, (long) handp); |
if (hscroll == NULL) goto bail; |
HiliteControl(hscroll, 255); |
vscroll = NewControl(theWindow, &vsRect, "\p", true, 0, 0, 100, kControlScrollBarLiveProc, (long) handp); |
if (vscroll == NULL) goto bail; |
HiliteControl(vscroll, 255); |
/* store our data */ |
handp->hScroll = hscroll; |
handp->vScroll = vscroll; |
handp->isActive = false; |
handp->useSlowDrawing = drawSlow; |
/* readjust the coordinates and we're done... */ |
HandyWindowSizeChanged(theWindow); |
GetIndString(title, kHandyWindowStrings, (handp->useSlowDrawing ? kHandyWindowSlowTitle : kHandyWindowTitle)); |
SetWTitle(theWindow, title); |
ShowWindow(theWindow); |
return theWindow; |
bail: |
if (hscroll != NULL) DisposeControl(hscroll); |
if (vscroll != NULL) DisposeControl(vscroll); |
if (theWindow != NULL) DisposeWindow(theWindow); |
if (handp != NULL) DisposeHandyWindowRec(handp); |
return NULL; |
} |
/* HandyWindowCloseWindow closes the about box window. |
this routine deallocates any structures allocated |
by the OpenHandyWindow. */ |
OSErr HandyWindowCloseWindow(WindowPtr theWindow) { |
HandyWindowPtr handp; |
handp = FindHandyWindow(theWindow); |
if (handp == NULL) return paramErr; |
DisposeControl(handp->hScroll); |
DisposeControl(handp->vScroll); |
DisposeWindow(handp->theWindow); |
DisposeHandyWindowRec(handp); |
return noErr; |
} |
/* CloseAllHandyWindows closes every handy window |
opened by calling OpenHandyWindow. */ |
void CloseAllHandyWindows(void) { |
while (gHWFirst != NULL) |
HandyWindowCloseWindow(gHWFirst->theWindow); |
} |
/* HandyWindowUpdate should be called for update events |
directed at a handy window. It calls |
BeginUpdate and EndUpdate and does all of the |
drawing required to refresh the handy window. */ |
OSErr HandyWindowUpdate(WindowPtr theWindow) { |
HandyWindowPtr handp; |
CGrafPtr windowGp; |
Rect contents, timerRect, wRect; |
/* set up our variables */ |
handp = FindHandyWindow(theWindow); |
if (handp == NULL) return paramErr; |
/* set up the window */ |
windowGp = GetWindowPort(theWindow); |
SetPort(windowGp); |
GetPortBounds(windowGp, &wRect); |
/* make sure the timer rectangle is part of the update region */ |
SetRect( &timerRect, wRect.left, wRect.bottom-15, wRect.left+kTimerDisplayWidth, wRect.bottom+1); |
InvalWindowRect(theWindow, &timerRect); |
contents = wRect; |
contents.right -= 15; |
contents.bottom -= 15; |
/* set the window for drawing */ |
BeginUpdate(theWindow); |
/* draw the controls and grow icon */ |
Draw1Control(handp->hScroll); |
Draw1Control(handp->vScroll); |
DrawGrowIcon(theWindow); |
/* update the image */ |
DrawHandyWindowImage(windowGp, &contents, handp); |
/* if the window is inactive, gray out the image */ |
if ( ! handp->isActive) |
GrayOutBox(&contents); |
/* done drawing */ |
EndUpdate(theWindow); |
return noErr; |
} |
/* HandyWindowActivate should be called for activate events |
directed at a handy window. */ |
OSErr HandyWindowActivate(WindowPtr theWindow, Boolean activate) { |
HandyWindowPtr handp; |
/* verify the window is a handy window, get the variables */ |
if ((handp = FindHandyWindow(theWindow)) == NULL) return paramErr; |
/* if the activate state is changing */ |
if (handp->isActive != activate) { |
Rect bounds; |
CGrafPtr wPort; |
/* set the new activation state */ |
handp->isActive = activate; |
wPort = GetWindowPort(theWindow); |
/* redraw as appropriate for new activation state */ |
SetPort(wPort); |
GetPortBounds(wPort, &bounds); |
bounds.right -= 15; |
bounds.bottom -= 15; |
if (handp->isActive) { |
/* if the window is active, enable scroll bars and redraw contents */ |
HiliteControl(handp->hScroll, 0); |
HiliteControl(handp->vScroll, 0); |
InvalWindowRect(theWindow, &bounds); |
} else { |
/* if the window is inactive, disable the scroll bars and gray out contents */ |
HiliteControl(handp->hScroll, 255); |
HiliteControl(handp->vScroll, 255); |
GrayOutBox(&bounds); |
DrawGrowIcon(theWindow); |
DrawTimer(wPort, handp, 0); |
} |
} |
return noErr; |
} |
/* gPreviousScrollValue contains the last value of the scroll bar before the |
most recent scroll bar tracking operation. This value is used for tracking |
previous scroll values so we can determine the distance the display needs |
to be scrolled during live scrolling and indicator draggs. It is only used |
in when the click is in the kControlIndicatorPart part. */ |
short gPreviousScrollValue = 0; |
/* WindowScrollBarProc is the callback procedure we pass to TrackControl |
during the handling of mouse clicks in scroll bars. It is also possible |
to call this routine specifying one of the part codes to obtain different |
scrolling behaviors (we do this for the arrow keys in the HandyWindowKey |
routine defined below). */ |
static pascal void WindowScrollBarProc(ControlHandle theControl, short partCode) { |
short prev, max, next, *delta; |
short dh, dv, pageSize; |
HandyWindowPtr handp; |
Rect content; |
RgnHandle parttodraw, clipsave; |
CGrafPtr wPort; |
/* set up our local variables */ |
handp = (HandyWindowPtr) GetControlReference(theControl); |
SetPort((wPort = GetWindowPort(handp->theWindow))); |
GetPortBounds(wPort, &content); |
content.right -= 15; |
content.bottom -= 15; |
/* initialize the horizontal and the vertical delta variables. |
dv and dh are used to store the vertical and horizontal distance |
the display is to be scrolled. */ |
dh = dv = 0; |
/* calculate the page size and the delta variable |
we are going to be using. The delta variable we are |
changing and the page size depends on the scroll |
bar we are tracking. */ |
if (theControl == handp->vScroll) { |
pageSize = content.bottom - content.top; |
delta = &dv; |
} else { |
pageSize = content.right - content.left; |
delta = &dh; |
} |
/* calculate the previous and next scroll bar |
positions. */ |
switch (partCode) { |
/* for the up and down buttons, we scroll |
sizteen pixels */ |
case kControlUpButtonPart: |
prev = GetControlValue(theControl); |
next = prev - 16; |
break; |
case kControlDownButtonPart: |
prev = GetControlValue(theControl); |
next = prev + 16; |
break; |
/* for the page up and page down areas, we scroll |
one page at a time (the width of the window for the |
horizontal scroll bar, and the height of the window for |
the vertical scroll bar. */ |
case kControlPageUpPart: |
prev = GetControlValue(theControl); |
next = prev - pageSize; |
break; |
case kControlPageDownPart: |
prev = GetControlValue(theControl); |
next = prev + pageSize; |
break; |
/* kControlIndicatorPart will be passed to this routine |
during live scrolling operations or when the user is tracking |
the indicator part. Here, we calculate the distance the indicator |
has been moved using the gPreviousScrollValue global variable. |
note, we set the gPreviousScrollValue variable to the current |
scroll bar value so we can calculate the next delta the next time |
we are called. */ |
case kControlIndicatorPart: |
prev = gPreviousScrollValue; |
gPreviousScrollValue = next = GetControlValue(theControl); |
break; |
/* if the user moves the mouse outside of the part that was |
originally clicked in, we may be called with a part code of zero. |
nothing to do here...*/ |
default: |
return; |
} |
/* verify that our next value is in the allowable range of value */ |
max = GetControlMaximum(theControl); |
if (next < 0) next = 0; else if (next > max) next = max; |
/* calculate the change. If there's no change, we're done. |
This is an important check as calling ScrollRect(.., 0, 0, xx) |
will crash on some systems. */ |
*delta = prev - next; |
if (*delta == 0) return; |
/* set the new control bar value. if we're tracking the indicator |
next will already be the scroll value so there's no need to change |
it here, that's why there's the extra check */ |
if (GetControlValue(theControl) != next) |
SetControlValue(theControl, next); |
/* save the current clipping region */ |
GetClip((clipsave = NewRgn())); |
/* scroll the content area using our delta. This call to |
ScrollRect will set the region parttodraw to the new |
'undrawn' area exposed by the operation */ |
ScrollRectInBlack(&content, dh, dv, (parttodraw = NewRgn())); |
/* DrawHandyWindowImage will only perform drawing operations |
that are visible in the current clipping region, so to tell it that it does |
not have to draw the entire image, we set the clipping region to |
only the part that has been erased by the scrolling operation. */ |
SetClip(parttodraw); |
/* draw the scrolled in area */ |
DrawHandyWindowImage(GetWindowPort(handp->theWindow), &content, handp); |
/* restore the clipping region and clean up our storage */ |
SetClip(clipsave); |
DisposeRgn(clipsave); |
DisposeRgn(parttodraw); |
} |
/* HandyWindowScrollUsingHand is called to scroll the window's view when the |
mouse is clicked in the window's contents. basically, this routine redraws the |
cursor as the 'grabbing hand' and then follows the mouse scrolling the contents |
and adjusting the scroll bar values as appropriate. */ |
static void HandyWindowScrollUsingHand(HandyWindowPtr handp, Point where) { |
Point currentpt, lastpt, startpt; |
Point start, next, last, offset; |
RgnHandle clip, drawp; |
Rect mypin; |
Rect content; |
CGrafPtr wPort; |
/* set up our local variables */ |
SetPort((wPort = GetWindowPort(handp->theWindow))); |
GetPortBounds(wPort, &content); |
content.right -= 15; |
content.bottom -= 15; |
startpt = lastpt = where; |
/* calculate the starting point */ |
SetPt(&start, GetControlValue(handp->hScroll), GetControlValue(handp->vScroll)); |
last = start; |
/* create a pin rectangle. We use this rectangle with the window manager |
PinRect routine to ensure the next value we calculate will always be |
within the allowable scrollable range. */ |
SetRect(&mypin, 0, 0, GetControlMaximum(handp->hScroll)+1, GetControlMaximum(handp->vScroll)+1); |
clip = NewRgn(); |
drawp = NewRgn(); |
/* set the cursor to the grabbing hand */ |
SetThemeCursor(kThemeClosedHandCursor); |
/* hilite the scroll bar indicators. This provides some visual feedback indicating |
the user is now scrolling using both scroll bar indicators at the same time. */ |
HiliteControl(handp->hScroll, kControlIndicatorPart); |
HiliteControl(handp->vScroll, kControlIndicatorPart); |
/* while the mouse is being held down */ |
while (StillDown()) { |
/* only recalculate when the mouse moves */ |
GetMouse(¤tpt); |
if ( ! EqualPt(currentpt, lastpt)) { |
lastpt = currentpt; |
/* calculate the new scroll position based on cursor movement */ |
next = startpt; |
SubPt(currentpt, &next); |
AddPt(start, &next); |
/* pin the new position into the maximum allowable range */ |
(* (long*) &next) = PinRect(&mypin, next); |
/* calculate the delta from the last drawn position */ |
offset = next; |
SubPt(last, &offset); |
/* only redraw and adjust scroll bar values if we |
are going to move the image.... */ |
if (offset.h != 0 || offset.v != 0) { |
/* set the new scrolling position */ |
SetControlValue(handp->hScroll, next.h); |
SetControlValue(handp->vScroll, next.v); |
last = next; |
/* save the clipping region */ |
GetClip(clip); |
/* scroll the image. drawp will be set to the area exposed by |
the scrolling operation. */ |
ScrollRectInBlack(&content, -offset.h, -offset.v, drawp); |
/* DrawHandyWindowImage will only perform drawing operations |
inside of the current clipping region. So, to speed up drawing, we |
set the clip region to the area exposed by the scroll. */ |
SetClip(drawp); |
/* redraw the empty part of the screen */ |
DrawHandyWindowImage(GetWindowPort(handp->theWindow), &content, handp); |
/* restore the clip region */ |
SetClip(clip); |
} |
} |
} |
/* clean up our storage */ |
DisposeRgn(clip); |
DisposeRgn(drawp); |
/* unhilite the scroll bar indicators */ |
HiliteControl(handp->hScroll, 0); |
HiliteControl(handp->vScroll, 0); |
/* set the cursor back to the open hand */ |
SetThemeCursor(kThemeOpenHandCursor); |
} |
/* HandyWindowScrollTo can be called to scroll the image to a particular |
location on screen. This routine attempts to scroll the image so that |
the location refered to by position is aligned with the top left corner of |
the window. Of course, the actual distance scrolled is pinned within |
the allowable range of scroll bar values. */ |
OSErr HandyWindowScrollTo(WindowPtr theWindow, Point position) { |
HandyWindowPtr handp; |
Point next, last, offset; |
RgnHandle clip, drawp; |
Rect mypin; |
Rect content; |
CGrafPtr wPort; |
/* set up our local variables */ |
handp = FindHandyWindow(theWindow); |
if (handp == NULL) return paramErr; |
SetPort((wPort = GetWindowPort(handp->theWindow))); |
GetPortBounds(wPort, &content); |
content.right -= 15; |
content.bottom -= 15; |
SetRect(&mypin, 0, 0, GetControlMaximum(handp->hScroll)+1, GetControlMaximum(handp->vScroll)+1); |
/* set the cursor to the grabbing hand */ |
SetPt(&last, GetControlValue(handp->hScroll), GetControlValue(handp->vScroll)); |
clip = NewRgn(); |
drawp = NewRgn(); |
next = position; |
(* (long*) &next) = PinRect(&mypin, next); |
/* calculate the delta from the last drawn position */ |
offset = next; |
SubPt(last, &offset); |
/* if the new drawing position has changed, scroll and redraw... */ |
if (offset.h != 0 || offset.v != 0) { |
/* set the new scrolling position */ |
SetControlValue(handp->hScroll, next.h); |
SetControlValue(handp->vScroll, next.v); |
/* scroll the image */ |
GetClip(clip); |
ScrollRectInBlack(&content, -offset.h, -offset.v, drawp); |
/* set the clip region to the area exposed by the scroll */ |
SetClip(drawp); |
/* redraw the empty part of the screen */ |
DrawHandyWindowImage(GetWindowPort(handp->theWindow), &content, handp); |
/* restore the clip region */ |
SetClip(clip); |
} |
DisposeRgn(clip); |
DisposeRgn(drawp); |
return noErr; |
} |
/* HandyWindowActivate should be called for activate events |
directed at the about box window. */ |
OSErr HandyWindowMouse(WindowPtr theWindow, Point where, short modifiers) { |
HandyWindowPtr handp; |
CGrafPtr wPort; |
Rect content, hscroll, vscroll; |
ControlHandle theControl; |
/* find the windows variables. If there are none, then it's |
not one of our windows. */ |
if ((handp = FindHandyWindow(theWindow)) == NULL) return paramErr; |
/* set up locals, thePort, etc... */ |
wPort = GetWindowPort(theWindow); |
SetPort(wPort); |
GetPortBounds(wPort, &content); |
content.right -= 15; |
content.bottom -= 15; |
theControl = NULL; |
/* check if its in a scroll bar. We do it this way incase other controls |
are being drawn in the window's content area. Since they are being |
drawn in a different coordinate system, when this call is made is may |
be possible that they overlap. */ |
if (PtInRect(where, GetControlBounds(handp->hScroll, &hscroll))) |
theControl = handp->hScroll; |
else if (PtInRect(where, GetControlBounds(handp->vScroll, &vscroll))) |
theControl = handp->vScroll; |
/* if it was a scroll bar, then we handle the click as either an |
indicator click or as a regular part click. */ |
if (theControl != NULL) { |
short partCode; |
static ControlActionUPP gMyActionProc = NULL; |
/* allocate our action procedure pointer. */ |
if (gMyActionProc == NULL) |
gMyActionProc = NewControlActionUPP(WindowScrollBarProc); |
/* find the part where the mouse was clicked. */ |
partCode = TestControl(theControl, where); |
if (partCode == kControlIndicatorPart) { |
/* if we're dragging the indicator, then we set the cursor |
so it looks like we are grabbing the indicator. */ |
SetThemeCursor(kThemeClosedHandCursor); |
/* save the previous scroll value. For live scrolling, this |
value is used inside of WindowScrollBarProc. */ |
gPreviousScrollValue = GetControlValue(theControl); |
TrackControl(theControl, where, gMyActionProc); |
/* if the scroll bar does not support live scrolling, then this |
is where the actual scrolling will occur. Performing the scrolling |
at this point is 'the old way' kControlIndicatorPart (inThumb) drags |
were handled. It's not really necessary here, but it has been included |
for illustration. Without live scrolling, we would also call TrackControl |
with a NULL action procedure parameter. */ |
if (gPreviousScrollValue != GetControlValue(theControl)) |
WindowScrollBarProc(theControl, kControlIndicatorPart); |
/* restore the cursor to the 'open hand' since we are 'letting go' |
of the indicator. */ |
SetThemeCursor(kThemeOpenHandCursor); |
} else if (partCode == kControlUpButtonPart || partCode == kControlDownButtonPart |
|| partCode == kControlPageUpPart || kControlPageDownPart) { |
/* if it's another part, then we handle all of the scrolling in our |
tracking procedure. */ |
TrackControl(theControl, where, gMyActionProc); |
} |
} else if (PtInRect(where, &content)) { |
HandyWindowScrollUsingHand(handp, where); |
} |
return noErr; |
} |
/* HandyWindowKey is called in response to key down events. Here, we dispatch |
special keys such as the arrows, page up, page down, home, and end to |
their equivalent actions using the scroll bars. */ |
OSErr HandyWindowKey(WindowPtr theWindow, char key, short modifiers) { |
HandyWindowPtr handp; |
Point position; |
/* find the window's variables. if it has none, it's |
not one of our windows. */ |
if ((handp = FindHandyWindow(theWindow)) == NULL) return paramErr; |
if ( ! handp->isActive) return paramErr; |
/* set up locals */ |
switch (key) { |
case SOH: /* HOME KEY*/ |
SetPt(&position, 0, 0); |
HandyWindowScrollTo(theWindow, position); |
break; |
case EOT: /* END KEY*/ |
SetPt(&position, kImageWidth, kImageHeight); |
HandyWindowScrollTo(theWindow, position); |
break; |
case VT: /* PAGEUP KEY*/ |
WindowScrollBarProc(handp->vScroll, kControlPageUpPart); |
break; |
case FF: /* PAGEDOWN KEY*/ |
WindowScrollBarProc(handp->vScroll, kControlPageDownPart); |
break; |
case FS: /* LEFT ARROW KEY */ |
WindowScrollBarProc(handp->hScroll, kControlUpButtonPart); |
break; |
case GS: /* RIGHT ARROW KEY */ |
WindowScrollBarProc(handp->hScroll, kControlDownButtonPart); |
break; |
case RS: /* UP ARROW KEY */ |
WindowScrollBarProc(handp->vScroll, kControlUpButtonPart); |
break; |
case US: /* DOWN ARROW KEY */ |
WindowScrollBarProc(handp->vScroll, kControlDownButtonPart); |
break; |
default: |
return paramErr; |
break; |
} |
return noErr; |
} |
/* HandyWindowGetSlow returns the state of the handy window's drawing |
speed. For illustration, it is possible to set the drawing speed to either |
slow or fast. */ |
OSErr HandyWindowGetSlow(WindowPtr theWindow, Boolean *drawnSlowly) { |
HandyWindowPtr handp; |
if ((handp = FindHandyWindow(theWindow)) == NULL) return paramErr; |
*drawnSlowly = handp->useSlowDrawing; |
return noErr; |
} |
/* HandyWindowSetSlow sets the state of the handy window's drawing |
speed and posts an update event so the contents will be re-drawn |
showing the new speed. For illustration, it is possible to set the |
drawing speed to either slow or fast. */ |
OSErr HandyWindowSetSlow(WindowPtr theWindow, Boolean drawSlowly) { |
HandyWindowPtr handp; |
Rect contentRect; |
Str255 title; |
if ((handp = FindHandyWindow(theWindow)) == NULL) return paramErr; |
handp->useSlowDrawing = drawSlowly; |
GetPortBounds( GetWindowPort( theWindow), &contentRect); |
contentRect.right -= 15; |
contentRect.bottom -= 15; |
InvalWindowRect(theWindow, &contentRect); |
GetIndString(title, kHandyWindowStrings, (handp->useSlowDrawing ? kHandyWindowSlowTitle : kHandyWindowTitle)); |
SetWTitle(theWindow, title); |
return noErr; |
} |
/* HandyWindowCursor is called when a handy window is the frontmost window and |
the HandySample application is the frontmost application. where is a point in |
the window's coordinates, and modifiers is the state of the modifier |
keys copied from the event record. mouseRgn is a handle to a region. If |
HandyWindowCursor sets the cursor, then it will also set this region to |
the region where it is appropriate for the cursor to remain as it has been |
set (in global coordinates). this region is appropriate for passing to WaitNextEvent |
in the mouse region parameter. */ |
Boolean HandyWindowCursor(WindowPtr theWindow, Point where, short modifiers, RgnHandle mouseRgn) { |
HandyWindowPtr handp; |
CGrafPtr wPort; |
Rect contentRect; |
Point offset; |
/* find the window's variables. if it has none, it's |
not one of our windows. */ |
if ((handp = FindHandyWindow(theWindow)) == NULL) return false; |
if ( ! handp->isActive) return false; |
/* set up locals */ |
SetPort((wPort = GetWindowPort( theWindow))); |
/* calculate the window's origin in global coordinates */ |
SetPt(&offset, 0, 0); |
LocalToGlobal( &offset); |
/* calculate the content rectangle */ |
GetPortBounds( wPort, &contentRect); |
contentRect.right -= 15; |
contentRect.bottom -= 15; |
/* find the click location */ |
if (PtInRect( where, &contentRect)) { |
/* if it's in the content area, then we use the open hand. */ |
RectRgn(mouseRgn, &contentRect); |
OffsetRgn(mouseRgn, offset.h, offset.v); |
SetThemeCursor(kThemeOpenHandCursor); |
return true; |
/* for scroll bars, we call the routine SetScrollBarCursor defined |
in SampleUtils.h. That routine sets the cursor to a hand when it's |
over the scroll bar's indicator part. Otherwise it sets the cursor to |
a pointing finger. */ |
} else if (SetScrollBarCursor(handp->hScroll, where, mouseRgn)) { |
OffsetRgn(mouseRgn, offset.h, offset.v); |
return true; |
} else if (SetScrollBarCursor(handp->vScroll, where, mouseRgn)) { |
OffsetRgn(mouseRgn, offset.h, offset.v); |
return true; |
} |
/* if the mouse isn't in our window, then we have nothing to say about it. */ |
return false; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30