mirror of
https://github.com/nickshanks/ResKnife.git
synced 2025-01-20 09:31:15 +00:00
1139 lines
32 KiB
C++
1139 lines
32 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 retreived 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;
|
||
}
|