mirror of
https://github.com/aaronsgiles/JPEGView.git
synced 2024-06-06 21:29:27 +00:00
92bdb55672
These are the sources for the final official release of JPEGView for the Mac, back in 1994.
1 line
15 KiB
C
1 line
15 KiB
C
/*********************************************************/
|
|
/* This source code copyright (c) 1991-2001, Aaron Giles */
|
|
/* See the Read Me file for licensing information. */
|
|
/* Contact email: mac@aarongiles.com */
|
|
/*********************************************************/
|
|
|
|
#if THINK_C
|
|
#include "THINK.Header"
|
|
#elif applec
|
|
#pragma load ":Headers:MPW.Header"
|
|
#elif __MWERKS__
|
|
//#include "MW.Header"
|
|
#else
|
|
#include "JPEGView.h"
|
|
#endif
|
|
|
|
/*
|
|
* Local variables:
|
|
* lTimeInstalled = count of the number of times the task record has been installed
|
|
* lTimeTask = Time Manager task record for calculating elapsed times
|
|
*
|
|
*/
|
|
|
|
static short gTimeInstalled = 0;
|
|
static long gStartTicks;
|
|
|
|
static Boolean HandleAbortMouseDown(EventRecord *theEvent);
|
|
static Boolean HandleAbortKeyDown(EventRecord *theEvent);
|
|
|
|
/*
|
|
* CheckAbort()
|
|
*
|
|
* Purpose: Scans the event queue for an aborting keypress
|
|
* Inputs: none
|
|
* Returns: true if we should abort
|
|
*
|
|
*/
|
|
|
|
Boolean CheckAbort(JVDrawParamsHandle theParams)
|
|
{
|
|
static long gLastTime = 0;
|
|
static short gCount = 0;
|
|
Boolean aborted = false, QDxInstalled;
|
|
ImageHandle theImage;
|
|
EventRecord theEvent;
|
|
EvQElPtr eventPtr;
|
|
short sleepTime;
|
|
Point newPoint;
|
|
|
|
// get the proper sleep time: according to preferences if in foreground, or the
|
|
// maximum if we're in the background or running as a screen saver
|
|
if (gInBackground || gScreenSaver ||
|
|
gThePrefs.backgroundTime == bgMaximal) sleepTime = kSleepTime;
|
|
else if (gThePrefs.backgroundTime == bgMinimal) sleepTime = 0;
|
|
else sleepTime = 3;
|
|
|
|
// if we're in screen saver mode, make sure we're frontmost
|
|
if (gScreenSaver) EnsureFrontmost();
|
|
|
|
// if we're not in the background, ensure that we get at least 2 ticks of continuous
|
|
// operation before giving up a full amount of time to others
|
|
if (!gInBackground && (TickCount() - gLastTime) < 2) sleepTime = 0;
|
|
PushPort();
|
|
MySetPort((CGrafPtr)FrontWindow());
|
|
QDxInstalled = GetQDxDispatchPatchState();
|
|
|
|
// get the next event; if sleepTime is zero, check event availability first
|
|
if (sleepTime || EventAvail(everyEvent, &theEvent)) {
|
|
|
|
// if we're in screen saver mode, and the mouse moved, quit immediately
|
|
newPoint = GlobalMouse();
|
|
if (gSlideShow && gScreenSaver &&
|
|
(abs(newPoint.h - gScreenSaverPoint.h) > 2 ||
|
|
abs(newPoint.v - gScreenSaverPoint.v) > 2)) {
|
|
SendQuitApplication();
|
|
aborted = true;
|
|
}
|
|
|
|
// otherwise, handle any pending events normally
|
|
else while (WaitNextEvent(everyEvent - highLevelEventMask, &theEvent, sleepTime, nil)) {
|
|
|
|
// dispatch the event as appropriate
|
|
switch (theEvent.what) {
|
|
case mouseDown:
|
|
aborted = HandleAbortMouseDown(&theEvent);
|
|
break;
|
|
case keyDown:
|
|
if (theEvent.message == kCreator) break;
|
|
case autoKey:
|
|
aborted = HandleAbortKeyDown(&theEvent);
|
|
break;
|
|
case updateEvt:
|
|
if (theImage = FindImage((WindowPtr)theEvent.message)) {
|
|
if (gDrawing && theImage == gDrawing &&
|
|
theParams && (*theParams)->on.port) {
|
|
aborted = true;
|
|
MySetPort((CGrafPtr)(*theImage)->window);
|
|
BeginUpdate((*theImage)->window);
|
|
UnionRgn((*theImage)->update, (*theImage)->window->visRgn, (*theImage)->update);
|
|
EndUpdate((*theImage)->window);
|
|
(*theImage)->flags |= ifNeedToRedraw;
|
|
} else DeferImageUpdate(theImage);
|
|
} else HandleUpdateEvent(&theEvent);
|
|
break;
|
|
case activateEvt:
|
|
HandleActivateEvent(&theEvent);
|
|
break;
|
|
case osEvt:
|
|
HandleOSEvent(&theEvent);
|
|
break;
|
|
}
|
|
StartSpinning();
|
|
|
|
// if we got a pending button, abort (unless it's during offscreen draw of
|
|
// user-controlled slide show)
|
|
if (gPendingButton != -1)
|
|
if (gPendingButton != kForwardButton ||
|
|
(theParams && (*theParams)->on.port))
|
|
aborted = true;
|
|
}
|
|
} else {
|
|
if (!(gCount = (gCount + 1) & 0x1f) && !gInBackground)
|
|
PPostEvent(keyDown, kCreator, &eventPtr);
|
|
}
|
|
SetQDxDispatchPatchState(QDxInstalled);
|
|
PopPort();
|
|
if (sleepTime) gLastTime = TickCount();
|
|
return aborted;
|
|
}
|
|
|
|
static Boolean HandleAbortMouseDown(EventRecord *theEvent)
|
|
{
|
|
Boolean aborted = false;
|
|
|
|
// if we're in screen saver mode, always abort
|
|
if (gSlideShow && gScreenSaver) {
|
|
SendQuitApplication();
|
|
return true;
|
|
}
|
|
|
|
// handle dialog events properly; if button 3 was hit on the progress dialog,
|
|
// we abort the drawing
|
|
if (IsDialogEvent(theEvent)) {
|
|
DialogPtr theDialog;
|
|
short itemHit;
|
|
|
|
StopSpinning(&qd.arrow);
|
|
if (DialogSelect(theEvent, &theDialog, &itemHit) &&
|
|
GetWRefCon(theDialog) == kProgressDialogID &&
|
|
itemHit == progressCancel) aborted = true;
|
|
|
|
// otherwise, do it the hard way....
|
|
} else {
|
|
WindowPtr theWindow;
|
|
short thePart = FindWindow(theEvent->where, &theWindow);
|
|
|
|
// if a slide show is happening and the mouse was clicked in a
|
|
// non-floating window, toggle the visible state of the slide show controls
|
|
if (gSlideShow && WindowKind(theWindow) != floatingWindowKind) {
|
|
theWindow = GetSlideControlsWindow();
|
|
if (!theWindow) {
|
|
if (OpenSlideControls() == noErr)
|
|
ChangeActive(GetSlideControlsWindow());
|
|
} else CloseSlideControls();
|
|
|
|
// otherwise, track dragging and menus as normal; check specially for clicks in
|
|
// the slide show controls floating window, and beep about everything else
|
|
} else {
|
|
switch (thePart) {
|
|
case inDrag:
|
|
StopSpinning(&qd.arrow);
|
|
HandleDragClick(theEvent, theWindow);
|
|
break;
|
|
case inMenuBar:
|
|
StopSpinning(&qd.arrow);
|
|
if (gMenuVisible) HandleMenuClick(theEvent);
|
|
break;
|
|
case inGrow:
|
|
case inContent:
|
|
if (theWindow == GetSlideControlsWindow()) {
|
|
StopSpinning(&qd.arrow);
|
|
HandleSlideControlsClick(theEvent->where, theEvent->modifiers, false);
|
|
break;
|
|
}
|
|
default:
|
|
SysBeep(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return aborted;
|
|
}
|
|
|
|
static Boolean HandleAbortKeyDown(EventRecord *theEvent)
|
|
{
|
|
uchar theChar = theEvent->message & charCodeMask;
|
|
Boolean aborted = false;
|
|
DialogPtr theDialog;
|
|
|
|
for (theDialog = FWFrontWindow(); theDialog; theDialog = NextWindow(theDialog))
|
|
if (GetWRefCon(theDialog) == kProgressDialogID) break;
|
|
|
|
// if we're in screen saver mode, always abort
|
|
if (gSlideShow && gScreenSaver) {
|
|
SendQuitApplication();
|
|
return true;
|
|
}
|
|
|
|
// for cmd-0, cmd-`, and cmd-\, dispatch to the standard menu handler;
|
|
// if cmd-. set the proper abort flag
|
|
if ((theEvent->modifiers & cmdKey) && theChar != '.') {
|
|
if (theChar == '0' || theChar == '`' || theChar == '\\' ||
|
|
theChar == 'Q' || theChar == 'q') {
|
|
HandleMenuChoice(PowerMenuKey(theEvent->message, theEvent->modifiers,
|
|
GetMHandle(rFileMenu)));
|
|
if (gDone) aborted = true;
|
|
}
|
|
}
|
|
|
|
// if escape, set the proper abort flag
|
|
else if (((theEvent->modifiers & cmdKey) && theChar == '.') || theChar == kEscapeChar) {
|
|
|
|
// if the we're in a slide show & the stop button is disabled, simply abort
|
|
if (gSlideShow == kPaused && GetSlideControlsWindow() &&
|
|
GetSlideControlButton(kStopButton) == disabled) aborted = 0x4d;
|
|
else if (gSlideShow) HandleSlideControlsKey(theChar, theEvent->modifiers, false);
|
|
else aborted = 0x4d;
|
|
|
|
// if we're in a slide show, pass the key through the controls handler
|
|
} else if (gSlideShow) HandleSlideControlsKey(theChar, theEvent->modifiers, false);
|
|
|
|
if (aborted == 0x4d && theDialog) {
|
|
PushPort();
|
|
MySetPort((CGrafPtr)theDialog);
|
|
HandleDialogCancelKey(theDialog);
|
|
aborted = true;
|
|
PopPort();
|
|
}
|
|
return aborted;
|
|
}
|
|
|
|
/*
|
|
* StartTimer()
|
|
*
|
|
* Purpose: Starts a Time Manager task to calculate an elapsed time
|
|
* Inputs: none
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
void StartTimer(void)
|
|
{
|
|
if (++gTimeInstalled != 1) return;
|
|
gStartTicks = TickCount();
|
|
}
|
|
|
|
/*
|
|
* StopTimer()
|
|
*
|
|
* Purpose: Stops the Time Manager task to calculate an elapsed time
|
|
* Inputs: none
|
|
* Returns: the elapsed time, in milliseconds
|
|
*
|
|
*/
|
|
|
|
long StopTimer(void)
|
|
{
|
|
if (--gTimeInstalled) return 0L;
|
|
return ((TickCount() - gStartTicks) * 1000L / 60L);
|
|
}
|
|
|
|
/*
|
|
* DirName(dirID, vRefNum, s)
|
|
*
|
|
* Purpose: Returns the name of the directory given by the dirID and vRefNum
|
|
* Inputs: dirID = the directory ID of the directory
|
|
* vRefNum = the vRef of the directory
|
|
* s = a pointer to a string to hold the final name
|
|
* Returns: s
|
|
*
|
|
*/
|
|
|
|
char *DirName(long dirID, short vRefNum, char *s)
|
|
{
|
|
Str255 directoryName;
|
|
CInfoPBRec block;
|
|
|
|
*s = 0;
|
|
block.dirInfo.ioNamePtr = directoryName;
|
|
block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID = dirID;
|
|
block.dirInfo.ioVRefNum = vRefNum;
|
|
block.dirInfo.ioFDirIndex = -1;
|
|
if (PBGetCatInfo(&block, false) == noErr) {
|
|
if (directoryName[0]) BlockMove(directoryName, s, directoryName[0] + 1);
|
|
else s[0] = 0;
|
|
} else s[0] = 0;
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* GetDirectoryID(dirID, vRefNum)
|
|
*
|
|
* Purpose: Gets the dirID of the given directory
|
|
* Inputs: theSpec = pointer to the directory spec
|
|
* theID = pointer to a long to store the ID
|
|
* Returns: an OSErr describing what went wrong
|
|
*
|
|
*/
|
|
|
|
OSErr GetDirectoryID(FSSpec *theSpec, long *theID)
|
|
{
|
|
CInfoPBRec block;
|
|
Str255 fileName;
|
|
OSErr theErr;
|
|
|
|
block.hFileInfo.ioNamePtr = fileName;
|
|
BlockMove(theSpec->name, fileName, *theSpec->name + 1);
|
|
block.hFileInfo.ioVRefNum = theSpec->vRefNum;
|
|
block.hFileInfo.ioFDirIndex = 0;
|
|
block.hFileInfo.ioDirID = theSpec->parID;
|
|
theErr = PBGetCatInfo(&block, false);
|
|
if (theErr != noErr) return theErr;
|
|
if (!(block.hFileInfo.ioFlAttrib & 0x10)) return errNotADirectory;
|
|
*theID = block.dirInfo.ioDrDirID;
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* GetFullPath(theSpec, thePath)
|
|
*
|
|
* Purpose: Gets the full pathname from a given FSSpec
|
|
* Inputs: theSpec = pointer to the directory spec
|
|
* thePath = pointer to a handle to receive the path
|
|
* Returns: an OSErr describing what went wrong
|
|
*
|
|
*/
|
|
|
|
OSErr GetFullPath(FSSpec *theSpec, Handle *thePath)
|
|
{
|
|
OSErr theErr = noErr;
|
|
Boolean isDirectory;
|
|
long size, tempID;
|
|
Str255 dirName;
|
|
DirInfo block;
|
|
|
|
theErr = GetDirectoryID(theSpec, &tempID);
|
|
isDirectory = (theErr == noErr);
|
|
*thePath = NewHandle(size = *theSpec->name);
|
|
if (*thePath) {
|
|
BlockMove(&theSpec->name[1], **thePath, *theSpec->name);
|
|
block.ioDrParID = theSpec->parID;
|
|
block.ioNamePtr = dirName;
|
|
do {
|
|
block.ioVRefNum = theSpec->vRefNum;
|
|
block.ioFDirIndex = -1;
|
|
block.ioDrDirID = block.ioDrParID;
|
|
theErr = PBGetCatInfo((CInfoPBPtr)&block, false);
|
|
if (theErr != noErr) {
|
|
if (isDirectory) return noErr;
|
|
break;
|
|
}
|
|
SetHandleSize(*thePath, size + 1 + *dirName);
|
|
theErr = MemError();
|
|
if (theErr != noErr) break;
|
|
BlockMove(**thePath, **thePath + *dirName + 1, size);
|
|
size += 1 + *dirName;
|
|
BlockMove(&dirName[1], **thePath, *dirName);
|
|
*(**thePath + *dirName) = ':';
|
|
} while (block.ioDrDirID != 2);
|
|
} else theErr = memFullErr;
|
|
if ((theErr != noErr) && *thePath) {
|
|
DisposeHandle(*thePath);
|
|
*thePath = nil;
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
/*
|
|
* ForceFolderUpdate(dirID, vRefNum)
|
|
*
|
|
* Purpose: Forces the Finder to update the directory information of the given folder
|
|
* Inputs: dirID = the directory ID of the directory
|
|
* vRefNum = the vRef of the directory
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
void ForceFolderUpdate(long dirID, short vRefNum)
|
|
{
|
|
CInfoPBRec block;
|
|
|
|
block.hFileInfo.ioCompletion = nil;
|
|
block.hFileInfo.ioNamePtr = nil;
|
|
block.hFileInfo.ioVRefNum = vRefNum;
|
|
block.hFileInfo.ioDirID = dirID;
|
|
block.hFileInfo.ioFDirIndex = -1;
|
|
PBGetCatInfo(&block, false);
|
|
GetDateTime(&block.dirInfo.ioDrMdDat);
|
|
PBSetCatInfo(&block, false);
|
|
}
|
|
|
|
/*
|
|
* AddString(theString, theAddition)
|
|
*
|
|
* Purpose: Appends theAddition, a pascal string, to the pascal string theString
|
|
* Inputs: theString = the destination string in pascal format
|
|
* theAddition = the text of the addition
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
void AddString(StringPtr theString, StringPtr theAddition)
|
|
{
|
|
BlockMove((uchar *)theAddition + 1, theString + theString[0] + 1, *theAddition);
|
|
*theString += *theAddition;
|
|
}
|
|
|
|
/*
|
|
* AddCString(theString, theAddition, length)
|
|
*
|
|
* Purpose: Appends theAddition, a C string of length length, to the pascal string
|
|
* theString
|
|
* Inputs: theString = the destination string in pascal format
|
|
* theAddition = the text of the addition
|
|
* length = the length of the addition
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
void AddCString(StringPtr theString, char *theAddition, int length)
|
|
{
|
|
BlockMove(theAddition, theString + theString[0] + 1, length);
|
|
*theString += length;
|
|
}
|
|
|
|
/*
|
|
* AddNumber(theString, theNum)
|
|
*
|
|
* Purpose: Appends an ASCII representation of theNum to the pascal string theString
|
|
* Inputs: theString = the destination string in pascal format
|
|
* theNum = the number to append
|
|
* Returns: nothing
|
|
*
|
|
*/
|
|
|
|
void AddNumber(StringPtr theString, long theNum)
|
|
{
|
|
Str255 tempNum, theAddition;
|
|
int i;
|
|
|
|
NumToString(theNum, tempNum);
|
|
*theAddition = 0;
|
|
for (i = 1; i <= *tempNum; i++) {
|
|
AddChar(theAddition, tempNum[i]);
|
|
if ((*tempNum > 4) && (*tempNum != i) && (((*tempNum - i) % 3) == 0))
|
|
AddChar(theAddition, gThousandsSep);
|
|
}
|
|
AddString(theString, theAddition);
|
|
}
|
|
|
|
void StuffNumber(StringPtr theString, short theItem, long theNum)
|
|
{
|
|
Str32 tempNum, fixedNum;
|
|
StringPtr src, dst;
|
|
short j;
|
|
|
|
NumToString(theNum, tempNum);
|
|
src = &tempNum[1];
|
|
dst = &fixedNum[1];
|
|
*fixedNum = *tempNum;
|
|
for (j = 1; j <= *tempNum; j++) {
|
|
*dst++ = *src++;
|
|
if ((*tempNum > 4) && (*tempNum != j) && (((*tempNum - j) % 3) == 0))
|
|
*dst++ = gThousandsSep, (*fixedNum)++;
|
|
}
|
|
StuffString(theString, theItem, fixedNum);
|
|
}
|
|
|
|
void StuffNumber0(StringPtr theString, short theItem, long theNum, short theLength)
|
|
{
|
|
Str32 tempNum;
|
|
|
|
NumToString(theNum, tempNum);
|
|
while (*tempNum < theLength) {
|
|
BlockMove(&tempNum[1], &tempNum[2], 30);
|
|
tempNum[1] = '0';
|
|
(*tempNum)++;
|
|
}
|
|
StuffString(theString, theItem, tempNum);
|
|
}
|
|
|
|
void StuffDepth(StringPtr theString, short theItem, short theDepth)
|
|
{
|
|
Str32 tempNum;
|
|
|
|
if (theDepth > 32) theDepth -= 32;
|
|
if (theDepth <= 8) NumToString(1L << theDepth, tempNum);
|
|
else if (theDepth == 16)
|
|
BlockMove(gString[strThousands], tempNum, *gString[strThousands] + 1);
|
|
else BlockMove(gString[strMillions], tempNum, *gString[strMillions] + 1);
|
|
StuffString(theString, theItem, tempNum);
|
|
}
|
|
|
|
void StuffString(StringPtr theString, short theItem, StringPtr addString)
|
|
{
|
|
Str255 tempStr;
|
|
StringPtr src = &theString[1], dst = &tempStr[1], num = &addString[1];
|
|
short i, j, len = *theString;
|
|
|
|
theItem += '0';
|
|
for (i = 0; i < len; i++) {
|
|
if ((*src == '~') && (*(src+1) == theItem)) break;
|
|
else *dst++ = *src++;
|
|
}
|
|
if (*src != '~') return;
|
|
num = &addString[1];
|
|
for (j = 0; j < *addString; j++) *dst++ = *num++;
|
|
src += 2, i += 2;
|
|
for ( ; i < len; i++) *dst++ = *src++;
|
|
*tempStr = len + *addString - 2;
|
|
BlockMove(tempStr, theString, *tempStr + 1);
|
|
}
|
|
|
|
/*
|
|
* GlobalMouse()
|
|
*
|
|
* Purpose: Gets the global coordinates of the mouse
|
|
* Inputs: none
|
|
* Returns: the mouse location in global coordinates
|
|
*
|
|
*/
|
|
|
|
Point GlobalMouse(void)
|
|
{
|
|
Point thePoint;
|
|
|
|
PushPort();
|
|
MySetPort(nil);
|
|
GetMouse(&thePoint);
|
|
LocalToGlobal(&thePoint);
|
|
PopPort();
|
|
return thePoint;
|
|
}
|