mirror of
https://github.com/nickshanks/ResKnife.git
synced 2024-06-03 01:29:40 +00:00
1146 lines
31 KiB
C++
1146 lines
31 KiB
C++
#include "Application.h"
|
||
#include "Asynchronous.h" // for initing, idling, and disposing
|
||
#include "Errors.h"
|
||
#include "Files.h" // for open & save etc.
|
||
#include "FileWindow.h" // no particuar reason
|
||
#include "ResourceObject.h"
|
||
#include "Utility.h"
|
||
|
||
// set up prefs and globals
|
||
globals g;
|
||
prefs p;
|
||
|
||
/*** MAIN ***/
|
||
int main(int argc, char* argv[])
|
||
{
|
||
#pragma unused(argc, argv)
|
||
|
||
// get system version
|
||
OSStatus error = Gestalt(gestaltSystemVersion, &g.systemVersion); // this loads HIToolbox.framework on OS X
|
||
if(error) return error;
|
||
|
||
// initalise application
|
||
error = InitToolbox();
|
||
if(error) return error;
|
||
error = InitMenubar();
|
||
if(error) return error;
|
||
error = InitAppleEvents();
|
||
if(error) return error;
|
||
error = InitCarbonEvents();
|
||
if(error) return error;
|
||
error = InitGlobals();
|
||
if(error) return error;
|
||
InitCursor();
|
||
|
||
// check system version is at least 7.1
|
||
if(g.systemVersion < kMacOS71)
|
||
{
|
||
DisplayError(kStringOSNotGoodEnough, kExplanationOSNotGoodEnough);
|
||
QuitResKnife();
|
||
}
|
||
|
||
#if TARGET_API_MAC_CARBON
|
||
// check carbon version is at least 1.1
|
||
error = Gestalt(gestaltCarbonVersion, &g.carbonVersion);
|
||
if(g.carbonVersion < kCarbonLib11 || error)
|
||
{
|
||
DisplayError(kStringMinimumCarbonLib, kExplanationMinimumCarbonLib);
|
||
QuitResKnife();
|
||
}
|
||
else if(g.carbonVersion < kCarbonLib131)
|
||
{
|
||
DisplayError(kStringRecommendedCarbonLib, kExplanationRecommendedCarbonLib);
|
||
}
|
||
#endif
|
||
|
||
#if __profile__
|
||
error = ProfilerInit(collectDetailed, bestTimeBase, 400, 40);
|
||
if(error) DebugStr("\pProfiler initalisation failed"); // profiler failed
|
||
#endif
|
||
|
||
#if TARGET_API_MAC_CARBON
|
||
// run event loop
|
||
RunApplicationEventLoop();
|
||
#else
|
||
EventRecord theEvent;
|
||
while(!g.quitting)
|
||
{
|
||
WaitNextEvent(everyEvent, &theEvent, 20, null);
|
||
if(IsDialogEvent(&theEvent))
|
||
ParseDialogEvents(null, &theEvent, null);
|
||
else ParseEvents(&theEvent);
|
||
}
|
||
#endif
|
||
|
||
#if __profile__
|
||
ProfilerDump("\pResKnife profile");
|
||
ProfilerTerm();
|
||
#endif
|
||
|
||
#if !TARGET_API_MAC_CARBON
|
||
QuitResKnife();
|
||
#endif
|
||
|
||
return error;
|
||
}
|
||
|
||
/*** INIT TOOLBOX ***/
|
||
OSStatus InitToolbox(void)
|
||
{
|
||
#if !TARGET_API_MAC_CARBON
|
||
InitGraf(&qd.thePort);
|
||
InitFonts();
|
||
FlushEvents(everyEvent, 0);
|
||
InitWindows();
|
||
InitMenus();
|
||
TEInit();
|
||
InitDialogs(0L);
|
||
#endif
|
||
return noErr;
|
||
}
|
||
|
||
/*** INITALIZE MENUBAR ***/
|
||
OSStatus InitMenubar(void)
|
||
{
|
||
OSStatus error = noErr;
|
||
|
||
#if USE_NIBS
|
||
IBNibRef nibRef = null;
|
||
|
||
// create a nib reference (only searches the application bundle)
|
||
error = CreateNibReference(CFSTR("ResKnife"), &nibRef);
|
||
if(error != noErr)
|
||
{
|
||
DisplayError("\pThe nib file reference could not be obtained.");
|
||
return error;
|
||
}
|
||
|
||
// get menu bar
|
||
error = SetMenuBarFromNib(nibRef, CFSTR("Menubar"));
|
||
if(error != noErr)
|
||
{
|
||
DisplayError("\pMenus could not be obtained from nib file.");
|
||
return error;
|
||
}
|
||
|
||
// dispose of nib ref
|
||
DisposeNibReference(nibRef);
|
||
|
||
#else /* ! USE_NIBS */
|
||
|
||
Handle menuList = GetNewMBar(kClassicMenuBar);
|
||
SetMenuBar(menuList);
|
||
ReleaseResource(menuList);
|
||
|
||
// delete quit and prefs on OS X
|
||
long result;
|
||
error = Gestalt(gestaltMenuMgrAttr, &result);
|
||
if(!error && (result & gestaltMenuMgrAquaLayoutMask))
|
||
{
|
||
MenuRef fileMenu = GetMenuRef(kFileMenu);
|
||
MenuRef editMenu = GetMenuRef(kEditMenu);
|
||
DeleteMenuItem(fileMenu, kFileMenuQuitItem);
|
||
DeleteMenuItem(fileMenu, kFileMenuQuitItem -1);
|
||
DeleteMenuItem(editMenu, kEditMenuPreferencesItem);
|
||
DeleteMenuItem(editMenu, kEditMenuPreferencesItem -1);
|
||
}
|
||
|
||
// set delete item character to the delete glyph
|
||
MenuRef editMenu = GetMenuRef(kEditMenu);
|
||
SetMenuItemKeyGlyph(editMenu, kEditMenuClearItem, kMenuDeleteLeftGlyph);
|
||
|
||
#if TARGET_API_MAC_CARBON
|
||
MenuRef windowMenu;
|
||
CreateStandardWindowMenu(0, &windowMenu);
|
||
InsertMenu(windowMenu, kWindowMenu);
|
||
#else
|
||
AppendResMenu(GetMenuRef(kAppleMenu), 'DRVR');
|
||
#endif /* TARGET_CARBON */
|
||
DrawMenuBar();
|
||
|
||
#endif /* USE_NIBS */
|
||
|
||
return error;
|
||
}
|
||
|
||
/*** INIT APPLE EVENTS ***/
|
||
OSStatus InitAppleEvents(void)
|
||
{
|
||
AEEventHandlerUPP appleEventParser = NewAEEventHandlerUPP(ParseAppleEvents);
|
||
AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, appleEventParser, 0, false);
|
||
AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, appleEventParser, 0, false);
|
||
AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, appleEventParser, 0, false);
|
||
AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, appleEventParser, 0, false);
|
||
AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, appleEventParser, 0, false);
|
||
return noErr;
|
||
}
|
||
|
||
/*** INIT CARBON EVENTS ***/
|
||
OSStatus InitCarbonEvents(void)
|
||
{
|
||
#if TARGET_API_MAC_CARBON
|
||
EventHandlerUPP handler = null;
|
||
EventHandlerRef ref = null;
|
||
EventTypeSpec update = { kEventClassMenu, kEventMenuEnableItems };
|
||
EventTypeSpec process = { kEventClassCommand, kEventCommandProcess };
|
||
|
||
// install menu adjust handler
|
||
handler = NewEventHandlerUPP(CarbonEventUpdateMenus);
|
||
InstallApplicationEventHandler(handler, 1, &update, null, &ref);
|
||
|
||
// install menu selection handler
|
||
handler = NewEventHandlerUPP(CarbonEventParseMenuSelection);
|
||
InstallApplicationEventHandler(handler, 1, &process, null, &ref);
|
||
|
||
// install default idle timer <20> 200 millisecond interval - bug: this should be in a seperate thread and the cursor blink time
|
||
EventLoopTimerUPP timerUPP = NewEventLoopTimerUPP(DefaultIdleTimer);
|
||
OSStatus error = InstallEventLoopTimer(GetMainEventLoop(), kEventDurationNoWait, kEventDurationMillisecond * 200, timerUPP, null, &g.idleTimer);
|
||
return error;
|
||
#else
|
||
return noErr;
|
||
#endif
|
||
}
|
||
|
||
/*** INITALIZE GLOBALS ***/
|
||
OSStatus InitGlobals(void)
|
||
{
|
||
OSStatus error = noErr;
|
||
|
||
// general app globals
|
||
g.quitting = false;
|
||
g.cancelQuit = false;
|
||
g.frontApp = true;
|
||
g.appResFile = CurResFile();
|
||
g.asyncSound = !(Boolean) SHInitSoundHelper(&g.callSH, kSHDefChannels);
|
||
g.emergencyMemory = NewHandleClear(kEmergencyMemory);
|
||
|
||
// files
|
||
g.tempCount = 0;
|
||
|
||
// debugging
|
||
g.debug = false;
|
||
g.surpressErrors = false;
|
||
g.useAppleEvents = true;
|
||
g.useSheets = (g.carbonVersion >= kCarbonLib11)? true:false;
|
||
|
||
// prefs dialog
|
||
g.prefsDialog = null;
|
||
p.warnOnDelete = true;
|
||
|
||
// colours
|
||
SetColour(&g.white, 0xFFFF, 0xFFFF, 0xFFFF);
|
||
SetColour(&g.bgColour, 0xEEEE, 0xEEEE, 0xEEEE);
|
||
SetColour(&g.sortColour, 0xDDDD, 0xDDDD, 0xDDDD);
|
||
SetColour(&g.bevelColour, 0xAAAA, 0xAAAA, 0xAAAA);
|
||
SetColour(&g.textColour, 0x7777, 0x7777, 0x7777);
|
||
SetColour(&g.frameColour, 0x5555, 0x5555, 0x5555);
|
||
SetColour(&g.black, 0x0000, 0x0000, 0x0000);
|
||
|
||
#if TARGET_API_MAC_CARBON
|
||
// window manager
|
||
g.windowMgrAvailable = true;
|
||
g.extendedWindowAttr = true;
|
||
|
||
// drag manager
|
||
g.dragAvailable = true;
|
||
g.translucentDrag = true;
|
||
|
||
// appearance manager
|
||
g.appearanceAvailable = true;
|
||
g.useAppearance = g.appearanceAvailable; // assume if user has Appearence, s/he wants to use it
|
||
if(g.useAppearance) RegisterAppearanceClient(); // register such with the OS
|
||
|
||
// nav services
|
||
g.navAvailable = true;
|
||
g.useNavServices = g.navAvailable; // assume if user has NavServices, s/he wants to use them
|
||
if(g.navAvailable) NavLoad(); // preload for efficiency - ignored on OS X (always loaded)
|
||
#else
|
||
// check for drag manager presence/attributes
|
||
SInt32 result = null;
|
||
error = Gestalt(gestaltDragMgrAttr, &result);
|
||
if(!error) {
|
||
g.dragAvailable = (Boolean) (result & (1 << gestaltDragMgrPresent));
|
||
g.translucentDrag = (Boolean) (result & (1 << gestaltDragMgrHasImageSupport));
|
||
} else {
|
||
g.dragAvailable = false;
|
||
g.translucentDrag = false;
|
||
}
|
||
|
||
// check appearance availablilty
|
||
result = null;
|
||
error = Gestalt(gestaltAppearanceAttr, &result);
|
||
if(!error) {
|
||
g.appearanceAvailable = (Boolean) (result & (1 << gestaltAppearanceExists));
|
||
g.useAppearance = g.appearanceAvailable; // assume if user has Appearence, s/he wants to use it
|
||
} else {
|
||
g.appearanceAvailable = false;
|
||
g.useAppearance = false;
|
||
}
|
||
if(g.useAppearance) RegisterAppearanceClient(); // register such with the OS
|
||
|
||
// check nav services availablilty
|
||
g.navAvailable = (Boolean) NavServicesAvailable();
|
||
g.useNavServices = g.navAvailable; // assume if user has NavServices, s/he wants to use them
|
||
if(g.navAvailable) NavLoad(); // preload for efficiency
|
||
|
||
// check for MacOS 8.5's window manager (also in CarbonLib 1.0 - backported to 8.1)
|
||
result = null;
|
||
error = Gestalt(gestaltWindowMgrAttr, &result);
|
||
if(!error) {
|
||
g.windowMgrAvailable = (Boolean) (result & (1 << gestaltWindowMgrPresentBit));
|
||
g.extendedWindowAttr = (Boolean) (result & (1 << gestaltExtendedWindowAttributes));
|
||
} else {
|
||
g.windowMgrAvailable = false;
|
||
g.extendedWindowAttr = false;
|
||
}
|
||
|
||
UpdateMenus(null);
|
||
#endif
|
||
return error;
|
||
}
|
||
|
||
#if !TARGET_API_MAC_CARBON
|
||
|
||
/*** PARSE EVENTS ***/
|
||
OSStatus ParseEvents(EventRecord *event)
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
switch(event->what)
|
||
{
|
||
case nullEvent:
|
||
IdleEvent();
|
||
break;
|
||
case mouseDown:
|
||
error = MouseDownEventOccoured(event);
|
||
break;
|
||
case mouseUp:
|
||
error = MouseUpEventOccoured(event);
|
||
break;
|
||
case keyDown:
|
||
error = KeyDownEventOccoured(event);
|
||
break;
|
||
case autoKey:
|
||
error = KeyRepeatEventOccoured(event);
|
||
break;
|
||
case keyUp:
|
||
error = KeyUpEventOccoured(event);
|
||
break;
|
||
case updateEvt:
|
||
error = UpdateEventOccoured(event);
|
||
break;
|
||
case activateEvt:
|
||
error = ActivateEventOccoured(event);
|
||
break;
|
||
case osEvt:
|
||
error = ParseOSEvents(event);
|
||
break;
|
||
case kHighLevelEvent:
|
||
error = AEProcessAppleEvent(event);
|
||
break;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*** PARSE DIALOG EVENTS ***/
|
||
pascal Boolean ParseDialogEvents(DialogPtr dialog, EventRecord *event, DialogItemIndex *itemHit)
|
||
{
|
||
#pragma unused(dialog, event, itemHit)
|
||
/* OSStatus error = eventNotHandledErr;
|
||
if(dialog == null && itemHit == null);
|
||
*/ return false;
|
||
}
|
||
|
||
/*** PARSE OS EVENTS ***/
|
||
OSStatus ParseOSEvents(EventRecord *event)
|
||
{
|
||
#pragma unused(event)
|
||
OSStatus error = eventNotHandledErr;
|
||
SInt8 eventType = event->message >> 24; // high byte of message field
|
||
if(eventType & mouseMovedMessage)
|
||
{
|
||
// mouse moved event
|
||
}
|
||
else if(eventType & suspendResumeMessage) // suspend/resume event
|
||
{
|
||
g.frontApp = (event->message & resumeFlag); // true on resume
|
||
if(FrontWindow()) // only de/activate front window (if present)
|
||
{
|
||
WindowObjectPtr winObj = (WindowObjectPtr) GetWindowRefCon(FrontWindow());
|
||
error = winObj->Activate(g.frontApp);
|
||
}
|
||
if(event->message & convertClipboardFlag)
|
||
{
|
||
// convert clipboard to private scrap
|
||
}
|
||
}
|
||
else error = paramErr;
|
||
return error;
|
||
}
|
||
|
||
#endif
|
||
|
||
/*** PARSE APPLE EVENTS ***/
|
||
pascal OSErr ParseAppleEvents(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
|
||
{
|
||
#pragma unused(reply, refCon)
|
||
|
||
OSErr error;
|
||
Size actualSize;
|
||
DescType actualType;
|
||
DescType eventClass, eventID;
|
||
|
||
error = AEGetAttributePtr((AppleEvent *) event, keyEventClassAttr, typeType, &actualType, (Ptr) &eventClass, sizeof(eventClass), &actualSize);
|
||
if(error) return errAEEventNotHandled;
|
||
|
||
error = AEGetAttributePtr((AppleEvent *) event, keyEventIDAttr, typeType, &actualType, (Ptr) &eventID, sizeof(eventID), &actualSize);
|
||
if(error) return errAEEventNotHandled;
|
||
|
||
switch(eventClass)
|
||
{
|
||
case kCoreEventClass:
|
||
switch(eventID)
|
||
{
|
||
case kAEOpenApplication: // sent when app opened directly (ie not file opened)
|
||
#if TARGET_API_MAC_CARBON
|
||
DisplayOpenDialog();
|
||
#else
|
||
if(g.useNavServices)
|
||
DisplayOpenDialog();
|
||
else DisplayStandardFileOpenDialog();
|
||
#endif
|
||
break;
|
||
|
||
case kAEReopenApplication: // sent when app is double-clicked on, but is already open
|
||
if(FrontWindow() == null)
|
||
{
|
||
AEDescList list = {};
|
||
#if TARGET_API_MAC_CARBON
|
||
AppleEventSendSelf(kCoreEventClass, kAEOpenApplication, list);
|
||
#else
|
||
if(g.useAppleEvents)
|
||
AppleEventSendSelf(kCoreEventClass, kAEOpenApplication, list);
|
||
else if(g.useNavServices)
|
||
DisplayOpenDialog();
|
||
else DisplayStandardFileOpenDialog();
|
||
#endif
|
||
}
|
||
break;
|
||
|
||
case kAEOpenDocuments: // sent when file is double-clicked on in finder,
|
||
AppleEventOpen(event); // or open is chosen in the file menu and g.useAppleEvents is true
|
||
break;
|
||
|
||
case kAEPrintDocuments: // sent when document is dragged onto printer
|
||
AppleEventPrint(event);
|
||
break;
|
||
|
||
case kAEQuitApplication: // sent from many locations (eg after restart command)
|
||
QuitResKnife();
|
||
break;
|
||
}
|
||
break;
|
||
|
||
/* case kAECoreSuite: // i'm not even registering for these yet
|
||
switch(eventID)
|
||
{
|
||
case kAECut:
|
||
case kAECopy:
|
||
case kAEPaste:
|
||
case kAEDelete:
|
||
DisplayErrorDialog("\pSorry, but cut, copy, paste and clear via Apple Events arn't yet supported.");
|
||
error = errAEEventNotHandled;
|
||
break;
|
||
}
|
||
break;
|
||
*/ }
|
||
return error;
|
||
}
|
||
|
||
/******************/
|
||
/* EVENT HANDLING */
|
||
/******************/
|
||
|
||
#if !TARGET_API_MAC_CARBON
|
||
|
||
/*** MOUSE DOWN EVENT OCCOURED ***/
|
||
OSStatus MouseDownEventOccoured(EventRecord *event)
|
||
{
|
||
// get the window
|
||
OSStatus error = eventNotHandledErr;
|
||
WindowRef window;
|
||
SInt16 windowPart = FindWindow(event->where, &window);
|
||
WindowObjectPtr winObj = (WindowObjectPtr) GetWindowRefCon(window);
|
||
|
||
// find out where the click occoured
|
||
if(!windowPart) return error;
|
||
else switch(windowPart)
|
||
{
|
||
case inMenuBar:
|
||
error = UpdateMenus(FrontWindow()); // error ignored at the moment
|
||
UInt32 menuChoice = MenuSelect(event->where);
|
||
UInt16 menu = HiWord(menuChoice);
|
||
UInt16 item = LoWord(menuChoice);
|
||
error = ParseMenuSelection(menu, item); // error ignored at the moment
|
||
HiliteMenu(0);
|
||
break;
|
||
|
||
case inSysWindow:
|
||
SystemClick(event, window);
|
||
break;
|
||
|
||
case inContent:
|
||
SelectWindow(window);
|
||
winObj->Click(event->where, event->modifiers);
|
||
break;
|
||
|
||
case inDrag:
|
||
DragWindow(window, event->where, &qdb);
|
||
winObj->BoundsChanged(null);
|
||
break;
|
||
|
||
case inGrow:
|
||
Rect bounds; // minimum and maximum bounds of window
|
||
SetRect(&bounds, 128, 128, 1024, 1024);
|
||
SInt32 result = GrowWindow(window, event->where, &bounds);
|
||
if(result)
|
||
{
|
||
SInt16 newWidth = LoWord(result);
|
||
SInt16 newHeight = HiWord(result);
|
||
SizeWindow(window, newWidth, newHeight, false);
|
||
winObj->BoundsChanged(null);
|
||
}
|
||
break;
|
||
|
||
case inGoAway:
|
||
if(TrackGoAway(window, event->where))
|
||
winObj->Close();
|
||
break;
|
||
|
||
case inZoomIn:
|
||
case inZoomOut:
|
||
if(TrackBox(window, event->where, windowPart))
|
||
{
|
||
ZoomWindow(window, windowPart, window == FrontWindow()); // I think the last param *might* need to be "g.frontApp"
|
||
winObj->BoundsChanged(null);
|
||
}
|
||
break;
|
||
|
||
case inCollapseBox:
|
||
case inProxyIcon:
|
||
break;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*** MOUSE UP EVENT OCCOURED ***/
|
||
OSStatus MouseUpEventOccoured(EventRecord *event)
|
||
{
|
||
#pragma unused(event)
|
||
OSStatus error = eventNotHandledErr;
|
||
return error;
|
||
}
|
||
|
||
/*** KEY DOWN EVENT OCCOURED ***/
|
||
OSStatus KeyDownEventOccoured(EventRecord *event)
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
char key = (char)(event->message & charCodeMask); // get the key pressed
|
||
if(event->modifiers & cmdKey) // was it a menu shortcut?
|
||
{
|
||
UpdateMenus(FrontWindow());
|
||
UInt32 menuChoice = MenuKey(key);
|
||
UInt16 menu = HiWord(menuChoice);
|
||
UInt16 item = LoWord(menuChoice);
|
||
if(menu && item)
|
||
error = ParseMenuSelection(menu, item);
|
||
}
|
||
return error;
|
||
}
|
||
|
||
/*** KEY REPEAT EVENT OCCOURED ***/
|
||
OSStatus KeyRepeatEventOccoured(EventRecord *event)
|
||
{
|
||
OSStatus error = KeyDownEventOccoured(event);
|
||
return error;
|
||
}
|
||
|
||
/*** KEY UP EVENT OCCOURED ***/
|
||
OSStatus KeyUpEventOccoured(EventRecord *event)
|
||
{
|
||
#pragma unused(event)
|
||
OSStatus error = eventNotHandledErr;
|
||
return error;
|
||
}
|
||
|
||
/*** UPDATE EVENT OCCOURED ***/
|
||
OSStatus UpdateEventOccoured(EventRecord *event)
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
GrafPtr oldPort;
|
||
WindowRef window = (WindowRef) event->message;
|
||
|
||
// send update events to window
|
||
GetPort(&oldPort);
|
||
SetPortWindowPort(window);
|
||
BeginUpdate(window); // this sets up the visRgn
|
||
{
|
||
RgnHandle updateRgn = NewRgn();
|
||
WindowObjectPtr winObj = (WindowObjectPtr) GetWindowRefCon(window);
|
||
GetPortVisibleRegion(GetWindowPort(window), updateRgn);
|
||
if(winObj) error = winObj->Update(updateRgn);
|
||
DisposeRgn(updateRgn);
|
||
}
|
||
EndUpdate(window);
|
||
SetPort(oldPort);
|
||
return error;
|
||
}
|
||
|
||
/*** ACTIVATE EVENT OCCOURED ***/
|
||
OSStatus ActivateEventOccoured(EventRecord *event)
|
||
{
|
||
OSStatus error = eventNotHandledErr;
|
||
WindowObjectPtr winObj = (WindowObjectPtr) GetWindowRefCon((WindowRef) event->message);
|
||
if(winObj) error = winObj->Activate((Boolean) (event->modifiers & activeFlag));
|
||
return error;
|
||
}
|
||
|
||
/*** IDLE EVENT ***/
|
||
OSStatus IdleEvent(void)
|
||
{
|
||
// call sound idle routine
|
||
if(g.asyncSound) SHIdle();
|
||
|
||
// compact all memory
|
||
/* SInt32 total, contig;
|
||
PurgeMem(kEmergencyMemory);
|
||
CompactMem(kEmergencyMemory);
|
||
PurgeSpace(&total, &contig);
|
||
|
||
// deal with emergency memory
|
||
if(total < kMinimumFreeMemory && g.emergencyMemory)
|
||
{
|
||
DisposeHandle(g.emergencyMemory); // release emergence memory
|
||
DisplayError("\pMemory is running low, please close some windows.");
|
||
}
|
||
else if(!g.emergencyMemory && contig > kEmergencyMemory) // try to recover handle if possible
|
||
g.emergencyMemory = NewHandleClear(kEmergencyMemory);
|
||
*/ return noErr;
|
||
}
|
||
|
||
#endif
|
||
|
||
/*** QUIT RES KNIFE ***/
|
||
void QuitResKnife(void)
|
||
{
|
||
// save all open files
|
||
WindowRef window = FrontNonFloatingWindow(), nextWindow;
|
||
while(window)
|
||
{
|
||
nextWindow = GetNextWindow(window);
|
||
SInt32 kind = GetWindowKind(window);
|
||
if(kind == kFileWindowKind)
|
||
{
|
||
#if TARGET_API_MAC_CARBON
|
||
EventRef event;
|
||
CreateEvent(null, kEventClassWindow, kEventWindowClose, kEventDurationNoWait, kEventAttributeNone, &event);
|
||
SendEventToWindow(event, window);
|
||
ReleaseEvent(event);
|
||
#else
|
||
// bug: this is totally the wrong thing to do here, but will have to do for now
|
||
DisposeWindow(window); // bug: windows don't close when sent a WindowClose event!
|
||
#endif
|
||
}
|
||
window = nextWindow;
|
||
}
|
||
|
||
if(!g.cancelQuit)
|
||
{
|
||
if(g.asyncSound) SHKillSoundHelper();
|
||
if(g.navAvailable) NavUnload();
|
||
#if TARGET_API_MAC_CARBON
|
||
QuitApplicationEventLoop();
|
||
#else
|
||
g.quitting = true;
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/*******************/
|
||
/* MENU SELECTIONS */
|
||
/*******************/
|
||
|
||
#if TARGET_API_MAC_CARBON
|
||
|
||
/*** CARBON EVENT UPDATE MENUS ***/
|
||
pascal OSStatus CarbonEventUpdateMenus(EventHandlerCallRef callRef, EventRef event, void *userData)
|
||
{
|
||
#pragma unused(callRef, event, userData)
|
||
OSStatus error = eventNotHandledErr;
|
||
Boolean fileOpen = (Boolean) (FrontNonFloatingWindow() != NULL);
|
||
|
||
// application menu (passing null causes all menus to be searched)
|
||
EnableCommand(null, kMenuCommandAbout, true);
|
||
EnableCommand(null, kHICommandPreferences, true);
|
||
|
||
// file menu
|
||
EnableCommand(null, kMenuCommandNewFile, true);
|
||
EnableCommand(null, kMenuCommandOpenFile, true);
|
||
EnableCommand(null, kMenuCommandCloseWindow, fileOpen);
|
||
EnableCommand(null, kMenuCommandCloseFile, fileOpen);
|
||
EnableCommand(null, kMenuCommandSaveFile, fileOpen); // bug: shoud be disabled if file is unmodified
|
||
EnableCommand(null, kMenuCommandSaveFileAs, fileOpen);
|
||
EnableCommand(null, kMenuCommandRevertFile, fileOpen);
|
||
EnableCommand(null, kMenuCommandPageSetup, fileOpen);
|
||
EnableCommand(null, kMenuCommandPrint, fileOpen);
|
||
|
||
/* if(fileOpen)
|
||
{
|
||
// edit window
|
||
EnableCommand(null, kHICommandUndo, false);
|
||
EnableCommand(null, kHICommandRedo, false);
|
||
EnableCommand(null, kHICommandCut, false);
|
||
EnableCommand(null, kHICommandCopy, false);
|
||
EnableCommand(null, kHICommandPaste, false);
|
||
EnableCommand(null, kHICommandClear, false);
|
||
EnableCommand(null, kHICommandSelectAll, false);
|
||
EnableCommand(null, kMenuCommandFind, false);
|
||
EnableCommand(null, kMenuCommandFindAgain, false);
|
||
|
||
// resource menu
|
||
EnableCommand(null, kMenuCommandNewResource, false);
|
||
EnableCommand(null, kMenuCommandOpenHex, false);
|
||
EnableCommand(null, kMenuCommandOpenDefault, false);
|
||
EnableCommand(null, kMenuCommandOpenTemplate, false);
|
||
EnableCommand(null, kMenuCommandOpenSpecific, false);
|
||
EnableCommand(null, kMenuCommandRevertResource, false);
|
||
EnableCommand(null, kMenuCommandPlaySound, false);
|
||
}
|
||
*/
|
||
// debug menu
|
||
EnableCommand(null, kMenuCommandDebug, true);
|
||
EnableCommand(null, kMenuCommandAppleEvents, false);
|
||
EnableCommand(null, kMenuCommandAppearance, false);
|
||
EnableCommand(null, kMenuCommandNavServices, false);
|
||
EnableCommand(null, kMenuCommandSheets, g.carbonVersion >= kCarbonLib11);
|
||
SetMenuCommandMark(null, kMenuCommandDebug, g.debug? kMenuCheckmarkGlyph : kMenuNullGlyph);
|
||
SetMenuCommandMark(null, kMenuCommandSurpressErrors, g.surpressErrors? kMenuCheckmarkGlyph : kMenuNullGlyph);
|
||
SetMenuCommandMark(null, kMenuCommandAppleEvents, g.useAppleEvents? kMenuCheckmarkGlyph : kMenuNullGlyph); // these three should always be true in Carbon
|
||
SetMenuCommandMark(null, kMenuCommandAppearance, g.useAppearance? kMenuCheckmarkGlyph : kMenuNullGlyph);
|
||
SetMenuCommandMark(null, kMenuCommandNavServices, g.useNavServices? kMenuCheckmarkGlyph : kMenuNullGlyph);
|
||
SetMenuCommandMark(null, kMenuCommandSheets, g.useSheets? kMenuCheckmarkGlyph : kMenuNullGlyph);
|
||
|
||
return error;
|
||
}
|
||
|
||
/*** CARBON EVENT PARSE MENU SELECTION ***/
|
||
pascal OSStatus CarbonEventParseMenuSelection(EventHandlerCallRef callRef, EventRef event, void *userData)
|
||
{
|
||
#pragma unused(callRef, userData)
|
||
|
||
// get menu command
|
||
HICommand menuCommand;
|
||
OSStatus error = GetEventParameter(event, kEventParamDirectObject, typeHICommand, null, sizeof(HICommand), null, &menuCommand);
|
||
if(error) return eventNotHandledErr;
|
||
|
||
switch(menuCommand.commandID)
|
||
{
|
||
// application menu
|
||
case kMenuCommandAbout:
|
||
ShowAboutBox();
|
||
break;
|
||
|
||
case kHICommandPreferences:
|
||
ShowPrefsWindow();
|
||
break;
|
||
|
||
// file menu
|
||
case kMenuCommandNewFile:
|
||
new FileWindow;
|
||
break;
|
||
|
||
case kMenuCommandOpenFile:
|
||
if(g.useSheets)
|
||
error = DisplayModelessGetFileDialog();
|
||
else error = DisplayOpenDialog();
|
||
break;
|
||
|
||
// debug menu
|
||
case kMenuCommandDebug:
|
||
g.debug = !g.debug;
|
||
break;
|
||
case kMenuCommandSurpressErrors:
|
||
g.surpressErrors = !g.surpressErrors;
|
||
break;
|
||
case kMenuCommandAppleEvents:
|
||
g.useAppleEvents = !g.useAppleEvents;
|
||
break;
|
||
case kMenuCommandAppearance:
|
||
g.useAppearance = !g.useAppearance;
|
||
break;
|
||
case kMenuCommandNavServices:
|
||
g.useNavServices = !g.useNavServices;
|
||
break;
|
||
case kMenuCommandSheets:
|
||
g.useSheets = !g.useSheets;
|
||
break;
|
||
|
||
default:
|
||
return eventNotHandledErr; // pass all other events
|
||
}
|
||
|
||
return error; // event handled here, so terminate.
|
||
}
|
||
|
||
/*** DEFAULT IDLE TIMER ***/
|
||
pascal void DefaultIdleTimer(EventLoopTimerRef timer, void *data)
|
||
{
|
||
#pragma unused(timer, data)
|
||
|
||
// idle controls (allow cursor blinking)
|
||
IdleControls(GetUserFocusWindow()); // bug: apple should have the control install it's own timer
|
||
|
||
// call sound idle routine
|
||
if(g.asyncSound && g.callSH) SHIdle();
|
||
}
|
||
|
||
#else
|
||
|
||
/*** UPDATE MENUS ***/
|
||
OSStatus UpdateMenus(WindowRef window)
|
||
{
|
||
#pragma unused(window)
|
||
OSStatus error = noErr;
|
||
|
||
// disable/checkmark items in the debug menu
|
||
MenuRef debugMenu = GetMenuRef(kDebugMenu);
|
||
MenuItemEnable(debugMenu, kDebugMenuAppearanceItem, g.appearanceAvailable);
|
||
MenuItemEnable(debugMenu, kDebugMenuNavServicesItem, g.navAvailable);
|
||
MenuItemEnable(debugMenu, kDebugMenuSheetsItem, false);
|
||
CheckMenuItem(debugMenu, kDebugMenuDebugItem, g.debug);
|
||
CheckMenuItem(debugMenu, kDebugMenuSurpressErrorsItem, g.surpressErrors);
|
||
CheckMenuItem(debugMenu, kDebugMenuAppleEventsItem, g.useAppleEvents);
|
||
CheckMenuItem(debugMenu, kDebugMenuAppearanceItem, g.useAppearance);
|
||
CheckMenuItem(debugMenu, kDebugMenuNavServicesItem, g.useNavServices);
|
||
|
||
return error;
|
||
}
|
||
|
||
/*** PARSE MENU SELECTION ***/
|
||
OSStatus ParseMenuSelection(UInt16 menu, UInt16 item)
|
||
{
|
||
// get the frontmost window, in all it's various forms
|
||
OSStatus error = eventNotHandledErr;
|
||
WindowRef window = FrontWindow();
|
||
WindowObjectPtr winObj = null;
|
||
FileWindowPtr file = null;
|
||
if(window)
|
||
{
|
||
winObj = (WindowObjectPtr) GetWindowRefCon(window);
|
||
if(GetWindowKind(window) == kFileWindowKind)
|
||
file = (FileWindowPtr) winObj;
|
||
}
|
||
|
||
// do the menu function
|
||
if(menu && item)
|
||
{
|
||
switch(menu)
|
||
{
|
||
case kAppleMenu:
|
||
switch(item)
|
||
{
|
||
case kAppleMenuAboutItem:
|
||
ShowAboutBox();
|
||
break;
|
||
|
||
default: // something from "apple menu items" folder
|
||
Str255 itemName;
|
||
MenuHandle appleMenu = GetMenuHandle(kAppleMenu);
|
||
GetMenuItemText(appleMenu, item, itemName);
|
||
OpenDeskAcc(itemName);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case kFileMenu:
|
||
switch(item)
|
||
{
|
||
case kFileMenuNewFileItem:
|
||
new FileWindow;
|
||
break;
|
||
|
||
case kFileMenuOpenFileItem:
|
||
if(g.useNavServices)
|
||
DisplayOpenDialog();
|
||
else DisplayStandardFileOpenDialog();
|
||
break;
|
||
|
||
case kFileMenuCloseWindowItem:
|
||
if(winObj) winObj->Close();
|
||
break;
|
||
|
||
case kFileMenuQuitItem:
|
||
AEDescList list = {};
|
||
error = AppleEventSendSelf(kCoreEventClass, kAEQuitApplication, list);
|
||
if(error) QuitResKnife();
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case kEditMenu:
|
||
/* switch(item)
|
||
{
|
||
case :
|
||
break;
|
||
}
|
||
*/ break;
|
||
|
||
case kResourceMenu:
|
||
switch(item)
|
||
{
|
||
case kResourceMenuNewResource:
|
||
if(file) file->DisplayNewResourceDialog();
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case kDebugMenu:
|
||
switch(item)
|
||
{
|
||
case kDebugMenuDebugItem:
|
||
g.debug = !g.debug;
|
||
break;
|
||
|
||
case kDebugMenuSurpressErrorsItem:
|
||
g.surpressErrors = !g.surpressErrors;
|
||
break;
|
||
|
||
case kDebugMenuAppleEventsItem:
|
||
g.useAppleEvents = !g.useAppleEvents;
|
||
break;
|
||
|
||
case kDebugMenuAppearanceItem:
|
||
g.useAppearance = !g.useAppearance;
|
||
break;
|
||
|
||
case kDebugMenuNavServicesItem:
|
||
g.useNavServices = !g.useNavServices;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
error = UpdateMenus(FrontWindow());
|
||
}
|
||
return error;
|
||
}
|
||
|
||
#endif
|
||
|
||
/****************/
|
||
/* APPLE EVENTS */
|
||
/****************/
|
||
|
||
/*** SEND MYSELF AN APPLE EVENT ***/
|
||
OSStatus AppleEventSendSelf(DescType eventClass, DescType eventID, AEDescList list)
|
||
{
|
||
OSStatus error;
|
||
AEAddressDesc myAddress; // all of these are really of type AEDesc
|
||
AppleEvent noReply;
|
||
AppleEvent event;
|
||
|
||
myAddress.descriptorType = typeNull;
|
||
myAddress.dataHandle = null;
|
||
noReply.descriptorType = typeNull;
|
||
noReply.dataHandle = null;
|
||
|
||
ProcessSerialNumber psn;
|
||
error = GetCurrentProcess(&psn);
|
||
if(error) return error;
|
||
|
||
error = AECreateDesc(typeProcessSerialNumber, &psn, sizeof(ProcessSerialNumber), &myAddress);
|
||
if(error) return error;
|
||
|
||
error = AECreateAppleEvent(eventClass, eventID, &myAddress, kAutoGenerateReturnID, kAnyTransactionID, &event);
|
||
if(error) return error;
|
||
|
||
error = AEPutParamDesc(&event, keyDirectObject, &list);
|
||
if(error) return error;
|
||
|
||
error = AESend(&event, &noReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, null, null);
|
||
return error;
|
||
}
|
||
|
||
/*** GOT REQUIRED PARAMS ***/
|
||
Boolean GotRequiredParams(const AppleEvent *event)
|
||
{
|
||
OSStatus error = noErr;
|
||
DescType actualType;
|
||
Size actualSize;
|
||
|
||
// check if we have retrieved the required parameters
|
||
error = AEGetAttributePtr(event, keyMissedKeywordAttr, typeWildCard, &actualType, null, 0, &actualSize);
|
||
return error == errAEDescNotFound;
|
||
}
|
||
|
||
/*** OPEN ***/
|
||
OSStatus AppleEventOpen(const AppleEvent *event)
|
||
{
|
||
OSStatus error = noErr;
|
||
|
||
// get the list of objects to open
|
||
AEDescList list;
|
||
AEKeyword aeKeyword;
|
||
aeKeyword = keyDirectObject;
|
||
error = AEGetParamDesc(event, aeKeyword, typeAEList, &list);
|
||
if(!GotRequiredParams(event) || error) return errAEEventNotHandled;
|
||
|
||
// count how many we have
|
||
SInt32 itemCount;
|
||
error = AECountItems(&list, &itemCount);
|
||
if(error) return errAEEventNotHandled;
|
||
|
||
// open each one
|
||
FSSpec fileSpec;
|
||
DescType actualType;
|
||
Size actualSize;
|
||
for(SInt32 n = 1; n <= itemCount; n++)
|
||
{
|
||
error = AEGetNthPtr(&list, n, typeFSS, &aeKeyword, &actualType, (Ptr) &fileSpec, sizeof(FSSpec), &actualSize);
|
||
if(actualType == typeFSS && !error) new FileWindow(&fileSpec);
|
||
}
|
||
|
||
AEDisposeDesc(&list);
|
||
return error;
|
||
}
|
||
|
||
/*** PRINT ***/
|
||
OSStatus AppleEventPrint(const AppleEvent *event)
|
||
{
|
||
#pragma unused(event)
|
||
return errAEEventNotHandled;
|
||
}
|
||
|
||
/************************/
|
||
/* NIB WINDOW MANAGMENT */
|
||
/************************/
|
||
|
||
/*** SHOW ABOUT BOX ***/
|
||
OSStatus ShowAboutBox(void)
|
||
{
|
||
#if TARGET_API_MAC_CARBON
|
||
#if USE_NIBS
|
||
// create a nib reference (only searches the application bundle)
|
||
IBNibRef nibRef = null;
|
||
OSStatus error = CreateNibReference(CFSTR("ResKnife"), &nibRef);
|
||
if(error != noErr || nibRef == null)
|
||
{
|
||
DisplayError("\pThe nib file reference could not be obtained.");
|
||
return error;
|
||
}
|
||
|
||
// create window
|
||
WindowRef window;
|
||
error = CreateWindowFromNib(nibRef, CFSTR("About Box"), &window);
|
||
if(error != noErr || window == null)
|
||
{
|
||
DisplayError("\pThe about box could not be obtained from the nib file.");
|
||
return error;
|
||
}
|
||
|
||
// dispose of nib ref
|
||
DisposeNibReference(nibRef);
|
||
|
||
// show window
|
||
ShowWindow(window);
|
||
#else
|
||
Rect creationBounds;
|
||
WindowRef window;
|
||
SetRect(&creationBounds, 0, 0, 300, 300);
|
||
OffsetRect(&creationBounds, 50, 50);
|
||
OSStatus error = CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowInWindowMenuAttribute, &creationBounds, &window);
|
||
if(error) return error;
|
||
|
||
ControlRef picControl;
|
||
ControlButtonContentInfo content;
|
||
/* content.contentType = kControlContentPictHandle;
|
||
content.u.picture = GetPicture(128);
|
||
if(content.u.picture == null) DebugStr("\ppicture == null");
|
||
*/ content.contentType = kControlContentPictRes;
|
||
content.u.resID = 128;
|
||
CreatePictureControl(window, &creationBounds, &content, true, &picControl);
|
||
// SetControlData(picControl, kControlPicturePart, kControlPictureHandleTag, sizeof(content.u.picture), content.u.picture);
|
||
#endif
|
||
#else
|
||
WindowRef window = null;
|
||
if(g.useAppearance && g.systemVersion >= kMacOS8)
|
||
window = GetNewCWindow(kFileWindow8, null, kFirstWindowOfClass);
|
||
else window = GetNewCWindow(kFileWindow7, null, kFirstWindowOfClass);
|
||
if(window == null) return paramErr;
|
||
PicHandle picture = (PicHandle) GetPicture(128);
|
||
SetWindowPic(window, picture);
|
||
#endif
|
||
|
||
ShowWindow(window);
|
||
return noErr;
|
||
}
|
||
|
||
/*** SHOW PREFERENCES WINDOW ***/
|
||
OSStatus ShowPrefsWindow(void)
|
||
{
|
||
#if USE_NIBS
|
||
// create a nib reference (only searches the application bundle)
|
||
IBNibRef nibRef = null;
|
||
OSStatus error = CreateNibReference(CFSTR("ResKnife"), &nibRef);
|
||
if(error != noErr || nibRef == null)
|
||
{
|
||
DisplayError("\pThe nib file reference could not be obtained.");
|
||
return error;
|
||
}
|
||
|
||
// create window
|
||
WindowRef window;
|
||
error = CreateWindowFromNib(nibRef, CFSTR("Preferences"), &window);
|
||
if(error != noErr || window == null)
|
||
{
|
||
DisplayError("\pThe preferences window could not be obtained from the nib file.");
|
||
return error;
|
||
}
|
||
|
||
// dispose of nib ref
|
||
DisposeNibReference(nibRef);
|
||
|
||
// install tabs handler
|
||
ControlRef control;
|
||
ControlID id = { 'tabs', 1 };
|
||
GetControlByID(window, &id, &control);
|
||
EventTypeSpec event = { kEventClassControl, kEventControlHit };
|
||
InstallEventHandler(GetControlEventTarget(control), NewEventHandlerUPP(PrefsTabEventHandler), 1, &event, &control, null);
|
||
|
||
// show window
|
||
ShowWindow(window);
|
||
#endif
|
||
return noErr;
|
||
}
|
||
|
||
/*** PREFS TAB EVENT HANDLER ***/
|
||
pascal OSStatus PrefsTabEventHandler(EventHandlerCallRef handlerRef, EventRef event, void* userData)
|
||
{
|
||
#pragma unused(handlerRef, event)
|
||
|
||
// get tabs control
|
||
OSStatus error = noErr;
|
||
ControlRef control = (ControlRef) userData;
|
||
SInt16 selectedTab = GetControlValue(control);
|
||
WindowRef window = GetControlOwner(control);
|
||
|
||
// select correct tab
|
||
ControlRef currentTab;
|
||
ControlID id = { 'pane', 1 };
|
||
GetControlByID(window, &id, ¤tTab);
|
||
SetControlVisibility(currentTab, selectedTab == id.id, true);
|
||
id.id = 2;
|
||
GetControlByID(window, &id, ¤tTab);
|
||
SetControlVisibility(currentTab, selectedTab == id.id, true);
|
||
|
||
return error;
|
||
}
|