/* FSNBrowser.m
 *  
 * Copyright (C) 2004-2024 Free Software Foundation, Inc.
 *
 * Author: Enrico Sersale
 *         Riccardo Mottola <rm@gnu.org>
 * Date: July 2004
 *
 * This file is part of the GNUstep Workspace application
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
 */


#include <math.h>
#include <unistd.h>

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <GNUstepBase/GNUstep.h>

/* Minimum column width used for reversible column count calculation
 * on window resize (see _recalcColumnCount).  Must match DEFAULT_INCR
 * in GWViewer.m so that initial and dynamic counts are consistent. */
#define FSNBROWSER_MIN_COL_WIDTH 150.0
#import "FSNBrowser.h"
#import "FSNBrowserColumn.h"
#import "FSNBrowserMatrix.h"
#import "FSNBrowserCell.h"
#import "FSNIcon.h"
#import "FSNFunctions.h"


#define DEFAULT_ISIZE 24

#ifndef max
#define max(a,b) ((a) >= (b) ? (a):(b))
#define min(a,b) ((a) <= (b) ? (a):(b))
#endif

@implementation FSNBrowser

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  /* Do NOT clear scroller target/action here — the scroller is a subview
   * of the parent NSScrollView and may already be deallocated by the time
   * this dealloc runs (subviews are released before the document view).
   * releaseScroller is called separately while the view hierarchy is intact. */
  RELEASE (baseNode);
  RELEASE (extInfoType);
  RELEASE (lastSelection);
  RELEASE (columns);
  RELEASE (nameEditor);
  RELEASE (cellPrototype);
  RELEASE (charBuffer);
  RELEASE (backColor);

  [super dealloc];
}

/* Before releasing the browser, clear the scroller's target/action
 * so the scroll view does not hold a dangling pointer when it next
 * tiles or the scroller is otherwise accessed. */
- (void)releaseScroller
{
  if (scroller)
    {
      [scroller setTarget: nil];
      [scroller setAction: NULL];
    }
}

- (id)initWithBaseNode:(FSNode *)bsnode
		    visibleColumns:(int)vcols 
              scroller:(NSScroller *)scrl
            cellsIcons:(BOOL)cicns
         editableCells:(BOOL)edcells
       selectionColumn:(BOOL)selcol
{
  self = [super init];
  
  if (self) {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];	
    NSString *appName = [defaults stringForKey: @"DesktopApplicationName"];
    NSString *selName = [defaults stringForKey: @"DesktopApplicationSelName"];
    id defentry;
    NSInteger i;
    
    fsnodeRep = [FSNodeRep sharedInstance];
    
    if (appName && selName) {
      Class desktopAppClass = [[NSBundle mainBundle] classNamed: appName];
      SEL sel = NSSelectorFromString(selName);
      desktopApp = [desktopAppClass performSelector: sel];
    }
    
    ASSIGN (backColor, [NSColor windowBackgroundColor]);
  
    defentry = [defaults objectForKey: @"fsn_info_type"];
    infoType = defentry ? [defentry intValue] : FSNInfoNameType;
    extInfoType = nil;
    
    if (infoType == FSNInfoExtendedType) {
      defentry = [defaults objectForKey: @"extended_info_type"];

      if (defentry) {
        NSArray *availableTypes = [fsnodeRep availableExtendedInfoNames];
      
        if ([availableTypes containsObject: defentry]) {
          ASSIGN (extInfoType, defentry);
        }
      }
      
      if (extInfoType == nil) {
        infoType = FSNInfoNameType;
      }
    }
    
    ASSIGN (baseNode, [FSNode nodeWithPath: [bsnode path]]);	    
    [self readNodeInfo];
    
    lastSelection = nil;
    visibleColumns = vcols;
    
    scroller = scrl;
    [scroller setTarget: self];
    [scroller setAction: @selector(scrollViaScroller:)];    
    
    cellsIcon = cicns;
    selColumn = selcol;

    updateViewsLock = 0;
   
    if ([defaults objectForKey:@"NSFontSize"])
      fontSize = [defaults integerForKey:@"NSFontSize"];
    else
      fontSize = 12;
    cellPrototype = [FSNBrowserCell new];
    [cellPrototype setFont: [NSFont systemFontOfSize: fontSize]];

    columns = [NSMutableArray new];
  
    nameEditor = nil;
  
    if (edcells) {
      nameEditor = [FSNCellNameEditor new];
      [nameEditor setDelegate: self];  
      [nameEditor setEditable: YES];
      [nameEditor setSelectable: YES];	   
      [nameEditor setFont: [cellPrototype font]];
      [nameEditor setBezeled: NO];
      [nameEditor setAlignment: NSLeftTextAlignment];
    }  
  
    for (i = 0; i < visibleColumns; i++) {
      [self createEmptyColumn];
    }
		    
    firstVisibleColumn = 0;
    lastVisibleColumn = visibleColumns - 1;	
    currentshift = 0;	
    lastColumnLoaded = -1;
    typingBufferColumn = -1;
		
    skipUpdateScroller = NO;
    lastKeyPressedTime = 0.0;
    charBuffer = nil;
    simulatingDoubleClick = NO;    
    isLoaded = NO;	

    viewer = nil;
    manager = nil;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(defaultsChanged:) name:NSUserDefaultsDidChangeNotification object:nil];
  }
  
  return self;
}

- (void)defaultsChanged:(NSNotification *)not
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];	
  NSInteger newSize;

  if ([defaults objectForKey:@"NSFontSize"]) {
    newSize = [defaults integerForKey:@"NSFontSize"];
    if (newSize != fontSize) {
      fontSize = newSize;
      [cellPrototype setFont: [NSFont systemFontOfSize: fontSize]];
      [nameEditor setFont: [cellPrototype font]];
      [self setVisibleColumns:[self visibleColumns]];
    }
  }

  [self reloadExtensionDisplay];
}

- (void)reloadExtensionDisplay
{
  NSInteger i;
  for (i = 0; i < [columns count]; i++) {
    FSNBrowserColumn *col = [columns objectAtIndex: i];
    NSMatrix *matrix = [col cmatrix];
    NSCell *cell;
    NSInteger r, c;
    for (r = 0; r < [matrix numberOfRows]; r++) {
      for (c = 0; c < [matrix numberOfColumns]; c++) {
        cell = [matrix cellAtRow: r column: c];
        if ([cell isKindOfClass: [FSNBrowserCell class]]) {
          [(FSNBrowserCell *)cell setNodeInfoShowType: [(FSNBrowserCell *)cell nodeInfoShowType]];
        }
      }
    }
  }
}

- (void)setBaseNode:(FSNode *)node
{
  ASSIGN (baseNode, [FSNode nodeWithPath: [node path]]);
  [self readNodeInfo];
  [self loadColumnZero];
  [self notifySelectionChange: [NSArray arrayWithObject: node]];
}

- (void)setUsesCellsIcons:(BOOL)cicns
{
  cellsIcon = cicns;
}

- (void)setUsesSelectionColumn:(BOOL)selcol
{
  selColumn = selcol;
}

- (void)setVisibleColumns:(NSInteger)vcols
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  NSArray *selection = nil;
  NSInteger i;

  updateViewsLock++;
  
  if (bc) {
    selection = [bc selectedNodes];
  
    if ((selection == nil) && [bc shownNode]) {
      selection = [NSArray arrayWithObject: [bc shownNode]];
    }
  }  
  
  if (selection == nil) {
    selection = [NSArray arrayWithObject: baseNode];
  }
  
  selection = [selection copy];
  
  for (i = 0; i < [columns count]; i++) {
    [[columns objectAtIndex: i] removeFromSuperview];
  }

  [columns removeAllObjects];

  visibleColumns = vcols;
  
  for (i = 0; i < visibleColumns; i++) {
    [self createEmptyColumn];
  }
		    
  firstVisibleColumn = 0;
  lastVisibleColumn = visibleColumns - 1;	
  currentshift = 0;	
  lastColumnLoaded = -1;
  skipUpdateScroller = NO;
  isLoaded = NO;	

  [self showSelection: selection];
  RELEASE (selection);
    
  updateViewsLock--;
  [self tile];

  bc = [self lastLoadedColumn];
  if (bc) {
    [[self window] makeFirstResponder: [bc cmatrix]];
  }  
}

