|
|
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.