home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscDragViews / MiscViews.subproj / MiscDragView.m < prev    next >
Encoding:
Text File  |  1995-12-18  |  29.5 KB  |  1,072 lines

  1. /**************************************************************************
  2.  * CLASS:        MiscDragView
  3.  * Copyright (C) 1995 Robert Todd Thomas
  4.  * Use is governed by the MiscKit license
  5.  *
  6.  *    See the header file for more information on this class.
  7.  *
  8.  * This object is included in the MiscKit by permission from the author
  9.  * and its use is governed by the MiscKit license, found in the file
  10.  * "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  11.  * for a list of all applicable permissions and restrictions.
  12.  **************************************************************************/
  13.  
  14. #import <misckit/MiscBase.h>
  15. #import "MiscDragView-Delegate.h"
  16. #import "MiscDragView.h"
  17.  
  18. // Since you can only conceivably have one drag going on at once, 
  19. // I made this a class var. You can access it from subclasses through
  20. // setDragImage: and dragImage.
  21. static id  sourceDragImage = nil;
  22.  
  23. // This image is for the "ghost" image we will display if we are the
  24. // destination view. 
  25. static id  destDragImage = nil;
  26. static NXSize  ddiSize = {0.0, 0.0};
  27.  
  28. // Used for shadowing images in the view.
  29. static id sourceDragView = nil;
  30. static id destinationDragView = nil;
  31.  
  32. /*
  33.  * A proper implementation of versioning. If you add ivars to the class (or
  34.  * just want to dork with the reading/writing) check out the comments tagged
  35.  * "Archiving: READ ME". BJM 5/24/94, adjusted 6/17/94.
  36.  */
  37.  
  38. /*
  39.  * **** Archiving: READ ME **** This is the current and defined version of the
  40.  * class. It is used to identify what data will be written and how that will
  41.  * happen. If the read/write stuff is modified AT ALL, this must be bumped up
  42.  * (I always bump by one) and the other comments must be followed. Failure to
  43.  * do so will result in palettes and nibs that cannot be read. BJM 5/24/94
  44.  */
  45. #define MISC_DRAG_VIEW_VERSION 3
  46.  
  47. @implementation MiscDragView
  48.  
  49. + initialize
  50. {
  51.     if (self == [MiscDragView class]) 
  52.     {
  53.         /*
  54.          * **** Archiving: READ ME **** After bumping the _VERSION, it is
  55.          * considered common practice to add a comment line indicating the new
  56.          * version number, date, and modifier. Optionally, the reason for the
  57.          * change. There is no need to modify the setVersion message. BJM
  58.          * 5/24/94 
  59.          */
  60.         // version 0: initial.  (bjm)
  61.         // version 1: add target/action support (Steve_Hayman@Objectario.com)
  62.         // version 2: add accepting image (e.g. opening folder)
  63.         [[MiscDragView class] setVersion:MISC_DRAG_VIEW_VERSION];
  64.     }
  65.     
  66.     return self;
  67. }
  68.  
  69.  
  70.  
  71. - initFrame: (const NXRect *)frameRect
  72. {
  73.     [super initFrame: frameRect];
  74.     [self initDragTypes];
  75.     [self setAllowSourceDragging: YES];
  76.     [self setAllowDestinationDragging: YES];
  77.     freeWhenDragDone = NO;
  78.     [self setBorderType: NX_BEZEL];    
  79.     theImage = theAcceptingImage = nil;    
  80.     [self setDelegate: nil];
  81.     [self setShadowIncoming:YES];
  82.     [self setTarget:nil];
  83.     [self setDragImageIsMyImage:YES];
  84.     return self;
  85. }
  86.  
  87.  
  88.  
  89. - initDragTypes
  90. {
  91.     // Override this method in subclasses to define what drag types you 
  92.     // want to allow.
  93.     if ([delegate respondsTo: @selector(initDragTypes:)])
  94.         [delegate initDragTypes:self];
  95.     return self;
  96. }
  97.  
  98.  
  99.  
  100. - free
  101. {
  102.     if (theImage != nil)
  103.         [theImage free];
  104.         
  105.     return [super free];
  106. }
  107.  
  108.  
  109.  
  110. - setImage: (NXImage *)newImage
  111. {        
  112.     // I'm not sure if delaying the freeing of the image is necessary,
  113.     // but better safe than sorry.
  114.     if (theImage != nil)
  115.         [self perform: @selector(_freeImage:) with: theImage 
  116.                              afterDelay: 0.0 cancelPrevious: NO];
  117.  
  118.     // Setup the new image. If newImage is nil, the view will be cleared.    
  119.     theImage = newImage;
  120.         
  121.     if (theImage != nil)
  122.     {
  123.         [theImage getSize: &imageSize];
  124.         [theImage setScalable: YES];
  125.         [theImage setDataRetained:YES];
  126.     }    
  127.     else
  128.     {
  129.         imageSize.width = 0.0;
  130.         imageSize.height = 0.0;
  131.     }
  132.  
  133.     [self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 
  134.     return self;
  135. }
  136.  
  137.  
  138. - setAcceptingImage: (NXImage *)newImage
  139. {        
  140.     // I'm not sure if delaying the freeing of the image is necessary,
  141.     // but better safe than sorry.
  142.     if (theImage != nil)
  143.         [self perform: @selector(_freeImage:) with: theAcceptingImage 
  144.                              afterDelay: 0.0 cancelPrevious: NO];
  145.  
  146.     // Setup the new image. If newImage is nil, the view will be cleared.    
  147.     theAcceptingImage = newImage;
  148.     [self setShadowIncoming:NO];
  149.     return self;
  150. }
  151. - setImageByName: (const char*)newImageName
  152. {    return [self setImage:[NXImage findImageNamed:newImageName]];
  153. }
  154. - setAcceptingImageByName: (const char*)newImageName
  155. {    return [self setAcceptingImage:[NXImage findImageNamed:newImageName]];
  156. }
  157. -(const char*) imageName
  158. {    return [theImage name];
  159. }
  160. -(const char*) acceptingImageName
  161. {    return [theAcceptingImage name];
  162. }
  163.  
  164. - setImageByFilename: (const char *)aFilename
  165. {
  166.     // If you pass a NULL for filename it will clear the image.
  167.     if (aFilename != NULL && strlen(aFilename) > 0)
  168.         [self setImage: [ [NXImage alloc] initFromFile: aFilename] ];
  169.     else
  170.         [self setImage:nil];
  171.  
  172.     return self;
  173. }
  174.  
  175.  
  176.  
  177. - (NXImage *)image
  178. {
  179.     // Returns the image currently displayed in the view.
  180.     return theImage;
  181. }
  182.  
  183. - (NXImage *)acceptingImage
  184. {
  185.     // Returns the image currently displayed in the view.
  186.     return theAcceptingImage;
  187. }
  188.  
  189.  
  190.  
  191. - setDragImage: (NXImage *)anImage
  192. {
  193.     // Set the image that is to be dragged. You are responsible for 
  194.     // freeing the image when you are done with it (if you allocated 
  195.     // it yourself).
  196.     [self setDragImage: anImage freeWhenDone: NO];
  197.     if(anImage != nil) [self setDragImageIsMyImage:NO];
  198.     return self;
  199. }
  200.  
  201.  
  202.  
  203. - setDragImage: (NXImage *)anImage freeWhenDone: (BOOL)freeIt
  204. {
  205.     // Use if you would like MiscDragView to take care of freeing
  206.     // the dragImage when the drag is complete.
  207.     
  208.     sourceDragImage = anImage;
  209.     freeWhenDragDone = freeIt;
  210.     return self;
  211. }
  212.  
  213.  
  214.  
  215. - (NXImage *)dragImage
  216. {
  217.     return     dragImageIsMyImage?theImage:sourceDragImage;
  218. }
  219.  
  220.  
  221.  
  222. - setBorderType: (int)aType
  223. {
  224.     // Border Type can be one of NX_BEZEL, NX_GROOVE, NX_LINE, or NX_NOBORDER,
  225.     // same as the appkit's Box.    
  226.     border = aType;
  227.     [self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 
  228.     return self;
  229. }
  230.  
  231.  
  232.  
  233. - (int)borderType
  234. {
  235.     return border;
  236. }
  237.  
  238.  
  239.  
  240. - setAllowSourceDragging: (BOOL)aBool
  241. {
  242.     // Sets whether you would like your view to begin a source drag.
  243.     allowSourceDragging = aBool;    
  244.     return self;
  245. }
  246.  
  247.  
  248.  
  249. - (BOOL)allowSourceDragging
  250. {
  251.     // If YES is returned, the view can behave as the source of dragging, 
  252.     // NO if it cannot. 
  253.     return allowSourceDragging;
  254. }
  255.  
  256.  
  257.  
  258. - setAllowDestinationDragging: (BOOL)aBool
  259. {
  260.     // Change whether the view can behave as the destination of dragging.
  261.     // Under certain circumstances you may want to not allow a drag into the 
  262.     // view.
  263.     allowDestinationDragging = aBool;
  264.     return self;
  265. }
  266.  
  267.  
  268.  
  269. - (BOOL)allowDestinationDragging
  270. {
  271.     // If YES is returned, the view can behave as the destination of
  272.     // dragging, NO if it cannot.
  273.     return allowDestinationDragging;
  274. }
  275.  
  276.  
  277.     
  278. - (BOOL)acceptForeignDrag
  279. {
  280.     // This method defines the behavior of the receiver.  If YES is returned, 
  281.     // the view can behave as the destination of dragging from outside of the     
  282.     // application, NO if it cannot. Subclasses can override this to change
  283.     // that behavior. 
  284.     return YES;
  285. }
  286.  
  287.  
  288.  
  289. - (BOOL)acceptLocalDrag
  290. {
  291.     // This method defines the behavior of the receiver.  If YES is 
  292.     // returned, the view can behave as the destination of dragging from 
  293.     // within the application, NO if it cannot. Subclasses can override
  294.     // this to change that behavior. 
  295.     return YES;
  296. }
  297.  
  298.  
  299.  
  300. - (BOOL)acceptSelfDrag
  301. {
  302.     // This method returns YES or NO, defining the behavior of the receiver.
  303.     // If YES is returned, the view can receive data that was dragged out of
  304.     // itself, NO if it cannot. Subclasses can override this to change that
  305.     // behavior.  If this method returns YES, then -acceptLocalDrag should
  306.     // return YES as well.
  307.     if ([self allowDestinationDragging] == NO)
  308.         return NO;
  309.         
  310.     return YES;
  311. }
  312.  
  313.  
  314.  
  315.  
  316. - (BOOL)retainData
  317. {
  318.     // This method returns YES or NO, defining the behavior of the receiver.  
  319.     // If YES is returned, the data being dragged from the view stays in the
  320.     // view when dragged, NO if it does not.  The default implementation
  321.     // returns YES for all instances.  Subclasses can override this to 
  322.     // change that behavior.
  323.     return YES;
  324. }
  325.  
  326.  
  327.  
  328. - (BOOL)shadowIncoming
  329. {
  330.     // If YES is returned, when a view is able to accept a drag, the 
  331.     // dragged object will be shadowed within it.  The default 
  332.     // implementation returns YES for all instances.  Subclasses can 
  333.     // override this to change that behavior. THIS DOES NOT WORK YET.
  334.     return shadowIncoming;
  335. }
  336.  
  337. - setShadowIncoming:(BOOL)f
  338. {    shadowIncoming=f;
  339.     return self;
  340. }
  341.  
  342. - (BOOL)dragImageIsMyImage
  343. {    return dragImageIsMyImage;
  344. }
  345. - setDragImageIsMyImage:(BOOL)aDragImageIsMyImage
  346. {    dragImageIsMyImage=aDragImageIsMyImage;
  347.     return self;
  348. }
  349.  
  350.  
  351. - (NXColor)shadowColor
  352. {
  353.     // This method returns the color used to created a dimmed appearance
  354.     // for a destination image.  The default is to return a partially
  355.     // transparent gray.  If you want a different appearance for shadowing,
  356.     // you can override this method.
  357.  
  358.       NXColor    shadowColor;
  359.     shadowColor = NX_COLORLTGRAY;
  360.     shadowColor = NXChangeAlphaComponent(shadowColor, 0.5);
  361.     return shadowColor;
  362. }
  363.  
  364.  
  365.  
  366. /*-------------------- methods to be the source of a dragging operation */
  367. #define STICKINESS 5.0
  368.  
  369. - mouseDown: (NXEvent *)theEvent
  370. {
  371.     // A source drag now will not begin until the mouse has been moved 
  372.     // more than STICKINESS pixels. This was sort of borrowed from Mike     
  373.     // Ferris's MODocumentWell class in the MOKit.
  374.     
  375.     NXEvent  origEvent, *loopEvent;
  376.       NXPoint  zero = {0.0, 0.0};
  377.       NXPoint  origPoint, newLoc;
  378.       NXCoord  distance = 0.0;
  379.       int  oldMask;
  380.  
  381.      if (theImage == nil || [self allowSourceDragging] == NO)
  382.         return [super mouseDown: theEvent]; 
  383.  
  384.     // if the overridden setupForSourceDrag returns YES continue
  385.     if (!([self setupForSourceDrag] && [self dragImage]))
  386.         return self;
  387.  
  388.     origPoint = theEvent->location;
  389.     [self convertPoint: &origPoint fromView: nil];     
  390.     origEvent = *theEvent;        
  391.     oldMask = [ [self window] addToEventMask: NX_MOUSEDRAGGEDMASK];
  392.                     
  393.     loopEvent = [NXApp getNextEvent:(NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)];
  394.     while (loopEvent->type != NX_MOUSEUP && distance < STICKINESS)
  395.     {
  396.         newLoc = loopEvent->location;
  397.         [self convertPoint: &newLoc fromView: nil];
  398.  
  399.         // if we've gone at least STICKINESS units from the original
  400.         // mousedown, start dragging our file.
  401.         distance = sqrt(((newLoc.x - origPoint.x) * (newLoc.x - origPoint.x)) + 
  402.                     ((newLoc.y - origPoint.y) * (newLoc.y - origPoint.y)));
  403.  
  404.         loopEvent = [NXApp getNextEvent:
  405.                         (NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)];        
  406.      }
  407.  
  408.     [ [self window] setEventMask:oldMask];
  409.     [self calculateDragPoint: &origPoint andOffset: &zero];
  410.     
  411.     // begin the drag session if the mouse moved far enough
  412.     if (distance >= STICKINESS)
  413.     {          
  414.         // send out the delegate message
  415.         [self _sourceDragInitiated: self];        
  416.  
  417.         sourceDragView = self;
  418.         [self display];
  419.         [self dragImage: [self dragImage] at: &origPoint offset: &zero 
  420.                     event: &origEvent    
  421.                     pasteboard: [self draggingPasteboard] 
  422.                     source: self slideBack: [self slideback] ];        
  423.         [self cleanupAfterSourceDrag];
  424.         [self display];
  425.      }
  426.      
  427.     return self;
  428. }
  429.  
  430.  
  431.  
  432. - cleanupAfterSourceDrag
  433. {
  434.     // If we are not supposed to remember the data, then free the image
  435.     // but on a delay. (It crashes after dragging in and out of the view
  436.     // a few times if I use theImage = [theImage free] instead of the 
  437.     // delayed free. It seems to still need the image in the window's
  438.     // drag methods.) Anyway, this seems to work fine this way.
  439.     
  440.     if (destinationDragView != self)
  441.         // If the drag came from ourselves then don't free the image.
  442.         if ([self retainData] == NO)
  443.             [self setImage: nil];
  444.                 
  445.     if (freeWhenDragDone == YES)
  446.         [self perform: @selector(_freeImage:) with: [self dragImage]
  447.                         afterDelay: 0.0 cancelPrevious: NO];
  448.     [self setDragImage: nil];
  449.     
  450.     sourceDragView = nil;
  451.     destinationDragView = nil;
  452.     return self;
  453. }
  454.  
  455.  
  456.  
  457. - (BOOL)setupForSourceDrag
  458. {
  459.     // A method that should be overridden in the subclass to put the 
  460.     // information on the desired pasteboard and select an image to drag. 
  461.     // If done successfully return YES for the drag to continue.
  462.  
  463.     if ([delegate respondsTo: @selector(setupForSourceDrag:)])
  464.     {    [delegate setupForSourceDrag:self];
  465.         return YES;
  466.     } else return NO;
  467. }
  468.  
  469.  
  470.  
  471. - (BOOL)slideback
  472. {
  473.     // Override this method if you like to change when images slide back to
  474.     // their destination view when they aren't deposited in another view.
  475.     // The default is to slide back if the view is retaining the data that
  476.     // is being dragged.
  477.     return [self retainData];
  478. }
  479.  
  480.  
  481.  
  482. - draggingPasteboard
  483. {
  484.     // Override this if you would like to use another pasteboard to do the
  485.     // dragging.
  486.     return [Pasteboard newName: NXDragPboard];
  487. }
  488.  
  489.  
  490.  
  491. - calculateDragPoint: (NXPoint *)dragPoint andOffset: (NXPoint *)offset
  492. {
  493.     // This method should be overridden if you want to have some control
  494.     // on where the dragged image is first placed, and how far it is offset
  495.     // from the original mousedown (dragPoint).
  496.     // Making the dragPoint be the middle of the image is the default,
  497.     // so it looks nice.
  498.     dragPoint->x -= imageSize.width/2.0;
  499.     dragPoint->y -= imageSize.height/2.0;
  500.     return self;
  501. }
  502.  
  503.  
  504.  
  505. - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
  506. {
  507.     return NX_DragOperationAll;
  508. }
  509.  
  510.  
  511.  
  512. - draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint 
  513.         deposited:(BOOL)flag
  514. {
  515.     // Sends a "didFinishSourceDrag:" to the delegate if it responds.
  516.     [self _sourceDragFinished: flag];
  517.             
  518.     return self;
  519. }
  520.  
  521.  
  522.  
  523. /*-------------------- methods to be the destination of a dragging operation */
  524.  
  525. - (NXDragOperation)draggingEntered: sender
  526. {
  527.     if ([self allowDestinationDragging] == NO)
  528.         return NX_DragOperationNone;
  529.         
  530.      if ([self acceptSelfDrag] == NO && [sender draggingSource] == self)
  531.         return NX_DragOperationNone;
  532.         
  533.     if ([self acceptForeignDrag] == NO && [sender isDraggingSourceLocal] == NO)
  534.         return NX_DragOperationNone;
  535.         
  536.     if ([self acceptLocalDrag] == NO && [sender isDraggingSourceLocal] == YES)
  537.         return NX_DragOperationNone;
  538.  
  539.     // Send a "didInitiateDestinationDrag:" to the delegate
  540.     [self _destinationDragInitiated: self];
  541.  
  542.     destinationDragView = self;
  543.     // We will free the image in our cleanupAfterDestinationDrag method.
  544.     destDragImage = [sender draggedImageCopy];
  545.     [destDragImage setScalable: YES];
  546.     [destDragImage getSize: &ddiSize];
  547.     if ([self shadowIncoming] && [destDragImage lockFocus] == YES)
  548.     {    
  549.         // Dim the image.
  550.         NXSetColor([self shadowColor]);
  551.         PScompositerect(0.0, 0.0, ddiSize.width, ddiSize.height, NX_SATOP);
  552.          [destDragImage unlockFocus];
  553.      }
  554.          
  555.     [self display];
  556.      
  557.     return NX_DragOperationCopy;
  558. }
  559.  
  560.  
  561.  
  562. - (NXDragOperation)draggingUpdated: sender
  563. {    
  564.     return [super draggingUpdated: sender]; 
  565. }
  566.  
  567.  
  568.  
  569. - draggingExited:sender
  570. {
  571.     // Destination drag came to an end, but was not successful, so send a NO
  572.     // to the delegate
  573.     [self _destinationDragFinished: NO];
  574.     
  575.     // Free the dragged image we made a copy of.
  576.     [self cleanupAfterDestinationDrag];
  577.     destinationDragView = nil;
  578.     [self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 
  579.  
  580.     return self;
  581. }
  582.  
  583.  
  584.  
  585. - (BOOL)prepareForDragOperation:sender
  586. {
  587.     return YES;
  588. }
  589.  
  590.  
  591.  
  592. - (BOOL)performDragOperation:sender
  593. {    if ([delegate respondsTo:@selector(takeDataFromPasteboard:)])
  594.         return [delegate takeDataFromPasteboard:self];
  595.     
  596.     return YES;
  597. }
  598.  
  599.  
  600.  
  601. - concludeDragOperation:sender
  602. {
  603.     // Send the delegate a "didFinishDestinationDrag" message. If you override
  604.     // this method make sure to call this so the delegate and action messages 
  605.     // will be sent.
  606.  
  607.     [self _destinationDragFinished: YES];
  608.     
  609.     // new - if we have a target/action combination, do it. ..shayman
  610.     if ( target && action && [target respondsTo:action] ) 
  611.     [target perform:action with:self];
  612.     
  613.     [self cleanupAfterDestinationDrag];
  614.     if(![self shadowIncoming]) destinationDragView=nil;    // "closed" icon again
  615.     [self display];
  616.     return self;
  617. }
  618.  
  619.  
  620.  
  621. - cleanupAfterDestinationDrag
  622. {
  623.     // Perform cleanup after the drag has completed.
  624.     if (destDragImage != nil)
  625.         destDragImage = [destDragImage free];
  626.     ddiSize.width = 0.0;
  627.     ddiSize.height = 0.0;
  628.  
  629.     return self;
  630. }
  631.  
  632.     
  633.  
  634. /*-------------------- methods to display, and other useful stuff */
  635.  
  636. - (BOOL)acceptsFirstMouse
  637. {
  638.     return YES;
  639. }
  640.  
  641.  
  642.  
  643. - (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent
  644. {
  645.     return YES;
  646. }
  647.  
  648.  
  649.  
  650. - drawSelf: (const NXRect *)rects :(int)rectCount
  651. {
  652.     NXImage  *dispImage = nil;
  653.     NXSize  origSize;
  654.     NXSize  currSize;
  655.     NXPoint  offset;
  656.     NXRect  imageRect;
  657.  
  658.     switch (border)
  659.     {
  660.         case NX_GROOVE:
  661.                 NXDrawGroove (&bounds, rects);
  662.         break;
  663.         
  664.         case NX_BEZEL:
  665.                  NXDrawGrayBezel(&bounds, rects);
  666.         break;
  667.         
  668.         case NX_LINE:
  669.             NXSetColor (NX_COLORLTGRAY);
  670.             NXRectFill (&bounds);
  671.                 NXSetColor (NX_COLORBLACK);
  672.               NXFrameRect (&bounds);
  673.         break;
  674.           
  675.         case NX_NOBORDER:
  676.         default:
  677.               NXSetColor (NX_COLORLTGRAY);
  678.               NXRectFill (&bounds);
  679.           break;
  680.       }
  681.  
  682.     // If there is no image, or we are supposed to give the illusion
  683.     // that the data (image) was not retained then no reason to continue.
  684.     // There seems to be alot of confusing choices where the view 
  685.     // should not show any images...
  686.     if ((sourceDragView != nil && destinationDragView == sourceDragView && 
  687.                     [self shadowIncoming] == NO && [self retainData] == NO)     
  688.                     || (destinationDragView != self && ([self image] == nil || 
  689.                     ([self retainData] == NO && self == sourceDragView))))
  690.         return self;
  691.      
  692.     // Set so the image will not wipe the border (if any).
  693.     imageRect.origin.x = NX_X(&bounds) + 2;
  694.        imageRect.origin.y = NX_Y(&bounds) + 2;
  695.     imageRect.size.width = NX_WIDTH(&bounds) - 4;
  696.     imageRect.size.height = NX_HEIGHT(&bounds) - 4;
  697.     
  698.     // Choose which image should be displayed in the view. If we
  699.     // are the destinationDragView and we are supposed to shadow
  700.     // the image, then use the destDragImage.
  701.     if ([self shadowIncoming] && destinationDragView == self && 
  702.                     destDragImage != nil)
  703.     {
  704.         dispImage = destDragImage;
  705.         origSize.width = ddiSize.width;
  706.         origSize.height = ddiSize.height;
  707.      }
  708.     else
  709.     {    // Otherwise display our image.
  710.         if(destinationDragView == self && theAcceptingImage)
  711.              dispImage = theAcceptingImage;
  712.         else dispImage = theImage;
  713.         origSize.width = imageSize.width;
  714.         origSize.height = imageSize.height;
  715.      }
  716.     [dispImage getSize: &currSize];
  717.     
  718.     // Use different offsets depending on if image scaled or not.
  719.     if (currSize.width > NX_WIDTH(&imageRect) ||
  720.             currSize.height > NX_HEIGHT(&imageRect))
  721.     {  
  722.           NXSize  scaledSize;
  723.           double xscale, yscale, scale;
  724.  
  725.         // scale the image and keep aspect ratio
  726.          xscale = NX_WIDTH(&imageRect) / origSize.width;
  727.         yscale = NX_HEIGHT(&imageRect) / origSize.height;
  728.         scale = ((xscale < yscale) ? xscale : yscale);
  729.         scaledSize.width = (origSize.width * scale); // - 2.0; 
  730.         scaledSize.height = (origSize.height * scale); // - 2.0;
  731.         
  732.         offset = imageRect.origin;
  733.         offset.x += (NX_WIDTH(&imageRect) - scaledSize.width) / 2;
  734.         offset.y += (NX_HEIGHT(&imageRect) - scaledSize.height) / 2;
  735.         [dispImage setSize: &scaledSize];
  736.         
  737.         if (dispImage == destDragImage && [self shadowIncoming])
  738.             // It seems like scaling the image makes it lose it's
  739.             // dimmed look (that we applied in draggingEntered) so
  740.             // do it again.
  741.             if ([dispImage lockFocus] == YES)
  742.             {
  743.                 NXSetColor([self shadowColor]);
  744.                 PScompositerect(0.0, 0.0, scaledSize.width,
  745.                                 scaledSize.height, NX_SATOP);
  746.                  [dispImage unlockFocus];
  747.              }    
  748.             
  749.      }
  750.     else
  751.     { 
  752.         // no scaling needed
  753.         offset = imageRect.origin;
  754.         offset.x += (NX_WIDTH(&imageRect) - currSize.width) / 2.0;
  755.         offset.y += (NX_HEIGHT(&imageRect) - currSize.height) / 2.0;
  756.      }
  757.  
  758.     [dispImage composite: NX_SOVER toPoint: &offset];
  759.     return self;
  760. }
  761.  
  762.  
  763.  
  764. // Archiving methods
  765.  
  766. - read:(NXTypedStream *)stream
  767. {
  768.     int version;
  769.     BOOL acceptForeignDrag, acceptLocalDrag;
  770.     BOOL acceptSelfDrag, retainData;
  771.  
  772.     [super read:stream];
  773.     
  774.     version = NXTypedStreamClassVersion(stream, "MiscDragView");
  775.     
  776.     /*
  777.      * **** Archiving: READ ME **** This code (and its analogue in write:) is
  778.      * critical. When you bump MISC_DRAG_VIEW_VERSION, copy the whole _current_
  779.      * case ("case MISC_DRAG_VIEW_VERSION: ... break;"), and duplicate it.
  780.      * Change the "MISC_DRAG_VIEW_VERSION" in the old case to the OLD
  781.      * (pre-bump) version number. Now, dork the "new current" version into
  782.      * whatever you want. See how this code can now read EITHER version out of
  783.      * the typed stream?
  784.      *
  785.      * If you are adding yet another version, leave the previous versions in
  786.      * place. That way you can still read archived objects that are more than
  787.      * one version old. Don't forget to take whatever actions are necessary to
  788.      * harmonize those old values. For example, if you converted an "int" ivar
  789.      * to "double", you'd need to (in the old version) to read the int version
  790.      * into a temp variable, and convert it in to the double var. BJM 5/24/94
  791.      */
  792.     switch (version)
  793.     {
  794.     case 0:
  795.         theImage = NXReadObject (stream);
  796.         NXReadSize (stream, &imageSize);
  797.         NXReadTypes (stream, "i", &border);    
  798.         
  799.         delegate = NXReadObject (stream);
  800.         NXReadTypes (stream, "c", &allowSourceDragging);
  801.         NXReadTypes (stream, "c", &allowDestinationDragging);
  802.         NXReadTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
  803.         NXReadTypes (stream, "cc", &acceptSelfDrag, &retainData);
  804.         break;
  805.         
  806.     case 1:
  807.         theImage = NXReadObject (stream);
  808.         NXReadSize (stream, &imageSize);
  809.         NXReadTypes (stream, "i", &border);    
  810.         
  811.         delegate = NXReadObject (stream);
  812.         NXReadTypes (stream, "c", &allowSourceDragging);
  813.         NXReadTypes (stream, "c", &allowDestinationDragging);
  814.         NXReadTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
  815.         NXReadTypes (stream, "cc", &acceptSelfDrag, &retainData);
  816.         // Version 1 adds target/action stuff -- shayman
  817.         NXReadTypes( stream,  "@:", &target, &action );
  818.         break;
  819.  
  820.     case 2:
  821.         theImage = NXReadObject (stream);
  822.         NXReadSize (stream, &imageSize);
  823.         NXReadTypes (stream, "i", &border);    
  824.         
  825.         delegate = NXReadObject (stream);
  826.         NXReadTypes (stream, "c", &allowSourceDragging);
  827.         NXReadTypes (stream, "c", &allowDestinationDragging);
  828.         NXReadTypes( stream,  "@:", &target, &action );
  829.         break;
  830.  
  831.     case MISC_DRAG_VIEW_VERSION:
  832.         theImage = NXReadObject (stream);
  833.         NXReadSize (stream, &imageSize);
  834.         NXReadTypes (stream, "i", &border);    
  835.         
  836.         delegate = NXReadObject (stream);
  837.         NXReadTypes (stream, "c", &allowSourceDragging);
  838.         NXReadTypes (stream, "c", &allowDestinationDragging);
  839.         NXReadTypes( stream,  "@:", &target, &action );
  840.  
  841.         NXReadTypes (stream, "cc", &shadowIncoming,&dragImageIsMyImage);    // new
  842.         theAcceptingImage = NXReadObject (stream);
  843.         break;
  844.  
  845.     default:
  846.         NXLogError("[%s %s] - unknown version of %s in typed stream",
  847.             [[self class] name], sel_getName(_cmd), 
  848.             [[MiscDragView class] name]);
  849.         break;
  850.     }
  851.  
  852.     return self;
  853. }
  854.  
  855.  
  856.  
  857. - write:(NXTypedStream *)stream
  858. {
  859.     BOOL acceptForeignDrag, acceptLocalDrag;
  860.     BOOL acceptSelfDrag, retainData;
  861.  
  862.     [super write:stream];
  863.     
  864.     /*
  865.      * **** Archiving: READ ME **** Home stretch. Now (just like read:)
  866.      * duplicate the current case ("case MISC_DRAG_VIEW_VERSION: ... break;").
  867.      * Once again, change the "MISC_DRAG_VIEW_VERSION" of the first one to the
  868.      * OLD version number. Now adjust the new/current MISC_DRAG_VIEW_VERSION
  869.      * (remember, you've bumped it) to the new way of writing vars. See how
  870.      * this code (because the constant is in the switch statement) always
  871.      * writes out ONLY the current version, but leaves the old version(s)
  872.      * around to posterity. DO NOT DELETE THE OLD VERSIONS. Leave them to
  873.      * clutter up the world. BJM 5/24/94  
  874.      */
  875.     switch (MISC_DRAG_VIEW_VERSION)
  876.     {
  877.     case 0:
  878.         NXWriteObject (stream, theImage);
  879.         NXWriteSize (stream, &imageSize);    
  880.         NXWriteTypes (stream, "i", &border);
  881.     
  882.         NXWriteObjectReference (stream, delegate);
  883.         NXWriteTypes (stream, "c", &allowSourceDragging);
  884.         NXWriteTypes (stream, "c", &allowDestinationDragging);
  885.         NXWriteTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
  886.         NXWriteTypes (stream, "cc", &acceptSelfDrag, &retainData);
  887.         break;
  888.  
  889.     case 1:
  890.         NXWriteObject (stream, theImage);
  891.         NXWriteSize (stream, &imageSize);    
  892.         NXWriteTypes (stream, "i", &border);
  893.     
  894.         NXWriteObjectReference (stream, delegate);
  895.         NXWriteTypes (stream, "c", &allowSourceDragging);
  896.         NXWriteTypes (stream, "c", &allowDestinationDragging);
  897.         NXWriteTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
  898.         NXWriteTypes (stream, "cc", &acceptSelfDrag, &retainData);
  899.         // Version 1 adds support for target/action -- shayman
  900.         NXWriteTypes (stream, "@:", &target, &action );
  901.         break;
  902.  
  903.     case 2:
  904.         NXWriteObject (stream, theImage);
  905.         NXWriteSize (stream, &imageSize);    
  906.         NXWriteTypes (stream, "i", &border);
  907.     
  908.         NXWriteObjectReference (stream, delegate);
  909.         NXWriteTypes (stream, "c", &allowSourceDragging);
  910.         NXWriteTypes (stream, "c", &allowDestinationDragging);
  911.         NXWriteTypes (stream, "@:", &target, &action );
  912.         break;
  913.  
  914.     case MISC_DRAG_VIEW_VERSION:
  915.         NXWriteObject (stream, theImage);
  916.         NXWriteSize (stream, &imageSize);    
  917.         NXWriteTypes (stream, "i", &border);
  918.     
  919.         NXWriteObjectReference (stream, delegate);
  920.         NXWriteTypes (stream, "c", &allowSourceDragging);
  921.         NXWriteTypes (stream, "c", &allowDestinationDragging);
  922.         NXWriteTypes (stream, "@:", &target, &action );
  923.  
  924.         NXWriteTypes (stream, "cc", &shadowIncoming,&dragImageIsMyImage);    // new
  925.         NXWriteObject(stream, theAcceptingImage);
  926.         break;
  927.  
  928.     default:
  929.         NXLogError("[%s %s] - unknown version of %s in typed stream",
  930.             [[self class] name], sel_getName(_cmd), [[self class] name]);
  931.         break;
  932.     }
  933.  
  934.     return self;
  935. }
  936.  
  937.  
  938.  
  939. - awake
  940. {
  941.     [super awake];
  942.     [self initDragTypes];
  943.     if(theAcceptingImage) [self setShadowIncoming:NO];
  944.  
  945.     return self;
  946. }
  947.  
  948. /*
  949.  * new for version 1 - some basic target/action handling, and the idea
  950.  * of a "stringValue" so that we can use this class just like various
  951.  * Control objects.
  952.  * steve hayman, aug 2 1994
  953.  */
  954. - target { return target; }
  955. - setTarget:aTarget
  956. {
  957.     target = aTarget;
  958.     return self;
  959. }
  960. - (SEL)action { return action; }
  961. - setAction:(SEL)anAction
  962. {
  963.     action = anAction;
  964.     return self;
  965. }
  966.  
  967. /*
  968.  * these should be overridden in the appropriate way by subclasses  -- shayman
  969.  */
  970. - (const char *)stringValue { return NULL; }
  971. - setStringValue:(const char *)aValue { return self; }
  972. - takeStringValueFrom:sender 
  973. {    
  974.     if ( [sender respondsTo:@selector(stringValue)] )
  975.         return [self setStringValue: [sender stringValue]];
  976.     return nil;
  977. }
  978.  
  979. @end
  980.  
  981.  
  982.  
  983. @interface MiscDragView (PrivateMethods)
  984.  
  985. - _freeImage: image;
  986.  
  987. @end
  988.  
  989. @implementation MiscDragView (PrivateMethods)
  990.  
  991. // Tries to fix the problem of freeing the image too early if the data
  992. // is not retained in the view. This method is called after a short
  993. // delay. Why isn't there a message perform:afterDelay:cancelPrevious?
  994. // Then you could just say [theImage perform:@selector(free)afterDelay..].
  995.  
  996. - _freeImage: image
  997. {
  998.     // Leak leak leak.... Uncomment at your own risk (probably OK if you
  999.     // are not using any of the methods that use NXImage's +findImageNamed:
  1000.     //image = [image free];
  1001.     return self;
  1002. }
  1003.  
  1004.  
  1005. @end
  1006.  
  1007.  
  1008. /***************************************************************************
  1009.   CHANGES:
  1010.   
  1011.   Bruce McKenzie (spuds@netcom.com) (aka BJM) on 5/24/94:
  1012.      1) Added proper versioning/archiving
  1013.      2) Reset variables so that class properly recognizes freed objects
  1014.      3) Changed "- setImageByFilename: (char *)aFilename" to 
  1015.         "- setImageByFilename: (const char *)aFilename
  1016.   Steve Hayman (shayman@Objectario.com)  
  1017.      4) Added Control's target/action paradigm. Added target and action
  1018.         ivars and target/setTarget:, action/setAction:, 
  1019.         stringValue/setStringValue and takeStringValueFrom.
  1020.   Bill Shirley (Bill_Shirley@WilTel.com)
  1021.        5) Made dragImage a class ivar since only one drag can take place 
  1022.          at a time. Added dragImage/setDragImage: to set the var.
  1023.      6) Removed the ivars acceptForeignDrag/localDrag/selfDrag/retainData
  1024.         and the methods to set them. (They have been put in the Backward
  1025.         compatability category) Each subclass of MiscDragView can
  1026.         make each choice for itself and you cannot change it at runtime.
  1027.      7) Added initDragTypes method so you don't have to override both
  1028.            initFrame: and awake: in order to register your pasteboard types.
  1029.      8) Added shadowIncoming/shadowOutgoing (note: they are not functional
  1030.          yet.)
  1031.      9) Added slideback method to determine if an unsuccessfully dragged
  1032.         image should slideback to it's destination view.
  1033.   Todd Thomas (todd@avocado.supernet.ab.ca) on Sept. 20, 1994
  1034.      10) Put all the delegate methods in a category and renamed all the
  1035.          methods sent to the delegate to be more inline with NeXT's.
  1036.      11) Changed mouseDown so you have to move the mouse a little
  1037.          before a drag will start.
  1038.   Version 1.4: (Significant bug fixes and small additions) (Todd)
  1039.        12) Fixed all (ok, so I'm being optimistic) the bugs related to
  1040.          non-retained views.
  1041.      13) Added -cleanupAfterDestinationDrag method which must be called
  1042.          from your prepareForDragOperation: or performForDragOperation:
  1043.         if you decide to stop the drag operation (by returning NO). This
  1044.         frees a copy of the dragged image we made in draggingEntered:.
  1045.      14) Changed method -cleanupImages to -cleanupAfterSourceDrag to
  1046.          be more in line with the newly created method in #13.
  1047.      15) Added the ability of the destination drag views to show a 
  1048.          "dimmed" image if it will accept the drag.
  1049.      16) Just some general cleanup in drawSelf and some other methods.
  1050. My personal changes:
  1051.     1)    Added accepting image outlet and supporting methods
  1052.     2)    dragImageIsMyImage flag introduced so that draggingImage
  1053.         can default to theImage
  1054.     3)    changed drawSelf to support an accepting image
  1055.     4)    setUpForDraggingOperation queries the delegate first
  1056.     5)    un-hardwiring shadowIncoming
  1057.     6)    Extended the Inspector so that icons can be set in IB
  1058.     7)    Providing a means for settin images by name
  1059.     8)    calculateDragPoint modified so that dragging looks nicer
  1060.         (as in MiscIconWell). Thought thas such a generic behaviour sooks nice
  1061.         in a generic class.
  1062.     9)    extended setDelegate in that it asks the delegate to provide an image
  1063.         (via - image method)
  1064.     10)    initDragTypes modified in that it first asks the delegate to load the types
  1065. toDo    1)    make freeing old theImage in setImage optional
  1066.         2)    supporting a Cell like Label
  1067.         3)    providing a delegate protocol
  1068.      
  1069.  ****************************************************************************/
  1070.       
  1071.      
  1072.