- (NSInteger)visibleColumns
{
  return visibleColumns;
}


- (void)showSubnode:(FSNode *)node
{
  NSArray *components;
  NSInteger column;
  NSInteger i;
  
  updateViewsLock++;
  
  if ([node isEqual: baseNode] || ([node isSubnodeOfNode: baseNode] == NO)) {
    updateViewsLock--;
    [self setBaseNode: node];
    [self tile];
		[self setNeedsDisplay: YES];
		return;
	}

  [self loadColumnZero];
  
  if ([[baseNode path] isEqual: path_separator()]) {
    components = [FSNode nodeComponentsToNode: node];
  } else {
    components = [FSNode nodeComponentsFromNode: baseNode toNode: node];
  }

  if ([components count] == 1) {
    updateViewsLock--;
    [self tile];
		[self setNeedsDisplay: YES];
		return;
  }

  components = [components subarrayWithRange: NSMakeRange(1, [components count] -1)];

  column = lastColumnLoaded;
  
  for (i = 0; i < [components count]; i++) {
    FSNBrowserColumn *bc = [columns objectAtIndex: column + i];
    FSNode *nd = [components objectAtIndex: i];
    FSNBrowserCell *cell = [bc selectCellOfNode: nd sendAction: NO];

    if (cell) {
      if ([cell isLeaf]) {
        break;
      }
    } else {
      NSDebugLLog(@"gwspace", @"Browser: unable to find cell '%@' in column %ld\n", [nd name], (long int)(column + i));
      break;
    }
    
    nd = [FSNode nodeWithRelativePath: [nd lastPathComponent] parent: [bc shownNode]];
    [self addAndLoadColumnForNode: nd];
  }
  
  updateViewsLock--;
  [self tile];
  [self setNeedsDisplay: YES];
}

- (void)showSelection:(NSArray *)selection
{
  if (selection && [selection count]) {
    FSNode *node = [selection objectAtIndex: 0];
    FSNBrowserColumn *bc;
    NSArray *selNodes;

    updateViewsLock++;

    if ([selection count] > 1) {
      BOOL alldirs = YES;
      int i;
      
      for (i = 0; i < [selection count]; i++) {
        FSNode *nd = [selection objectAtIndex: i];  
        
        if ([nd isDirectory] == NO) {
          node = nd;
          alldirs = NO;
          break;
        }
      }
    
      if (alldirs) {
        node = [FSNode nodeWithPath: [node parentPath]];
      }
    }
    
    [self showSubnode: node];

    bc = [self lastLoadedColumn];
    [bc selectCellsOfNodes: selection sendAction: NO];

    if (selColumn) {
      if ([selection count] == 1) {
        FSNode *node = [selection objectAtIndex: 0];

        if (([node isDirectory] == NO) || [node isPackage]) {
          [self addFillingColumn];
        }

      } else {
        [self addFillingColumn];
      }
    }

    updateViewsLock--;
    [self tile];

    selNodes = [bc selectedNodes];
    if (selNodes == nil) {
      selNodes = [NSArray arrayWithObject: [bc shownNode]];
    }
    [self notifySelectionChange: selNodes];
  }
}

- (void)showPathsSelection:(NSArray *)selpaths
{
  if (selpaths && [selpaths count]) {
    FSNode *node = [FSNode nodeWithPath: [selpaths objectAtIndex: 0]];      
    FSNBrowserColumn *bc;
    NSArray *selNodes;

    updateViewsLock++;

    if ([selpaths count] > 1) {
      BOOL alldirs = YES;
      int i;
      
      for (i = 0; i < [selpaths count]; i++) {
        FSNode *nd = [FSNode nodeWithPath: [selpaths objectAtIndex: i]];
        
        if ([nd isDirectory] == NO) {
          node = nd;
          alldirs = NO;
          break;
        }
      }
    
      if (alldirs) {
        node = [FSNode nodeWithPath: [node parentPath]];
      }
    }

    [self showSubnode: node];

    bc = [self lastLoadedColumn];
    [bc selectCellsWithPaths: selpaths sendAction: NO];

    if (selColumn) {
      if ([selpaths count] == 1) {
        if (([node isDirectory] == NO) || [node isPackage]) {
          [self addFillingColumn];
        }

      } else {
        [self addFillingColumn];
      }
    }

    updateViewsLock--;
    [self tile];

    selNodes = [bc selectedNodes];
    if (selNodes == nil) {
      selNodes = [NSArray arrayWithObject: [bc shownNode]];
    }
    [self notifySelectionChange: selNodes];
  }
}


- (void)loadColumnZero
{
  updateViewsLock++;
  [self setLastColumn: -1];
  [self addAndLoadColumnForNode: baseNode];
  isLoaded = YES;
  updateViewsLock--;
  [self tile];
}

- (FSNBrowserColumn *)createEmptyColumn
{
  CREATE_AUTORELEASE_POOL(arp);
  int count = [columns count];
  FSNBrowserColumn *bc = [[FSNBrowserColumn alloc] initInBrowser: self
                                       atIndex: count
                                 cellPrototype: cellPrototype
                                     cellsIcon: cellsIcon
                                  nodeInfoType: infoType
                                  extendedType: extInfoType
                               backgroundColor: backColor];
                                     
  [columns insertObject: bc atIndex: count];
  [self addSubview: bc]; 
  RELEASE(bc);
  RELEASE (arp);
  	
  return bc;
}

- (void)addAndLoadColumnForNode:(FSNode *)node
{
  FSNBrowserColumn *bc;
  NSInteger i;
			
  if (lastColumnLoaded + 1 >= [columns count])
    {
      i = [columns indexOfObject: [self createEmptyColumn]];
    }
  else
    {
      i = lastColumnLoaded + 1;
    }

  bc = [columns objectAtIndex: i];
  [bc showContentsOfNode: node];

  updateViewsLock++;
  [self setLastColumn: i];
  isLoaded = YES;

  if ((i > 0) && ((i - 1) == lastVisibleColumn))
    {
      [self scrollColumnsRightBy: 1];
    }

  updateViewsLock--;
  [self tile];
}

- (void)addFillingColumn 
{
  NSInteger i;
			
  if (lastColumnLoaded + 1 >= [columns count])
    {
      i = [columns indexOfObject: [self createEmptyColumn]];
    }
  else
    {
      i = lastColumnLoaded + 1;
    }
        
  updateViewsLock++;
  [self setLastColumn: i];

  if ((i > 0) && ((i - 1) == lastVisibleColumn))
    { 
      [self scrollColumnsRightBy: 1];
    } 
  
  updateViewsLock--;
  [self tile];
}

- (void)unloadFromColumn:(NSInteger)column
{
  FSNBrowserColumn *bc = nil; 
  NSInteger count = [columns count];
  NSInteger i;			
  
  updateViewsLock++;
	
  for (i = column; i < count; ++i) {
		bc = [columns objectAtIndex: i];

		if ([bc isLoaded]) {			
	  	[bc showContentsOfNode: nil];
		}
		
		if (i >= visibleColumns) {
	  	[bc removeFromSuperview];
      [columns removeObject: bc];			
	  	count--;
	  	i--;
		}
  }
  
  if (column == 0) {
		isLoaded = NO;
	}
  
  if (column <= lastVisibleColumn) {
		[self scrollColumnsLeftBy: lastVisibleColumn - column + 1];
	}
	
  updateViewsLock--;
  [self tile];
}

