
/*
   MiscDragView.m

   Copyright (C) 1995, 1996 Todd Thomas
   Use is governed by the MiscKit license

   This object is included in the MiscKit by permission from the author
   and its use is governed by the MiscKit license, found in the file
   "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
   for a list of all applicable permissions and restrictions.
*/

// RCS identification information
static char *rcsID = "$Id: MiscDragView.m,v 1.2 1996/10/07 02:49:17 todd Exp $";
static void __AvoidCompilerWarning(void) {if(!rcsID)__AvoidCompilerWarning();}

#import <AppKit/AppKit.h>

#import "MiscDragCell.h"
#import "MiscDragView.h"

// **** Archiving: READ ME **** This is the current and defined version of the
// class. It is used to identify what data will be written and how that will
// happen. If the read/write stuff is modified AT ALL, this must be bumped up
// (I always bump by one) and the other comments must be followed. Failure to
// do so will result in palettes and nibs that cannot be read. BJM 5/24/94
#define MISC_DRAG_VIEW_VERSION 4

// Our default drag cell class.
static Class _dragCellClass = nil;


@implementation MiscDragView

/*"
   MiscDragView defines an NSView subclass that can be
   either the source or sink of a dragging session. The behavior
   of the drag view almost completely depends upon the class of
   drag cell you make use of. You can set the drag cell that
   we use in many ways. You can either create a MiscDragView
   instance and use #setCell: (from NSControl) to set a
   concrete instance of a subclass of MiscDragCell, or you
   can create a subclass of MiscDragView that overrides
   #{+cellClass} to return a subclass of MiscDragCell. For
   an example of the latter, see MiscFileDragView.
"*/


//-------------------------------------------------------------------
// 	Class initialization
//-------------------------------------------------------------------

+ (void) initialize
/*"
   Sets our class verison for archiving and sets our cell class to
   MiscDragCell by default.
"*/
{
    if (self == [MiscDragView class]) {
        /*
         * **** Archiving: READ ME **** After bumping the _VERSION, it is
         * considered common practice to add a comment line indicating the new
         * version number, date, and modifier. Optionally, the reason for the
         * change. There is no need to modify the setVersion message. BJM
         * 5/24/94
         */
        // version 0: initial.  (bjm)
        // version 1: add target/action support (Steve_Hayman@Objectario.com)
        // version 2: add accepting image (e.g. opening folder)
        // version 3: created a MiscDragCell class which does most of the work.
        // version 4: OpenStep conversion (Cell now has ivars)
        [self setVersion:MISC_DRAG_VIEW_VERSION];

        [self setCellClass:[MiscDragCell class]];
    }
}


+ (Class) cellClass
/*"
   Returns our cell class. By default it is MiscDragCell, though you
   should override these methods (#cellClass/setCellClass:) in any
   subclasses because MiscDragCell is an abstract class.
"*/
{
    return _dragCellClass;
}


+ (void) setCellClass:(Class)newDragCellClass
/*"
   Sets our cell class. By default it is MiscDragCell.
"*/
{
    _dragCellClass = newDragCellClass;
}


//-------------------------------------------------------------------
// 	Initialization/deallocation
//-------------------------------------------------------------------

- initWithFrame:(NSRect)frameRect
/*"
   Our designated initializer. We call our #initializeDragTypes method
   (to register the types of pasteboard data we will accept) and
   set our border type to NSBezelBorder.
"*/
{
    BOOL error = ([super initWithFrame:frameRect] == nil);

    if (!error) {
        [self initializeDragTypes];
        [self setBorderType:NSBezelBorder];
    }
    return error ? nil : self;
}


- (void) dealloc
/*"
   Releases our resources, which right now seem to be nothing.
"*/
{
    [super dealloc]; 
}


//-------------------------------------------------------------------
// 	Our images
//-------------------------------------------------------------------

