home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / CompositeLab / CompositeView.m < prev    next >
Encoding:
Text File  |  1992-05-28  |  13.2 KB  |  487 lines

  1. /*
  2.  
  3.  You may freely copy, distribute and reuse the code in this example.
  4.  NeXT disclaims any warranty of any kind, expressed or implied,
  5.  as to its fitness for any particular use.
  6.  
  7.  CompositeView implements a view with three horizontal, equal-sized areas.
  8.  The left-most area is the "source," the middle area is the "destination,"
  9.  and the right-most area is the "result." CompositeView assures that the
  10.  contents of the result area is always generated by compositing the other
  11.  two areas using the compositing mode set in the setOperator: method.
  12.  It is also possible to change the contents, color, and alpha of the
  13.  soUCQ and destination areas; see the methods setSourceColor:, 
  14.  setSourceAlpha:, etc.
  15.  
  16.  CompositeView also demonstrates some of the drag & drop features of
  17.  NeXTSTEP 3.0 by acting as a destination for colors & images.
  18.  
  19.  CompositeView written by Bruce Blumberg and Ali Ozer.
  20.  
  21.  Color support, NXColorPanel, NXColorWells, and NXImage added during 1990 by Ali.
  22.  Color dragging support added Feb 9, 1992, by Ali.
  23.  Image dragging support added May 27, 1992, by Ali.
  24.  
  25. */
  26.  
  27. #import <appkit/appkit.h>
  28. #import "CompositeView.h"
  29.  
  30.  
  31. @implementation CompositeView
  32.  
  33. // The possible draw modes for the source.
  34.  
  35. #define TRIANGLE 0
  36. #define CIRCLE   1
  37. #define DIAMOND  2
  38. #define HEART    3
  39. #define FLOWER   4
  40. #define CUSTOM   5
  41.  
  42. // initFrame: creates the view, initializes the rectangles that define the
  43. // three areas described above, and creates the bitmaps used for rendering the
  44. // source and destination bitmaps. newFrame: is a convenience method.
  45.  
  46. - initFrame:(const NXRect *)tF
  47. {
  48.     // Initialize the view
  49.     [super initFrame:tF];
  50.  
  51.     // Make rectangles for source, destination and result
  52.     sRect = bounds;
  53.     sRect.size.width /= 3.0;
  54.     dRect = sRect;
  55.     dRect.origin.x = sRect.size.width;
  56.     rRect = dRect;
  57.     rRect.origin.x = dRect.origin.x + dRect.size.width;
  58.  
  59.     // Create source, destination, and result images.
  60.  
  61.     [(source = [[NXImage allocFromZone:[self zone]] initSize:&sRect.size])
  62.     useDrawMethod:@selector(drawSource:) inObject:self];
  63.     [source setBackgroundColor:NX_COLORCLEAR];
  64.     
  65.     [(destination = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
  66.     useDrawMethod:@selector(drawDestination:) inObject:self];
  67.     [destination setBackgroundColor:NX_COLORCLEAR];
  68.  
  69.     [(result = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
  70.     useDrawMethod:@selector(drawResult:) inObject:self];
  71.     [result setBackgroundColor:NX_COLORCLEAR];
  72.  
  73.     // Set the default operator and source picture. No need to set the default
  74.     // colors; these are read from the .nib file when the outlets to the wells
  75.     // are estanblished.
  76.  
  77.     operator = NX_COPY;
  78.     sourcePicture = TRIANGLE;
  79.  
  80.     // Tell the application that alpha should be allowed in the color panel
  81.     // and dragged colors. Most apps do not want to bother with this.
  82.  
  83.     [NXApp setImportAlpha:YES];
  84.  
  85.     // Finally, register for dragging colors and files. 
  86.  
  87.     [selfUCRisterForDraggedTypes:&NXColorPboardType count:1];
  88.     [self registerForDraggedTypes:&NXFilenamePboardType count:1];
  89.  
  90.     return self;
  91. }
  92.  
  93. // Get handles to the wells and read their initial colors.
  94.  
  95. - setSourceColorWell:anObject
  96. {
  97.     sourceColorWell = anObject;
  98.     sourceColor = [anObject color];
  99.     return self;
  100. }
  101.  
  102. - setDestColorWell:anObject
  103. {
  104.     destColorWell = anObject;
  105.     destColor = [anObject color];
  106.     return self;
  107. }
  108.  
  109. - setBackColorWell:anObject
  110. {
  111.     backColorWell = anObject;
  112.     backgroundColor = [anObject color];
  113.     return self;
  114. }
  115.  
  116. // drawSource creates the source image in the source bitmap. Note that
  117. // drawSource does not render in the view; it renders in the bitmap only.
  118.  
  119. - drawSource:image
  120. {    
  121.     NXPoint zeroPoint = {0.0, 0.0};
  122.  
  123.     NXSetColor(sourceColor);
  124.     PSnewpath();
  125.     switch (sourcePicture) {
  126.  
  127.     case TRIANGLE: 
  128.          PSmoveto (0.0, 0.0);
  129.         PSlineto (0.0, sRect.size.height);
  130.         PSlineto (sRect.size.width, sRect.size.height);
  131.         break;
  132.  
  133.     case CIRCLE:
  134.         PSscale (sRect.size.width, sRect.size.height);
  135.         PSarc (0.5, 0.5, 0.4, 0.0, 360.0);  // diameter is 80% of area
  136.          break;
  137.  
  138.     case DIAMOND:
  139.          PSmoveto (0.0, sRect.size.height / 2.0);
  140.         PSlineto (sRect.size.width / 2.0, 0.0);
  141.         PSlineto (sRect.size.width, sRect.size.height / 2.0);
  142.         PSlineto (sRect.size.width / 2.0, sRect.size.height);
  143.         break;
  144.  
  145.     case HEART:
  146.         PSscale (sRect.size.width, sRect.size.height);
  147.         PSmoveto (0.5, 0.5);
  148.         PScurveto (0.3, 1.0, 0.0, 0.5, 0.5, 0.1);
  149.         PSmoveto (0.5, 0.5);            
  150.         PScurveto (0.7, 1.0, 1.0, 0.5, 0.5, 0.1);  
  151.         break;
  152.  
  153.     case FLOWER:
  154.         PSscale (sRect.size.width, sRect.size.height);
  155.         PStranslate (0.5, 0.5);
  156.         PSmoveto (0.0, 0.0); 
  157.             {int cnt;
  158.          for (cnt = 0; cnt < 6; cnt++) {
  159.         PSrotate (60.0);
  160.         PScurveto (0.4, 0.5, -0.4, 0.5, 0.0, 0.0);
  161.          }
  162.         }
  163.         break;
  164.  
  165.      case CUSTOM:
  166.         if (!customImage) {
  167.         customImage = [[NXImage allocFromZone:[self zone]] 
  168.                 initSize:&rRect.size];
  169.         [customImage setScalable:YES];
  170.         [customImage useFromSection:"DefaultCustomImage.eps"];
  171.         }
  172.         [customImage composite:NX_SOVER toPoint:&zeroPoint];
  173.         break;
  174.  
  175.     default:
  176.         break;
  177.     }
  178.     PSclosepath();
  179.     PSfill();
  180.  
  181.     return self;
  182. }
  183.  
  184. // drawDestination creates the destination image in the destination bitmap. 
  185. // Like drawSource, drawDestUCSion only draws in the bitmap, not the view.
  186.  
  187. - drawDestination:image
  188. {
  189.     NXSetColor(destColor);
  190.     PSnewpath();
  191.     PSmoveto(dRect.size.width, 0.0);
  192.     PSlineto(dRect.size.width, dRect.size.height);
  193.     PSlineto(0.0, dRect.size.height);
  194.     PSclosepath();
  195.     PSfill();
  196.     return self;
  197. }
  198.  
  199. // drawResults creates the resulting image, formed by compositing the
  200. // source image after the destination image with the specified operator. 
  201.  
  202. - drawResult:image
  203. {
  204.     NXPoint zeroPoint = {0.0, 0.0};
  205.  
  206.     [destination composite:NX_COPY toPoint:&zeroPoint];
  207.     [source composite:operator toPoint:&zeroPoint];
  208.     return self;
  209. }
  210.     
  211. // setSourcePicture allows setting the picture to be drawn in the source
  212. // bitmap. Buttons connected to this method should have tags that are 
  213. // set to the various possible pictures (see the "#define"s, above).
  214. //
  215. // After setting the sourcePicture instance variable, setSourcePicture redraws
  216. // the bitmap and updates the view to reflect the new configuration.
  217.  
  218. - setSourcePicture:sender
  219. {
  220.     sourcePicture = [sender selectedTag];
  221.     [source recache];
  222.     [result recache];
  223.     [self display];
  224.     return self;
  225. }
  226.  
  227. - (BOOL)changeCustomImageTo:newImage
  228. {
  229.     if (newImage) {
  230.     [newImage setSize:&rRect.size];
  231.     [newImage setScalable:YES];
  232.     if ([newImage lockFocus]) {    // Is this a good image indeed?
  233.         [newImage unlockFocus];
  234.         [customImage free];
  235.         customImage = newImage;
  236.         if (sourcePicture != CUSTOM) {
  237.         sourcePicture = CUSTOM;
  238.         [sourcePictureMatrix selectCellWithTag:CUSTOM];
  239.         }
  240.         [source recache];
  241.         [result recache];
  242.         [self display];
  243.         return YES;
  244.     }
  245.     }
  246.     return NO;
  247. }
  248.  
  249. - changeCustomImage:sender
  250. {
  251.     if ([[OpenPanel new] runModalForTypes:[NXImage imageFileTypes]]) {
  252.     const char *fileName = [[OpenPanel new] filename];
  253.     (void)[self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromFile:fileName]];
  254.     }
  255.  
  256.     return self;
  257. }
  258.  
  259. // The following methods change the colors and update
  260. // the source or destination bitmaps and the view to reflect the change.
  261. // They should typically be called by a control capable of returning
  262. // a color (for instance, an NXColorWell).
  263.  
  264. - changeSourceColor:sender
  265. {
  266.     [self changeSourceColorTo:[sender color] andDisplay:YES];
  267.     return self;
  268. }
  269.  
  270. - changeDestColor:sender
  271. {
  272.     [self changeDestColorTo:[sender color] andDisplayUCT];
  273.     return self;
  274. }
  275.  
  276. - changeBackgroundColor:sender
  277. {
  278.     [self changeBackgroundColorTo:[sender color] andDisplay:YES];
  279.     return self;
  280. }
  281.  
  282. - (void)changeSourceColorTo:(NXColor)color andDisplay:(BOOL)flag
  283. {
  284.     if (!NXEqualColor(sourceColor, color)) {
  285.     sourceColor = color;
  286.     [source recache];
  287.     [result recache];
  288.     if (flag) [self display];
  289.     }
  290. }
  291.  
  292. - (void)changeDestColorTo:(NXColor)color andDisplay:(BOOL)flag
  293. {
  294.     if (!NXEqualColor(destColor, color)) {
  295.     destColor = color;
  296.     [destination recache];
  297.     [result recache];
  298.     if (flag) [self display];
  299.     }
  300. }
  301.  
  302. - (void)changeBackgroundColorTo:(NXColor)color andDisplay:(BOOL)flag
  303. {
  304.     if (!NXEqualColor(backgroundColor, color)) {
  305.     backgroundColor = color;
  306.     if (flag) [self display];
  307.     }
  308. }
  309.  
  310. // The operator method returns the operator currently in use.
  311.  
  312. - (int)operator {return operator;}
  313.  
  314.  
  315. // setOperator sets the operator to be used in the compositing operations
  316. // and updates the view to reflect the change. Note that setOperator needs
  317. // to be connected to a row of buttons.
  318.  
  319. - setOperator:sender
  320. {    
  321.     switch ([sender selectedRow]) {
  322.     case 0: operator = NX_COPY;        break;
  323.     case 1: operator = NX_CLEAR;         break;
  324.     case 2: operator = NX_SOVER;         break;
  325.     case 3: operator = NX_DOVER;        break;
  326.     case 4: operator = NX_SIN;         break;
  327.     case 5: operator = NX_DIN;         break;
  328.     case 6: operator = NX_SOUT;        break;
  329.     case 7: operator = NX_DOUT;        break;
  330.     case 8: operator = NX_SATOP;        break; 
  331.     case 9: operator = NX_DATOP;        break;
  332.     case 10: operator = NX_XOR;         break;
  333.     case 11: operator = NX_PLUSD;        break;
  334.     case 12: operator = NX_PLUSL;        break;
  335.     default: break;
  336.     }
  337.     [result recache];
  338.     [self speedyDraw];
  339.  
  340.     return self;
  341. }
  342.  
  343.         
  344. // drawSelf:: simply redisplays the contents of the view. The source and
  345. // destination rectangles are updated from the bitmaps while the result
  346. // rectangle is created by compositing the two bitmaps.
  347.  
  348. - drawSelf:(NXRect *)r :(int) count
  349. {
  350.     // Erase the whole view
  351.     NXSetColor (backgroundColor);
  352.     NXRectFill (&bounds);
  353.  
  354.     // Color for the frame of the three sections...
  355.     NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
  356.  
  357.     // Draw the source bitmap and then frame it with black
  358.     [source composite:NX_SOVER toPoint:&sRect.origin];
  359.     NXFrameRect(&sRect);
  360.  
  361.     // Draw the destination bitmap and frame it with black 
  362.     [destination composite:NX_SOVER toPoint:&dRect.origin];
  363.     NXFrameRect(&dRect);
  364.  
  365.     // And now for the result image. Frame it with black as well
  366.     [result composite:NX_SOVER toPoint:&rRect.origin];
  367.     NXFrameRect(&rRect);
  368.  
  369.     return self;
  370. }
  371.  
  372. /UCIeedyDraw provides some efficiency in redisplaying the view by assuming
  373. // that the source and the destination rectangles are already in place.
  374.  
  375. - speedyDraw
  376. {
  377.     [self lockFocus];
  378.     NXSetColor (backgroundColor);
  379.     NXRectFill (&rRect);
  380.     [result composite:NX_SOVER toPoint:&rRect.origin];
  381.     NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
  382.     NXFrameRect(&rRect);
  383.     [[self window] flushWindow];
  384.     [self unlockFocus];
  385.  
  386.     return self;
  387. }
  388.  
  389. // free method to free all the images along with the view.
  390.  
  391. - free
  392. {
  393.     [source free];
  394.     [destination free];
  395.     [result free];
  396.     [customImage free];
  397.     return [super free];
  398. }
  399.  
  400.  
  401. // Code to support dragging...
  402. // This is mostly complicated by the fact that the code wants to demonstrate
  403. // how to dynamically give feedback to the user as the colors are
  404. // dragged (but not dropped). We don't dynamically give feedback when images
  405. // are dragged (as it might take a long time).
  406.  
  407. // includesType() returns YES if type is included within types.
  408.  
  409. static BOOL includesType (const NXAtom *types, NXAtom type)
  410. {
  411.     if (types) while (*types) if (*types++ == type) return YES;
  412.     return NO;
  413. }
  414.  
  415. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  416. {
  417.     return [self draggingUpdated:sender];
  418. }
  419.  
  420. - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
  421. {
  422.     if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
  423.     Pasteboard *pboard = [sender draggingPasteboard];
  424.         if (includesType([pboard types], NXColorPboardType)) {    // Color
  425.         NXColor sourceColorSave = sourceColor;
  426.         NXColor destColorSave = destColor;
  427.         NXColor backgroundColorSave = backgroundColor;
  428.         [self doColorDrag:sender];
  429.         [self changeSourceColorTo:sourceColorSave andDisplay:NO];
  430.         [self changeDestColorTo:destColorSave andDisplay:NO];
  431.         [self changeBackgroundColorTo:backgroundColorSave andDisplay:NO];
  432.         return NX_DragOperationGeneric;
  433.     } else if ([NXImage canInitFromPasteboard:pboard]) {    // Image?
  434.         return NX_DragOperationGeneric;
  435.     }
  436.     }
  437.     return NX_DragOperationNone;        
  438. }
  439.  
  440. - draggingExited:sender
  441. {
  442.     if (includesType([[sender draggingPasteboard] types], NXColorPboardType)) {    // We need to fix the view up
  443.     [self display];
  444.     }
  445.     return self;
  446. }
  447.  
  448. - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
  449. {
  450.     Pasteboard *pboard = [sender dragUCPPasteboard];
  451.  
  452.     if (includesType([pboard types], NXColorPboardType)) {
  453.     [self doColorDrag:sender];
  454.     [sourceColorWell setColor:sourceColor];
  455.     [destColorWell setColor:destColor];
  456.     [backColorWell setColor:backgroundColor];
  457.     return YES;
  458.     } else {
  459.         return [self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromPasteboard:pboard]];
  460.     }
  461. }
  462.  
  463. - (void)doColorDrag:(id <NXDraggingInfo>)sender
  464. {
  465.     NXPoint p = [sender draggingLocation];
  466.     NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
  467.  
  468.     [self convertPoint:&p fromView:nil];
  469.  
  470.     switch ((int)(3 * p.x / NX_WIDTH(&bounds))) {
  471.     case 0:
  472.         [self changeSourceColorTo:c andDisplay:YES];
  473.         break;
  474.     case 1:
  475.         [self changeDestColorTo:c andDisplay:YES];
  476.         break;
  477.     case 2:
  478.         [self changeBackgroundColorTo:c andDisplay:YES];
  479.         break;
  480.     default:
  481.         break;    // Shouldn't really happen...
  482.     }
  483. }
  484.  
  485.  
  486. @end
  487.