- (void)reloadColumnWithNode:(FSNode *)anode
{
  FSNBrowserColumn *col = [self columnWithNode: anode];
  
  if (col) {
    [col showContentsOfNode: anode];    
  }
}

- (void)reloadColumnWithPath:(NSString *)path
{
  FSNBrowserColumn *col = [self columnWithPath: path];
  
  if (col) {
    FSNBrowserColumn *parentCol = [self columnBeforeColumn: col];
    FSNode *node;  
    
    if (parentCol) {
      node = [FSNode nodeWithRelativePath: path parent: [parentCol shownNode]];      
    } else {
      node = [FSNode nodeWithPath: path];      
    }
    
    [col showContentsOfNode: node];    
  }
}

- (void)reloadFromColumn:(FSNBrowserColumn *)col
{
  CREATE_AUTORELEASE_POOL(arp);
  NSInteger index = [col index];
  NSInteger i = 0;

  updateViewsLock++;
  
  for (i = index; i < [columns count]; i++) {
    FSNBrowserColumn *nextcol = [columns objectAtIndex: i];
    NSArray *selection = [self selectionInColumnBeforeColumn: nextcol];
    BOOL done = NO;
    
    if (selection && ([selection count] == 1)) {
      FSNode *node = [selection objectAtIndex: 0];
    
      if ([node isDirectory] && (([node isPackage] == NO) || (i == 0))) {
        [nextcol showContentsOfNode: node]; 
      } else {
        done = YES;
      }
    } else {
      done = YES;
    }
    
    if (done) {
      int last = (i > 0) ? i - 1 : 0;
      int shift = 0;
      int leftscr = 0;
    
      if (last >= visibleColumns) {
        if (last < firstVisibleColumn) {
          shift = visibleColumns - 1;
        } else if (last > lastVisibleColumn) {
          leftscr = last - lastVisibleColumn;
        } else {
          shift = lastVisibleColumn - last;
        }
      }

      [self setLastColumn: last];

      if (shift) {
        currentshift = 0;
        [self setShift: shift];
      } else if (leftscr) {
        [self scrollColumnsLeftBy: leftscr];
      }

      break;
    }
	}

  col = [self lastLoadedColumn];

  if (col) {
    NSArray *selection = [col selectedNodes];
    int index = [col index];

    if (index < firstVisibleColumn) {
      [self scrollColumnToVisible: index];      
    }
    
    [[self window] makeFirstResponder: [col cmatrix]];

    if (selection) {
      if (selColumn && (index == lastColumnLoaded)) {
        if ([selection count] == 1) {
          FSNode *node = [selection objectAtIndex: 0];
        
          if (([node isDirectory] == NO) || [node isPackage]) {
            [self addFillingColumn];
          }
        
        } else {
          [self addFillingColumn];
        }
      }
    
      [self notifySelectionChange: selection];	
    
    } else {
      FSNode *node = [col shownNode];
      [self notifySelectionChange: [NSArray arrayWithObject: node]];
    }
  }

  updateViewsLock--;
  [self tile];
  RELEASE (arp);
}

- (void)reloadFromColumnWithNode:(FSNode *)anode
{
  FSNBrowserColumn *col = [self columnWithNode: anode];
  
  if (col) {
    [self reloadFromColumn: col];
  }
}

- (void)reloadFromColumnWithPath:(NSString *)path
{
  FSNBrowserColumn *col = [self columnWithPath: path];
  
  if (col) {
    [self reloadFromColumn: col];
  }
}

- (void)setLastColumn:(int)column
{
  lastColumnLoaded = column;
  [self unloadFromColumn: column + 1];
}

- (void)selectRow:(NSInteger)row inColumn:(NSInteger)column
{
  if (column >= 0 && column < [columns count])
    {
      FSNBrowserColumn *bc = [columns objectAtIndex: column];
      NSMatrix *matrix = [bc cmatrix];
      if (row >= 0 && row < [matrix numberOfRows])
        {
          [bc selectCell: [matrix cellAtRow: row column: 0] sendAction: YES];
        }
    }
}


- (void)tile
{
  updateViewsLock = (updateViewsLock < 0) ? 0 : updateViewsLock;

  if (updateViewsLock == 0) {
    NSWindow *window = [self window];
    NSRect r = [self bounds];
    float frameWidth = r.size.width - visibleColumns;
    NSInteger count = [columns count];
    NSRect colrect;
    NSInteger i;

    columnSize.height = r.size.height;
    columnSize.width = myrintf(frameWidth / visibleColumns);
    
    [window disableFlushWindow];
    
    for (i = 0; i < count; i++)
      {
	NSInteger n = i - firstVisibleColumn;
    
	colrect = NSZeroRect;
	colrect.size = columnSize;
	colrect.origin.y = 0;

	if (i < firstVisibleColumn)
	  {
	    colrect.origin.x = (n * columnSize.width) - 8;
	  }
	else
	  {
	    if (i == firstVisibleColumn)
	      {
		colrect.origin.x = (n * columnSize.width);
	      }
	    else if (i <= lastVisibleColumn)
	      {
		colrect.origin.x = (n * columnSize.width) + n;
	      }
	    else
	      {
		colrect.origin.x = (n * columnSize.width) + n + 8;
	      }
	  }

	if (i == lastVisibleColumn)
	  {
	    colrect.size.width = [self bounds].size.width - colrect.origin.x;
	  }
      
	[[columns objectAtIndex: i] setFrame: colrect];
      }
    
    [self synchronizeViewer];
    [self updateScroller];
    [self stopCellEditing];
        
    [window enableFlushWindow];
    [window flushWindowIfNeeded];
  }
}

- (void)scrollViaScroller:(NSScroller *)sender
{
  NSScrollerPart hit = [sender hitPart];
  BOOL needsDisplay = NO;
  
  updateViewsLock++;
  skipUpdateScroller = YES;
  
  switch (hit) {
		// Scroll to the left
		case NSScrollerDecrementLine:
		case NSScrollerDecrementPage:        
			[self scrollColumnsLeftBy: 1];
			if (currentshift > 0) {
        [self setLastColumn: (lastColumnLoaded - currentshift)];
        [self setShift: currentshift - 1];
      }
			break;
      
		// Scroll to the right
		case NSScrollerIncrementLine:
		case NSScrollerIncrementPage:
      [self scrollColumnsRightBy: 1];
      needsDisplay = YES;
			break;
      
		// The knob or knob slot
		case NSScrollerKnob:
		case NSScrollerKnobSlot: 
      {
	  		float f = [sender floatValue];
	  		float n = lastColumnLoaded + 1 - visibleColumns;
				
	  		[self scrollColumnToVisible: myrintf(f * n) + visibleColumns - 1];
				
        if (currentshift > 0) {
          [self setLastColumn: (lastColumnLoaded - currentshift)];			
		      currentshift = 0;
        }
        
        needsDisplay = YES;
			}
      break;
      
		default:
			break;
	}
  
  skipUpdateScroller = NO;
  
  updateViewsLock--;
  [self tile];
  [self setNeedsDisplay: needsDisplay];
} 

