/* 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 #ifdef URLACCESS #include #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-upTickswhen!=oldWhen) { int dx = event->where.h - downSpot.h; int dy = event->where.v - downSpot.v; if (ABS(dx)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É BMD(*ghSpoolBuf+gSpoolBufIdx,dataPtr+dataOffset,remainingBytes); dataOffset += remainingBytes; count -= remainingBytes; } // É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 (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;ired = 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(pChunkcType; 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; }