mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-06-06 20:29:29 +00:00
1799 lines
58 KiB
C++
1799 lines
58 KiB
C++
|
#include "FileWindow.h"
|
|||
|
#include "Application.h"
|
|||
|
#include "Asynchronous.h"
|
|||
|
#include "ResourceObject.h"
|
|||
|
#include "Errors.h"
|
|||
|
#include "InspectorWindow.h"
|
|||
|
#include "PlugObject.h" // for LoadEditor()
|
|||
|
#include "PickerWindow.h"
|
|||
|
#include "Utility.h"
|
|||
|
extern globals g;
|
|||
|
|
|||
|
#pragma mark Constructor
|
|||
|
|
|||
|
/*** FILE WINDOW CONSTRUCTOR ***/
|
|||
|
FileWindow::FileWindow( FSSpecPtr spec )
|
|||
|
{
|
|||
|
OSStatus error = noErr;
|
|||
|
|
|||
|
#if USE_NIBS
|
|||
|
// create a nib reference (only searches the application bundle)
|
|||
|
IBNibRef nibRef = null;
|
|||
|
error = CreateNibReference( CFSTR("ResKnife"), &nibRef );
|
|||
|
if( error != noErr || nibRef == null )
|
|||
|
{
|
|||
|
DisplayError( "\pThe nib file reference could not be obtained." );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// create window
|
|||
|
error = CreateWindowFromNib( nibRef, CFSTR("File Window"), &window );
|
|||
|
if( error != noErr || window == null )
|
|||
|
{
|
|||
|
DisplayError( "\pA file window could not be obtained from the nib file." );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// dispose of nib ref
|
|||
|
DisposeNibReference( nibRef );
|
|||
|
|
|||
|
#elif TARGET_API_MAC_CARBON
|
|||
|
// create window
|
|||
|
Rect creationBounds;
|
|||
|
SetRect( &creationBounds, 0, 0, kDefaultFileWindowWidth, kDefaultFileWindowHeight );
|
|||
|
OffsetRect( &creationBounds, 10, 48 );
|
|||
|
WindowAttributes attributes = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowInWindowMenuAttribute;
|
|||
|
if( g.systemVersion >= kMacOSX ) attributes |= kWindowLiveResizeAttribute;
|
|||
|
error = CreateNewWindow( kDocumentWindowClass, attributes, &creationBounds, &window );
|
|||
|
if( error ) return;
|
|||
|
#else
|
|||
|
if( g.useAppearance && g.systemVersion >= kMacOSEight )
|
|||
|
{
|
|||
|
window = GetNewCWindow( kFileWindow8, null, kFirstWindowOfClass );
|
|||
|
themeSavvy = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
window = GetNewCWindow( kFileWindow7, null, kFirstWindowOfClass );
|
|||
|
themeSavvy = false;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// save FileWindow class in window's refcon
|
|||
|
SetWindowRefCon( window, (UInt32) this );
|
|||
|
SetWindowKind( window, kFileWindowKind );
|
|||
|
SetWindowTitle( window, spec->name );
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// set title used in window menu
|
|||
|
SetWindowAlternateTitle( window, null ); // bug: should use path here
|
|||
|
#endif
|
|||
|
|
|||
|
// set window's background to default for theme
|
|||
|
if( g.useAppearance )
|
|||
|
SetThemeWindowBackground( window, kThemeBrushModelessDialogBackgroundActive, false );
|
|||
|
|
|||
|
#if USE_NIBS // Carbon nib file on Public Beta doesn't let me set this
|
|||
|
WindowAttributes setThese = null, clearThese = null;
|
|||
|
setThese = kWindowInWindowMenuAttribute;
|
|||
|
ChangeWindowAttributes( window, setThese, clearThese );
|
|||
|
#endif
|
|||
|
|
|||
|
// initalise file variables
|
|||
|
fileSpec = (FSSpecPtr) NewPtrClear( sizeof(FSSpec) );
|
|||
|
tempSpec = (FSSpecPtr) NewPtrClear( sizeof(FSSpec) );
|
|||
|
fileDirty = false;
|
|||
|
SetFileSpec( spec );
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
dataBrowser = null;
|
|||
|
#endif
|
|||
|
|
|||
|
// initalise resource variables
|
|||
|
numTypes = 0;
|
|||
|
numResources = 0;
|
|||
|
dataFork = null;
|
|||
|
resourceMap = null;
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// install window event handler
|
|||
|
EventHandlerRef ref = null;
|
|||
|
EventHandlerUPP handler = NewEventHandlerUPP( FileWindowEventHandler );
|
|||
|
EventTypeSpec events[] = { { kEventClassWindow, kEventWindowClose },
|
|||
|
{ kEventClassWindow, kEventWindowBoundsChanging },
|
|||
|
{ kEventClassWindow, kEventWindowBoundsChanged },
|
|||
|
{ kEventClassWindow, kEventWindowGetIdealSize },
|
|||
|
{ kEventClassWindow, kEventWindowZoomed } };
|
|||
|
InstallWindowEventHandler( window, handler, GetEventTypeCount(events), (EventTypeSpec *) &events, this, &ref );
|
|||
|
|
|||
|
EventTypeSpec update = { kEventClassMenu, kEventMenuEnableItems };
|
|||
|
EventTypeSpec process = { kEventClassCommand, kEventCommandProcess };
|
|||
|
|
|||
|
// install menu update handler
|
|||
|
handler = NewEventHandlerUPP( FileWindowUpdateMenus );
|
|||
|
InstallWindowEventHandler( window, handler, 1, &update, this, &ref );
|
|||
|
|
|||
|
// install menu selection handler
|
|||
|
handler = NewEventHandlerUPP( FileWindowParseMenuSelection );
|
|||
|
InstallWindowEventHandler( window, handler, 1, &process, this, &ref );
|
|||
|
#endif
|
|||
|
|
|||
|
#if USE_NIBS
|
|||
|
// set window property
|
|||
|
ControlRef dataBrowser;
|
|||
|
ControlID id = { kDataBrowserSignature, 0 };
|
|||
|
GetControlByID( window, &id, &dataBrowser );
|
|||
|
SetWindowProperty( window, kResKnifeCreator, kWindowPropertyDataBrowser, sizeof(ControlRef), &dataBrowser );;
|
|||
|
|
|||
|
#elif TARGET_API_MAC_CARBON // CarbonLib 1.1+ only
|
|||
|
// create root control
|
|||
|
ControlRef root;
|
|||
|
CreateRootControl( window, &root );
|
|||
|
|
|||
|
// create header control
|
|||
|
Rect windowBounds, rect;
|
|||
|
GetWindowPortBounds( window, &windowBounds );
|
|||
|
SetRect( &rect, windowBounds.left, windowBounds.top, windowBounds.right, windowBounds.bottom );
|
|||
|
InsetRect( &rect, -1, -1 );
|
|||
|
rect.bottom = rect.top + kDefaultHeaderHeight +1;
|
|||
|
CreateWindowHeaderControl( window, &rect, true, &header );
|
|||
|
ControlID id = { kHeaderSignature, 0 };
|
|||
|
SetControlID( header, &id );
|
|||
|
|
|||
|
// create text controls
|
|||
|
ControlFontStyleRec fontStyle;
|
|||
|
fontStyle.flags = kControlUseFontMask + kControlUseJustMask;
|
|||
|
fontStyle.font = kControlFontSmallSystemFont;
|
|||
|
fontStyle.just = teJustLeft;
|
|||
|
SetRect( &rect, windowBounds.left +4, windowBounds.top +4, (windowBounds.right - windowBounds.left) /5 *3, windowBounds.top + kDefaultHeaderHeight -4 );
|
|||
|
CreateStaticTextControl( window, &rect, CFSTR("left"), &fontStyle, &left );
|
|||
|
id.id = 0;
|
|||
|
id.signature = kLeftTextSignature;
|
|||
|
SetControlID( left, &id );
|
|||
|
|
|||
|
fontStyle.just = teJustRight;
|
|||
|
SetRect( &rect, (windowBounds.right - windowBounds.left) /5 *2, windowBounds.top +4, windowBounds.right -4, windowBounds.top + kDefaultHeaderHeight -4 );
|
|||
|
CreateStaticTextControl( window, &rect, CFSTR("right"), &fontStyle, &right );
|
|||
|
id.id = 0;
|
|||
|
id.signature = kRightTextSignature;
|
|||
|
SetControlID( right, &id );
|
|||
|
|
|||
|
// embed text controls within header
|
|||
|
EmbedControl( left, header );
|
|||
|
EmbedControl( right, header );
|
|||
|
|
|||
|
// create data browser
|
|||
|
SetRect( &rect, windowBounds.left, windowBounds.top + kDefaultHeaderHeight +1, windowBounds.right, windowBounds.bottom );
|
|||
|
CreateDataBrowserControl( window, &rect, kDataBrowserListView, &dataBrowser );
|
|||
|
id.id = 0;
|
|||
|
id.signature = kDataBrowserSignature;
|
|||
|
SetControlID( dataBrowser, &id );
|
|||
|
SetControlReference( dataBrowser, (UInt32) this );
|
|||
|
SetWindowProperty( window, kResKnifeCreator, kWindowPropertyDataBrowser, sizeof(ControlRef), &dataBrowser );
|
|||
|
SetKeyboardFocus( window, dataBrowser, kControlDataBrowserPart );
|
|||
|
|
|||
|
#elif !TARGET_API_MAC_CARBON
|
|||
|
// create controls
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
// create basic window controls
|
|||
|
header = GetNewControl( kFileHeaderControl, window );
|
|||
|
horizScroll = GetNewControl( kAppearanceScrollBarControl, window );
|
|||
|
vertScroll = GetNewControl( kAppearanceScrollBarControl, window );
|
|||
|
|
|||
|
// create sort buttons
|
|||
|
Rect bounds = {};
|
|||
|
sortName = NewControl( window, &bounds, "\pName", true, 0, kControlBehaviorSticky + kControlContentTextOnly, 0, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
sortType = NewControl( window, &bounds, "\pType", true, 0, kControlBehaviorSticky + kControlContentTextOnly, 0, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
sortID = NewControl( window, &bounds, "\pID", true, 0, kControlBehaviorSticky + kControlContentTextOnly, 0, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
sortSize = NewControl( window, &bounds, "\pSize", true, 0, kControlBehaviorSticky + kControlContentTextOnly, 0, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
sortAttrs = NewControl( window, &bounds, "\pAttributes", true, 0, kControlBehaviorSticky + kControlContentTextOnly, 0, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
sortDir = NewControl( window, &bounds, "\p", true, 0, kControlContentIconSuiteRes, kSortUpIcon, kControlBevelButtonSmallBevelProc, 0 );
|
|||
|
nameColumnWidth = kFileWindowDefaultNameColumnWidth;
|
|||
|
|
|||
|
// put the text in the right place
|
|||
|
SetBevelButtonTextAlignment( sortName, kControlBevelButtonAlignTextFlushLeft, kFileWindowNameColumnTextOffset -1 );
|
|||
|
SetBevelButtonTextAlignment( sortType, kControlBevelButtonAlignTextFlushRight, 5 );
|
|||
|
SetBevelButtonTextAlignment( sortSize, kControlBevelButtonAlignTextFlushRight, 5 );
|
|||
|
SetBevelButtonTextAlignment( sortID, kControlBevelButtonAlignTextFlushRight, 5 );
|
|||
|
SetBevelButtonTextAlignment( sortAttrs, kControlBevelButtonAlignTextCenter, 0 );
|
|||
|
|
|||
|
// set up font details
|
|||
|
ControlFontStylePtr fontStyle = (ControlFontStylePtr) NewPtrClear( sizeof(ControlFontStyleRec) );
|
|||
|
fontStyle->flags = kControlUseFontMask + kControlUseSizeMask;
|
|||
|
fontStyle->font = kControlFontSmallSystemFont;
|
|||
|
|
|||
|
// apply font details to controls
|
|||
|
SetControlFontStyle( sortName, fontStyle );
|
|||
|
SetControlFontStyle( sortType, fontStyle );
|
|||
|
SetControlFontStyle( sortSize, fontStyle );
|
|||
|
SetControlFontStyle( sortID, fontStyle );
|
|||
|
SetControlFontStyle( sortAttrs, fontStyle );
|
|||
|
|
|||
|
// depress type control
|
|||
|
SetControlValue( sortType, 1 );
|
|||
|
sortOrder = kSortType;
|
|||
|
|
|||
|
// size the controls correctly
|
|||
|
MoveControl( sortName, -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortName, nameColumnWidth, kBevelButtonHeight );
|
|||
|
|
|||
|
MoveControl( sortType, nameColumnWidth -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortType, kFileWindowTypeColumnWidth, kBevelButtonHeight );
|
|||
|
|
|||
|
MoveControl( sortID, nameColumnWidth + kFileWindowTypeColumnWidth -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortID, kFileWindowIDColumnWidth, kBevelButtonHeight );
|
|||
|
|
|||
|
MoveControl( sortSize, nameColumnWidth + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortSize, kFileWindowSizeColumnWidth, kBevelButtonHeight );
|
|||
|
|
|||
|
MoveControl( sortAttrs, nameColumnWidth + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth + kFileWindowSizeColumnWidth -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortAttrs, kFileWindowAttributesColumnWidth, kBevelButtonHeight );
|
|||
|
|
|||
|
MoveControl( sortDir, nameColumnWidth + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth + kFileWindowSizeColumnWidth + kFileWindowAttributesColumnWidth -1, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( sortDir, kFileWindowSortColumnWidth, kBevelButtonHeight );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
nameColumnWidth = kFileWindowDefaultNameColumnWidth;
|
|||
|
horizScroll = GetNewControl( kSystem7ScrollBarControl, window );
|
|||
|
vertScroll = GetNewControl( kSystem7ScrollBarControl, window );
|
|||
|
}
|
|||
|
|
|||
|
// move & update scroll bars to where they should be :)
|
|||
|
BoundsChanged( null );
|
|||
|
#endif
|
|||
|
|
|||
|
// read forks into memory and
|
|||
|
error = ReadResourceFork();
|
|||
|
error = ReadDataFork( error );
|
|||
|
if( error )
|
|||
|
{
|
|||
|
delete this;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON // CarbonLib 1.1+ or OS X only
|
|||
|
// initalise data browser
|
|||
|
InitDataBrowser();
|
|||
|
#endif
|
|||
|
|
|||
|
// now finally we can show the window
|
|||
|
ShowWindow( window );
|
|||
|
new InspectorWindow;
|
|||
|
}
|
|||
|
|
|||
|
/*** DESTRUCTOR ***/
|
|||
|
FileWindow::~FileWindow( void )
|
|||
|
{
|
|||
|
DisposeWindow( window );
|
|||
|
if( fileSpec ) DisposePtr( (Ptr) fileSpec );
|
|||
|
if( resourceMap ) DisposeResourceMap();
|
|||
|
}
|
|||
|
|
|||
|
/*** WINDOW ACCESSOR ***/
|
|||
|
WindowRef FileWindow::Window( void )
|
|||
|
{
|
|||
|
return window;
|
|||
|
}
|
|||
|
|
|||
|
/********************/
|
|||
|
/* EVENT PROCESSING */
|
|||
|
/********************/
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
|
|||
|
/*** FILE WINDOW EVENT HANDLER ***/
|
|||
|
pascal OSStatus FileWindowEventHandler( EventHandlerCallRef callRef, EventRef event, void *userData )
|
|||
|
{
|
|||
|
#pragma unused( callRef )
|
|||
|
OSStatus error = eventNotHandledErr;
|
|||
|
unsigned long eventClass = GetEventClass( event );
|
|||
|
unsigned long eventKind = GetEventKind( event );
|
|||
|
FileWindowPtr file = (FileWindowPtr) userData;
|
|||
|
if( !file ) return eventNotHandledErr;
|
|||
|
|
|||
|
switch( eventClass )
|
|||
|
{
|
|||
|
case kEventClassWindow:
|
|||
|
switch( eventKind )
|
|||
|
{
|
|||
|
case kEventWindowClose:
|
|||
|
{ WindowRef window = FrontNonFloatingWindow(), nextWindow;
|
|||
|
while( window )
|
|||
|
{
|
|||
|
nextWindow = GetNextWindow( window );
|
|||
|
SInt32 kind = GetWindowKind( window );
|
|||
|
if( kind == kPickerWindowKind || kind == kEditorWindowKind )
|
|||
|
{
|
|||
|
FileWindowPtr owner = (FileWindowPtr) ((PlugWindowPtr) GetWindowRefCon( window ))->File();
|
|||
|
if( owner == file )
|
|||
|
{
|
|||
|
// DisposeWindow( window ); // bug: windows didn't used to close when sent a WindowClose event! (seems to work now though)
|
|||
|
EventRef event;
|
|||
|
CreateEvent( null, kEventClassWindow, kEventWindowClose, kEventDurationNoWait, kEventAttributeNone, &event );
|
|||
|
SendEventToWindow( event, window );
|
|||
|
ReleaseEvent( event );
|
|||
|
}
|
|||
|
}
|
|||
|
window = nextWindow;
|
|||
|
}
|
|||
|
|
|||
|
// feature to add: user chooses either resource-level or file-level (or both) save sheets
|
|||
|
if( file->IsFileDirty() )
|
|||
|
if( g.useSheets ) error = file->DisplayModelessAskSaveChangesDialog();
|
|||
|
else error = file->DisplaySaveDialog();
|
|||
|
else error = noErr;
|
|||
|
if( error != userCanceledErr )
|
|||
|
delete file;
|
|||
|
} break;
|
|||
|
|
|||
|
case kEventWindowBoundsChanging:
|
|||
|
error = file->BoundsChanging( event );
|
|||
|
break;
|
|||
|
|
|||
|
case kEventWindowBoundsChanged:
|
|||
|
error = file->BoundsChanged( event );
|
|||
|
break;
|
|||
|
|
|||
|
case kEventWindowZoomed:
|
|||
|
error = file->Zoomed( event );
|
|||
|
break;
|
|||
|
|
|||
|
case kEventWindowGetIdealSize:
|
|||
|
error = file->SetIdealSize( event );
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case kEventClassCommand:
|
|||
|
HICommand command;
|
|||
|
error = GetEventParameter( event, kEventParamDirectObject, typeHICommand, null, sizeof(HICommand), null, &command );
|
|||
|
if( error ) return eventNotHandledErr;
|
|||
|
else error = eventNotHandledErr;
|
|||
|
switch( eventKind )
|
|||
|
{
|
|||
|
case kEventCommandProcess:
|
|||
|
/* switch( command.commandID )
|
|||
|
{
|
|||
|
}
|
|||
|
*/ break;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** FILE WINDOW UPDATE MENUS ***/
|
|||
|
pascal OSStatus FileWindowUpdateMenus( EventHandlerCallRef callRef, EventRef event, void *userData )
|
|||
|
{
|
|||
|
#pragma unused( callRef, event )
|
|||
|
OSStatus error = eventNotHandledErr;
|
|||
|
|
|||
|
// get file window
|
|||
|
FileWindowPtr file = (FileWindowPtr) userData;
|
|||
|
if( !file ) return eventNotHandledErr;
|
|||
|
|
|||
|
// get number of resources selected
|
|||
|
UInt32 numSelected;
|
|||
|
GetDataBrowserItemCount( file->GetDataBrowser(), kDataBrowserNoItem, true, kDataBrowserItemIsSelected, &numSelected );
|
|||
|
|
|||
|
// determine if selected resource is of type 'snd '
|
|||
|
Boolean canPlaySound = false;
|
|||
|
ResourceObjectPtr resource = null;
|
|||
|
DataBrowserItemID first, last, n;
|
|||
|
GetDataBrowserSelectionAnchor( file->GetDataBrowser(), &first, &last );
|
|||
|
if( first != kDataBrowserNoItem && last != kDataBrowserNoItem )
|
|||
|
{
|
|||
|
for( n = first; n <= last; n++ )
|
|||
|
{
|
|||
|
resource = file->GetResource( n );
|
|||
|
if( resource->Type() == soundListRsrc )
|
|||
|
canPlaySound = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// edit menu
|
|||
|
EnableCommand( null, kHICommandUndo, false );
|
|||
|
EnableCommand( null, kHICommandRedo, false );
|
|||
|
/* EnableCommand( null, kHICommandCut, numSelected > 0 );
|
|||
|
EnableCommand( null, kHICommandCopy, numSelected > 0 );
|
|||
|
EnableCommand( null, kHICommandPaste, numSelected > 0 );
|
|||
|
EnableCommand( null, kHICommandClear, numSelected > 0 );
|
|||
|
*/ EnableCommand( null, kHICommandCut, false );
|
|||
|
EnableCommand( null, kHICommandCopy, false );
|
|||
|
EnableCommand( null, kHICommandPaste, false );
|
|||
|
EnableCommand( null, kHICommandClear, false );
|
|||
|
EnableCommand( null, kHICommandSelectAll, true );
|
|||
|
EnableCommand( null, kMenuCommandFind, false );
|
|||
|
EnableCommand( null, kMenuCommandFindAgain, false );
|
|||
|
|
|||
|
// resource menu
|
|||
|
EnableCommand( null, kMenuCommandNewResource, true );
|
|||
|
EnableCommand( null, kMenuCommandOpenHex, numSelected > 0 );
|
|||
|
EnableCommand( null, kMenuCommandOpenDefault, numSelected > 0 );
|
|||
|
EnableCommand( null, kMenuCommandOpenTemplate, numSelected > 0 );
|
|||
|
EnableCommand( null, kMenuCommandOpenSpecific, numSelected > 0 );
|
|||
|
EnableCommand( null, kMenuCommandRevertResource, numSelected > 0 );
|
|||
|
EnableCommand( null, kMenuCommandPlaySound, numSelected > 0 && canPlaySound );
|
|||
|
return eventNotHandledErr;
|
|||
|
}
|
|||
|
|
|||
|
/*** FILE WINDOW PARSE MENU SELECTION ***/
|
|||
|
pascal OSStatus FileWindowParseMenuSelection( EventHandlerCallRef callRef, EventRef event, void *userData )
|
|||
|
{
|
|||
|
#pragma unused( callRef )
|
|||
|
|
|||
|
// get menu command
|
|||
|
HICommand menuCommand;
|
|||
|
OSStatus error = GetEventParameter( event, kEventParamDirectObject, typeHICommand, null, sizeof(HICommand), null, &menuCommand );
|
|||
|
if( error ) return eventNotHandledErr;
|
|||
|
|
|||
|
// get file window
|
|||
|
FileWindowPtr file = (FileWindowPtr) userData;
|
|||
|
if( !file ) return eventNotHandledErr;
|
|||
|
|
|||
|
switch( menuCommand.commandID )
|
|||
|
{
|
|||
|
// file menu
|
|||
|
case kMenuCommandCloseWindow:
|
|||
|
case kMenuCommandCloseFile:
|
|||
|
EventRef event;
|
|||
|
CreateEvent( null, kEventClassWindow, kEventWindowClose, kEventDurationNoWait, kEventAttributeNone, &event );
|
|||
|
SendEventToWindow( event, file->Window() );
|
|||
|
ReleaseEvent( event );
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandSaveFile:
|
|||
|
error = file->SaveFile( null );
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandSaveFileAs:
|
|||
|
if( g.useSheets ) error = file->DisplayModelessPutFileDialog();
|
|||
|
else error = file->DisplaySaveAsDialog();
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandRevertFile:
|
|||
|
if( g.useSheets ) error = file->DisplayModelessAskDiscardChangesDialog();
|
|||
|
else error = file->DisplayRevertFileDialog();
|
|||
|
break;
|
|||
|
|
|||
|
// edit menu
|
|||
|
/* case kHICommandOK:
|
|||
|
case kHICommandCancel:
|
|||
|
case kHICommandQuit:
|
|||
|
case kHICommandUndo:
|
|||
|
case kHICommandRedo:
|
|||
|
case kHICommandCut:
|
|||
|
case kHICommandCopy:
|
|||
|
case kHICommandPaste:
|
|||
|
case kHICommandClear:
|
|||
|
*/ case kHICommandSelectAll:
|
|||
|
ForEachDataBrowserItem( file->GetDataBrowser(), kDataBrowserNoItem, true, kDataBrowserItemIsSelected, null, null );
|
|||
|
break;
|
|||
|
/* case kHICommandHide:
|
|||
|
case kHICommandPreferences:
|
|||
|
case kHICommandZoomWindow:
|
|||
|
case kHICommandMinimizeWindow:
|
|||
|
case kHICommandArrangeInFront:
|
|||
|
case kHICommandAbout:
|
|||
|
break;
|
|||
|
*/
|
|||
|
// resource menu
|
|||
|
case kMenuCommandNewResource:
|
|||
|
if( g.useSheets ) error = file->DisplayNewResourceSheet();
|
|||
|
else error = file->DisplayNewResourceDialog();
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandOpenDefault:
|
|||
|
case kMenuCommandOpenTemplate:
|
|||
|
case kMenuCommandOpenSpecific:
|
|||
|
case kMenuCommandOpenHex:
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
for( DataBrowserItemID item = 1; item <= file->GetResourceCount(); item++ )
|
|||
|
{
|
|||
|
if( IsDataBrowserItemSelected( file->GetDataBrowser(), item ) )
|
|||
|
error = file->OpenResource( item, menuCommand.commandID );
|
|||
|
if( error ) return eventNotHandledErr;
|
|||
|
}
|
|||
|
#else
|
|||
|
DisplayDialog( "\pAs I haven't wired up Classic Events, no Editors work in classic mode." );
|
|||
|
#endif
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandRevertResource:
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandPlaySound:
|
|||
|
DataBrowserItemID first, last, n;
|
|||
|
GetDataBrowserSelectionAnchor( file->GetDataBrowser(), &first, &last );
|
|||
|
for( n = first; n <= last; n++ )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = file->GetResource( n );
|
|||
|
if( resource->Type() == soundListRsrc )
|
|||
|
file->PlaySound( n );
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return eventNotHandledErr;
|
|||
|
}
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
/*** WINDOW BOUNDS ARE CHANGING ***/
|
|||
|
OSStatus FileWindow::BoundsChanging( EventRef event )
|
|||
|
{
|
|||
|
OSStatus error = noErr;
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// check that window is not just being dragged
|
|||
|
UInt32 attributes;
|
|||
|
error = GetEventParameter( event, kEventParamAttributes, typeUInt32, null, sizeof(UInt32), null, &attributes );
|
|||
|
if( error || attributes & kWindowBoundsChangeUserDrag ) return eventNotHandledErr;
|
|||
|
if( g.systemVersion < kMacOSX ) return noErr;
|
|||
|
|
|||
|
// resize window's contents
|
|||
|
error = eventNotHandledErr;
|
|||
|
if( g.systemVersion >= kMacOSX )
|
|||
|
{
|
|||
|
Rect windowBounds;
|
|||
|
error = GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, null, sizeof(Rect), null, &windowBounds );
|
|||
|
if( error ) return eventNotHandledErr;
|
|||
|
SizeControl( dataBrowser, windowBounds.right - windowBounds.left, windowBounds.bottom - windowBounds.top - kDefaultHeaderHeight -1 );
|
|||
|
}
|
|||
|
#else
|
|||
|
#pragma unused( event )
|
|||
|
#endif
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** WINDOW BOUNDS HAVE CHANGED ***/
|
|||
|
OSStatus FileWindow::BoundsChanged( EventRef event )
|
|||
|
{
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
if( event )
|
|||
|
{
|
|||
|
// check that window is not just being dragged
|
|||
|
UInt32 attributes;
|
|||
|
OSStatus error = GetEventParameter( event, kEventParamAttributes, typeUInt32, null, sizeof(UInt32), null, &attributes );
|
|||
|
if( error || attributes & kWindowBoundsChangeUserDrag ) return eventNotHandledErr;
|
|||
|
}
|
|||
|
#else
|
|||
|
#pragma unused( event )
|
|||
|
#endif
|
|||
|
|
|||
|
GrafPtr oldPort;
|
|||
|
GetPort( &oldPort );
|
|||
|
SetPortWindowPort( window );
|
|||
|
|
|||
|
// move everything and invalidate the area
|
|||
|
Rect windowBounds;
|
|||
|
if( g.windowMgrAvailable ) GetWindowBounds( window, kWindowContentRgn, &windowBounds );
|
|||
|
else GetPortBounds( (CGrafPtr) window, &windowBounds );
|
|||
|
OffsetRect( &windowBounds, -windowBounds.left, -windowBounds.top );
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// resize header & data browser
|
|||
|
SizeControl( header, (windowBounds.right - windowBounds.left) +2, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( left, (windowBounds.right - windowBounds.left) /2 -4, kDefaultHeaderHeight -4 );
|
|||
|
SizeControl( right, (windowBounds.right - windowBounds.left) /2 -4, kDefaultHeaderHeight -4 );
|
|||
|
MoveControl( right, (windowBounds.right - windowBounds.left) /2, 2 );
|
|||
|
SizeControl( dataBrowser, windowBounds.right - windowBounds.left, windowBounds.bottom - windowBounds.top - kDefaultHeaderHeight -1 );
|
|||
|
#else
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
nameColumnWidth = windowBounds.right - kFileWindowAllOtherColumnWidths -6;
|
|||
|
if( nameColumnWidth < kFileWindowMinimumNameColumnWidth ) nameColumnWidth = kFileWindowMinimumNameColumnWidth;
|
|||
|
SizeControl( header, windowBounds.right +2 - windowBounds.left, kDefaultHeaderHeight +2 );
|
|||
|
SizeControl( sortName, nameColumnWidth, kBevelButtonHeight );
|
|||
|
MoveControl( sortType, (nameColumnWidth -1), kDefaultHeaderHeight +1 );
|
|||
|
MoveControl( sortID, (nameColumnWidth -1) + kFileWindowTypeColumnWidth, kDefaultHeaderHeight +1 );
|
|||
|
MoveControl( sortSize, (nameColumnWidth -1) + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth, kDefaultHeaderHeight +1 );
|
|||
|
MoveControl( sortAttrs, (nameColumnWidth -1) + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth + kFileWindowSizeColumnWidth, kDefaultHeaderHeight +1 );
|
|||
|
MoveControl( sortDir, (nameColumnWidth -1) + kFileWindowTypeColumnWidth + kFileWindowIDColumnWidth + kFileWindowSizeColumnWidth + kFileWindowAttributesColumnWidth, kDefaultHeaderHeight +1 );
|
|||
|
}
|
|||
|
MoveControl( horizScroll, -1, windowBounds.bottom - (kScrollBarWidth -1) );
|
|||
|
SizeControl( horizScroll, windowBounds.right - (kScrollBarWidth -3), kScrollBarWidth );
|
|||
|
MoveControl( vertScroll, windowBounds.right - (kScrollBarWidth -1), kFileWindowHeaderHeight );
|
|||
|
SizeControl( vertScroll, kScrollBarWidth, windowBounds.bottom - kFileWindowHeaderHeight - (kScrollBarWidth -2) );
|
|||
|
if( themeSavvy && g.systemVersion >= kMacOSEightPointFive )
|
|||
|
{
|
|||
|
SetControlViewSize( horizScroll, nameColumnWidth + kFileWindowAllOtherColumnWidths );
|
|||
|
SetControlViewSize( vertScroll, windowBounds.bottom - (kScrollBarWidth -1) - kFileWindowHeaderHeight );
|
|||
|
}
|
|||
|
UpdateScrollBars();
|
|||
|
#endif
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
InvalidateWindowRect( window, &windowBounds );
|
|||
|
#else
|
|||
|
if( g.windowMgrAvailable ) InvalidateWindowRect( window, &windowBounds );
|
|||
|
else InvalidateRect( &windowBounds );
|
|||
|
#endif
|
|||
|
SetPort( oldPort );
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
|
|||
|
/*** WINDOW HAS BEEN ZOOMED ***/
|
|||
|
OSStatus FileWindow::Zoomed( EventRef event )
|
|||
|
{
|
|||
|
// get new bounds
|
|||
|
WindowRef window;
|
|||
|
Rect windowBounds;
|
|||
|
OSStatus error = GetEventParameter( event, kEventParamDirectObject, typeWindowRef, null, sizeof(WindowRef), null, &window );
|
|||
|
if( error ) return eventNotHandledErr;
|
|||
|
GetWindowBounds( window, kWindowGlobalPortRgn, &windowBounds );
|
|||
|
OffsetRect( &windowBounds, -windowBounds.left, -windowBounds.top );
|
|||
|
|
|||
|
// resize controls to the window size
|
|||
|
SizeControl( header, windowBounds.right - windowBounds.left +2, kDefaultHeaderHeight +1 );
|
|||
|
SizeControl( left, (windowBounds.right - windowBounds.left) /5 *3 - windowBounds.left +4, kDefaultHeaderHeight - windowBounds.top -8 );
|
|||
|
MoveControl( right, (windowBounds.right - windowBounds.left) /5 *2, windowBounds.top +4 );
|
|||
|
SizeControl( right, windowBounds.right - ((windowBounds.right - windowBounds.left) /5 *2) -4, kDefaultHeaderHeight - windowBounds.top -8 );
|
|||
|
SizeControl( dataBrowser, windowBounds.right - windowBounds.left, windowBounds.bottom - windowBounds.top - kDefaultHeaderHeight -1 );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** SET IDEAL SIZE OF WINDOW ***/
|
|||
|
OSStatus FileWindow::SetIdealSize( EventRef event )
|
|||
|
{
|
|||
|
Point idealSize;
|
|||
|
SetPoint( &idealSize, 512, 512 );
|
|||
|
OSStatus error = SetEventParameter( event, kEventParamDimensions, typeQDPoint, sizeof(Point), &idealSize );
|
|||
|
return error? eventNotHandledErr:noErr;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
#if !TARGET_API_MAC_CARBON
|
|||
|
|
|||
|
/*** UPDATE WINDOW ***/
|
|||
|
OSStatus FileWindow::Update( RgnHandle update )
|
|||
|
{
|
|||
|
GrafPtr oldPort;
|
|||
|
GetPort( &oldPort );
|
|||
|
SetPortWindowPort( window );
|
|||
|
|
|||
|
// get rect into which I can draw
|
|||
|
Rect windowBounds, rect;
|
|||
|
if( g.windowMgrAvailable ) GetWindowBounds( window, kWindowContentRgn, &windowBounds );
|
|||
|
else GetPortBounds( (CGrafPort *) window, &windowBounds );
|
|||
|
SetRect( &rect, 0, 0, windowBounds.right - windowBounds.left, windowBounds.bottom - windowBounds.top );
|
|||
|
rect.top += kFileWindowHeaderHeight +1;
|
|||
|
rect.bottom -= (kScrollBarWidth -1);
|
|||
|
rect.right -= (kScrollBarWidth -1);
|
|||
|
|
|||
|
// only draw resources if they are in the update region
|
|||
|
if( !update || RectInRgn( &windowBounds, update ) )
|
|||
|
{
|
|||
|
RgnHandle clip = NewRgn(), oldClip = NewRgn();
|
|||
|
RectToRegion( clip, &rect );
|
|||
|
GetPortClipRegion( GetWindowPort( window ), oldClip );
|
|||
|
SetPortClipRegion( GetWindowPort( window ), clip );
|
|||
|
|
|||
|
SInt16 value = GetControlValue( vertScroll );
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
// create light bg for all columns
|
|||
|
RGBBackColor( &g.bgColour );
|
|||
|
EraseRect( &rect );
|
|||
|
|
|||
|
// add dark bg for sorted column
|
|||
|
Rect controlBounds;
|
|||
|
switch( sortOrder )
|
|||
|
{
|
|||
|
case kSortName:
|
|||
|
GetControlBounds( sortName, &controlBounds );
|
|||
|
rect.left = controlBounds.left;
|
|||
|
rect.right = controlBounds.right;
|
|||
|
break;
|
|||
|
|
|||
|
case kSortType:
|
|||
|
GetControlBounds( sortType, &controlBounds );
|
|||
|
rect.left = controlBounds.left;
|
|||
|
rect.right = controlBounds.right;
|
|||
|
break;
|
|||
|
|
|||
|
case kSortID:
|
|||
|
GetControlBounds( sortID, &controlBounds );
|
|||
|
rect.left = controlBounds.left;
|
|||
|
rect.right = controlBounds.right;
|
|||
|
break;
|
|||
|
|
|||
|
case kSortSize:
|
|||
|
GetControlBounds( sortSize, &controlBounds );
|
|||
|
rect.left = controlBounds.left;
|
|||
|
rect.right = controlBounds.right;
|
|||
|
break;
|
|||
|
|
|||
|
case kSortAttrs:
|
|||
|
GetControlBounds( sortAttrs, &controlBounds );
|
|||
|
rect.left = controlBounds.left;
|
|||
|
rect.right = controlBounds.right;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
GetControlBounds( sortAttrs, &controlBounds );
|
|||
|
rect.left = controlBounds.right;
|
|||
|
rect.right = controlBounds.right +1;
|
|||
|
}
|
|||
|
|
|||
|
RGBBackColor( &g.sortColour );
|
|||
|
EraseRect( &rect );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// erase background
|
|||
|
RGBBackColor( &g.white );
|
|||
|
EraseRect( &rect );
|
|||
|
}
|
|||
|
|
|||
|
// draw all resources
|
|||
|
ResourceObjectPtr current = resourceMap;
|
|||
|
for( UInt16 line = 1; line <= numResources; line++ )
|
|||
|
{
|
|||
|
// draw resource icon
|
|||
|
DrawResourceIcon( current, line );
|
|||
|
|
|||
|
// reset text incase it was condensed or emboldened
|
|||
|
RGBForeColor( &g.black );
|
|||
|
TextFace( normal );
|
|||
|
|
|||
|
// resource type
|
|||
|
Str255 typeStr = "\p";
|
|||
|
TypeToPString( current->Type(), typeStr );
|
|||
|
MoveTo( nameColumnWidth + kFileWindowTypeColumnOffset + kFileWindowTypeColumnWidth - StringWidth(typeStr) -5, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight - kFileWindowTextHeight)/2 - 3 - value );
|
|||
|
DrawString( typeStr );
|
|||
|
|
|||
|
// resource ID
|
|||
|
Str255 idStr;
|
|||
|
NumToString( current->ID(), idStr );
|
|||
|
MoveTo( nameColumnWidth + kFileWindowIDColumnOffset + kFileWindowIDColumnWidth - StringWidth(idStr) -5, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight - kFileWindowTextHeight)/2 - 3 - value );
|
|||
|
DrawString( idStr );
|
|||
|
|
|||
|
// resource size
|
|||
|
Str255 sizeStr;
|
|||
|
NumToString( current->Size(), sizeStr );
|
|||
|
MoveTo( nameColumnWidth + kFileWindowSizeColumnOffset + kFileWindowSizeColumnWidth - StringWidth(sizeStr) -5, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight - kFileWindowTextHeight)/2 - 3 - value );
|
|||
|
DrawString( sizeStr );
|
|||
|
|
|||
|
// resource attribute icons
|
|||
|
/* Rect iconRect;
|
|||
|
SetRect( &iconRect, 0, 0, 16, 16 );
|
|||
|
OffsetRect( &iconRect, kFileWindowSizeColumnWidth + kAttrColumnOffset, (kFileLineHeight * line) +1 );
|
|||
|
if( current->Attributes() & resPreload) PlotIconID( &iconRect, kAlignNone, kTransformNone, kPreloadIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kPreloadIcon );
|
|||
|
OffsetRect( &iconRect, kAttrIconSep, 0 );
|
|||
|
if( current->Attributes() & resProtected) PlotIconID( &iconRect, kAlignNone, kTransformNone, kProtectedIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kProtectedIcon );
|
|||
|
OffsetRect( &iconRect, kAttrIconSep, 0 );
|
|||
|
if( current->Attributes() & resLocked) PlotIconID( &iconRect, kAlignNone, kTransformNone, kLockedIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kLockedIcon );
|
|||
|
OffsetRect( &iconRect, kAttrIconSep, 0 );
|
|||
|
if( current->Attributes() & resPurgeable) PlotIconID( &iconRect, kAlignNone, kTransformNone, kPurgableIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kPurgableIcon );
|
|||
|
OffsetRect( &iconRect, kAttrIconSep, 0 );
|
|||
|
if( current->Attributes() & resSysHeap) PlotIconID( &iconRect, kAlignNone, kTransformNone, kSysHeapIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kSysHeapIcon );
|
|||
|
OffsetRect( &iconRect, kAttrIconSep, 0 );
|
|||
|
if( current->Attributes() & null) PlotIconID( &iconRect, kAlignNone, kTransformNone, kCompressedIcon );
|
|||
|
else PlotIconID( &iconRect, kAlignNone, kTransformDisabled, kCompressedIcon );
|
|||
|
*/
|
|||
|
/* resSysHeap = 64, // System or application heap?
|
|||
|
resPurgeable = 32, // Purgeable resource?
|
|||
|
resLocked = 16, // Load it in locked?
|
|||
|
resProtected = 8, // Protected?
|
|||
|
resPreload = 4, // Load in on OpenResFile?
|
|||
|
resChanged = 2, // Resource changed? - not used by ResKnife */
|
|||
|
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
// draw divider line
|
|||
|
MoveTo( 0, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - value );
|
|||
|
RGBForeColor( &g.white );
|
|||
|
Line( windowBounds.right - windowBounds.left -1, 0 );
|
|||
|
}
|
|||
|
|
|||
|
// move on
|
|||
|
current = current->Next();
|
|||
|
}
|
|||
|
|
|||
|
SetPortClipRegion( GetWindowPort( window ), oldClip );
|
|||
|
DisposeRgn( clip );
|
|||
|
DisposeRgn( oldClip );
|
|||
|
}
|
|||
|
|
|||
|
// update the controls
|
|||
|
if( !themeSavvy ) DrawGrowIcon( window );
|
|||
|
UpdateControls( window, update );
|
|||
|
SetPort( oldPort );
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/*** ACTIVATE WINDOW ***/
|
|||
|
OSStatus FileWindow::Activate( Boolean active )
|
|||
|
{
|
|||
|
// invalidate the whole window
|
|||
|
GrafPtr oldPort;
|
|||
|
GetPort( &oldPort );
|
|||
|
SetPortWindowPort( window );
|
|||
|
|
|||
|
Rect windowBounds;
|
|||
|
if( g.windowMgrAvailable ) GetWindowBounds( window, kWindowContentRgn, &windowBounds );
|
|||
|
else GetPortBounds( (CGrafPort *) window, &windowBounds );
|
|||
|
if( g.windowMgrAvailable ) InvalidateWindowRect( window, &windowBounds );
|
|||
|
else InvalidateRect( &windowBounds );
|
|||
|
SetPort( oldPort );
|
|||
|
|
|||
|
// set the controls to display correctly
|
|||
|
ControlPartCode hiliteState = active? kControlNoPart:kControlInactivePart;
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
HiliteControl( header, hiliteState );
|
|||
|
HiliteControl( sortName, hiliteState );
|
|||
|
HiliteControl( sortType, hiliteState );
|
|||
|
HiliteControl( sortID, hiliteState );
|
|||
|
HiliteControl( sortSize, hiliteState );
|
|||
|
HiliteControl( sortAttrs, hiliteState );
|
|||
|
HiliteControl( sortDir, hiliteState );
|
|||
|
}
|
|||
|
HiliteControl( horizScroll, hiliteState );
|
|||
|
HiliteControl( vertScroll, hiliteState );
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/*** MOUSE CLICK ***/
|
|||
|
OSStatus FileWindow::Click( Point globalMouse, EventModifiers modifiers )
|
|||
|
{
|
|||
|
// set the mouse location to local co-ords
|
|||
|
GrafPtr oldPort;
|
|||
|
GetPort( &oldPort );
|
|||
|
SetPortWindowPort( window );
|
|||
|
Point localMouse = globalMouse, windowLoc = {0,0};
|
|||
|
GlobalToLocal( &localMouse );
|
|||
|
LocalToGlobal( &windowLoc );
|
|||
|
SetPort( oldPort );
|
|||
|
|
|||
|
// handle a click on a control first
|
|||
|
ControlHandle control;
|
|||
|
SInt16 controlPart;
|
|||
|
if( themeSavvy ) control = FindControlUnderMouse( localMouse, window, &controlPart );
|
|||
|
else controlPart = FindControl( localMouse, window, &control );
|
|||
|
if( control && controlPart != kControlNoPart )
|
|||
|
{
|
|||
|
// scroll the windoow
|
|||
|
if( themeSavvy )
|
|||
|
{
|
|||
|
ControlActionUPP scrollAction = NewControlActionUPP( FileWindowScrollAction );
|
|||
|
controlPart = HandleControlClick( control, localMouse, modifiers, scrollAction );
|
|||
|
DisposeControlActionUPP( scrollAction );
|
|||
|
}
|
|||
|
else if( controlPart != kControlIndicatorPart )
|
|||
|
{
|
|||
|
ControlActionUPP scrollAction = NewControlActionUPP( FileWindowScrollAction );
|
|||
|
TrackControl( control, localMouse, scrollAction );
|
|||
|
DisposeControlActionUPP( scrollAction );
|
|||
|
}
|
|||
|
else // clicks in the thumb cause crashes if given a ControlActionUPP
|
|||
|
{
|
|||
|
controlPart = TrackControl( control, localMouse, nil );
|
|||
|
Update();
|
|||
|
}
|
|||
|
|
|||
|
// save new offset & return
|
|||
|
UpdateScrollBars();
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// click was not on a control<6F>
|
|||
|
ResourceObjectPtr resource = resourceMap;
|
|||
|
static unsigned long clickTime;
|
|||
|
|
|||
|
// check shift key status
|
|||
|
Boolean shiftKeyDown = false,
|
|||
|
optionKeyDown = false;
|
|||
|
|
|||
|
KeyMap theKeys;
|
|||
|
GetKeys( theKeys );
|
|||
|
if( theKeys[1] & (shiftKey >> shiftKeyBit) ) shiftKeyDown = true;
|
|||
|
if( theKeys[1] & (optionKey >> shiftKeyBit) ) optionKeyDown = true;
|
|||
|
|
|||
|
// clicked on a part of the window where no resource is listed
|
|||
|
SInt16 newSelection = ( localMouse.v + GetControlValue(vertScroll) - kFileWindowHeaderHeight ) / kFileWindowRowHeight +1;
|
|||
|
if( newSelection > numResources )
|
|||
|
{
|
|||
|
ClearSelection(); // should go to drag selection instead
|
|||
|
Update();
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
// cycle to clicked resource
|
|||
|
for( newSelection; newSelection > 1; newSelection-- )
|
|||
|
resource = resource->Next();
|
|||
|
|
|||
|
// check for click on attributes
|
|||
|
/* if( mouse.h >= g.nameColumnWidth + kAttrColumnOffset ) // clicked on attribute
|
|||
|
{
|
|||
|
short attrClicked = mouse.h;
|
|||
|
attrClicked -= g.nameColumnWidth + kAttrColumnOffset;
|
|||
|
attrClicked /= kAttrIconSep;
|
|||
|
attrClicked += resPreloadBit;
|
|||
|
if( (r.attrs >> attrClicked) & 0x01 ) r.attrs -= 1 << attrClicked;
|
|||
|
else r.attrs += 1 << attrClicked;
|
|||
|
|
|||
|
// mark file & resource for saving
|
|||
|
dirty = true;
|
|||
|
r.dirty = true;
|
|||
|
ChangedResource( r.resource );
|
|||
|
|
|||
|
// update the window
|
|||
|
Update();
|
|||
|
}
|
|||
|
|
|||
|
// if in nameIconRgn, must be a click on a resource
|
|||
|
else*/ if( PtInRgn( localMouse, resource->nameIconRgn ) )
|
|||
|
{
|
|||
|
if( resource->Selected() == false )
|
|||
|
{
|
|||
|
if( shiftKeyDown ) // shift click on unselected
|
|||
|
{
|
|||
|
resource->Select( true );
|
|||
|
numSelected += 1;
|
|||
|
}
|
|||
|
else // click on unselected
|
|||
|
{
|
|||
|
clickTime = TickCount();
|
|||
|
ClearSelection();
|
|||
|
resource->Select( true );
|
|||
|
numSelected = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( TickCount() <= clickTime + GetDblTime() ) // double click
|
|||
|
{
|
|||
|
ClearSelection();
|
|||
|
resource->Select( true );
|
|||
|
numSelected = 1;
|
|||
|
OpenResource( resource->Number(), optionKeyDown? kMenuCommandOpenHex:kMenuCommandOpenDefault );
|
|||
|
}
|
|||
|
else if( shiftKeyDown ) // shift click on selected
|
|||
|
{
|
|||
|
resource->Select( false );
|
|||
|
numSelected -= 1;
|
|||
|
}
|
|||
|
else // click on selected
|
|||
|
{
|
|||
|
clickTime = TickCount();
|
|||
|
resource->Select( true );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// update the window
|
|||
|
Update();
|
|||
|
|
|||
|
// check for resource drags
|
|||
|
if( WaitMouseMoved( globalMouse ) ) // drag activated
|
|||
|
{
|
|||
|
OSErr error = noErr;
|
|||
|
DragReference theDragRef;
|
|||
|
ItemReference theItemRef = 1;
|
|||
|
FlavorFlags theFlags = flavorNotSaved;
|
|||
|
DragSendDataUPP sendProc = null; // bug: NewDragSendDataProc( SendFileDataProc );
|
|||
|
|
|||
|
// setup imaginary file
|
|||
|
PromiseHFSFlavor theFile;
|
|||
|
theFile.fileType = kResourceTransferType;
|
|||
|
theFile.fileCreator = kResKnifeCreator;
|
|||
|
theFile.fdFlags = null; // finder flags
|
|||
|
theFile.promisedFlavor = kResourceTransferType;
|
|||
|
|
|||
|
// create the drag reference
|
|||
|
NewDrag( &theDragRef );
|
|||
|
error = MemError();
|
|||
|
if( error ) return error;
|
|||
|
SetDragSendProc( theDragRef, sendProc, this );
|
|||
|
|
|||
|
RgnHandle dragRgn = NewRgn(),
|
|||
|
subtractRgn = NewRgn();
|
|||
|
|
|||
|
// get region of dragged items, using translucent dragging where possible
|
|||
|
GWorldPtr imageGWorld = null;
|
|||
|
|
|||
|
resource = resourceMap;
|
|||
|
while( resource )
|
|||
|
{
|
|||
|
if( resource->Selected() )
|
|||
|
UnionRgn( resource->nameIconRgn, dragRgn, dragRgn ); // add new region to rest of drag region
|
|||
|
resource = resource->Next();
|
|||
|
}
|
|||
|
|
|||
|
/* if( g.translucentDrag )
|
|||
|
{
|
|||
|
Point dragOffset;
|
|||
|
DataBrowserItemID resCounter = 0;
|
|||
|
SetPt( &dragOffset, 0, kFileWindowHeaderHeight );
|
|||
|
resource = resourceMap;
|
|||
|
|
|||
|
while( !resource->Selected() )
|
|||
|
{
|
|||
|
resCounter++;
|
|||
|
resource = resource->Next();
|
|||
|
}
|
|||
|
|
|||
|
error = CreateDragImage( resource, &imageGWorld );
|
|||
|
if( !error )
|
|||
|
{
|
|||
|
// init mask region
|
|||
|
RgnHandle maskRgn = NewRgn();
|
|||
|
CopyRgn( resource->nameIconRgn, maskRgn );
|
|||
|
OffsetRgn( maskRgn, 0, -kFileWindowRowHeight * resCounter );
|
|||
|
|
|||
|
// init rects
|
|||
|
Rect sourceRect, destRect;
|
|||
|
SetRect( &sourceRect, 0, 0, nameColumnWidth, kFileWindowRowHeight );
|
|||
|
SetRect( &destRect, 0, 0, nameColumnWidth, kFileWindowRowHeight );
|
|||
|
OffsetRect( &destRect, 0, kFileWindowHeaderHeight );
|
|||
|
|
|||
|
// init GWorld
|
|||
|
PixMapHandle imagePixMap = GetGWorldPixMap( imageGWorld );
|
|||
|
DragImageFlags imageFlags = kDragStandardTranslucency | kDragRegionAndImage;
|
|||
|
error = SetDragImage( theDragRef, imagePixMap, maskRgn, dragOffset, imageFlags );
|
|||
|
CopyBits( &GrafPtr( imageGWorld )->portBits, &GrafPtr( window )->portBits, &sourceRect, &destRect, srcCopy, maskRgn );
|
|||
|
if( error ) SysBeep(0);
|
|||
|
DisposeGWorld( imageGWorld );
|
|||
|
DisposeRgn( maskRgn );
|
|||
|
}
|
|||
|
}
|
|||
|
*/
|
|||
|
// subtract middles from icons
|
|||
|
CopyRgn( dragRgn, subtractRgn ); // duplicate region
|
|||
|
InsetRgn( subtractRgn, 2, 2 ); // inset it by 2 pixels
|
|||
|
DiffRgn( dragRgn, subtractRgn, dragRgn ); // subtract subRgn from addRgn, save in nameIconRgn
|
|||
|
OffsetRgn( dragRgn, windowLoc.h, windowLoc.v ); // change drag region to global coords
|
|||
|
|
|||
|
// add flavour data to drag
|
|||
|
error = AddDragItemFlavor( theDragRef, theItemRef, flavorTypePromiseHFS, &theFile, sizeof(PromiseHFSFlavor), theFlags );
|
|||
|
error = AddDragItemFlavor( theDragRef, theItemRef, kResourceTransferType, null, 0, theFlags );
|
|||
|
|
|||
|
// track the drag, then clean up
|
|||
|
EventRecord event;
|
|||
|
event.what = mouseDown;
|
|||
|
event.message = null;
|
|||
|
event.when = TickCount();
|
|||
|
event.where = globalMouse;
|
|||
|
event.modifiers = modifiers;
|
|||
|
|
|||
|
error = TrackDrag( theDragRef, &event, dragRgn );
|
|||
|
if( theDragRef ) DisposeDrag( theDragRef );
|
|||
|
if( subtractRgn ) DisposeRgn( subtractRgn );
|
|||
|
if( dragRgn ) DisposeRgn( dragRgn );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// otherwise, must be a drag selection or de-selection
|
|||
|
else
|
|||
|
{
|
|||
|
// clear current selections
|
|||
|
if( shiftKeyDown == false )
|
|||
|
{
|
|||
|
ClearSelection();
|
|||
|
Update();
|
|||
|
}
|
|||
|
|
|||
|
// check for resource drags
|
|||
|
if( WaitMouseMoved( globalMouse ) )
|
|||
|
{
|
|||
|
// set pen
|
|||
|
PenState oldState, dragState;
|
|||
|
GetPenState( &oldState );
|
|||
|
PenSize( 2, 2 );
|
|||
|
PenPat( &qd.gray );
|
|||
|
PenMode( srcXor );
|
|||
|
GetPenState( &dragState );
|
|||
|
SetPenState( &oldState );
|
|||
|
|
|||
|
// do drag selection
|
|||
|
|
|||
|
// RgnHandle oldClip = NewRgn();
|
|||
|
// GetClip( oldClip );
|
|||
|
// SetClip( bodyRgn );
|
|||
|
|
|||
|
Point mouse, savedMouse = globalMouse, oldMouse = globalMouse;
|
|||
|
Rect dragRect = {0,0,0,0};
|
|||
|
while( WaitMouseUp() )
|
|||
|
{
|
|||
|
GetMouse( &mouse );
|
|||
|
SetPortWindowPort( window );
|
|||
|
if( mouse.h != oldMouse.h || mouse.v != oldMouse.v )
|
|||
|
{
|
|||
|
SetPenState( &dragState );
|
|||
|
FrameRect( &dragRect );
|
|||
|
SetRect( &dragRect, (savedMouse.h < mouse.h)? savedMouse.h : mouse.h,
|
|||
|
(savedMouse.v < mouse.v)? savedMouse.v : mouse.v,
|
|||
|
(savedMouse.h > mouse.h)? savedMouse.h : mouse.h,
|
|||
|
(savedMouse.v > mouse.v)? savedMouse.v : mouse.v );
|
|||
|
OffsetRect( &dragRect, -windowLoc.h, -windowLoc.v );
|
|||
|
FrameRect( &dragRect );
|
|||
|
SetPenState( &oldState );
|
|||
|
|
|||
|
// check to see if selection region coincides with resources
|
|||
|
short i;
|
|||
|
RgnHandle dragRgn = NewRgn(), resultRgn = NewRgn();
|
|||
|
RectRgn( dragRgn, &dragRect );
|
|||
|
ResourceObjectPtr resource = resourceMap;
|
|||
|
for( i = 1; i <= GetControlValue(vertScroll); i++ )
|
|||
|
resource = resource->Next(); // move to first res in window
|
|||
|
for( i = GetControlValue(vertScroll); i < numResources - GetControlValue(vertScroll); i++ )
|
|||
|
{
|
|||
|
SetEmptyRgn( resultRgn );
|
|||
|
SectRgn( dragRgn, resource->nameIconRgn, resultRgn );
|
|||
|
resource->Select( !EmptyRgn( resultRgn ) );
|
|||
|
DrawResourceIcon( resource, i+1 );
|
|||
|
resource = resource->Next();
|
|||
|
}
|
|||
|
DisposeRgn( resultRgn );
|
|||
|
DisposeRgn( dragRgn );
|
|||
|
}
|
|||
|
SetPort( oldPort );
|
|||
|
oldMouse = mouse;
|
|||
|
}
|
|||
|
|
|||
|
// restore clip region
|
|||
|
// SetClip( oldClip );
|
|||
|
}
|
|||
|
|
|||
|
// update the window
|
|||
|
Update();
|
|||
|
}
|
|||
|
return eventNotHandledErr;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*** FILE WINDOW SCROLL ACTION ***/
|
|||
|
pascal void FileWindowScrollAction( ControlHandle control, SInt16 controlPart )
|
|||
|
{
|
|||
|
// get owning window
|
|||
|
if( !controlPart ) return;
|
|||
|
SInt8 state = HGetState( (Handle) control );
|
|||
|
HLock( (Handle) control );
|
|||
|
WindowRef window = GetControlOwner( control );
|
|||
|
HSetState( (Handle) control, state );
|
|||
|
WindowObjectPtr winObj = (WindowObjectPtr) GetWindowRefCon( window );
|
|||
|
|
|||
|
// get window bounds
|
|||
|
Rect windowBounds;
|
|||
|
if( g.windowMgrAvailable ) GetWindowBounds( window, kWindowContentRgn, &windowBounds );
|
|||
|
else GetPortBounds( (CGrafPort *) window, &windowBounds );
|
|||
|
windowBounds.top += kFileWindowHeaderHeight +1;
|
|||
|
windowBounds.bottom -= (kScrollBarWidth -1);
|
|||
|
windowBounds.right -= (kScrollBarWidth -1);
|
|||
|
UInt16 windowHeight = windowBounds.bottom - windowBounds.top;
|
|||
|
|
|||
|
// decide how many pixels to scroll (and in which direction)
|
|||
|
SInt16 delta = 0;
|
|||
|
switch( controlPart )
|
|||
|
{
|
|||
|
case kControlUpButtonPart: // up (20)
|
|||
|
delta = -kFileWindowRowHeight;
|
|||
|
break;
|
|||
|
|
|||
|
case kControlDownButtonPart: // down (21)
|
|||
|
delta = kFileWindowRowHeight;
|
|||
|
break;
|
|||
|
|
|||
|
case kControlPageUpPart: // page up (22)
|
|||
|
delta = kFileWindowRowHeight - windowHeight;
|
|||
|
break;
|
|||
|
|
|||
|
case kControlPageDownPart: // page down (23)
|
|||
|
delta = windowHeight - kFileWindowRowHeight;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// calculate and correct control value
|
|||
|
SInt16 max = GetControlMaximum( control );
|
|||
|
SInt16 startValue = GetControlValue( control );
|
|||
|
SInt16 endValue = startValue + delta;
|
|||
|
if( endValue < 0 ) endValue = 0;
|
|||
|
if( endValue > max ) endValue = max;
|
|||
|
|
|||
|
// only update window if anything changed
|
|||
|
if( endValue != startValue || controlPart == kControlIndicatorPart )
|
|||
|
{
|
|||
|
SetControlValue( control, endValue );
|
|||
|
winObj->Update();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
/*** NEW RESOURCE SHEET WINDOW EVENT HANDLER ***/
|
|||
|
pascal OSStatus NewResourceEventHandler( EventHandlerCallRef callRef, EventRef event, void *userData )
|
|||
|
{
|
|||
|
#pragma unused( callRef, userData )
|
|||
|
// get control that was hit
|
|||
|
UInt32 command;
|
|||
|
ControlRef control;
|
|||
|
GetEventParameter( event, kEventParamDirectObject, typeControlRef, null, sizeof(ControlRef), null, &control );
|
|||
|
GetControlCommandID( control, &command );
|
|||
|
|
|||
|
// get parent window (so resource is added to correct file)
|
|||
|
WindowRef parent;
|
|||
|
WindowRef sheet = GetControlOwner( control );
|
|||
|
GetSheetWindowParent( sheet, &parent );
|
|||
|
FileWindowPtr file = (FileWindowPtr) GetWindowRefCon( parent );
|
|||
|
|
|||
|
// do the button action
|
|||
|
switch( command )
|
|||
|
{
|
|||
|
case kHICommandOK:
|
|||
|
{ // close the sheet
|
|||
|
HideSheetWindow( sheet );
|
|||
|
DisposeWindow( sheet );
|
|||
|
|
|||
|
// extract all the info about the resource we need
|
|||
|
Str255 name = "\pTest Resource Beta";
|
|||
|
ResType type = 'test';
|
|||
|
SInt16 resID = 129;
|
|||
|
SInt16 attribs = 0;
|
|||
|
file->CreateNewResource( name, type, resID, attribs );
|
|||
|
} break;
|
|||
|
|
|||
|
case kHICommandCancel:
|
|||
|
// close the sheet
|
|||
|
HideSheetWindow( sheet );
|
|||
|
DisposeWindow( sheet );
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
DebugError( "\pCommand assigned to that control was not dealt with." );
|
|||
|
}
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/********************************/
|
|||
|
/* FILE WINDOW DRAWING ROUTINES */
|
|||
|
/********************************/
|
|||
|
|
|||
|
#if !TARGET_API_MAC_CARBON
|
|||
|
|
|||
|
/*** UPDATE SCROLL BARS ***/
|
|||
|
OSStatus FileWindow::UpdateScrollBars( void )
|
|||
|
{
|
|||
|
// get rect into which I can draw
|
|||
|
Rect windowBounds;
|
|||
|
if( g.windowMgrAvailable ) GetWindowBounds( window, kWindowContentRgn, &windowBounds );
|
|||
|
else GetPortBounds( (CGrafPort *) window, &windowBounds );
|
|||
|
windowBounds.top += kFileWindowHeaderHeight +1;
|
|||
|
windowBounds.bottom -= (kScrollBarWidth -1);
|
|||
|
windowBounds.right -= (kScrollBarWidth -1);
|
|||
|
UInt16 windowHeight = windowBounds.bottom - windowBounds.top;
|
|||
|
|
|||
|
// horizontal scroll bar
|
|||
|
if( nameColumnWidth + kFileWindowAllOtherColumnWidths - windowBounds.right <= 0 )
|
|||
|
SetControlMaximum( horizScroll, 0 );
|
|||
|
else SetControlMaximum( horizScroll, nameColumnWidth + kFileWindowAllOtherColumnWidths - windowBounds.right );
|
|||
|
|
|||
|
// vertical scroll bar
|
|||
|
SInt16 value = GetControlValue( vertScroll );
|
|||
|
if( windowHeight > numResources * kFileWindowRowHeight - value )
|
|||
|
SetControlMaximum( vertScroll, value );
|
|||
|
else SetControlMaximum( vertScroll, numResources * kFileWindowRowHeight - windowHeight );
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/*** DRAW RESOURCE ICON ***/
|
|||
|
OSStatus FileWindow::DrawResourceIcon( ResourceObjectPtr resource, UInt16 line )
|
|||
|
{
|
|||
|
OSStatus error = noErr;
|
|||
|
Rect nameRect, iconRect;
|
|||
|
Str255 nameStr;
|
|||
|
IconAlignmentType alignment;
|
|||
|
IconTransformType transformation;
|
|||
|
SInt16 value = GetControlValue( vertScroll );
|
|||
|
|
|||
|
// resource icon
|
|||
|
alignment = kAlignNone;
|
|||
|
if( resource->Selected() ) transformation = kTransformSelected;
|
|||
|
else transformation = kTransformNone;
|
|||
|
SetRect( &iconRect, 0, 0, 16, 16 );
|
|||
|
OffsetRect( &iconRect, kFileWindowNameColumnTextOffset -20, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight/2) - 8 - value );
|
|||
|
|
|||
|
IconSuiteRef iconSuite;
|
|||
|
GetIconSuite( &iconSuite, kDefaultResourceIcon, kSelectorAllSmallData );
|
|||
|
error = PlotIconSuite( &iconRect, alignment, transformation, iconSuite );
|
|||
|
if( error ) return error;
|
|||
|
|
|||
|
/* IconSelectorValue whichIcons = kSelectorSmall32Bit | kSelectorSmall8BitMask;
|
|||
|
IconFamilyHandle iconFamily;
|
|||
|
IconSuiteRef iconSuite;
|
|||
|
|
|||
|
iconFamily = (IconFamilyHandle) Get1Resource( kIconFamilyType, kDefaultResourceIcon );
|
|||
|
IconFamilyToIconSuite( iconFamily, whichIcons, &iconSuite );
|
|||
|
PlotIconSuite( &iconRect, alignment, transformation, iconSuite );
|
|||
|
DisposeIconSuite( iconSuite, false );
|
|||
|
ReleaseResource( (Handle) iconFamily );
|
|||
|
*/
|
|||
|
// get name
|
|||
|
if( *resource->Name() != 0x00 )
|
|||
|
{
|
|||
|
CopyPString( resource->name, nameStr );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Str255 unnamedStr;
|
|||
|
if( resource->dataFork ) GetIndString( unnamedStr, kResourceNameStrings, kStringDataFork );
|
|||
|
else if( resource->ID() == -16455 && (resource->Type() == 'icns' || resource->Type() == 'icl8' || resource->Type() == 'icl4' || resource->Type() == 'ICN#' || resource->Type() == 'ics8' || resource->Type() == 'ics4' || resource->Type() == 'ics#') )
|
|||
|
GetIndString( unnamedStr, kResourceNameStrings, kStringCustomIcon );
|
|||
|
else GetIndString( unnamedStr, kResourceNameStrings, kStringUntitledResource );
|
|||
|
CopyPString( unnamedStr, nameStr );
|
|||
|
}
|
|||
|
|
|||
|
// condense & italicise if necessary
|
|||
|
if( StringWidth( nameStr ) > nameColumnWidth - 45 )
|
|||
|
{
|
|||
|
if( resource->Dirty() ) TextFace( condense | bold );
|
|||
|
else TextFace( condense );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( resource->Dirty() ) TextFace( bold );
|
|||
|
else TextFace( normal );
|
|||
|
}
|
|||
|
|
|||
|
// add ellipsis if necessary
|
|||
|
while( StringWidth( nameStr ) > nameColumnWidth - kFileWindowNameColumnTextOffset -3 )
|
|||
|
{
|
|||
|
nameStr[0] -= 1; // shorten string
|
|||
|
nameStr[ nameStr[0] ] = 0xC9; // replace new last char with '<27>'
|
|||
|
}
|
|||
|
|
|||
|
// set text/box colour
|
|||
|
if( *resource->Name() == 0x00 )
|
|||
|
RGBForeColor( &g.frameColour );
|
|||
|
else RGBForeColor( &g.black );
|
|||
|
|
|||
|
// draw hilight box if selected
|
|||
|
if( resource->Selected() )
|
|||
|
{
|
|||
|
SetRect( &nameRect, 0, 0, StringWidth( nameStr ) +4, kFileWindowTextHeight );
|
|||
|
OffsetRect( &nameRect, kFileWindowNameColumnTextOffset -2, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight - kFileWindowTextHeight)/2 - kFileWindowTextHeight - value );
|
|||
|
PaintRect( &nameRect );
|
|||
|
RGBForeColor( &g.white ); // set text colour to white
|
|||
|
}
|
|||
|
|
|||
|
// draw name in set colour
|
|||
|
SInt16 font;
|
|||
|
GetFNum( "\pGeneva", &font );
|
|||
|
TextFont( font );
|
|||
|
TextSize( 10 );
|
|||
|
// TextFont( kControlFontSmallSystemFont ); // doesn't give right effect
|
|||
|
MoveTo( kFileWindowNameColumnTextOffset, kFileWindowHeaderHeight + (kFileWindowRowHeight * line) - (kFileWindowRowHeight - kFileWindowTextHeight)/2 - 3 - value );
|
|||
|
DrawString( nameStr );
|
|||
|
RGBForeColor( &g.black );
|
|||
|
|
|||
|
// create name & icon region for click detection and dragging
|
|||
|
RgnHandle addRgn = NewRgn();
|
|||
|
if( !addRgn )
|
|||
|
{
|
|||
|
DisposeIconSuite( iconSuite, true );
|
|||
|
return memFullErr;
|
|||
|
}
|
|||
|
|
|||
|
if( resource->nameIconRgn )
|
|||
|
DisposeRgn( resource->nameIconRgn );
|
|||
|
resource->nameIconRgn = NewRgn();
|
|||
|
if( !resource->nameIconRgn )
|
|||
|
{
|
|||
|
DisposeRgn( addRgn );
|
|||
|
DisposeIconSuite( iconSuite, true );
|
|||
|
return memFullErr;
|
|||
|
}
|
|||
|
|
|||
|
// resource icon
|
|||
|
SetRect( &iconRect, 0, 0, 16, 16 );
|
|||
|
OffsetRect( &iconRect, 22, kFileWindowHeaderHeight + (kFileWindowRowHeight * (line-1)) +2 );
|
|||
|
IconSuiteToRgn( resource->nameIconRgn, &iconRect, kAlignNone, iconSuite );
|
|||
|
|
|||
|
// resource name
|
|||
|
SetRect( &nameRect, 0, 0, StringWidth( nameStr ) +4, kFileWindowTextHeight );
|
|||
|
OffsetRect( &nameRect, 40, kFileWindowHeaderHeight + (kFileWindowRowHeight * (line-1)) +3 );
|
|||
|
RectRgn( addRgn, &nameRect ); // convert textRect to region
|
|||
|
UnionRgn( addRgn, resource->nameIconRgn, resource->nameIconRgn ); // add new region to icon region
|
|||
|
DisposeRgn( addRgn );
|
|||
|
DisposeIconSuite( iconSuite, true );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
/*****************************/
|
|||
|
/* RESOURCE MAP MANIPULATION */
|
|||
|
/*****************************/
|
|||
|
|
|||
|
/*** DISPLAY NEW RESOURCE SHEET ***/
|
|||
|
OSStatus FileWindow::DisplayNewResourceSheet( void )
|
|||
|
{
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// create a nib reference (only searches the application bundle)
|
|||
|
IBNibRef nibRef = null;
|
|||
|
OSStatus error = CreateNibReference( CFSTR("ResKnife"), &nibRef );
|
|||
|
if( error != noErr )
|
|||
|
{
|
|||
|
DisplayError( "\pThe nib file reference could not be obtained." );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
// create save sheet
|
|||
|
WindowRef sheet;
|
|||
|
error = CreateWindowFromNib( nibRef, CFSTR("New Resource"), &sheet );
|
|||
|
if( error != noErr )
|
|||
|
{
|
|||
|
DisplayError( "\pA sheet window could not be obtained from the nib file." );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
// dispose of nib ref
|
|||
|
DisposeNibReference( nibRef );
|
|||
|
|
|||
|
// install window event handler
|
|||
|
EventTypeSpec events = { kEventClassControl, kEventControlHit };
|
|||
|
EventHandlerUPP eventHandler = NewEventHandlerUPP( NewResourceEventHandler );
|
|||
|
InstallWindowEventHandler( sheet, eventHandler, 1, &events, 0, null );
|
|||
|
|
|||
|
// show sheet window
|
|||
|
ShowSheetWindow( sheet, window );
|
|||
|
return error;
|
|||
|
#else
|
|||
|
return eventNotHandledErr;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
/*** DISPLAY NEW RESOURCE DIALOG ***/
|
|||
|
OSStatus FileWindow::DisplayNewResourceDialog( void )
|
|||
|
{
|
|||
|
// create and show dialog
|
|||
|
OSStatus error = noErr;
|
|||
|
GrafPtr oldPort;
|
|||
|
DialogRef dialog = GetNewDialog( kNewResourceDialog, null, kFirstWindowOfClass );
|
|||
|
SetDialogDefaultItem( dialog, ok );
|
|||
|
GetPort( &oldPort );
|
|||
|
SetPortWindowPort( GetDialogWindow(dialog) );
|
|||
|
ShowWindow( GetDialogWindow(dialog) );
|
|||
|
|
|||
|
// set up variables
|
|||
|
Str255 name = "\pTest Resource Alpha";
|
|||
|
ResType type = 'test';
|
|||
|
SInt16 resID = 128;
|
|||
|
SInt16 attributes = 0x0000;
|
|||
|
|
|||
|
// handle dialog events
|
|||
|
Rect box;
|
|||
|
Handle item;
|
|||
|
short itemHit;
|
|||
|
DialogItemType itemType;
|
|||
|
do
|
|||
|
{
|
|||
|
ModalDialog( null, &itemHit );
|
|||
|
GetDialogItem( dialog, itemHit, &itemType, &item, &box );
|
|||
|
switch( itemHit )
|
|||
|
{
|
|||
|
case 5:
|
|||
|
{ Str255 popupText;
|
|||
|
MenuRef popupMenu = GetMenu( 140 );
|
|||
|
SInt16 popupItem = GetControlValue( (ControlRef) item );
|
|||
|
GetMenuItemText( popupMenu, popupItem, popupText );
|
|||
|
GetDialogItem( dialog, 4, &itemType, &item, &box );
|
|||
|
SetDialogItemText( item, popupText );
|
|||
|
if( popupItem == 1 ) SelectDialogItemText( dialog, 4, 0x0000, 0x0000 );
|
|||
|
else SelectDialogItemText( dialog, 4, 0x0000, 0xFFFF );
|
|||
|
RgnHandle updateRgn = NewRgn();
|
|||
|
RectRgn( updateRgn, &box );
|
|||
|
UpdateDialog( dialog, updateRgn );
|
|||
|
DisposeRgn( updateRgn );
|
|||
|
DisposeMenu( popupMenu );
|
|||
|
} break;
|
|||
|
|
|||
|
case 7:
|
|||
|
case 8:
|
|||
|
case 9:
|
|||
|
case 10:
|
|||
|
case 11:
|
|||
|
{ attributes ^= 1 << (itemHit -5);
|
|||
|
SetControlValue( (ControlRef) item, (short) (attributes & 1 << (itemHit -5)) );
|
|||
|
} break;
|
|||
|
}
|
|||
|
} while( itemHit != ok && itemHit != cancel );
|
|||
|
|
|||
|
HideWindow( GetDialogWindow(dialog) );
|
|||
|
if( itemHit == cancel )
|
|||
|
{
|
|||
|
DisposeDialog( dialog );
|
|||
|
SetPort( oldPort );
|
|||
|
return userCanceledErr;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
|
|||
|
// get resource name
|
|||
|
GetDialogItem( dialog, 3, &itemType, &item, &box );
|
|||
|
GetDialogItemText( item, name );
|
|||
|
|
|||
|
// get resource type
|
|||
|
Str255 string;
|
|||
|
GetDialogItem( dialog, 4, &itemType, &item, &box );
|
|||
|
GetDialogItemText( item, string );
|
|||
|
if( *string != sizeof(ResType) )
|
|||
|
{
|
|||
|
DisplayError( "\pInvalid resource type given", "\pYou should either choose a type from the pop-up menu, or enter a four character resource type of your own." );
|
|||
|
SetPort( oldPort );
|
|||
|
DisposeDialog( dialog );
|
|||
|
return paramErr;
|
|||
|
}
|
|||
|
BlockMoveData( string +1, &type, sizeof(ResType) );
|
|||
|
|
|||
|
// get resource ID
|
|||
|
long number;
|
|||
|
GetDialogItem( dialog, 6, &itemType, &item, &box );
|
|||
|
GetDialogItemText( item, string );
|
|||
|
StringToNum( string, &number );
|
|||
|
if( number < -32768 || number > 32767 )
|
|||
|
{
|
|||
|
DisplayError( "\pInvalid resource ID chosen", "\pYou have to pick a number between -32768 and +32767. All ID numbers less than 128 are reserved by Apple." );
|
|||
|
SetPort( oldPort );
|
|||
|
DisposeDialog( dialog );
|
|||
|
return paramErr;
|
|||
|
}
|
|||
|
resID = (short) number;
|
|||
|
|
|||
|
// create resource
|
|||
|
error = CreateNewResource( name, type, resID, attributes );
|
|||
|
}
|
|||
|
SetPort( oldPort );
|
|||
|
DisposeDialog( dialog );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** CREATE NEW RESOURCE ***/
|
|||
|
OSStatus FileWindow::CreateNewResource( ConstStr255Param name, ResType type, SInt16 resID, SInt16 attributes )
|
|||
|
{
|
|||
|
// cycle through ResourceObject chain
|
|||
|
OSStatus error = noErr;
|
|||
|
ResourceObjectPtr last = resourceMap, addition = (ResourceObjectPtr) NewPtrClear( sizeof(ResourceObject) );
|
|||
|
while( last->next ) last = last->next;
|
|||
|
|
|||
|
// append new resource to chain
|
|||
|
last->next = addition;
|
|||
|
addition->file = this;
|
|||
|
addition->number = last->number + 1;
|
|||
|
addition->data = NewHandleClear( 0 );
|
|||
|
BlockMoveData( name, addition->name, sizeof(Str255) );
|
|||
|
addition->size = 0;
|
|||
|
addition->type = type;
|
|||
|
addition->resID = resID;
|
|||
|
addition->attribs = attributes;
|
|||
|
|
|||
|
// update the file's resource counts
|
|||
|
numResources += 1;
|
|||
|
if( GetResourceCount(type) == 0 )
|
|||
|
numTypes += 1;
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
// add the resource to the databrowser
|
|||
|
error = AddDataBrowserItems( dataBrowser, kDataBrowserNoItem, 1, &addition->number, kDataBrowserItemNoProperty );
|
|||
|
#endif
|
|||
|
|
|||
|
// mark file as dirty
|
|||
|
fileDirty = true;
|
|||
|
SetWindowModified( window, fileDirty );
|
|||
|
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** OPEN RESOURCE ***/
|
|||
|
OSStatus FileWindow::OpenResource( DataBrowserItemID itemID, MenuCommand command )
|
|||
|
{
|
|||
|
#pragma unused( command )
|
|||
|
// get opened resource
|
|||
|
OSStatus error = noErr;
|
|||
|
Boolean stop = false;
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
|
|||
|
// check for null resource
|
|||
|
if( resource == null )
|
|||
|
{
|
|||
|
DisplayError( "\pYou just double-clicked on a non-existant resource!" );
|
|||
|
return paramErr;
|
|||
|
}
|
|||
|
|
|||
|
// open correct editor
|
|||
|
switch( command )
|
|||
|
{
|
|||
|
case kMenuCommandOpenHex:
|
|||
|
LoadEditor( resource, "\pHex Editor" );
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandOpenDefault:
|
|||
|
Str255 typeStr;
|
|||
|
TypeToPString( resource->Type(), typeStr );
|
|||
|
AppendPString( typeStr, "\p Editor" );
|
|||
|
LoadEditor( resource, typeStr );
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandOpenTemplate:
|
|||
|
LoadEditor( resource, "\pTemplate Editor" );
|
|||
|
error = eventNotHandledErr;
|
|||
|
break;
|
|||
|
|
|||
|
case kMenuCommandOpenSpecific:
|
|||
|
// bug: display template selection dialog here
|
|||
|
error = eventNotHandledErr;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
DebugError( "\pWrong command sent to FileWindow::OpenResource()" );
|
|||
|
error = eventNotHandledErr;
|
|||
|
break;
|
|||
|
}
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
/*** DISPOSE RESOURCE MAP ***/
|
|||
|
OSStatus FileWindow::DisposeResourceMap()
|
|||
|
{
|
|||
|
if( !resourceMap ) return noErr; // resource map already disposed
|
|||
|
|
|||
|
UInt32 numDeleted = 0;
|
|||
|
ResourceObjectPtr current = resourceMap, next;
|
|||
|
while( current )
|
|||
|
{
|
|||
|
next = current->Next();
|
|||
|
if( current->nameIconRgn ) DisposeRgn( current->nameIconRgn );
|
|||
|
if( current->data ) DisposeHandle( current->data );
|
|||
|
DisposePtr( (Ptr) current );
|
|||
|
// delete current;
|
|||
|
numDeleted += 1;
|
|||
|
current = next;
|
|||
|
}
|
|||
|
if( numResources != numDeleted ) return paramErr; // my lazy way of saying I don't know what happened
|
|||
|
else return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/******************/
|
|||
|
/* SOUND ROUTINES */
|
|||
|
/******************/
|
|||
|
|
|||
|
/*** PLAY SOUND ***/
|
|||
|
OSStatus FileWindow::PlaySound( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
long refNum = 0; // unused
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource->Data() )
|
|||
|
{
|
|||
|
SInt8 state = HGetState( resource->Data() );
|
|||
|
HLock( resource->Data() );
|
|||
|
if( g.asyncSound ) SHPlayByHandle( resource->Data(), &refNum );
|
|||
|
else SndPlay( nil, (SndListHandle) resource->Data(), false );
|
|||
|
HSetState( resource->Data(), state );
|
|||
|
}
|
|||
|
return noErr;
|
|||
|
}
|
|||
|
|
|||
|
/*******************************/
|
|||
|
/* VARIABLE ACCESSOR FUNCTIONS */
|
|||
|
/*******************************/
|
|||
|
|
|||
|
/*** GET FILE SPEC ***/
|
|||
|
FSSpecPtr FileWindow::GetFileSpec( void )
|
|||
|
{
|
|||
|
return fileSpec;
|
|||
|
}
|
|||
|
|
|||
|
/*** SET FILE SPEC ***/
|
|||
|
void FileWindow::SetFileSpec( FSSpecPtr spec )
|
|||
|
{
|
|||
|
fileExists = spec? true:false;
|
|||
|
if( fileExists )
|
|||
|
{
|
|||
|
BlockMoveData( (Ptr) spec, (Ptr) fileSpec, sizeof(FSSpec) );
|
|||
|
SetWindowProxyFSSpec( window, fileSpec );
|
|||
|
SetWindowModified( window, fileDirty );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DisposePtr( (Ptr) fileSpec );
|
|||
|
fileSpec = (FSSpecPtr) NewPtrClear( sizeof(FSSpec) );
|
|||
|
SetWindowProxyCreatorAndType( window, kResKnifeCreator, kResourceFileType, kOnSystemDisk );
|
|||
|
SetWindowModified( window, true );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*** IS FILE DIRTY ***/
|
|||
|
Boolean FileWindow::IsFileDirty( void )
|
|||
|
{
|
|||
|
return fileDirty;
|
|||
|
}
|
|||
|
|
|||
|
/*** SET FILE DIRTY ***/
|
|||
|
void FileWindow::SetFileDirty( Boolean dirty )
|
|||
|
{
|
|||
|
fileDirty = dirty; // bug: used to crash my machine but mysteriously doesn't any more
|
|||
|
SetWindowModified( window, dirty );
|
|||
|
}
|
|||
|
|
|||
|
#if TARGET_API_MAC_CARBON
|
|||
|
|
|||
|
/*** GET DATA BROWSER ***/
|
|||
|
ControlRef FileWindow::GetDataBrowser( void )
|
|||
|
{
|
|||
|
return dataBrowser;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
/**********************/
|
|||
|
/* RESOURCE ACCESSORS */
|
|||
|
/**********************/
|
|||
|
|
|||
|
/*** GET RESOURCE COUNT ***/
|
|||
|
UInt32 FileWindow::GetResourceCount( ResType wanted )
|
|||
|
{
|
|||
|
#pragma unused( wanted )
|
|||
|
return numResources;
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE ***/
|
|||
|
ResourceObjectPtr FileWindow::GetResource( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr res = resourceMap;
|
|||
|
if( itemID == kDataBrowserNoItem ) return null;
|
|||
|
while( itemID != res->Number() )
|
|||
|
{
|
|||
|
res = res->Next();
|
|||
|
if( res == null ) return null;
|
|||
|
}
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE NAME ***/
|
|||
|
UInt8* FileWindow::GetResourceName( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource == null ) return null;
|
|||
|
return resource->Name();
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE SIZE ***/
|
|||
|
UInt32 FileWindow::GetResourceSize( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource == null ) return null;
|
|||
|
return resource->Size();
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE TYPE ***/
|
|||
|
ResType FileWindow::GetResourceType( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource == null ) return null;
|
|||
|
return resource->Type();
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE ID ***/
|
|||
|
SInt16 FileWindow::GetResourceID( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource == null ) return null;
|
|||
|
return resource->ID();
|
|||
|
}
|
|||
|
|
|||
|
/*** GET RESOURCE ATTRIBUTES ***/
|
|||
|
SInt16 FileWindow::GetResourceAttributes( DataBrowserItemID itemID )
|
|||
|
{
|
|||
|
ResourceObjectPtr resource = GetResource( itemID );
|
|||
|
if( resource == null ) return null;
|
|||
|
return resource->Attributes();
|
|||
|
}
|