ResKnife/Carbon/Classes/FileWindow.cpp
2003-04-03 15:38:42 +00:00

1803 lines
58 KiB
C++
Raw Blame History

#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 )
{
DisposeNibReference( nibRef );
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, sectRect;
SetRect( &creationBounds, 0, 0, kDefaultFileWindowWidth, kDefaultFileWindowHeight );
GetAvailableWindowPositioningBounds( GetMainDevice(), &sectRect );
InsetRect( &sectRect, 0, 10 );
SectRect( &sectRect, &creationBounds, &creationBounds );
OffsetRect( &creationBounds, 10, 0 );
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 >= kMacOS8 )
{
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 >= kMacOS85 )
{
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();
}