- (void)updateScroller
{
  if ((lastColumnLoaded == 0) || (lastColumnLoaded <= (visibleColumns - 1))) {
		[scroller setEnabled: NO];

	} else {
		if (skipUpdateScroller == NO) {
			float prop = (float)visibleColumns / (float)(lastColumnLoaded + 1);
			float i = lastColumnLoaded - visibleColumns + 1;
			float f = 1 + ((lastVisibleColumn - lastColumnLoaded) / i);
      
	    if (lastVisibleColumn > lastColumnLoaded) {   
        prop = (float)visibleColumns / (float)(lastVisibleColumn + 1);
      }
      
			[scroller setFloatValue: f knobProportion: prop];
		}

		[scroller setEnabled: YES];
	}
  
  [scroller setNeedsDisplay: YES];
}

- (void)scrollColumnsLeftBy:(int)shiftAmount
{	
  if ((firstVisibleColumn - shiftAmount) < 0) {
    shiftAmount = firstVisibleColumn;
	}
	
  if (shiftAmount <= 0) {
    return;
	}
		
  firstVisibleColumn = firstVisibleColumn - shiftAmount;
  lastVisibleColumn = lastVisibleColumn - shiftAmount;

  [self tile];
  
  [self setNeedsDisplay: YES];
}

- (void)scrollColumnsRightBy:(int)shiftAmount
{	
  if ((shiftAmount + lastVisibleColumn) > lastColumnLoaded) {
    shiftAmount = lastColumnLoaded - lastVisibleColumn;
	}
	
  if (shiftAmount <= 0) {
    return;
	}
		
  firstVisibleColumn = firstVisibleColumn + shiftAmount;
  lastVisibleColumn = lastVisibleColumn + shiftAmount;

  [self tile];
}

- (void)scrollColumnToVisible:(NSInteger)column
{
  NSInteger i;
	
  if (lastVisibleColumn == column)
    return;
	
  if ((lastColumnLoaded + 1) <= visibleColumns)
    return;
		
  i = lastVisibleColumn - column;
  if (i > 0)
    {
      [self scrollColumnsLeftBy: i];
    }
  else
    {
      [self scrollColumnsRightBy: -i];
    }
}

- (void)moveLeft
{
  FSNBrowserColumn *selCol = [self selectedColumn];
  NSInteger index;
  			
  if (selCol == nil)
    {
      return;
    }

  index = [selCol index];
  
  if (index > 0)
    {
      updateViewsLock++;
    
      index--;
      if (index < firstVisibleColumn)
	{
	  [self scrollColumnToVisible: index];      
	}
    
      selCol = [columns objectAtIndex: index];
      [[self window] makeFirstResponder: [selCol cmatrix]];
      [self clickInMatrixOfColumn: selCol];
    
      updateViewsLock--;
      [self tile];
    }
}

- (void)moveRight
{
	FSNBrowserColumn *selCol = [self selectedColumn];
  			
	if (selCol == nil) {
		selCol = [columns objectAtIndex: 0];
    
    if ([selCol selectFirstCell]) {
      [[self window] makeFirstResponder: [selCol cmatrix]];
    }
	} else {
    NSMatrix *matrix = [selCol cmatrix];
    
    if ([[matrix cells] count]) {
      NSInteger index = [selCol index];
      
      [matrix sendAction];
      
      if (index < ([columns count] - 1)) {
        selCol = [columns objectAtIndex: index + 1];
        matrix = [selCol cmatrix];
        
        if ([[matrix cells] count]) {
          if ([selCol selectFirstCell]) {
            [matrix sendAction];  
            [[self window] makeFirstResponder: matrix];
          }
        }
      }
    }
  }
}

- (void)setShift:(int)s
{
  NSInteger i;
    			
  for (i = 0; i < s; i++)
    {
      [self createEmptyColumn];
    }
	
  currentshift = s;  
  updateViewsLock++;
  [self setLastColumn: (lastColumnLoaded + s)];
  [self scrollColumnsRightBy: s];
  updateViewsLock--;
  [self tile];
}

- (FSNode *)nodeOfLastColumn
{
  FSNBrowserColumn *col = [self lastLoadedColumn];

  if (col)
    return [col shownNode];
  
  return nil;
}

- (NSString *)pathToLastColumn
{
  FSNode *node = [self nodeOfLastColumn];

  if (node)
    return [node path];
  
  return nil;
}

- (NSArray *)selectionInColumnBeforeColumn:(FSNBrowserColumn *)col
{
  NSInteger index = [col index];
  
  if (index == 0) {
    return [NSArray arrayWithObject: baseNode];
  } 
  
  return [[columns objectAtIndex: index - 1] selectedNodes];
}

- (void)selectCellsWithNames:(NSArray *)names 
            inColumnWithPath:(NSString *)cpath
                  sendAction:(BOOL)act
{
  FSNBrowserColumn *col = [self columnWithPath: cpath];
  
  if (col) {
    [col selectCellsWithNames: names sendAction: act];
  }
}

- (void)selectAllInLastColumn
{
  FSNBrowserColumn *col = [self lastLoadedColumn];

  if (col) {
    [col selectAll];
  }
}

- (void)notifySelectionChange:(NSArray *)newsel
{
  if (newsel) {
    if ((lastSelection == nil) || ([newsel isEqual: lastSelection] == NO)) {
      ASSIGN (lastSelection, newsel);
      [self synchronizeViewer];
      [desktopApp selectionChanged: newsel];
    }
  }      
}

- (void)synchronizeViewer
{
  if (viewer)
    {
      NSRange range = NSMakeRange(firstVisibleColumn, visibleColumns);
      [viewer setSelectableNodesRange: range];
    }
}

- (void)addCellsWithNames:(NSArray *)names 
         inColumnWithPath:(NSString *)cpath
{
  FSNBrowserColumn *col = [self columnWithPath: cpath];
  
  if (col) {
    [col addCellsWithNames: names];
  }
}

- (void)removeCellsWithNames:(NSArray *)names 
            inColumnWithPath:(NSString *)cpath
{
  FSNBrowserColumn *col = [self columnWithPath: cpath];
  
  if (col) {
    [col removeCellsWithNames: names];
  }
}            

- (NSInteger)firstVisibleColumn
{
  return firstVisibleColumn;
}

- (NSInteger)lastColumnLoaded
{
  return lastColumnLoaded;
}

- (NSInteger)lastVisibleColumn
{
  return lastVisibleColumn;
}

- (FSNBrowserColumn *)selectedColumn
{
  NSInteger i;
  
  for (i = lastColumnLoaded; i >= 0; i--)
    {
      FSNBrowserColumn *col = [columns objectAtIndex: i];
    
      if ([col isSelected])
	{
	  return col;
	}
    }
  
  return nil;
}

- (FSNBrowserColumn *)lastLoadedColumn
{
  NSInteger i;
  
  for (i = [columns count] - 1; i >= 0; i--)
    {
      FSNBrowserColumn *col = [columns objectAtIndex: i];
  
      if ([col isLoaded])
	{
	  return col;        
	}
    }
  
  return nil;
}

- (FSNBrowserColumn *)columnWithNode:(FSNode *)anode
{
  NSInteger i;
  
  for (i = 0; i < [columns count]; i++)
    {
      FSNBrowserColumn *col = [columns objectAtIndex: i];
  
      if ([[col shownNode] isEqual: anode])
	{
	  return col;
	}
    }
  
  return nil;   
}

- (FSNBrowserColumn *)columnWithPath:(NSString *)cpath
{
  NSInteger i;
  
  for (i = 0; i < [columns count]; i++) {
    FSNBrowserColumn *col = [columns objectAtIndex: i];
  
    if ([[[col shownNode] path] isEqual: cpath]) {
      return col;
    }
  }
  
  return nil;   
}

- (FSNBrowserColumn *)columnBeforeColumn:(FSNBrowserColumn *)col
{
  NSInteger index = [col index];

  if (index > 0)
    {
      return [columns objectAtIndex: index - 1];  
    }
  
  return nil;
}

