Building PICT 1
Volume Number: | | 10
|
Issue Number: | | 2
|
Column Tag: | | Getting Started
|
Related Info: Color Quickdraw List Manager
Using The List Manager
Building & using a list of PICT Resources
By Dave Mark, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
At the end of last months column, I said that this months column would go back to Color Quickdraw. I lied. I was working on a program I was writing for Daniel (it teaches little kids to read) when I got embroiled in a knotty problem involving the List Manager. As I was wrestling with the problem, I came up with the idea for this months column. I liked the program so much that, instead of waiting till next month, I decided to go ahead and present it now. Dont worry, well get back to Color Quickdraw eventually...
PictLister
This months program is called PictLister. PictLister lets you create a window listing all the PICT resources available to your program. Note that this includes PICT resources found in your applications resource fork as well as those found in any resource files opened by the System. Figure 1 shows the PICT resources found by my copy of PictLister.
Figure 1. A PictLister window.
When you double-click on the name of a PICT resource, PictLister creates a new window containing the PICT.
Creating the PictLister Resources
Start by creating a folder named PictLister inside your Development folder. Fire up ResEdit and create a file named PictLister.Π.rsrc inside your PictLister folder.
Now create an ALRT resource (along with a corresponding DITL resource) for our error alert. The ALRT resource should have a Top: of 40, a Left: of 30, a Height: of 116, and a Width: of 292. Be sure to set the DITL ID: to 128.
Next, create a DITL with an id of 128. Use the specifications in Figure 2 to create the OK button and the specifications in Figure 3 to create the error alerts static text item.
Figure 2. Specifications for the error alerts OK button.
Figure 3. Specifications for the error alerts static text item.
Next, create an MBAR resource with an id of 128. The MBAR should list three MENU ids: 128, 129, and 130. Create three MENU resources according to the specifications in Figure 4. Be sure to include a separator line as the third item in the File menu.
Figure 4. The three MENU resources.
Finally, go into the scrapbook (or your favorite graphics application) and use Copy and Paste to create a series of PICT resources in the resource file. Select Get Resource Info from the Resource menu and give each resource a name. Its important to name your PICT resources, since PictLister uses the PICTs name to represent the PICT in a PictLister window. Figure 5 shows the Get Resource Info window for my first PICT resource. Notice that the Purgeable check box is checked.
Figure 5. Get Resource Info for my first PICT resource.
Creating the PictLister Project
Save your changes and quit ResEdit. Launch THINK C and create a new project named PictLister.Π in the PictLister folder. Add MacTraps to the project. Next, create a new source code window, save it as PictLister.c and add it to the project.
Type in the following source code:
/* 1 */
#define kMBARResID 128
#define kSleep 60L
#define kMoveToFront (WindowPtr)-1L
#define kNilFilterProc (ProcPtr)0L
#define kEmptyString "\p"
#define kHasGoAway true
#define kInvisible false
#define kListDefProc 0
#define kDontDrawYet false
#define kHasGrow true
#define kHasHScrolltrue
#define kHasVScrolltrue
#define kFindNexttrue
#define kListWindow0
#define kDAWindow1
#define kUnknownWindow 2
#define kPictWindow3
#define kNilWindow 4
#define kMinWindowWidth 210
#define kMinWindowHeight 63
#define kWindowHeight255
#define kMinPictWinHeight 50
#define kMinPictWinWidth 150
#define mApple 128
#define iAbout 1
#define mFile 129
#define iNewList 1
#define iClose 2
#define iQuit 4
#define kErrorAlertID128
/************************/
/* Typedefs */
/************************/
typedef struct
{
WindowRecord w;
short wType;
ListHandle list;
} ListRecord, *ListPeek;
typedef struct
{
WindowRecord w;
short wType;
short PictResID;
} PictRecord, *PictPeek;
/*************/
/* Globals */
/*************/
Boolean gDone;
short gNewWindowX = 20, gNewWindowY = 50;
/***************/
/* Functions */
/***************/
void ToolboxInit( void );
void MenuBarInit( void );
void CreateListWindow( void );
void DestroyWindow( WindowPtr w );
void EventLoop( void );
void DoEvent( EventRecord *eventPtr );
void DoUpdate( EventRecord *eventPtr );
void DoActivate( EventRecord *eventPtr );
void HandleMouseDown( EventRecord *eventPtr );
void DoContentClick( EventRecord *eventPtr, WindowPtr w );
void CreatePictWindow( ListHandle list );
void BumpGlobalXandY( void );
void DoGrow( EventRecord *eventPtr, WindowPtr w );
void HandleMenuChoice( long menuChoice );
void HandleAppleChoice( short item );
void HandleFileChoice( short item );
void CenterWindow( WindowPtr w );
void CenterPict( PicHandle picture, Rect *destRectPtr );
short WindowType( WindowPtr window );
void DoError( Str255 errorString );
/********************* main ******************/
void main( void )
{
ToolboxInit();
MenuBarInit();
EventLoop();
}
/********************* ToolboxInit ******************/
void ToolboxInit( void )
{
InitGraf( &thePort );
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs( nil );
InitCursor();
}
/********************* MenuBarInit ******************/
void MenuBarInit( void )
{
Handle menuBar;
MenuHandle menu;
menuBar = GetNewMBar( kMBARResID );
SetMenuBar( menuBar );
menu = GetMHandle( mApple );
AddResMenu( menu, 'DRVR' );
DrawMenuBar();
}
/********************* CreateListWindow ******************/
void CreateListWindow( void )
{
Rect r, dataBounds;
WindowPtrw;
Point cSize, cIndex;
ListHandle list;
short i, dummy, numPicts;
Handle rHandle;
short resID;
ResTypetheResType;
Str255 rName;
Ptr wStorage;
ListPeek l;
SetRect( &r, gNewWindowX, gNewWindowY, gNewWindowX +
kMinWindowWidth, gNewWindowY + kWindowHeight);
BumpGlobalXandY();
wStorage = NewPtr( sizeof( ListRecord ) );
w = NewWindow( wStorage, &r, "\pPicture List", kInvisible,
documentProc, kMoveToFront, kHasGoAway, 0L );
SetPort( w );
TextFont( systemFont );
SetRect( &dataBounds, 0, 0, 1, 0 );
SetPt( &cSize, 0, 0 );
SetRect (&r, 0, 0, kMinWindowWidth - 15,
kWindowHeight - 15);
list = LNew( &r, &dataBounds, cSize, kListDefProc,
w, kDontDrawYet, kHasGrow, kHasHScroll, kHasVScroll );
(**list).selFlags = lOnlyOne;
l = (ListPeek)w;
l->wType = kListWindow;
l->list = list;
numPicts = CountResources( 'PICT' );
for ( i = 0; i<numPicts; i++ )
{
rHandle = GetIndResource( 'PICT', i + 1 );
GetResInfo( rHandle, &resID, &theResType, rName );
dummy = LAddRow( 1, i, list );
SetPt( &cIndex, 0, i );
if ( rName[ 0 ] > 0 )
LAddToCell( &(rName[1]), rName[0], cIndex, list );
else
LAddToCell( "<Unnamed>", 10, cIndex, list );
}
ShowWindow( w );
LDoDraw( true, list );
}
/********************* DestroyWindow ******************/
void DestroyWindow( WindowPtr w )
{
ListPeek l;
if ( WindowType( w ) == kListWindow )
{
HideWindow( w );
l = (ListPeek)w;
LDispose( l->list );
CloseWindow( w );
DisposePtr( (Ptr)w );
}
else if ( WindowType( w ) == kPictWindow )
{
CloseWindow( w );
DisposePtr( (Ptr)w );
}
}
/********************* EventLoop ******************/
void EventLoop( void )
{
EventRecordevent;
gDone = false;
while ( gDone == false )
{
if ( WaitNextEvent( everyEvent, &event, kSleep, NULL ) )
DoEvent( &event );
}
}
/********************* DoEvent ******************/
void DoEvent( EventRecord *eventPtr )
{
char theChar;
switch ( eventPtr->what )
{
case mouseDown:
HandleMouseDown( eventPtr );
break;
case keyDown:
case autoKey:
theChar = eventPtr->message & charCodeMask;
if ( (eventPtr->modifiers & cmdKey) != 0 )
HandleMenuChoice( MenuKey( theChar ) );
break;
case updateEvt:
DoUpdate( eventPtr );
break;
case activateEvt:
DoActivate( eventPtr );
break;
}
}
/********************* DoUpdate ******************/
void DoUpdate( EventRecord *eventPtr )
{
WindowPtrw;
short numPicts, i;
ListPeek l;
ListHandle list;
GrafPtroldPort;
Rect r;
Point cellIndex;
PicHandlepic;
PictPeek p;
w = (WindowPtr)(eventPtr->message);
BeginUpdate( w );
if ( WindowType( w ) == kListWindow )
{
GetPort( &oldPort );
SetPort( w );
l = (ListPeek)w;
list = l->list;
DrawGrowIcon( w );
LUpdate( (**list).port->visRgn, list );
SetPort( oldPort );
}
else if ( WindowType( w ) == kPictWindow )
{
GetPort( &oldPort );
SetPort( w );
r = w->portRect;
p = (PictPeek)w;
pic = GetPicture( p->PictResID );
CenterPict( pic, &r );
DrawPicture( pic, &r );
SetPort( oldPort );
}
EndUpdate( w );
}
/********************* DoActivate ******************/
void DoActivate( EventRecord *eventPtr )
{
WindowPtrw;
ListPeek l;
ListHandle list;
w = (WindowPtr)(eventPtr->message);
if ( WindowType( w ) == kListWindow )
{
l = (ListPeek)w;
list = l->list;
if ( eventPtr->modifiers & activeFlag )
LActivate( true, list );
else
LActivate( false, list );
DrawGrowIcon( w );
}
}
/********************* HandleMouseDown ******************/
void HandleMouseDown( EventRecord *eventPtr )
{
WindowPtrwindow;
short thePart;
long menuChoice;
GrafPtroldPort;
long windSize;
Rect growRect;
thePart = FindWindow( eventPtr->where, &window );
switch ( thePart )
{
case inMenuBar:
menuChoice = MenuSelect( eventPtr->where );
HandleMenuChoice( menuChoice );
break;
case inSysWindow :
SystemClick( eventPtr, window );
break;
case inContent:
DoContentClick( eventPtr, window );
break;
case inGrow:
DoGrow( eventPtr, window );
break;
case inDrag :
DragWindow( window, eventPtr->where,
&screenBits.bounds );
break;
case inGoAway:
if ( TrackGoAway( window, eventPtr->where ) )
DestroyWindow( window );
break;
}
}
/********************* DoContentClick ******************/
void DoContentClick( EventRecord *eventPtr, WindowPtr w )
{
GrafPtroldPort;
ListHandle list;
ListPeek l;
if ( w != FrontWindow() )
{
SelectWindow( w );
}
else if ( WindowType( w ) == kListWindow )
{
GetPort( &oldPort );
SetPort( w );
GlobalToLocal( &(eventPtr->where) );
l = (ListPeek)w;
list = l->list;
if (LClick( eventPtr->where, eventPtr->modifiers, list ))
CreatePictWindow( list );
SetPort( oldPort );
}
}
/********************* CreatePictWindow ******************/
void CreatePictWindow( ListHandle list )
{
Cell cell;
PicHandlepic;
Handle rHandle;
Rect r;
short resID;
ResTypetheResType;
Str255 rName;
PictPeek p;
Ptr wStorage;
WindowPtrw;
SetPt( &cell, 0, 0 );
if ( LGetSelect( kFindNext, &cell, list ) )
{
rHandle = GetIndResource( 'PICT', cell.v + 1 );
pic = (PicHandle)rHandle;
r = (**pic).picFrame;
if ( r.right - r.left < kMinPictWinWidth )
r.right = r.left + kMinPictWinWidth;
if ( r.bottom - r.top < kMinPictWinHeight )
r.bottom = r.top + kMinPictWinHeight;
OffsetRect( &r, gNewWindowX - r.left,
gNewWindowY - r.top );
BumpGlobalXandY();
wStorage = NewPtr( sizeof( PictRecord ) );
GetResInfo( rHandle, &resID, &theResType, rName );
if ( rName[ 0 ] > 0 )
{
w = NewWindow( wStorage, &r, rName, kInvisible,
noGrowDocProc, kMoveToFront, kHasGoAway, 0L );
}
else
{
w = NewWindow( wStorage, &r, "\p<Unnamed>", kInvisible,
noGrowDocProc, kMoveToFront, kHasGoAway, 0L );
}
ShowWindow( w );
SetPort( w );
p = (PictPeek)w;
p->wType = kPictWindow;
p->PictResID = resID;
}
}
/********************* BumpGlobalXandY ******************/
void BumpGlobalXandY( void )
{
gNewWindowX += 20;
gNewWindowY += 20;
if ( (gNewWindowX > screenBits.bounds.right - 100) ||
(gNewWindowY > screenBits.bounds.bottom - 100) )
{
gNewWindowX = 20;
gNewWindowY = 50;
}
}
/********************* DoGrow ******************/
void DoGrow( EventRecord *eventPtr, WindowPtr w )
{
Rect r;
GrafPtroldPort;
Cell cSize;
long windSize;
ListHandle list;
if ( WindowType( w ) == kListWindow )
{
r.top = kMinWindowHeight;
r.bottom = 32767;
r.left = kMinWindowWidth;
r.right = 32767;
windSize = GrowWindow( w, eventPtr->where, &r );
if ( windSize )
{
GetPort( &oldPort );
SetPort( w );
EraseRect( &w->portRect );
SizeWindow( w, LoWord (windSize),
HiWord(windSize), true );
list = ((ListPeek)w)->list;
LSize( LoWord(windSize) - 15,
HiWord(windSize) - 15, list );
HLock( (Handle)list );
cSize = (*list)->cellSize;
HUnlock( (Handle)list );
cSize.h = LoWord( windSize ) - 15;
LCellSize( cSize, list );
InvalRect( &w->portRect );
SetPort( oldPort );
}
}
}
/********************* HandleMenuChoice ******************/
void HandleMenuChoice( long menuChoice )
{
short menu;
short item;
if ( menuChoice != 0 )
{
menu = HiWord( menuChoice );
item = LoWord( menuChoice );
switch ( menu )
{
case mApple:
HandleAppleChoice( item );
break;
case mFile:
HandleFileChoice( item );
break;
}
HiliteMenu( 0 );
}
}
/********************* HandleAppleChoice ******************/
void HandleAppleChoice( short item )
{
MenuHandle appleMenu;
Str255 accName;
short accNumber;
switch ( item )
{
case iAbout:
SysBeep( 20 );
break;
default:
appleMenu = GetMHandle( mApple );
GetItem( appleMenu, item, accName );
accNumber = OpenDeskAcc( accName );
break;
}
}
/********************* HandleFileChoice ******************/
void HandleFileChoice( short item )
{
switch ( item )
{
case iNewList:
CreateListWindow();
break;
case iClose:
DestroyWindow( FrontWindow() );
break;
case iQuit:
gDone = true;
break;
}
}
/********************* CenterPict ******************/
void CenterPict( PicHandle picture, Rect *destRectPtr )
{
Rect windRect, pictRect;
windRect = *destRectPtr;
pictRect = (**( picture )).picFrame;
OffsetRect( &pictRect, windRect.left - pictRect.left,
windRect.top - pictRect.top);
OffsetRect( &pictRect, (windRect.right - pictRect.right)/2,
(windRect.bottom - pictRect.bottom)/2);
*destRectPtr = pictRect;
}
/********************* WindowType ******************/
short WindowType( WindowPtr window )
{
if ( window == nil )
return( kNilWindow );
if ( ((WindowPeek)window)->windowKind < 0 )
return( kDAWindow );
if ( ((ListPeek)window)->wType == kListWindow )
return( kListWindow );
if ( ((ListPeek)window)->wType == kPictWindow )
return( kPictWindow );
return( kUnknownWindow );
}
/********************* DoError ******************/
void DoError( Str255 errorString )
{
ParamText( errorString, kEmptyString,
kEmptyString, kEmptyString );
StopAlert( kErrorAlertID, kNilFilterProc );
ExitToShell();
}
Save your source code, then run PictLister.
Running PictLister
The first thing youll see when you run PictLister is the menu bar, featuring the , File, and Edit menus. Select New List from the File menu. A PictLister window will appear, listing all available PICT resources by name. If the resource doesnt have a name, the string <Unnamed> will appear.
Play with the window a bit. Resize it. Notice that there is a definite minimum size. Click on an item. Notice that the item highlights. Try scrolling the list. Click on an item and drag it down or up. If the window is small enough to enable scrolling, dragging an item up or down causes the window to auto-scroll to the top or bottom of the list.
Select New List from the File menu again. Notice that the new window is created 20 pixels down and 20 pixels to the right of the previous window. If you create enough windows, eventually the windows will wrap back around to the upper left corner of the main display.
Double-click on an item in the list. A new window will appear containing the named PICT. Click on the close box of a window to close it. You can also close a window by selecting Close from the File menu.
Till Next Month
Next month, well walk through the PictLister source code. If you get a chance to look through the code on your own, you might want to try adding this feature to the program: Instead of using the string <Unnamed> to name unnamed PICT resources, try creating a string containing the resources resource id, using the string both in the list window and in the PICT windows title. See you next month...