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