- (NSImage*) image
/*"
   Returns our current image or nil if we don't have one.
"*/
{
    return [[self cell] image];
}

- (void) setImage:(NSImage*)newImage
/*"
   Sets our image. If newImage is nil it will clear any image
   we used to have.
"*/
{
    [[self cell] setImage:newImage];
}


- (NSImage*) acceptingImage
/*"
   Returns the image that's dislayed when we are accepting a
   drag, or nil if we don't have an accepting image. If we
   do have an accepting image then the usual "dimmed" icoming
   image won't be shown.
"*/
{
    // 
    return [[self cell] acceptingImage];
}


- (void) setAcceptingImage:(NSImage *)newImage
/*"
   Sets the image that's dislayed when we are accepting a
   drag. If we do have an accepting image then the usual "dimmed"
   icoming image won't be shown. If newImage is nil then we
   revert back to using the "dimmed" incoming image.
"*/
{		
    [[self cell] setAcceptingImage:newImage];
}


- (NSImage*) dragImage
/*"
   Returns the image we use as the drag image. If it is nil
   then by default we use the image returned by sending
   ourself an #image message.
"*/
{
    return [[self cell] dragImage];
}


- (void) setDragImage:(NSImage *)anImage
/*"
   Sets our drag image. If we don't have a drag image then
   we use the image we are currently displaying.
"*/
{
    [[self cell] setDragImage:anImage];
}


//-------------------------------------------------------------------
// 	Options
//-------------------------------------------------------------------

- (NSBorderType) borderType
/*"
   Returns our current border type, which will be one of NSNoBorder,
   NSLineBorder, NSBezelBorder or NSGrooveBorder. By default we use
   NSGrooveBorder.
"*/
{
    return [[self cell] borderType];
}


- (void) setBorderType:(NSBorderType)aType
/*"
    Sets our current border type. aType can be one of NSNoBorder,
    NSLineBorder, NSBezelBorder or NSGrooveBorder. By default we use
    NSBezelBorder.
"*/

{
    [[self cell] setBorderType:aType];
    [self setNeedsDisplay:YES];
}


- (BOOL) allowsSourceDragging
/*"
   Returns YES if we are allowed to begin a dragging session.
   The default is YES.
"*/
{
    return [[self cell] allowsSourceDragging];
}


- (void) setAllowsSourceDragging:(BOOL)aBool
/*"
    Sets whether we are allowed to begin a dragging session.
    The default is YES.
"*/
{
    [[self cell] setAllowsSourceDragging:aBool];
}


- (BOOL) allowsDestinationDragging
/*"
    Returns YES if we are allowed to accept a drag.
    The default is YES.
"*/
{
    return [[self cell] allowsDestinationDragging];
}


- (void) setAllowsDestinationDragging:(BOOL)aBool
/*"
   Sets whether we are allowed to accept a drag.
   The default is YES.
"*/
{
    [[self cell] setAllowsDestinationDragging:aBool];
}

	
- (BOOL) acceptsForeignDrag
/*"
   Returns YES if we accept drags that don't originate from
   within our own application. The default is YES.
"*/
{
    return [[self cell] acceptsForeignDrag];
}


- (BOOL) acceptsLocalDrag
/*"
    Returns YES if we accept drags that originate from
    within our own application. The default is YES.
"*/
{
    return [[self cell] acceptsLocalDrag];
}


- (BOOL) acceptsSelfDrag
/*"
   Returns YES if we accept drags that originated from
   our own drag cell. The default is YES. You see this
   behavior in the Workspace shelf. If you drag a file
   from one of the cells you are still able to put it back
   in the same cell.
"*/
{
    return [[self cell] acceptsSelfDrag];
}


- (BOOL) retainsData
/*"
   Returns YES if when we drag that we leave a copy of
   ourselves behind. If you were to emulate the Workspace
   shelf then this would return NO. The default is NO.
"*/
{
    return [[self cell] acceptsLocalDrag];
}