- (FSNBrowserColumn *)columnAfterColumn:(FSNBrowserColumn *)col
{
  NSInteger index = [col index];

  if (index < ([columns count] - 1))
    {
      return [columns objectAtIndex: index + 1];  
    }
  
  return nil;
}

- (void)clickInColumn:(FSNBrowserColumn *)col
{
  if (viewer)
    {
      NSArray *selection = [col selectedNodes];

      if (selection && [selection count])
	{
	  [viewer multipleNodeViewDidSelectSubNode: [col shownNode]];
	}
    }
}

- (void)clickInMatrixOfColumn:(FSNBrowserColumn *)col
{
  NSInteger index = [col index];
  NSInteger pos = index - firstVisibleColumn + 1;  
  BOOL mustshift = (firstVisibleColumn > 0);
  NSInteger added = 0;
  NSArray *selection = [col selectedNodes];
  
  if ((selection == nil) || ([selection count] == 0))
    {
      [self notifySelectionChange: [NSArray arrayWithObject: [col shownNode]]];
      return;
    }

  if (selColumn)
    {
      if ((pos == visibleColumns) && (index == ([columns count] -1)))
        {
          NSPoint p = [[self window] mouseLocationOutsideOfEventStream];
          
          mousePointX = p.x;
          mousePointY = p.y;
          simulatingDoubleClick = YES;
          
          [NSTimer scheduledTimerWithTimeInterval: 0.3
                                           target: self 
                                         selector: @selector(doubleClikTimeOut:)
                                         userInfo: nil 
                                          repeats: NO];
        }
    }
  
  currentshift = 0;
  updateViewsLock++;
  
  [self setLastColumn: index];
  
  if ([selection count] == 1)
    {
      FSNode *node = [selection objectAtIndex: 0];
      
      if ([node isDirectory] && ([node isPackage] == NO))
        {
          [self addAndLoadColumnForNode: node];
          added = 1;
          
        }
      else if (selColumn)
        {
          [self addFillingColumn];
        }  
    }
  else if (selColumn)
    {
      [self addFillingColumn];
    } 
  
  if (selColumn == NO)
    {
      if (mustshift && (pos < visibleColumns))
        { 
          [self setShift: visibleColumns - pos - added];
        }
    }
  else
    {
      if (mustshift && (pos < (visibleColumns - 1)))
        { 
          [self setShift: visibleColumns - pos - 1];
        }
    }
  
  updateViewsLock--;
  [self tile];
  
  [self notifySelectionChange: [col selectedNodes]];		  
}

- (void)doubleClickInMatrixOfColumn:(FSNBrowserColumn *)col
{
  if (manager) {
    unsigned int mouseFlags = [(FSNBrowserMatrix *)[col cmatrix] mouseFlags];
    BOOL closesndr = ((mouseFlags == NSAlternateKeyMask) 
                              || (mouseFlags == NSControlKeyMask));
    
    [viewer openSelectionInNewViewer: closesndr];
//    [manager openSelectionInViewer: viewer closeSender: closesndr];
  } else {
    [desktopApp openSelectionInNewViewer: NO];
  }
}

- (void)doubleClikTimeOut:(id)sender
{
  simulatingDoubleClick = NO;
}

- (void)mouseDown:(NSEvent*)theEvent
{
  if (simulatingDoubleClick) {
    NSPoint p = [[self window] mouseLocationOutsideOfEventStream];
      
    if ((max(p.x, mousePointX) - min(p.x, mousePointX)) <= 3
            && (max(p.y, mousePointY) - min(p.y, mousePointY)) <= 3) {
      if (manager) {
        [manager openSelectionInViewer: viewer closeSender: NO];
      } else {
        [desktopApp openSelectionInNewViewer: NO];
      }
    }
  }
  
  [super mouseDown: theEvent];
}

- (void)keyDown:(NSEvent *)theEvent
{
  NSString *characters = [theEvent characters];
  unichar character = 0;
  NSString *characterStr = nil;
  FSNBrowserColumn *column = [self selectedColumn];
  NSMatrix *matrix;

  if (column == nil)
    {
      [super keyDown: theEvent];
      return;
    }

  matrix = [column cmatrix];

  if (matrix == nil)
    {
      [super keyDown: theEvent];
      return;
    }
		
  if ([characters length] > 0)
    {
      character = [characters characterAtIndex: 0];
      characterStr = [characters substringToIndex: 1];
    }

  switch (character)
    {
    case NSUpArrowFunctionKey:
    case NSDownArrowFunctionKey:
      [super keyDown: theEvent];
      return;
	
    case NSLeftArrowFunctionKey:
      {
	if ([theEvent modifierFlags] & NSControlKeyMask)
	  {
	    [super keyDown: theEvent];
	  }
	else
	  {
	    [self moveLeft];
	  }
      }
      return;

    case NSRightArrowFunctionKey:
      {
	if ([theEvent modifierFlags] & NSControlKeyMask)
	  {
	    [super keyDown: theEvent];
	  }
	else
	  {
	    [self moveRight];
	  }
      }
      return;

    case NSCarriageReturnCharacter:
      [(FSNBrowserMatrix *)matrix setMouseFlags: [theEvent modifierFlags]];
      [matrix sendDoubleAction];
      DESTROY(charBuffer);
      return;
    case 0x01B: // Escape
      DESTROY(charBuffer);
      return;
    default:
      break;
  }  
  
  if (([characters length] > 0) && (character < 0xF700))
    {
      column = [self lastLoadedColumn];

      if (column)
	{
	  NSInteger index = [column index];

	  matrix = [column cmatrix];

	  if (matrix == nil)
	    {
	      return;
	    }

	  if (charBuffer != nil)
	    {
	      if (([theEvent timestamp] - lastKeyPressedTime < 5.0) // seconds
		  && (typingBufferColumn == index))
		{
		  NSString *appendBuffer = [charBuffer stringByAppendingString:characterStr];

		  // Try selecting
		  if ([column selectCellWithPrefix: appendBuffer])
		    {
		      ASSIGN(charBuffer, appendBuffer);
		      [[self window] makeFirstResponder: matrix];
		      return;
		    }
		  // unable to select - fall-through and reinit as if typed is first char
		}
	    }

	  ASSIGN(charBuffer, characterStr);
	  typingBufferColumn = index;
	  lastKeyPressedTime = [theEvent timestamp];

	  // Try selecting
	  if ([column selectCellWithPrefix: charBuffer])
            {
              [[self window] makeFirstResponder: matrix];
              return;
            }

	  // Selection failed, reinitialize and use mismatching character as new buffer beginning
	  DESTROY(charBuffer);
	}
    }

  [super keyDown: theEvent];
}

- (BOOL)acceptsFirstResponder
{
  return YES;
}

- (BOOL)becomeFirstResponder
{
  FSNBrowserColumn *selCol;
  NSMatrix *matrix;

  selCol = [self selectedColumn];
  
  if (selCol == nil) {
    matrix = [[columns objectAtIndex: 0] cmatrix];
  } else {
    matrix = [selCol cmatrix];
	}
	
  if ([[matrix cells] count]) {
    [[self window] makeFirstResponder: matrix];
	}
	
  return YES;
}

- (void)viewDidMoveToSuperview
{
  [super viewDidMoveToSuperview];

  if (_observedClipView)
    {
      [[NSNotificationCenter defaultCenter] removeObserver: self
                                                      name: NSViewFrameDidChangeNotification
                                                    object: _observedClipView];
      _observedClipView = nil;
    }

  if ([self superview] && [[self superview] isKindOfClass: [NSClipView class]])
    {
      _observedClipView = [self superview];
      [[NSNotificationCenter defaultCenter] addObserver: self
                                               selector: @selector(clipViewFrameDidChange:)
                                                   name: NSViewFrameDidChangeNotification
                                                 object: _observedClipView];
    }

  if ([self superview]) {
    [self setFrame: [[self superview] bounds]];
    [self tile];
  }
}

