home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / IBPalettes / WWTCLKit / WWSimpleMovieView.m < prev    next >
Encoding:
Text File  |  1995-03-22  |  28.2 KB  |  1,034 lines

  1. // WWSimpleMovieView is exactly that: a pretty simple view for showing movies.
  2. // It has a bunch of different behaviors to do all the obvious things:
  3. // all of the following do it at some given frames/second:
  4. // - plays movie either from the beginning each time or bounces
  5. //   - plays movie continuously
  6. //   - starts playing movie once when clicked, stops when clicked
  7. //   - plays movie when the mouse is over it; resets when the mouse leaves
  8.  
  9. // one thing that would be handy is if the view had a notion of a
  10. //control panel for itself.  It might have two outlets, one which is the
  11. //controlPanelView, and some other view (say, a WW3DWell) could come
  12. //along and switch that view in so that the view might have some visible
  13. //control.  This is sort of a bug of NS; no notion of objects having
  14. //control panels (i.e. the live version of an inspector).
  15.  
  16. #import "WWSimpleMovieView.h"
  17. #import "aspectRatios.h"
  18. // for IB protocols
  19. #import <apps/InterfaceBuilder.h>
  20.  
  21. // for mkdir()
  22. #import <sys/types.h>
  23. #import <sys/stat.h>
  24.  
  25.  
  26. @implementation WWSimpleMovieView
  27.  
  28.  
  29. #define WW_BOTTOMALIGNED  0
  30. #define WW_TOPALIGNED     1
  31.  
  32.  
  33. /*
  34. ==========================================================================
  35. subext() -- Remove extension from fname only if it is there.
  36.  
  37. ==========================================================================
  38. */
  39. static char 
  40. *subext(fname, ext)
  41. register char *fname, *ext;
  42. {
  43.   int fl, el;
  44.  
  45.     for ( fl = strlen(fname), el = strlen(ext);
  46.         --el > -1 && --fl > -1 && fname[fl] == ext[el]; )
  47.         ;
  48.     if (el == -1) 
  49.     {  fname[fl] = '\0';
  50.     }
  51.     return(fname);
  52. }
  53.  
  54.  
  55. /*
  56. ==========================================================================
  57. basename() -- deletes any prefix ending in `/' and the suffix.
  58.  
  59. ==========================================================================
  60. */
  61. static char 
  62. *basename(str, sfx, dest)
  63. char *str, *sfx, *dest;
  64. {
  65. char temp[1024];
  66. char *p, *p1;
  67. char *subext();
  68.  
  69.     p = p1 = temp;
  70.     strcpy(p, str);
  71.     while (*p) 
  72.     {  if (*p++ == '/') 
  73.            {  p1 = p;
  74.        }
  75.     }
  76.     strcpy (dest, subext(p1, sfx));
  77.     return(dest);
  78. }
  79.  
  80. + initialize { return [WWSimpleMovieView setVersion:3], self; }
  81.  
  82.  
  83. - showFrameAndIncrement:sender
  84. {
  85.    if (drawCorrectly && (alpha < 1.0))
  86.    {  clearTheView = YES; [self display];
  87.       clearTheView = NO;  [window display];
  88.    }
  89.    else
  90.    {  clearTheView = NO;  [self display];
  91.    }
  92.    NXPing();
  93.    if (!stationaryFrameTE)  // if we're not really playing, we're outta here
  94.    {  return self;
  95.    }
  96.  
  97.    // check to see if we're at the last frame of the sequence
  98.    if (goingForward)
  99.    {  if (currentFrameIndex == (frameCount - 1)) // we're at the end!
  100.       {  if (loop)
  101.          {  currentFrameIndex = 0;
  102.          }
  103.          else
  104.          {  currentFrameIndex--;
  105.             goingForward = NO;
  106.          }
  107.       }
  108.       else // more frames to go...
  109.       {  currentFrameIndex++;
  110.       }
  111.    }
  112.    else // going backwards; 0 is the end frame
  113.    {  if (!currentFrameIndex) // we're at the end!
  114.       {  if (loop)  // no way; we don't backwards if we're looping, doofus...
  115.          {  NXLogError("shouldn't be here, pal.\n");
  116.          }
  117.          goingForward = YES;
  118.          currentFrameIndex++;
  119.       }
  120.       else // keep counting down
  121.       {  currentFrameIndex--;
  122.       }
  123.    }
  124.  
  125.    movieImage = [imageList objectAt:currentFrameIndex];
  126.    return self;
  127. }
  128.  
  129. void nextFrame(DPSTimedEntry teNumber, double now, void *userData)
  130. {
  131.   id  myObj = (id)userData;
  132.  
  133.   [myObj showFrameAndIncrement:nil];
  134.   return ;
  135. }
  136.  
  137.  
  138.  
  139. - beginStationaryBehavior
  140. {
  141.   // make sure that I have some images in my list
  142.   if (![imageList count])
  143.   {  return self;
  144.   }
  145.   goingForward = YES;
  146.   stationaryFrameTE = DPSAddTimedEntry(frameTimeIncr, 
  147.                        (DPSTimedEntryProc)nextFrame, 
  148.                        self, NX_MODALRESPTHRESHOLD);
  149.   if (stationaryBehavior == WW_PLAY_MOUSE_IN)
  150.   {  [self setMyTrackingRect:YES];
  151.   }
  152.   return self;
  153. }
  154.  
  155. - endStationaryBehavior
  156. {
  157.   if (stationaryFrameTE)
  158.   {  DPSRemoveTimedEntry(stationaryFrameTE);
  159.      stationaryFrameTE = 0;
  160.   }
  161.  
  162.   return self;
  163. }
  164.  
  165. - copyFromZone:(NXZone *)zone
  166. {
  167.   id newCopy = [super copyFromZone:zone];
  168.  
  169.   if (frameCount)
  170.   {  // rather than copy through the movie data, we'll just 
  171.      // mark both this instance and the new one as using
  172.      // a shared movie
  173.      [self setMovieIsShared:YES];
  174.      [newCopy setMovieIsShared:YES];
  175.   }
  176.  
  177.   return newCopy;
  178. }
  179.  
  180. - free
  181. {
  182.   if (controlPanel)
  183.   {  [controlPanel setFreeWhenClosed:NO];
  184.      [controlPanel close];
  185.      [controlPanel free];
  186.      controlPanel = nil;
  187.   }
  188.   [self endStationaryBehavior];
  189.   if (stationaryBehavior == WW_PLAY_MOUSE_IN)
  190.   {  [self setMyTrackingRect:NO];
  191.   }
  192.  
  193.   if (!movieIsShared)
  194.   {  [imageList freeObjects];
  195.   }
  196.   [imageList free];
  197.  
  198.   if (!imageIsShared)
  199.   {  [image free];
  200.   }
  201.  
  202.   return [super free];
  203. }
  204.  
  205. - initFrame:(const NXRect *)r 
  206. {
  207.    [super initFrame:r];
  208.    backgroundColor = NXConvertRGBToColor(0.96, 1.0, 0.6);
  209.    borderType = 0;
  210.    alpha = 1.0;
  211.    image = nil;
  212.    movieImage = nil;
  213.    imageList = [[List alloc] init];
  214.    imageIsShared = NO;
  215.    movieIsShared = NO;
  216.    frameCount = 0;
  217.    aspectRatioType = WW_ASPECT_DONT_CARE;
  218.    aspectRatio = 1.0;
  219.    scaleToFit = NO;
  220.    imageUnder = NO;
  221.    horizontalLayoutType = NX_LEFTALIGNED;
  222.    verticalLayoutType = WW_BOTTOMALIGNED;
  223.    fps = 15.0;
  224.    frameTimeIncr = 1.0/fps;
  225.    loop = NO; // in other words, "breathe"
  226.    stationaryBehavior = WW_PLAY_MOUSE_CLICK;
  227.    stationaryFrameTE = (DPSTimedEntry)0;
  228.    sink = NO;
  229.    source = NO;
  230.    archiveImageData = YES;
  231.    archiveMovieData = YES;
  232.    trackingRect = 0;
  233.    drawCorrectly = YES;
  234.    controlPanel = nil;
  235.  
  236.    if (stationaryBehavior == WW_PLAY_CONTINUOUS)
  237.    {  [self beginStationaryBehavior];
  238.    }
  239.  
  240.    return self;
  241. }
  242.  
  243. - awake
  244. {
  245.    [super awake];
  246.    stationaryFrameTE = (DPSTimedEntry)0;
  247.    trackingRect = 0;
  248.    if ([NXApp conformsTo:@protocol(IB)])  // what a hacque!! (def: "hacqe: a particularly baroque (in both senses of the word...) hack")
  249.    {  [self perform:@selector(awakeFromNib) with:nil afterDelay:0.01 cancelPrevious:NO];
  250.       // the reason we do this is because I'm not certain that if we awake in IB our "window" will be set yet.
  251.    }
  252.    currentFrameIndex = 0;
  253.    frameCount = [imageList count];
  254.    controlPanel = nil;
  255.    if (frameCount)
  256.    {  movieImage = [imageList objectAt:currentFrameIndex];
  257.       if (stationaryBehavior == WW_PLAY_CONTINUOUS)
  258.       {  [self beginStationaryBehavior];
  259.       }
  260.    }
  261.  
  262.    return self;
  263. }
  264.  
  265. - awakeFromNib
  266. {
  267.   if (stationaryBehavior == WW_PLAY_MOUSE_IN)
  268.   {  [self setMyTrackingRect:YES];
  269.   }
  270.   return self;
  271. }
  272.  
  273.  
  274. #define EPSILON 10
  275.  
  276. - sizeTo:(NXCoord)width :(NXCoord)height
  277. {
  278.   // don't want to make it too small...
  279.   if (width < EPSILON) return nil;
  280.   if (height < EPSILON) return nil;
  281.  
  282.  
  283.   // let's say it comes in at 100, 100 and you're supposed to be constrained to 4:3
  284.   // you don't want it to get bigger than 100, 100, so... you frob the width, not the height
  285.   switch (aspectRatioType)
  286.   {  case WW_ASPECT_DONT_CARE:
  287.             aspectRatio = width/height;
  288.             break;
  289.      case WW_ASPECT_NTSC:  // 1.33:1
  290.             height = width/1.33;
  291.             aspectRatio = 1.33;
  292.             break;
  293.      case WW_ASPECT_SQUARE:  // 1:1
  294.             if (width < height)
  295.         {  height = width;
  296.         }
  297.         else
  298.         {  width = height;
  299.         }
  300.             aspectRatio = 1.0;
  301.             break;
  302.      case WW_ASPECT_CUSTOM:  //aspectX:aspectY
  303.             if (aspectRatio < 1.0)
  304.         {  width = height * aspectRatio;
  305.         }
  306.         else
  307.         {  height = width/aspectRatio;
  308.         }
  309.             break;
  310.      default:
  311.             NXLogError("unknown aspect ratio type: %d", aspectRatioType);
  312.             break;
  313.   }
  314.  
  315.   [super sizeTo:width :height];
  316.   [self display];
  317.   if (stationaryBehavior == WW_PLAY_MOUSE_IN)
  318.   {  [self setMyTrackingRect:YES];
  319.   }
  320.  
  321.   return self;
  322. }
  323.  
  324. - setBorderType:(int)newBorderType { borderType = newBorderType; return self; }
  325. - (int)borderType { return borderType; }
  326.  
  327. - setFPS:(float)newFPS { fps = newFPS; frameTimeIncr = 1.0/fps; return self; }
  328. - (float)fps { return fps; }
  329.  
  330. //
  331. - sizeToImage:sender 
  332. {
  333.    NXSize s;
  334.  
  335.  
  336.    if (image)
  337.    {  [image getSize:&s];
  338.       [self sizeTo:s.width :s.height];
  339.    }
  340.    return self;
  341. }
  342.  
  343. - sizeToMovie:sender 
  344. {
  345.    NXSize s;
  346.  
  347.  
  348.    if (movieImage)
  349.    {  [movieImage getSize:&s];
  350.       [self sizeTo:s.width :s.height];
  351.    }
  352.    return self;
  353. }
  354.  
  355. - setScaleToFit:(BOOL)flag { scaleToFit = flag; return self; }
  356. - (BOOL)scaleToFit { return scaleToFit; }
  357. - setImageUnder:(BOOL)flag { imageUnder = flag; return self; }
  358. - (BOOL)imageUnder { return imageUnder; }
  359.  
  360. - (int)aspectRatioType { return aspectRatioType; }
  361. - (float)aspectRatio { return aspectRatio; }
  362. - setAspectRatioFromMatrix:sender
  363. {
  364.   NXRect  newRect;
  365.  
  366.  
  367.   [self getFrame:&newRect];
  368.   aspectRatioType = [[sender selectedCell] tag];
  369.   switch (aspectRatioType)
  370.   {  case WW_ASPECT_DONT_CARE:
  371.             break;
  372.      case WW_ASPECT_NTSC:  // 1.33:1
  373.             newRect.size.height = newRect.size.width/1.33;
  374.             break;
  375.      case WW_ASPECT_SQUARE:  // 1:1
  376.             if (newRect.size.width < newRect.size.height)
  377.             {  newRect.size.height = newRect.size.width;
  378.         }
  379.         else
  380.         {  newRect.size.width = newRect.size.height;
  381.         }
  382.             break;
  383.      case WW_ASPECT_CUSTOM:  //aspectX:aspectY
  384.             if (aspectRatio < 1.0)
  385.         {  newRect.size.width = newRect.size.height * aspectRatio;
  386.         }
  387.         else
  388.         {  newRect.size.height = newRect.size.width/aspectRatio;
  389.         }
  390.             break;
  391.      default:
  392.             NXLogError("unknown aspect ratio type: %d", aspectRatioType);
  393.             break;
  394.   }
  395.   [self sizeTo:newRect.size.width :newRect.size.height];
  396.  
  397.   return self;
  398. }
  399. //
  400. - setAspectRatio:sender
  401. {
  402.   NXRect  newRect;
  403.  
  404.  
  405.   [self getFrame:&newRect];
  406.   aspectRatio = [sender floatValue];
  407.  
  408.   if (aspectRatioType != WW_ASPECT_CUSTOM)
  409.   {  return self;
  410.   }
  411.   if (aspectRatio < 1.0)
  412.   {  newRect.size.width = newRect.size.height * aspectRatio;
  413.   }
  414.   else
  415.   {  newRect.size.height = newRect.size.width/aspectRatio;
  416.   }
  417.   [self sizeTo:newRect.size.width :newRect.size.height];
  418.  
  419.   return self;
  420. }
  421. //
  422. - setHorizontalLayoutFromMatrix:sender  {  horizontalLayoutType = [[sender selectedCell] tag];  return self; }
  423. - (int)horizontalLayoutType { return horizontalLayoutType; }
  424.  
  425. - setVerticalLayoutFromMatrix:sender  {  verticalLayoutType = [[sender selectedCell] tag];  return self; }
  426. - (int)verticalLayoutType { return verticalLayoutType; }
  427.  
  428.  
  429. - (NXColor) backgroundColor     { return backgroundColor; }
  430. - setBackgroundColor:(NXColor)c { return backgroundColor = c, self; }
  431. - (float) alpha            { return alpha; }
  432. - setBackgroundAlpha:(float)n    { return alpha = n, self; }
  433.  
  434. - (int)stationaryBehavior    { return stationaryBehavior; }
  435. - setStationaryBehavior:(int)n    
  436. {  
  437.   [self endStationaryBehavior];
  438.   stationaryBehavior = n;
  439.   if (stationaryBehavior == WW_PLAY_CONTINUOUS)
  440.   {  [self beginStationaryBehavior];
  441.   }
  442.   if (stationaryBehavior == WW_PLAY_MOUSE_IN)
  443.   {  [self setMyTrackingRect:YES];
  444.   }
  445.   return self; 
  446. }
  447. - setStationaryBehaviorFromMatrix:sender 
  448.   [self setStationaryBehavior:[[sender selectedCell] tag]]; 
  449.   return self; 
  450. }
  451.  
  452. - setLoop:(BOOL)n { loop = n; return self; }
  453. - (BOOL)loop { return loop; }
  454.  
  455. - setArchiveImageData:(BOOL)n { archiveImageData = n; return self; }
  456. - (BOOL)archiveImageData { return archiveImageData; }
  457.  
  458. - setArchiveMovieData:(BOOL)n { archiveMovieData = n; return self; }
  459. - (BOOL)archiveMovieData { return archiveMovieData; }
  460.  
  461. - setDrawCorrectly:(BOOL)n { drawCorrectly = n; return self; }
  462. - (BOOL)drawCorrectly { return drawCorrectly; }
  463.  
  464. - (int)frameCount { return frameCount; }
  465.  
  466. - setImageIsShared:(BOOL)flag { imageIsShared = flag; return self; } 
  467. - setMovieIsShared:(BOOL)flag { movieIsShared = flag; return self; } 
  468.  
  469. - removeMovie:sender
  470. {
  471.   if (!movieIsShared)
  472.   {  [imageList freeObjects];
  473.      movieImage = nil;
  474.   }
  475.   else
  476.   {  [imageList empty];
  477.      movieImage = nil;
  478.   }
  479.   return self;
  480.   
  481. }
  482.  
  483. - setAnimDir:(const char *)filename 
  484. {
  485.    BOOL  done = NO;
  486.    char  theBaseName[MAXPATHLEN+1], 
  487.          tiffFileName[MAXPATHLEN+1], 
  488.          *animSuffixPtr;
  489.    int   frameIndex = 1;// remind me to slap Keith next time I see him...
  490.  
  491.  
  492.    if (filename && *filename)
  493.    {  if (!movieIsShared)
  494.       {  [imageList freeObjects];
  495.       }
  496.       movieImage = nil;
  497.       done = NO;
  498.       animSuffixPtr=rindex(filename, '.');
  499.       if (animSuffixPtr && strcmp(animSuffixPtr, ".anim"))
  500.       {  NXLogError("the file %s doesn't end in anim, dude!\n", filename);
  501.          return nil;
  502.       }
  503.       basename(filename, ".anim", theBaseName);  
  504.       while (!done)
  505.       {  sprintf(tiffFileName, "%s/%s.%d.tiff", filename, theBaseName, frameIndex);
  506.          // does the file exist?
  507.          if (access(tiffFileName, R_OK) == -1) 
  508.          {  done = YES;  // nope; we're outta here
  509.          }
  510.          else
  511.          {  movieImage = [NXImage findImageNamed:tiffFileName];
  512.             if (!movieImage) 
  513.             {  movieImage = [[NXImage alloc] init];
  514.                if (archiveMovieData)
  515.                {  [movieImage setDataRetained:YES];
  516.                }
  517.                if (![movieImage loadFromFile:tiffFileName])  
  518.                {  NXLogError("unable to load image from file <%s>\n", tiffFileName);
  519.                   done = YES; // guess the last image was bad; a common occurrence when the anim is still being generated...
  520.                   }
  521.             }
  522.             [imageList insertObject:movieImage at:(frameIndex - 1)]; // I really gotta whap Keith; 1 based indices; jeez...
  523.             frameIndex++;
  524.          }
  525.       }
  526.    }
  527.    frameCount = [imageList count];
  528.    currentFrameIndex = 0;
  529.    movieImage = [imageList objectAt:currentFrameIndex];
  530.    clearTheView = YES; [self display];
  531.    clearTheView = NO;  [window display];
  532.    NXPing();
  533.    if (stationaryBehavior == WW_PLAY_CONTINUOUS)
  534.    {  [self beginStationaryBehavior];
  535.    }
  536.  
  537.    return self;
  538. }
  539.  
  540.  
  541.  
  542. - removeImage:sender
  543. {
  544.    if (!imageIsShared)
  545.    {  [image free];
  546.    }
  547.    image = nil;
  548.    return self;
  549. }
  550.  
  551. - setImageFile:(const char *)filename 
  552. {
  553.     if (!imageIsShared)
  554.     {  [image free];
  555.     }
  556.     image = nil;
  557.     if (filename && *filename)
  558.     { image = [NXImage findImageNamed:filename];
  559.       if (!image) 
  560.       {  image = [[NXImage alloc] init];
  561.          [image setDataRetained:YES];
  562.          if (![image loadFromFile:filename])
  563.          {  NXLogError("unable to load image from file <%s>\n", filename);
  564.             return nil; 
  565.       }
  566.       }
  567.     }
  568.     clearTheView = YES; [self display];
  569.     clearTheView = NO;  [window display];
  570.     NXPing();
  571.     return self;
  572. }
  573.  
  574. - setImage:i  
  575. {  
  576.    if (!imageIsShared)
  577.    {  [image free];
  578.    }
  579.    image = i; 
  580.    clearTheView = YES; [self display];
  581.    clearTheView = NO;  [window display];
  582.    NXPing();
  583.    return self; 
  584. }
  585. - image { return image; }
  586.  
  587. - movieImage { return movieImage; }
  588.  
  589.  
  590. - drawSelf:(const NXRect *)theRect :(int)count
  591. {
  592.   NXPoint  p = {0.0, 0.0};
  593.   NXSize   imageSize, movieSize;
  594.  
  595.  
  596.   if (clearTheView)  
  597.   {  NXSetColor(backgroundColor);
  598.      PSsetalpha(0.0);
  599.      PScompositerect(theRect->origin.x, theRect->origin.y, 
  600.                theRect->size.width, theRect->size.height,
  601.                NX_SOVER);
  602.      return self;
  603.   }
  604.  
  605.   NXSetColor(backgroundColor);
  606.   if (!frameCount || drawCorrectly) // if there's no movie or we're being anal, use my alpha value
  607.   {  PSsetalpha(alpha);
  608.   }
  609.   else // otherwise, make it opaque, since the other views in the window aren't being redrawn too
  610.   {  PSsetalpha(1.0);
  611.   }
  612.  
  613.   PScompositerect(theRect->origin.x, theRect->origin.y, 
  614.               theRect->size.width, theRect->size.height,
  615.             NX_SOVER);
  616.  
  617.   [image getSize:&imageSize];
  618.   [movieImage getSize:&movieSize];
  619.  
  620.   switch (verticalLayoutType)
  621.   {  case WW_BOTTOMALIGNED:
  622.           p.y = 0.0;
  623.       break;
  624.      case WW_TOPALIGNED:
  625.           if (imageSize.height > movieSize.height)
  626.           {  p.y = theRect->size.height - imageSize.height;
  627.        }
  628.           else
  629.           {  p.y = theRect->size.height - movieSize.height;
  630.        }
  631.        break;
  632.     default:
  633.           p.y = 0.0;
  634.       break;
  635.   }
  636.   switch (horizontalLayoutType)
  637.   {  case NX_LEFTALIGNED:
  638.           p.x = 0.0;
  639.       break;
  640.      case NX_RIGHTALIGNED:
  641.           if (imageSize.width > movieSize.width)
  642.           {  p.x = theRect->size.width - imageSize.width;
  643.       }
  644.       else
  645.           {  p.x = theRect->size.width - movieSize.width;
  646.       }
  647.        break;
  648.      case NX_CENTERED:
  649.           if (imageSize.width > movieSize.width)
  650.           {  if (theRect->size.width > imageSize.width)
  651.              {  p.x = (theRect->size.width - imageSize.width)/2.0;
  652.              }
  653.              else
  654.              {  p.x = 0.0;
  655.              }
  656.       }
  657.           else
  658.           {  if (theRect->size.width > movieSize.width)
  659.              {  p.x = (theRect->size.width - movieSize.width)/2.0;
  660.              }
  661.              else
  662.              {  p.x = 0.0;
  663.              }
  664.       }
  665.  
  666.           if (imageSize.height > movieSize.height)
  667.           {  if (theRect->size.height > imageSize.height)
  668.              {  p.y = (theRect->size.height - imageSize.height)/2.0;
  669.              }
  670.              else
  671.              {  p.y = 0.0;
  672.              }
  673.        }
  674.           else
  675.           {  if (theRect->size.height > movieSize.height)
  676.              {  p.y = (theRect->size.height - movieSize.height)/2.0;
  677.              }
  678.              else
  679.              {  p.y = 0.0;
  680.              }
  681.        }
  682.       break;
  683.     default:
  684.           p.x = 0.0;
  685.       break;
  686.   }
  687.  
  688.   if (imageUnder)
  689.   {  [image composite:NX_SOVER toPoint:&p];
  690.      [movieImage composite:NX_SOVER toPoint:&p];
  691.   }
  692.   else
  693.   {  [movieImage composite:NX_SOVER toPoint:&p];
  694.      [image composite:NX_SOVER toPoint:&p];
  695.   }
  696.  
  697.   if (borderType)
  698.   {  NXRect  r = *theRect;
  699.  
  700.  
  701.      PSsetalpha(1.0);
  702.  
  703.      r.origin.x++; r.origin.y--;        
  704.      NXSetColor(NX_COLORBLACK); NXFrameRect(&r);
  705.  
  706.      r.origin.x--, r.origin.y++;        
  707.      NXSetColor(NX_COLORGRAY); NXFrameRect(&r);
  708.  
  709.      r.origin.x--; r.size.width++; r.size.height++; 
  710.      NXSetColor(NX_COLORWHITE); NXFrameRect(&r);
  711.   }
  712.  
  713.   return self;
  714. }
  715.  
  716. - (BOOL)acceptsFirstResponder { return YES; }
  717.  
  718. - setMyTrackingRect:(BOOL)flag
  719. {
  720.   NXRect  visible;
  721.  
  722.  
  723.   if (trackingRect)
  724.   {  [window discardTrackingRect:trackingRect];
  725.      trackingRect = 0;
  726.   }
  727.   if (flag)
  728.   {  if ([self getVisibleRect:&visible])
  729.      {  [self convertRect:&visible toView:nil];
  730.         [window setTrackingRect:&visible inside:NO owner:self tag:(int)self left:NO right:NO];
  731.         trackingRect = (int)self;
  732.      }
  733.   }
  734.   return self;
  735. }
  736.  
  737. // might want to allow alt-click to move only one frame...
  738. - mouseDown:(NXEvent *)theEvent
  739. {
  740.   if (theEvent->flags & NX_COMMANDMASK)  // show the control panel
  741.   {  [self loadControlPanel];
  742.      return self;
  743.   }
  744.   if (stationaryBehavior == WW_PLAY_MOUSE_CLICK)
  745.   {  if (!stationaryFrameTE)  // is it running?
  746.      {  if (theEvent->flags & NX_ALTERNATEMASK)  // click one frame
  747.         {  stationaryFrameTE = (DPSTimedEntry)1; // set it to something so the frame gets incremented...
  748.            [self showFrameAndIncrement:nil];
  749.            stationaryFrameTE = 0;
  750.         }
  751.         else
  752.         {  [self beginStationaryBehavior];  // start it up
  753.         }
  754.      }
  755.      else
  756.      {  [self endStationaryBehavior];  // stop it
  757.      }
  758.   }
  759.   return self;
  760. }
  761.  
  762. - mouseEntered:(NXEvent *)theEvent
  763. {
  764.   if (!stationaryFrameTE && (stationaryBehavior == WW_PLAY_MOUSE_IN))
  765.   {  [window makeFirstResponder:self]; // do I need to do this?
  766.      [self beginStationaryBehavior];
  767.   }
  768.   return self;
  769. }
  770.  
  771. - mouseExited:(NXEvent *)theEvent
  772. {
  773.   if (stationaryFrameTE && (stationaryBehavior == WW_PLAY_MOUSE_IN))
  774.   {  [self endStationaryBehavior];
  775.   }
  776.   return self;
  777. }
  778.  
  779. - (const char *)getInspectorClassName { return "WWSimpleMovieViewIBInspector"; }
  780.  
  781.  
  782. // Control Panel stuff (stolen from my IBInspector
  783.  
  784.  
  785. - loadControlPanel
  786. {
  787.    char buf[MAXPATHLEN + 1];
  788.    id bundle;
  789.  
  790.    
  791.    // we want to support two different things here.  
  792.    // 1st scenario: lots of MovieViews, screen space is expensive, we want to have these control panels 
  793.     
  794.    if (!controlPanel)
  795.    {  bundle = [NXBundle bundleForClass:[self class]];
  796.       [bundle getPath:buf forResource:"WWSimpleMovieViewControlPanel" ofType:"nib"];
  797.       [NXApp loadNibFile:buf owner:self withNames:NO fromZone:[self zone]];
  798.       if (!controlPanel)
  799.       {  NXLogError("problem loading WWSimpleMovieViewControlPanel\n");
  800.          return nil;
  801.       }
  802.       [controlPanel setDelegate:self];
  803.    }
  804.    [self revertControlPanel:nil];
  805.    [controlPanel makeKeyAndOrderFront:self];
  806.    return self;
  807. }
  808.  
  809.  
  810. - revertControlPanel:sender 
  811.    [theColor setColor:[self backgroundColor]];
  812.    [alphaSlider setFloatValue:[self alpha]];
  813.    if ([self drawCorrectly])
  814.    {  [alphaSlider setEnabled:YES];
  815.    }
  816.    else
  817.    {  if ( [self frameCount])
  818.       {  [alphaSlider setEnabled:NO];
  819.       }
  820.       else
  821.       {  [alphaSlider setEnabled:YES];
  822.       }
  823.    }
  824.    if ([self archiveImageData])
  825.    {  [theImage setStringValue:""];
  826.    }
  827.    else
  828.    {  [theImage setStringValue:[[self image] name]];
  829.    }
  830.  
  831.    if ([self archiveMovieData])
  832.    {  [theImage setStringValue:""];
  833.    }
  834.    else
  835.    {  [theMovie setStringValue:[[self movieImage] name]];
  836.    }
  837.  
  838.    [drawCorrectlySwitch setIntValue:[self drawCorrectly]];
  839.    [aspectRatioMatrix selectCellWithTag:[self aspectRatioType]];
  840.    [horizontalLayoutMatrix selectCellWithTag:[self horizontalLayoutType]];
  841.    [verticalLayoutMatrix selectCellWithTag:[self verticalLayoutType]];
  842.    [customAspectRatioText setFloatValue:[self aspectRatio]];
  843.    [customAspectRatioText setEnabled:([self aspectRatioType] == WW_ASPECT_CUSTOM)];
  844.    [scaleSwitch setIntValue:(int)[(WWSimpleMovieView *)self scaleToFit]];
  845.    [imageUnderSwitch setIntValue:[self imageUnder]];
  846.    [rotateTo setFloatValue:[self frameAngle]];
  847.    [borderTypeMatrix selectCellWithTag:[self borderType]];
  848.    [stationaryBehaviorMatrix selectCellWithTag:[self stationaryBehavior]];
  849.    [loopSwitch setIntValue:[self loop]];
  850.    [archiveMovieDataSwitch setIntValue:[self archiveMovieData]];
  851.    [archiveImageDataSwitch setIntValue:[self archiveImageData]];
  852.  
  853.    [fpsText setFloatValue:[self fps]];
  854.    clearTheView = YES; [self display];
  855.    clearTheView = NO;  [window display]; NXPing();
  856.  
  857.    return self;
  858. }
  859.  
  860. - takeColor_:s    { [self setBackgroundColor:[theColor color]]; [self revertControlPanel:nil]; return self; }
  861. - setAlpha_:s    { [self setBackgroundAlpha:[s floatValue]]; [self revertControlPanel:nil]; return self; }
  862. - setImage_:s    { [self setImageFile:(char *)[s stringValue]]; [self revertControlPanel:nil]; return self; }
  863. - setMovie_:s    { [self setAnimDir:(char *)[s stringValue]]; [self revertControlPanel:nil]; return self; }
  864. - setAspectRatio_:s    { [self setAspectRatio:s]; [self revertControlPanel:nil]; return self; }
  865. - setImageUnder_:s    { [self setImageUnder:[s intValue]]; [self revertControlPanel:nil]; return self; }
  866. - setScale_:s    { [self setScaleToFit:[s intValue]]; [self revertControlPanel:nil]; return self; }
  867. - setAspectRatioFromMatrix_:s    { [self setAspectRatioFromMatrix:s]; [self revertControlPanel:nil]; return self; }
  868. - setHorizontalLayoutFromMatrix_:s    { [self setHorizontalLayoutFromMatrix:s]; [self revertControlPanel:nil]; return self; }
  869. - setBorderTypeFromMatrix_:s    { [self setBorderType:[[s selectedCell] tag]]; [self revertControlPanel:nil]; return self; }
  870. - setVerticalLayoutFromMatrix_:s    { [self setVerticalLayoutFromMatrix:s]; [self revertControlPanel:nil]; return self; }
  871. - setFPS_:s    { [self setFPS:[s floatValue]]; [self revertControlPanel:nil]; return self; }
  872. - setStationaryBehaviorFromMatrix_:s    { [self setStationaryBehaviorFromMatrix:s]; [self revertControlPanel:nil]; return self; }
  873. - setLoop_:s    { [self setLoop:[s intValue]]; [self revertControlPanel:nil]; return self; }
  874. - setArchiveMovieData_:s    { [self setArchiveMovieData:[s intValue]]; [self revertControlPanel:nil]; return self; }
  875. - setArchiveImageData_:s    { [self setArchiveImageData:[s intValue]]; [self revertControlPanel:nil]; return self;  }
  876. - setDrawCorrectly_:s    { [self setDrawCorrectly:[s intValue]]; [self revertControlPanel:nil]; return self; }
  877.  
  878. - rotateTo_:s { [self rotateTo:[s floatValue]];  [self display]; [self revertControlPanel:nil]; return self; }
  879.  
  880. - sizeToImage_:s { [self sizeToImage:s];  [self display]; [self revertControlPanel:nil]; return self; }
  881. - sizeToMovie_:s { [self sizeToMovie:s];  [self display]; [self revertControlPanel:nil]; return self; }
  882.  
  883. - saveImage:sender
  884. {
  885.   static id savePanel=nil;
  886.   NXStream *ts;
  887.  
  888.  
  889.   if (!savePanel) 
  890.   {  savePanel=[SavePanel new];
  891.   }
  892.  
  893.   [savePanel setRequiredFileType:"tiff"];
  894.   if([savePanel runModal])
  895.   {  ts = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  896.      [image writeTIFF:ts allRepresentations:NO usingCompression:NX_TIFF_COMPRESSION_LZW andFactor:1.0];
  897.      NXSaveToFile(ts, [savePanel filename]);
  898.      NXCloseMemory(ts,NX_FREEBUFFER);
  899.   }
  900.  
  901.   return self;
  902. }
  903.  
  904. - saveMovie:sender
  905. {
  906.   static 
  907.       id    savePanel=nil;
  908.   NXStream  *ts;
  909.   int       ret, i;
  910.   char      imageNameBase[MAXPATHLEN+1];
  911.   char      imageFilename[MAXPATHLEN+1];
  912.   char      *animFilename;
  913.  
  914.  
  915.   if (!savePanel) 
  916.   {  savePanel=[SavePanel new];
  917.   }
  918.  
  919.   [savePanel setRequiredFileType:"anim"];
  920.   if([savePanel runModal])
  921.   {  // this is a little dangerous, but hey, it's faster...
  922.      animFilename = (char *)[savePanel filename];
  923.      ret = mkdir(animFilename, 0777);
  924.      if (ret)
  925.      {  NXLogError("couldn't save anim to %s", animFilename);
  926.         return nil;
  927.      }
  928.      basename(animFilename, ".anim", imageNameBase);
  929.  
  930.      for (i = 1; i < frameCount; i++)
  931.      {  ts = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  932.         if (!ts)
  933.         {  NXLogError("unable to open a memory stream!\n");
  934.            return nil;
  935.         }
  936.         if (![[imageList objectAt:(i-1)] writeTIFF:ts allRepresentations:NO usingCompression:NX_TIFF_COMPRESSION_LZW andFactor:1.0])
  937.         {  NXLogError("unable to write image %d to a memory stream!\n", i);
  938.            NXCloseMemory(ts, NX_FREEBUFFER);
  939.            return nil;
  940.         }
  941.         sprintf(imageFilename, "%s/%s.%d.tiff", animFilename, imageNameBase, i);
  942.         if (NXSaveToFile(ts, imageFilename))
  943.         {  NXLogError("unable to save memory stream to file <%s>\n", imageFilename);
  944.            NXCloseMemory(ts, NX_FREEBUFFER);
  945.            return nil;
  946.         }
  947.         NXCloseMemory(ts, NX_SAVEBUFFER);
  948.      }
  949.      ts = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  950.      if (!ts)
  951.      {  NXLogError("unable to open a memory stream!\n");
  952.         return nil;
  953.      }
  954.      if (![[imageList objectAt:(i-1)] writeTIFF:ts allRepresentations:NO usingCompression:NX_TIFF_COMPRESSION_LZW andFactor:1.0])
  955.      {  NXLogError("unable to write image %d to a memory stream!\n", i);
  956.         NXCloseMemory(ts, NX_FREEBUFFER);
  957.         return nil;
  958.      }
  959.      sprintf(imageFilename, "%s/%s.%d.tiff", animFilename, imageNameBase, i);
  960.      if (NXSaveToFile(ts, imageFilename))
  961.      {  NXLogError("unable to save memory stream to file <%s>\n", imageFilename);
  962.         NXCloseMemory(ts, NX_FREEBUFFER);
  963.         return nil;
  964.      }
  965.      NXCloseMemory(ts, NX_FREEBUFFER);
  966.   }
  967.  
  968.   return self;
  969. }
  970.  
  971.  
  972. - windowWillClose:sender
  973. {
  974.   if (controlPanel && (sender == controlPanel))
  975.   { controlPanel = nil;
  976.   }
  977.   return self;
  978. }
  979.  
  980.  
  981.  
  982. #define typeVectorVersion1 "@fifcciiiffcicccc@"
  983. #define typeValuesVersion1 &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType, &horizontalLayoutType, &verticalLayoutType, &fps, &frameTimeIncr, &loop, &stationaryBehavior, &sink, &source, &archiveImageData, &archiveMovieData, &imageList
  984.  
  985. #define typeVectorVersion2 "@fifcciiiffcicccc@c"
  986. #define typeValuesVersion2 &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType, &horizontalLayoutType, &verticalLayoutType, &fps, &frameTimeIncr, &loop, &stationaryBehavior, &sink, &source, &archiveImageData, &archiveMovieData, &imageList, &drawCorrectly
  987.  
  988. #define typeVector "@fifcciiiffcicccc@ccc"
  989. #define typeValues &image, &alpha, &aspectRatioType, &aspectRatio, &imageUnder, &scaleToFit, &borderType, &horizontalLayoutType, &verticalLayoutType, &fps, &frameTimeIncr, &loop, &stationaryBehavior, &sink, &source, &archiveImageData, &archiveMovieData, &imageList, &drawCorrectly, &imageIsShared, &movieIsShared
  990.  
  991. - read:(NXTypedStream*)stream 
  992. {
  993.     int version;
  994.     [super read:stream];
  995.  
  996.  
  997.     version = NXTypedStreamClassVersion(stream, "WWSimpleMovieView");
  998.     if (version == 0) NXReadTypes(stream, "i", &version), version=1;
  999.     if (version == 1)
  1000.     {  NXReadTypes(stream, typeVectorVersion1, typeValuesVersion1);
  1001.        backgroundColor = NXReadColor(stream);
  1002.        drawCorrectly = YES;
  1003.        imageIsShared = NO;
  1004.        movieIsShared = NO;
  1005.     } 
  1006.     if (version == 2)
  1007.     {  NXReadTypes(stream, typeVectorVersion2, typeValuesVersion2);
  1008.        backgroundColor = NXReadColor(stream);
  1009.        imageIsShared = NO;
  1010.        movieIsShared = NO;
  1011.     } 
  1012.     if (version == 3)
  1013.     {  NXReadTypes(stream, typeVector, typeValues);
  1014.        backgroundColor = NXReadColor(stream);
  1015.     } 
  1016.     return self;
  1017. }
  1018.  
  1019.  
  1020. - write:(NXTypedStream *)stream 
  1021. {
  1022.     [super write:stream];
  1023.     NXWriteTypes(stream, typeVector, typeValues);
  1024.     NXWriteColor(stream, backgroundColor);
  1025.  
  1026.     return self;
  1027. }
  1028.  
  1029.     
  1030.  
  1031. @end
  1032.