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
- soUCQ and destination areas; see the methods setSourceColor:,
- setSourceAlpha:, etc.
-
- CompositeView also demonstrates some of the drag & drop features of
- NeXTSTEP 3.0 by acting as a destination for colors & images.
-
- CompositeView written by Bruce Blumberg and Ali Ozer.
-
- Color support, NXColorPanel, NXColorWells, and NXImage added during 1990 by Ali.
- Color dragging support added Feb 9, 1992, by Ali.
- Image dragging support added May 27, 1992, by Ali.
-
- */
-
- #import <appkit/appkit.h>
- #import "CompositeView.h"
-
-
- @implementation CompositeView
-
- // The possible draw modes for the source.
-
- #define TRIANGLE 0
- #define CIRCLE 1
- #define DIAMOND 2
- #define HEART 3
- #define FLOWER 4
- #define CUSTOM 5
-
- // initFrame: 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. newFrame: is a convenience method.
-
- - initFrame:(const NXRect *)tF
- {
- // Initialize the view
- [super initFrame:tF];
-
- // Make rectangles for source, destination and result
- sRect = 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 = [[NXImage allocFromZone:[self zone]] initSize:&sRect.size])
- useDrawMethod:@selector(drawSource:) inObject:self];
- [source setBackgroundColor:NX_COLORCLEAR];
-
- [(destination = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
- useDrawMethod:@selector(drawDestination:) inObject:self];
- [destination setBackgroundColor:NX_COLORCLEAR];
-
- [(result = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
- useDrawMethod:@selector(drawResult:) inObject:self];
- [result setBackgroundColor:NX_COLORCLEAR];
-
- // 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 = NX_COPY;
- sourcePicture = TRIANGLE;
-
- // Tell the application that alpha should be allowed in the color panel
- // and dragged colors. Most apps do not want to bother with this.
-
- [NXApp setImportAlpha:YES];
-
- // Finally, register for dragging colors and files.
-
- [selfUCRisterForDraggedTypes:&NXColorPboardType count:1];
- [self registerForDraggedTypes:&NXFilenamePboardType count:1];
-
- return self;
- }
-
- // Get handles to the wells and read their initial colors.
-
- - setSourceColorWell:anObject
- {
- sourceColorWell = anObject;
- sourceColor = [anObject color];
- return self;
- }
-
- - setDestColorWell:anObject
- {
- destColorWell = anObject;
- destColor = [anObject color];
- return self;
- }
-
- - setBackColorWell:anObject
- {
- backColorWell = anObject;
- backgroundColor = [anObject color];
- return self;
- }
-
- // drawSource creates the source image in the source bitmap. Note that
- // drawSource does not render in the view; it renders in the bitmap only.
-
- - drawSource:image
- {
- NXPoint zeroPoint = {0.0, 0.0};
-
- NXSetColor(sourceColor);
- PSnewpath();
- switch (sourcePicture) {
-
- case TRIANGLE:
- PSmoveto (0.0, 0.0);
- PSlineto (0.0, sRect.size.height);
- PSlineto (sRect.size.width, sRect.size.height);
- break;
-
- case CIRCLE:
- 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 DIAMOND:
- 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 HEART:
- 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 FLOWER:
- 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 CUSTOM:
- if (!customImage) {
- customImage = [[NXImage allocFromZone:[self zone]]
- initSize:&rRect.size];
- [customImage setScalable:YES];
- [customImage useFromSection:"DefaultCustomImage.eps"];
- }
- [customImage composite:NX_SOVER toPoint:&zeroPoint];
- break;
-
- default:
- break;
- }
- PSclosepath();
- PSfill();
-
- return self;
- }
-
- // drawDestination creates the destination image in the destination bitmap.
- // Like drawSource, drawDestUCSion only draws in the bitmap, not the view.
-
- - drawDestination:image
- {
- NXSetColor(destColor);
- PSnewpath();
- PSmoveto(dRect.size.width, 0.0);
- PSlineto(dRect.size.width, dRect.size.height);
- PSlineto(0.0, dRect.size.height);
- PSclosepath();
- PSfill();
- return self;
- }
-
- // drawResults creates the resulting image, formed by compositing the
- // source image after the destination image with the specified operator.
-
- - drawResult:image
- {
- NXPoint zeroPoint = {0.0, 0.0};
-
- [destination composite:NX_COPY toPoint:&zeroPoint];
- [source composite:operator toPoint:&zeroPoint];
- return self;
- }
-
- // setSourcePicture allows setting the picture to be drawn in the source
- // bitmap. Buttons connected to this method should have tags that are
- // set to the various possible pictures (see the "#define"s, above).
- //
- // After setting the sourcePicture instance variable, setSourcePicture redraws
- // the bitmap and updates the view to reflect the new configuration.
-
- - setSourcePicture:sender
- {
- sourcePicture = [sender selectedTag];
- [source recache];
- [result recache];
- [self display];
- return self;
- }
-
- - (BOOL)changeCustomImageTo:newImage
- {
- if (newImage) {
- [newImage setSize:&rRect.size];
- [newImage setScalable:YES];
- if ([newImage lockFocus]) { // Is this a good image indeed?
- [newImage unlockFocus];
- [customImage free];
- customImage = newImage;
- if (sourcePicture != CUSTOM) {
- sourcePicture = CUSTOM;
- [sourcePictureMatrix selectCellWithTag:CUSTOM];
- }
- [source recache];
- [result recache];
- [self display];
- return YES;
- }
- }
- return NO;
- }
-
- - changeCustomImage:sender
- {
- if ([[OpenPanel new] runModalForTypes:[NXImage imageFileTypes]]) {
- const char *fileName = [[OpenPanel new] filename];
- (void)[self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromFile:fileName]];
- }
-
- return self;
- }
-
- // 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 NXColorWell).
-
- - changeSourceColor:sender
- {
- [self changeSourceColorTo:[sender color] andDisplay:YES];
- return self;
- }
-
- - changeDestColor:sender
- {
- [self changeDestColorTo:[sender color] andDisplayUCT];
- return self;
- }
-
- - changeBackgroundColor:sender
- {
- [self changeBackgroundColorTo:[sender color] andDisplay:YES];
- return self;
- }
-
- - (void)changeSourceColorTo:(NXColor)color andDisplay:(BOOL)flag
- {
- if (!NXEqualColor(sourceColor, color)) {
- sourceColor = color;
- [source recache];
- [result recache];
- if (flag) [self display];
- }
- }
-
- - (void)changeDestColorTo:(NXColor)color andDisplay:(BOOL)flag
- {
- if (!NXEqualColor(destColor, color)) {
- destColor = color;
- [destination recache];
- [result recache];
- if (flag) [self display];
- }
- }
-
- - (void)changeBackgroundColorTo:(NXColor)color andDisplay:(BOOL)flag
- {
- if (!NXEqualColor(backgroundColor, color)) {
- backgroundColor = color;
- if (flag) [self display];
- }
- }
-
- // The operator method returns the operator currently in use.
-
- - (int)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.
-
- - setOperator:sender
- {
- switch ([sender selectedRow]) {
- case 0: operator = NX_COPY; break;
- case 1: operator = NX_CLEAR; break;
- case 2: operator = NX_SOVER; break;
- case 3: operator = NX_DOVER; break;
- case 4: operator = NX_SIN; break;
- case 5: operator = NX_DIN; break;
- case 6: operator = NX_SOUT; break;
- case 7: operator = NX_DOUT; break;
- case 8: operator = NX_SATOP; break;
- case 9: operator = NX_DATOP; break;
- case 10: operator = NX_XOR; break;
- case 11: operator = NX_PLUSD; break;
- case 12: operator = NX_PLUSL; break;
- default: break;
- }
- [result recache];
- [self speedyDraw];
-
- return self;
- }
-
-
- // drawSelf:: 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.
-
- - drawSelf:(NXRect *)r :(int) count
- {
- // Erase the whole view
- NXSetColor (backgroundColor);
- NXRectFill (&bounds);
-
- // Color for the frame of the three sections...
- NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
-
- // Draw the source bitmap and then frame it with black
- [source composite:NX_SOVER toPoint:&sRect.origin];
- NXFrameRect(&sRect);
-
- // Draw the destination bitmap and frame it with black
- [destination composite:NX_SOVER toPoint:&dRect.origin];
- NXFrameRect(&dRect);
-
- // And now for the result image. Frame it with black as well
- [result composite:NX_SOVER toPoint:&rRect.origin];
- NXFrameRect(&rRect);
-
- return self;
- }
-
- /UCIeedyDraw provides some efficiency in redisplaying the view by assuming
- // that the source and the destination rectangles are already in place.
-
- - speedyDraw
- {
- [self lockFocus];
- NXSetColor (backgroundColor);
- NXRectFill (&rRect);
- [result composite:NX_SOVER toPoint:&rRect.origin];
- NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
- NXFrameRect(&rRect);
- [[self window] flushWindow];
- [self unlockFocus];
-
- return self;
- }
-
- // free method to free all the images along with the view.
-
- - free
- {
- [source free];
- [destination free];
- [result free];
- [customImage free];
- return [super free];
- }
-
-
- // 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).
-
- // includesType() returns YES if type is included within types.
-
- static BOOL includesType (const NXAtom *types, NXAtom type)
- {
- if (types) while (*types) if (*types++ == type) return YES;
- return NO;
- }
-
- - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
- {
- return [self draggingUpdated:sender];
- }
-
- - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
- {
- if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
- Pasteboard *pboard = [sender draggingPasteboard];
- if (includesType([pboard types], NXColorPboardType)) { // Color
- NXColor sourceColorSave = sourceColor;
- NXColor destColorSave = destColor;
- NXColor backgroundColorSave = backgroundColor;
- [self doColorDrag:sender];
- [self changeSourceColorTo:sourceColorSave andDisplay:NO];
- [self changeDestColorTo:destColorSave andDisplay:NO];
- [self changeBackgroundColorTo:backgroundColorSave andDisplay:NO];
- return NX_DragOperationGeneric;
- } else if ([NXImage canInitFromPasteboard:pboard]) { // Image?
- return NX_DragOperationGeneric;
- }
- }
- return NX_DragOperationNone;
- }
-
- - draggingExited:sender
- {
- if (includesType([[sender draggingPasteboard] types], NXColorPboardType)) { // We need to fix the view up
- [self display];
- }
- return self;
- }
-
- - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
- {
- Pasteboard *pboard = [sender dragUCPPasteboard];
-
- if (includesType([pboard types], NXColorPboardType)) {
- [self doColorDrag:sender];
- [sourceColorWell setColor:sourceColor];
- [destColorWell setColor:destColor];
- [backColorWell setColor:backgroundColor];
- return YES;
- } else {
- return [self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromPasteboard:pboard]];
- }
- }
-
- - (void)doColorDrag:(id <NXDraggingInfo>)sender
- {
- NXPoint p = [sender draggingLocation];
- NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
-
- [self convertPoint:&p fromView:nil];
-
- switch ((int)(3 * p.x / NX_WIDTH(&bounds))) {
- case 0:
- [self changeSourceColorTo:c andDisplay:YES];
- break;
- case 1:
- [self changeDestColorTo:c andDisplay:YES];
- break;
- case 2:
- [self changeBackgroundColorTo:c andDisplay:YES];
- break;
- default:
- break; // Shouldn't really happen...
- }
- }
-
-
- @end
-