home *** CD-ROM | disk | FTP | other *** search
- /*
-
- You may freely copy, distribute and reuse the code in this example.
- NeXT disclaims any warranty of any kind, expressed or implied,
- as to its fitness for any particular use.
-
- CompositeView implements a view with three horizontal, equal-sized areas.
- The left-most area is the "source," the middle area is the "destination,"
- and the right-most area is the "result." CompositeView assures that the
- contents of the result area is always generated by compositing the other
- two areas using the compositing mode set in the setOperator: method.
- It is also possible to change the contents, color, and alpha of the
- source and destination areas; see the methods setSourceColor:,
- setSourceAlpha:, etc.
-
- CompositeView also demonstrates some of the drag & drop features of
- OPENSTEP by acting as a destination for colors & images.
-
- CompositeView written by Bruce Blumberg and Ali Ozer.
-
- Color support, NXColorPanel, NXColorWells, and NXImage added in 1990.
- Color dragging support added Feb 9, 1992.
- Image dragging support added May 27, 1992.
- Image dragging problem fixed Sep 29, 1992 (for 3.1).
- Conversion to OPENSTEP, Jan 21, 1996.
-
- */
-
- #import <AppKit/AppKit.h>
- #import "CompositeView.h"
-
- @implementation CompositeView
-
- // initWithFrame: creates the view, initializes the rectangles that define the
- // three areas described above, and creates the bitmaps used for rendering the
- // source and destination bitmaps.
-
- - (id)initWithFrame:(NSRect)rect {
-
- // Initialize the view
-
- if (!(self = [super initWithFrame:rect])) return nil;
-
- // Make rectangles for source, destination and result
-
- sRect = [self bounds];
- sRect.size.width /= 3.0;
- dRect = sRect;
- dRect.origin.x = sRect.size.width;
- rRect = dRect;
- rRect.origin.x = dRect.origin.x + dRect.size.width;
-
- // Create source, destination, and result images.
-
- [(source = [[NSImage allocWithZone:[self zone]] initWithSize:sRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawSource:) delegate:self] autorelease]];
- [source setBackgroundColor:[NSColor clearColor]];
-
- [(destination = [[NSImage allocWithZone:[self zone]] initWithSize:dRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawDestination:) delegate:self] autorelease]];
- [destination setBackgroundColor:[NSColor clearColor]];
-
- [(result = [[NSImage allocWithZone:[self zone]] initWithSize:dRect.size]) addRepresentation:[[[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawResult:) delegate:self] autorelease]];
- [result setBackgroundColor:[NSColor clearColor]];
-
- // Set the default operator and source picture. No need to set the default
- // colors; these are read from the .nib file when the outlets to the wells
- // are estanblished.
-
- operator = NSCompositeCopy;
- sourcePicture = TrianglePicture;
-
- // Tell the application that alpha should be allowed in the color panel
- // and dragged colors. Most apps do not want to bother with this.
-
- [NSColor setIgnoresAlpha:NO];
-
- // Finally, register for dragging colors and files.
-
- [self registerForDraggedTypes:[NSArray arrayWithObjects:NSColorPboardType, NSFilenamesPboardType, nil]];
-
- return self;
- }
-
- // Get handles to the wells and read their initial colors.
-
- - (void)setSourceColorWell:(id)anObject {
- sourceColorWell = anObject;
- sourceColor = [[anObject color] copyWithZone:[self zone]];
- }
-
- - (void)setDestColorWell:(id)anObject {
- destColorWell = anObject;
- destColor = [[anObject color] copyWithZone:[self zone]];
- }
-
- - (void)setBackColorWell:(id)anObject {
- backColorWell = anObject;
- backgroundColor = [[anObject color] copyWithZone:[self zone]];
- }
-
- // drawSource: creates the source image in the currently locked focus.
- // It's specified as a callback to the source NSImage, allowed the image to
- // be redrawn whenever needed...
-
- - (void)drawSource:(NSCustomImageRep *)imageRep {
- [sourceColor set];
- PSnewpath();
- switch (sourcePicture) {
-
- case TrianglePicture:
- PSmoveto (0.0, 0.0);
- PSlineto (0.0, sRect.size.height);
- PSlineto (sRect.size.width, sRect.size.height);
- break;
-
- case CirclePicture:
- PSscale (sRect.size.width, sRect.size.height);
- PSarc (0.5, 0.5, 0.4, 0.0, 360.0); // diameter is 80% of area
- break;
-
- case DiamondPicture:
- PSmoveto (0.0, sRect.size.height / 2.0);
- PSlineto (sRect.size.width / 2.0, 0.0);
- PSlineto (sRect.size.width, sRect.size.height / 2.0);
- PSlineto (sRect.size.width / 2.0, sRect.size.height);
- break;
-
- case HeartPicture:
- PSscale (sRect.size.width, sRect.size.height);
- PSmoveto (0.5, 0.5);
- PScurveto (0.3, 1.0, 0.0, 0.5, 0.5, 0.1);
- PSmoveto (0.5, 0.5);
- PScurveto (0.7, 1.0, 1.0, 0.5, 0.5, 0.1);
- break;
-
- case FlowerPicture:
- PSscale (sRect.size.width, sRect.size.height);
- PStranslate (0.5, 0.5);
- PSmoveto (0.0, 0.0);
- {int cnt;
- for (cnt = 0; cnt < 6; cnt++) {
- PSrotate (60.0);
- PScurveto (0.4, 0.5, -0.4, 0.5, 0.0, 0.0);
- }
- }
- break;
-
- case CustomPicture:
- if (!customImage) {
- NSString *fileName = [[NSBundle mainBundle] pathForImageResource:@"DefaultCustomImage.eps"];
- if (fileName) {
- customImage = [[NSImage allocWithZone:[self zone]] initByReferencingFile:fileName];
- [customImage setScalesWhenResized:YES];
- [customImage setSize:rRect.size];
- }
- }
- [customImage compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
- break;
-
- default:
- break;
- }
- PSclosepath();
- PSfill();
- }
-
- // drawDestination: creates the destination image; like drawSource: is it
- // specified as a callback to the destination NSImage
-
- - (void)drawDestination:(NSCustomImageRep *)imageRep {
- [destColor set];
- PSnewpath();
- PSmoveto(dRect.size.width, 0.0);
- PSlineto(dRect.size.width, dRect.size.height);
- PSlineto(0.0, dRect.size.height);
- PSclosepath();
- PSfill();
- }
-
- // drawResult: creates the resulting image, formed by compositing the
- // source image after the destination image with the specified operator.
-
- - (void)drawResult:(NSCustomImageRep *)imageRep {
- [destination compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
- [source compositeToPoint:NSZeroPoint operation:operator];
- }
-
- // setSourcePicture: allows setting the picture to be drawn in the source
- // image. Buttons connected to this method should have tags that are
- // set to the various possible pictures.
- //
- // After setting the sourcePicture instance variable, setSourcePicture:
- // invalidates the images and the view to reflect the new configuration.
-
- - (void)setSourcePicture:(id)sender {
- sourcePicture = [sender selectedTag];
- [source recache];
- [result recache];
- [self setNeedsDisplay:YES];
- }
-
- // changeCustomImageTo: allows changing the image to be drawn
- // in the "Custom" drawing mode
-
- - (BOOL)changeCustomImageTo:(NSImage *)newImage {
- if (newImage && (newImage != customImage)) {
- [newImage setSize:rRect.size];
- [newImage setScalesWhenResized:YES];
- if ([newImage isValid]) {
- [customImage release];
- customImage = newImage;
- if (sourcePicture != CustomPicture) {
- sourcePicture = CustomPicture;
- [sourcePictureMatrix selectCellWithTag:CustomPicture];
- }
- [source recache];
- [result recache];
- [self setNeedsDisplay:YES];
- return YES;
- }
- }
- return NO;
- }
-
- // Action method to change the custom image; puts up an open panel allowing any
- // recognized image file type to be chosen
-
- - (void)changeCustomImage:(id)sender {
- NSOpenPanel *openPanel = [NSOpenPanel openPanel];
- if ([openPanel runModalForTypes:[NSImage imageFileTypes]]) {
- (void)[self changeCustomImageTo:[[NSImage allocWithZone:[self zone]] initByReferencingFile:[openPanel filename]]];
- }
- }
-
- // The following methods change the colors and update
- // the source or destination bitmaps and the view to reflect the change.
- // They should typically be called by a control capable of returning
- // a color (for instance, an NSColorWell).
-
- - (void)changeSourceColor:(id)sender {
- [self changeSourceColorTo:[sender color] andDisplay:YES];
- }
-
- - (void)changeDestColor:(id)sender {
- [self changeDestColorTo:[sender color] andDisplay:YES];
- }
-
- - (void)changeBackgroundColor:(id)sender {
- [self changeBackgroundColorTo:[sender color] andDisplay:YES];
- }
-
- - (void)changeSourceColorTo:(NSColor *)color andDisplay:(BOOL)flag {
- if (![sourceColor isEqual:color]) {
- [sourceColor release];
- sourceColor = [color copyWithZone:[self zone]];
- [source recache];
- [result recache];
- if (flag) [self setNeedsDisplay:YES];
- }
- }
-
- - (void)changeDestColorTo:(NSColor *)color andDisplay:(BOOL)flag {
- if (![destColor isEqual:color]) {
- [destColor release];
- destColor = [color copyWithZone:[self zone]];
- [destination recache];
- [result recache];
- if (flag) [self setNeedsDisplay:YES];
- }
- }
-
- - (void)changeBackgroundColorTo:(NSColor *)color andDisplay:(BOOL)flag {
- if (![backgroundColor isEqual:color]) {
- [backgroundColor release];
- backgroundColor = [color copyWithZone:[self zone]];
- if (flag) [self setNeedsDisplay:YES];
- }
- }
-
- // The operator method returns the operator currently in use.
-
- - (NSCompositingOperation)operator {
- return operator;
- }
-
- // setOperator sets: the operator to be used in the compositing operations
- // and updates the view to reflect the change. Note that setOperator needs
- // to be connected to a row of buttons.
-
- - (void)setOperator:(id)sender {
- switch ([sender selectedRow]) {
- case 0: operator = NSCompositeCopy; break;
- case 1: operator = NSCompositeClear; break;
- case 2: operator = NSCompositeSourceOver; break;
- case 3: operator = NSCompositeDestinationOver; break;
- case 4: operator = NSCompositeSourceIn; break;
- case 5: operator = NSCompositeDestinationIn; break;
- case 6: operator = NSCompositeSourceOut; break;
- case 7: operator = NSCompositeDestinationOut; break;
- case 8: operator = NSCompositeSourceAtop; break;
- case 9: operator = NSCompositeDestinationAtop; break;
- case 10: operator = NSCompositeXOR; break;
- case 11: operator = NSCompositePlusDarker; break;
- case 12: operator = NSCompositePlusLighter; break;
- default: break;
- }
- [result recache];
- [self setNeedsDisplayInRect:rRect];
- }
-
-
- // drawRect: simply redisplays the contents of the view. The source and
- // destination rectangles are updated from the bitmaps while the result
- // rectangle is created by compositing the two bitmaps.
-
- - (void)drawRect:(NSRect)rect {
-
- // Erase the whole view
-
- [backgroundColor set];
- NSRectFill([self bounds]);
-
- // Color for the frame of the three sections...
-
- [[NSColor blackColor] set];
-
- // Draw the source bitmap and then frame it with black
-
- [source compositeToPoint:sRect.origin operation:NSCompositeSourceOver];
- NSFrameRect(sRect);
-
- // Draw the destination bitmap and frame it with black
-
- [destination compositeToPoint:dRect.origin operation:NSCompositeSourceOver];
- NSFrameRect(dRect);
-
- // And now for the result image. Frame it with black as well
-
- [result compositeToPoint:rRect.origin operation:NSCompositeSourceOver];
- NSFrameRect(rRect);
- }
-
- // dealloc method to free all the images and colors along with the view.
-
- - (void)dealloc {
- [source release];
- [destination release];
- [result release];
- [sourceColor release];
- [destColor release];
- [backgroundColor release];
- [customImage release];
- [super dealloc];
- }
-
-
- // Code to support dragging...
- // This is mostly complicated by the fact that the code wants to demonstrate
- // how to dynamically give feedback to the user as the colors are
- // dragged (but not dropped). We don't dynamically give feedback when images
- // are dragged (as it might take a long time).
-
- - (unsigned int)draggingEntered:(id <NSDraggingInfo>)sender {
- return [self draggingUpdated:sender];
- }
-
- // To provide dragging feedback, we change the color of appropriate rect,
- // force an immediate display, and then revert the color back. This way
- // during the drag operation the colors still retain the original values.
-
- - (unsigned int)draggingUpdated:(id <NSDraggingInfo>)sender {
- if ([sender draggingSourceOperationMask] & NSDragOperationGeneric) {
- NSPasteboard *pboard = [sender draggingPasteboard];
- if ([[pboard types] containsObject:NSColorPboardType]) { // Color
- NSColor *sourceColorSave = [[sourceColor retain] autorelease];
- NSColor *destColorSave = [[destColor retain] autorelease];
- NSColor *backgroundColorSave = [[backgroundColor retain] autorelease];
- [self doColorDrag:sender];
- [self displayIfNeeded]; /* Force a display right now */
- [self changeSourceColorTo:sourceColorSave andDisplay:NO]; /* Revert without displaying */
- [self changeDestColorTo:destColorSave andDisplay:NO];
- [self changeBackgroundColorTo:backgroundColorSave andDisplay:NO];
- return NSDragOperationGeneric;
- } else if ([NSImage canInitWithPasteboard:pboard]) { // Image?
- return NSDragOperationGeneric;
- }
- }
- return NSDragOperationNone;
- }
-
- - (void)draggingExited:(id <NSDraggingInfo>)sender {
- if ([[[sender draggingPasteboard] types] containsObject:NSColorPboardType]) { // We need to fix the view up
- [self setNeedsDisplay:YES];
- }
- }
-
- - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
- return ([self draggingUpdated:sender] == NSDragOperationNone) ? NO : YES;
- }
-
- - (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
- NSPasteboard *pboard = [sender draggingPasteboard];
-
- if ([[pboard types] containsObject:NSColorPboardType]) {
- [self doColorDrag:sender];
- [sourceColorWell setColor:sourceColor];
- [destColorWell setColor:destColor];
- [backColorWell setColor:backgroundColor];
- } else {
- (void)[self changeCustomImageTo:[[NSImage allocWithZone:[self zone]] initWithPasteboard:pboard]];
- }
- }
-
- - (void)doColorDrag:(id <NSDraggingInfo>)sender {
- NSPoint point = [sender draggingLocation];
- NSColor *color = [NSColor colorFromPasteboard:[sender draggingPasteboard]];
-
- point = [self convertPoint:point fromView:nil];
-
- switch ((int)(3 * point.x / NSWidth([self bounds]))) {
- case 0:
- [self changeSourceColorTo:color andDisplay:YES];
- break;
- case 1:
- [self changeDestColorTo:color andDisplay:YES];
- break;
- case 2:
- [self changeBackgroundColorTo:color andDisplay:YES];
- break;
- default:
- break; // Shouldn't really happen...
- }
- }
-
-
- @end
-
-