- (void)clipViewFrameDidChange:(NSNotification *)notif
{
  [self _recalcColumnCount];
}

- (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize
{
  [self _recalcColumnCount];
}

- (void)_recalcColumnCount
{
  NSRect r = [[self superview] bounds];
  [self setFrame: r];

  if (r.size.width > 0) {
    /* Use a fixed minimum column width so that the column count
     * is purely a function of the current width — fully reversible
     * when the window is made larger then smaller again. */
    NSInteger ncols = r.size.width / FSNBROWSER_MIN_COL_WIDTH;
    if (ncols < 1) ncols = 1;
    if (ncols != visibleColumns) {
      updateViewsLock++;
      [self setVisibleColumns: ncols];
      updateViewsLock--;
    }
  }

  [self tile];
}

/*
- (void)drawRect:(NSRect)rect
{
  int i;
  
	[[NSColor blackColor] set];
  
  for (i = 0; i < visibleColumns; i++) { 
    NSPoint p1 = NSMakePoint((columnSize.width * i) + 1 + (i-1), columnSize.height);
    NSPoint p2 = NSMakePoint((columnSize.width * i) + 1 + (i-1), 0);	
    [NSBezierPath strokeLineFromPoint: p1 toPoint: p2];
  }  
}
*/

@end


@implementation FSNBrowser (NodeRepContainer)

- (void)showContentsOfNode:(FSNode *)anode
{
  [self showSubnode: anode];
}

- (NSDictionary *)readNodeInfo
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSString *prefsname = [NSString stringWithFormat: @"viewer_at_%@", [baseNode path]];
  NSDictionary *nodeDict = [defaults dictionaryForKey: prefsname];

  if (nodeDict) {
    id entry = [nodeDict objectForKey: @"fsn_info_type"];
    infoType = entry ? [entry intValue] : infoType;

    if (infoType == FSNInfoExtendedType) {
      DESTROY (extInfoType);
      entry = [nodeDict objectForKey: @"ext_info_type"];

      if (entry) {
        NSArray *availableTypes = [fsnodeRep availableExtendedInfoNames];

        if ([availableTypes containsObject: entry]) {
          ASSIGN (extInfoType, entry);
        }
      }

      if (extInfoType == nil) {
        infoType = FSNInfoNameType;
      }
    }
  }
        
  return nodeDict;
}

- (NSMutableDictionary *)updateNodeInfo:(BOOL)ondisk
{
  CREATE_AUTORELEASE_POOL(arp);
  NSMutableDictionary *updatedInfo = nil;

  if ([baseNode isValid]) {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *prefsname = [NSString stringWithFormat: @"viewer_at_%@", [baseNode path]];

    NSDictionary *prefs = [defaults dictionaryForKey: prefsname];
    if (prefs)
      updatedInfo = [prefs mutableCopy];
    else
      updatedInfo = [NSMutableDictionary new];

    [updatedInfo setObject: [NSNumber numberWithInt: infoType]
                    forKey: @"fsn_info_type"];

    if (infoType == FSNInfoExtendedType)
      [updatedInfo setObject: extInfoType forKey: @"ext_info_type"];

    if (ondisk)
      [defaults setObject: updatedInfo forKey: prefsname];
  }
      
  RELEASE (arp);
  
  return (AUTORELEASE (updatedInfo));
}

- (void)reloadContents
{
  [self reloadFromColumnWithNode: baseNode];
}

- (void)reloadFromNode:(FSNode *)anode
{
  [self reloadFromColumnWithNode: anode];
}

- (FSNode *)baseNode
{
  return baseNode;
}

- (FSNode *)shownNode
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  
  if (bc) {
    return [bc shownNode];
  }
  
  return baseNode;
}

- (BOOL)isSingleNode
{
  return NO;
}

- (BOOL)isShowingNode:(FSNode *)anode
{
  return ([self columnWithNode: anode] ? YES : NO);
}

- (BOOL)isShowingPath:(NSString *)path
{
  return ([self columnWithPath: path] ? YES : NO);
}

- (void)sortTypeChangedAtPath:(NSString *)path
{
  if (path) {
    [self reloadColumnWithPath: path];
  } else {
    [self reloadContents];
  }
}

- (void)nodeContentsWillChange:(NSDictionary *)info
{
  NSString *operation = [info objectForKey: @"operation"];
  
  if ([operation isEqual: @"WorkspaceRenameOperation"] == NO) {
    [self checkLockedReps];
  }
}

- (void)nodeContentsDidChange:(NSDictionary *)info
{
  NSString *operation = [info objectForKey: @"operation"];
  NSString *source = [info objectForKey: @"source"];
  NSString *destination = [info objectForKey: @"destination"];
  NSArray *files = [info objectForKey: @"files"];
  
  if ([operation isEqual: @"WorkspaceRenameOperation"]) {
    files = [NSArray arrayWithObject: [destination lastPathComponent]];
    destination = [destination stringByDeletingLastPathComponent]; 
  }

  if ([operation isEqual: NSWorkspaceRecycleOperation]) {
		files = [info objectForKey: @"origfiles"];
  }	

  if ([operation isEqual: NSWorkspaceMoveOperation] 
        || [operation isEqual: NSWorkspaceCopyOperation]
        || [operation isEqual: NSWorkspaceLinkOperation]
        || [operation isEqual: NSWorkspaceDuplicateOperation]
        || [operation isEqual: @"WorkspaceCreateDirOperation"]
        || [operation isEqual: @"WorkspaceCreateFileOperation"]
        || [operation isEqual: NSWorkspaceRecycleOperation]
        || [operation isEqual: @"WorkspaceRenameOperation"]
			  || [operation isEqual: @"WorkspaceRecycleOutOperation"]) { 
    FSNBrowserColumn *bc = [self columnWithPath: destination];        
   
    if (bc) {
      [self reloadFromColumn: bc];     
      
      if ([[self window] isKeyWindow]) {
        BOOL selectCell = NO;
      
        if ([operation isEqual: @"WorkspaceCreateFileOperation"]
                || [operation isEqual: @"WorkspaceCreateDirOperation"]) {  
          selectCell = YES;
          
        } else if ([operation isEqual: @"WorkspaceRenameOperation"]) { 
          NSString *newname = [files objectAtIndex: 0];
          NSString *newpath = [destination stringByAppendingPathComponent: newname];
          
          selectCell = ([bc cellWithPath: newpath] != nil);
        }
        
        if (selectCell) {
          [self selectCellsWithNames: files
                    inColumnWithPath: destination
                          sendAction: YES];
        }
      }
    }
  }

  if ([operation isEqual: NSWorkspaceMoveOperation]
        || [operation isEqual: NSWorkspaceDestroyOperation]
				|| [operation isEqual: NSWorkspaceRecycleOperation]
				|| [operation isEqual: @"WorkspaceRecycleOutOperation"]
				|| [operation isEqual: @"WorkspaceemptyTrashOperation"]) {
    if ([self isShowingPath: source]) {
      [self reloadFromColumnWithPath: source]; 
    }
  }
}

