home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Examples / AppKit / CompositeLab / CompositeView.m < prev    next >
Text File  |  1996-04-16  |  15KB  |  437 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.  source and destination areas; see the methods setSourceColor:, 
  14.  setSourceAlpha:, etc.
  15.  
  16.  CompositeView also demonstrates some of the drag & drop features of
  17.  OPENSTEP 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 in 1990.
  22.  Color dragging support added Feb 9, 1992.
  23.  Image dragging support added May 27, 1992.
  24.  Image dragging problem fixed Sep 29, 1992 (for 3.1).
  25.  Conversion to OPENSTEP, Jan 21, 1996.
  26.  
  27. */
  28.  
  29. #import <AppKit/AppKit.h>
  30. #import "CompositeView.h"
  31.  
  32. @implementation CompositeView
  33.  
  34. // initWithFrame: creates the view, initializes the rectangles that define the
  35. // three areas described above, and creates the bitmaps used for rendering the
  36. // source and destination bitmaps.
  37.  
  38. - (id)initWithFrame:(NSRect)rect {
  39.  
  40.     // Initialize the view
  41.  
  42.     if (!(self = [super initWithFrame:rect])) return nil;
  43.  
  44.     // Make rectangles for source, destination and result
  45.  
  46.     sRect = [self bounds];
  47.     sRect.size.width /= 3.0;
  48.     dRect = sRect;
  49.     dRect.origin.x = sRect.size.width;
  50.     rRect = dRect;
  51.     rRect.origin.x = dRect.origin.x + dRect.size.width;
  52.  
  53.     // Create source, destination, and result images.
  54.  
  55.     [(source = [[NSImage allocWithZone:[self zone]] initWithSize:sRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawSource:) delegate:self] autorelease]];
  56.     [source setBackgroundColor:[NSColor clearColor]];
  57.     
  58.     [(destination = [[NSImage allocWithZone:[self zone]] initWithSize:dRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawDestination:) delegate:self] autorelease]];
  59.     [destination setBackgroundColor:[NSColor clearColor]];
  60.  
  61.     [(result = [[NSImage allocWithZone:[self zone]] initWithSize:dRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawResult:) delegate:self] autorelease]];
  62.     [result setBackgroundColor:[NSColor clearColor]];
  63.  
  64.     // Set the default operator and source picture. No need to set the default
  65.     // colors; these are read from the .nib file when the outlets to the wells
  66.     // are estanblished.
  67.  
  68.     operator = NSCompositeCopy;
  69.     sourcePicture = TrianglePicture;
  70.  
  71.     // Tell the application that alpha should be allowed in the color panel
  72.     // and dragged colors. Most apps do not want to bother with this.
  73.  
  74.     [NSColor setIgnoresAlpha:NO];
  75.  
  76.     // Finally, register for dragging colors and files. 
  77.  
  78.     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSColorPboardType, NSFilenamesPboardType, nil]];
  79.  
  80.     return self;
  81. }
  82.  
  83. // Get handles to the wells and read their initial colors.
  84.  
  85. - (void)setSourceColorWell:(id)anObject {
  86.     sourceColorWell = anObject;
  87.     sourceColor = [[anObject color] copyWithZone:[self zone]]; 
  88. }
  89.  
  90. - (void)setDestColorWell:(id)anObject {
  91.     destColorWell = anObject;
  92.     destColor = [[anObject color] copyWithZone:[self zone]];
  93. }
  94.  
  95. - (void)setBackColorWell:(id)anObject {
  96.     backColorWell = anObject;
  97.     backgroundColor = [[anObject color] copyWithZone:[self zone]]; 
  98. }
  99.  
  100. // drawSource: creates the source image in the  currently locked focus.
  101. // It's specified as a callback to the source NSImage, allowed the image to
  102. // be redrawn whenever needed...
  103.  
  104. - (void)drawSource:(NSCustomImageRep *)imageRep {    
  105.     [sourceColor set];
  106.     PSnewpath();
  107.     switch (sourcePicture) {
  108.  
  109.     case TrianglePicture: 
  110.          PSmoveto (0.0, 0.0);
  111.         PSlineto (0.0, sRect.size.height);
  112.         PSlineto (sRect.size.width, sRect.size.height);
  113.         break;
  114.  
  115.     case CirclePicture:
  116.         PSscale (sRect.size.width, sRect.size.height);
  117.         PSarc (0.5, 0.5, 0.4, 0.0, 360.0);  // diameter is 80% of area
  118.          break;
  119.  
  120.     case DiamondPicture:
  121.          PSmoveto (0.0, sRect.size.height / 2.0);
  122.         PSlineto (sRect.size.width / 2.0, 0.0);
  123.         PSlineto (sRect.size.width, sRect.size.height / 2.0);
  124.         PSlineto (sRect.size.width / 2.0, sRect.size.height);
  125.         break;
  126.  
  127.     case HeartPicture:
  128.         PSscale (sRect.size.width, sRect.size.height);
  129.         PSmoveto (0.5, 0.5);
  130.         PScurveto (0.3, 1.0, 0.0, 0.5, 0.5, 0.1);
  131.         PSmoveto (0.5, 0.5);            
  132.         PScurveto (0.7, 1.0, 1.0, 0.5, 0.5, 0.1);  
  133.         break;
  134.  
  135.     case FlowerPicture:
  136.         PSscale (sRect.size.width, sRect.size.height);
  137.         PStranslate (0.5, 0.5);
  138.         PSmoveto (0.0, 0.0); 
  139.             {int cnt;
  140.          for (cnt = 0; cnt < 6; cnt++) {
  141.         PSrotate (60.0);
  142.         PScurveto (0.4, 0.5, -0.4, 0.5, 0.0, 0.0);
  143.          }
  144.         }
  145.         break;
  146.  
  147.      case CustomPicture:
  148.         if (!customImage) {
  149.         NSString *fileName = [[NSBundle mainBundle] pathForImageResource:@"DefaultCustomImage.eps"];
  150.         if (fileName) {
  151.                     customImage = [[NSImage allocWithZone:[self zone]] initByReferencingFile:fileName];
  152.                     [customImage setScalesWhenResized:YES];
  153.                     [customImage setSize:rRect.size];
  154.         }
  155.         }
  156.             [customImage compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
  157.         break;
  158.  
  159.     default:
  160.         break;
  161.     }
  162.     PSclosepath();
  163.     PSfill(); 
  164. }
  165.  
  166. // drawDestination: creates the destination image; like drawSource: is it
  167. // specified as a callback to the destination NSImage
  168.  
  169. - (void)drawDestination:(NSCustomImageRep *)imageRep {
  170.     [destColor set];
  171.     PSnewpath();
  172.     PSmoveto(dRect.size.width, 0.0);
  173.     PSlineto(dRect.size.width, dRect.size.height);
  174.     PSlineto(0.0, dRect.size.height);
  175.     PSclosepath();
  176.     PSfill(); 
  177. }
  178.  
  179. // drawResult: creates the resulting image, formed by compositing the
  180. // source image after the destination image with the specified operator. 
  181.  
  182. - (void)drawResult:(NSCustomImageRep *)imageRep {
  183.     [destination compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
  184.     [source compositeToPoint:NSZeroPoint operation:operator]; 
  185. }
  186.     
  187. // setSourcePicture: allows setting the picture to be drawn in the source
  188. // image. Buttons connected to this method should have tags that are 
  189. // set to the various possible pictures.
  190. //
  191. // After setting the sourcePicture instance variable, setSourcePicture:
  192. // invalidates the images and the view to reflect the new configuration.
  193.  
  194. - (void)setSourcePicture:(id)sender {
  195.     sourcePicture = [sender selectedTag];
  196.     [source recache];
  197.     [result recache];
  198.     [self setNeedsDisplay:YES]; 
  199. }
  200.  
  201. // changeCustomImageTo: allows changing the image to be drawn
  202. // in the "Custom" drawing mode
  203.  
  204. - (BOOL)changeCustomImageTo:(NSImage *)newImage {
  205.     if (newImage && (newImage != customImage)) {
  206.     [newImage setSize:rRect.size];
  207.     [newImage setScalesWhenResized:YES];
  208.     if ([newImage isValid]) {
  209.         [customImage release];
  210.         customImage = newImage;
  211.         if (sourcePicture != CustomPicture) {
  212.         sourcePicture = CustomPicture;
  213.         [sourcePictureMatrix selectCellWithTag:CustomPicture];
  214.         }
  215.         [source recache];
  216.         [result recache];
  217.         [self setNeedsDisplay:YES];
  218.         return YES;
  219.     }
  220.     }
  221.     return NO;
  222. }
  223.  
  224. // Action method to change the custom image; puts up an open panel allowing any
  225. // recognized image file type to be chosen
  226.  
  227. - (void)changeCustomImage:(id)sender {
  228.     NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  229.     if ([openPanel runModalForTypes:[NSImage imageFileTypes]]) {
  230.     (void)[self changeCustomImageTo:[[NSImage allocWithZone:[self zone]] initByReferencingFile:[openPanel filename]]];
  231.     } 
  232. }
  233.  
  234. // The following methods change the colors and update
  235. // the source or destination bitmaps and the view to reflect the change.
  236. // They should typically be called by a control capable of returning
  237. // a color (for instance, an NSColorWell).
  238.  
  239. - (void)changeSourceColor:(id)sender {
  240.     [self changeSourceColorTo:[sender color] andDisplay:YES]; 
  241. }
  242.  
  243. - (void)changeDestColor:(id)sender {
  244.     [self changeDestColorTo:[sender color] andDisplay:YES]; 
  245. }
  246.  
  247. - (void)changeBackgroundColor:(id)sender {
  248.     [self changeBackgroundColorTo:[sender color] andDisplay:YES]; 
  249. }
  250.  
  251. - (void)changeSourceColorTo:(NSColor *)color andDisplay:(BOOL)flag {
  252.     if (![sourceColor isEqual:color]) {
  253.     [sourceColor release];
  254.     sourceColor = [color copyWithZone:[self zone]];
  255.     [source recache];
  256.     [result recache];
  257.     if (flag) [self setNeedsDisplay:YES];
  258.     }
  259. }
  260.  
  261. - (void)changeDestColorTo:(NSColor *)color andDisplay:(BOOL)flag {
  262.     if (![destColor isEqual:color]) {
  263.     [destColor release];
  264.     destColor = [color copyWithZone:[self zone]];
  265.     [destination recache];
  266.     [result recache];
  267.         if (flag) [self setNeedsDisplay:YES];
  268.     }
  269. }
  270.  
  271. - (void)changeBackgroundColorTo:(NSColor *)color andDisplay:(BOOL)flag {
  272.     if (![backgroundColor isEqual:color]) {
  273.     [backgroundColor release];
  274.     backgroundColor = [color copyWithZone:[self zone]];
  275.         if (flag) [self setNeedsDisplay:YES];
  276.     }
  277. }
  278.  
  279. // The operator method returns the operator currently in use.
  280.  
  281. - (NSCompositingOperation)operator {
  282.     return operator;
  283. }    
  284.  
  285. // setOperator sets: the operator to be used in the compositing operations
  286. // and updates the view to reflect the change. Note that setOperator needs
  287. // to be connected to a row of buttons.
  288.  
  289. - (void)setOperator:(id)sender {
  290.     switch ([sender selectedRow]) {
  291.     case 0: operator = NSCompositeCopy;            break;
  292.     case 1: operator = NSCompositeClear;            break;
  293.     case 2: operator = NSCompositeSourceOver;         break;
  294.     case 3: operator = NSCompositeDestinationOver;        break;
  295.     case 4: operator = NSCompositeSourceIn;         break;
  296.     case 5: operator = NSCompositeDestinationIn;         break;
  297.     case 6: operator = NSCompositeSourceOut;        break;
  298.     case 7: operator = NSCompositeDestinationOut;        break;
  299.     case 8: operator = NSCompositeSourceAtop;        break; 
  300.     case 9: operator = NSCompositeDestinationAtop;        break;
  301.     case 10: operator = NSCompositeXOR;             break;
  302.     case 11: operator = NSCompositePlusDarker;        break;
  303.     case 12: operator = NSCompositePlusLighter;        break;
  304.     default: break;
  305.     }
  306.     [result recache];
  307.     [self setNeedsDisplayInRect:rRect];
  308. }
  309.  
  310.         
  311. // drawRect: simply redisplays the contents of the view. The source and
  312. // destination rectangles are updated from the bitmaps while the result
  313. // rectangle is created by compositing the two bitmaps.
  314.  
  315. - (void)drawRect:(NSRect)rect {
  316.  
  317.     // Erase the whole view
  318.  
  319.     [backgroundColor set];
  320.     NSRectFill([self bounds]);
  321.  
  322.     // Color for the frame of the three sections...
  323.  
  324.     [[NSColor blackColor] set];
  325.  
  326.     // Draw the source bitmap and then frame it with black
  327.  
  328.     [source compositeToPoint:sRect.origin operation:NSCompositeSourceOver];
  329.     NSFrameRect(sRect);
  330.  
  331.     // Draw the destination bitmap and frame it with black 
  332.  
  333.     [destination compositeToPoint:dRect.origin operation:NSCompositeSourceOver];
  334.     NSFrameRect(dRect);
  335.  
  336.     // And now for the result image. Frame it with black as well
  337.  
  338.     [result compositeToPoint:rRect.origin operation:NSCompositeSourceOver];
  339.     NSFrameRect(rRect);
  340. }
  341.  
  342. // dealloc method to free all the images and colors along with the view.
  343.  
  344. - (void)dealloc {
  345.     [source release];
  346.     [destination release];
  347.     [result release];
  348.     [sourceColor release];
  349.     [destColor release];
  350.     [backgroundColor release];
  351.     [customImage release];
  352.     [super dealloc];
  353. }
  354.  
  355.  
  356. // Code to support dragging...
  357. // This is mostly complicated by the fact that the code wants to demonstrate
  358. // how to dynamically give feedback to the user as the colors are
  359. // dragged (but not dropped). We don't dynamically give feedback when images
  360. // are dragged (as it might take a long time).
  361.  
  362. - (unsigned int)draggingEntered:(id <NSDraggingInfo>)sender {
  363.     return [self draggingUpdated:sender];
  364. }
  365.  
  366. // To provide dragging feedback, we change the color of appropriate rect,
  367. // force an immediate display, and then revert the color back. This way
  368. // during the drag operation the colors still retain the original values.
  369.  
  370. - (unsigned int)draggingUpdated:(id <NSDraggingInfo>)sender {
  371.     if ([sender draggingSourceOperationMask] & NSDragOperationGeneric) {
  372.     NSPasteboard *pboard = [sender draggingPasteboard];
  373.         if ([[pboard types] containsObject:NSColorPboardType]) {    // Color
  374.         NSColor *sourceColorSave = [[sourceColor retain] autorelease];
  375.         NSColor *destColorSave = [[destColor retain] autorelease];
  376.         NSColor *backgroundColorSave = [[backgroundColor retain] autorelease];
  377.         [self doColorDrag:sender];
  378.         [self displayIfNeeded];    /* Force a display right now */
  379.         [self changeSourceColorTo:sourceColorSave andDisplay:NO];    /* Revert without displaying */
  380.         [self changeDestColorTo:destColorSave andDisplay:NO];
  381.         [self changeBackgroundColorTo:backgroundColorSave andDisplay:NO];
  382.         return NSDragOperationGeneric;
  383.     } else if ([NSImage canInitWithPasteboard:pboard]) {    // Image?
  384.         return NSDragOperationGeneric;
  385.     }
  386.     }
  387.     return NSDragOperationNone;        
  388. }
  389.  
  390. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  391.     if ([[[sender draggingPasteboard] types] containsObject:NSColorPboardType]) {    // We need to fix the view up
  392.     [self setNeedsDisplay:YES];
  393.     } 
  394. }
  395.  
  396. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
  397.     return ([self draggingUpdated:sender] == NSDragOperationNone) ? NO : YES;
  398. }
  399.  
  400. - (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
  401.     NSPasteboard *pboard = [sender draggingPasteboard];
  402.  
  403.     if ([[pboard types] containsObject:NSColorPboardType]) {
  404.     [self doColorDrag:sender];
  405.     [sourceColorWell setColor:sourceColor];
  406.     [destColorWell setColor:destColor];
  407.     [backColorWell setColor:backgroundColor];
  408.     } else {
  409.         (void)[self changeCustomImageTo:[[NSImage allocWithZone:[self zone]] initWithPasteboard:pboard]];
  410.     }
  411. }
  412.  
  413. - (void)doColorDrag:(id <NSDraggingInfo>)sender {
  414.     NSPoint point = [sender draggingLocation];
  415.     NSColor *color = [NSColor colorFromPasteboard:[sender draggingPasteboard]];
  416.  
  417.     point = [self convertPoint:point fromView:nil];
  418.  
  419.     switch ((int)(3 * point.x / NSWidth([self bounds]))) {
  420.     case 0:
  421.             [self changeSourceColorTo:color andDisplay:YES];
  422.         break;
  423.     case 1:
  424.             [self changeDestColorTo:color andDisplay:YES];
  425.         break;
  426.     case 2:
  427.             [self changeBackgroundColorTo:color andDisplay:YES];
  428.         break;
  429.     default:
  430.         break;    // Shouldn't really happen...
  431.     }
  432. }
  433.  
  434.  
  435. @end
  436.  
  437.