- (BOOL) shadowsIncoming
/*"
   Returns YES if we show a dimmed image when there is
   an incoming drag. This assumes that we are currently
   accepting drags. The default is YES.
"*/
{
    return [[self cell] shadowsIncoming];
}


- (void) setShadowsIncoming:(BOOL)shadows
/*"
   Sets whether we show a dimmed image when there is
   an incoming drag. The default is YES.
"*/
{	
    [[self cell] setShadowsIncoming:shadows];
}


- (NSColor*) shadowColor
/*"
   Returns the color used to created a dimmed appearance
   for a destination image.  The default is to return a partially
   transparent gray.  If you want a different appearance for shadowing,
   you can override this method.
"*/
{
    return [[self cell] shadowColor];
}


- (BOOL) dragImageSlidesBack
/*"
   Returns YES if the dragging image should slide back to it's destination
   when it isn't deposited in another view. The default is to slide back
   only if we retain our data (#retainsData returns YES).
"*/
{
    return [[self cell] dragImageSlidesBack];
}


//-------------------------------------------------------------------
// 	Title manipulation
//-------------------------------------------------------------------

- (NSString*) title
/*"
   Returns our title. This may or may not be displayed depending on the
   return value of #displaysTitle.
"*/
{
    return [[self cell] title];
}


- (void) setTitle:(NSString*)newTitle
/*"
   Sets our title. In subclasses it is your responsibility to set the title
   when you accept a drag.
"*/
{
    [[self cell] setTitle:newTitle];
}


- (BOOL) displaysTitle
/*"
   Returns YES if we display a title in our view. The default is YES.
"*/
{
    return [[self cell] displaysTitle];
}


- (void) setDisplaysTitle:(BOOL)displays
/*"
   Sets whether we display a title in our view. The default is YES.
"*/
{
    [[self cell] setDisplaysTitle:displays];
}



- (BOOL) isTitleEditable
/*"
   Returns whether single or double clicking on the title will
   allow you to edit it. (Title editing NOT IMPLEMENTED YET)
"*/
{
    return [[self cell] isTitleEditable];
}


- (void) setTitleEditable:(BOOL)isEditable
/*"
    Sets whether single or double clicking on the title will
    allow you to edit it. (Title editing NOT IMPLEMENTED YET)
"*/
{
    [[self cell] setTitleEditable:isEditable];
}


- (BOOL) isTitleMultiline
/*"
   NOT IMPLEMENTED YET
"*/
{
    return [[self cell] isTitleMultiline];
}


- (void) setTitleMultiline:(BOOL)isMultiline
/*"
   NOT IMPLEMENTED YET
"*/
{
    [[self cell] setTitleMultiline:isMultiline];
}


//-------------------------------------------------------------------
// 	Delegation
//-------------------------------------------------------------------

- (id) delegate
/*"
   Returns our delegate or nil if we don't have one. Right now there
   are no delegate messages but there will be soon (similar to the
   ones from the old drag view).
"*/
{
    return _delegate;
}


- (void) setDelegate:(id)newDelegate
/*"
   Sets our delegate to newDelegate.
"*/
{
    _delegate = newDelegate;
}


//-------------------------------------------------------------------
// 	Destination dragging setup
//-------------------------------------------------------------------

- (void) initializeDragTypes
/*"
   This method registers the dragging types our cell is interested
   in receiving, by calling our drag cell's #acceptingPasteboardTypes
   method and registering them. This is called from both our
   #init method and #initWithCoder: so both new and unarchived drag
   views will be registered.
"*/
{
	NSArray* draggedTypes;
	
	draggedTypes = [[self cell] acceptingPasteboardTypes];
	if ([draggedTypes count] > 0) {
		[self registerForDraggedTypes:draggedTypes];
	}
}


//-------------------------------------------------------------------
// 	Overridden NSView methods
//-------------------------------------------------------------------

