libs-gui/Source/NSPathControl.m
2023-11-20 22:43:49 +01:00

575 lines
14 KiB
Objective-C

/* 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 <Foundation/NSNotification.h>
#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<NSPathControlDelegate>) delegate
{
return _delegate;
}
- (void) setDelegate: (id<NSPathControlDelegate>) 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<NSDraggingInfo>)sender
{
if (_delegate != nil)
{
NSDragOperation dop = [_delegate pathControl: self
validateDrop: sender];
return dop;
}
return NSDragOperationCopy;
}
- (BOOL) performDragOperation: (id<NSDraggingInfo>)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