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