JPEGView/Source/C/ProgressProcs.c
Aaron Giles 92bdb55672 JPEGView 3.3 for Macintosh
These are the sources for the final official release of JPEGView for the
Mac, back in 1994.
2015-02-05 00:18:10 -08:00

1 line
22 KiB
C

/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information. */
/* Contact email: mac@aarongiles.com */
/*********************************************************/
//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================
#if THINK_C
#include "THINK.Header"
#include <stddef.h>
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif
//=====================================================================================
// Includes specific to this module
//=====================================================================================
#include "ProgressProcs.h"
//=====================================================================================
// Global variables local to this module
//=====================================================================================
static UserItemUPP gUpdateBar = nil, gUpdateBarIndef;
static Fixed gProgressPercent;
//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================
static pascal void UpdateBar(DialogPtr theDialog, short theItem);
static pascal void UpdateBarIndef(DialogPtr theDialog, short theItem);
static void InitDrawProgress(Point *thePoint, JVDrawParamsHandle theParams);
static void MakeDrawProgressText(short theMessage, StringPtr theNum, Fixed thePercent);
//=====================================================================================
// pascal OSErr DummyProgress(short theMessage, Fixed thePercent, long refCon)
//=====================================================================================
// Dummy progress proc; goes through all the motions, but is just used to make sure we
// give up time to other applications while providing no real feedback to the user.
//=====================================================================================
extern pascal OSErr DummyProgress(short theMessage, Fixed thePercent, long refCon)
{
#if applec
#pragma unused(thePercent)
#endif
NestedProgressPtr theProgress = nil;
JVDrawParamsHandle theParams = nil;
OSErr theErr = noErr;
char hState;
// figure out of we've got a handle to drawing parameters, or just a pointer
// to a progress proc as the parameter; if we have a handle, lock it and
// extract the pointer to the progress proc
if (((NestedProgressPtr)refCon)->prog.progressProc == gDummyProg.prog.progressProc)
theProgress = (NestedProgressPtr)refCon;
else {
theParams = (JVDrawParamsHandle)refCon;
hState = HGetState((Handle)theParams);
HLock((Handle)theParams);
theProgress = &(*theParams)->progress;
}
// keep the cursor spinning, and track the various nesting levels as necessary
// when open and close messages are received
KeepSpinning();
switch (theMessage) {
case codecProgressOpen:
if (++theProgress->level <= 1) {
theProgress->aborted = false;
}
break;
case codecProgressClose:
if (--theProgress->level > 0) break;
break;
}
// check for aborts
if (theProgress->aborted || (theProgress->aborted = CheckAbort(theParams)))
theErr = codecAbortErr;
// restore the drawing parameters handle to its original state and exit
if (theParams) HSetState((Handle)theParams, hState);
return theErr;
}
//=====================================================================================
// pascal OSErr GenericProgress(short theMessage, Fixed thePercent, long refCon)
//=====================================================================================
// Generic progress procedure, which displays a dialog with a progress bar and a cancel
// button. When calling with the open message, the thePercent parameter specifies
// special options. If thePercent equals kModalProgress, then the dialog displayed is
// a modal dialog, centered on the screen, rather than the usual movable modal. If
// thePercent is set to kIndefProgress, then the percentage parameter is ignored in the
// future, and the famous Finder "barber shop" progress bar is displayed instead. This
// function can be called as often as desired; the display is never updated more than
// 3 times/second.
//=====================================================================================
extern pascal OSErr GenericProgress(short theMessage, Fixed thePercent, long refCon)
{
static DialogPtr gTheDialog = nil;
static long gLastTime;
NestedProgressPtr theProgress = nil;
JVDrawParamsHandle theParams = nil;
OSErr theErr = noErr;
char hState;
long ticks;
// figure out of we've got a handle to drawing parameters, or just a pointer
// to a progress proc as the parameter; if we have a handle, lock it and
// extract the pointer to the progress proc
if (((NestedProgressPtr)refCon)->prog.progressProc == gGenericProgress)
theProgress = (NestedProgressPtr)refCon;
else {
theParams = (JVDrawParamsHandle)refCon;
hState = HGetState((Handle)theParams);
HLock((Handle)theParams);
theProgress = &(*theParams)->progress;
}
// save the port now, and calculate the scaled percentage
KeepSpinning();
PushPort();
switch (theMessage) {
case codecProgressOpen:
// open the dialog if this is the first call
if (++theProgress->level <= 1) {
theProgress->aborted = false;
MySetPort(nil);
if (!(gTheDialog = AllocateDialog())) break;
// we use the modal dialog if thePercent is set to kModalProgress
if (gTheDialog = FWGetNewDialog((long)thePercent == kModalProgress ?
rModalProgressDialog : rProgressDialog, nil, (WindowPtr)-1)) {
MySetPort((CGrafPtr)gTheDialog);
// set up the user item handler, either for indefinite progress
// (thePercent == kIndefProgress) or for standard progress
if (!gUpdateBar) {
gUpdateBar = NewUserItemProc((ProcPtr)UpdateBar);
gUpdateBarIndef = NewUserItemProc((ProcPtr)UpdateBarIndef);
}
SetItemHandle(gTheDialog, progressUser,
(long)thePercent == kIndefProgress ?
(Handle)gUpdateBarIndef : (Handle)gUpdateBar);
gProgressPercent = 0L;
PlaceWindow(gTheDialog, &gThePrefs.progressBounds,
CenterWindow(gTheDialog));
HiliteMenu(0);
MySetPort(nil);
ShowWindow(gTheDialog);
DrawDialog(gTheDialog);
AdjustMenus();
} else DeallocateDialog(gTheDialog), gTheDialog = nil;
}
// reset the last time so that we draw right away, and update the initial
// percentage
gLastTime = 0;
theErr = GenericProgress(codecProgressUpdatePercent, 0, refCon);
break;
case codecProgressUpdatePercent:
if (gTheDialog) {
// update our user item no more than 3x a second; do this by setting the
// gProgressPercent global, and forcing an update of the bar userItem
if ((TickCount() - gLastTime) < 10L) break;
gLastTime = TickCount();
// scale the displayed percentage according to begin/end
gProgressPercent = theProgress->begin +
FixMul(thePercent, theProgress->end - theProgress->begin);
UpdateDialogItem(gTheDialog, progressUser);
}
break;
case codecProgressClose:
// display the final percentage and delay for an instant
GenericProgress(codecProgressUpdatePercent, 0x10000, refCon);
Delay(1, &ticks);
if (--theProgress->level > 0) break;
// if this is the final nested call, save the position and exit
MySetPort(nil);
if (gTheDialog) {
if (GetWVariant(gTheDialog) != dBoxProc)
SaveWindowPosition(gTheDialog, &gThePrefs.progressBounds);
if (gInBackground) FWHideFloatingWindows();
FWCloseDialog(gTheDialog);
DisposeHandle(((DialogPeek)gTheDialog)->items);
DeallocateDialog(gTheDialog);
}
gTheDialog = nil;
break;
}
// restore the original port and check for aborts
PopPort();
if (theErr == noErr) {
if (theProgress->aborted || (theProgress->aborted = CheckAbort(theParams)))
theErr = codecAbortErr;
}
// if there was an abort signalled, and the dialog is visible, hide it immediately
// to give better user feedback
if (theErr == codecAbortErr && gTheDialog && WindowVisible(gTheDialog))
FWHideWindow(gTheDialog);
// restore the drawing parameters handle to its original state and exit
if (theParams) HSetState((Handle)theParams, hState);
return theErr;
}
//=====================================================================================
// pascal void UpdateBar(DialogPtr theDialog, short theItem)
//=====================================================================================
// Draws the progress bar for the generic progress dialog, according to the percentage
// stored in the global gProgressPercent.
//=====================================================================================
static pascal void UpdateBar(DialogPtr theDialog, short theItem)
{
static RGBColor gBarBackground = { 0xcccc, 0xcccc, 0xffff };
static RGBColor gBarForeground = { 0x4000, 0x4000, 0x4000 };
Boolean useBW = ((CGrafPtr)theDialog)->portPixMap &&
(*((CGrafPtr)theDialog)->portPixMap)->pixelSize == 1;
Rect itemRect, leftRect, rightRect;
Handle itemHandle;
Fixed thePercent;
short itemType;
PushPort();
MySetPort((CGrafPtr)theDialog);
thePercent = gProgressPercent;
GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect);
PenSize(1, 1);
RGBForeColor(&gBlack);
FrameRect(&itemRect);
InsetRect(&itemRect, 1, 1);
rightRect = leftRect = itemRect;
leftRect.right = itemRect.left +
FixRound(FixMul(thePercent, Long2Fix(Width(&itemRect))));
rightRect.left = leftRect.right;
if (leftRect.right > leftRect.left) {
RGBForeColor(&gBarForeground);
PaintRect(&leftRect);
}
if (rightRect.left < rightRect.right) {
if (useBW) RGBForeColor(&gWhite);
else RGBForeColor(&gBarBackground);
PaintRect(&rightRect);
}
RGBForeColor(&gBlack);
PopPort();
}
//=====================================================================================
// pascal void UpdateBarIndef(DialogPtr theDialog, short theItem)
//=====================================================================================
// Draws the progress bar for the generic progress dialog, in the Finder's "barber
// shop" style. Used for progress display when the final count of things to be done is
// not initially known.
//=====================================================================================
static pascal void UpdateBarIndef(DialogPtr theDialog, short theItem)
{
static RGBColor gBarBackground = { 0xcccc, 0xcccc, 0xffff };
static RGBColor gBarForeground = { 0x4000, 0x4000, 0x4000 };
static short gBarOffset = 0;
Boolean useBW = ((CGrafPtr)theDialog)->portPixMap &&
(*((CGrafPtr)theDialog)->portPixMap)->pixelSize == 1;
short itemType, left;
Handle itemHandle;
RgnHandle oldClip;
Rect itemRect;
if (!theDialog) return;
PushPort();
MySetPort((CGrafPtr)theDialog);
GetDItem(theDialog, theItem, &itemType, &itemHandle, &itemRect);
if (oldClip = NewRgn()) {
GetClip(oldClip);
PenSize(1, 1);
RGBForeColor(&gBlack);
FrameRect(&itemRect);
InsetRect(&itemRect, 1, 1);
ClipRect(&itemRect);
left = itemRect.left - (3 * Height(&itemRect)) +
(gBarOffset * Height(&itemRect) / 2);
PenSize(Height(&itemRect), 1);
while (left < itemRect.right) {
RGBForeColor(&gBarForeground);
MoveTo(left, itemRect.top);
LineTo(left += Height(&itemRect), itemRect.bottom);
if (useBW) RGBForeColor(&gWhite);
else RGBForeColor(&gBarBackground);
MoveTo(left, itemRect.top);
LineTo(left += Height(&itemRect), itemRect.bottom);
}
PenSize(1, 1);
SetClip(oldClip);
DisposeRgn(oldClip);
gBarOffset = (gBarOffset + 1) & 3;
}
RGBForeColor(&gBlack);
PopPort();
}
//=====================================================================================
// pascal OSErr DrawProgress(short theMessage, Fixed thePercent,
// JVDrawParamsHandle theParams)
//=====================================================================================
// Handles progress that is displayed during onscreen drawing. This is shown as a
// small percentage in the bottom-right corner of the screen. There is a lot that we
// need to be concerned with here, especially the possibility of overwriting an area
// that has already been drawn, and calculating the best place to put the percentage.
//=====================================================================================
extern pascal OSErr DrawProgress(short theMessage, Fixed thePercent,
JVDrawParamsHandle theParams)
{
static Fixed gLastPercent;
static Point gThePoint;
static long gLastTime;
static short gLastH;
Boolean state = (thePercent != 0);
OSErr theErr = noErr;
RgnHandle tempRgn;
FontInfo theFont;
Str255 theNum;
Rect theRect;
short i;
// if we're not drawing onscreen, don't do this at all; otherwise, set port to the
// onscreen port and go for it
if (!(*theParams)->on.port) return noErr;
KeepSpinning();
PushPort();
MySetPort((*theParams)->on.port);
// calculate the scaled percentage, according to the nested progress we use
thePercent = (*theParams)->progress.begin +
FixMul(thePercent, (*theParams)->progress.end - (*theParams)->progress.begin);
switch (theMessage) {
case codecProgressOpen:
// if this is the first nesting level, set the gDrawing flag, readjust the
// menus, and calculate the best place to draw the progress percentage
if (++(*theParams)->progress.level > 1) break;
if (!gDrawing) gDrawing = (ImageHandle)-1;
HiliteMenu(0);
AdjustMenus();
InitDrawProgress(&gThePoint, theParams);
(*theParams)->progress.aborted = false;
GetFontInfo(&theFont);
(*theParams)->endprogress = gThePoint.v - theFont.ascent - 2;
gLastTime = 0;
gLastH = 0;
DrawProgress(codecProgressUpdatePercent, 0L, theParams);
break;
// if we get scaling or dithering messages, we use the last percentage
case codecProgressUpdateScaling:
case codecProgressUpdateDithering:
thePercent = gLastPercent;
case codecProgressUpdatePercent:
gLastPercent = thePercent;
// if we haven't drawn over the area where the progress is, we check to be
// sure there's been at least 1/3 sec. since the last update; of course, this
// doesn't apply if we've received the UpdateScaling or UpdateDithering msgs.
if (!(*theParams)->endprogress ||
((TickCount() - gLastTime) < 10L &&
theMessage == codecProgressUpdatePercent)) break;
gLastTime = TickCount();
// here's where we actually determine the string and draw it onscreen
SetClip(qd.thePort->visRgn);
RGBForeColor(&gBlack);
RGBBackColor(&gWhite);
MakeDrawProgressText(state ? theMessage : codecProgressUpdatePercent, theNum,
thePercent);
i = gThePoint.h - StringWidth(theNum);
MoveTo(i, gThePoint.v - 2);
DrawString(theNum);
MySetRect(&theRect, gLastH ? gLastH : i - 1, (*theParams)->endprogress, i,
(*theParams)->drawProgressRect.bottom);
PaintRect(&theRect);
gLastH = i;
break;
case codecProgressClose:
// if this isn't the last nesting level, just ignore for now
if (--(*theParams)->progress.level > 0) break;
gDrawing = nil;
AdjustMenus();
// if we never drew over the area where the progress is, make sure we erase
// it; otherwise it'll look funny
if (!(*theParams)->endprogress) break;
if (tempRgn = NewRgn()) {
GetClip(tempRgn);
SetClip((*theParams)->newupdates);
MySetRect(&theRect, gLastH ? gLastH : (*theParams)->drawProgressRect.left,
(*theParams)->endprogress, (*theParams)->drawProgressRect.right,
(*theParams)->drawProgressRect.bottom);
PaintRect(&theRect);
SetClip(tempRgn);
DisposeRgn(tempRgn);
}
break;
}
PopPort();
// restore the original port and check for aborts
if ((*theParams)->progress.aborted ||
((*theParams)->progress.aborted = CheckAbort(theParams)))
theErr = codecAbortErr;
return theErr;
}
//=====================================================================================
// void InitProgress(Point *thePoint)
//=====================================================================================
// Handles progress that is displayed during onscreen drawing. This is shown as a
// small percentage in the bottom-right corner of the screen. There is a lot that we
// need to be concerned with here, especially the possibility of overwriting an area
// that has already been drawn, and calculating the best place to put the percentage.
//=====================================================================================
static void InitDrawProgress(Point *thePoint, JVDrawParamsHandle theParams)
{
RgnHandle theRgn, textRgn;
short height, width;
FontInfo theFont;
Str255 tempStr;
Rect fullRect;
PenNormal();
TextFont(geneva);
TextMode(notSrcCopy);
TextSize(9);
BlockMove(gString[strDecompressing], tempStr, gString[strDecompressing][0] + 1);
GetFontInfo(&theFont);
height = theFont.leading + theFont.ascent + theFont.descent;
width = StringWidth(tempStr);
fullRect = (*theParams)->drawProgressRect;
fullRect.bottom = (*qd.thePort->visRgn)->rgnBBox.bottom;
if (fullRect.bottom > (*theParams)->drawProgressRect.bottom)
fullRect.bottom = (*theParams)->drawProgressRect.bottom;
MySetPt(thePoint, fullRect.right, fullRect.bottom);
if (theRgn = NewRgn()) {
if (textRgn = NewRgn()) {
while (true) {
while (true) {
SetRectRgn(theRgn, fullRect.left, fullRect.bottom - 1,
fullRect.right, fullRect.bottom);
SectRgn(theRgn, qd.thePort->visRgn, theRgn);
SetRectRgn(textRgn, (*theRgn)->rgnBBox.right - width,
(*theRgn)->rgnBBox.bottom - height,
(*theRgn)->rgnBBox.right, (*theRgn)->rgnBBox.bottom);
SectRgn(textRgn, qd.thePort->visRgn, theRgn);
if (EqualRgn(textRgn, theRgn)) break;
if (EmptyRgn(theRgn)) break;
fullRect.right = (*theRgn)->rgnBBox.right - 1;
if (Width(&fullRect) < width) {
fullRect.right = (*theParams)->drawProgressRect.right;
break;
}
}
if (EqualRgn(textRgn, theRgn)) break;
fullRect.bottom -= 4;
if (Height(&fullRect) < (height << 2)) {
fullRect = (*theParams)->drawProgressRect;
break;
}
}
MySetPt(thePoint, (*theRgn)->rgnBBox.right - 1, (*theRgn)->rgnBBox.bottom);
DisposeRgn(textRgn);
}
DisposeRgn(theRgn);
}
}
//=====================================================================================
// void MakeProgressText(short theMessage, StringPtr theNum, Fixed thePercent)
//=====================================================================================
// Assemble the string to display for the drawing progress.
//=====================================================================================
static void MakeDrawProgressText(short theMessage, StringPtr theNum, Fixed thePercent)
{
theNum[0] = 0;
if (theMessage == codecProgressUpdateScaling && thePercent)
AddString(theNum, gString[strScaling]);
else if (theMessage == codecProgressUpdateDithering && thePercent)
AddString(theNum, gString[strDithering]);
else AddString(theNum, gString[strDecompressing]);
StuffNumber(theNum, 1, FixRound(FixMul(thePercent, Long2Fix(100L))));
}
//=====================================================================================
// pascal OSErr SlideProgress(short theMessage, Fixed thePercent, long refCon)
//=====================================================================================
// Progress function for slide shows; draws a rotating little 2x1 dot in the
// upper-right corner of the screen.
//=====================================================================================
extern pascal OSErr SlideProgress(short theMessage, Fixed thePercent, long refCon)
{
#if applec
#pragma unused(thePercent)
#endif
static long gLastTime, gLastPos;
JVDrawParamsHandle theParams = (JVDrawParamsHandle)refCon;
ImageHandle theImage = FrontImage();
WindowPtr theWindow;
Rect theRect;
theWindow = (theImage && Full(theImage)) ? (*theImage)->window : GetSlideBackWindow();
KeepSpinning();
if (theWindow) {
theRect = gImageRoot ? (*(*gImageRoot)->dmon)->rect : theWindow->portRect;
PushPort();
MySetPort((CGrafPtr)theWindow);
ClipRect(&theRect);
switch (theMessage) {
case codecProgressRealOpen:
gLastTime = gLastPos = 0;
SetCPixel(theRect.right - 1, theRect.top + 0, &gWhite);
SetCPixel(theRect.right - 2, theRect.top + 0, &gWhite);
SetCPixel(theRect.right - 1, theRect.top + 1, &gBlack);
SetCPixel(theRect.right - 2, theRect.top + 1, &gBlack);
break;
case codecProgressUpdatePercent:
if ((TickCount() - gLastTime) < 20L) break;
gLastTime = TickCount();
switch (gLastPos = (gLastPos + 1) & 3) {
case 0:
SetCPixel(theRect.right - 2, theRect.top + 1, &gBlack);
SetCPixel(theRect.right - 1, theRect.top + 0, &gWhite);
break;
case 1:
SetCPixel(theRect.right - 2, theRect.top + 0, &gBlack);
SetCPixel(theRect.right - 1, theRect.top + 1, &gWhite);
break;
case 2:
SetCPixel(theRect.right - 1, theRect.top + 0, &gBlack);
SetCPixel(theRect.right - 2, theRect.top + 1, &gWhite);
break;
case 3:
SetCPixel(theRect.right - 1, theRect.top + 1, &gBlack);
SetCPixel(theRect.right - 2, theRect.top + 0, &gWhite);
break;
}
break;
case codecProgressRealClose:
SetCPixel(theRect.right - 1, theRect.top + 0, &gBlack);
SetCPixel(theRect.right - 2, theRect.top + 0, &gBlack);
SetCPixel(theRect.right - 1, theRect.top + 1, &gBlack);
SetCPixel(theRect.right - 2, theRect.top + 1, &gBlack);
break;
}
PopPort();
}
if (theParams &&
((*theParams)->progress.aborted || ((*theParams)->progress.aborted = CheckAbort(theParams))))
return codecAbortErr;
else return noErr;
}