- (void)watchedPathChanged:(NSDictionary *)info
{
  NSString *event = [info objectForKey: @"event"];
  NSString *path = [info objectForKey: @"path"];

  if ([event isEqual: @"GWWatchedPathDeleted"]) {
    NSString *s = [path stringByDeletingLastPathComponent];

    if ([self isShowingPath: s]) {
      [self reloadFromColumnWithPath: s]; 
    }
    
  } else if ([event isEqual: @"GWFileDeletedInWatchedDirectory"]) {    
    if ([self isShowingPath: path]) {
      FSNBrowserColumn *col;
    
      [self reloadFromColumnWithPath: path]; 
       
      col = [self lastLoadedColumn];

      if (col) {
        NSArray *selection = [col selectedNodes];
        
        if (selection == nil) {
          selection = [NSArray arrayWithObject: [col shownNode]];
        }
        
        [viewer selectionChanged: selection];
        [self synchronizeViewer];
      }  
    }      
                 
  } else if ([event isEqual: @"GWFileCreatedInWatchedDirectory"]) {   
    [self addCellsWithNames: [info objectForKey: @"files"]
           inColumnWithPath: path];
  }
}

- (void)setShowType:(FSNInfoType)type
{
  if (infoType != type)
    {
      NSInteger i;
    
      infoType = type;
      DESTROY (extInfoType);
    
      for (i = 0; i < [columns count]; i++)
	{
	  [[columns objectAtIndex: i] setShowType: infoType];
	}
    
      [self tile];
    }
}

- (void)setExtendedShowType:(NSString *)type
{
  if ((extInfoType == nil) || ([extInfoType isEqual: type] == NO))
    {
      NSInteger i;
    
      infoType = FSNInfoExtendedType;
      ASSIGN (extInfoType, type);

      for (i = 0; i < [columns count]; i++)
	{
	  FSNBrowserColumn *col = [columns objectAtIndex: i];
	  [col setExtendedShowType: extInfoType];
	}
    
      [self tile];
    }
}

- (FSNInfoType)showType
{
  return infoType;
}

- (int)iconSize
{
  return DEFAULT_ISIZE;
}

- (int)labelTextSize
{
  return fontSize;
}

- (NSCellImagePosition)iconPosition
{
  return NSImageLeft;
}

- (void)updateIcons
{
}

- (id)repOfSubnode:(FSNode *)anode
{
  if ([[anode path] isEqual: path_separator()] == NO) {
    FSNBrowserColumn *bc = [self columnWithPath: [anode parentPath]];
    
    if (bc) {
      return [bc cellOfNode: anode];
    }
  }
  
  return nil;
}

- (id)repOfSubnodePath:(NSString *)apath
{
  if ([apath isEqual: path_separator()] == NO) {
    NSString *parentPath = [apath stringByDeletingLastPathComponent];
    FSNBrowserColumn *bc = [self columnWithPath: parentPath];
    
    if (bc) {
      return [bc cellWithPath: apath];
    }
  }

  return nil;
}

- (id)addRepForSubnode:(FSNode *)anode
{
  return [self addRepForSubnodePath: [anode path]];
}

- (id)addRepForSubnodePath:(NSString *)apath
{
  if ([apath isEqual: path_separator()] == NO) {
    NSString *bcpath = [apath stringByDeletingLastPathComponent];
    FSNBrowserColumn *bc = [self columnWithPath: bcpath];
  
    if (bc) {
      [bc addCellsWithNames: [NSArray arrayWithObject: [apath lastPathComponent]]];
      return [bc cellWithPath: apath];
    }
  }
  
  return nil;
}

- (void)removeRepOfSubnode:(FSNode *)anode
{
  [self removeRepOfSubnodePath: [anode path]];
}

- (void)removeRepOfSubnodePath:(NSString *)apath
{
  if ([apath isEqual: path_separator()] == NO) {
    NSString *bcpath = [apath stringByDeletingLastPathComponent];
    FSNBrowserColumn *bc = [self columnWithPath: bcpath];
  
    if (bc) {
      [bc removeCellsWithNames: [NSArray arrayWithObject: [apath lastPathComponent]]];
    }
  }
}

- (void)removeRep:(id)arep
{
  [self removeRepOfSubnode: [arep node]];
}

- (void)unloadFromNode:(FSNode *)anode
{
  FSNBrowserColumn *bc = [self columnWithNode: anode];

  if (bc)
    {
      FSNBrowserColumn *col = [self columnBeforeColumn: bc];
      NSInteger index;  
      NSInteger pos;  
      BOOL mustshift;
    
      if (col == nil)
	{
	  col = [columns objectAtIndex: 0];
	}
    
      index = [col index];
      pos = index - firstVisibleColumn + 1; 
      mustshift = (firstVisibleColumn > 0);
        
      updateViewsLock++; 
    
      [[col cmatrix] deselectAllCells];
      [self setLastColumn: index];
      [self reloadFromColumn: col];
    
      if (mustshift && (pos < visibleColumns)){ 
	currentshift = 0;
	[self setShift: visibleColumns - pos];
      }
    
      updateViewsLock--;
      [self tile];
    }  
}

- (void)repSelected:(id)arep
{
}

- (void)unselectOtherReps:(id)arep
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  
  if (bc) {
    [[bc cmatrix] deselectAllCells];
    [self notifySelectionChange: [NSArray arrayWithObject: [bc shownNode]]];
  }
}

- (void)selectReps:(NSArray *)reps
{
  if (reps && [reps count]) {
    FSNode *node = [[reps objectAtIndex: 0] node];
    FSNBrowserColumn *bc = [self columnWithPath: [node parentPath]];
    
    if (bc) {
      [bc selectCells: reps sendAction: NO];
      [[self window] makeFirstResponder: [bc cmatrix]];
    }
  }
}

- (void)selectRepsOfSubnodes:(NSArray *)nodes
{
  if (nodes && [nodes count]) {
    FSNode *node = [nodes objectAtIndex: 0];
    
    if ([node isSubnodeOfNode: baseNode]) {
      FSNBrowserColumn *bc = [self columnWithPath: [node parentPath]];
    
      if (bc) {
        [bc selectCellsOfNodes: nodes sendAction: YES];
      } else {
        [self showSelection: nodes];
      }
      
      bc = [self lastLoadedColumn];
      if (bc) {
        [[self window] makeFirstResponder: [bc cmatrix]];
      }  
    }
  }
}

- (void)selectRepsOfPaths:(NSArray *)paths
{
  if (paths && [paths count]) {
    NSString *basepath = [paths objectAtIndex: 0];
    
    if ([baseNode isParentOfPath: basepath]) {
      FSNBrowserColumn *bc = [self columnWithPath: [basepath stringByDeletingLastPathComponent]];
    
      if (bc) {
        [bc selectCellsWithPaths: paths sendAction: YES];
      } else {
        [self showPathsSelection: paths];
      }
      
      bc = [self lastLoadedColumn];
      if (bc) {
        [[self window] makeFirstResponder: [bc cmatrix]];
      }  
    }
  }
}

- (void)selectAll
{
  [self selectAllInLastColumn];
}

- (NSArray *)reps
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  
  if (bc) {
    return [[bc cmatrix] cells];
  }

  return nil;
}

- (NSArray *)selectedReps
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  NSArray *selection = nil;

  if (bc) {
    selection = [bc selectedCells];  
  
    if ((selection == nil) && [bc shownNode]) {
      FSNBrowserColumn *col = [self columnBeforeColumn: bc];
    
      if (col) {
        return [col selectedCells];  
      }
    }
  }
  
  return selection;
}

- (NSArray *)selectedNodes
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  NSArray *selection = nil;
  
  if (bc) {
    selection = [bc selectedNodes];
  
    if ((selection == nil) && [bc shownNode]) {
      selection = [NSArray arrayWithObject: [bc shownNode]];
    }
    
  } else {
    selection = [NSArray arrayWithObject: baseNode];
  }

  return selection;
}

