eudora-mac/filegraphic.c

1 line
102 KiB
C
Executable File
Raw Permalink Blame History

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "filegraphic.h"
#include "pete.h"
#include <QuickTimeComponents.h>
#ifdef URLACCESS
#include <urlaccess.h>
#else
typedef void* URLReference;
#endif
#if NETSCAPE
#include "netscapeplugins.h"
#endif
#define FILE_NUM 81
/* Copyright (c) 1996 by Qualcomm, Inc. */
#pragma segment FILEGRAPHIC
#ifdef DEBUG
// Display missing graphic asserts?
// Don't normally need this. Usually missing graphics are ones that have
// been deleted by transferring a message to the trash.
//#define MISSING_GRAPHIC_ASSERTS
#endif
#ifdef ATT_ICONS
enum { kBorderSize = 2 };
#define kMaxDownloads 4
#define kAttStubIconWd 20
#define kQTYesList 'QTys'
#define kQTNoList 'QTno'
typedef struct GetURLDataStruct **GetURLDataHandle;
typedef struct GetURLDataStruct
{
GetURLDataHandle next;
GetURLDataHandle duplicate;
FGIHandle graphic;
PETEHandle pte;
URLReference urlRef;
FSSpec spec;
OSStatus urlError;
Boolean finished;
Boolean reschedule;
} GetURLData;
// Globals
static short gPicRefN;
static Handle ghSpoolBuf;
static long gSpoolBufSize,gSpoolBufIdx;
static Handle gMissingGraphicIconSuite;
static Handle gBadGraphicIconSuite;
static short gDownloadCount;
static GetURLDataHandle gURLDataList;
static QTImageHandle gQTImageList;
static Boolean gMovieClick;
static MyWindowPtr gGetGraphicsWin;
pascal OSErr FileGraphicSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag);
void PeteMakeFileGraphic(PETEHandle pte,FGIHandle newGHndl,FSSpecPtr spec,short maxWidth,short ht,Boolean displayInline);
OSErr PeteFileGraphicHit(PETEHandle pte,FGIHandle graphic,long offset,EventRecord *event);
Rect *FileGraphicIconRect(Rect *iconR, Rect *r);
void FileGraphicDisplay(PETEHandle pte, FGIHandle graphic, long offset);
static PicHandle SpecPicture(FSSpecPtr spec,long maxBytes);
void SpoolPictDisplay(PicHandle picture,FSSpecPtr spec,Rect *r);
Rect *FileGraphicTextRect(Rect *textR, Rect *r);
void FileGraphicAdjustSize(FGIHandle graphic, short width);
HFSFlavor *BuildHFSFlavor(HFSFlavor *hfs, FSSpecPtr spec);
void FileGraphicFancyRgn(PETEHandle pte,FGIHandle graphic,long offset,Point pt,RgnHandle rgn);
Boolean FileGraphicDrag(PETEHandle pte,FGIHandle graphic,long offset,EventRecord *event);
OSErr GetDropSpec(DragReference drag,FSSpecPtr spec);
OSErr FinderTricks(DragReference drag,OSErr dragErr,FSSpecPtr orig);
static void DrawBorder(Boolean doGrey,Rect *r,short thickness,Boolean linkBorder);
static void ImporterDataToHandle(FGIHandle hGraphic);
static CodecType ValidGraphicFile(FGIHandle hGraphic);
static void GetHTMLSpec(FGIHandle graphic,PETEHandle pte,long offset);
static void FileGraphicRect(Rect *r,PETEHandle pte, FGIHandle graphic, long offset, Boolean withHTMLBorder);
static OSErr GetFileGraphicSpec(FGIHandle graphic, FSSpecPtr spec);
static Boolean CanImport(FSSpecPtr spec, GraphicsImportComponent *importer, Component *c, OSType fileType);
static void SetMovieBounds(FGIHandle graphic,Rect *r,PETEHandle pte);
static OSErr OpenQTMovie(FSSpecPtr spec,Movie *theMovie,WindowPtr win,OSType fileType,FGIHandle newGHndl,Boolean active,Boolean justChecking);
static OSErr MakeFileRef(FGIHandle graphic,FSSpecPtr spec);
static void MissingGraphicIcon(FGIHandle graphic,Boolean zapDimensions);
static void BadGraphicIcon(FGIHandle graphic,Boolean zapDimensions);
static OSErr InsertGraphic(PETEHandle pte,FGIHandle graphic);
static void ReleaseFileRef(GFileRefHandle fileRef);
static short GetMaxWidth(PETEHandle pte);
static void SetupMovie(Movie theMovie,FGIHandle newGHndl,Rect *r,Boolean hideController,Handle hFile);
static void MakeURLFileSpec(FGIHandle graphic,PETEHandle pte,FSSpec *spec);
static Boolean GetHTMLGraphic(PETEHandle pte,FGIHandle graphic,Boolean allowDownload,Boolean forceDownload);
static void DisposeURLRef(FGIHandle graphic);
#ifdef URLACCESS
static pascal OSStatus URLNotifyProc(void *userContext, URLEvent event, URLCallbackInfo *callbackInfo);
#endif
static void CheckCacheLimit(void);
static void DisplayDownload(GetURLDataHandle hURLData);
static OSErr BeginDownload(GetURLDataHandle hURLData,URLReference *urlRef);
static Boolean URLAccessIsInstalled(void);
static Boolean CanQTImport(FSSpecPtr spec,OSType fileType);
static Boolean IsImporterFileType(OSType fileType,Handle h);
static Boolean IsImporterFileExt(Handle h,PStr s);
static void DownloadURLFinished(long refCon,OSErr err,DownloadInfo *info);
static GraphicsImportComponent GetImageImporter(FGIHandle graphic);
static pascal OSErr ImportProgress(short message, Fixed completeness, long refcon);
static void URLDeleteFile(GetURLDataHandle hURLData);
static void DisplayFileName(StringPtr s,Rect *rText);
static void DisposeImage(FGIHandle graphic);
static QTImageHandle CanReuseImage(FSSpecPtr spec);
static Ptr FindPNGChunk(uLong chunkType,Handle hData,long *pLength);
void SetupPNGTransparencyLo(GraphicsImportComponent importer,Handle hData);
static CodecType GetGraphicType(GraphicsImportComponent importer);
static Boolean FindPNGTransparency(Handle hData,RGBColor *transColor);
static void DisposeGraphic(FGIHandle graphic);
static Boolean TryQTImport(OSType fType,OSType fCreator);
static Boolean FindQTImporter(ResType rListResType,short rIdx,OSType ftype,OSType fCreator);
pascal void MyGetPic(Ptr dataPtr, short byteCount);
#define IsMovieFile(spec,type) (!OpenQTMovie(spec,nil,nil,type,nil,false,true))
Boolean HTMLGraphicIsObnoxious(FGIHandle graphic);
/************************************************************************
* PeteFileGraphicStyle - make a pse for a graphic style
************************************************************************/
OSErr PeteFileGraphicStyle(PETEHandle pte,FSSpecPtr spec,HTMLGraphicInfo *html,PETEStyleEntryPtr pse,long flags)
{
FGIHandle gHandle;
OSErr err=noErr;
short wide = RectWi((*PeteExtra(pte))->win->contR)-2*GROW_SIZE;
if (gHandle = NuHandleClear(sizeof(FileGraphicInfo)))
{
(*gHandle)->pgi.itemProc = FileGraphic;
(*gHandle)->attachment = (flags&fgAttachment)!=0;
(*gHandle)->centerInWin = (flags&fgCenterInWindow)!=0;
(*gHandle)->isEmoticon = (flags&fgEmoticon)!=0;
if (html)
{
if(html->pictResID || html->pictHandle)
{
PicHandle picture;
picture = html->pictResID ? GetPicture(html->pictResID) : html->pictHandle;
if(!picture)
{
ZapHandle(gHandle);
return ((err = ResError()) != noErr) ? err : resNotFound;
}
(*gHandle)->pgi.privateType = html->pictResID ? pgtIndexPict : pgtPictHandle;
(*gHandle)->displayInline = true;
(*gHandle)->width = (*picture)->picFrame.right - (*picture)->picFrame.left;
(*gHandle)->height = (*picture)->picFrame.bottom - (*picture)->picFrame.top;
}
else
{
// For HTML images we don't have the FSSpec yet. We'll set up for it and get it later
(*gHandle)->pgi.privateType = pgtHTMLPending;
(*gHandle)->displayInline = (flags&fgDisplayInline)!=0;
(*gHandle)->width = 28;
(*gHandle)->height = 18;
}
(*gHandle)->htmlInfo = *html;
if (html->alt)
{
PCopy((*gHandle)->name,html->alt); // Any Alt text is here
(*gHandle)->htmlInfo.alt = (StringPtr)-1L; // Flag for HTML generator
} else {
(*gHandle)->htmlInfo.alt = nil;
}
}
if (spec)
PeteMakeFileGraphic(pte,gHandle,spec,wide,RectHi((*PeteExtra(pte))->win->contR)-2*GROW_SIZE,(flags&fgDisplayInline)!=0);
else if(!html || html->pictResID != 0)
(*gHandle)->pgi.wantsEvents = true;
(*gHandle)->pgi.cloneOnlyText = (flags&fgDontCopyToClip)!=0;
pse->psStartChar = 0;
pse->psGraphic = 1;
pse->psStyle.graphicStyle.graphicInfo = (void*)gHandle;
}
else err = MemError();
return(err);
}
/**********************************************************************
* PeteFileGraphicRange - Called by editor to set up graphic file display
**********************************************************************/
OSErr PeteFileGraphicRange(PETEHandle pte,long start,long stop,FSSpecPtr spec,long flags)
{
PETEStyleEntry pse;
long junk;
OSErr err=noErr;
Handle text;
Str255 old;
Boolean dirty = PeteIsDirty(pte);
Boolean winDirty = (*PeteExtra(pte))->win->isDirty;
short wide = RectWi((*PeteExtra(pte))->win->contR)-2*GROW_SIZE;
Zero(pse);
PETEGetStyle(PETE,pte,start,&junk,&pse);
if (!pse.psGraphic)
{
if (!(err=PeteFileGraphicStyle(pte,spec,nil,&pse,flags)))
{
PeteGetTextAndSelection(pte,&text,nil,nil);
MakePStr(old,*text+start,stop-start);
PeteDelete(pte,start,stop);
**Pslh = pse;
if (err=PETEInsertTextPtr(PETE,pte,start,old+1,*old,Pslh))
ZapHandle(pse.psStyle.graphicStyle.graphicInfo);
PETEMarkDocDirty(PETE,pte,dirty);
(*PeteExtra(pte))->win->isDirty = winDirty;
}
else err = MemError();
}
return(err);
}
/**********************************************************************
* FileGraphicChangeGraphic - the graphic has changed
**********************************************************************/
OSErr FileGraphicChangeGraphic(PETEHandle pte,long offset,FSSpecPtr spec)
{
OSErr err = noErr;
PETEStyleEntry pse;
long junk;
Zero(pse);
if (!(err = PETEGetStyle(PETE,pte,offset,&junk,&pse)))
{
FGIHandle graphic;
if (pse.psGraphic && (graphic = pse.psStyle.graphicStyle.graphicInfo))
{
DisposeGraphic(graphic); // dispose of any previous graphic data
PeteMakeFileGraphic(pte,graphic,spec,GetMaxWidth(pte),(*graphic)->height,(*graphic)->displayInline);
PeteRecalc(pte); // redraw
PeteUpdate(pte);
}
else
err = -1;
}
return(err);
}
/**********************************************************************
* FileGraphic - Callback to handle a graphic that is a file
**********************************************************************/
pascal OSErr FileGraphic(PETEHandle pte,FGIHandle graphic,long offset,PETEGraphicMessage message,void *data)
{
OSErr err = noErr;
Rect r;
Handle picture=nil;
Boolean isSelected;
short oldResFile = CurResFile();
FGIHandle graphicClone;
EventRecord *event;
MyWindowPtr win;
Boolean keyEvent;
short maxWidth;
PushGWorld();
if ((*graphic)->pgi.privateType == pgtHTMLPending)
// Still waiting for the HTML graphic from the "related:" line
GetHTMLSpec(graphic,pte,offset);
switch (message)
{
case peGraphicDraw: /* data is (Point *) with penLoc at baseline left as a point */
if (data)
(*graphic)->urlLink = (((PETEGraphicStylePtr)data)->tsLabel&pLinkLabel) != 0;
FileGraphicDisplay(pte,graphic,offset);
break;
case peGraphicClone: /* data is (PETEGraphicInfoHandle *) into which to put duplicate */
// When user does copy or drag and drop, make copy of graphic
if (graphicClone = NuHTempOK(sizeof(FileGraphicInfo)))
{
// make clone
FGIPtr pClone = LDRef(graphicClone);
*pClone = **graphic;
pClone->clone = true;
if (pClone->fileRef)
(*pClone->fileRef)->count++;
if (pClone->htmlInfo.absURL)
HandToHand((Handle *) &pClone->htmlInfo.absURL);
if (pClone->htmlInfo.pictHandle)
HandToHand((Handle *) &pClone->htmlInfo.pictHandle);
WriteZero(&pClone->u,sizeof(pClone->u));
pClone->pURLInfo = nil;
pClone->pgi.isSelected = false;
UL(graphicClone);
}
else err = MemError();
*(FGIHandle*)data = graphicClone;
break;
case peGraphicInsert: /* data is nil. Graphic has been inserted into a new document. */
err = InsertGraphic(pte,graphic);
break;
case peGraphicTest: /* data is (Point *) from top left; return errOffsetIsOutsideOfView if not hit */
// Can cancel hit by returning errOffsetIsOutsideOfView
if (ClickType==Double && (*graphic)->urlLink)
// Don't allow Pete to mess up double-click on link
err = errOffsetIsOutsideOfView;
else
{
Point pt = *(Point*)data;
Point hiwi;
if (pt.h < 3 || pt.v < 3) err = errOffsetIsOutsideOfView;
else
{
hiwi.h = (*graphic)->pgi.width;
hiwi.v = (*graphic)->pgi.height;
if (hiwi.h-pt.h < 3 || hiwi.v-pt.v < 3) err = errOffsetIsOutsideOfView;
}
}
break;
case peGraphicHit: /* data is (EventRecord *) if mouse down, nil if time to turn off */
isSelected = (*graphic)->pgi.isSelected;
if (data)
{
if (!isSelected)
{
(*graphic)->pgi.isSelected = True;
}
}
else
{
if (isSelected)
{
(*graphic)->pgi.isSelected = False;
}
}
if (isSelected != (*graphic)->pgi.isSelected)
{
// Changed selection, redraw
if ((*graphic)->pgi.privateType == pgtIcon)
FileGraphicDisplay(pte,graphic,offset);
else
{
Rect rHilite;
FileGraphicRect(&rHilite,pte,graphic,offset,true);
if ((*graphic)->pgi.isSelected)
DrawBorder(true,&rHilite,kBorderSize,(*graphic)->urlLink);
else
{
InsetRect(&rHilite,-2,-2);
InvalWindowRect(GetMyWindowWindowPtr((*PeteExtra(pte))->win),&rHilite);
}
}
}
if (data && !(*graphic)->noImage && !(*graphic)->isEmoticon)
err = PeteFileGraphicHit(pte,graphic,offset,(EventRecord *)data);
break;
case peGraphicRemove: /* data is nil; just dispose a copy of graphic */
if (!(*graphic)->clone)
{
GetURLDataHandle hURLData;
DisposeGraphic(graphic);
if (hURLData = (GetURLDataHandle)(*graphic)->pURLInfo)
{
if (!(*hURLData)->reschedule && !(*hURLData)->finished)
{
// Still downloading a graphic. Abort.
#ifdef URLACCESS
if (URLAccessIsInstalled())
{
URLAbort((*hURLData)->urlRef);
URLDeleteFile(hURLData);
}
else
#endif
URLDownloadAbort((long)(*hURLData)->urlRef);
}
DisposeURLRef(graphic);
}
}
ReleaseFileRef((*graphic)->fileRef);
ZapHandle((*graphic)->htmlInfo.absURL);
ZapHandle(graphic);
break;
case peGraphicResize: /* data is a (short *) of the max width */
if ((*graphic)->pgi.privateType == pgtHTMLPending)
{
// The image wasn't sent with the message. Use the "no-image" icon for now.
MissingGraphicIcon(graphic,(*graphic)->htmlInfo.cid!=0);
#ifdef MISSING_GRAPHIC_ASSERTS
if ((*graphic)->htmlInfo.cid && RunType!=Production) Dprintf("\pCould't find graphic in parts stack.");
#endif
// Try to load the image.
if (!(*graphic)->htmlInfo.cid && !GetHTMLGraphic(pte,graphic,PeteIsInAdWindow(pte) || !(PrefIsSet(PREF_DONT_FETCH_GRAPHICS)||PeteIsJunk(pte)),PeteIsInAdWindow(pte)))
{
// This HTML graphic hasn't been downloaded yet.
// Download it when the use clicks on the "GetGraphic" button which is
// then disabled.
(*graphic)->notDownloaded = true;
// Make sure when the "GetGraphics" control is created that it is visible
gGetGraphicsWin = (*PeteExtra(pte))->win;
}
}
else if ((*graphic)->notDownloaded)
{
ControlHandle hCtl = FindControlByRefCon((*PeteExtra(pte))->win,mcGetGraphics);
if (hCtl && GetControlHilite(hCtl)==255) // Download if "GetGraphics" button is now disabled
{
GetHTMLGraphic(pte,graphic,true,true);
(*graphic)->notDownloaded = false;
}
}
maxWidth = *(short*)data;
// don't bother calculating size if the maxWidth hasn't changed
if (maxWidth != (*graphic)->maxWidth)
{
(*graphic)->maxWidth = maxWidth;
FileGraphicAdjustSize(graphic,maxWidth);
}
break;
case peGraphicRegion: /* data is a RgnHandle which is to be changed to the graphic's region */
// Used mostly for changing the cursor
FileGraphicRect(&r,pte,graphic,offset,true);
RectRgn((RgnHandle)data,&r);
break;
case peGraphicEvent: /* data is (EventRecord *) */
event = (EventRecord *)data;
win = (*PeteExtra(pte))->win;
keyEvent = event->what==keyDown || event->what==autoKey;
if (GetMyWindowWindowPtr(win) != FrontWindow_() && (event->what==mouseDown || keyEvent))
// Ignore clicks and keys if window isn't in front
break;
switch ((*graphic)->pgi.privateType)
{
ComponentResult movieResult;
MovieController aController;
case pgtMovie:
{
static Point downSpot; /* Point of previous mouseDown */
static long upTicks; // time of last mouseUp */
Point localPt = event->where;
SetPort_(GetMyWindowCGrafPtr(win));
aController = (*graphic)->u.movie.aController;
GlobalToLocal(&localPt);
// We have to do our own double-click processing so
// that we can avoid sending double-clicks to the movie
// player.
if (event->what==mouseDown && PtInPETE(localPt,pte))
{
long dblTime = GetDblTime();
long tolerance = GetRLong(DOUBLE_TOLERANCE);
static long oldWhen;
/*
* figure out whether this is a double or triple click,
* and update double and triple click times
*/
if (event->when-upTicks<dblTime && event->when!=oldWhen)
{
int dx = event->where.h - downSpot.h;
int dy = event->where.v - downSpot.v;
if (ABS(dx)<tolerance && ABS(dy)<tolerance && !(event->modifiers&cmdKey))
{
/* upgrade the click */
ClickType++;
if (ClickType > Triple) ClickType = Single;
}
else
ClickType = Single;
}
else
ClickType = Single;
downSpot = event->where;
oldWhen = event->when;
}
if (event->what==mouseUp || event->what==mouseDown) upTicks = TickCount();
FileGraphicRect(&r,pte,graphic,offset,false);
// Ok, now we know if we had a doubleclick. If we did
// launch the app
if (event->what==mouseDown && ClickType==Double)
{
Point localPt = event->where;
GlobalToLocal(&localPt);
if (PtInRect(localPt,&r))
{
FSSpec spec;
if (!GetFileGraphicSpec(graphic,&spec))
OpenOtherDoc(&spec,(event->modifiers&controlKey)!=0,false,pte);
event->what = nullEvent;
}
break;
}
// Make sure movie is in right location
if ((*graphic)->attachment)
r.bottom -= GetLeading(SmallSysFontID(),SmallSysFontSize());
SetMovieBounds(graphic,&r,pte);
if (ClickType!=Double) // don't let the movie controller have double-clicks
if (!keyEvent || (*graphic)->pgi.isSelected) // Don't process keys unless graphic image has been selected
if ((!keyEvent && event->what != mouseDown) || MCGetVisible(aController)) // Don't process keys or clicks if controller not visible
{
SAVE_STUFF;
ForeColor(blackColor);
BackColor(whiteColor);
gMovieClick = false;
movieResult = MCIsPlayerEvent(aController, event);
if (movieResult && !gMovieClick &&
(event->what != mouseDown || (*graphic)->pgi.isSelected)) // Keep mouseDown event if graphic image hasn't been selected yet
event->what = nullEvent; // Event handled by QT. Cancel it.
REST_STUFF;
}
}
break;
#if NETSCAPE
case pgtNPlugin:
NPluginEvent((*graphic)->u.plugin.hPlugin,(EventRecord *)data);
break;
#endif
}
break;
}
PopGWorld();
UseResFile(oldResFile);
return(err);
}
/**********************************************************************
* DisposeGraphic - dispose of graphic data
**********************************************************************/
static void DisposeGraphic(FGIHandle graphic)
{
switch ((*graphic)->pgi.privateType)
{
Handle suite;
case pgtIcon:
if (suite = (*graphic)->u.icon.suite)
{
if (suite != gMissingGraphicIconSuite && suite != gBadGraphicIconSuite && !(*graphic)->u.icon.sharedSuite)
DisposeIconSuite((*graphic)->u.icon.suite,True);
}
else if ((*graphic)->u.icon.iconRef)
ReleaseIconRef((*graphic)->u.icon.iconRef);
break;
case pgtPICT:
case pgtResPict:
ZapHandle((*graphic)->u.pict.picture);
break;
case pgtImage:
DisposeImage(graphic);
break;
case pgtMovie:
if ((*graphic)->u.movie.aController) DisposeMovieController((*graphic)->u.movie.aController);
if ((*graphic)->u.movie.theMovie) DisposeMovie((*graphic)->u.movie.theMovie);
break;
#if NETSCAPE
case pgtNPlugin:
if ((*graphic)->u.plugin.hPlugin) NPluginClose((*graphic)->u.plugin.hPlugin);
break;
#endif
case pgtPictHandle:
ZapHandle((*graphic)->htmlInfo.pictHandle);
break;
}
}
/**********************************************************************
* MakeFileRef - make a new file reference
**********************************************************************/
static OSErr MakeFileRef(FGIHandle graphic,FSSpecPtr spec)
{
OSErr err = noErr;
AliasHandle alias;
GFileRefHandle fileRef=nil;
if (!(err=NewAliasMinimal(spec,&alias)))
{
if (fileRef = NuHandleClear(sizeof(GraphicFileRef)))
{
(*fileRef)->alias = alias;
(*fileRef)->count = 1;
}
else err = MemError();
}
(*graphic)->fileRef = fileRef;
return err;
}
/**********************************************************************
* SetMovieBounds - make sure the movie is in the right place
**********************************************************************/
static void SetMovieBounds(FGIHandle graphic,Rect *r,PETEHandle pte)
{
Rect bounds;
Movie theMovie = (*graphic)->u.movie.theMovie;
MovieController aController = (*graphic)->u.movie.aController;
Boolean controllerVisible = aController && MCGetVisible(aController);
if (controllerVisible)
MCGetControllerBoundsRect(aController, &bounds);
else
GetMovieBox(theMovie,&bounds);
if (!EqualRect(r,&bounds))
{
// Check again. May be off by one.
InsetRect(&bounds,1,1);
if (!EqualRect(r,&bounds))
{
RgnHandle clipRgn;
// Change location
if (controllerVisible)
MCSetControllerBoundsRect(aController, r);
else
{
SetMovieBox(theMovie,r);
MCPositionController(aController,r,nil,mcTopLeftMovie);
}
// Set clipping region also
if (clipRgn=NewRgn())
{
PETEDocInfo peteInfo;
PETEGetDocInfo(PETE,pte,&peteInfo);
RectRgn(clipRgn,&peteInfo.viewRect);
// if (controllerVisible)
MCSetClip(aController,clipRgn,nil);
// else
// SetMovieClipRgn(theMovie,clipRgn);
DisposeRgn(clipRgn);
}
}
}
}
/**********************************************************************
* FileGraphicAdjustSize - adjust the size of a graphic
**********************************************************************/
void FileGraphicAdjustSize(FGIHandle graphic,short maxWidth)
{
short width, height;
if ((*graphic)->htmlInfo.height && (*graphic)->htmlInfo.width)
{
// Size specified in html IMG tag
width = (*graphic)->htmlInfo.width;
height = (*graphic)->htmlInfo.height;
}
else
{
// Use image's default size
width = (*graphic)->width;
height = (*graphic)->height;
}
width += 2*((*graphic)->htmlInfo.hSpace + (*graphic)->htmlInfo.border);
height += 2*((*graphic)->htmlInfo.vSpace + (*graphic)->htmlInfo.border);
if (maxWidth && width > maxWidth && !(*graphic)->centerInWin)
{
// Too wide, scale down
height = maxWidth*height/width;
width = maxWidth;
}
(*graphic)->pgi.width = width;
(*graphic)->pgi.height = height;
(*graphic)->pgi.descent = 3;
}
/**********************************************************************
* DrawBorder - draw/erase border around graphic
**********************************************************************/
static void DrawBorder(Boolean doGrey,Rect *r,short thickness,Boolean linkBorder)
{
Rect rBorder = *r;
RGBColor color;
if (linkBorder)
{
GetRColor(&color,URL_COLOR);
DarkenColor(&color,50);
RGBForeColor(&color);
}
else if (doGrey)
SetForeGrey(k8Grey4);
PenSize(thickness,thickness);
InsetRect(&rBorder,-2,-2);
FrameRect(&rBorder);
PenNormal();
SetForeGrey(0);
}
/**********************************************************************
* FileGraphicDisplay - draw a file
**********************************************************************/
void FileGraphicDisplay(PETEHandle pte, FGIHandle graphic, long offset)
{
Rect r;
Str255 s;
short oldResFile = CurResFile();
OSErr err = noErr;
HLock((Handle)graphic);
FileGraphicRect(&r,pte,graphic,offset,false);
if ((*graphic)->pgi.privateType == pgtIcon)
{
// Display as icon
Rect rText,rIcon;
if (PeteIsInAdWindow(pte)) goto Done; // Don't display missing graphic icon in ad window
PSCopy(s,(*graphic)->name);
SetSmallSysFont();
TextFace(0);
if ((*graphic)->u.icon.attachmentStub)
r.left+=kAttStubIconWd;
FileGraphicTextRect(&rText,&r);
FileGraphicIconRect(&rIcon,&r);
rText.right = rText.left + StringWidth(s);
if ((*graphic)->noImage)
{
// "missing graphic" icon.
short wi = RectWi(r);
short ht = RectHi(r);
short wiText = RectWi(rText);
short htText = RectHi(rText);
if (wi < 16 || ht < 16) goto Done; // display rect too small
CenterRectIn(&rIcon,&r);
if (*s)
{
CenterRectIn(&rText,&r);
if (ht > 32)
{
// text below
if (rText.right >= r.right)
// text is too wide, don't display
*s = 0;
else
{
OffsetRect(&rIcon,0,-8);
OffsetRect(&rText,0,8);
}
}
else
{
// text right
if (wiText+24 >= wi)
// text is too wide, don't display
*s = 0;
else
{
OffsetRect(&rIcon,r.left-rIcon.left+4,0);
OffsetRect(&rText,r.left-rText.left+24,0);
}
}
}
if ((*graphic)->pgi.isSelected || (!(*graphic)->urlLink && !(*graphic)->isEmoticon))
DrawBorder(true,&r,(*graphic)->urlLink ? kBorderSize : 1,(*graphic)->urlLink);
}
// Draw text
if (*s)
{
DisplayFileName(s,&rText);
if ((*graphic)->pgi.isSelected) InvertRect(&rText);
}
// Draw icon
if ((*graphic)->u.icon.suite)
err=PlotIconSuite(&rIcon,atAbsoluteCenter,(*graphic)->pgi.isSelected ? ttSelected:ttNone,(*graphic)->u.icon.suite);
else if ((*graphic)->u.icon.iconRef)
{
PlotIconRef(&rIcon,atAbsoluteCenter,(*graphic)->pgi.isSelected ? ttSelected:ttNone,kIconServicesNormalUsageFlag,(*graphic)->u.icon.iconRef);
}
else
err=PlotIconFromICache(GetICache((*graphic)->type,(*graphic)->creator),(*graphic)->pgi.isSelected ? ttSelected:ttNone,&rIcon);
if ((*graphic)->u.icon.attachmentStub)
{
r.left-=kAttStubIconWd;
FileGraphicIconRect(&rIcon,&r);
PlotIconID(&rIcon,atAbsoluteCenter,ttNone,FETCH_SICN);
}
}
else
{
FSSpec spec;
PicHandle picture;
GDHandle gd;
CGrafPtr port;
Boolean fPortChange;
GetGWorld(&port, &gd);
if (fPortChange = true /*(port != (*graphic)->port || gd != (*graphic)->gd)*/)
{
(*graphic)->port = port;
(*graphic)->gd = gd;
}
if ((*graphic)->pgi.isSelected)
DrawBorder(true,&r,kBorderSize,(*graphic)->urlLink);
if ((*graphic)->attachment)
{
// display the attachment's file name
Rect rText;
short wdText;
rText = r;
r.bottom -= GetLeading(SmallSysFontID(),SmallSysFontSize());
rText.top = r.bottom;
PSCopy(s,(*graphic)->name);
SetSmallSysFont();
TextFace(0);
wdText = StringWidth(s);
DisplayFileName(s,&rText);
}
if ((*graphic)->pgi.privateType == pgtIndexPict || (*graphic)->pgi.privateType == pgtPictHandle)
{
PicHandle picture;
Byte hState;
picture = (*graphic)->pgi.privateType == pgtIndexPict ? GetPicture((*graphic)->htmlInfo.pictResID) : (*graphic)->htmlInfo.pictHandle;
if (picture)
{
hState = HGetState((Handle)picture);
HNoPurge((Handle)picture);
DrawPicture(picture,&r);
if (QDError()==noMemForPictPlaybackErr)
err = memFullErr;
HSetState((Handle)picture, hState);
}
}
else if (!GetFileGraphicSpec(graphic,&spec))
{
switch ((*graphic)->pgi.privateType)
{
case pgtPICT:
if ((*graphic)->u.pict.spool)
SpoolPictDisplay((*graphic)->u.pict.picture,&spec,&r);
else
{
if (picture = (*graphic)->u.pict.picture)
{
if (!*picture)
{
ZapHandle(picture);
picture = SpecPicture(&spec,0);
(*graphic)->u.pict.picture = picture;
}
}
if (picture)
{
HNoPurge((Handle)picture);
DrawPicture(picture,&r);
if (QDError()==noMemForPictPlaybackErr)
err = memFullErr;
HPurge((Handle)picture);
}
}
break;
case pgtResPict:
if (picture = (*graphic)->u.pict.picture)
{
if (!*picture)
{
// Purged
ZapHandle(picture);
picture = SpecResPicture(&spec);
(*graphic)->u.pict.picture = picture;
}
}
if (picture)
{
HNoPurge((Handle)picture);
DrawPicture(picture,&r);
if (QDError()==noMemForPictPlaybackErr)
err = memFullErr;
HPurge((Handle)picture);
}
break;
case pgtImage:
{
// Imported image, let QuickTime handle the display
GWorldPtr gWorld = nil;
Rect rOriginal;
RGBColor saveForeColor,saveBackColor;
RGBColor blackColor = { 0,0,0 };
RGBColor whiteColor = { 0xffff,0xffff,0xffff };
GraphicsImportComponent importer;
PixMapHandle hPM;
PETEDocInfo peteInfo;
Handle hData;
QTImageHandle hQTImage = (*graphic)->u.image.hQTImage;
RgnHandle clipRgn = NewRgn();
rOriginal = r;
importer = GetImageImporter(graphic);
if (!importer) break;
hData = (*hQTImage)->hData;
// Check for purged data
if (hData && *hData==0)
{
// Need to reload
GraphicsImportSetDataFile(importer, &spec);
ImporterDataToHandle(graphic);
}
if (hData)
{
HNoPurge(hData);
SetupPNGTransparencyLo(importer,hData);
}
if (fPortChange)
{
// Don't set the GWorld unless the port has changed.
// According to the documentation for GraphicsImportSetGWorld,
// this is an expensive procedure
GraphicsImportSetGWorld(importer, port, gd);
}
PETEGetDocInfo(PETE,pte,&peteInfo);
if (peteInfo.printing)
{
// QuickTime 2.5 will not print correctly if the image is not at 0,0
// Draw it offscreen at 0,0 and then CopyBits it into the printer port
short depth,devDepth;
OffsetRect(&r,-r.left,-r.top); // Move to location 0,0
depth = 0;
devDepth = GetPortPixelDepth(port);
if (devDepth < 8)
depth = devDepth; // Printing in B&W doesn't work well without this
err = NewGWorld(&gWorld, depth, &r, nil, nil, useTempMem); // Try temp memory first
if (err)
err = NewGWorld(&gWorld, depth, &r, nil, nil, nil); // Failed, use application heap
if (!err)
{
GraphicsImportSetGWorld(importer, gWorld,nil);
SetGWorld(gWorld,nil);
fPortChange = true;
hPM = GetGWorldPixMap(gWorld);
LockPixels(hPM);
EraseRect(&r);
GetPortClipRegion(gWorld,clipRgn);
GraphicsImportSetClip(importer,clipRgn);
}
else
{
r = rOriginal; // Failed to get offscreen world. Try drawing at original location. Maybe QuickTime has been fixed.
}
}
else
{
GetPortClipRegion(port,clipRgn);
GraphicsImportSetClip(importer,clipRgn);
}
GraphicsImportSetBoundsRect(importer, &r);
// Currently there is a bug in QuickTime 2.5. The image will not draw
// if the forecolor is not black and only a portion of the image is being drawn
GetForeColor(&saveForeColor);
RGBForeColor(&blackColor);
GetBackColor(&saveBackColor);
RGBBackColor(&whiteColor);
MemCanFail = True; // Don't be purging memory for this graphic
err = GraphicsImportDraw(importer);
MemCanFail = false;
if (gWorld)
{
// OK, copy the offscreen image to the printer port
SetGWorld(port, gd);
GetForeColor(&saveForeColor);
RGBForeColor(&blackColor);
GetBackColor(&saveBackColor);
RGBBackColor(&whiteColor);
GetPortClipRegion(port,clipRgn);
CopyBits(GetPortBitMapForCopyBits(gWorld),GetPortBitMapForCopyBits(port), &r, &rOriginal, srcCopy, clipRgn);
DisposeGWorld(gWorld);
}
RGBForeColor(&saveForeColor);
RGBBackColor(&saveBackColor);
if (hData)
HPurge(hData);
// Close the importer component because it keeps a large handle (increments of 32K)
// to quickly redraw. We'll reopen if needed.
CloseComponent(importer);
(*hQTImage)->importer = nil;
DisposeRgn(clipRgn);
}
break;
case pgtMovie:
{
MovieController aController;
if (aController = (*graphic)->u.movie.aController)
{
WindowPtr winWP = GetMyWindowWindowPtr ((*PeteExtra(pte))->win);
CGrafPtr port = (*graphic)->port;
Boolean offscreen = GetWindowPort(winWP) != port;
SetMovieBounds(graphic,&r,pte);
if (offscreen)
// Draw to the offscreen port
MCSetControllerPort(aController, port);
MCDraw(aController,winWP);
if (offscreen)
// Restore drawing to the window
MCSetControllerPort(aController, GetWindowPort(winWP));
// MCDraw(aController,(GrafPtr)(*graphic)->port);
err = UpdateMovie((*graphic)->u.movie.theMovie);
}
}
break;
#if NETSCAPE
case pgtNPlugin:
NPluginDraw((*graphic)->u.plugin.hPlugin,&r,port,fPortChange);
break;
#endif
}
}
if ((*graphic)->htmlInfo.border)
{
short thickness = (*graphic)->htmlInfo.border;
InsetRect(&r,-thickness,-thickness);
DrawBorder(false,&r,thickness,false);
}
SetGWorld(port, gd);
}
if (err == memFullErr)
{
SAVE_STUFF;
// Out of memory. Display out of memory message
if (!(*graphic)->pgi.isSelected)
FrameRect(&r);
GetRString(s,GRAPHIC_MEM);
InsetRect(&r,4,4);
if (r.bottom-r.top > 50)
// Do some rough vertical centering
r.top = (r.top+r.bottom)/2 - 10;
SetSmallSysFont();
TextFace(0);
TETextBox(s+1,*s,&r,teCenter);
REST_STUFF;
}
Done:
HUnlock((Handle)graphic);
UseResFile(oldResFile);
}
/**********************************************************************
*
**********************************************************************/
pascal void MyGetPic(Ptr dataPtr, short byteCount)
{
long count = byteCount;
long remainingBytes;
long dataOffset = 0;
remainingBytes = gSpoolBufSize-gSpoolBufIdx;
while (count > remainingBytes)
{
// Don't have enough bytes left in the buffer to service this request.
if (remainingBytes > 0)
{
// Copy what we have<76>
BMD(*ghSpoolBuf+gSpoolBufIdx,dataPtr+dataOffset,remainingBytes);
dataOffset += remainingBytes;
count -= remainingBytes;
}
// <09>and read some more.
HLock(ghSpoolBuf);
FSRead(gPicRefN,&gSpoolBufSize,*ghSpoolBuf);
HUnlock(ghSpoolBuf);
gSpoolBufIdx = 0;
remainingBytes = gSpoolBufSize;
}
BMD(*ghSpoolBuf+gSpoolBufIdx,dataPtr+dataOffset,count);
gSpoolBufIdx += count;
}
/**********************************************************************
* SpoolPictDisplay - display a spooled picture
**********************************************************************/
void SpoolPictDisplay(PicHandle picture,FSSpecPtr spec,Rect *r)
{
QDProcsPtr savedProcs;
QDProcs myProcs;
CQDProcsPtr savedCProcs;
CQDProcs myCProcs;
FSSpec newSpec;
DECLARE_UPP(MyGetPic,QDGetPic);
INIT_UPP(MyGetPic,QDGetPic);
/*
* set up the bottlenecks
*/
if (ThereIsColor)
{
savedCProcs = GetPortGrafProcs(GetQDGlobalsThePort());
if (savedCProcs)
myCProcs = *savedCProcs; // Start with existing procs
else
SetStdCProcs(&myCProcs); // None existing, get standard procs
SetPortGrafProcs(GetQDGlobalsThePort(),&myCProcs);
}
else
{
savedProcs = GetPortGrafProcs(GetQDGlobalsThePort());
if (savedProcs)
myProcs = *savedProcs; // Start with existing procs
else
SetStdProcs(&myProcs); // None existing, get standard procs
SetPortGrafProcs(GetQDGlobalsThePort(),&myProcs);
}
myCProcs.getPicProc = MyGetPicUPP;
myProcs.getPicProc = MyGetPicUPP;
if (!AFSpOpenDF(spec,&newSpec,fsRdPerm,&gPicRefN))
{
SetFPos(gPicRefN,fsFromStart,512+sizeof(Picture));
// Allocate spool buffer memory
// Try halving the size if memory allocation fails
for(gSpoolBufSize = 8 K;gSpoolBufSize && (!(ghSpoolBuf = NuHTempOK(gSpoolBufSize)));gSpoolBufSize >>= 1);
gSpoolBufIdx = gSpoolBufSize;
if (ghSpoolBuf)
{
DrawPicture(picture,r);
ZapHandle(ghSpoolBuf);
}
FSClose(gPicRefN);
}
/*
* put the bottlenecks back
*/
if (ThereIsColor)
SetPortGrafProcs(GetQDGlobalsThePort(),savedCProcs);
else
SetPortGrafProcs(GetQDGlobalsThePort(),savedProcs);
}
/**********************************************************************
* SpecPicture - grab a picture from a file
**********************************************************************/
static PicHandle SpecPicture(FSSpecPtr spec,long maxBytes)
{
PicHandle picture = nil;
long len;
Boolean
MemCanFail = True; // Don't be purging memory for this graphic
Snarf(spec,(void*)&picture,maxBytes ? maxBytes+512:0);
MemCanFail = False;
if (picture)
{
len = GetHandleSize_(picture);
if (len<512) ZapHandle(picture);
else
{
BMD((UPtr)*picture+512,(UPtr)*picture,len-512);
SetHandleBig_(picture,len-512);
}
}
return(picture);
}
/**********************************************************************
* SpecResPicture - grab a picture resource from a file
**********************************************************************/
PicHandle SpecResPicture(FSSpecPtr spec)
{
Handle picture = nil;
short refN;
typedef struct
{
long modDate;
short unknown;
ResType type;
short resID;
} pnotType,**pnotHandle;
pnotHandle hpnot;
short oldResF = CurResFile ();
if (-1!=(refN=FSpOpenResFile(spec,fsRdPerm)))
{
OSType fileType = FileTypeOf(spec);
if (fileType=='clpp' || fileType=='SCRN' || fileType=='EPSF')
{
// picture clipping, startup screen, or EPS file. Get 1st PICT resource
picture = Get1IndResource('PICT',1);
}
// Look for QuickTime preview
else if ((hpnot = (pnotHandle)GetIndResource('pnot',1)) &&
GetHandleSize_(hpnot)>= sizeof(pnotType) &&
(*hpnot)->type == 'PICT')
{
picture = Get1Resource('PICT',(*hpnot)->resID);
}
if (picture)
DetachResource(picture);
CloseResFile(refN);
}
UseResFile (oldResF);
return (PicHandle)picture;
}
/**********************************************************************
*
**********************************************************************/
OSErr PeteFileGraphicHit(PETEHandle pte,FGIHandle graphic,long offset,EventRecord *event)
{
FSSpec spec;
Boolean trash = False;
Boolean dragged = False;
PETEDocInfo di;
if (event->what!=nullEvent)
{
if ((*graphic)->pgi.privateType==pgtPictHandle)
{
if (MyWaitMouseMoved(event->where,False))
{
trash = FileGraphicDrag(pte,graphic,offset,event);
dragged = True;
}
}
else if (!GetFileGraphicSpec(graphic,&spec))
{
#ifdef IMAP
// if this spec is inside an IMAP Attachments folder, download it rather than open it.
if ((*graphic)->pgi.privateType == pgtIcon && (*graphic)->u.icon.attachmentStub)
{
if (event->modifiers&optionKey)
{
// fetch all attachments
FetchAllIMAPAttachmentsBySpec(&spec, PETEHandleToMailboxNode(pte), false);
}
else
{
// if no one else is fetching this attachment, go get it now.
if (CanFetchAttachment(&spec)) DownloadIMAPAttachment(&spec, PETEHandleToMailboxNode(pte), false);
}
return (noErr);
}
#endif
if (ClickType==Double || event->modifiers&cmdKey)
{
if (!(*graphic)->urlLink)
{
OpenOtherDoc(&spec,(event->modifiers&controlKey)!=0,false,pte);
ClickType = Single; // prevent other triggers from going
event->modifiers &= ~cmdKey; // ditto
}
}
else if (MyWaitMouseMoved(event->where,False))
{
#ifdef IMAP
if (!((*graphic)->pgi.privateType == pgtIcon && (*graphic)->u.icon.attachmentStub))
{
#endif
trash = FileGraphicDrag(pte,graphic,offset,event);
dragged = True;
#ifdef IMAP
}
#endif IMAP
}
}
}
if (!trash) (*graphic)->pgi.isSelected = True;
if (!dragged)
{
PETEGetDocInfo(PETE,pte,&di);
if (di.docActive && event->what!=nullEvent)
return(noErr);
else
{
event->what = nullEvent;
return(tsmDocNotActiveErr);
}
}
else return(noErr);
}
//#define FINDER_AE
static Boolean DeleteIt;
/**********************************************************************
* FileGraphicDrag - drag a file graphic. returns true if trashed
**********************************************************************/
Boolean FileGraphicDrag(PETEHandle pte,FGIHandle graphic,long offset,EventRecord *event)
{
PETEStyleEntry pse;
Boolean trash = False;
long len;
RgnHandle dragRgn = nil;
FSSpec spec;
DragReference drag;
OSErr err = noErr;
HFSFlavor hfsData;
PromiseHFSFlavor promise;
Point pt;
Boolean finderTricks = HaveScriptableFinder();
FSSpec dropSpec;
WindowPtr theWindow = GetMyWindowWindowPtr ((*PeteExtra(pte))->win);
Boolean comp = GetWindowKind(theWindow)==COMP_WIN ||
GetWindowKind(theWindow)==MESS_WIN
&& MessFlagIsSet(Win2MessH((*PeteExtra(pte))->win),FLAG_OUT);
PETEStyleListHandle styleScrap;
Handle text;
DECLARE_UPP(FileGraphicSend,DragSendData);
INIT_UPP(FileGraphicSend,DragSendData);
GetFileGraphicSpec(graphic,&spec);
dragRgn = NewRgn();
if (dragRgn && !MyNewDrag((*PeteExtra(pte))->win,&drag))
{
BuildHFSFlavor(&hfsData,&spec);
if (comp)
{
// we promise an alias to the file to the finder
// so as to avoid actually moving files when
// dealing with outbound attachments, because the
// user most likely means to detach them when
// dragging to the trash, not really delete them
Zero(promise);
promise.fileType = hfsData.fileType;
promise.fileCreator = hfsData.fileType;
promise.promisedFlavor = SPEC_FLAVOR;
}
// build the fancy region, making sure it includes the mouse
pt = event->where;
GlobalToLocal(&pt);
FileGraphicFancyRgn(pte,graphic,offset,pt,dragRgn);
OutlineRgn(dragRgn,1);
GlobalizeRgn(dragRgn);
// copy our file into dropSpec, so the send data proc can find it
dropSpec = spec;
// add the flavors and set the send proc
if (comp)
{
if (!(err=SetDragSendProc(drag,FileGraphicSendUPP,(void*)&dropSpec)))
if (!(err=AddDragItemFlavor(drag,1,flavorTypePromiseHFS,&promise,sizeof(promise),0)))
err=AddDragItemFlavor(drag,1,SPEC_FLAVOR,nil,0,0);
}
else if ((*graphic)->pgi.privateType==pgtPictHandle)
{
// PICT data
err=AddDragItemFlavor(drag,1,kScrapFlavorTypePicture,LDRef((*graphic)->htmlInfo.pictHandle),GetHandleSize((*graphic)->htmlInfo.pictHandle),0);
UL((*graphic)->htmlInfo.pictHandle);
}
else
// not comp or picthandle
err=AddDragItemFlavor(drag,1,flavorTypeHFS,&hfsData,sizeof(hfsData),0);
if (!err)
{
// add styled text so we can drag and drop graphics within a messsage
if (!(comp && CompHeadCurrent(pte)==ATTACH_HEAD))
if (!(err=PeteGetTextStyleScrap(pte,offset,offset+1,&text,&styleScrap,nil)))
{
if (!(err=AddDragItemFlavor(drag,1,flavorTypeText,LDRef(text),GetHandleSize_(text),flavorSenderOnly+flavorNotSaved)))
err=AddDragItemFlavor(drag,1,kPETEStyleFlavor,LDRef(styleScrap),GetHandleSize_(styleScrap),flavorSenderOnly+flavorNotSaved);
ZapHandle(text);
ZapHandle(styleScrap);
}
}
if (!err)
{
// ok, let's do the drag
err = MyTrackDrag(drag,&MainEvent,dragRgn);
// did the user try to trash the file?
trash = DragTargetWasTrash(drag);
// if it was dragged to the trash, delete it from the window
if (!err)
{
if (trash)
{
PETEGetStyle(PETE,pte,offset,&len,&pse);
PeteDelete(pte,pse.psStartChar,pse.psStartChar+len);
}
}
else if (err==userCanceledErr)
{
// might be an application
err = GetDropSpec(drag,&dropSpec);
if (!err && FileTypeOf(&dropSpec)=='APPL')
err = OpenDocWith(&spec,&dropSpec,false);
}
}
MyDisposeDrag(drag);
}
if (dragRgn) DisposeRgn(dragRgn);
return(trash && !err);
}
/**********************************************************************
* FileGraphicSend - drag data proc for file graphics
**********************************************************************/
pascal OSErr FileGraphicSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag)
{
#pragma unused(theItemRef)
OSErr err=cantGetFlavorErr;
FSSpecPtr spec = (FSSpecPtr)dragSendRefCon;
AEDesc dropLocation;
FSSpec drop;
//FInfo info;
/*
* return the filename
*/
if (flavor==SPEC_FLAVOR)
{
drop = *spec;
NullADList(&dropLocation,nil);
if (!(err=GetDropLocation(drag,&dropLocation)))
if (!(err=GetDropLocationDirectory(&dropLocation,&drop.vRefNum,&drop.parID)))
{
if (SameVRef(drop.vRefNum,spec->vRefNum) && drop.parID==spec->parID)
err = dupFNErr;
else
{
err = MakeAFinderAlias(spec,&drop);
if (!err)
{
err = MySetDragItemFlavorData(drag,1,SPEC_FLAVOR,&drop,sizeof(FSSpec));
*spec = drop;
}
}
}
DisposeADList(&dropLocation,nil);
}
return(err);
}
/**********************************************************************
* GetDropSpec - we dropped it on the Finder
**********************************************************************/
OSErr GetDropSpec(DragReference drag,FSSpecPtr spec)
{
AEDesc drop, newDrop;
OSErr err;
NullADList(&drop,&newDrop,nil);
if (!(err=GetDropLocation(drag,&drop)))
if (!(err=AECoerceDesc(&drop,typeFSS,&newDrop)))
AEGetDescData(&newDrop,spec,sizeof(FSSpec));
DisposeADList(&drop,&newDrop,nil);
return(err);
}
/**********************************************************************
*
**********************************************************************/
void FileGraphicFancyRgn(PETEHandle pte,FGIHandle graphic,long offset,Point pt,RgnHandle rgn)
{
Rect r, iconR, textR;
// get rectangles for text & icon
FileGraphicRect(&r,pte,graphic,offset,true);
if ((*graphic)->pgi.privateType == pgtIcon)
{
FileGraphicIconRect(&iconR,&r);
FileGraphicTextRect(&textR,&r);
}
// does the user want us to make sure a point is in the region?
if ((pt.h || pt.v) && !PtInRect(pt,&iconR) && !PtInRect(pt,&r))
{
pt.h = pt.h - iconR.left - 8;
pt.v = pt.v - iconR.top - 8;
OffsetRect(&r,pt.h,pt.v);
if ((*graphic)->pgi.privateType == pgtIcon)
{
OffsetRect(&iconR,pt.h,pt.v);
OffsetRect(&textR,pt.h,pt.v);
}
}
if ((*graphic)->pgi.privateType != pgtIcon)
{
RectRgn(rgn, &r);
}
else
{
// add the icon's region
IconSuiteToRgn(rgn,&iconR,atAbsoluteCenter,(*GetICache((*graphic)->type,(*graphic)->creator))->cache);
// and the text region
RgnPlusRect(rgn,&textR);
}
}
/**********************************************************************
* BuildHFSFlavor - build data for hfs drag flavor
**********************************************************************/
HFSFlavor *BuildHFSFlavor(HFSFlavor *hfs, FSSpecPtr spec)
{
FInfo info;
FSpGetFInfo(spec,&info);
hfs->fileType = info.fdType;
hfs->fileCreator = info.fdCreator;
hfs->fdFlags = info.fdFlags;
hfs->fileSpec = *spec;
return(hfs);
}
/**********************************************************************
* FileGraphicIconRect - return the rectangle of the icon in a graphic
**********************************************************************/
Rect *FileGraphicIconRect(Rect *iconR, Rect *r)
{
SetRect(iconR,r->left+9,r->top+1,r->left+25,r->top+17);
return(iconR);
}
/**********************************************************************
* FileGraphicTextRect - return the rectangle of the text in a graphic
**********************************************************************/
Rect *FileGraphicTextRect(Rect *textR, Rect *r)
{
Rect tempR;
FileGraphicIconRect(&tempR,r);
SetRect(textR,tempR.right+2,tempR.bottom-GetLeading(SmallSysFontID(),SmallSysFontSize()),r->right,tempR.bottom);
return(textR);
}
/**********************************************************************
* HaveQuickTime - Do we have the indicated version of QuickTime?
**********************************************************************/
Boolean HaveQuickTime(short minVersion)
{
static short qtVersion;
static Boolean haveChecked;
long gestaltResult;
if (!haveChecked)
{
// Check for QuickTime
if (!Gestalt(gestaltQuickTime, &gestaltResult))
{
// Get version
qtVersion = HiWord(gestaltResult);
haveChecked = true;
}
}
return qtVersion >= minVersion; // Make sure we have the minimum version
}
/**********************************************************************
* ImporterDataToHandle - load the graphic image into a handle (if possible)
**********************************************************************/
static void ImporterDataToHandle(FGIHandle hGraphic)
{
unsigned long offset, size;
GraphicsImportComponent importer;
Handle hData;
OSErr err;
QTImageHandle hQTImage = (*hGraphic)->u.image.hQTImage;
importer = GetImageImporter(hGraphic);
if (!importer) return;
if (!GraphicsImportGetDataHandle(importer,&hData) && hData && *hData)
{
(*hQTImage)->hData = hData;
return; // Already have data in a non-purged handle
}
hData = (*hQTImage)->hData;
if (!(err = GraphicsImportGetDataOffsetAndSize(importer, &offset, &size)))
{
// We need to read the entire file, not just the data portion
size += offset;
offset = 0;
if (hData)
{
// Reuse handle
ReallocateHandle(hData,size);
}
else
{
// New handle
hData = (*hQTImage)->hData = NuHTempBetter(size);
}
if (!(err = MemError()))
{
HLock(hData);
if (!(err = GraphicsImportReadData(importer,*hData, offset, size)))
err = GraphicsImportSetDataHandle(importer,hData);
HUnlock(hData);
}
}
if (err)
{
ZapHandle(hData);
(*hQTImage)->hData = nil;
}
else
{
HPurge(hData); // Make it purgeable
}
}
/**********************************************************************
* ValidGraphicFile - check a graphic file to make sure it hasn't been truncated
**********************************************************************/
static CodecType ValidGraphicFile(FGIHandle hGraphic)
{
GraphicsImportComponent importer;
Handle hData;
CodecType cType = 0;
long size;
QTImageHandle hQTImage = (*hGraphic)->u.image.hQTImage;
if (!(importer = GetImageImporter(hGraphic))) return 0;
if ((hData = (*hQTImage)->hData) && *hData && (size = GetHandleSize(hData)))
{
ImageDescriptionHandle hDesc;
if (!GraphicsImportGetImageDescription(importer, &hDesc))
{
unsigned char *pbData;
short *pwData;
cType = (*hDesc)->cType;
pbData = *hData + size - 1; // Point to last byte in file
switch ((*hDesc)->cType)
{
case 'qdrw':
// PICT
pwData = (short *)(*hData+0x200);
if (pwData[5]==0x0011 && pwData[6]==0x02FF)
{
// Version 2. Must end with 0x00FF
if (pbData[0] != 0xFF || pbData[-1] != 0x00)
cType = 0;
}
else
{
// Version 1. Check length and last byte must be 0xFF
if (pwData[0] + 512 > size || pbData[0] != 0xFF)
cType = 0;
}
break;
case 'gif ':
case 'giff':
// GIF file ends with 0x3b end-of-image code. Search for it starting from the end.
// May have false positives due to 0x3b's found elsewhere in file.
cType = 0;
while (size)
{
if (*pbData == 0x3b)
{
cType = (*hDesc)->cType;
break;
}
pbData--;
size--;
}
break;
case 'jpeg':
case 'jpg ':
// JPEG file ends with 0xFFD9 end-of-image marker. Since the marker is unique. Let's search for it
// starting from the end of the file.
cType = 0;
while (size)
{
pbData--;
if (pbData[0] == 0xFF && pbData[1] == 0xD9)
{
cType = (*hDesc)->cType;
break;
}
size--;
}
break;
}
ZapHandle(hDesc);
}
}
return cType;
}
/**********************************************************************
* GraphicsImportComponent - make a graphic handle from a file
**********************************************************************/
static Boolean CanImport(FSSpecPtr spec, GraphicsImportComponent *importer, Component *c, OSType fileType)
{
Boolean result = false;
if (IsPDFFile(spec,fileType) && PrefIsSet(PREF_DONT_DISPLAY_PDF)) return false;
*importer = nil;
*c = nil;
MemCanFail = True; // Don't be purging memory
if (HaveQuickTime(0x0250))
{
// Save time by handling common file types here
// (Suggested by Sam Bushell at Apple)
if (OpenADefaultComponent && (
fileType==kQTFileTypeGIF ||
fileType==kQTFileTypeJPEG ||
fileType==kQTFileTypeTIFF ||
fileType==kQTFileTypeBMP ||
fileType=='BMPp' ||
fileType=='PDF ' ||
fileType==kQTFileTypePNG ||
fileType==kQTFileTypePicture))
{
// Sometimes file types are wrong and if we open the wrong importer,
// QuickTime may crash. Load the file into RAM and verify we have the
// right file type.
Handle hData;
if (!Snarf(spec,&hData,0))
{
Boolean valid = false;
Ptr pData = *hData;
switch (fileType)
{
case kQTFileTypeGIF:
valid = !memcmp(pData,"GIF",3);
break;
case kQTFileTypeJPEG:
valid = !memcmp(pData,"\xFF\xD8\xFF\xE0",4)
&& !memcmp(pData+6,"\x4A\x46\x49\x46\x00",5);
break;
case kQTFileTypeTIFF:
valid = !memcmp(pData,"MM",2) || !memcmp(pData,"II",2);
break;
case 'BMPp':
case kQTFileTypeBMP:
valid = !memcmp(pData,"BM",2);
break;
case kQTFileTypePicture:
valid = !memcmp(pData+522,"\x11\x01",2) || // Version 1
!memcmp(pData+522,"\x00\x11\x02\xFF",4); // Version 2
break;
case kQTFileTypePNG:
valid = !memcmp(pData,"\x89PNG\x0D\x0A\x1a\x0A",8);
break;
case 'PDF ':
valid = !memcmp(pData,"%PDF",4);
break;
}
if (valid && !OpenADefaultComponent(GraphicsImporterComponentType,fileType,importer))
{
GraphicsImportSetDataFile(*importer,spec);
GraphicsImportSetDataHandle(*importer,hData);
}
else
DisposeHandle(hData);
}
}
if (!*importer && CanQTImport(spec,fileType))
// QuickTime 3.0's GetGraphicsImporterForFileWithFlags will speedily tell us if QuickTime can display this file
// OK, so it's not so speedy. Don't use it. (alb)_
// GetGraphicsImporterForFileWithFlags(spec,&importer,kDontUseValidateToFindGraphicsImporter);
GetGraphicsImporterForFile(spec, importer);
if (*importer)
{
ComponentDescription cd;
GetComponentInfo((Component)*importer,&cd,nil,nil,nil);
*c = (Component)cd.componentFlagsMask;
result = true;
}
}
MemCanFail = false;
return result;
}
/**********************************************************************
* CanQTImport - see if QT can import this file as an image
**********************************************************************/
static Boolean CanQTImport(FSSpecPtr spec,OSType fileType)
{
static Handle hFileTypeList,hFileExtList;
GraphicsImportComponent importer = nil;
ComponentDescription looking;
Component cmpIndex;
Str32 sExt;
short i;
if (fileType == 'PDF ' && PrefIsSet(PREF_DONT_DISPLAY_PDF))
// Don't display PDF
return false;
if (!hFileTypeList)
{
// generate file type list
hFileTypeList = NuHandle(0);
cmpIndex = 0;
do
{
Zero(looking);
looking.componentType = 'grip';
if (cmpIndex = FindNextComponent(cmpIndex, &looking))
{
ComponentDescription cd;
GetComponentInfo(cmpIndex,&cd,nil,nil,nil);
if (!(cd.componentFlags&(1<<12)))
{
if (!IsImporterFileType(cd.componentSubType,hFileTypeList))
PtrAndHand(&cd.componentSubType,hFileTypeList,sizeof(OSType));
}
}
} while (cmpIndex);
}
if (IsImporterFileType(fileType,hFileTypeList))
return true; // found file type
// Look for a file extension
for(i=spec->name[0];i;i--)
if (spec->name[i]=='.')
break;
if (!i)
return false; // no file extension
// Get file extension
MakePStr(sExt,&spec->name[i+1],spec->name[0]-i);
MyLowerStr(sExt);
if (!hFileExtList && HaveQuickTime(0x0300) && GraphicsImportGetMIMETypeList)
{
// make file extension list using MIME type list (requires QT 3.0)
hFileExtList = NuHandleClear(sizeof(short));
cmpIndex = 0;
do
{
Zero(looking);
looking.componentType = 'grip';
if (cmpIndex = FindNextComponent(cmpIndex, &looking))
{
ComponentInstance instance;
if (instance=OpenComponent(cmpIndex))
{
QTAtomContainer atom = nil;
QTAtom child;
short i;
GraphicsImportGetMIMETypeList(instance, &atom);
if (atom)
{
for(i=1;child=QTFindChildByIndex(atom,kParentAtomIsContainer,'ext ',i,nil);i++)
{
long dataSize;
Ptr atomData;
Str255 sFileExtList;
Str32 sFileExt;
UPtr spot;
QTGetAtomDataPtr(atom,child,&dataSize,&atomData);
MakePStr(sFileExtList,atomData,dataSize);
spot = sFileExtList+1;
while(PToken(sFileExtList,sFileExt,&spot,","))
{
if (!IsImporterFileExt(hFileExtList,sFileExt))
{
// add to list
PtrAndHand(sFileExt,hFileExtList,*sFileExt+1);
(*(short *)(*hFileExtList))++;
}
}
}
}
CloseComponent(instance);
}
}
} while (cmpIndex);
}
return IsImporterFileExt(hFileExtList,sExt);
}
/**********************************************************************
* IsImporterFileExt - is file extension in list
**********************************************************************/
static Boolean IsImporterFileExt(Handle h,PStr s)
{
short n;
short i;
UPtr p;
if (h && (n=*(short *)(*h)))
{
for (i=1,p=*h+sizeof(short);i<=n;i++,p+=*p+1)
{
if (p[0]==s[0] && !memcmp(p+1,s+1,s[0]))
return true;
}
}
return false;
}
/**********************************************************************
* IsImporterFileType - is file type in list
**********************************************************************/
static Boolean IsImporterFileType(OSType fileType,Handle h)
{
short n = GetHandleSize_(h)/sizeof(OSType);
OSType *pList = (OSType*)*h;
while (n--)
{
if (pList[n]==fileType)
return true;
}
return false;
}
/**********************************************************************
* OpenQTMovie - see if we can open the file as a QuickTime movie
**********************************************************************/
static OSErr OpenQTMovie(FSSpecPtr spec,Movie *theMovie,WindowPtr theWindow,OSType fileType,FGIHandle graphic,Boolean active,Boolean justCheck)
{
short movieResFile;
OSErr err = noErr;
Boolean isMovie = false;
if (fileType == 'PDF ' && PrefIsSet(PREF_DONT_DISPLAY_PDF))
// Don't display PDF
return noMediaHandler;
if (spec && SpecEndsWithExtensionR(spec,NOT_MOVIE_EXTENSIONS))
return noMediaHandler;
if (!HaveQuickTime(0x0100) || !EnterMovies) // Make sure we have QuickTime 1.0
return noMediaHandler;
if (!QTMoviesInited)
{
if (err = EnterMovies()) // Need to do this once
return err;
QTMoviesInited = true;
}
if (fileType==MovieFileType)
isMovie = true;
else
{
// See if movie can be imported
if (HaveQuickTime(0x0300) && GetMovieImporterForDataRef)
{
// QuickTime 3.0's GetMovieImporterForDataRef will speedily tell us if QuickTime can display this file
Boolean tempAlias=false;
AliasHandle alias=nil;
Component importerComponent;
if (graphic && (*graphic)->fileRef)
alias = (*(*graphic)->fileRef)->alias;
if (!alias)
{
// We don't have an alias yet. Make one
if (!(err = NewAliasMinimal(spec, &alias)))
tempAlias = true;
}
if (alias)
{
if (!GetMovieImporterForDataRef(rAliasType,(Handle)alias,kGetMovieImporterDontConsiderGraphicsImporters,&importerComponent))
{
ComponentDescription cd;
if (!GetComponentInfo(importerComponent,&cd,nil,nil,nil))
{
if (cd.componentFlags & movieImportSubTypeIsFileExtension)
{
// (Got the following from Chris Flick at Apple)
// this is for the file extension, so check if it's a component alias and
// if it is, get that resolved component's information
Component aliasedComponent;
if ((long)ResolveComponentAlias != kUnresolvedCFragSymbolAddress)
if (aliasedComponent = ResolveComponentAlias(importerComponent))
GetComponentInfo(aliasedComponent,&cd,nil,nil,nil);
}
if (cd.componentSubType != 'TEXT' || PrefIsSet(PREF_TEXT_MOVIES))
isMovie = true;
}
}
if (tempAlias)
DisposeHandle((Handle)alias);
}
}
else
{
if (TypeIsOnList(FileTypeOf(spec),FILE_MOVIE_LIST_TYPE) != kTOLNot)
isMovie = true;
}
}
// If we just want to know if it's a movie, time to return
if (justCheck) return isMovie ? noErr : noMediaHandler;
if (isMovie && !OpenMovieFile(spec, &movieResFile,fsRdPerm))
{
short movieResID = 0; /* want first movie */
Boolean wasChanged;
SetPort(GetWindowPort(theWindow));
err = NewMovieFromFile (theMovie, movieResFile, &movieResID, nil, active?newMovieActive:0, &wasChanged);
CloseMovieFile(movieResFile);
}
else
err = noMediaHandler;
return err;
}
/**********************************************************************
* PeteMakeFileGraphic - make a graphic handle from a file
**********************************************************************/
void PeteMakeFileGraphic(PETEHandle pte,FGIHandle newGHndl,FSSpecPtr spec,short maxWidth,short ht,Boolean displayInline)
{
FSSpec origSpec = *spec;
long tlid;
Handle suite;
Rect r;
FInfo info;
short width,height;
short oldResFile = CurResFile();
Movie theMovie;
OSErr err;
Str255 longName;
DECLARE_UPP(ImportProgress,ICMProgress);
#if __profile__
// ProfilerSetStatus(true);
#endif
INIT_UPP(ImportProgress,ICMProgress);
PushGWorld();
SetPort(InsurancePort);
SetSmallSysFont();
(*newGHndl)->spec = *spec; // Save original spec
// Resolve if alias
IsAlias(spec,spec);
// Grab long name
if ((*newGHndl)->isEmoticon) *longName = 0;
else if (FSpGetLongName(spec,kTextEncodingUnknown,longName))
PSCopy(longName,spec->name);
(*newGHndl)->pgi.privateType = pgtIcon; // Assume displaying icon
SetRect(&r,0,0,StringWidth(longName)+27,18);
(*newGHndl)->peteID = (*PeteExtra(pte))->id;
(*newGHndl)->displayInline = displayInline;
err = MakeFileRef(newGHndl,spec);
if (!err)
{
if (FSpIsItAFolder(spec))
{
(*newGHndl)->type = kSystemIconsCreator;
(*newGHndl)->creator = kGenericFolderIcon;
}
else
{
err = FSpGetFInfo(spec,&info);
(*newGHndl)->type = info.fdType;
(*newGHndl)->creator = info.fdCreator;
}
}
if (displayInline && !(PrefIsSet(PREF_DONT_DISPLAY_PDF) && IsPDFFile(spec,info.fdType)))
{
if (!err)
{
PicHandle picture=nil;
GraphicsImportComponent importer;
Component c;
QTImageHandle hQTImage;
Boolean allowQTImport;
#if NETSCAPE
Handle hNetscapePlugin;
#endif
// If this is a duplicate of an image we are already displaying,
// reuse it
if (hQTImage = CanReuseImage(spec))
{
(*newGHndl)->u.image.hQTImage = hQTImage;
(*newGHndl)->pgi.privateType = pgtImage;
r = (*hQTImage)->rBounds;
(*hQTImage)->refCount++;
}
// See if it's an image QuickTime can import
else if ((allowQTImport=TryQTImport(info.fdType,info.fdCreator)) && CanImport(spec,&importer,&c,info.fdType))
{
// We're in luck. There is an importer.
CodecType cType;
QTImageHandle hImageInfo = NuHandleClear(sizeof(QTImageInfo));
(*hImageInfo)->importer = importer;
(*hImageInfo)->component = c;
(*hImageInfo)->spec = *spec;
(*hImageInfo)->refCount = 1;
(*hImageInfo)->modDate = FSpModDate(spec);
(*newGHndl)->u.image.hQTImage = hImageInfo;
LL_Queue(gQTImageList,hImageInfo,(QTImageHandle));
ImporterDataToHandle(newGHndl); // Move data into a handle
if (ValidAdImage(pte,(*hImageInfo)->hData) ||
!(cType = ValidGraphicFile(newGHndl)) ||
GraphicsImportGetNaturalBounds(importer, &r))
{
// Not a valid graphic file
DisposeImage(newGHndl);
importer = nil;
}
else
{
MatrixRecord defaultMatrix;
GraphicsImportGetNaturalBounds(importer, &r);
// Setup default display parameters. For example, a fax tiff file
// may have different horizontal and vertical resolutions.
// Adapted from code by Sam Bushell <bushell@apple.com> (QuickTime Engineering)
// Some or all of the following functions may not be available in older
// versions of QuickTime. Does the QuickTime reference doc indicate when
// these versions are available? Nope.
if (SetIdentityMatrix && GraphicsImportGetDefaultMatrix && TransformRect)
{
SetIdentityMatrix(&defaultMatrix);
GraphicsImportGetDefaultMatrix(importer,&defaultMatrix); // ignore errors
TransformRect(&defaultMatrix,&r,nil);
//GraphicsImportSetMatrix(importer,&defaultMatrix);
}
(*hImageInfo)->rBounds = r;
(*newGHndl)->pgi.privateType = pgtImage;
if (GraphicsImportSetProgressProc)
{
// set up a progress function for slow drawing
ICMProgressProcRecord progressInfo;
progressInfo.progressProc = ImportProgressUPP;
GraphicsImportSetProgressProc(importer,&progressInfo);
}
if (((cType == 'gif ' || cType == 'giff') && PrefIsSet(PREF_ANIMATED_GIFS) && HaveQuickTime(0x0300))
|| cType == 'pdf ')
{
// Open as a movie to see if it's an animated GIF or multi-page PDF
if (!OpenQTMovie(spec,&theMovie,GetMyWindowWindowPtr((*PeteExtra(pte))->win),info.fdType,newGHndl,false,false))
{
if (GetMediaSampleCount(GetTrackMedia(GetMovieIndTrack(theMovie,1))) > 1)
{
// it's got multiple frames
Handle hData = (*(*newGHndl)->u.image.hQTImage)->hData;
(*(*newGHndl)->u.image.hQTImage)->hData = nil; // Don't dispose yet
DisposeImage(newGHndl);
importer = nil;
// The movie was opened inactive in case there was only one frame and we didn't need the movie.
// This saves much time.
SetMovieActive(theMovie,true);
SetupMovie(theMovie,newGHndl,&r,cType != 'pdf ',hData);
if (hData)
ZapHandle(hData);
if (cType != 'pdf ')
StartMovie(theMovie);
(*newGHndl)->u.movie.animatedGraphic = true;
}
else
{
// simple GIF. don't need movie
DisposeMovie(theMovie);
}
}
}
}
}
// Check for PICT
else if (info.fdType=='PICT')
{
//
if ( FSpDFSize(spec)>GetRLong(PICT_SPOOL_SIZE))
{
picture = SpecPicture(spec,sizeof(Picture));
(*newGHndl)->u.pict.spool = True;
}
else
{
if (picture = SpecPicture(spec,0))
HPurge((Handle)picture);
}
if (picture)
{
r = (*picture)->picFrame;
(*newGHndl)->pgi.privateType = pgtPICT;
(*newGHndl)->u.pict.picture = picture;
}
}
// How about a QuickTime movie?
else if (allowQTImport && !OpenQTMovie(spec,&theMovie,GetMyWindowWindowPtr((*PeteExtra(pte))->win),info.fdType,newGHndl,true,false))
{
SetupMovie(theMovie,newGHndl,&r,false,nil);
}
#if NETSCAPE
// Well, then how about a Netscape plugin?
else if (hNetscapePlugin = NPluginCheck(spec,&r))
{
(*newGHndl)->u.plugin.hPlugin = hNetscapePlugin;
(*newGHndl)->pgi.privateType = pgtNPlugin;
SetRect(&r, 0, 0, maxWidth, ht);
}
#endif
// Look for previews and PICT resources
else if (picture = SpecResPicture(spec))
{
r = (*picture)->picFrame;
HPurge((Handle)picture);
(*newGHndl)->pgi.privateType = pgtResPict;
(*newGHndl)->u.pict.picture = picture;
}
// If it's an HTML image and the size was specified, use the specified size
if ((*newGHndl)->htmlInfo.width && (*newGHndl)->htmlInfo.height)
SetRect(&r,0,0,(*newGHndl)->htmlInfo.width,(*newGHndl)->htmlInfo.height);
}
}
else
{
// If not displying inline, get rid of any html attributes
(*newGHndl)->htmlInfo.height = 0;
(*newGHndl)->htmlInfo.width = 0;
(*newGHndl)->htmlInfo.hSpace = 0;
(*newGHndl)->htmlInfo.vSpace = 0;
(*newGHndl)->htmlInfo.border = 0;
}
// Get graphic size
if ((*newGHndl)->htmlInfo.height || (*newGHndl)->htmlInfo.width)
{
// Size specified in html IMG tag
width = (*newGHndl)->htmlInfo.width;
height = (*newGHndl)->htmlInfo.height;
}
else
{
// Use image's default size
width = r.right - r.left;
height = r.bottom - r.top;
}
// Add in any html border and/or extra space
(*newGHndl)->width = width + 2*((*newGHndl)->htmlInfo.hSpace + (*newGHndl)->htmlInfo.border);
(*newGHndl)->height = height + 2*((*newGHndl)->htmlInfo.vSpace + (*newGHndl)->htmlInfo.border);
FileGraphicAdjustSize(newGHndl,maxWidth);
PSCopy((*newGHndl)->name,longName);
if (err)
{
// File error. Use missing icon
MissingGraphicIcon(newGHndl,true);
#ifdef MISSING_GRAPHIC_ASSERTS
if (RunType!=Production) Dprintf("\pCan't get file info. Err: %d",err);
#endif
}
else
AdWinGotImage(pte,spec); // See if the ad window needs to know
if ((*newGHndl)->pgi.privateType == pgtIcon)
{
if (info.fdType==MIME_FTYPE &&
!ETLReadTL(spec,&tlid) &&
!(ETLIDToFileIcon(tlid,&suite)))
(*newGHndl)->u.icon.suite = suite;
else
{
IconRef iconRef;
SInt16 label;
if ((*newGHndl)->u.icon.attachmentStub = IsIMAPAttachmentStub(spec))
(*newGHndl)->width += kAttStubIconWd;
if (!GetIconRefFromFile(spec,&iconRef,&label))
(*newGHndl)->u.icon.iconRef = iconRef;
}
}
else if ((*newGHndl)->attachment && displayInline)
// Leave room to display filename below graphic
(*newGHndl)->height += GetLeading(SmallSysFontID(),SmallSysFontSize());
PopGWorld();
UseResFile(oldResFile);
#if __profile__
// ProfilerSetStatus(false);
// ProfilerDump("\pfilegraphic-profile");
// ProfilerClear();
#endif
}
#endif
/**********************************************************************
* DisplayFileName - display attachment file name
**********************************************************************/
static void DisplayFileName(StringPtr s,Rect *rText)
{
RGBColor textColor;
SAVE_STUFF;
if (ThereIsColor)
{
GetRColor(&textColor,TEXT_COLOR);
RGBForeColor(&textColor);
}
SetSmallSysFont();
TextFace(0);
MoveTo(rText->left,rText->bottom-GetDescent(GetPortTextFont(GetQDGlobalsThePort()),GetPortTextSize(GetQDGlobalsThePort())));
EraseRect(rText);
DrawString(s);
REST_STUFF;
}
/**********************************************************************
* SetupMovie - setup to display this movie
**********************************************************************/
static void SetupMovie(Movie theMovie,FGIHandle newGHndl,Rect *r,Boolean hideController,Handle hFile)
{
MovieController aController=nil;
Rect rController;
(*newGHndl)->u.movie.theMovie = theMovie;
GetMovieBox(theMovie, r);
if (aController = NewMovieController(theMovie, r,hideController ? mcTopLeftMovie+mcNotVisible : mcTopLeftMovie))
{
MCGetControllerBoundsRect(aController, &rController);
if (hideController)
{
if (!hFile || (SearchStrHandle("\p\x21\xff\x0bNETSCAPE2.0\x03\x01",hFile,0,false,false,nil)>0)) // See note below
// If Netscape looping extension is found, loop the animation
MCDoAction(aController, mcActionSetLooping, (void*)true); // enable looping
}
else
{
MCDoAction(aController, mcActionSetKeysEnabled, (void*)true); // Process keys
*r = rController;
}
MCDoAction(aController, mcActionSetDragEnabled, (void*)false); // override QT's drag and drop
(*newGHndl)->u.movie.aController = aController;
}
(*newGHndl)->pgi.privateType = pgtMovie;
(*newGHndl)->pgi.wantsEvents = true;
}
/*
Note: Documentation on Netscape's GIF extension for looping animations.
Netscape Navigator has an Application Extension Block that tells Navigator to loop
the entire GIF file. The Netscape block must appear immediately after the global color
table of the logical screen descriptor. Only Navigator 2.0 Beta4 or better willl
recognize this Extension block. The block is 19 bytes long composed off: (note: hexadecimal equivalent supplied
for programmers)
byte 1 : 33 (hex 0x21) GIF Extension code
byte 2 : 255 (hex 0xFF) Application Extension Label
byte 3 : 11 (hex 0x0B) Length of Application Block
(eleven bytes of data to follow)
bytes 4 to 11 : "NETSCAPE"
bytes 12 to 14 : "2.0"
byte 15 : 3 (hex 0x03) Length of Data Sub-Block
(three bytes of data to follow)
byte 16 : 1 (hex 0x01)
bytes 17 to 18 : 0 to 65535, an unsigned integer in
lo-hi byte format. This indicate the
number of iterations the loop should
be executed.
byte 19 : 0 (hex 0x00) a Data Sub-Block Terminator.
*/
/**********************************************************************
* FindPart - find a part
**********************************************************************/
Boolean FindPart(StackHandle parts,uLong hash,PartDesc *pd,Boolean checkCID)
{
if (hash)
{
short item;
for (item=0;item<(*parts)->elCount;item++)
{
StackItem(pd,item,parts);
if (hash == (checkCID ? pd->cid : pd->absURL))
return true;
}
}
return false; // Not found
}
/**********************************************************************
* GetHTMLSpec - get the file spec for an HTML image
**********************************************************************/
static void GetHTMLSpec(FGIHandle graphic,PETEHandle pte,long offset)
{
MyWindowPtr win;
StackHandle partStack;
PartDesc pd;
if (PeteIsValid(pte))
if (win = (*PeteExtra(pte))->win)
if (partStack = (*PeteExtra(pte))->partStack)
// Find the part info for the image
if ((FindPart(partStack,(*graphic)->htmlInfo.cid,&pd,true) || // Try cid
FindPart(partStack,(*graphic)->htmlInfo.withBase,&pd,false) || // Try with base
FindPart(partStack,(*graphic)->htmlInfo.withSource,&pd,false) || // Try with source
FindPart(partStack,(*graphic)->htmlInfo.url,&pd,false))) // Try raw URL
{
// Now get graphic info for the file
Boolean wantsEvents = (*graphic)->pgi.wantsEvents;
PeteMakeFileGraphic(pte,graphic,&pd.spec,GetMaxWidth(pte),RectHi((*PeteExtra(pte))->win->contR)-2*GROW_SIZE,(*graphic)->displayInline);
if (wantsEvents != (*graphic)->pgi.wantsEvents)
// If wantsEvents changes, we need to notify the editor
PETEForceRecalc(PETE,pte,offset,offset+1);
}
}
/**********************************************************************
* MissingGraphicIcon - using the "missing graphic" icon
**********************************************************************/
static void MissingGraphicIcon(FGIHandle graphic,Boolean zapDimensions)
{
Str255 s;
(*graphic)->pgi.privateType = pgtIcon;
if (!gMissingGraphicIconSuite)
GetIconSuite(&gMissingGraphicIconSuite, MISSING_IMAGE_ICON, svAllSmallData);
(*graphic)->u.icon.suite = gMissingGraphicIconSuite;
SetSmallSysFont();
PCopy(s,(*graphic)->name);
(*graphic)->width = 28 + StringWidth(s);
(*graphic)->height = 18;
(*graphic)->noImage = true;
if (zapDimensions)
{
(*graphic)->htmlInfo.width = 0;
(*graphic)->htmlInfo.height = 0;
(*graphic)->htmlInfo.vSpace = 0;
(*graphic)->htmlInfo.hSpace = 0;
(*graphic)->htmlInfo.border = 0;
}
// (*graphic)->htmlInfo.height = (*graphic)->htmlInfo.width = 0;
FileGraphicAdjustSize(graphic,300);
}
/**********************************************************************
* BadGraphicIcon - using the "missing graphic" icon
**********************************************************************/
static void BadGraphicIcon(FGIHandle graphic,Boolean zapDimensions)
{
Str255 s;
(*graphic)->pgi.privateType = pgtIcon;
if (!gBadGraphicIconSuite)
GetIconSuite(&gBadGraphicIconSuite, BAD_IMAGE_ICON, svAllSmallData);
(*graphic)->u.icon.suite = gBadGraphicIconSuite;
SetSmallSysFont();
PCopy(s,(*graphic)->name);
(*graphic)->width = 28 + StringWidth(s);
(*graphic)->height = 18;
(*graphic)->noImage = true;
if (zapDimensions)
{
(*graphic)->htmlInfo.width = 0;
(*graphic)->htmlInfo.height = 0;
(*graphic)->htmlInfo.vSpace = 0;
(*graphic)->htmlInfo.hSpace = 0;
(*graphic)->htmlInfo.border = 0;
}
// (*graphic)->htmlInfo.height = (*graphic)->htmlInfo.width = 0;
FileGraphicAdjustSize(graphic,300);
}
/**********************************************************************
* FileGraphicRect - get the display area for a graphic
**********************************************************************/
static void FileGraphicRect(Rect *r,PETEHandle pte, FGIHandle graphic, long offset, Boolean withHTMLBorder)
{
short hSpace,vSpace,border;
PeteGraphicRect(r,pte,(PETEGraphicInfoHandle)graphic,offset);
if ((*graphic)->centerInWin)
{
Rect rWin = (*PeteExtra(pte))->win->contR;
CenterRectIn(r,&rWin);
}
else
{
border = withHTMLBorder ? 0 : (*graphic)->htmlInfo.border;
hSpace = (*graphic)->htmlInfo.hSpace + border;
vSpace = (*graphic)->htmlInfo.vSpace + border;
if (hSpace || vSpace)
InsetRect(r,hSpace,vSpace);
}
}
/**********************************************************************
* GetFileGraphicSpec - get the spec for the file graphic
**********************************************************************/
static OSErr GetFileGraphicSpec(FGIHandle graphic, FSSpecPtr spec)
{
OSErr err;
GFileRefHandle fileRef;
if (fileRef = (*graphic)->fileRef)
{
if (!(err=SimpleResolveAlias((*fileRef)->alias,spec)))
IsAlias(spec,spec);
}
else err = paramErr;
return err;
}
/**********************************************************************
* IsGraphicFile - is this file a graphic?
**********************************************************************/
Boolean IsGraphicFile(FSSpecPtr spec)
{
Boolean result = false;
PicHandle picture;
OSType fileType;
// Resolve if alias
IsAlias(spec,spec);
fileType = FileTypeOf(spec);
// Text?
if (fileType=='TEXT') return PrefIsSet(PREF_TEXT_MOVIES);
// Is it a filetype that is a graphic? or a movie?
if (TypeIsOnList(fileType,FILE_GRAPHIC_LIST_TYPE) != kTOLNot || IsMovieFile(spec,fileType))
result = true;
// Is there a PICT resource?
else if (picture = SpecResPicture(spec))
{
ZapHandle((Handle)picture);
result = true;
}
// Can QuickTime display it?
else if (CanQTImport(spec,fileType))
{
result = true;
}
return result;
}
/**********************************************************************
* InsertGraphic - insert a graphic that has been cloned (copy & paste, drag & drop)
**********************************************************************/
static OSErr InsertGraphic(PETEHandle pte,FGIHandle graphic)
{
GFileRefHandle fileRef;
FSSpec spec,newSpec;
Str32 s;
OSErr err = noErr;
if ((*graphic)->clone) // Insert clones only, otherwise it has already been inserted
{
if (fileRef = (*graphic)->fileRef)
if (!(err=GetFileGraphicSpec(graphic,&spec)))
{
// Make a duplicate of the file if it is from a different message and the file is in
// the spool folder, the parts folder or the trash
FSSpec folderSpec,parentSpec;
Boolean duplicate = false;
if ((*graphic)->peteID != (*PeteExtra(pte))->id) // Different pete ID means different message
{
if (!SubFolderSpec(SPOOL_FOLDER,&folderSpec) &&
!FSMakeFSSpec(spec.vRefNum,spec.parID,"",&parentSpec) &&
parentSpec.parID == folderSpec.parID && SameVRef(parentSpec.vRefNum,folderSpec.vRefNum))
// Message is in spool folder
duplicate = true;
else if (!SubFolderSpec(PARTS_FOLDER,&folderSpec) &&
spec.parID == folderSpec.parID && SameVRef(spec.vRefNum,folderSpec.vRefNum))
// Message is in parts folder
duplicate = true;
else if (!GetTrashSpec(spec.vRefNum,&folderSpec) &&
spec.parID == folderSpec.parID && SameVRef(spec.vRefNum,folderSpec.vRefNum))
// Message is in the trash
duplicate = true;
}
if (duplicate)
{
// Make duplicate
MyWindowPtr win;
MessHandle messH;
if (win = (*PeteExtra(pte))->win)
if (messH = (MessHandle)GetMyWindowPrivateData(win))
if (!MakeAttSubFolder(messH,SumOf(messH)->uidHash,&newSpec))
{
short suffix = 2;
short saveLen;
// Get a unique file name
PCopy(newSpec.name,spec.name);
saveLen = *newSpec.name;
while (!FSpExists(&newSpec))
{
// No error means that the file/folder exists. Change the file name by adding a numeric suffix
NumToString(suffix++,s);
if (saveLen+*s > 30)
saveLen = 30-*s; // Name was too long
*newSpec.name = saveLen; // Remove any suffix
PCatC(newSpec.name,' ');
PCat(newSpec.name,s);
}
if ((err=FSpDupFile(&newSpec,&spec,false,false)))
FileSystemError(COPY_FAILED,spec.name,err);
spec = newSpec;
}
}
}
// Insert the graphic
if (fileRef && !err)
{
ReleaseFileRef(fileRef); // Not referencing original file ref anymore
PeteMakeFileGraphic(pte,graphic,&spec,(*graphic)->width,(*graphic)->height,(*graphic)->displayInline);
}
else if ((*graphic)->pgi.privateType != pgtHTMLPending && (*graphic)->pgi.privateType != pgtPictHandle)
{
// Error. Use missing graphic icon
MissingGraphicIcon(graphic,false);
#ifdef MISSING_GRAPHIC_ASSERTS
{
Str32 sHex;
Long2Hex(sHex,(long)fileRef);
if (RunType!=Production) Dprintf("\pInsert graphic error. Err: %d, FileRef: %p",err,sHex);
}
#endif
}
(*graphic)->clone = false;
}
return err;
}
/**********************************************************************
* UnrefFileREf - no longer reference this file ref. Dispose if count goes to zero
**********************************************************************/
static void ReleaseFileRef(GFileRefHandle fileRef)
{
if (fileRef && --(*fileRef)->count == 0)
{
// No more references to file. Dispose of file reference.
ZapHandle((*fileRef)->alias);
ZapHandle(fileRef);
}
}
/**********************************************************************
* GetMaxWidth - return the maximum width of a graphic for a given PETE handle
**********************************************************************/
static short GetMaxWidth(PETEHandle pte)
{
Rect r;
PeteRect(pte,&r);
return RectWi(r);
}
/**********************************************************************
* DisplayFetchGraphics - should we display the "GetGraphics" button in this window?
**********************************************************************/
Boolean DisplayGetGraphics(MyWindowPtr win)
{
Boolean result = win==gGetGraphicsWin;
gGetGraphicsWin = nil;
return result;
}
/**********************************************************************
* URLAccessIsInstalled - is URL Access installed?
**********************************************************************/
static Boolean URLAccessIsInstalled(void)
{
#ifdef URLACCESS
return URLAccessAvailable() && PrefIsSet(PREF_URL_ACCESS);
#else
return false;
#endif
}
/**********************************************************************
* MakeURLFileName - make a filespec for a URL.
* Name format: host/hash.extension
**********************************************************************/
static void MakeURLFileSpec(FGIHandle graphic,PETEHandle pte,FSSpec *spec)
{
StringHandle absURL = (*graphic)->htmlInfo.absURL;
Str255 s;
Zero(*spec);
if (absURL)
{
PCopyTrim(s,*absURL,sizeof(Str255));
GetCacheSpec(s,spec,PeteIsInAdWindow(pte));
}
}
/**********************************************************************
* MakeURLFileName - make a filespec for a URL.
* Name format: host/hash.extension
**********************************************************************/
void GetCacheSpec(StringPtr sURL,FSSpec *spec,Boolean useAdCache)
{
Str255 sHost;
Str32 sExt;
PStr pExt,name;
long hash;
short maxHostLen;
// Get cache folder location
SubFolderSpec(useAdCache?AD_FOLDER_NAME:CACHE_FOLDER,spec);
MyLowerStr(sURL);
hash = Hash(sURL);
// Find any extension
*sExt = 0;
if (pExt = PRIndex(sURL,'.'))
{
long lenExt;
lenExt = *sURL - (pExt - sURL) + 1;
if (lenExt < 8)
MakePStr(sExt,pExt,lenExt);
}
// Get host
ParseURL(sURL,nil,sHost,nil);
// Format the name
maxHostLen = 29-8-*sExt;
if (*sHost>maxHostLen)
*sHost = maxHostLen;
name = spec->name;
PCopy(name,sHost);
name[++name[0]] = '/';
PXCat(name,hash);
PCat(name,sExt);
}
/**********************************************************************
* DisposeURLRef - dispose of URL ref
**********************************************************************/
static void DisposeURLRef(FGIHandle graphic)
{
GetURLDataHandle hURLData,hDuplicate,hNextDup;
if (hURLData = (GetURLDataHandle)(*graphic)->pURLInfo)
{
for(hDuplicate=(*hURLData)->duplicate;hDuplicate;hDuplicate=hNextDup)
{
// Dispose of any duplicates
hNextDup = (*hDuplicate)->duplicate;
ZapHandle(hDuplicate);
}
#ifdef URLACCESS
if (URLAccessIsInstalled())
URLDisposeReference((*hURLData)->urlRef);
#endif
(*graphic)->pURLInfo = nil;
if (!(*hURLData)->reschedule)
gDownloadCount--;
LL_Remove(gURLDataList,hURLData,(GetURLDataHandle));
ZapHandle(hURLData);
}
}
#ifdef URLACCESS
/**********************************************************************
* URLNotifyProc - download URL notification proc
**********************************************************************/
static pascal OSStatus URLNotifyProc(void *userContext, URLEvent event, URLCallbackInfo *callbackInfo)
{
OSStatus urlError;
GetURLDataHandle hData;
hData = userContext;
switch (event)
{
case kURLCompletedEvent:
(*hData)->finished = true; // Mark it for disposal by idle function
URLGetError(callbackInfo->urlRef,&urlError);
(*hData)->urlError = urlError;
break;
case kURLDownloadingEvent:
// Starting download. Temporarily change file type to "in progress"
TweakFileType(&(*hData)->spec,kFileInProcessType,CREATOR);
break;
}
return noErr;
}
#endif
/**********************************************************************
* BeginDownload - begin the download
**********************************************************************/
static OSErr BeginDownload(GetURLDataHandle hURLData,URLReference *urlRef)
{
OSErr err;
char URLcstring[256];
FSSpec spec;
short oldResFile = CurResFile();
PCopyTrim(URLcstring,*(*(*hURLData)->graphic)->htmlInfo.absURL,sizeof(Str255)); // Remove any leading or trailing spaces from URL
p2cstr(URLcstring);
#ifdef URLACCESS
if (URLAccessIsInstalled())
{
err = URLNewReference(URLcstring,urlRef);
if (!err)
{
FSSpec spec = (*hURLData)->spec;
DECLARE_UPP(URLNotifyProc,URLNotify);
INIT_UPP(URLNotifyProc,URLNotify);
MoveHHi((Handle)hURLData);
HLock((Handle)hURLData); // Need to lock down the spec because URL Access doesn't save it
err = URLOpen(*urlRef,&(*hURLData)->spec,kURLReplaceExistingFlag+kURLExpandFileFlag/*+kURLDisplayAuthFlag*/,
URLNotifyProcUPP,kURLDownloadingMask+kURLErrorOccurredEventMask+kURLCompletedEventMask,hURLData);
if (err)
URLDisposeReference(*urlRef);
}
}
else
#endif
{
spec = (*hURLData)->spec;
err = DownloadURL(URLcstring,&spec,(long)hURLData,DownloadURLFinished,(long *)urlRef,nil);
}
if (!err)
{
gDownloadCount++;
(*hURLData)->urlRef = *urlRef;
}
UseResFile(oldResFile); // URL Access is bad about changing curresfile
return err;
}
/**********************************************************************
* DownloadURLFinished - we have finished downloading a URL (not with URL Access)
**********************************************************************/
static void DownloadURLFinished(long refCon,OSErr err,DownloadInfo *info)
{
GetURLDataHandle hURLData = (GetURLDataHandle)refCon;
(*hURLData)->finished = true; // Mark it for disposal by idle function
(*hURLData)->urlError = err;
}
/**********************************************************************
* GetHTMLGraphic - get a graphic from a URL, may need to download
**********************************************************************/
static Boolean GetHTMLGraphic(PETEHandle pte,FGIHandle graphic,Boolean allowDownload,Boolean forceDownload)
{
URLReference urlRef;
OSStatus err = noErr;
GetURLData URLData;
GetURLDataHandle hURLData,hDuplicate=nil;
Zero(URLData);
MakeURLFileSpec(graphic,pte,&URLData.spec);
if (!*URLData.spec.name)
return false; // Unable to generate a spec
(*graphic)->wasDownloaded = true; // Indicate this graphic came from a URL
// Determine if we are already downloading this file
for (hURLData=gURLDataList;hURLData;hURLData=(*hURLData)->next)
{
if (EqualString(URLData.spec.name,(*hURLData)->spec.name,true,true))
{
hDuplicate=hURLData;
break;
}
}
if (!hDuplicate && !FSpExists(&URLData.spec))
{
// File is already in cache.
CInfoPBRec hfi;
HGetCatInfo(URLData.spec.vRefNum,URLData.spec.parID,URLData.spec.name,&hfi);
if (hfi.hFileInfo.ioFlFndrInfo.fdType==kFileInProcessType && hfi.hFileInfo.ioFlFndrInfo.fdCreator==CREATOR)
{
// File not completely downloaded.
// Delete and start over.
FSpDelete(&URLData.spec);
}
else
{
// Use the file in the cache
FSpTouch(&URLData.spec); // Set file's mod date to test for least-recently-used cache purge
PeteMakeFileGraphic(pte,graphic,&URLData.spec,GetMaxWidth(pte),(*graphic)->height,(*graphic)->displayInline);
return true;
}
}
// Don't try a download if setting is disabled or we aren't allowed to, unless the user has hit the fetch button.
if (!forceDownload && (!allowDownload || !DownloadURLOK()))
return false;
// Don't download certain obnoxious graphics
if (!forceDownload && HTMLGraphicIsObnoxious(graphic))
{
BadGraphicIcon(graphic,true);
return false;
}
URLData.graphic = graphic;
URLData.pte = pte;
if (hURLData = NuHandle(sizeof(GetURLData)))
{
BMD(&URLData,*hURLData,sizeof(GetURLData));
if (hDuplicate)
{
// Add to list of duplicates
(*hURLData)->duplicate = (*hDuplicate)->duplicate;
(*hDuplicate)->duplicate = hURLData;
}
else
{
// Not a duplicate
if (gDownloadCount < kMaxDownloads)
err = BeginDownload(hURLData,&urlRef);
else
// Too many downloads. Try again later
(*hURLData)->reschedule = true;
if (!err)
{
(*graphic)->pURLInfo = (Ptr)hURLData;
LL_Push(gURLDataList,hURLData); // Add to URL list
}
else
{
ZapHandle(hURLData);
return false;
}
}
}
return true;
}
Boolean HTMLGraphicIsObnoxious(FGIHandle graphic)
{
short width = (*graphic)->htmlInfo.width;
short height = (*graphic)->htmlInfo.height;
Str255 s;
if (width && height && width*height < GetRLong(HTML_MIN_IMAGE_SIZE)) return true;
ComposeString(s,"\p%dx%d",width,height);
if (StrIsItemFromRes(s,HTML_BAD_IMAGE_DIMENSIONS,",")) return true;
return false;
}
/**********************************************************************
* CheckCacheLimit - purge the cache if necessary
**********************************************************************/
static void CheckCacheLimit(void)
{
long sizeLimit,cacheSize,delSize,thisSize;
Str32 name;
FSSpec spec;
sizeLimit = GetRLong(GRAPHICS_CACHE_MAX);
if (!sizeLimit)
sizeLimit = 5 K; // Default 5M
sizeLimit *= 1 K; // Convert KBytes to bytes
SubFolderSpec(CACHE_FOLDER,&spec);
do
{
CInfoPBRec hfi;
unsigned long delDate;
hfi.hFileInfo.ioNamePtr = name;
hfi.hFileInfo.ioFDirIndex = 0;
delDate = 0;
cacheSize = 0;
while (!DirIterate(spec.vRefNum,spec.parID,&hfi))
{
thisSize = hfi.hFileInfo.ioFlPyLen + hfi.hFileInfo.ioFlRPyLen;
cacheSize += thisSize; // Add in this file size
if (!delDate || hfi.hFileInfo.ioFlMdDat < delDate)
{
delDate = hfi.hFileInfo.ioFlMdDat;
delSize = thisSize;
PCopy(spec.name,name); // save name of least-recently-used
}
}
if (cacheSize > sizeLimit)
{
// The cache is too big. Delete the least-recently-used file
if (FSpDelete(&spec)) return; // On error, return. Can't get rid of this file!
cacheSize -= delSize;
}
} while (cacheSize > sizeLimit);
}
/**********************************************************************
* DisplayDownload - display this downloaded graphic
**********************************************************************/
static void DisplayDownload(GetURLDataHandle hURLData)
{
FGIHandle graphic = (*hURLData)->graphic;
PETEHandle pte = (*hURLData)->pte;
// is the pte stale?
if (PETEDocCheck(PETE,pte,true,true))
{
ASSERT(0);
return;
}
LDRef(hURLData);
PeteMakeFileGraphic(pte,graphic,&(*hURLData)->spec,GetMaxWidth(pte),(*graphic)->height,(*graphic)->displayInline);
(*(*hURLData)->graphic)->wasDownloaded = true; // Indicate this graphic came from a URL
UL(hURLData);
}
/**********************************************************************
* GraphicDownloadIdle - check for completed downloads and ones needing scheduling
**********************************************************************/
void GraphicDownloadIdle(void)
{
GetURLDataHandle hURLData,hURLNext,hDuplicate;
OSErr err;
PETEHandle redisplayPTE = nil;
if (!gURLDataList) return; // Nothing in the list
if (URLAccessIsInstalled())
URLIdle(); // Give some time to URL Access
// Search for completed downloads
for (hURLData=gURLDataList;hURLData;hURLData=hURLNext)
{
hURLNext=(*hURLData)->next;
if ((*hURLData)->finished)
{
// Done with this one
if (!(*hURLData)->urlError)
{
// Cause this image to be displayed
DisplayDownload(hURLData);
redisplayPTE = (*hURLData)->pte;
// Cause all duplicates to be displayed also
for(hDuplicate=(*hURLData)->duplicate;hDuplicate;hDuplicate=(*hDuplicate)->duplicate)
DisplayDownload(hDuplicate);
CheckCacheLimit();
DisposeURLRef((*hURLData)->graphic);
}
#if 0
// not going to worry about this for now --alb 2/19/99
else if ((*hURLData)->urlError==kURLAuthenticationError)
{
// Get name and password for authentication
}
#endif
else
{
// Error, dispose of file.
URLDeleteFile(hURLData);
DisposeURLRef((*hURLData)->graphic);
}
}
}
// Search for items needing to be scheduled
for (hURLData=gURLDataList;hURLData && gDownloadCount < kMaxDownloads;hURLData=(*hURLData)->next)
{
if ((*hURLData)->reschedule)
{
URLReference urlRef;
if (err = BeginDownload(hURLData,&urlRef))
{
// Error. Dispose of it next time.
(*hURLData)->finished = true;
(*hURLData)->urlError = err;
}
(*hURLData)->reschedule = false;
}
}
if (redisplayPTE)
{
MyWindowPtr win = (*PeteExtra(redisplayPTE))->win;
Boolean noUpdates = win->noUpdates;
// Don't do updates while doing Pete recalc
win->noUpdates = true;
PeteRecalc(redisplayPTE);
win->noUpdates = noUpdates;
}
}
/**********************************************************************
* GraphicsImportComponent - reopen importer if it has been closed
**********************************************************************/
static GraphicsImportComponent GetImageImporter(FGIHandle graphic)
{
QTImageHandle hQTImage = (*graphic)->u.image.hQTImage;
GraphicsImportComponent importer = (*hQTImage)->importer;
FSSpec spec;
if (!importer)
{
// It was closed. Reopen importer component.
if (importer = OpenComponent((*hQTImage)->component))
{
(*hQTImage)->importer = importer;
if ((*hQTImage)->hData)
GraphicsImportSetDataHandle(importer,(*hQTImage)->hData);
else
{
GetFileGraphicSpec(graphic,&spec);
GraphicsImportSetDataFile(importer,&spec);
}
}
}
return importer;
}
/**********************************************************************
* ImportProgress - progress function for importer
**********************************************************************/
static pascal OSErr ImportProgress(short message, Fixed completeness, long refcon)
{
CycleBalls();
return noErr;
}
/**********************************************************************
* URLDeleteFile - close (if open) and delete a file
**********************************************************************/
static void URLDeleteFile(GetURLDataHandle hURLData)
{
CInfoPBRec hfi;
FSSpec spec = (*hURLData)->spec;
if (!AFSpGetCatInfo(&spec,&spec,&hfi))
{
if (hfi.hFileInfo.ioFRefNum)
// URL Access may have left file open
FSClose(hfi.hFileInfo.ioFRefNum);
FSpDelete(&spec);
}
}
/**********************************************************************
* DisposeImage - we're done with this image
**********************************************************************/
static void DisposeImage(FGIHandle graphic)
{
QTImageHandle hQTImage = (*graphic)->u.image.hQTImage;
if (hQTImage)
if (--(*hQTImage)->refCount == 0)
{
// Done with this component
CloseComponent((*hQTImage)->importer);
if ((*hQTImage)->hData)
ZapHandle((*hQTImage)->hData);
LL_Remove(gQTImageList,hQTImage,(QTImageHandle));
ZapHandle(hQTImage);
(*graphic)->u.image.hQTImage = nil;
}
}
/**********************************************************************
* CanReuseImage - search for a duplicate of this image that we can reuse
**********************************************************************/
static QTImageHandle CanReuseImage(FSSpecPtr spec)
{
QTImageHandle hQTImage;
uLong modDate = FSpModDate(spec);
for(hQTImage=gQTImageList;hQTImage;hQTImage=(*hQTImage)->next)
if ((modDate == (*hQTImage)->modDate) && SameSpec(spec,&(*hQTImage)->spec))
return hQTImage; // Found!
return nil; // not found
}
/************************************************************************
* GetPNGTransColor - if it's a PNG graphic, return any transparency color
************************************************************************/
Boolean GetPNGTransColor(GraphicsImportComponent importer,FSSpec *spec,RGBColor *transColor)
{
Boolean found = false;
if (GetGraphicType(importer)==kPNGCodecType)
{
Handle hData;
if (!Snarf(spec,&hData,0))
{
found = FindPNGTransparency(hData,transColor);
ZapHandle(hData);
}
}
return found;
}
/************************************************************************
* SetupPNGTransparency - if it's a PNG graphic, enable transparency color
************************************************************************/
void SetupPNGTransparency(GraphicsImportComponent importer,FSSpec *spec)
{
// QuickTime doesn't yet support transparency colors for
// PNG files. We'll set it up ourselves
if (GetGraphicType(importer)==kPNGCodecType)
{
Handle hData;
if (!Snarf(spec,&hData,0))
{
SetupPNGTransparencyLo(importer,hData);
ZapHandle(hData);
}
}
}
/************************************************************************
* SetupPNGTransparencyLo - enable PNG transparency color
************************************************************************/
void SetupPNGTransparencyLo(GraphicsImportComponent importer,Handle hData)
{
static RGBColor color;
if (FindPNGTransparency(hData,&color))
GraphicsImportSetGraphicsMode(importer,transparent,&color);
}
/************************************************************************
* FindPNGTransparency - search for PNG transparency color
************************************************************************/
static Boolean FindPNGTransparency(Handle hData,RGBColor *transColor)
{
// Search for transparency and palette chunks
long length;
Ptr pAlpha;
Boolean found = false;
if (pAlpha = FindPNGChunk('tRNS',hData,&length))
{
unsigned short i;
Ptr pColor;
for(i=0;i<length;i++)
{
// Find an alpha value of zero.
// That's our transparency color index
if (pAlpha[i]==0)
{
found = true;
break;
}
}
if (found)
if (pColor = FindPNGChunk('PLTE',hData,&length))
{
pColor += 3*i;
transColor->red = pColor[0]<<8;
transColor->green = pColor[1]<<8;
transColor->blue =pColor[2]<<8;;
}
}
return found;
}
/************************************************************************
* FindPNGChunk - find a chunk in a PNG file
************************************************************************/
static Ptr FindPNGChunk(uLong chunkType,Handle hData,long *pLength)
{
uLong *pChunk;
uLong length;
Ptr pEnd = GetHandleSize(hData) + *hData;
pChunk = (uLong*)*hData;
// Verify PNG signature
if (*pChunk++ == 0x89504e47)
if (*pChunk++ == 0x0d0a1a0a)
{
while(pChunk<pEnd)
{
length = pChunk[0];
if (pChunk[1]==chunkType)
{
// Found it
*pLength = length;
return (Ptr)(pChunk+2); // Point to chunk data
}
pChunk = (uLong*)((Ptr)pChunk+length+12);
}
}
return nil; // Not found
}
/************************************************************************
* GetGraphicType - what type is this graphic importer?
************************************************************************/
static CodecType GetGraphicType(GraphicsImportComponent importer)
{
ImageDescriptionHandle hDesc;
CodecType cType = 0;
if (!GraphicsImportGetImageDescription(importer, &hDesc))
{
cType = (*hDesc)->cType;
ZapHandle(hDesc);
}
return cType;
}
/************************************************************************
* TryQTImport - do we want to allow QuickTime to attempt to import this file?
************************************************************************/
static Boolean TryQTImport(OSType fType,OSType fCreator)
{
short fIdx;
short count;
if (fType=='PDF ' && PrefIsSet(PREF_DONT_DISPLAY_PDF)) return false;
// Check "yes" list first
count = CountResources(kQTYesList);
for (fIdx=1;fIdx<=count;fIdx++)
if (FindQTImporter(kQTYesList,fIdx,fType,fCreator))
return true; // Attempt to import
// Now check "no" list
count = CountResources(kQTNoList);
for (fIdx=1;fIdx<=count;fIdx++)
if (FindQTImporter(kQTNoList,fIdx,fType,fCreator))
return false; // Don't import
return true; // Go ahead and try
}
/************************************************************************
* FindQTImporter - look up type and creator in a resource list
************************************************************************/
static Boolean FindQTImporter(ResType rListResType,short rIdx,OSType fType,OSType fCreator)
{
typedef struct { OSType fType; OSType fCreator; } TypeAndCreator;
typedef struct
{
short count;
TypeAndCreator list[];
} **QTImportListHandle;
QTImportListHandle hList;
if (hList = (QTImportListHandle)GetIndResource(rListResType,rIdx))
{
short i;
TypeAndCreator *pList = (*hList)->list;
for(i=0;i<(*hList)->count;i++,pList++)
{
if (pList->fType==fType || pList->fType=='****')
if (pList->fCreator==fCreator || pList->fCreator=='****')
return true; // found
}
}
return false;
}