- (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
/*"
   Returns YES otherwise we wouldn't even get a #mouseDown: message.
"*/
{
    return YES;
}


- (BOOL) shouldDelayWindowOrderingForEvent:(NSEvent*)theEvent 
/*"
   Returns YES but I'm not sure why any more?
"*/
{
    return YES;
}

    
//-------------------------------------------------------------------
// 	Archiving methods
//-------------------------------------------------------------------

- (id) initWithCoder:(NSCoder*)aDecoder
/*"
   Unarchives an instance of MiscDragView. Returns self.
"*/
{
    int version;
    // What used to be ivars in the older versions.
    NSSize imageSize;
    int	border;
    BOOL allowSourceDragging;
    BOOL allowDestinationDragging;
    BOOL acceptForeignDrag;
    BOOL acceptLocalDrag;
    BOOL acceptSelfDrag;
    BOOL retainData;
    BOOL shadowIncoming;
    BOOL dragImageIsMyImage;
    id target;
    SEL action;
    
    [super initWithCoder:aDecoder];

    version = [aDecoder versionForClassName:@"MiscDragView"];
    
	/*
	 * **** Archiving: READ ME **** This code (and its analogue in write:) is
	 * critical. When you bump MISC_DRAG_VIEW_VERSION, copy the whole _current_
	 * case ("case MISC_DRAG_VIEW_VERSION: ... break;"), and duplicate it.
	 * Change the "MISC_DRAG_VIEW_VERSION" in the old case to the OLD
	 * (pre-bump) version number. Now, dork the "new current" version into
	 * whatever you want. See how this code can now read EITHER version out of
	 * the typed stream?
	 *
	 * If you are adding yet another version, leave the previous versions in
	 * place. That way you can still read archived objects that are more than
	 * one version old. Don't forget to take whatever actions are necessary to
	 * harmonize those old values. For example, if you converted an "int" ivar
	 * to "double", you'd need to (in the old version) to read the int version
	 * into a temp variable, and convert it in to the double var. BJM 5/24/94
	 */
    switch (version) {
        case 0: 
            [self setImage:[aDecoder decodeObject]];
            // We don't deal with image sizes anymore.
            imageSize = [aDecoder decodeSize];
            [aDecoder decodeValuesOfObjCTypes:"i", &border];	
		
            [self setDelegate:[aDecoder decodeObject]];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowSourceDragging];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowDestinationDragging];
            // We don't have ivars for the rest of these any more.
            [aDecoder decodeValuesOfObjCTypes:"cc", &acceptForeignDrag,
                &acceptLocalDrag];
            [aDecoder decodeValuesOfObjCTypes:"cc", &acceptSelfDrag,
                &retainData];

            // Keep the attributes that we still care about.
            [self setBorderType:(NSBorderType)border];
            [self setAllowsSourceDragging:allowSourceDragging];
            [self setAllowsDestinationDragging:allowDestinationDragging];
            break;

        case 1:
            [self setImage:[aDecoder decodeObject]];
            // We don't care about image size anymore.
            imageSize = [aDecoder decodeSize];
            [aDecoder decodeValuesOfObjCTypes:"i", &border];	

            [self setDelegate:[aDecoder decodeObject]];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowSourceDragging];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowDestinationDragging];
            // We don't use these values any more.
            [aDecoder decodeValuesOfObjCTypes:"cc", &acceptForeignDrag,
                &acceptLocalDrag];
            [aDecoder decodeValuesOfObjCTypes:"cc", &acceptSelfDrag, &retainData];
            // Version 1 adds target/action stuff -- shayman
            [aDecoder decodeValuesOfObjCTypes:"@:", &target, &action];

            // Keep the attributes we still care about.
            [self setBorderType:(NSBorderType)border];
            [self setAllowsSourceDragging:allowSourceDragging];
            [self setAllowsDestinationDragging:allowDestinationDragging];
            [self setTarget:target];
            [self setAction:action];
            break;

        case 2:
            [self setImage:[aDecoder decodeObject]];
            // We don't care about image size anymore.
            imageSize = [aDecoder decodeSize];
            [aDecoder decodeValuesOfObjCTypes:"i", &border];	

            [self setDelegate:[aDecoder decodeObject]];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowSourceDragging];
            [aDecoder decodeValuesOfObjCTypes:"c", &allowDestinationDragging];
            [aDecoder decodeValuesOfObjCTypes:"@:", &target, &action];

            // Keep the attributes we still care about.
            [self setBorderType:(NSBorderType)border];
            [self setAllowsSourceDragging:allowSourceDragging];
            [self setAllowsDestinationDragging:allowDestinationDragging];
            [self setTarget:target];
            [self setAction:action];
            break;

      case 3:
          [self setImage:[aDecoder decodeObject]];
          // We don't care about the image size anymore.
          imageSize = [aDecoder decodeSize];
          [aDecoder decodeValuesOfObjCTypes:"i", &border];	

          [self setDelegate:[aDecoder decodeObject]];
          [aDecoder decodeValuesOfObjCTypes:"c", &allowSourceDragging];
          [aDecoder decodeValuesOfObjCTypes:"c", &allowDestinationDragging];
          [aDecoder decodeValuesOfObjCTypes:"@:", &target, &action];

          [aDecoder decodeValuesOfObjCTypes:"cc", &shadowIncoming,&dragImageIsMyImage];	
          [self setAcceptingImage:[aDecoder decodeObject]];
          break;

      case MISC_DRAG_VIEW_VERSION:
          // We now only have an ivar for our delegate.
          [self setDelegate:[aDecoder decodeObject]];
          break;

      default:
          NSLog(@"[%@ %@] - unknown version of MiscDragView encountered.",
              NSStringFromClass([self class]), NSStringFromSelector(_cmd));
          break;
    }

    [self initializeDragTypes];
    return self;
}


