apps-gorm/GormCore/GormPalettesManager.m
Gregory John Casamento c51b7b4e4b * English.lproj/GormPalettePanel.gorm: Moved palette window to
gorm file.
	* GNUmakefile: Added new gorm here.
	* GormCore/GormDocument.m: Made toolbar items smaller
	* GormCore/GormPalettesManager.[hm]: Added toolbar implementation
	to this class to allow the user to switch palettes using the toolbar.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/apps/gorm/trunk@26477 72102866-910b-0410-8b05-ffd578937521
2008-04-27 23:34:38 +00:00

864 lines
23 KiB
Objective-C

/* GormPalettesManager.m
*
* Copyright (C) 1999 Free Software Foundation, Inc.
*
* Author: Richard Frith-Macdonald <richard@brainstrom.co.uk>
* Author: Gregory John Casamento <greg_casamento@yahoo.com>
* Date: 1999, 2004
*
* This file is part of GNUstep.
*
* 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
*/
#include "GormPrivate.h"
#include <Foundation/NSArray.h>
#include <Foundation/NSSet.h>
#include <AppKit/NSImage.h>
#include <AppKit/NSSound.h>
#include <GNUstepBase/GSObjCRuntime.h>
#include "GormFunctions.h"
#define BUILTIN_PALETTES @"BuiltinPalettes"
#define USER_PALETTES @"UserPalettes"
@interface GormPalettePanel : NSPanel
@end
@implementation GormPalettePanel
- (BOOL) canBecomeKeyWindow
{
return NO;
}
- (BOOL) canBecomeMainWindow
{
return NO;
}
@end
@interface GormPaletteView : NSView
{
NSPasteboard *dragPb;
}
- (void) draggedImage: (NSImage*)i endedAt: (NSPoint)p deposited: (BOOL)f;
- (unsigned int) draggingSourceOperationMaskForLocal: (BOOL)flag;
@end
@implementation GormPaletteView
static NSImage *dragImage = nil;
+ (void) initialize
{
if (self == [GormPaletteView class])
{
// nothing to do...
}
}
- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent
{
return YES; /* Ensure we get initial mouse down event. */
}
/*
* Initialisation - register to receive DnD with our own types.
*/
- (id) initWithFrame: (NSRect)aFrame
{
self = [super initWithFrame: aFrame];
if (self != nil)
{
[self registerForDraggedTypes: [NSArray arrayWithObjects:
IBCellPboardType, IBMenuPboardType, IBMenuCellPboardType,
IBObjectPboardType, IBViewPboardType, IBWindowPboardType,
IBFormatterPboardType,nil]];
}
return self;
}
- (void) dealloc
{
DESTROY(dragPb);
[super dealloc];
}
/*
* Dragging source protocol implementation
*/
- (void) draggedImage: (NSImage*)i endedAt: (NSPoint)p deposited: (BOOL)f
{
NSString *type = [[dragPb types] lastObject];
/*
* Windows and Menus are an exception to the normal DnD mechanism -
* we create them if they are dropped anywhere except back in the \
* pallettes view ie. if they are dragged, but the drop fails.
*/
if (f == NO && ([type isEqual: IBWindowPboardType] == YES ||
[type isEqual: IBMenuPboardType] == YES))
{
id<IBDocuments> active = [(id<IB>)NSApp activeDocument];
if (active != nil)
{
if([active objectForName: @"NSMenu"] != nil &&
[type isEqual: IBMenuPboardType] == YES)
return;
[active pasteType: type fromPasteboard: dragPb parent: nil];
}
}
}
- (unsigned int) draggingSourceOperationMaskForLocal: (BOOL)flag
{
return NSDragOperationCopy;
}
/*
* Dragging destination protocol implementation
*
* We actually don't handle anything being dropped on the palette,
* but we pretend to accept drops from ourself, so that the drag
* session quietly terminates - and it looks like the drop has
* been successful - this stops windows being created when they are
* dropped back on the palette (a window is normally created if the
* dnd drop is refused).
*/
- (unsigned) draggingEntered: (id<NSDraggingInfo>)sender
{
return NSDragOperationCopy;;
}
- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
{
return YES;
}
- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
{
return YES;
}
/*
* Intercepting events in the view and handling them
*/
- (NSView*) hitTest: (NSPoint)loc
{
/*
* Stop the subviews receiving events - we grab them all.
*/
if ([super hitTest: loc] != nil)
return self;
return nil;
}
- (void) mouseDown: (NSEvent*)theEvent
{
NSPoint dragPoint = [theEvent locationInWindow];
NSWindow *w = [self window];
NSView *view;
GormDocument *active = (GormDocument *)[(id<IB>)NSApp activeDocument];
NSRect rect;
NSString *type;
id obj;
NSPasteboard *pb;
NSImageRep *rep;
NSMenu *menu;
if ([self superview] != nil)
{
dragPoint = [[self superview] convertPoint: dragPoint fromView: nil];
}
view = [super hitTest: dragPoint];
if (view == self || view == nil)
{
return; // No subview to drag.
}
/* Make sure we're dragging the proper control and not a subview of a
control (like the contentView of an NSBox) */
while (view != nil && [view superview] != self)
view = [view superview];
// this will always get the correct coordinates...
rect = [[view superview] convertRect: [view frame] toView: nil];
if (active == nil)
{
NSRunAlertPanel (nil, _(@"No document is currently active"),
_(@"OK"), nil, nil);
return;
}
RELEASE(dragImage);
dragImage = [[NSImage alloc] init];
[dragImage setSize: rect.size];
rep = [[NSCachedImageRep alloc] initWithSize: rect.size
depth: [w depthLimit]
separate: YES
alpha: [w alphaValue]>0.0 ? YES : NO];
[dragImage addRepresentation: rep];
RELEASE(rep);
/* Copy the contents of the clicked view from our window into the
* cached image representation.
* NB. We use lockFocusOnRepresentation: for this because it sets
* up cached image representation information in the image, and if
* that's not done before our copy, the image will overwrite our
* copied data when asked to draw the representation.
*/
[dragImage lockFocusOnRepresentation: rep];
NSCopyBits([w gState], rect, NSZeroPoint);
[dragImage unlockFocus];
type = [IBPalette typeForView: view];
obj = [IBPalette objectForView: view];
pb = [NSPasteboard pasteboardWithName: NSDragPboard];
ASSIGN(dragPb, pb);
[active copyObject: obj type: type toPasteboard: pb];
NSDebugLog(@"type: %@, obj: %@,", type, obj);
menu = [active objectForName: @"NSMenu"];
[self dragImage: dragImage
at: [view frame].origin
offset: NSMakeSize(0,0)
event: theEvent
pasteboard: pb
source: self
slideBack: ([type isEqual: IBWindowPboardType] ||
([type isEqual: IBMenuPboardType] && menu == nil)) ? NO : YES];
// Temporary fix for the art backend. This is harmless, and
// shouldn't effect users of xlib, but it's necessary for now
// so that users can work.
[self setNeedsDisplay: YES];
}
@end
@implementation GormPalettesManager
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver: self];
RELEASE(panel);
RELEASE(bundles);
RELEASE(palettes);
RELEASE(palettesDict);
RELEASE(importedClasses);
RELEASE(importedImages);
RELEASE(importedSounds);
RELEASE(substituteClasses);
[super dealloc];
}
- (void) handleNotification: (NSNotification*)aNotification
{
NSString *name = [aNotification name];
if ([name isEqual: IBWillBeginTestingInterfaceNotification] == YES)
{
if ([panel isVisible] == YES)
{
hiddenDuringTest = YES;
[panel orderOut: self];
}
}
else if ([name isEqual: IBWillEndTestingInterfaceNotification] == YES)
{
if (hiddenDuringTest == YES)
{
hiddenDuringTest = NO;
[panel orderFront: self];
}
}
}
- (id) init
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
NSArray *array;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *userPalettes = [defaults arrayForKey: USER_PALETTES];
self = [super init];
if(self != nil)
{
if([NSBundle loadNibNamed: @"GormPalettePanel" owner: self] == NO)
{
return nil;
}
}
else
{
return nil;
}
//
// Initialize dictionary
//
palettesDict = [[NSMutableDictionary alloc] init];
palettes = [[NSMutableArray alloc] init];
paletteNames = [[NSMutableArray alloc] init];
substituteClasses = [[NSMutableDictionary alloc] init];
//
// Set frame name
//
[panel setFrameUsingName: @"Palettes"];
[panel setFrameAutosaveName: @"Palettes"];
current = -1;
//
// Set up the toolbar.
//
toolbar = [(NSToolbar *)[NSToolbar alloc] initWithIdentifier: @"GormPalettesToolbar"];
[toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
[toolbar setSizeMode: NSToolbarSizeModeSmall];
[toolbar setAllowsUserCustomization: NO];
[toolbar setDelegate: self];
[panel setToolbar: toolbar];
RELEASE(toolbar);
array = [[NSBundle mainBundle] pathsForResourcesOfType: @"palette"
inDirectory: nil];
if ([array count] > 0)
{
unsigned index;
array = [array sortedArrayUsingSelector: @selector(compare:)];
for (index = 0; index < [array count]; index++)
{
[self loadPalette: [array objectAtIndex: index]];
}
}
// if we have any user palettes load them as well.
if(userPalettes != nil)
{
NSEnumerator *en = [userPalettes objectEnumerator];
id paletteName = nil;
while((paletteName = [en nextObject]) != nil)
{
[self loadPalette: paletteName];
}
}
/*
* Select initial palette - this should be the standard controls palette.
*/
[self setCurrentPaletteWithTag: 2];
[nc addObserver: self
selector: @selector(handleNotification:)
name: IBWillBeginTestingInterfaceNotification
object: nil];
[nc addObserver: self
selector: @selector(handleNotification:)
name: IBWillEndTestingInterfaceNotification
object: nil];
return self;
}
- (BOOL) bundlePathIsLoaded: (NSString *)path
{
int col = 0;
NSBundle *bundle;
for (col = 0; col < [bundles count]; col++)
{
bundle = [bundles objectAtIndex: col];
if ([path isEqualToString: [bundle bundlePath]] == YES)
{
return YES;
}
}
return NO;
}
- (BOOL) loadPalette: (NSString*)path
{
NSBundle *bundle;
NSWindow *window;
Class paletteClass;
NSDictionary *paletteInfo;
NSString *className;
NSArray *exportClasses;
NSArray *exportSounds;
NSArray *exportImages;
NSDictionary *subClasses;
IBPalette *palette;
if([self bundlePathIsLoaded: path])
{
NSRunAlertPanel (nil, _(@"Palette has already been loaded"),
_(@"OK"), nil, nil);
return NO;
}
bundle = [NSBundle bundleWithPath: path];
if (bundle == nil)
{
NSRunAlertPanel(nil, _(@"Could not load Palette"),
_(@"OK"), nil, nil);
return NO;
}
path = [bundle pathForResource: @"palette" ofType: @"table"];
if (path == nil)
{
NSRunAlertPanel(nil, _(@"File 'palette.table' missing"),
_(@"OK"), nil, nil);
return NO;
}
// attempt to load the palette table in either the strings or plist format.
NS_DURING
{
paletteInfo = [[NSString stringWithContentsOfFile: path] propertyList];
if (paletteInfo == nil)
{
paletteInfo = [[NSString stringWithContentsOfFile: path] propertyListFromStringsFileFormat];
if(paletteInfo == nil)
{
NSRunAlertPanel(_(@"Problem Loading Palette"),
_(@"Failed to load 'palette.table' using strings or property list format."),
_(@"OK"),
nil,
nil);
return NO;
}
}
}
NS_HANDLER
{
NSString *message = [NSString stringWithFormat:
_(@"Encountered exception %@ attempting to load 'palette.table'."),
[localException reason]];
NSRunAlertPanel(_(@"Problem Loading Palette"),
message,
_(@"OK"),
nil,
nil);
return NO;
}
NS_ENDHANDLER
className = [paletteInfo objectForKey: @"Class"];
if (className == nil)
{
NSRunAlertPanel(nil, _(@"No palette class in 'palette.table'"),
_(@"OK"), nil, nil);
return NO;
}
paletteClass = [bundle classNamed: className];
if (paletteClass == 0)
{
NSRunAlertPanel (nil, _(@"Could not load palette class"),
_(@"OK"), nil, nil);
return NO;
}
palette = [[paletteClass alloc] init];
if ([palette isKindOfClass: [IBPalette class]] == NO)
{
NSRunAlertPanel (nil, _(@"Palette contains wrong type of class"),
_(@"OK"), nil, nil);
RELEASE(palette);
return NO;
}
// add to the bundles list...
[bundles addObject: bundle];
exportClasses = [paletteInfo objectForKey: @"ExportClasses"];
if(exportClasses != nil)
{
[self importClasses: exportClasses withDictionary: nil];
}
exportImages = [paletteInfo objectForKey: @"ExportImages"];
if(exportImages != nil)
{
[self importImages: exportImages withBundle: bundle];
}
exportSounds = [paletteInfo objectForKey: @"ExportSounds"];
if(exportSounds != nil)
{
[self importSounds: exportSounds withBundle: bundle];
}
subClasses = [paletteInfo objectForKey: @"SubstituteClasses"];
if(subClasses != nil)
{
[substituteClasses addEntriesFromDictionary: subClasses];
}
[palette finishInstantiate];
window = [palette originalWindow];
[window setExcludedFromWindowsMenu: YES];
// Resize the window appropriately so that we don't have issues
// with scrolling.
if([window styleMask] & NSBorderlessWindowMask)
{
[window setFrame: NSMakeRect(0,0,272,160) display: NO];
}
else
{
[window setFrame: NSMakeRect(0,0,272,192) display: NO];
}
// manage palette data.
[palettesDict setObject: palette forKey: className];
[palettes addObject: palette];
[paletteNames addObject: className];
[selectionView addColumn];
[toolbar insertItemWithItemIdentifier: className atIndex: ([palettes count] - 1)];
[self setCurrentPalette: 0];
RELEASE(palette);
return YES;
}
- (id) openPalette: (id) sender
{
NSArray *fileTypes = [NSArray arrayWithObject: @"palette"];
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
int result;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *userPalettes = [defaults arrayForKey: USER_PALETTES];
NSMutableArray *newUserPalettes =
(userPalettes == nil)?[NSMutableArray array]:[NSMutableArray arrayWithArray: userPalettes];
[oPanel setAllowsMultipleSelection: YES];
[oPanel setCanChooseFiles: YES];
[oPanel setCanChooseDirectories: NO];
result = [oPanel runModalForDirectory: NSHomeDirectory()
file: nil
types: fileTypes];
if (result == NSOKButton)
{
NSArray *filesToOpen = [oPanel filenames];
unsigned count = [filesToOpen count];
unsigned i;
for (i = 0; i < count; i++)
{
NSString *aFile = [filesToOpen objectAtIndex: i];
if([self bundlePathIsLoaded: aFile] == YES &&
[userPalettes containsObject: aFile] == NO)
{
// This is done here so that, if we try to reload a palette
// that has previously been deleted during this session that
// the palette manager won't fail, but it will simply add
// the palette back in. If this returns NO, then we should
// flag a problem otherwise it's successful if the palette is
// already in the bundles array. This is to address bug#15989.
[newUserPalettes addObject: aFile];
}
else if([self loadPalette: aFile] == NO)
{
return nil;
}
else
{
[newUserPalettes addObject: aFile];
}
}
// reset the defaults to include the new palette.
[defaults setObject: newUserPalettes forKey: USER_PALETTES];
return self;
}
return nil;
}
- (NSPanel*) panel
{
return panel;
}
- (void) setCurrentPalette: (id)anObject
{
[self setCurrentPaletteWithTag: [anObject tag]];
}
- (void) setCurrentPaletteWithTag: (int)tag
{
NSView *wv;
NSView *sv;
NSEnumerator *enumerator;
if (current >= 0)
{
/*
* Move the views in the drag view back to the content view of the
* window they originally came from.
*/
wv = [[[palettes objectAtIndex: current] originalWindow] contentView];
enumerator = [[dragView subviews] objectEnumerator];
while ((sv = [enumerator nextObject]) != nil)
{
RETAIN(sv);
[sv removeFromSuperview];
[wv addSubview: sv];
RELEASE(sv);
}
}
current = tag;
if (current >= 0 && current < [palettes count])
{
id palette = [palettes objectAtIndex: current];
/*
* Set the window title to reflect the palette selection.
*/
[panel setTitle: [NSString stringWithFormat: @"Palettes (%@)",
[palette className]]];
/*
* Move the views from their original window into our drag view.
* Resize our drag view to the right size fitrst.
*/
wv = [[palette originalWindow] contentView];
if(wv)
{
[dragView setFrameSize: [wv frame].size];
}
// iterate over the subviews and add them to the dragview.
enumerator = [[wv subviews] objectEnumerator];
while ((sv = [enumerator nextObject]) != nil)
{
RETAIN(sv);
[sv removeFromSuperview];
[dragView addSubview: sv];
RELEASE(sv);
}
}
else
{
NSLog(@"Bad palette selection - %d", tag);
current = -1;
}
[dragView setNeedsDisplay: YES];
}
- (NSMutableArray *) actionsForClass: (Class) cls
{
NSArray *methodArray = _GSObjCMethodNamesForClass(cls, NO);
NSEnumerator *en = [methodArray objectEnumerator];
NSMethodSignature *actionSig = [NSMethodSignature signatureWithObjCTypes: "v12@0:4@8"];
NSMutableArray *actionsArray = [NSMutableArray array];
NSString *methodName = nil;
NSRange setRange = NSMakeRange(0,3);
while((methodName = [en nextObject]) != nil)
{
SEL sel = NSSelectorFromString(methodName);
NSMethodSignature *signature = [cls instanceMethodSignatureForSelector: sel];
if([signature numberOfArguments] == 3)
{
if([actionSig isEqual: signature] && NSEqualRanges([methodName rangeOfString: @"set"], setRange) == NO &&
[methodName isEqual: @"encodeWithCoder:"] == NO && [methodName isEqual: @"mouseDown:"] == NO)
{
[actionsArray addObject: methodName];
}
}
}
return actionsArray;
}
- (NSMutableArray *) outletsForClass: (Class) cls
{
NSArray *methodArray = _GSObjCMethodNamesForClass(cls, NO);
NSEnumerator *en = [methodArray objectEnumerator];
NSMethodSignature *outletSig = [NSMethodSignature signatureWithObjCTypes: "v12@0:4@8"];
NSMutableArray *outletsArray = [NSMutableArray array];
NSString *methodName = nil;
NSRange setRange = NSMakeRange(0,3);
while((methodName = [en nextObject]) != nil)
{
SEL sel = NSSelectorFromString(methodName);
NSMethodSignature *signature = [cls instanceMethodSignatureForSelector: sel];
if([signature numberOfArguments] == 3)
{
if([outletSig isEqual: signature] && NSEqualRanges([methodName rangeOfString: @"set"], setRange) == YES &&
[methodName isEqual: @"encodeWithCoder:"] == NO && [methodName isEqual: @"mouseDown:"] == NO)
{
NSRange range = NSMakeRange(3,([methodName length] - 4));
NSString *outletMethod = [[methodName substringWithRange: range] lowercaseString];
if([methodArray containsObject: outletMethod])
{
[outletsArray addObject: outletMethod];
}
}
}
}
return outletsArray;
}
- (void) importClasses: (NSArray *)classes withDictionary: (NSDictionary *)dict
{
NSEnumerator *en = [classes objectEnumerator];
id className = nil;
NSMutableDictionary *masterDict = [NSMutableDictionary dictionary];
// import the classes.
while((className = [en nextObject]) != nil)
{
NSMutableDictionary *classDict = [NSMutableDictionary dictionary];
Class cls = NSClassFromString(className);
Class supercls = [cls superclass];
NSString *superClassName = NSStringFromClass(supercls);
NSMutableArray *actions = [self actionsForClass: cls];
NSMutableArray *outlets = [self outletsForClass: cls];
// if the superclass is defined, set it. if not, don't since
// this might be a palette which adds a root class.
if(superClassName != nil)
{
[classDict setObject: superClassName forKey: @"Super"];
}
// set the action/outlet keys
if(actions != nil)
{
[classDict setObject: actions forKey: @"Actions"];
}
if(outlets != nil)
{
[classDict setObject: outlets forKey: @"Outlets"];
}
[masterDict setObject: classDict forKey: className];
}
// override any elements needed, if it's present.
if(dict != nil)
{
[masterDict addEntriesFromDictionary: dict];
}
// add the classes to the dictionary...
[importedClasses addEntriesFromDictionary: masterDict];
}
- (NSDictionary *) importedClasses
{
return importedClasses;
}
- (void) importImages: (NSArray *)images withBundle: (NSBundle *) bundle
{
NSEnumerator *en = [images objectEnumerator];
id name = nil;
NSMutableArray *paths = [NSMutableArray array];
while((name = [en nextObject]) != nil)
{
NSString *path = [bundle pathForImageResource: name];
[paths addObject: path];
}
[importedImages addObjectsFromArray: paths];
}
- (NSArray *) importedImages
{
return importedImages;
}
- (void) importSounds: (NSArray *)sounds withBundle: (NSBundle *) bundle
{
NSEnumerator *en = [sounds objectEnumerator];
id name = nil;
NSMutableArray *paths = [NSMutableArray array];
while((name = [en nextObject]) != nil)
{
NSString *path = [bundle pathForSoundResource: name];
[paths addObject: path];
}
[importedSounds addObjectsFromArray: paths];
}
- (NSArray *) importedSounds
{
return importedSounds;
}
- (NSDictionary *) substituteClasses
{
return substituteClasses;
}
@end
@implementation GormPalettesManager (NSToolbarDelegate)
- (NSToolbarItem*)toolbar: (NSToolbar*)toolbar
itemForItemIdentifier: (NSString*)itemIdentifier
willBeInsertedIntoToolbar: (BOOL)flag
{
NSToolbarItem *toolbarItem = AUTORELEASE([[NSToolbarItem alloc]
initWithItemIdentifier: itemIdentifier]);
id palette = [palettesDict objectForKey: itemIdentifier];
NSImage *image = [palette paletteIcon];
int tag = [palettes indexOfObject: palette];
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle: itemIdentifier
action: @selector(setCurrentPalette:)
keyEquivalent: @""];
// set up the toolbar...
[toolbarItem setLabel: itemIdentifier];
[toolbarItem setToolTip: itemIdentifier];
[toolbarItem setImage: image];
[toolbarItem setTarget: self];
[toolbarItem setAction: @selector(setCurrentPalette:)];
[toolbarItem setTag: tag];
[toolbarItem setMenuFormRepresentation: menuItem];
// complete setup of menu item.
[menuItem setTarget: self];
[menuItem setTag: tag];
RELEASE(menuItem);
return toolbarItem;
}
- (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*)toolbar
{
return paletteNames;
}
- (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*)toolbar
{
return [NSArray array];
}
- (NSArray*) toolbarSelectableItemIdentifiers: (NSToolbar*)toolbar
{
return paletteNames;
}
@end