- (NSArray *)selectedPaths
{
  FSNBrowserColumn *bc = [self lastLoadedColumn];
  NSArray *selection = nil;
  
  if (bc) {
    selection = [bc selectedPaths];
  
    if ((selection == nil) && [bc shownNode]) {
      selection = [NSArray arrayWithObject: [[bc shownNode] path]];
    } 
    
  } else {
    selection = [NSArray arrayWithObject: [baseNode path]];
  }

  return selection;
}

- (void)selectionDidChange
{
}

- (void)checkLockedReps
{
  NSInteger i;
  
  for (i = 0; i < [columns count]; i++) {
    [[columns objectAtIndex: i] checkLockedReps];
  }
}

- (void)setSelectionMask:(FSNSelectionMask)mask
{
}

- (FSNSelectionMask)selectionMask
{
  return NSSingleSelectionMask;
}

- (void)openSelectionInNewViewer:(BOOL)newv
{
  [desktopApp openSelectionInNewViewer: newv];
}

- (void)restoreLastSelection
{
  if (lastSelection) {
    [self selectRepsOfSubnodes: lastSelection];
  }
}

- (void)setLastShownNode:(FSNode *)anode
{
  FSNBrowserColumn *bc = [self columnWithNode: anode];

  if (bc) {
    FSNBrowserColumn *prev = [self columnBeforeColumn: bc];

    updateViewsLock++;
        
    if (prev) {
      if ([prev selectCellOfNode: anode sendAction: YES] == nil) {
        [self setLastColumn: [prev index]];
        [self notifySelectionChange: [NSArray arrayWithObject: [prev shownNode]]];
      }
    } else {
      [self setLastColumn: 0];
      [bc unselectAllCells];
      [self notifySelectionChange: [NSArray arrayWithObject: baseNode]];
    }
    
    updateViewsLock--;
    [self tile];    

    bc = [self lastLoadedColumn];
    if (bc) {
      [[self window] makeFirstResponder: [bc cmatrix]];
    }
  }
}

- (BOOL)needsDndProxy
{
  return NO;
}

- (BOOL)involvedByFileOperation:(NSDictionary *)opinfo
{
  NSInteger i;
  
  for (i = 0; i < [columns count]; i++) {
    FSNode *node = [[columns objectAtIndex: i] shownNode];
    
    if (node && [node involvedByFileOperation: opinfo]) {
      return YES;
    }
  }

  return NO;
}

- (BOOL)validatePasteOfFilenames:(NSArray *)names
                       wasCut:(BOOL)cut
{
  FSNode *node = [self nodeOfLastColumn];
  NSString *nodePath = [node path];
  NSString *prePath = [NSString stringWithString: nodePath];
  NSString *basePath;
  
	if ([names count] == 0) {
		return NO;
  } 

  if ([node isWritable] == NO) {
    return NO;
  }
    
  basePath = [[names objectAtIndex: 0] stringByDeletingLastPathComponent];
  if ([basePath isEqual: nodePath]) {
    return NO;
  }  
    
  if ([names containsObject: nodePath]) {
    return NO;
  }

  while (1) {
    if ([names containsObject: prePath]) {
      return NO;
    }
    if ([prePath isEqual: path_separator()]) {
      break;
    }            
    prePath = [prePath stringByDeletingLastPathComponent];
  }

  return YES;
}

- (NSColor *)backgroundColor
{
  return backColor;
}

- (NSColor *)textColor
{
  return [NSColor controlTextColor];
}

- (NSColor *)disabledTextColor
{
  return [NSColor disabledControlTextColor];
}

@end


@implementation FSNBrowser (IconNameEditing)

- (void)setEditorForCell:(FSNBrowserCell *)cell 
                inColumn:(FSNBrowserColumn *)col
{
  if (nameEditor) {
    FSNode *cellnode = [cell node];
    BOOL canedit = (([cell isLocked] == NO) 
                        && ([cellnode isMountPoint] == NO));
    
    [self stopCellEditing];

    if (canedit) {   
      NSMatrix *matrix = [col cmatrix];
      NSFont *edfont = [nameEditor font];
      float fnheight = [fsnodeRep heightOfFont: edfont];
      NSRect r = [cell labelRect];
      
      r = [matrix convertRect: r toView: self];
      r.origin.y += ((r.size.height - fnheight) / 2);
      r.size.height = fnheight;
      r = NSIntegralRect(r);  
      
      [nameEditor setFrame: r];

      [nameEditor setNode: cellnode 
              stringValue: [cellnode name]
                    index: 0];

      [nameEditor setEditable: YES];
      [nameEditor setSelectable: YES];	
      [self addSubview: nameEditor];
    }
  }
}

- (void)stopCellEditing
{
  if (nameEditor && [[self subviews] containsObject: nameEditor]) {
    [nameEditor abortEditing];
    [nameEditor setEditable: NO];
    [nameEditor setSelectable: NO];
    [nameEditor setNode: nil stringValue: @"" index: -1];
    [nameEditor removeFromSuperview];
    [self setNeedsDisplayInRect: [nameEditor frame]];
  }
}

- (void)stopRepNameEditing
{
  [self stopCellEditing];
}

- (void)controlTextDidChange:(NSNotification *)aNotification
{
}

- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
  FSNode *ednode = [nameEditor node];

#define CLEAREDITING \
  [self stopCellEditing]; \
  return 
    

    if ([ednode isParentWritable] == NO)
      {
	showAlertNoPermission([FSNode class], [ednode parentName]);
	CLEAREDITING;
      }
    else if ([ednode isSubnodeOfPath: [desktopApp trashPath]])
      {
	showAlertInRecycler([FSNode class]);
	CLEAREDITING;
      }
    else
      {
      NSString *userName = [nameEditor stringValue];
      NSString *origName = [ednode name];
      GSFilenameExtensionDisplayMode mode = GSCurrentExtensionDisplayMode();
      NSString *dispName = GSDisplayNameForFilename(origName, mode);
      NSString *hiddenExt = GSFilenameHiddenExtension(origName, mode);
      NSString *newname = userName;
      NSString *newpath;
      NSString *extension;
      NSCharacterSet *notAllowSet = [NSCharacterSet characterSetWithCharactersInString: @"/\\*:?\33"];
      NSRange range;
      NSArray *dirContents;
      NSMutableDictionary *opinfo;

      if ([userName isEqualToString: dispName])
	{
	  CLEAREDITING;
	}

      if ([hiddenExt length] > 0 && [userName rangeOfString: @"."].location == NSNotFound)
	{
	  newname = [userName stringByAppendingString: hiddenExt];
	}

      if ([newname isEqualToString: origName])
	{
	  CLEAREDITING;
	}

      newpath = [[ednode parentPath] stringByAppendingPathComponent: newname];
      extension = [newpath pathExtension];
      range = [newname rangeOfCharacterFromSet: notAllowSet];
      dirContents = [ednode subNodeNamesOfParent];
      opinfo = [NSMutableDictionary dictionary];

      if (([newname length] == 0) || (range.length > 0))
	{
	  showAlertInvalidName([FSNode class]);
	  CLEAREDITING;
	}

      if (([extension length]
              && ([ednode isDirectory] && ([ednode isPackage] == NO))))
	{
          if (showAlertExtensionChange([FSNode class], extension) == NSAlertDefaultReturn)
            {
              CLEAREDITING;
            }
	}

      if ([dirContents containsObject: newname])
	{
	  showAlertNameInUse([FSNode class], newname);
	  CLEAREDITING;
	}

      [opinfo setObject: @"WorkspaceRenameOperation" forKey: @"operation"];
      [opinfo setObject: [ednode path] forKey: @"source"];
      [opinfo setObject: newpath forKey: @"destination"];
      [opinfo setObject: [NSArray arrayWithObject: @""] forKey: @"files"];

      [self stopCellEditing];
      [desktopApp performFileOperation: opinfo];
    }
}

@end
