/* Implementation of class NSPathControl Copyright (C) 2020 Free Software Foundation, Inc. By: Gregory John Casamento Date: Wed Apr 22 18:19:40 EDT 2020 This file is part of the GNUstep Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 USA. */ #import #import "AppKit/NSPathControl.h" #import "AppKit/NSPathCell.h" #import "AppKit/NSGraphics.h" #import "AppKit/NSDragging.h" #import "AppKit/NSPasteboard.h" #import "AppKit/NSMenu.h" #import "AppKit/NSOpenPanel.h" #import "AppKit/NSPathComponentCell.h" #import "AppKit/NSPathControlItem.h" #import "AppKit/NSEvent.h" #import "AppKit/NSTrackingArea.h" static NSNotificationCenter *nc = nil; static Class pathCellClass; @interface NSPathCell (PathControlPrivate) - (void) _setClickedPathComponentCell: (NSPathComponentCell *)c; @end @interface NSPathComponentCell (PathControlPrivate) - (NSPathControlItem *) _pathControlItem; @end @implementation NSPathControl + (void) initialize { if (self == [NSPathControl class]) { [self setVersion: 1.0]; [self setCellClass: [NSPathCell class]]; nc = [NSNotificationCenter defaultCenter]; } } + (Class) cellClass { return pathCellClass; } + (void) setCellClass: (Class)classId { pathCellClass = classId; } - (void) resetCursorRects { [[self superview] removeTrackingRect: _trackingTag]; _trackingTag = [[self superview] addTrackingRect: [self frame] owner: self userData: nil assumeInside: YES]; } - (instancetype) init { self = [super init]; if (self != nil) { [self setPathStyle: NSPathStyleStandard]; [self setURL: nil]; [self setDelegate: nil]; [self setAllowedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]]; } return self; } - (void) dealloc { [[self superview] removeTrackingRect: _trackingTag]; [super dealloc]; } - (void) mouseEntered: (NSEvent *)event { [_cell mouseEntered: event withFrame: [self frame] inView: self]; } - (void) mouseExited: (NSEvent *)event { [_cell mouseExited: event withFrame: [self frame] inView: self]; } - (void) setPathStyle: (NSPathStyle)style { [_cell setPathStyle: style]; [self setNeedsDisplay]; } - (NSPathStyle) pathStyle { return [_cell pathStyle]; } - (NSPathComponentCell *) clickedPathComponentCell { return [_cell clickedPathComponentCell]; } - (NSArray *) pathComponentCells { return [_cell pathComponentCells]; } - (void) setPathComponentCells: (NSArray *)cells { [_cell setPathComponentCells: cells]; [self setNeedsDisplay]; } - (SEL) doubleAction; { return [_cell doubleAction]; } - (void) setDoubleAction: (SEL)doubleAction { [_cell setDoubleAction: doubleAction]; } - (void) _createPathItems { NSArray *a = [_cell pathComponentCells]; NSEnumerator *en = [a objectEnumerator]; NSPathComponentCell *c = nil; NSMutableArray *items = [NSMutableArray arrayWithCapacity: [a count]]; while ((c = [en nextObject]) != nil) { NSPathControlItem *pi = [c _pathControlItem]; [items addObject: pi]; } [self setPathItems: [items copy]]; } - (NSURL *) URL { return [_cell URL]; } - (void) setURL: (NSURL *)url { [_cell setURL: url]; [self _createPathItems]; [self setNeedsDisplay]; } - (id) delegate { return _delegate; } - (void) setDelegate: (id) delegate { _delegate = delegate; } - (NSDragOperation) draggingSourceOperationMaskForLocal: (BOOL)flag { if (flag) { return _localMask; } return _remoteMask; } - (void) setDraggingSourceOperationMask: (NSDragOperation)mask forLocal: (BOOL)local { if (local) { _localMask = mask; } else { _remoteMask = mask; } } - (NSArray *) allowedTypes; { return [_cell allowedTypes]; } - (void) setAllowedTypes: (NSArray *)allowedTypes { [_cell setAllowedTypes: allowedTypes]; [self registerForDraggedTypes: allowedTypes]; } - (NSPathControlItem *) clickedPathItem { return [[self clickedPathComponentCell] _pathControlItem]; } - (NSArray *) pathItems { return _pathItems; } - (void) setPathItems: (NSArray *)items { NSEnumerator *en = [items objectEnumerator]; NSMutableArray *array = [NSMutableArray arrayWithCapacity: [items count]]; NSPathControlItem *item = nil; while ((item = [en nextObject]) != nil) { NSPathComponentCell *cell = [[NSPathComponentCell alloc] init]; [cell setImage: [item image]]; [cell setURL: [item URL]]; [array addObject: cell]; } [self setPathComponentCells: array]; ASSIGNCOPY(_pathItems, items); [self setNeedsDisplay]; } - (NSAttributedString *) placeholderAttributedString { return [_cell placeholderAttributedString]; } - (void) setPlaceholderAttributedString: (NSAttributedString *)string { [_cell setPlaceholderAttributedString: string]; [self setNeedsDisplay]; } - (NSString *) placeholderString { return [_cell placeholderString]; } - (void) setPlaceholderString: (NSString *)string { [_cell setPlaceholderString: string]; [self setNeedsDisplay]; } - (NSColor *) backgroundColor { return [_cell backgroundColor]; } - (void) setBackgroundColor: (NSColor *)color { [_cell setBackgroundColor: color]; [self setNeedsDisplay]; } - (void) _doMenuAction: (id)sender { NSPathComponentCell *cc = (NSPathComponentCell *)[sender representedObject]; [_cell _setClickedPathComponentCell: cc]; if ([[self cell] action]) { [self sendAction: [[self cell] action] to: [[self cell] target]]; } [[sender menu] close]; } - (void) _doChooseMenuAction: (id)sender { NSOpenPanel *op = [NSOpenPanel openPanel]; int result = 0; NSFileManager *fm = [NSFileManager defaultManager]; BOOL isDir = NO; NSString *path = nil; NSString *file = nil; [fm fileExistsAtPath: [[self URL] path] isDirectory: &isDir]; if (isDir) { path = [[self URL] path]; } else { path = [[[self URL] path] stringByDeletingLastPathComponent]; file = [[[self URL] path] lastPathComponent]; } [op setAllowsMultipleSelection: NO]; [op setCanChooseFiles: YES]; [op setCanChooseDirectories: YES]; if ([(id)_delegate respondsToSelector: @selector(pathCell:willPopUpMenu:)]) { [_delegate pathControl: self willDisplayOpenPanel: op]; } if ([(id)[_cell delegate] respondsToSelector: @selector(pathCell:willPopUpMenu:)]) { [[_cell delegate] pathCell: _cell willDisplayOpenPanel: op]; } result = [op runModalForDirectory: path file: file types: nil]; if (result == NSOKButton) { NSArray *urls = [op URLs]; NSURL *url = [urls objectAtIndex: 0]; [self setURL: url]; } [[sender menu] close]; } - (void) mouseDown: (NSEvent *)event { if (![self isEnabled]) { [super mouseDown: event]; return; } if ([self pathStyle] == NSPathStylePopUp) { NSPathCell *acell = (NSPathCell *)[self cell]; NSArray *array = [acell pathComponentCells]; NSMenu *menu = AUTORELEASE([[NSMenu alloc] initWithTitle: nil]); NSPathComponentCell *c = nil; NSEnumerator *en = [array objectEnumerator]; while ((c = [en nextObject]) != nil) { NSURL *u = [c URL]; NSString *s = [[u path] lastPathComponent]; NSMenuItem *i = [[NSMenuItem alloc] init]; [i setTitle: s]; [i setTarget: self]; [i setAction: @selector(_doMenuAction:)]; [i setRepresentedObject: c]; [menu insertItem: i atIndex: 0]; RELEASE(i); } // Add separator [menu insertItem: [NSMenuItem separatorItem] atIndex: 0]; // Add choose menu option NSMenuItem *i = [[NSMenuItem alloc] init]; [i setTitle: _(@"Choose...")]; [i setTarget: self]; [i setAction: @selector(_doChooseMenuAction:)]; [menu insertItem: i atIndex: 0]; RELEASE(i); [self setMenu: menu]; if (_delegate) { if ([(id)_delegate respondsToSelector: @selector(pathControl:willPopUpMenu:)]) { [_delegate pathControl: self willPopUpMenu: menu]; } } if ([_cell delegate]) { if ([(id)[_cell delegate] respondsToSelector: @selector(pathCell:willPopUpMenu:)]) { [[_cell delegate] pathCell: _cell willPopUpMenu: menu]; } } [menu popUpMenuPositionItem: [menu itemAtIndex: 0] atLocation: NSMakePoint(0.0, 0.0) inView: self]; } else { NSPathComponentCell *pcc = [_cell pathComponentCellAtPoint: [event locationInWindow] withFrame: [self frame] inView: self]; [_cell _setClickedPathComponentCell: pcc]; if ([[self cell] action]) { [self sendAction: [[self cell] action] to: [[self cell] target]]; } } } - (NSDragOperation) draggingEntered: (id)sender { if (_delegate != nil) { NSDragOperation dop = [_delegate pathControl: self validateDrop: sender]; return dop; } return NSDragOperationCopy; } - (BOOL) performDragOperation: (id)sender { NSPasteboard *pb = [sender draggingPasteboard]; if ([[pb types] containsObject: NSFilenamesPboardType]) { NSArray *files = [pb propertyListForType: NSFilenamesPboardType]; if ([files count] > 0) { NSString *file = [files objectAtIndex: 0]; NSURL *u = [NSURL URLWithString: file]; BOOL accept = NO; if ([self delegate]) { accept = [_delegate pathControl: self acceptDrop: sender]; } else { accept = YES; } if (accept) { [self setURL: u]; } } } return YES; } - (instancetype) initWithCoder: (NSKeyedUnarchiver *)coder { self = [super initWithCoder: coder]; if (self != nil) { if ([coder allowsKeyedCoding]) { if ([coder containsValueForKey: @"NSBackgroundColor"]) { [self setBackgroundColor: [coder decodeObjectForKey: @"NSBackgroundColor"]]; } else { [self setBackgroundColor: [NSColor windowBackgroundColor]]; } if ([coder containsValueForKey: @"NSDragTypes"]) { [self setAllowedTypes: [coder decodeObjectForKey: @"NSDragTypes"]]; } else { [self setAllowedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]]; } if ([coder containsValueForKey: @"NSControlAction"]) { NSString *s = [coder decodeObjectForKey: @"NSControlAction"]; [self setAction: NSSelectorFromString(s)]; } if ([coder containsValueForKey: @"NSControlTarget"]) { id t = [coder decodeObjectForKey: @"NSControlTarget"]; [self setTarget: t]; } } else { [self setBackgroundColor: [coder decodeObject]]; [self setAllowedTypes: [coder decodeObject]]; [self setAction: NSSelectorFromString([coder decodeObject])]; [self setTarget: [coder decodeObject]]; } } return self; } - (void) encodeWithCoder: (NSCoder *)coder { [super encodeWithCoder: coder]; if ([coder allowsKeyedCoding]) { [coder encodeObject: [self backgroundColor] forKey: @"NSBackgroundColor"]; [coder encodeObject: [self allowedTypes] forKey: @"NSDragTypes"]; [coder encodeObject: NSStringFromSelector([self action]) forKey: @"NSControlAction"]; [coder encodeObject: [self target] forKey: @"NSControlTarget"]; } else { [coder encodeObject: [self backgroundColor]]; [coder encodeObject: [self allowedTypes]]; [coder encodeObject: NSStringFromSelector([self action])]; [coder encodeObject: [self target]]; } } @end @implementation NSPathCell (PathControlPrivate) - (void) _setClickedPathComponentCell: (NSPathComponentCell *)c { _clickedPathComponentCell = c; } @end @implementation NSPathComponentCell (PathControlPrivate) - (NSPathControlItem *) _pathControlItem { NSPathControlItem *pi = [[NSPathControlItem alloc] init]; NSURL *u = [self URL]; NSString *path = [u path]; [pi setImage: [self image]]; [pi setURL: u]; [pi setTitle: [path lastPathComponent]]; AUTORELEASE(pi); return pi; } @end