- (void) encodeWithCoder:(NSCoder *)aCoder
/*"
   Encodes an instance of MiscDragView.
"*/
{
    [super encodeWithCoder:aCoder];
    
    [aCoder encodeConditionalObject:[self delegate]];
}

@end


@implementation MiscDragView (NSDraggingSource)

/*"
   As you can see we just pass all these onto our cell.
"*/

- (unsigned int) draggingSourceOperationMaskForLocal:(BOOL)flag
{
    return [[self cell] draggingSourceOperationMaskForLocal:flag];
}


- (void) draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint
            deposited:(BOOL)flag
{
    [[self cell] draggedImage:image endedAt:screenPoint deposited:flag];
}

@end


@implementation MiscDragView (NSDraggingDestination)

/*"
   All destination dragging is also handled by the cell.
"*/

- (unsigned int) draggingEntered:(id <NSDraggingInfo>)sender
{
    return [[self cell] draggingEntered:sender];
}


- (unsigned int) draggingUpdated:(id <NSDraggingInfo>)sender
{	
    return [[self cell] draggingUpdated:sender];
}


- (void) draggingExited:(id <NSDraggingInfo>)sender
{
    [[self cell] draggingExited:sender];
}


- (BOOL) prepareForDragOperation:(id <NSDraggingInfo>)sender
{
    return [[self cell] prepareForDragOperation:sender];
}


- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
{	
    return [[self cell] performDragOperation:sender];
}


- (void) concludeDragOperation:(id <NSDraggingInfo>)sender
{
    return [[self cell] concludeDragOperation:sender];
}

@end


/***************************************************************************
  CHANGES:

   July 13, 1996
   (1) Converted the code to OpenStep and moved all the dragging
   logic into MiscDragCell so down the road I (or someone else)
   can write a MiscDragMatrix.
   
 ****************************************************************************/
 	 
	 