ResKnife/Carbon/Classes/FileWindow.cpp

1803 lines
57 KiB
C++
Raw Permalink 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();
}