#import "FileShelfView.h"
#import "FileCollection.h"
#import "TokenString.h"
#import "FileName.h"

static id font;

@interface FileShelfView (PRIVATE)
- _burstNames;				     
- (const char *) _formatFileName: str width: (float)len; 
- (NXPoint *) _pointForIndex: (int) index;
- (int) _indexForPoint: (NXPoint *)hit;
@end

@implementation FileShelfView

-initFrame:(const NXRect *)rect
{
   [super initFrame: rect];
   [self registerForDraggedTypes:&NXFilenamePboardType count:1];
   images = [[List alloc] init];
   fileNames = [[List alloc] init];
   singleNames = [[FileCollection alloc] init];
   nameListDirty = NO;
   font = [Font systemFontOfSize: 0 matrix: NX_IDENTITYMATRIX];

   [self setEnabled: YES];
   [self setTitleWidth: 60];
   [self setShowNames: YES];

   return self;
}

- free
{
   [self unregisterDraggedTypes];
   [images freeObjects];
   [images free];
   [fileNames freeObjects];
   [fileNames free];
   [singleNames freeObjects];
   [singleNames free];
   return [super free];
}

- empty
{
   [images freeObjects];
   [fileNames freeObjects];
   [self display];
   return self;
}

- drawSelf:(NXRect *)r :(int) count
{
   NXPoint *point;
   int cnt, y;
   const char *label;

   /* Erase the whole view */
   if(enabled)
       NXDrawGrayBezel(&bounds, &bounds);    /* with bezel if enabled */
   else
   {
      NXSetColor ([window backgroundColor]);
      NXRectFill (&bounds);
      return self;
   }
   
   PSsetgray(NX_BLACK);
   [font set];

   for(y = 0, cnt = [images count]; y < cnt; y++)
   {
      [[images objectAt: y] composite: NX_SOVER toPoint: point = [self _pointForIndex: y]];
      if(showNames)
      {
	 label = [self _formatFileName: [fileNames objectAt: y] width: titleWidth - 4.0 ]; 
	 PSmoveto((point->x + 24.0) - ([font getWidthOf: label]/2.0) , point->y - [font pointSize]);
	 PSshow(label);
      }
   }
   return self;
}

- fileNames
{
   if(nameListDirty)			     /* if the list has changed we need to burst it before accessing */
       [self _burstNames];

   return singleNames;
}

- setEnabled: (BOOL)flag		     /* will it accept files? */
{
   if(flag == enabled)
       return self;

   enabled = flag;
   [self empty];
   [self display];
   return self;
}

- (BOOL) enabled
{
   return enabled;
}

- addFile: (const char *)filename	     /* add file(s) manually */
{
   id file = [[StringStorage alloc] init: filename];
   NXImage *image;
   
   image = [[Application workspace] getIconForFile: [file stringValue]];

   if(!(image && enabled))
   {
      [file free];
      return nil;
   }

   [images addObject: image];
   [fileNames addObject: file];
   [self display];
   nameListDirty = YES;
   return self;
}

- mouseDown: (NXEvent *)anEvent
{
   NXEvent saveEvent;
   NXPoint offset, origin;
   Pasteboard *dragPasteboard;

   offset.x = offset.y = 0;
   saveEvent = *anEvent;
					     /* find the hit icon and its origin */
   origin = anEvent->location;
   [self convertPoint: &origin fromView: nil];
   dragIndex = [self _indexForPoint: &origin];
					     /* create and load pasteboard - then start drag */
   dragPasteboard = [Pasteboard newName: NXDragPboard];
   [dragPasteboard declareTypes: &NXFilenamePboardType num:1 owner:self];
   [dragPasteboard writeType: NXFilenamePboardType data: [[fileNames objectAt: dragIndex] stringValue] 
        length: [[fileNames objectAt: dragIndex] strlen]];
   [self dragImage: [images objectAt: dragIndex] at: [self _pointForIndex: dragIndex] offset: &offset
        event: &saveEvent pasteboard: dragPasteboard source: self slideBack: NO];

   return [super mouseDown: anEvent];
}

- setShowNames: (BOOL) flag		     /* Should the names appear on the shelf? */
{
   showNames = flag;
   return self;
}

-(BOOL) showNames
{
   return showNames;
}

