home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / SlideShow 1.0 / Slideshow.c < prev    next >
Encoding:
Text File  |  1995-08-29  |  27.3 KB  |  1,230 lines  |  [TEXT/MMCC]

  1. // slide show-specific procedures
  2.  
  3.  
  4. #include    "Prototypes.h"
  5. #include    "QDOffscreen.h"
  6. #include    "ImageFileFormats.h"
  7. #include    "Palettes.h"
  8. #include    "PictUtils.h"
  9. #include    "DialogUtils.h"
  10. #include    "ScrollDialog.h"
  11. #include     "ShowHideMenubar.h"
  12. #include    "fade.h"
  13. #include    <Aliases.h>
  14.  
  15. long        gSlideTicks;
  16. long        gLastTicks;
  17. short        gSlideIndex;
  18. short        gSlideMaxIndex;
  19. short        gSlidePhase;
  20. short        gMonitorDepth = 8;
  21. FSSpec        gSlideFile;
  22. GWorldPtr    gSlideImage = NULL;
  23. Boolean        gRepeat = FALSE;
  24. Boolean        gTransitions = FALSE;
  25. Boolean        gUsePalManager = FALSE;
  26. Boolean        gDisplayRandom = FALSE;
  27. Boolean        gShowNames = FALSE;
  28. Boolean        gScaleImages = FALSE;
  29. Boolean        gHideMBar = FALSE;
  30. Boolean        gAtLeastOneOpened;
  31. Boolean        gAutoRun;
  32. Boolean        gIsFaded = FALSE;
  33. OSType        gFType;
  34. long        gDirectory = 0;
  35. CTabHandle    gCLUT = NULL;
  36.  
  37. unsigned long*    gSlideList = NULL;    // used for random order display
  38.  
  39. extern        FSSpec        gSlideFolder;
  40. extern        Boolean        gFolderChosen;
  41. extern        WindowPtr    gSlideWindow;
  42. extern        MenuHandle    gFileMenu;
  43.  
  44. enum
  45. {
  46.     kSlideStopped,
  47.     kSlideRunning,
  48.     kSlidePaused,
  49.     kSlideError
  50. };
  51.  
  52.  
  53. static void    InstallDitherProc(GrafPtr theOSPort);
  54. static pascal void DitherBitsProc(BitMap *srcBits,Rect *srcRect,
  55.                                 Rect *destRect,int mode,RgnHandle maskRgn);
  56. static void    RemoveDitherProc(GrafPtr theOSPort);
  57.  
  58.  
  59.  
  60. void    StartSlideshow()
  61. {
  62.     GDHandle    mainDevice;
  63.     
  64.     if (gFolderChosen)
  65.     {
  66.         if (gSlidePhase == kSlideStopped)
  67.         {
  68.             gSlideIndex = 0;
  69.             gAtLeastOneOpened = FALSE;
  70.             
  71.             // find out how many slides to show. This is the number of files in the
  72.             // folder. This also sets up the target folder.
  73.             
  74.             gSlideMaxIndex = FSpCountFiles(&gSlideFolder);
  75.             
  76.             // find out the monitor depth so that we can optimise palettes, etc.
  77.             
  78.             mainDevice = GetMainDevice();
  79.             gMonitorDepth = (*(*mainDevice)->gdPMap)->pixelSize;
  80.             
  81.             if (! gUsePalManager && (gMonitorDepth < 16))
  82.                 RestoreDeviceClut(NULL);
  83.             
  84.             // make sure first slide gets show immediately
  85.                 
  86.             gLastTicks = TickCount() - gSlideTicks;
  87.             
  88.             if (gDisplayRandom)
  89.                 SetUpRandomList();
  90.         }
  91.         
  92.         if (gSlideMaxIndex > 0)
  93.         {
  94.             if (gHideMBar)
  95.                 SetMBarState(HIDE);
  96.  
  97.             gSlidePhase = kSlideRunning;
  98.         
  99.             DisableItem(gFileMenu,kItemChooseSlides);
  100.             DisableItem(gFileMenu,kItemGo);
  101.             EnableItem(gFileMenu,kItemStop);
  102.             EnableItem(gFileMenu,kItemStartOver);
  103.         }
  104.     }
  105. }
  106.  
  107.  
  108.  
  109. void    RunSlideshow()
  110. {
  111.     long    curTicks;
  112.     OSErr    theErr;
  113.     
  114.     if (gSlidePhase == kSlideRunning && gFolderChosen)
  115.     {
  116.         curTicks = TickCount();
  117.         
  118.         if (curTicks >= (gSlideTicks + gLastTicks))    
  119.         {
  120.             // time for the next slide!
  121.             
  122.             gSlideIndex++;
  123.             
  124.             if (gSlideIndex > gSlideMaxIndex)
  125.             {
  126.                 if (! gAtLeastOneOpened)
  127.                 {
  128.                     // if we have circled completely but nowt was opened,
  129.                     // tell the user that there is nothing in the folder
  130.                     
  131.                     (void) Alert(130,NULL);
  132.                     gSlidePhase = kSlideError;
  133.                     StopSlideshow();
  134.                     return;
  135.                 }
  136.                 
  137.                 if (gRepeat)
  138.                     gSlideIndex = 1;    // otherwise repeat from first slide
  139.                 else
  140.                 {
  141.                     (void) Alert(129,NULL);
  142.                     gSlidePhase = kSlideError;
  143.                     StopSlideshow();
  144.                     return;
  145.                 }
  146.             }
  147.             
  148.             gFType = 0;
  149.             
  150.             // if here, the slide show is to continue, so set up the next file.
  151.             
  152.             SetUpNextSlideFile();
  153.                 
  154.             // if gFType is not zero, then open and display the file. If it IS zero it
  155.             // means that the file indexed is one we cannot open. If we end up circling around
  156.             // completely, we should show a message that the folder doesn't contain any openable
  157.             // images.
  158.             
  159.             if (gSlidePhase == kSlideRunning && gFType)
  160.             {
  161.                 gLastTicks = curTicks;
  162.                 gAtLeastOneOpened = TRUE;
  163.                 OpenNextFile();
  164.             }
  165.         }
  166.     }
  167. }
  168.  
  169.  
  170.  
  171. void    StopSlideshow()
  172. {
  173.     if (gSlidePhase == kSlideRunning)
  174.         gSlidePhase = kSlidePaused;
  175.     else
  176.         gSlidePhase = kSlideStopped;
  177.         
  178.     SetMBarState(SHOW);
  179.     
  180.     if (gTransitions && gIsFaded)
  181.     {
  182.         fade_to_black(16,fadeMainOnly,FALSE);
  183.         gIsFaded = FALSE;
  184.     }
  185.     EnableItem(gFileMenu,kItemChooseSlides);
  186.     EnableItem(gFileMenu,kItemGo);
  187.     DisableItem(gFileMenu,kItemStop);
  188.     EnableItem(gFileMenu,kItemStartOver);
  189. }
  190.  
  191.  
  192. void    ResetSlideshow()
  193. {
  194.     if (gSlidePhase == kSlidePaused)
  195.         gSlidePhase = kSlideStopped;
  196.     gSlideIndex = 0;    
  197.     StartSlideshow();
  198. }
  199.  
  200.  
  201.  
  202. short    FSpCountFiles(FSSpec* aFolderSpec)
  203. {
  204.     // returns the number of files in the given folder. If the filespec is not a folder,
  205.     // this returns zero. This does not take into account the type of the files.
  206.     
  207.     CInfoPBRec    cb;
  208.     OSErr        theErr;
  209.     short        count = 0;
  210.     Str255        parentName;
  211.     
  212.     // search the parent directory
  213.     
  214.     CopyPString(aFolderSpec->name,parentName);
  215.     cb.dirInfo.ioCompletion = NULL;
  216.     cb.dirInfo.ioNamePtr = parentName;
  217.     cb.dirInfo.ioVRefNum = aFolderSpec->vRefNum;
  218.     cb.dirInfo.ioFDirIndex = 0;
  219.     cb.dirInfo.ioDrDirID = aFolderSpec->parID;    // search the parent for this one
  220.     
  221.     theErr = PBGetCatInfo(&cb,FALSE);
  222.  
  223.     if (theErr == noErr && cb.dirInfo.ioFlAttrib & 16)
  224.     {
  225.         // OK, we found the desired directory here. That was done to obtain the
  226.         // "HARD" directory ID of the folder we want to use.
  227.         
  228.         gDirectory = cb.dirInfo.ioDrDirID;
  229.         count = cb.dirInfo.ioDrNmFls;
  230.     }
  231.     else
  232.         gDirectory = 0;
  233.  
  234.     return count;
  235. }
  236.  
  237.  
  238. void    SetUpNextSlideFile()
  239. {
  240.     // prepares the next slide for display. This gets the next indexed file in the directory,
  241.     // and sets the gSlideFile variable to it's FSSpec. it also sets gFType to the file's type.
  242.  
  243.     CInfoPBRec        cb;
  244.     OSErr            theErr;
  245.  
  246.     if (gFolderChosen && gDirectory != 0)
  247.     {
  248.         cb.hFileInfo.ioNamePtr = gSlideFile.name;
  249.         cb.hFileInfo.ioVRefNum = gSlideFile.vRefNum = gSlideFolder.vRefNum;
  250.         if (gDisplayRandom && gSlideList)
  251.             cb.hFileInfo.ioFDirIndex = HiWord(gSlideList[gSlideIndex - 1]);
  252.         else
  253.             cb.hFileInfo.ioFDirIndex = gSlideIndex;
  254.         cb.hFileInfo.ioDirID = gSlideFile.parID = gDirectory;
  255.         
  256.         theErr = PBGetCatInfo(&cb,FALSE);
  257.         
  258.         if (theErr == noErr)
  259.         {
  260.             // make sure the file spec is correct, and get the file type
  261.         
  262.             if (! (cb.hFileInfo.ioFlAttrib & 16))
  263.             {
  264.                 gFType = cb.hFileInfo.ioFlFndrInfo.fdType;
  265.                 
  266.                 // is it one we can open?
  267.                 
  268.                 if (gFType == 'TEXT')
  269.                 {
  270.                     // see if this is really a GIF file- if it is we can open
  271.                     // it after all. This is common with ISO 9660 CD-ROMs
  272.                     
  273.                     short    refNum;
  274.                     OSErr    fErr;
  275.                     long    byteCount;
  276.                     long    sigBytes;
  277.                 
  278.                     fErr = FSpOpenDF(&gSlideFile,fsCurPerm,&refNum);
  279.                     if (fErr == noErr)
  280.                     {
  281.                         byteCount = sizeof(long);
  282.                         
  283.                         fErr = FSRead(refNum,&byteCount,&sigBytes);
  284.                         
  285.                         if (sigBytes == 'GIF8')
  286.                             gFType = 'GIFf';
  287.                         
  288.                         fErr = FSClose(refNum);
  289.                     }
  290.                 }
  291.                 
  292.                 if (gFType == 'PICT' ||
  293.                     gFType == 'TIFF' ||
  294.                     gFType == 'JPEG' ||
  295.                     gFType == 'JFIF' ||
  296.                     gFType == 'GIFf')
  297.                     return;
  298.             }
  299.         }
  300.     }
  301.     gFType = 0;
  302. }
  303.  
  304.  
  305.  
  306. void    OpenNextFile()
  307. {
  308.     PicHandle    tempPic = NULL;
  309.     short        refNum = -1;
  310.     OSErr        theErr;
  311.     
  312.     ObscureCursor();
  313.     theErr = FSpOpenDF(&gSlideFile,fsCurPerm,&refNum);
  314.     
  315.     if (theErr == noErr)
  316.     {
  317.         switch (gFType)
  318.         {
  319.             case 'PICT':
  320.                 tempPic = OpenPICT(refNum);
  321.                 break;
  322.             case 'JPEG':
  323.             case 'JFIF':
  324.                 tempPic = OpenJPEG(refNum);
  325.                 break;
  326.             case 'GIFf':
  327.                 FSClose(refNum);
  328.                 tempPic = OpenGIF(refNum);
  329.                 refNum = -1;
  330.                 break;
  331.             case 'TIFF':
  332.                 FSClose(refNum);
  333.                 tempPic = OpenTIFF(refNum);
  334.                 refNum = -1;
  335.                 break;
  336.             default:
  337.                 tempPic = NULL;
  338.                 break;
  339.         }
  340.         if (refNum != -1)
  341.             FSClose(refNum);
  342.         
  343.         if (tempPic)
  344.         {
  345.             if (GetHandleSize((Handle) tempPic) != sizeof(Picture))
  346.                 DisplayPicture(tempPic);
  347.             else
  348.                 SysBeep(1);
  349.             KillPicture(tempPic);
  350.         }
  351.         else
  352.             SysBeep(1);    // an error occurred
  353.     }
  354. }
  355.  
  356. #define        kPICTHeaderSize        512
  357.  
  358. PicHandle    OpenPICT(short refNum)
  359. {
  360.     long    pSize;
  361.     Handle    pH;
  362.     OSErr    theErr;
  363.     
  364.     theErr = GetEOF(refNum,&pSize);
  365.     
  366.     pSize -= kPICTHeaderSize;
  367.     
  368.     pH = NewHandle(pSize);
  369.     
  370.     if (pH)
  371.     {
  372.         SetFPos(refNum,fsFromStart,kPICTHeaderSize);
  373.         
  374.         theErr = FSRead(refNum,&pSize,*pH);
  375.         
  376.         if (theErr)
  377.         {
  378.             DisposeHandle(pH);
  379.             pH = NULL;
  380.         }
  381.     }
  382.     return (PicHandle) pH;
  383. }
  384.  
  385. PicHandle    OpenJPEG(short refNum)
  386. {
  387.     OSErr        theErr;
  388.     PicHandle    pH;
  389.     
  390.     theErr = ConvertFromJFIF(gSlideFile.name,refNum,&pH);
  391.     
  392.     if (theErr)
  393.         return NULL;
  394.     else
  395.         return pH;
  396. }
  397.  
  398. PicHandle    OpenTIFF(short refNum)
  399. {
  400.     PicHandle    pH = NULL;
  401.     OSErr        theErr;
  402.     
  403.     theErr = ConvertTIFFToPICT(&gSlideFile,&pH);
  404.     if (theErr)
  405.         return NULL;
  406.     else
  407.         return pH;
  408. }
  409.  
  410. PicHandle    OpenGIF(short refNum)
  411. {
  412.     PicHandle    pH = NULL;
  413.     OSErr        theErr;
  414.     
  415.     theErr = ConvertGIFtoPICT(&gSlideFile,&pH);
  416.     if (theErr)
  417.         return NULL;
  418.     else
  419.         return pH;
  420. }
  421.  
  422.  
  423. const        RGBColor    BLACK = {0,0,0};
  424. const        RGBColor    WHITE = {0xFFFF,0xFFFF,0xFFFF};
  425.  
  426.  
  427. void    DisplayPicture(PicHandle thePic)
  428. {
  429.     // prepares the GWorld. This function always uses the screen depth for its port and dithers
  430.     // when creating it, never when showing it.    
  431.     
  432.     Rect            frame;
  433.     OSErr            theErr;
  434.     CTabHandle        pCT = NULL;
  435.     short            picDepth;
  436.     PictInfo        pi;
  437.     Boolean            usingColourReduction = FALSE;
  438.     GWorldPtr        tempWorld = NULL;
  439.     PixMapHandle    tPix;
  440.     
  441.     if (gSlideImage)
  442.         DisposeGWorld(gSlideImage);
  443.     
  444.     gSlideImage = NULL;
  445.     frame = (*thePic)->picFrame;
  446.     OffsetRect(&frame,-frame.left,-frame.top);
  447.     
  448.     // the colour table for the image depends on what characteristics the target screen has.
  449.     // If indexed, we need to obtain an optimal palette if that's what the user wants. For
  450.     // 16 colour screens, we never try to optimise the palette (too few colours to make it
  451.     // worthwhile), so only 8-bit screens are considered for this.
  452.     
  453.     if ((gMonitorDepth == 8) && gUsePalManager)
  454.     {
  455.         theErr = GetPictureInfo(thePic,&gPicInfo);
  456.         picDepth = gPicInfo.picDepth;
  457.         
  458.         // if the pic depth is greater than 8, we need to do a colour reduction    to obtain a
  459.         // good colour table. If less, we use the default colour table, and if equal, we use
  460.         // its own colour table.
  461.         
  462.         switch (picDepth)
  463.         {
  464.             case 16:
  465.             case 32:
  466.                 // the image is direct. To get the best results with JPEG compressed pictures,
  467.                 // it is necessary to image the picture into a temporary GWorld and sample
  468.                 // the colours from its pixmap, because the picture utilities do not do a
  469.                 // good job on such pictures. This is tedious and memory hungry, but what can
  470.                 // you do?
  471.                 
  472.                 theErr = NewGWorld(&tempWorld,picDepth,&frame,NULL,NULL,0);
  473.                 
  474.                 if (theErr == noErr)
  475.                 {
  476.                     GDHandle    saveDevice;
  477.                     CGrafPtr    savePort;
  478.                     
  479.                     GetGWorld(&savePort,&saveDevice);
  480.                     SetGWorld(tempWorld,NULL);
  481.                     
  482.                     ForeColor(blackColor);
  483.                     BackColor(whiteColor);
  484.                     
  485.                     tPix = GetGWorldPixMap(tempWorld);
  486.                     
  487.                     if (LockPixels(tPix))
  488.                     {
  489.                         EraseRect(&frame);
  490.                         DrawPicture(thePic,&frame);
  491.  
  492.                         theErr = GetPixMapInfo(    tPix,
  493.                                                 &pi,
  494.                                                  returnColorTable + suppressBlackAndWhite,
  495.                                                  254,
  496.                                                  medianMethod,
  497.                                                  0    );
  498.                          UnlockPixels(tPix);
  499.                     }
  500.                         
  501.                      SetGWorld(savePort,saveDevice);
  502.                 }
  503.                 
  504.                  if (theErr == noErr)
  505.                  {
  506.                     short    i;
  507.                     
  508.                     pCT = pi.theColorTable;
  509.                     
  510.                     // this table has 254 colours, but no B & W, so we extend it by two entries
  511.                     // and add in the B & W entries.
  512.                     
  513.                     SetHandleSize((Handle) pCT,GetHandleSize((Handle) pCT) + (sizeof(ColorSpec) * 2));
  514.                     
  515.                     for (i = 253;i >= 0;i--)
  516.                     {
  517.                         (*pCT)->ctTable[i + 1].rgb = (*pCT)->ctTable[i].rgb;
  518.                         (*pCT)->ctTable[i + 1].value = i + 1;
  519.                     }    
  520.                     (*pCT)->ctTable[0].rgb = WHITE;
  521.                     (*pCT)->ctTable[0].value = 0;
  522.                     (*pCT)->ctTable[255].rgb = BLACK;
  523.                     (*pCT)->ctTable[255].value = 255;
  524.                     usingColourReduction = TRUE;
  525.                 }
  526.                 break;
  527.             case 8:
  528.                 pCT = gPicInfo.picColours;
  529.                 break;
  530.             default:
  531.                 break;
  532.         }
  533.     }
  534.     else
  535.     {
  536.         if (gMonitorDepth < 16)
  537.         {
  538.             GDHandle    mS = GetMainDevice();
  539.             
  540.             pCT = (*(*mS)->gdPMap)->pmTable;
  541.             HandToHand((Handle*) &pCT);
  542.         }
  543.     }
  544.         
  545.     // create the GWorld with the monitor's depth, and the colour table we have established.
  546.     
  547.     theErr = NewGWorld(&gSlideImage,gMonitorDepth,&frame,pCT,NULL,0);
  548.     
  549.     if (theErr == noErr)
  550.     {
  551.         GDHandle        saveDevice;
  552.         CGrafPtr        savePort;
  553.         PixMapHandle    ppix;
  554.     
  555.         GetGWorld(&savePort,&saveDevice);
  556.         SetGWorld(gSlideImage,NULL);
  557.         
  558.         ForeColor(blackColor);
  559.         BackColor(whiteColor);
  560.         
  561.         ppix = GetGWorldPixMap(gSlideImage);
  562.         
  563.         if (LockPixels(ppix))
  564.         {
  565.             if (usingColourReduction && tempWorld)
  566.             {
  567.                 LockPixels(tPix);
  568.                 CopyBits((BitMap*) *tPix,(BitMap*) *ppix,&frame,&frame,ditherCopy,NULL);
  569.                 UnlockPixels(tPix);
  570.             }
  571.             else
  572.             {
  573.                 if (gMonitorDepth < picDepth)
  574.                     InstallDitherProc((GrafPtr) gSlideImage);
  575.         
  576.                 EraseRect(&frame);
  577.                 DrawPicture(thePic,&frame);
  578.                 
  579.                 if (gMonitorDepth < picDepth)
  580.                     RemoveDitherProc((GrafPtr) gSlideImage);
  581.             }
  582.             UnlockPixels(ppix);
  583.         }
  584.         SetGWorld(savePort,saveDevice);
  585.         
  586.         if (tempWorld)
  587.             DisposeGWorld(tempWorld);
  588.             
  589.         if (pCT)
  590.             DisposeCTable(pCT);
  591.  
  592.             
  593.         // if we are fading the screen, do it here.
  594.         
  595.         if (gTransitions)
  596.         {
  597.             fade_to_black(16,fadeMainOnly,TRUE);
  598.             gIsFaded = TRUE;
  599.         }
  600.         SetPort(gSlideWindow);
  601.         SelectWindow(gSlideWindow);
  602.         
  603.         ForeColor(blackColor);
  604.         PaintRect(&gSlideWindow->portRect);
  605.             
  606.         // refresh the window
  607.         
  608.         InvalRect(&gSlideWindow->portRect);    
  609.     }
  610. }
  611.  
  612. /*
  613. void    DisplayPictureOldWay(PicHandle thePic)
  614. {
  615.     // prepares the picture for drawing. This sets up the global GWorld to the size, depth,
  616.     // colour, etc of the picture, and draws it to the GWorld. It then forces an update of the
  617.     // display window which presents the image to the viewer, overwriting the old one. This
  618.     // function also creates a palette for the window with optimal colours.
  619.     
  620.     OSErr        theErr;
  621.     Rect        frame;
  622.     PictInfo     pi;
  623.     short        nColours = 0,picDepth;
  624.     CTabHandle    pCT;
  625.     
  626.     if (gSlideImage)
  627.         DisposeGWorld(gSlideImage);
  628.     
  629.     gSlideImage = NULL;
  630.     
  631.     // get the colour information for the picture
  632.     
  633.     if (gMonitorDepth > 4)
  634.     {
  635.         theErr = GetPictureInfo(thePic,&gPicInfo);
  636.     
  637.         if ((theErr == noErr) && gPicInfo.infoValid)
  638.         {
  639.             pCT = gPicInfo.picColours;
  640.             frame = gPicInfo.picSize;
  641.             picDepth = gPicInfo.picDepth;
  642.         
  643.             theErr = NewGWorld(&gSlideImage,picDepth,&frame,pCT,NULL,0);
  644.         }
  645.     }
  646.     else
  647.     {
  648.         // for 16-colour images, we use a different strategy. The GWorld is built using
  649.         // the SCREEN parameters, and the image is dithered into it by installing a
  650.         // ditherProc. This gives the best results on 16-colour screens and is faster.
  651.         // Since the poor old LC with 16 grays is a definite target machine, this
  652.         // "optimisation" is done to help it along.
  653.         
  654.         GDHandle    mainScreen;
  655.         
  656.         mainScreen = GetMainDevice();
  657.         
  658.         pCT = (*(*mainScreen)->gdPMap)->pmTable;
  659.         frame = (*thePic)->picFrame;
  660.         OffsetRect(&frame,-frame.left,-frame.top);
  661.         picDepth = 8;
  662.         
  663.         theErr = NewGWorld(&gSlideImage,gMonitorDepth,&frame,pCT,NULL,0);
  664.         
  665.         pCT = NULL;
  666.     }
  667.     
  668.     
  669.     if (theErr == noErr)
  670.     {
  671.         PixMapHandle    ppix;
  672.         CGrafPtr        savePort;
  673.         GDHandle        saveDevice;
  674.         
  675.         GetGWorld(&savePort,&saveDevice);
  676.         SetGWorld(gSlideImage,NULL);
  677.         
  678.         // install a ditherProc into the GWorld port to ensure that DrawPicture gets
  679.         // dithered correctly. (16 colours or less only.)
  680.  
  681.         if (gMonitorDepth < 8)
  682.             InstallDitherProc((GrafPtr) gSlideImage);
  683.             
  684.         ForeColor(blackColor);
  685.         BackColor(whiteColor);
  686.  
  687.         if (LockPixels(ppix = GetGWorldPixMap(gSlideImage)))
  688.         {
  689.             EraseRect(&frame);            // in case image has transparent parts
  690.             DrawPicture(thePic,&frame);    // may take a while if using embedded compression
  691.             UnlockPixels(ppix);
  692.         }
  693.         SetGWorld(savePort,saveDevice);
  694.  
  695.         if (gMonitorDepth < 8)
  696.             RemoveDitherProc((GrafPtr) gSlideImage);
  697.  
  698.         if (gTransitions)
  699.         {
  700.             fade_to_black(16,fadeMainOnly,TRUE);
  701.             gIsFaded = TRUE;
  702.         }
  703.         SetPort(gSlideWindow);
  704.         SelectWindow(gSlideWindow);
  705.         
  706.         ForeColor(blackColor);
  707.         PaintRect(&gSlideWindow->portRect);
  708.         
  709.         // Set up a display palette. We pursue a number of strategies: If the monitor is not
  710.         // indexed, don't bother at all. If the monitor has 256 colours, and the image is also
  711.         // 256 colours, use the image's colour table to make a palette. Otherwise use the
  712.         // system colour reduction utility. For 16 colours, the GWorld is matched to the screen.
  713.         // The user can switch of this facility altogether, which affects the gUsePalManager
  714.         // flag.
  715.             
  716.         if (gUsePalManager && (gMonitorDepth == 8))
  717.         {
  718.             if (pCT)
  719.             {
  720.                 pi.thePalette = NewPalette(256,pCT,pmTolerant,0);
  721.                 theErr = noErr;
  722.             }
  723.             else
  724.                 theErr = GetPixMapInfo(ppix,&pi,returnPalette + suppressBlackAndWhite,254,systemMethod,0);        
  725.             
  726.             if (theErr == noErr)
  727.             {
  728.                 NSetPalette(gSlideWindow,pi.thePalette,pmAllUpdates);
  729.                 DisposePalette(pi.thePalette);
  730.             }
  731.         }
  732.     }
  733.     else
  734.         SysBeep(1);
  735.     
  736.     if (pCT)
  737.         DisposeCTable(pCT);
  738.         
  739.     SetPort(gSlideWindow);
  740.     ActivatePalette(gSlideWindow);
  741.     InvalRect(&gSlideWindow->portRect);
  742. }
  743. */
  744.  
  745.  
  746. void    CentreRects(Rect *r1,Rect *r2)
  747. {
  748.     /* Offsets the coordinates of r2 so that it is centred with respect to r1. r1 is not
  749.         changed. Rectangles can be different sizes or same size */
  750.     
  751.     short        dH,dV;
  752.     
  753.     dH = ((r1->right - r1->left) - (r2->right - r2->left))/2 - r2->left + r1->left;
  754.     dV = ((r1->bottom - r1->top) - (r2->bottom - r2->top))/2 - r2->top  + r1->top;
  755.     OffsetRect(r2,dH,dV);
  756. }
  757.  
  758.  
  759.  
  760. void    DrawTheWindow(WindowPtr theWindow)
  761. {
  762.     // this function draws the contents of the window. This is called from an update event.
  763.     
  764.     Rect            r,sr,pr;
  765.     PixMapHandle    ppix;
  766.     RGBColor        saveBack;
  767.     Str15            numStr;
  768.     Str255            nameStr;
  769.     
  770.     if (gSlideImage)
  771.     {
  772.         if ((gMonitorDepth == 8) && gUsePalManager)
  773.         {
  774.             PaletteHandle    pH;
  775.             CTabHandle        pCT;
  776.             
  777.             ppix = GetGWorldPixMap(gSlideImage);
  778.             pCT = (*ppix)->pmTable;
  779.             
  780.             pH = NewPalette(256,pCT,pmTolerant + pmExplicit,0);
  781.             NSetPalette(gSlideWindow,pH,pmNoUpdates);
  782.             
  783.             DisposePalette(pH);
  784.         }
  785.         
  786.         if (gTransitions && gIsFaded)
  787.             fade_to_black(1,fadeMainOnly,TRUE);
  788.         
  789.         r = sr = gSlideImage->portRect;
  790.         pr = theWindow->portRect;
  791.         
  792.         if (gScaleImages)
  793.             Scale2Rects(&r,&pr);
  794.         else
  795.             CentreRects(&pr,&r);
  796.         
  797.         GetBackColor(&saveBack);
  798.         ForeColor(blackColor);
  799.         BackColor(whiteColor);
  800.         
  801.         if (LockPixels(ppix = GetGWorldPixMap(gSlideImage)))
  802.         {
  803.             short mode;
  804.             
  805.             mode = gScaleImages? ditherCopy : srcCopy;
  806.             
  807.             CopyBits((BitMap*) *ppix,&theWindow->portBits,&sr,&r,mode,NULL);
  808.             UnlockPixels(ppix);
  809.         }
  810.         
  811.         if (gShowNames)
  812.         {
  813.             short    remaining;
  814.             
  815.             CopyPString(gSlideFile.name,nameStr);
  816.             ConcatPStrings(nameStr,"\p  (Slide ");
  817.             if (gDisplayRandom && gSlideList)
  818.                 NumToString(HiWord(gSlideList[gSlideIndex - 1]),numStr);
  819.             else
  820.                 NumToString(gSlideIndex,numStr);
  821.             ConcatPStrings(nameStr,numStr);
  822.             ConcatPStrings(nameStr,"\p of ");
  823.             NumToString(gSlideMaxIndex,numStr);
  824.             ConcatPStrings(nameStr,numStr);
  825.             if (gDisplayRandom && ! gRepeat)
  826.             {
  827.                 remaining = gSlideMaxIndex - gSlideIndex;
  828.             
  829.                 if (remaining > 0)
  830.                 {
  831.                     ConcatPStrings(nameStr,"\p, ");
  832.                     NumToString(remaining,numStr);
  833.                     ConcatPStrings(nameStr,numStr);
  834.                     ConcatPStrings(nameStr,"\p left to show.");
  835.                 }
  836.             }
  837.             ConcatPStrings(nameStr,"\p)");
  838.             
  839.             sr = pr;
  840.             sr.top = r.bottom + 8;
  841.             sr.bottom = sr.top + 20;
  842.             
  843.             ForeColor(whiteColor);
  844.             BackColor(blackColor);
  845.             
  846.             TextFont(geneva);
  847.             TextSize(9);
  848.             TextFace(bold);
  849.             TextBox(&nameStr[1],nameStr[0],&sr,teJustCenter);
  850.             ForeColor(blackColor);
  851.         }
  852.         
  853.         if (gTransitions && gIsFaded && (gMonitorDepth < 16))
  854.         {
  855.             fade_to_black(16,fadeMainOnly,FALSE);
  856.             gIsFaded = FALSE;
  857.         }
  858.         RGBBackColor(&saveBack);
  859.     }
  860. }
  861.  
  862.  
  863. #define        kOptionsDialogID    1000
  864.  
  865. enum
  866. {
  867.     kOpItemDelay = 3,
  868.     kOpItemRepeat = 6,
  869.     kOpItemPalette,
  870.     kOpItemNames,
  871.     kOpItemScale,
  872.     kOpItemLine = 11,
  873.     kOpItemHideMBar,
  874.     kOpItemTransition = 14,
  875.     kOpItemRandom
  876. };
  877.  
  878. Boolean        DoOptionsDialog()
  879. {
  880.     // handles the settings. Since this affects globals only (this is a quick & dirty app,
  881.     // remeber) the result can be ignored.
  882.  
  883.     DialogPtr    theDialog;
  884.     short        item,itemType;
  885.     Handle        itemHand;
  886.     Rect        itemBox;
  887.     Boolean        result = FALSE;
  888.     long        val;
  889.     Str255        tempStr;
  890.  
  891.     theDialog = GetNewDialog(kOptionsDialogID,NULL,(WindowPtr) - 1L);
  892.     
  893.     if (theDialog)
  894.     {
  895.         StdDialogSetup(theDialog);
  896.         SetItemValue(theDialog,kOpItemRepeat,gRepeat);
  897.         SetItemValue(theDialog,kOpItemPalette,gUsePalManager);
  898.         SetItemValue(theDialog,kOpItemRandom,gDisplayRandom);
  899.         SetItemValue(theDialog,kOpItemTransition,gTransitions);
  900.         SetItemValue(theDialog,kOpItemNames,gShowNames);
  901.         SetItemValue(theDialog,kOpItemScale,gScaleImages);
  902.         SetItemValue(theDialog,kOpItemHideMBar,gHideMBar);
  903.         SetUniversalUserItem(theDialog,kOpItemLine,(ProcPtr) FrameRectUserItem);
  904.         SetUniversalUserItem(theDialog,13,(ProcPtr) EditFieldDecorator);
  905.  
  906.         GetDItem(theDialog,kOpItemDelay,&itemType,&itemHand,&itemBox);
  907.         val = gSlideTicks / 60;
  908.         NumToString(val,tempStr);
  909.         SetIText(itemHand,tempStr);
  910.         SelIText(theDialog,kOpItemDelay,0,32767);
  911.  
  912.         ShowWindow(theDialog);
  913.         OutlineDialogItem(theDialog,ok);
  914.         
  915.         item = 0;
  916.         
  917.         while(item == 0)
  918.         {
  919.             ModalDialog(NULL,&item);
  920.             
  921.             switch (item)
  922.             {
  923.                 case ok:
  924.                     gRepeat = GetItemValue(theDialog,kOpItemRepeat);
  925.                     gTransitions = GetItemValue(theDialog,kOpItemTransition);
  926.                     gShowNames = GetItemValue(theDialog,kOpItemNames);
  927.                     gScaleImages = GetItemValue(theDialog,kOpItemScale);
  928.                     gHideMBar = GetItemValue(theDialog,kOpItemHideMBar);
  929.                     gDisplayRandom = GetItemValue(theDialog,kOpItemRandom);
  930.                     gUsePalManager = GetItemValue(theDialog,kOpItemPalette);
  931.                     
  932.                     GetDItem(theDialog,kOpItemDelay,&itemType,&itemHand,&itemBox);
  933.                     GetIText(itemHand,tempStr);
  934.                     StringToNum(tempStr,&val);
  935.                     gSlideTicks = val * 60;
  936.                     result = TRUE;
  937.                 case cancel:
  938.                     break;
  939.                 default:
  940.                     GetDItem(theDialog,item,&itemType,&itemHand,&itemBox);
  941.                     
  942.                     if ((itemType & 0x7F) == ctrlItem + chkCtrl)
  943.                         SetCtlValue((ControlHandle) itemHand,GetCtlValue((ControlHandle) itemHand) ^ 1);
  944.                     item = 0;
  945.                     break;
  946.             }
  947.         }
  948.         DisposeUserItem(theDialog,kOpItemLine);
  949.         DisposeUserItem(theDialog,13);
  950.         DisposeDialog(theDialog);
  951.     }
  952.     return result;
  953. }
  954.  
  955.  
  956. typedef struct
  957. {
  958.     Boolean     repeatFlag;
  959.     Boolean     transitFlag;
  960.     Boolean     namesFlag;
  961.     Boolean     scaleFlag;
  962.     Boolean     folderValid;
  963.     Boolean     hideMBar;
  964.     Boolean  autoRun;
  965.     Boolean     usePalManager;
  966.     Boolean     randomOrder;
  967.     Boolean     spare1;
  968.     Boolean     spare2;
  969.     Boolean  spare3;
  970.     long     tticks;
  971. }
  972. slidePrefs, *slidePrefPtr, **slidePrefHdl;
  973.  
  974. #define        kSlidePrefResType    'SLID'
  975. #define        kSlidePrefResID        128
  976.  
  977.  
  978. void    SavePrefs()
  979. {
  980.     // this saves the current settings and target folder in its OWN RESOURCES. Though not
  981.     // recommended really, this is done to make it easy to create stand-alone slideshows on
  982.     // a disk - the user can simply double-click and away it goes. If the folder is not found,
  983.     // the slide show will stop.
  984.     
  985.     slidePrefHdl    spH;
  986.     Boolean            isResource = TRUE;
  987.     AliasHandle        folderAlias;
  988.     OSErr            theErr;
  989.     
  990.     spH = (slidePrefHdl) GetResource(kSlidePrefResType,kSlidePrefResID);
  991.     
  992.     if (spH == NULL)
  993.     {
  994.         spH = (slidePrefHdl) NewHandleClear(sizeof(slidePrefs));
  995.         isResource = FALSE;
  996.     }
  997.     if (spH)
  998.     {
  999.         (*spH)->repeatFlag = gRepeat;
  1000.         (*spH)->transitFlag = gTransitions;
  1001.         (*spH)->namesFlag = gShowNames;
  1002.         (*spH)->scaleFlag = gScaleImages;
  1003.         (*spH)->hideMBar = gHideMBar;
  1004.         (*spH)->autoRun = (gSlidePhase == kSlideRunning);
  1005.         (*spH)->usePalManager = gUsePalManager;
  1006.         (*spH)->randomOrder = gDisplayRandom;
  1007.         (*spH)->tticks = gSlideTicks;
  1008.         
  1009.         (*spH)->folderValid = gFolderChosen;
  1010.     }
  1011.     
  1012.     if (isResource)
  1013.         ChangedResource((Handle) spH);
  1014.     else
  1015.         AddResource((Handle) spH,kSlidePrefResType,kSlidePrefResID,"\p");
  1016.     
  1017.     if (! ResError())
  1018.     {    
  1019.         WriteResource((Handle) spH);
  1020.         ReleaseResource((Handle) spH);
  1021.     }
  1022.     
  1023.     // we now save the folder spec as an alias record, which works across volumes-
  1024.     // necessary if you want to make a slideshow on a floppy disk that runs automatically
  1025.     // on any machine it is inserted into.
  1026.     
  1027.     folderAlias = (AliasHandle) GetResource('alis',128);
  1028.     
  1029.     if (folderAlias)
  1030.     {
  1031.         RmveResource((Handle) folderAlias);
  1032.         DisposeHandle((Handle) folderAlias);
  1033.     }
  1034.     
  1035.     theErr = NewAlias(NULL,&gSlideFolder,&folderAlias);
  1036.     
  1037.     if (theErr == noErr)
  1038.     {
  1039.         AddResource((Handle) folderAlias,'alis',128,"\p");
  1040.         WriteResource((Handle) folderAlias);
  1041.         ReleaseResource((Handle) folderAlias);
  1042.     }
  1043. }
  1044.  
  1045.  
  1046. void    RestorePrefs()
  1047. {
  1048.     // this recovers the settings from the resources.
  1049.  
  1050.     slidePrefHdl    spH;
  1051.     AliasHandle        folderAlias;
  1052.     OSErr            theErr;
  1053.     Boolean            wasChanged;
  1054.  
  1055.     spH = (slidePrefHdl) GetResource(kSlidePrefResType,kSlidePrefResID);
  1056.     
  1057.     if (spH)
  1058.     {
  1059.         gRepeat = (*spH)->repeatFlag;
  1060.         gTransitions = (*spH)->transitFlag;
  1061.         gShowNames = (*spH)->namesFlag;
  1062.         gScaleImages = (*spH)->scaleFlag;
  1063.         gHideMBar = (*spH)->hideMBar;
  1064.         gSlideTicks = (*spH)->tticks;
  1065.         
  1066.         gFolderChosen = (*spH)->folderValid;
  1067.         gAutoRun = (*spH)->autoRun;
  1068.         gDisplayRandom = (*spH)->randomOrder;
  1069.         gUsePalManager = (*spH)->usePalManager;
  1070.         
  1071.         ReleaseResource((Handle) spH);
  1072.     }
  1073.     
  1074.     // fetch the folder spec
  1075.     
  1076.     folderAlias = (AliasHandle) GetResource('alis',128);
  1077.     
  1078.     if (folderAlias)
  1079.     {
  1080.         theErr = ResolveAlias(NULL,folderAlias,&gSlideFolder,&wasChanged);
  1081.     
  1082.         ReleaseResource((Handle) folderAlias);
  1083.         
  1084.         if (theErr)
  1085.             gFolderChosen = FALSE;
  1086.     }
  1087. }
  1088.  
  1089.  
  1090. void    DoAboutBox()
  1091. {
  1092.     // shows the enhanced about box which contains the scrollable help text, etc.
  1093.  
  1094.     short aHit = ScrollTextDialog(1001,2,128);
  1095. }
  1096.  
  1097.  
  1098. void    SetSlideWindowSize(WindowPtr theWindow)
  1099. {
  1100.     Rect    bounds;
  1101.     
  1102.     bounds = qd.screenBits.bounds;
  1103.     if (! gHideMBar)
  1104.         bounds.top += GetMBarHeight();
  1105.     
  1106.     SizeWindow(theWindow,bounds.right - bounds.left,bounds.bottom - bounds.top,TRUE);
  1107.     MoveWindow(theWindow,bounds.left,bounds.top,FALSE);
  1108. }
  1109.  
  1110.  
  1111. void    DoOptions()
  1112. {
  1113.     if (DoOptionsDialog())
  1114.     {
  1115.         if (gHideMBar && gSlidePhase == kSlideRunning)
  1116.             SetMBarState(HIDE);
  1117.         else
  1118.             SetMBarState(SHOW);
  1119.             
  1120.         SetPort(gSlideWindow);
  1121.         SetSlideWindowSize(gSlideWindow);
  1122.         PaintRect(&gSlideWindow->portRect);
  1123.         InvalRect(&gSlideWindow->portRect);
  1124.     }
  1125. }
  1126.  
  1127.  
  1128. void    InstallDitherProc(GrafPtr theOSPort)
  1129. {
  1130.     /* sets up a bottleneck procedure for DrawPicture to dither pictures drawn into the
  1131.         port. This is useful when drawing deep pictures into shallow ports */
  1132.     
  1133.     CQDProcsPtr        osProcs;
  1134.     Boolean            pIsColour;
  1135.     QDBitsUPP        ditherProcUPP;
  1136.     
  1137.     if (theOSPort)
  1138.     {
  1139.         pIsColour = ((theOSPort->portBits.rowBytes & 0xC000) != 0);
  1140.         if (pIsColour)
  1141.         {
  1142.             osProcs = (CQDProcsPtr) NewPtr(sizeof(CQDProcs));
  1143.             if (osProcs)
  1144.             {
  1145.                 SetStdCProcs(osProcs);
  1146.                 
  1147.                 ditherProcUPP = NewQDBitsProc((ProcPtr) DitherBitsProc);
  1148.                 
  1149.                 osProcs->bitsProc = ditherProcUPP;
  1150.                 theOSPort->grafProcs = (QDProcsPtr) osProcs;
  1151.             }
  1152.         }
  1153.     }
  1154. }
  1155.  
  1156.  
  1157.  
  1158. pascal void DitherBitsProc(BitMap *srcBits,Rect *srcRect,
  1159.                                 Rect *destRect,int mode,RgnHandle maskRgn)
  1160. {
  1161.     /* special filter proc to dither pictures containing copybits opcodes */
  1162.     
  1163.     StdBits(srcBits,srcRect,destRect,mode | ditherCopy,maskRgn);
  1164. }
  1165.  
  1166.  
  1167. void    RemoveDitherProc(GrafPtr theOSPort)
  1168. {
  1169.     // dispose of the dither proc's routine descriptor
  1170.  
  1171.     CQDProcsPtr        grafProcs;
  1172.     QDBitsUPP        ditherProcUPP;
  1173.  
  1174.  
  1175.     grafProcs = (CQDProcsPtr) theOSPort->grafProcs;
  1176.     
  1177.     if (grafProcs)
  1178.     {
  1179.         ditherProcUPP = grafProcs->bitsProc;
  1180.         if (ditherProcUPP)
  1181.             DisposeRoutineDescriptor(ditherProcUPP);
  1182.         
  1183.         DisposePtr((Ptr) grafProcs);
  1184.         theOSPort->grafProcs = NULL;
  1185.     }
  1186. }
  1187.  
  1188.  
  1189. void    SetUpRandomList()
  1190. {
  1191.     // if we're displaying randomly, we create a random list. This consists of an array of
  1192.     // longs, with one value per slide. The HiWord is the slide index (1,2,3...n) and the
  1193.     // LoWord is a random number. Once done, we sort the list based on the random number
  1194.     // using a simple bubblesort. The file index is then picked at run time in array order.
  1195.     // This ensures that each slide gets shown once and only once even in Random order.
  1196.     
  1197.     short            i,m,n,k;
  1198.     unsigned long    temp;
  1199.     
  1200.     GetDateTime((unsigned long*) &qd.randSeed);
  1201.     
  1202.     if (gSlideList)
  1203.         DisposePtr((Ptr) gSlideList);
  1204.         
  1205.     gSlideList = (unsigned long*) NewPtrClear(sizeof(long) * gSlideMaxIndex);
  1206.     
  1207.     if (gSlideList)
  1208.     {
  1209.         for(i = 0;i < gSlideMaxIndex;i++)
  1210.             gSlideList[i] = ((unsigned long)(i + 1) << 16) | (unsigned short) Random();
  1211.         
  1212.         // now sort the list based on the LoWord
  1213.         
  1214.         m = gSlideMaxIndex - 1;
  1215.         
  1216.         for (n = m;n >= 0;n--)
  1217.         {
  1218.             for (i = 0;i < n;i++)
  1219.             {
  1220.                 if (LoWord(gSlideList[i + 1]) < LoWord(gSlideList[i]))
  1221.                 {
  1222.                     temp = gSlideList[i];
  1223.                     gSlideList[i] = gSlideList[i + 1];
  1224.                     gSlideList[i + 1] = temp;
  1225.                 }
  1226.             }
  1227.         }
  1228.     }
  1229. }
  1230.