home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / HyperCard / TIFFWindow1.1 / TIFFWindow.c < prev    next >
C/C++ Source or Header  |  1993-06-12  |  28KB  |  1,234 lines

  1. /*
  2.  * This software is copyright 1992 by Robert Morris.
  3.  * You may freely redistribute this software as shareware
  4.  * if you do so in the same form as you got it. If you find
  5.  * this software useful, please send $12 to:
  6.  *   Robert Morris
  7.  *   P.O. Box 1044
  8.  *   Harvard Square Station
  9.  *   Cambridge, MA 02238
  10.  *   ecognome@aol.com
  11.  * If you incorporate any of this software in any kind of
  12.  * commercial product, please send $2 per copy distributed
  13.  * to the above address.
  14.  */
  15.  
  16. /*
  17.  * TIFFWindow file, windowStyle, visible, depth, customPalette
  18.  *
  19.  * If no file is supplied, or if the file is "", TIFFWindow prompts the user.
  20.  * It uses hypercard's file search path.
  21.  *
  22.  * WindowStyle has no effect right now.
  23.  *
  24.  * Visible controls whether the window starts out visible.
  25.  *
  26.  * Depth sets the depth of the off-screen buffer; such a buffer greatly improves
  27.  * redraw and scroll speed. 0 means no buffer
  28.  * at all: all drawing is done from the disk. Only 0, 16 and 32 are universal; 1, 2, 4,
  29.  * and 8 only work well if the TIFF image is actually that depth.
  30.  * TIFFWindow will reduce the off-screen depth to the depth of the TIFF image.
  31.  * If there is not enough memory, it won't use an off-screen buffer.
  32.  *
  33.  * CustomPalette is true, a cpmt resource number, or false. The default is true,
  34.  * which causes TIFFWindow to call the Picture Utilities to compute a reasonable
  35.  * palette for the deepest CLUT screen at the time when TIFFWindow is called.
  36.  * A number indicates the resource number of a custom sampling method.
  37.  * False means use the color map in the TIFF file, or none if there was none.
  38.  * Computing a palette can take a few seconds. Custom palettes only work with
  39.  * an off-screen buffer.
  40.  *
  41.  * If the result is not empty, it contains an error message. TIFFWindow usually
  42.  * produces an error message if it doesn't understand the TIFF file format, but
  43.  * occasionally it doesn't find this out until too late, in which case it will
  44.  * display an empty window.
  45.  *
  46.  * The dithering property controls dithering. This only works well from an off-screen
  47.  * graphics buffer.
  48.  *
  49.  * The scrollbars property controls whether scrollbars are visible.
  50.  *
  51.  * The picturewidth and pictureheight properties yield the size of the image
  52.  * in pixels.
  53.  *
  54.  * The scale property, a number, controls zooming of the image. Each displayed
  55.  * pixel represents a square 2^scale pixels on a side in the original. So, for
  56.  * instance, a scale of 0 (the default) means no change, 1 means double the image
  57.  * size, and -1 means halve it.
  58.  *
  59.  * The picturedepth property yields the pixel depth of the TIFF image.
  60.  *
  61.  * If the user clicks on the picture, TIFFWindow sends to HyperTalk messages,
  62.  * one when the user presses the mouse button, and one when he or she releases
  63.  * the button. The messages are MouseDownInPicture and MouseUpInPicture. The
  64.  * messages have two arguments, the window name and the image coordinates.
  65.  */
  66.  
  67. #include <HyperXCmd.h>
  68. #include <stdlib.h>
  69. #include "SetUpA4.h"
  70. #include <string.h>
  71. #include <ctype.h>
  72. #include "tiffinfo.h"
  73. #include <QDOffscreen.h>
  74. #include <Palette.h>
  75. #include <PictUtil.h>
  76.  
  77. typedef struct thunk{
  78.     TIFFPtr ti;
  79.     CGrafPtr gw; /* off-screen graphics world */
  80.     ControlHandle hScroll, vScroll;
  81.     int ref; /* keep the file open */
  82.     Boolean active;
  83.     
  84.     /* properties */
  85.     Boolean visible;
  86.     Boolean scrollbars;
  87.     int depth; /* desired off-screen depth, or 0 for none */
  88.     double scale;
  89.     Boolean dither;
  90.     Boolean customPalette;
  91.     Boolean customMethod;
  92. } ThunkRec, **Thunk;
  93.  
  94. Thunk th;
  95. WindowPtr w;
  96. XCmdPtr xptr;
  97.  
  98. #define SBWIDTH        15
  99.  
  100. void InitPalette(void);
  101. void DisposeThunk(Thunk);
  102. Handle HStr(char *);
  103. int pstrcmp(StringPtr, StringPtr);
  104. int SetUpWindow(StringPtr);
  105. void UpdateWindow(void);
  106. void DoContent(EventRecord *theEvent);
  107. void MyGrowWindow(Point);
  108. int cmp(char *, char *);
  109. void FileOfPath(StringPtr path, StringPtr file);
  110. OSErr CopyToGWorld();
  111. int FixDepth(int depth);
  112. Boolean SetProp(StringPtr prop, Handle h);
  113. Handle GetProp(StringPtr prop);
  114. int DeepestCLUTScreen();
  115. long myatoi(char *);
  116. Point MaxWindowSize();
  117. void MySizeWindow(short width, short height);
  118. double myatof(char *s);
  119. double OldScaleToNew(int scale);
  120. int NewScaleToOld(double scale);
  121. void myftoa(double d, char *s);
  122. void myitoa(long i, char *a);
  123.  
  124. /* there was no GetGWorldPixMap() before System 7 */
  125. #define GWPM(gw) (HasSystem7() ? GetGWorldPixMap(gw) : (gw)->portPixMap)
  126.  
  127. pascal void
  128. main(paramPtr)
  129. XCmdPtr paramPtr;
  130. {
  131.     XWEventInfoPtr ei;
  132.     WindowPtr whichWindow;
  133.     GrafPtr oport;
  134.     int theChar, err, ref, vref, findCode;
  135.     Point pt;
  136.     Rect r;
  137.     char **h;
  138.     StringPtr prop;
  139.     Str255 tmp, fullname, ttl;
  140.     PaletteHandle pltt;
  141.     Thunk oth;
  142.     WindowPtr ow;
  143.     XCmdPtr oxptr;
  144.     SFReply sf;
  145.     SFTypeList types;
  146.     OSType type;
  147.     CGrafPtr agw;
  148.     
  149.     RememberA0();
  150.     SetUpA4();
  151.     
  152.     /* save old values */
  153.     oth = th;
  154.     ow = w;
  155.     oxptr = xptr;
  156.     
  157.     xptr = paramPtr;
  158.     
  159.     if(paramPtr->paramCount == -1){
  160.         /* it's a window event */
  161.         ei = (XWEventInfoPtr) (paramPtr->params[0]);
  162.         w = ei->eventWindow;
  163.         th = (Thunk) GetWRefCon(w);
  164.         if(th == 0){
  165.             paramPtr->passFlag = TRUE;
  166.             goto out;
  167.         }
  168.         
  169.         GetPort(&oport);
  170.         SetPort(w);
  171.         
  172.         switch(ei->event.what){
  173.         case xOpenEvt:
  174.             XWAllowReEntrancy(paramPtr, w, TRUE, TRUE);
  175.             if((*th)->visible)
  176.                 SelectWindow(w);    /* if I don't do this, it's not highlighted... */
  177.             break;
  178.         case xCloseEvt:
  179.             XWAllowReEntrancy(paramPtr, w, FALSE, FALSE);
  180.             DisposeThunk(th);
  181.             SetWRefCon(w, 0L);
  182.             if(HasColorQD() && (pltt = GetPalette(w))){
  183.                 SetPalette(w, 0L, FALSE);
  184.                 DisposePalette(pltt);
  185.             }
  186.             paramPtr->passFlag = TRUE;    /* yes, we're willing to close */
  187.             break;
  188.         case xCursorWithin:
  189.             paramPtr->passFlag = TRUE;    /* ask HyperCard for an arrow cursor */
  190.             break;
  191.         case xSetPropEvt:
  192.             paramPtr->passFlag = !SetProp((StringPtr)ei->eventParams[0],
  193.                                           (Handle)ei->eventParams[1]);
  194.             break;
  195.         case xGetPropEvt:
  196.             h = GetProp((StringPtr) (ei->eventParams[0]));
  197.             if(h){
  198.                 ei->eventResult = h;
  199.                 paramPtr->passFlag = FALSE;
  200.             } else {
  201.                 paramPtr->passFlag = TRUE;    /* ask HyperCard to handle it for us */
  202.                 ei->eventResult = (char **) 0;
  203.             }
  204.             break;
  205.         case xGiveUpEditEvt:
  206.             EndXWEdit(paramPtr, w);
  207.             break;
  208.         case xEditUndo:
  209.             /* what should I do here? */
  210.             break;
  211.         case xEditCut:
  212.             break;
  213.         case xEditCopy:
  214.             break;
  215.         case xEditPaste:
  216.             break;
  217.         case xEditClear:
  218.             break;
  219.         case nullEvent:
  220.             break;
  221.         case activateEvt:
  222.             if((*th)->scrollbars){
  223.                 if (ei->event.modifiers & activeFlag) {
  224.                     ShowControl((*th)->vScroll);
  225.                     ShowControl((*th)->hScroll);
  226.                     (*th)->active = TRUE;
  227.                 } else {
  228.                     HideControl((*th)->vScroll);
  229.                     HideControl((*th)->hScroll);
  230.                     (*th)->active = FALSE;
  231.                 }
  232.                 DrawGrowIcon(w);
  233.             }
  234.             break;
  235.         case mouseDown:
  236.             switch(findCode = FindWindow(ei->event.where, &whichWindow)){
  237.             case inGoAway:
  238.                 if(TrackGoAway(w, ei->event.where)){
  239.                     CloseXWindow(paramPtr, w);    /* ask HyperCard to close us */
  240.                 }
  241.                 break;
  242.             case inDrag:
  243.                 /*
  244.                  * ask HyperCard to deal with moving the window. this is
  245.                  * an undocumented feature as of B39.
  246.                  */
  247.                 paramPtr->passFlag = TRUE;
  248.                 break;
  249.             case inContent:
  250.                 DoContent(&(ei->event));
  251.                 break;
  252.             case inGrow:
  253.                 MyGrowWindow(ei->event.where);
  254.                 break;
  255.             case inZoomIn:
  256.             case inZoomOut:
  257.                 if(TrackBox(w, ei->event.where, findCode) == TRUE){
  258.                     ZoomWindow(w, findCode, TRUE);
  259.                     FixScrollBars();
  260.                 }
  261.                 break;
  262.             }
  263.             break;
  264.         case keyDown:
  265.         case autoKey: 
  266.             theChar = ei->event.message & charCodeMask;
  267.             if ((ei->event.modifiers & cmdKey) != 0){  
  268.             } else {
  269.             }
  270.             break;
  271.         case updateEvt:
  272.             UpdateWindow();
  273.             break;
  274.         }
  275.         
  276.         SetPort(oport);
  277.     } else {
  278.         th = (Thunk) NewHandle((long) sizeof(**th));
  279.         if(th == 0){
  280.             paramPtr->returnValue = HStr("out of memory");
  281.             goto out;
  282.         }
  283.         (*th)->ref = -1;
  284.         (*th)->ti = 0;
  285.         (*th)->gw = 0;
  286.         (*th)->hScroll = (*th)->vScroll = 0;
  287.         (*th)->active = FALSE;
  288.         (*th)->visible = TRUE;
  289.         (*th)->scrollbars = TRUE;
  290.         (*th)->depth = 32;
  291.         (*th)->dither = FALSE;
  292.         (*th)->scale = 1;
  293.         (*th)->customPalette = TRUE;
  294.         (*th)->customMethod = systemMethod;
  295.         
  296.         if(paramPtr->paramCount > 2 && cmp(*(paramPtr->params[2]), "true") != 1)
  297.             (*th)->visible = FALSE;
  298.             
  299.         if(paramPtr->paramCount > 3)
  300.             (*th)->depth = myatoi(*(paramPtr->params[3]));
  301.             
  302.         if(paramPtr->paramCount > 4){
  303.             if(cmp(*(paramPtr->params[4]), "true")){
  304.                 (*th)->customPalette = TRUE;
  305.                 (*th)->customMethod = systemMethod;
  306.             } else if(cmp(*(paramPtr->params[4]), "false")){
  307.                 (*th)->customPalette = FALSE;
  308.             } else {
  309.                 (*th)->customPalette = TRUE;
  310.                 (*th)->customMethod = myatoi(*(paramPtr->params[4]));
  311.             }
  312.         }
  313.         
  314.         if(paramPtr->paramCount > 0 && (*(paramPtr->params[0]))[0] != '\0'){
  315.             strncpy((char *)tmp, *(paramPtr->params[0]), sizeof(tmp) - 1);
  316.             CtoPstr(tmp);
  317.             types[0] = 'TIFF';
  318.             type = 'TIFF';
  319.             if(GetFilePath(xptr, tmp, -1, types, TRUE, &type, fullname) != TRUE){
  320.                 paramPtr->returnValue = HStr("cannot find file");
  321.                 goto out;
  322.             }
  323.             err = FSOpen(fullname, 0, &ref);
  324.             FileOfPath(fullname, ttl);
  325.         } else {
  326.             pt.h = pt.v = 75;
  327.             types[0] = 'TIFF';
  328.             SFGetFile(pt, "\pWhat TIFF file?", 0L, 1, types, 0L, &sf);
  329.             if(sf.good == 0){
  330.                 paramPtr->returnValue = HStr("cancel");
  331.                 goto out;
  332.             }
  333.             err = FSOpen(sf.fName, sf.vRefNum, &ref);
  334.             FileOfPath(sf.fName, ttl);
  335.         }
  336.         if(err != 0){
  337.             DisposeThunk(th);
  338.             paramPtr->returnValue = HStr("cannot open file");
  339.             goto out;
  340.         }
  341.         (*th)->ref = ref;
  342.         
  343.         (*th)->ti = ScanTIFF((*th)->ref);
  344.         if((*th)->ti == 0 || GetTIFFError((*th)->ti, tmp) != 0){
  345.             DisposeThunk(th);
  346.             paramPtr->returnValue = HStr("cannot understand TIFF file");
  347.             goto out;
  348.         }
  349.         
  350.         if(TIFFDrawable((*th)->ti) == FALSE){
  351.             DisposeThunk(th);
  352.             paramPtr->returnValue = HStr("Cannot draw the picture on this Macintosh.");
  353.             goto out;
  354.         }
  355.         
  356.         /* attempt to set up an off-screen copy */
  357.         if((*th)->depth > 0 && HasQD32()){
  358.             /* eliminate excess depth */
  359.             if((*th)->ti->samplesPerPixel == 1 &&
  360.                (*th)->ti->bitsPerSample[0] < (*th)->depth){
  361.                 (*th)->depth = FixDepth((*th)->ti->bitsPerSample[0]);
  362.             }
  363.             
  364.             r.left = 0;
  365.             r.right = (*th)->ti->imageWidth;
  366.             r.top = 0;
  367.             r.bottom = (*th)->ti->imageLength;
  368.             
  369.             err = NewGWorld(&agw, (*th)->depth, &r, (*th)->ti->colorMap, 0L, 0);
  370.             if(err == 0){
  371.                 (*th)->gw = agw;
  372.                 if((err = CopyToGWorld()) != 0){
  373.                     if(GetTIFFError((*th)->ti, tmp) != 0 && tmp[0] != 0)
  374.                         paramPtr->returnValue = PasToZero(xptr, tmp);
  375.                     else if(err == memFullErr)
  376.                         paramPtr->returnValue = HStr("out of memory");
  377.                     else
  378.                         paramPtr->returnValue = HStr("could not understand TIFF file");
  379.                     DisposeThunk(th);
  380.                     goto out;
  381.                 }
  382.                 FSClose((*th)->ref);
  383.                 (*th)->ref = -1;
  384.             }
  385.         }
  386.         
  387.         if(SetUpWindow(ttl) < 0){
  388.             DisposeThunk(th);
  389.             paramPtr->returnValue = HStr("window creation failed");
  390.             goto out;
  391.         }
  392.     
  393.         InitPalette();
  394.     }
  395.     
  396. out:
  397.     /* restore old globals in case of reentrance */
  398.     xptr = oxptr;
  399.     w = ow;
  400.     th = oth;
  401.     
  402.     RestoreA4();
  403. }
  404.  
  405. OSErr
  406. CopyToGWorld()
  407. {
  408.     CGrafPtr oldPort;
  409.     GDHandle oldGD;
  410.     PixMapHandle pm;
  411.     OSErr err;
  412.     Rect r;
  413.  
  414.     GetGWorld(&oldPort, &oldGD);
  415.     
  416.     SetGWorld((*th)->gw, 0);
  417.     pm = GWPM((*th)->gw);
  418.     if(LockPixels(pm) != TRUE){
  419.         SetGWorld(oldPort, oldGD);
  420.         return(memFullErr);
  421.     }
  422.  
  423.     r = (*th)->gw->portRect;
  424.     err = DrawTIFF((*th)->ref, (*th)->ti, r, r, 0);
  425.  
  426.     UnlockPixels(pm);
  427.     SetGWorld(oldPort, oldGD);
  428.     
  429.     return(err);
  430. }
  431.  
  432. void
  433. DisposeThunk(Thunk th)
  434. {
  435.     if(th){
  436.         if((*th)->ti)
  437.             DisposeTIFF((*th)->ti);
  438.         (*th)->ti = 0;
  439.         if((*th)->ref != -1)
  440.             FSClose((*th)->ref);
  441.         (*th)->ref = -1;
  442.         if((*th)->gw)
  443.             DisposeGWorld((*th)->gw);
  444.         (*th)->gw = 0;
  445.         DisposHandle(th);
  446.     }
  447. }
  448.  
  449. /*
  450.  * returns < 0 if there was a problem.
  451.  */
  452. int
  453. SetUpWindow(StringPtr ttl)
  454. {
  455.     Rect wr, sr, r;
  456.     GDHandle gd;
  457.     int iheight, iwidth, wwidth, wheight;
  458.     GrafPtr wmport;
  459.     
  460.     /*
  461.      * Choose a window size: try to show the whole image, but stay on
  462.      * the screen.
  463.      */
  464.     if(HasColorQD()){
  465.         gd = GetMainDevice();
  466.         sr = (*gd)->gdRect;
  467.     } else {
  468.         GetWMgrPort(&wmport);
  469.         sr = wmport->portRect;
  470.     }
  471.     
  472.     iwidth = (*th)->ti->imageWidth;
  473.     iheight = (*th)->ti->imageLength;
  474.     
  475. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  476.     wwidth = MIN(iwidth+SBWIDTH, sr.right - sr.left - 40);
  477.     wheight = MIN(iheight+SBWIDTH, sr.bottom - sr.top - 70);
  478.  
  479.     wr.left = ((sr.right - sr.left) / 2) - (wwidth / 2);
  480.     wr.right = wr.left + wwidth;
  481.     wr.top = ((sr.bottom - sr.top - 30) / 2) - (wheight / 2) + 30;
  482.     wr.bottom = wr.top + wheight;
  483.     
  484.     w = NewXWindow(xptr,
  485.                     &wr,
  486.                     ttl,             /* title */
  487.                     (*th)->visible,
  488.                     documentProc,
  489.                     HasColorQD(),    /* color? */
  490.                     FALSE);            /* floating? */
  491.                         
  492.                          
  493.     if(w == 0)
  494.         return(-1);
  495.         
  496.     SetWRefCon(w, (long)th);
  497.         
  498.     r.right = wr.right;
  499.     r.left = r.right - SBWIDTH;
  500.     r.top = wr.top;
  501.     r.bottom = wr.bottom;
  502.     (*th)->vScroll = NewControl(w, &r, "", TRUE, 0, 0, 1, scrollBarProc, 0L);
  503.         
  504.     r.left = wr.left;
  505.     r.right = wr.right;
  506.     r.bottom = wr.bottom;
  507.     r.top = r.bottom - SBWIDTH;
  508.     (*th)->hScroll = NewControl(w, &r, "", TRUE, 0, 0, 1, scrollBarProc, 0L);
  509.     
  510.     FixScrollBars();
  511.     
  512.     return(0);
  513. }
  514.  
  515. /*
  516.  * the window has changed size, and the scroll bars will have to be
  517.  * resized, and their max and min values re-set.
  518.  */
  519. FixScrollBars()
  520. {
  521.     Rect wr, r;
  522.     int oh, ov, iheight, iwidth, wheight, wwidth;
  523.     
  524.     wr = w->portRect;
  525.     
  526.     iheight = (*th)->ti->imageLength;
  527.     iwidth = (*th)->ti->imageWidth;
  528.     
  529.     HideControl((*th)->vScroll);
  530.     HideControl((*th)->hScroll);
  531.     
  532.     oh = GetCtlValue((*th)->hScroll);
  533.     ov = GetCtlValue((*th)->vScroll);
  534.     
  535. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  536.     wheight = UnScaleIt(wr.bottom - wr.top - ((*th)->scrollbars ? SBWIDTH : 0));
  537.     SetCtlMin((*th)->vScroll, 0);
  538.     SetCtlMax((*th)->vScroll, MAX(0, iheight - wheight));
  539.     
  540.     wwidth = UnScaleIt(wr.right - wr.left - ((*th)->scrollbars ? SBWIDTH : 0));
  541.     SetCtlMin((*th)->hScroll, 0);
  542.     SetCtlMax((*th)->hScroll, MAX(0, iwidth - wwidth));
  543.     
  544.     if(oh != GetCtlValue((*th)->hScroll) || ov != GetCtlValue((*th)->vScroll)){
  545.         InvalRect(&(w->portRect));
  546.     }
  547.     
  548.     MoveControl((*th)->vScroll, wr.right - SBWIDTH, wr.top - 1);
  549.     SizeControl((*th)->vScroll, SBWIDTH+1, wr.bottom - wr.top - SBWIDTH + 2);
  550.     
  551.     MoveControl((*th)->hScroll, wr.left - 1, wr.bottom - SBWIDTH);
  552.     SizeControl((*th)->hScroll, wr.right - wr.left - SBWIDTH + 2, SBWIDTH+1);
  553.     
  554.     if((*th)->scrollbars){
  555.         if((*th)->active){
  556.             ShowControl((*th)->vScroll);
  557.             ShowControl((*th)->hScroll);
  558.             DrawGrowIcon(w);
  559.         }
  560.     }
  561. }
  562.  
  563. int
  564. UnScaleIt(x)
  565. int x;
  566. {
  567.     return(x / (*th)->scale);
  568. }
  569.  
  570. int
  571. ScaleIt(x)
  572. int x;
  573. {
  574.     return(x * (*th)->scale);
  575. }
  576.  
  577. double
  578. OldScaleToNew(int scale)
  579. {
  580.     double x;
  581.     int i;
  582.     
  583.     x = 1;
  584.     /* calculate how to scale the picture */
  585.     if(scale > 0){
  586.         for(i = 0; i < scale; i++)
  587.             x *= 2;
  588.     } else if(scale < 0){
  589.         for(i = 0; i > scale; --i)
  590.             x /= 2;
  591.     }
  592.     
  593.     return(x);
  594. }
  595.  
  596. int
  597. NewScaleToOld(double scale)
  598. {
  599.     long i, x, sign;
  600.     
  601.     if(scale <= 0.0)
  602.         return(0);
  603.         
  604.     if(scale < 1.0){
  605.         scale = 1 / scale;
  606.         sign = -1;
  607.     } else
  608.         sign = 1;
  609.     
  610.     x = 1;
  611.     for(i = 0; x < scale && i < 32; i++, x *= 2)
  612.         ;
  613.     return(i * sign);
  614. }
  615.  
  616. void
  617. UpdateWindow()
  618. {
  619.     BeginUpdate( w );
  620.     
  621.     DrawControls( w );
  622.     if((*th)->scrollbars)
  623.         DrawGrowIcon( w );
  624.     ReDraw();
  625.     
  626.     EndUpdate( w );
  627. }
  628.  
  629. ReDraw()
  630. {
  631.     Rect ir, dr, gr;
  632.     int xoff, yoff, dh, dv;
  633.     PixMapHandle pm;
  634.     
  635.     xoff = GetCtlValue((*th)->hScroll);
  636.     yoff = GetCtlValue((*th)->vScroll);
  637.     
  638.     ir.left = xoff;
  639.     ir.top = yoff;
  640.     ir.right = (*th)->ti->imageWidth;
  641.     ir.bottom = (*th)->ti->imageLength;
  642.     
  643.     dr = w->portRect;
  644.     dr.right = dr.left + ScaleIt(ir.right - ir.left);
  645.     dr.bottom = dr.top + ScaleIt(ir.bottom - ir.top);
  646.         
  647.     dh = dr.right - (w->portRect.right - ((*th)->scrollbars ? SBWIDTH : 0));
  648.     if(dh > 0){
  649.         dr.right -= dh;
  650.         ir.right -= UnScaleIt(dh);
  651.     }
  652.     dv = dr.bottom - (w->portRect.bottom - ((*th)->scrollbars ? SBWIDTH : 0));
  653.     if(dv > 0){
  654.         dr.bottom -= dv;
  655.         ir.bottom -= UnScaleIt(dv);
  656.     }
  657.  
  658.     if((*th)->gw){
  659.         pm = GWPM((*th)->gw);
  660.         LockPixels(pm);
  661.         CopyBits(*pm, &(w->portBits), &ir, &dr,
  662.                  (*th)->dither ? 64 : srcCopy, 0L);
  663.         UnlockPixels(pm);
  664.     } else if((*th)->ref != -1){
  665.         DrawTIFF((*th)->ref, (*th)->ti, ir, dr, (*th)->dither);
  666.     }
  667.     
  668.     /* now gray out invalid parts of the window */
  669.     gr.left = dr.right;
  670.     gr.right = w->portRect.right - ((*th)->scrollbars ? SBWIDTH : 0);
  671.     gr.top = 0;
  672.     gr.bottom = w->portRect.bottom - ((*th)->scrollbars ? SBWIDTH : 0);
  673.     FillRect(&gr, &gray);
  674.     
  675.     gr.left = 0;
  676.     gr.top = dr.bottom;
  677.     FillRect(&gr, &gray);
  678. }
  679.  
  680. /*
  681.  * Give the window an appropriate palette. Such a palette should have a separate
  682.  * set of colors for each different depth CLUT device the window intersects.
  683.  * This is possible using inhibited entries, but would be expensive since it
  684.  * would require multiple calls to GetPixMapInfo(). And the palette might have
  685.  * to be recomputed whenever the user moves the window onto a different screen
  686.  * or changes the depth with the Monitors control panel.
  687.  *
  688.  * It would be nice if the color sampling methods used by GetPixMapInfo() returned
  689.  * a palette with the most important colors first, or with multiple sets of
  690.  * inhibited colors; such a palette would be useful at multiple depths.
  691.  *
  692.  * This routine computes a palette for the deepest CLUT device existing at the
  693.  * time the TIFF window was created. This isn't perfect; the user might put
  694.  * the window on a shallower screen, or change the depth.
  695.  *
  696.  * InitPalette the color map in the TIFF file if it can. Otherwise it uses the
  697.  * Picture Utilities. The sampling method cpmt resource number can be set with
  698.  * the 5th argument to TIFFWindow.
  699.  */
  700. void
  701. InitPalette()
  702. {
  703.     CTabHandle cth;
  704.     PaletteHandle pltt = 0, opltt;
  705.     PictInfo pi;
  706.     PixMapHandle pm;
  707.     OSErr err;
  708.     int deepest, size;
  709.         
  710.     if(HasColorQD() == 0)
  711.         return;
  712.         
  713.     deepest = DeepestCLUTScreen();
  714.     size = DepthToSize(deepest);
  715.     
  716.     /*
  717.      * Don't bother if there are no color CLUT screens.
  718.      */
  719.     if(deepest <= 1)
  720.         return;
  721.     
  722.     if((cth = (*th)->ti->colorMap) && ((*cth)->ctSize+1) <= size){
  723.         /*
  724.          * Use the color map in the TIFF file if it isn't too large.
  725.          */
  726.         pltt = NewPalette((*cth)->ctSize + 1, cth, pmTolerant, 0);
  727.     } else if(((*th)->ti->samplesPerPixel > 1 || (*th)->ti->bitsPerSample[0] > 1) &&
  728.               (*th)->customPalette && (*th)->gw && HasSystem7()){
  729.         pm = GWPM((*th)->gw);
  730.         if(LockPixels(pm) == TRUE){
  731.             err = GetPixMapInfo(pm, &pi, returnPalette|suppressBlackAndWhite,
  732.                                 size - 2, (*th)->customMethod, 0);
  733.             if(err == 0){
  734.                 pltt = pi.thePalette;
  735.                 pi.thePalette = 0;
  736.             }
  737.             UnlockPixels(pm);
  738.         }
  739.     }
  740.     
  741.     if(pltt)
  742.         SetPalette(w, pltt, TRUE);
  743. }
  744.  
  745. /*
  746.  * Return the depth of the deepest CLUT screen, or zero if there are no
  747.  * CLUT screens.
  748.  */
  749. int
  750. DeepestCLUTScreen()
  751. {
  752.     GDHandle gd;
  753.     int depth = 0;
  754.     PixMapHandle pm;
  755.     
  756.     gd = GetDeviceList();
  757.     while(gd){
  758.         if(TestDeviceAttribute(gd, screenDevice) == TRUE &&
  759.            TestDeviceAttribute(gd, screenActive) == TRUE &&
  760.            (*gd)->gdType == 0){
  761.             pm = (*gd)->gdPMap;
  762.             if(pm && (*pm)->cmpCount == 1 && (*pm)->cmpSize > depth)
  763.                 depth = (*pm)->cmpSize;
  764.         }
  765.         gd = GetNextDevice(gd);
  766.     }
  767.     
  768.     return(depth);
  769. }
  770.  
  771. int
  772. DepthToSize(int depth)
  773. {
  774.     switch(depth){
  775.     case 1: return(2);
  776.     case 2: return(4);
  777.     case 4: return(16);
  778.     default: return(256);
  779.     }
  780. }
  781.  
  782. pascal void
  783. TrackIt(ctl, part)
  784. ControlHandle ctl;
  785. int part;
  786. {
  787.     int amount, pagesize;
  788.     
  789.     if(ctl == (*th)->hScroll)
  790.         pagesize = UnScaleIt((w->portRect.right - w->portRect.left) / 2);
  791.     else
  792.         pagesize = UnScaleIt((w->portRect.bottom - w->portRect.top) / 2);
  793.         
  794.     switch (part){
  795.     case inUpButton: 
  796.         amount = -4;
  797.         break;
  798.     case inDownButton: 
  799.         amount = 4;
  800.         break;
  801.     case inPageUp: 
  802.         amount = -pagesize;
  803.         break;
  804.     case inPageDown: 
  805.         amount = pagesize;
  806.         break;
  807.     default:
  808.         return;
  809.     }
  810.     
  811.     SetCtlValue(ctl, GetCtlValue(ctl) + amount);
  812.     ReDraw();
  813. }
  814.  
  815. void
  816. DoContent(EventRecord    *theEvent)
  817. {
  818.     Point pt;
  819.     ControlHandle ctl;
  820.     int part, x, y;
  821.     char tmp[256], ttl[256], tmp1[64];
  822.     
  823.     if (w != FrontDocWindow(xptr)){
  824.         SelectWindow(w);
  825.         return;
  826.     }
  827.     
  828.     pt = theEvent->where;
  829.     GlobalToLocal(&pt);
  830.     part = FindControl(pt, w, &ctl);
  831.     if(part == inThumb){
  832.         part = TrackControl(ctl, pt, 0L);
  833.         if(part)
  834.             ReDraw();
  835.     } else if(part != 0){
  836.         part = TrackControl(ctl, pt, TrackIt);
  837.     } else {
  838.         x = UnScaleIt(pt.h - w->portRect.left) + GetCtlValue((*th)->hScroll);
  839.         y = UnScaleIt(pt.v - w->portRect.top) + GetCtlValue((*th)->vScroll);
  840.         
  841.         GetWTitle(w, ttl);
  842.         PtoCstr(ttl);
  843.         
  844.         strcpy(tmp, "mouseDownInPicture \"");
  845.         strcat(tmp, ttl);
  846.         strcat(tmp, "\", \"");
  847.         myitoa(x, tmp1);
  848.         strcat(tmp, tmp1);
  849.         strcat(tmp, ",");
  850.         myitoa(y, tmp1);
  851.         strcat(tmp, tmp1);
  852.         strcat(tmp, "\"");
  853.         CtoPstr(tmp);
  854.         
  855.         SendCardMessage(xptr, (StringPtr)tmp);
  856.                 
  857.         while(StillDown() == TRUE)
  858.             ;
  859.             
  860.         GetMouse(&pt);
  861.         x = UnScaleIt(pt.h - w->portRect.left) + GetCtlValue((*th)->hScroll);
  862.         y = UnScaleIt(pt.v - w->portRect.top) + GetCtlValue((*th)->vScroll);
  863.         
  864.         strcpy(tmp, "mouseUpInPicture \"");
  865.         strcat(tmp, ttl);
  866.         strcat(tmp, "\", \"");
  867.         myitoa(x, tmp1);
  868.         strcat(tmp, tmp1);
  869.         strcat(tmp, ",");
  870.         myitoa(y, tmp1);
  871.         strcat(tmp, tmp1);
  872.         strcat(tmp, "\"");
  873.         CtoPstr(tmp);
  874.         
  875.         SendCardMessage(xptr, (StringPtr)tmp);
  876.     }
  877. }
  878.  
  879. Point
  880. MaxWindowSize()
  881. {
  882.     Point pt;
  883.     int iwidth, iheight;
  884.     
  885.     iwidth = (*th)->ti->imageWidth;
  886.     iheight = (*th)->ti->imageLength;
  887.     
  888.     pt.h = ScaleIt(iwidth) +
  889.              ((*th)->scrollbars ? SBWIDTH : 0);
  890.     pt.v = ScaleIt(iheight) +
  891.              ((*th)->scrollbars ? SBWIDTH : 0);
  892.              
  893.     return(pt);
  894. }
  895.  
  896. void
  897. MyGrowWindow(p)
  898. Point p;
  899. {
  900.     long    theResult;
  901.     Rect     r;
  902.     Point max;
  903.     
  904.     max = MaxWindowSize();
  905.  
  906.     SetRect(&r, 80, 80, max.h + 1, max.v + 1);
  907.     theResult = GrowWindow( w, p, &r );
  908.     if (theResult == 0)
  909.         return;
  910.     
  911.     MySizeWindow(LoWord(theResult), HiWord(theResult));
  912. }
  913.  
  914. /*
  915.  * change the size of the window, making sure it's not bigger than the
  916.  * displayed image, and making sure the old grow box is destroyed.
  917.  */
  918. void
  919. MySizeWindow(short width, short height)
  920. {
  921.     Point max;
  922.     Rect r;
  923.     
  924.     max = MaxWindowSize();
  925.     if(width > max.h)
  926.         width = max.h;
  927.     if(height > max.v)
  928.         height = max.v;
  929.         
  930.     if((*th)->scrollbars){
  931.         /* we want to make sure the old grow box is redrawn */
  932.         r = w->portRect;
  933.         r.left = r.right - SBWIDTH;
  934.         r.top = r.bottom - SBWIDTH;
  935.         InvalRect(&r);
  936.         EraseRect(&r);
  937.     }
  938.  
  939.     SizeWindow( w, width, height, TRUE);
  940.     FixScrollBars();
  941. }
  942.  
  943. /* case-insensitive pascal string compare */
  944. int
  945. pstrcmp(StringPtr a, StringPtr b)
  946. {
  947.     int alen, blen;
  948.     
  949.     if(EqualString(a, b, FALSE, FALSE) == TRUE)
  950.         return(0);
  951.     return(1);
  952. }
  953.  
  954. /* case-insensitive C string compare; returns 1 if equal */
  955. int
  956. cmp(char *a, char *b)
  957. {
  958.     int i, ac, bc;
  959.     
  960.     for(i = 0; a[i] && b[i]; i++){
  961.         ac = a[i];
  962.         if(isupper(ac))
  963.             ac = tolower(ac);
  964.         bc = b[i];
  965.         if(isupper(bc))
  966.             bc = tolower(bc);
  967.         if(ac != bc)
  968.             return(0);
  969.     }
  970.     if(a[i] == b[i])
  971.         return(1);
  972.     return(0);
  973. }
  974.  
  975. Handle
  976. HStr(str)
  977. char *str;
  978. {
  979.     Handle newHndl;
  980.     
  981.     newHndl = (Handle) NewHandle((long) strlen(str) + 1);
  982.     if(newHndl == 0)
  983.         return(0);
  984.     strcpy((char *) (*newHndl), str);
  985.     return(newHndl);
  986. }
  987.  
  988. void
  989. FileOfPath(StringPtr path, StringPtr file)
  990. {
  991.     int i, len;
  992.     
  993.     /* search backwards for the last : */
  994.     for(i = path[0] - 1; i >= 0; --i){
  995.         if(path[i+1] == ':'){
  996.             len = path[0] - i - 1;
  997.             strncpy((char *) file+1, (char *)path + i + 2, len);
  998.             file[0] = len;
  999.             return;
  1000.         }
  1001.     }
  1002.     
  1003.     strncpy((char *)file+1, (char *)path+1, path[0]);
  1004.     file[0] = path[0];
  1005. }
  1006.  
  1007. /*
  1008.  * Take whatever depth the user provided and round it up to the nearest
  1009.  * legal QuickDraw depth.
  1010.  */
  1011. int
  1012. FixDepth(int depth)
  1013. {
  1014.     if(depth < 1)
  1015.         return(0);
  1016.     if(depth <= 1)
  1017.         return(1);
  1018.     if(depth <= 2)
  1019.         return(2);
  1020.     if(depth <= 4)
  1021.         return(4);
  1022.     if(depth <= 8)
  1023.         return(8);
  1024.     if(depth <= 16)
  1025.         return(16);
  1026.     return(32);
  1027. }
  1028.  
  1029. /*
  1030.  * Returns TRUE if it could handle the property.
  1031.  */
  1032. Boolean
  1033. SetProp(StringPtr prop, Handle h)
  1034. {
  1035.     Boolean handled = TRUE;
  1036.     Rect r;
  1037.     Point pt;
  1038.     Str255 tmp;
  1039.     int ndither;
  1040.     double nscale;
  1041.     Handle lh;
  1042.     
  1043.     MoveHHi(h);
  1044.     HLock(h);
  1045.     
  1046.     if(pstrcmp(prop, "\pdithering") == 0){
  1047.         ndither = cmp(*h, "true");
  1048.         if(HasQD32() == 0)
  1049.             ndither = FALSE;
  1050.         if(ndither != (*th)->dither){
  1051.             (*th)->dither = ndither;
  1052.             ReDraw();
  1053.         }
  1054.     } else if(pstrcmp(prop, "\pscrollbars") == 0){
  1055.         int nscrollbars = cmp(*h, "true");
  1056.         if((*th)->scrollbars != nscrollbars){
  1057.             (*th)->scrollbars = nscrollbars;
  1058.             r = w->portRect;
  1059.             if(nscrollbars == TRUE){
  1060.                 /* make room for them */
  1061.                 SizeWindow(w, r.right - r.left + SBWIDTH,
  1062.                            r.bottom - r.top + SBWIDTH, TRUE);
  1063.             } else {
  1064.                 /* get rid of them */
  1065.                 SizeWindow(w, r.right - r.left - SBWIDTH,
  1066.                            r.bottom - r.top - SBWIDTH, TRUE);
  1067.             }
  1068.             FixScrollBars();
  1069.         }
  1070.     } else if(pstrcmp(prop, "\pscale") == 0){
  1071.         nscale = OldScaleToNew(myatoi(*h));
  1072.         if(nscale != (*th)->scale){
  1073.             (*th)->scale = nscale;
  1074.             FixScrollBars();
  1075.             ReDraw();
  1076.         }
  1077.     } else if(pstrcmp(prop, "\pnscale") == 0){
  1078.         nscale = myatof(*h);
  1079.         if(nscale != (*th)->scale && nscale > 0.001){
  1080.             (*th)->scale = nscale;
  1081.             FixScrollBars();
  1082.             ReDraw();
  1083.         }
  1084.     } else if(pstrcmp(prop, "\pscroll") == 0){
  1085.         ZeroToPas(xptr, *h, tmp);
  1086.         StrToPoint(xptr, tmp, &pt);
  1087.         SetCtlValue((*th)->hScroll, pt.h);
  1088.         SetCtlValue((*th)->vScroll, pt.v);
  1089.         ReDraw();
  1090.     } else if(pstrcmp(prop, "\prect") == 0 || pstrcmp(prop, "\prectangle") == 0){
  1091.         ZeroToPas(xptr, *h, tmp);
  1092.         StrToRect(xptr, tmp, &r);
  1093.         
  1094.         lh = EvalExpr(xptr, "\pthe loc of the card window");
  1095.         ZeroToPas(xptr, *lh, tmp);
  1096.         StrToPoint(xptr, tmp, &pt);
  1097.         DisposHandle(h);
  1098.         
  1099.         OffsetRect(&r, pt.h, pt.v);
  1100.             
  1101.         MoveWindow(w, r.left, r.top, FALSE);
  1102.         MySizeWindow(r.right - r.left, r.bottom - r.top);
  1103.     } else if(pstrcmp(prop, "\pglobalrect") == 0){
  1104.         ZeroToPas(xptr, *h, tmp);
  1105.         StrToRect(xptr, tmp, &r);
  1106.         
  1107.         MoveWindow(w, r.left, r.top, FALSE);
  1108.         MySizeWindow(r.right - r.left, r.bottom - r.top);
  1109.     } else if(pstrcmp(prop, "\pgloballoc") == 0){
  1110.         ZeroToPas(xptr, *h, tmp);
  1111.         StrToPoint(xptr, tmp, &pt);
  1112.         MoveWindow(w, pt.h, pt.v, FALSE);
  1113.     } else
  1114.         handled = FALSE;
  1115.         
  1116.     HUnlock(h);
  1117.     return(handled);
  1118. }
  1119.  
  1120. Handle
  1121. GetProp(StringPtr prop)
  1122. {
  1123.     Handle h = 0;
  1124.     Point pt;
  1125.     Rect r;
  1126.     Str255 tmp;
  1127.     
  1128.     /* hypercard deals with loc and visible */
  1129.     if(pstrcmp(prop, "\pdithering") == 0){
  1130.         h = HStr((*th)->dither ? "true" : "false");
  1131.     } else if(pstrcmp(prop, "\pscrollbars") == 0){
  1132.         h = HStr((*th)->scrollbars ? "true" : "false");
  1133.     } else if(pstrcmp(prop, "\pglobalrect") == 0){
  1134.         pt.h = w->portRect.left;
  1135.         pt.v = w->portRect.top;
  1136.         LocalToGlobal(&pt);
  1137.         r.left = pt.h;
  1138.         r.top = pt.v;
  1139.         r.right = r.left + (w->portRect.right - w->portRect.left);
  1140.         r.bottom = r.top + (w->portRect.bottom - w->portRect.top);
  1141.         RectToStr(xptr, &r, tmp);
  1142.         h = PasToZero(xptr, tmp);
  1143.     } else if(pstrcmp(prop, "\pgloballoc") == 0){
  1144.         pt.h = w->portRect.left;
  1145.         pt.v = w->portRect.top;
  1146.         LocalToGlobal(&pt);
  1147.         PointToStr(xptr, pt, tmp);
  1148.         h = PasToZero(xptr, tmp);
  1149.     } else if(pstrcmp(prop, "\pscroll") == 0){
  1150.         pt.h = GetCtlValue((*th)->hScroll);
  1151.         pt.v = GetCtlValue((*th)->vScroll);
  1152.         PointToStr(xptr, pt, tmp);
  1153.         h = PasToZero(xptr, tmp);
  1154.     } else if(pstrcmp(prop, "\ppicturewidth") == 0){
  1155.         NumToString((long) (*th)->ti->imageWidth, tmp);
  1156.         h = PasToZero(xptr, tmp);
  1157.     } else if(pstrcmp(prop, "\ppictureheight") == 0){
  1158.         NumToString((long) (*th)->ti->imageLength, tmp);
  1159.         h = PasToZero(xptr, tmp);
  1160.     } else if(pstrcmp(prop, "\pscale") == 0){
  1161.         NumToString((long) NewScaleToOld((*th)->scale), tmp);
  1162.         h = PasToZero(xptr, tmp);
  1163.     } else if(pstrcmp(prop, "\pnscale") == 0){
  1164.         myftoa((*th)->scale, (char *)tmp);
  1165.         CtoPstr(tmp);
  1166.         h = PasToZero(xptr, tmp);
  1167.     } else if(pstrcmp(prop, "\ppicturedepth") == 0){
  1168.         NumToString((long)(*th)->ti->samplesPerPixel * (*th)->ti->bitsPerSample[0], tmp);
  1169.         h = PasToZero(xptr, tmp);
  1170.     }
  1171.     
  1172.     return(h);
  1173. }
  1174.  
  1175. long
  1176. myatoi(char *s)
  1177. {
  1178.     long l;
  1179.     char tmp[256];
  1180.     
  1181.     strncpy(tmp, s, sizeof(tmp)-1);
  1182.     CtoPstr(tmp);
  1183.     StringToNum(tmp, &l);
  1184.     return(l);
  1185. }
  1186.  
  1187. double
  1188. myatof(char *s)
  1189. {
  1190.     double d, denom;
  1191.     
  1192.     d = 0;
  1193.     while(isdigit(*s)){
  1194.         d *= 10;
  1195.         d += *s - '0';
  1196.         s++;
  1197.     }
  1198.     
  1199.     if(*s == '.'){
  1200.         s++;
  1201.         denom = 10;
  1202.         while(isdigit(*s)){
  1203.             d += (*s - '0') / denom;
  1204.             s++;
  1205.             denom *= 10;
  1206.         }
  1207.     }
  1208.     
  1209.     return(d);
  1210. }
  1211.  
  1212. /* only two digits after the decimal point */
  1213. void
  1214. myftoa(double d, char *s)
  1215. {
  1216.     char f[256];
  1217.     
  1218.     myitoa((long) d, s);
  1219.     
  1220.     d -= (long) d;
  1221.     myitoa((long) (d * 100), f);
  1222.     
  1223.     strcat(s, ".");
  1224.     if(strlen(f) == 1)
  1225.         strcat(s, "0");
  1226.     strcat(s, f);
  1227. }
  1228.  
  1229. void
  1230. myitoa(long i, char *a)
  1231. {
  1232.     NumToString(i, a);
  1233.     PtoCstr(a);
  1234. }