- setTitleWidth: (float) aWidth		     /* how wide is a shelf slot - min 50 */
{
   titleWidth = (aWidth > 50.0 ? aWidth : 50.0);
   return self;
}

- (float) titleWidth
{
   return titleWidth;
}


@end

@implementation FileShelfView (NXDraggingDestinationProtocol)
- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{

   if(enabled && ([sender draggingSourceOperationMask] & NX_DragOperationCopy))
       return NX_DragOperationCopy;
   else
       return NX_DragOperationNone;
}

- draggingExited:(id <NXDraggingInfo>) sender
{
   return self;
}

- (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender
{
   return YES;
}

- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
{
   return ([self draggingEntered: sender] == NX_DragOperationNone) ? NO : YES;
}

- concludeDragOperation:(id <NXDraggingInfo>)sender
{
   Pasteboard *pboard = [sender draggingPasteboard];
   char *files;
   int len;
   id aFile;

   [pboard readType:NXFilenamePboardType data: &files length: &len];
   if(len)
   {
      [images addObject: [sender draggedImageCopy]];
      aFile = [[StringStorage alloc] init: files];
      [fileNames addObject:aFile];
      [self display];
      nameListDirty = YES;
   }
   return self;
}


@end

@implementation FileShelfView (FSVNXDraggingSource)

- (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
   return NX_DragOperationCopy;
}

- draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint deposited:(BOOL)didDeposit
{

   if(!didDeposit)
   {
      [[images removeObjectAt: dragIndex] free];
      [[fileNames removeObjectAt: dragIndex] free];
      [self display];
      nameListDirty = YES;
   }
   return self;
}

- (BOOL)ignoreModifierKeysWhileDragging
{
   return NO;
}

@end

@implementation FileShelfView (PRIVATE)
/*
 * fileNames may contain multiple file entries - break them out to single entries.
 */
- _burstNames
{
   int x, cnt;
   id someNames = [[TokenString alloc] init];
   id aFileName;
   const char *fileNameStr;
   
   [singleNames freeObjects];		     /* free old version of the list */
   [someNames setSeparator: '\t'];

   for(x = 0, cnt = [fileNames count]; x < cnt; x++)
   {
      [someNames setStringValue: [[fileNames objectAt: x] stringValue]];
      
      while(fileNameStr = [someNames popStringValue])
      {
	 aFileName = [[StringStorage alloc] init: fileNameStr];
	 [singleNames addObject: aFileName];
      }
   }

   [someNames free];
   nameListDirty = NO;
   return self;
}

- (const char *) _formatFileName: fn width: (float)len 
{					     /* format file names like the Workspace shelf. Handle multiple items */
   static char buf[MAXPATHLEN];
   const char *str = [fn stringValue];
   int x, cnt;

   for(cnt = 0, x = 0; str[x]; x++)
   {
      if(str[x] == '\t')
	  cnt++;
   }
   
   if(cnt)
       sprintf(buf,"%d items", cnt + 1);
   else
       strcpy(buf,[fn basename]);

   if([font getWidthOf: buf] > len)
   {
      for(x = strlen(str); x > 0 ; x--)
      {
	 if([font getWidthOf: buf] <= len)
	     break;
	 buf[x - 1] = (char)0;
      }
      strcpy(buf + (x - 3), "...");
   }
   

   return buf;
}

- (NXPoint *) _pointForIndex: (int) index    /* calculate the corner point for the nth icon */
{
   static NXPoint point;
   int row, perRow;

   perRow = NX_WIDTH(&bounds)/titleWidth;
   row = (index/perRow) + 1;
   point.x = (float)((index % perRow) * titleWidth) + ((titleWidth/2.0) - 24.0);
   point.y = NX_HEIGHT(&bounds) - (row * 52); 
   if(showNames)
       point.y -= ((row - 1) * [font pointSize]);
   return &point;
}

- (int) _indexForPoint: (NXPoint *)hit	     /* based on a point which icon was hit? */
{
   int column, row;

   row = (NX_HEIGHT(&bounds) - hit->y)/(52 + (showNames ? [font pointSize] : 0));
   column = hit->x/titleWidth;
   return column + (row * (int)(NX_WIDTH(&bounds)/titleWidth));
}

@end
