diff --git a/ChangeLog b/ChangeLog index 257fc4bee..af08ca13e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,25 @@ +2004-05-23 Quentin Mathe + + * Source/GSToolbarView.m: + * Headers/GNUstepGUI/GSToolbarView.h: + * Source/GSToolbar.m: + * Headers/GNUstepGUI/GSToolbar.h: + * Source/NSToolbar.m: + * Headers/AppKit/NSToolbar.h: + * Source/NSToolbarItem.m: + * Headers/AppKit/NSToolbarItem.h: + * Source/NSWindow+Toolbar.m: + Last part in the validation implementation, border taken in account by the + layout process, lot of memory leaks fixed, selected item possibility + implemented and other minor improvements. + 2004-05-18 Quentin Mathe - * Source/GSToolbarView: Added color customization (default background + * Source/GSToolbarView.m: Added color customization (default background color is now no color), part of the toolbar validation mechanism and other minor changes. * Headers/GNUstepGUI/GSToolbarView.h: Same. - * Source/NSToolbarItem: Added part of the toolbar validation mechanism, + * Source/NSToolbarItem.m: Added part of the toolbar validation mechanism, new menuFormRepresentation implementation to have the menu item titles used as label with the text display mode and other minor changes. diff --git a/Headers/Additions/GNUstepGUI/GSToolbar.h b/Headers/Additions/GNUstepGUI/GSToolbar.h index d3888e506..325cf5ec2 100644 --- a/Headers/Additions/GNUstepGUI/GSToolbar.h +++ b/Headers/Additions/GNUstepGUI/GSToolbar.h @@ -76,6 +76,7 @@ APPKIT_EXPORT NSString *NSToolbarWillAddItemNotification; NSToolbarDisplayMode _displayMode; NSToolbarSizeMode _sizeMode; NSString *_identifier; + NSString *_selectedItemIdentifier; NSMutableArray *_items; GSToolbarView *_toolbarView; NSWindow *_window; @@ -109,6 +110,7 @@ APPKIT_EXPORT NSString *NSToolbarWillAddItemNotification; - (void) setConfigurationFromDictionary: (NSDictionary*)configDict; - (void) setDelegate: (id)delegate; - (void) setSelectedItemIdentifier: (NSString *) identifier; +- (void) setUsesStandardBackgroundColor: (BOOL)standard; - (NSToolbarSizeMode) sizeMode; @end /* interface of NSToolbar */ @@ -138,7 +140,7 @@ willBeInsertedIntoToolbar: (BOOL)flag; // Extensions @interface NSArray (ObjectsWithValueForKey) -- (NSArray *) objectsWithValue: (NSString *)value forKey: (NSString *)key; +- (NSArray *) objectsWithValue: (id)value forKey: (NSString *)key; @end #endif /* _GNUstep_H_NSToolbar */ diff --git a/Headers/Additions/GNUstepGUI/GSToolbarView.h b/Headers/Additions/GNUstepGUI/GSToolbarView.h index 28275ec44..6d5c18e38 100644 --- a/Headers/Additions/GNUstepGUI/GSToolbarView.h +++ b/Headers/Additions/GNUstepGUI/GSToolbarView.h @@ -52,9 +52,9 @@ enum { }; typedef enum { - _ToolbarViewDefaultHeight = 61, - _ToolbarViewRegularHeight = 61, - _ToolbarViewSmallHeight = 51 + _ToolbarViewDefaultHeight = 62, + _ToolbarViewRegularHeight = 62, + _ToolbarViewSmallHeight = 52 } _ToolbarViewHeight; typedef enum { @@ -88,6 +88,7 @@ static const int _ClippedItemsViewWidth = 28; unsigned int _borderMask; NSToolbarDisplayMode _displayMode; NSToolbarSizeMode _sizeMode; + NSRect _rectAvailable; unsigned int _heightFromLayout; } @@ -105,12 +106,9 @@ static const int _ClippedItemsViewWidth = 28; @end // Toolbar related NSColor methods - @interface NSColor (Extensions) - + (NSColor *) toolbarBackgroundColor; + (NSColor *) toolbarBorderColor; - @end #endif diff --git a/Headers/AppKit/NSToolbarItem.h b/Headers/AppKit/NSToolbarItem.h index 2913fe0b2..ff44be347 100644 --- a/Headers/AppKit/NSToolbarItem.h +++ b/Headers/AppKit/NSToolbarItem.h @@ -72,6 +72,7 @@ APPKIT_EXPORT NSString *NSToolbarPrintItemIdentifier; id _view; NSView *_backView; BOOL _modified; + BOOL _selectable; // size NSSize _maxSize; @@ -134,8 +135,9 @@ APPKIT_EXPORT NSString *NSToolbarPrintItemIdentifier; @end /* interface of NSToolbarItem */ -@protocol NSToolbarItemValidation -- (BOOL) validateToolbarItem: (NSToolbarItem *)theItem; +// Informal protocol for the toolbar validation +@interface NSObject (NSToolbarItemValidation) +- (BOOL) validateToolbarItem: (NSToolbarItem *)toolbarItem; @end #endif /* _GNUstep_H_NSToolbarItem */ diff --git a/Source/GSToolbar.m b/Source/GSToolbar.m index ec71fc082..c3fe3165e 100644 --- a/Source/GSToolbar.m +++ b/Source/GSToolbar.m @@ -34,6 +34,7 @@ #include #include #include +#include "AppKit/NSApplication.h" #include "AppKit/NSToolbarItem.h" #include "AppKit/NSView.h" #include "AppKit/NSClipView.h" @@ -44,6 +45,7 @@ #include "AppKit/NSMenu.h" #include "AppKit/NSEvent.h" #include "AppKit/NSWindow.h" +#include "AppKit/NSWindow+Toolbar.h" #include "GNUstepGUI/GSToolbarView.h" #include "GNUstepGUI/GSToolbar.h" @@ -53,12 +55,14 @@ static const int current_version = 1; static NSMutableArray *toolbars; +// Validation stuff +static const unsigned int ValidationInterval = 10; +static id validationCenter; // Extensions - @implementation NSArray (ObjectsWithValueForKey) -- (NSArray *) objectsWithValue: (NSString *)value forKey: (NSString *)key +- (NSArray *) objectsWithValue: (id)value forKey: (NSString *)key { NSMutableArray *result = [[NSMutableArray alloc] init]; NSArray *keys = [self valueForKey: key]; @@ -71,7 +75,7 @@ static NSMutableArray *toolbars; for (i = 0; i < n; i++) { - if ([[keys objectAtIndex: i] isEqualToString: value]) + if ([[keys objectAtIndex: i] isEqual: value]) { [result addObject: [self objectAtIndex: i]]; } @@ -80,10 +84,361 @@ static NSMutableArray *toolbars; if ([result count] == 0) return nil; + AUTORELEASE(result); + return result; } @end +// Validation support + +@interface NSView (ToolbarValidation) +- (void) mouseDown: (NSEvent *)event; +@end + +@interface GSValidationObject : NSObject +{ + NSMutableArray *_observers; + NSView *_view; +} + +- (id) initWithView: (NSView *)view; +- (NSArray *) observers; +- (void) addObserver: (id)observer; +- (void) removeObserver: (id)observer; +- (void) validate; +- (NSView *) view; +@end + +@interface GSValidationManager : NSObject +{ + NSWindow *_window; + NSView *_trackingRectView; + NSTrackingRectTag _trackingRect; + NSTimer *_validationTimer; + BOOL _inside; +} + +- (id) initWithWindow: (NSWindow *)window; +- (void) validate; + +- (void) invalidate; + +// Tracking rect methods +- (void) mouseEntered: (NSEvent *)event; +- (void) mouseExited: (NSEvent *)event; +@end + +@interface GSValidationCenter : NSObject +{ + NSMutableArray *_validationManagers; + NSMutableArray *_validationObjects; + NSWindow *_prevWindow; +} + ++ (GSValidationCenter *) sharedValidationCenter; +- (id) init; +- (void) viewWillMove: (NSNotification *)notification; +- (void) viewDidMove: (NSNotification *)notification; +- (void) unregisterValidationObject: (GSValidationObject *)validationObject; +- (GSValidationObject *) validationObjectWithView: (NSView *)view; + +// Private methods +- (GSValidationManager *) _validationManagerWithWindow: (NSWindow *)window; +- (NSArray *) _validationObjectsWithWindow: (NSWindow *)window; +@end + +// Validation mechanism + +@implementation NSView (ToolbarValidation) + +- (void) mouseDown: (NSEvent *)event +{ + GSValidationManager *validationManager = + [[GSValidationCenter sharedValidationCenter] _validationManagerWithWindow: [self window]]; + + [validationManager performSelector: @selector(validate) withObject: nil afterDelay: 0.1]; +} +@end + +@implementation GSValidationObject + +- (id) initWithView: (NSView *)view +{ + if ((self = [super init]) != nil) + { + ASSIGN(_view, view); + _observers = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void) dealloc +{ + RELEASE(_view); + RELEASE(_observers); + [super dealloc]; +} + +- (NSArray *) observers +{ + return _observers; +} + +- (void) addObserver: (id)observer +{ + [_observers addObject: observer]; +} + +- (void) removeObserver: (id)observer +{ + if ([_observers containsObject: observer]) + [_observers removeObject: observer]; +} + +- (void) validate +{ + if ([_view superview] != nil) + { + [_observers makeObjectsPerformSelector: @selector(_validate)]; + } +} + +- (NSView *) view +{ + return _view; +} + +@end + +@interface NSWindow (GNUstepPrivate) +- (NSView *) _windowView; +@end + +@implementation GSValidationManager + +- (id) initWithWindow: (NSWindow *)window { + if ((self = [super init]) != nil) + {; + NSView *vw = [window _windowView]; + + ASSIGN(_window, window); + [nc addObserver: self selector: @selector(windowWillClose:) + name: NSWindowWillCloseNotification object: _window]; + + ASSIGN(_trackingRectView, vw); + _trackingRect = [_trackingRectView addTrackingRect: [_trackingRectView bounds] + owner: self + userData: nil + assumeInside: NO]; + } + + return self; +} + +- (void) invalidate +{ + [_trackingRectView removeTrackingRect: _trackingRect]; // tracking rect retains us, that is normal ? + [_validationTimer invalidate]; // timer idem +} + +- (void) dealloc +{ + RELEASE(_trackingRectView); + RELEASE(_window); + + [super dealloc]; +} + +- (void) windowWillClose: (NSNotification *)notification +{ + GSValidationCenter *vc = [GSValidationCenter sharedValidationCenter]; + NSEnumerator *e; + id vo; + + [nc removeObserver: self]; + + e = [[vc _validationObjectsWithWindow: _window] objectEnumerator]; + while ((vo = [e nextObject]) != nil) + { + [vc unregisterValidationObject: vo]; + } +} + +- (void) mouseEntered: (NSEvent *)event +{ + _inside = YES; + if (_validationTimer == nil || ![_validationTimer isValid]) + { + _validationTimer = [NSTimer timerWithTimeInterval: ValidationInterval + target: self + selector: @selector(validate) + userInfo: nil + repeats: YES]; + [[NSRunLoop currentRunLoop] addTimer: _validationTimer forMode: NSDefaultRunLoopMode]; + [self validate]; + } +} + +- (void) mouseExited: (NSEvent *)event +{ + _inside = NO; + [_validationTimer invalidate]; + _validationTimer = nil; +} + +- (void) validate +{ + [[[GSValidationCenter sharedValidationCenter] + _validationObjectsWithWindow: _window] makeObjectsPerformSelector: @selector(validate)]; +} + +@end + +@implementation GSValidationCenter + ++ (GSValidationCenter *) sharedValidationCenter +{ + if (validationCenter == nil) + validationCenter = [[GSValidationCenter alloc] init]; + + return validationCenter; +} + +- (id) init +{ + if ((self = [super init]) != nil) + { + _validationManagers = [[NSMutableArray alloc] init]; + _validationObjects = [[NSMutableArray alloc] init]; + + [nc addObserver: self selector: @selector(viewWillMove:) name: @"GSViewWillMoveToWindow" object: nil]; + [nc addObserver: self selector: @selector(viewDidMove:) name: @"GSViewDidMoveToWindow" object: nil]; + } + + return self; +} + +- (void) viewWillMove: (NSNotification *)notification +{ + _prevWindow = [[notification object] window]; + RETAIN(_prevWindow); +} + +- (void) viewDidMove: (NSNotification *)notification +{ + NSWindow *window = [[notification object] window]; + GSValidationManager *validationManager = [self _validationManagerWithWindow : _prevWindow]; + + if (validationManager != nil && [[self _validationObjectsWithWindow: _prevWindow] count] == 0) + { + [validationManager invalidate]; + [_validationManagers removeObject: validationManager]; + } + + if (window == nil) + return; + + validationManager = [self _validationManagerWithWindow: window]; + if (validationManager == nil) + { + validationManager = [[GSValidationManager alloc] initWithWindow: window]; + [_validationManagers addObject: validationManager]; + RELEASE(validationManager); + } + + RELEASE(_prevWindow); +} + +/* validationObjectWithView: opposite method + * Remove the object in the validation objects list. + * Release the validation manager associated to the window (related to the validation object and its + * view) in the case there are no other validation objects related to this window. + */ +- (void) unregisterValidationObject: (GSValidationObject *)validationObject +{ + int index; + + if ((index = [_validationObjects indexOfObject: validationObject]) != NSNotFound) + { + NSWindow *window = [[validationObject view] window]; + + [_validationObjects removeObjectAtIndex: index]; + + if ([[self _validationObjectsWithWindow: window] count] == 0) + { + GSValidationManager *validationManager = [self _validationManagerWithWindow: window]; + [validationManager invalidate]; + [_validationManagers removeObject: validationManager]; + } + } +} + +/* Return the validation object associated with the parameter view. + * If there is no such validation object, create it by using view and then check that an associated + * validation manager (bound to the window which the view depends on) exists. + * If there is no such validation manager, create it. + */ +- (GSValidationObject *) validationObjectWithView: (NSView *)view +{ + GSValidationObject *validationObject; + GSValidationManager *validationManager; + NSWindow *window = [view window]; + + if (view == nil) + { + NSLog(@"Validation object cannot be created because the view is nil"); + return nil; + } + + validationObject = [[_validationObjects objectsWithValue: view forKey: @"_view"] objectAtIndex: 0]; + if (validationObject == nil) + { + validationObject = [[GSValidationObject alloc] initWithView: view]; + [_validationObjects addObject: validationObject]; + + if (window == nil) + return nil; + + validationManager = [self _validationManagerWithWindow: window]; + if (validationManager == nil) + { + validationManager = [[GSValidationManager alloc] initWithWindow: window]; + [_validationManagers addObject: validationManager]; + RELEASE(validationManager); + } + } + + return validationObject; +} + +// Private methods + +- (GSValidationManager *) _validationManagerWithWindow: (NSWindow *)window +{ + GSValidationManager *validationManager = + [[_validationManagers objectsWithValue: window forKey: @"_window"] objectAtIndex: 0]; + + return validationManager; +} + +- (NSArray *) _validationObjectsWithWindow: (NSWindow *)window +{ + NSEnumerator *e = [_validationObjects objectEnumerator]; + id validationObject; + NSMutableArray *array = [NSMutableArray array]; + + while ((validationObject = [e nextObject]) != nil) + { + if ([[validationObject view] window] == window) + [array addObject: validationObject]; + } + + return array; +} + +@end + // --- @interface GSToolbar (GNUstepPrivate) @@ -112,6 +467,7 @@ static NSMutableArray *toolbars; - (void) _loadConfig; - (NSToolbarItem *) _toolbarItemForIdentifier: (NSString *)itemIdent; - (GSToolbar *) _toolbarModel; +- (void) _validate; // Accessors - (void) _setToolbarView: (GSToolbarView *)toolbarView; @@ -121,14 +477,21 @@ static NSMutableArray *toolbars; @end @interface NSToolbarItem (GNUstepPrivate) +- (BOOL) _selectable; +- (void) _setSelectable: (BOOL)selectable; +- (BOOL) _selected; +- (void) _setSelected: (BOOL)selected; - (void) _setToolbar: (GSToolbar *)toolbar; @end @interface GSToolbarView (GNUstepPrivate) - (void) _reload; + +// Accessors - (NSArray *) _visibleBackViews; - (BOOL) _willBeVisible; - (void) _setWillBeVisible: (BOOL)willBeVisible; +- (void) _setUsesStandardBackgroundColor: (BOOL)standard; @end // --- @@ -195,8 +558,6 @@ static NSMutableArray *toolbars; } //[self _loadConfig]; - - [self _setDelegate: [toolbarModel delegate] broadcast: NO]; } else { @@ -220,8 +581,11 @@ static NSMutableArray *toolbars; - (void) dealloc { - DESTROY (_identifier); - DESTROY (_configurationDictionary); + // use DESTROY ? + RELEASE(_identifier); + RELEASE(_selectedItemIdentifier); + RELEASE(_configurationDictionary); + RELEASE(_items); if (_delegate != nil) { @@ -311,7 +675,7 @@ static NSMutableArray *toolbars; - (NSString *) selectedItemIdentifier { - return nil; + return _selectedItemIdentifier; } - (NSArray *) visibleItems @@ -355,7 +719,33 @@ static NSMutableArray *toolbars; - (void) setSelectedItemIdentifier: (NSString *)itemIdentifier { - // do something here + NSArray *selectedItems; + NSArray *itemsToSelect; + NSEnumerator *e; + NSToolbarItem *item; + NSArray *selectableIdentifiers; + + // First, we have to deselect the previous selected toolbar items + selectedItems = [[self items] objectsWithValue: [self selectedItemIdentifier] + forKey: @"_itemIdentifier"]; + e = [selectedItems objectEnumerator]; + while ((item = [e nextObject]) != nil) + { + [item _setSelected: NO]; + } + + ASSIGN(_selectedItemIdentifier, itemIdentifier); + + if ([_delegate respondsToSelector: @selector(toolbarSelectableItemIdentifiers:)]) + selectableIdentifiers = [_delegate toolbarSelectableItemIdentifiers: self]; + + itemsToSelect = [_items objectsWithValue: _selectedItemIdentifier forKey: @"_itemIdentifier"]; + e = [itemsToSelect objectEnumerator]; + while ((item = [e nextObject]) != nil) + { + if ([selectableIdentifiers containsObject: [item itemIdentifier]] && ![item _selected]) + [item _setSelected: YES]; + } } - (NSToolbarSizeMode) sizeMode @@ -363,6 +753,11 @@ static NSMutableArray *toolbars; return _sizeMode; } +- (void) setUsesStandardBackgroundColor: (BOOL)standard +{ + [_toolbarView _setUsesStandardBackgroundColor: standard]; +} + // Private methods - (void) _build @@ -385,9 +780,7 @@ static NSMutableArray *toolbars; toolbarModel = [self _toolbarModel]; - if (toolbarModel != nil - && toolbarModel != self - && [toolbarModel delegate] == _delegate) + if (toolbarModel != nil && [toolbarModel delegate] == _delegate) { wantedItemIdentifiers = [[toolbarModel items] valueForKey: @"_itemIdentifier"]; @@ -411,7 +804,7 @@ static NSMutableArray *toolbars; - (void) _loadConfig { - if(_identifier != nil) + if (_identifier != nil) { NSUserDefaults *defaults; NSString *tableKey; @@ -441,6 +834,9 @@ static NSMutableArray *toolbars; if (linked != nil && [linked count] > 0) { toolbar = [linked objectAtIndex: 0]; + + // toolbar model class must be identical to self class : + // an NSToolbar instance cannot use a GSToolbar instance as a model if ([toolbar isMemberOfClass: [self class]] && toolbar != self) return toolbar; } @@ -471,7 +867,7 @@ static NSMutableArray *toolbars; * * The methods below handles the toolbar edition and broacasts each associated * event to the other toolbars with identical identifiers. - * + * Warning : broadcast process only happens between instances based on the same class. */ #define TRANSMIT(signature) \ @@ -503,9 +899,17 @@ static NSMutableArray *toolbars; if (item != nil) { - [nc postNotificationName: NSToolbarWillAddItemNotification + NSArray *selectableItems; + + if ([_delegate respondsToSelector: @selector(toolbarSelectableItemIdentifiers:)]) + { + selectableItems = [_delegate toolbarSelectableItemIdentifiers: self]; + if ([selectableItems containsObject: itemIdentifier]) + [item _setSelectable: YES]; + } + + [nc postNotificationName: NSToolbarWillAddItemNotification object: self]; - [item _setToolbar: self]; [_items insertObject: item atIndex: index]; @@ -574,8 +978,16 @@ static NSMutableArray *toolbars; - (void) _setDelegate: (id)delegate broadcast: (BOOL)broadcast { - if (_delegate == delegate) + + if (_delegate == delegate + || (broadcast == NO && [_delegate isMemberOfClass: [delegate class]])) return; + // We don't reload instances which received this message and already have a + // delegate based on a class identical to the parameter delegate, it permits + // to use only one nib owner class as a toolbar delegate even if a new instance + // of the nib owner are created with each new window (see MiniController.m in + // the toolbar example application). + #define CHECK_REQUIRED_METHOD(selector_name) \ if (![delegate respondsToSelector: @selector(selector_name)]) \ @@ -616,7 +1028,26 @@ static NSMutableArray *toolbars; - (void) _setToolbarView: (GSToolbarView *)toolbarView { + GSValidationObject *validationObject = nil; + GSValidationCenter *vc = [GSValidationCenter sharedValidationCenter]; + GSToolbar *toolbarModel = [self _toolbarModel]; + + validationObject = [vc validationObjectWithView: _toolbarView]; + if (validationObject != nil) + { + [validationObject removeObserver: self]; + if ([[validationObject observers] count] == 0) + [vc unregisterValidationObject: validationObject]; + } + ASSIGN(_toolbarView, toolbarView); + + validationObject = [vc validationObjectWithView: _toolbarView]; + if (validationObject != nil) + [validationObject addObserver: self]; + + if (_delegate == nil) + [self _setDelegate: [toolbarModel delegate] broadcast: NO]; } - (GSToolbarView *) _toolbarView @@ -624,4 +1055,9 @@ static NSMutableArray *toolbars; return _toolbarView; } +- (void) _validate +{ + [self validateVisibleItems]; +} + @end diff --git a/Source/GSToolbarView.m b/Source/GSToolbarView.m index 41dc41f43..bbcadac07 100644 --- a/Source/GSToolbarView.m +++ b/Source/GSToolbarView.m @@ -47,7 +47,7 @@ // internal static const int current_version = 1; static NSColorList *SystemExtensionsColors; -static NSColor *ClassicBackgroundColor; +static NSColor *StandardBackgroundColor; static NSColor *BackgroundColor; static NSColor *BorderColor; @@ -103,7 +103,6 @@ static void initSystemExtensionsColors(void) continue; color = [colors objectForKey: colorKey]; - [SystemExtensionsColors setColor: color forKey: colorKey]; changed = YES; @@ -131,16 +130,20 @@ static void initSystemExtensionsColors(void) */ @interface GSToolbar (GNUstepPrivate) - (void) _build; + +// Accessors - (void) _setToolbarView: (GSToolbarView *)toolbarView; - (GSToolbarView *) _toolbarView; @end @interface NSToolbarItem (GNUstepPrivate) +- (void) _layout; + +// Accessors - (NSView *) _backView; - (NSMenuItem *) _defaultMenuFormRepresentation; - (BOOL) _isModified; - (BOOL) _isFlexibleSpace; -- (void) _layout; @end @interface GSToolbarView (GNUstepPrivate) @@ -153,6 +156,8 @@ static void initSystemExtensionsColors(void) // Accessors - (float) _heightFromLayout; - (NSArray *) _visibleBackViews; +- (void) _setSizeMode: (NSToolbarSizeMode)sizeMode; +- (NSToolbarSizeMode) _sizeMode; - (void) _setWillBeVisible: (BOOL)willBeVisible; - (BOOL) _willBeVisible; @end @@ -180,7 +185,7 @@ static void initSystemExtensionsColors(void) NSImage *image = [NSImage imageNamed: @"common_ToolbarClippedItemsMark"]; if ((self = [super initWithFrame: NSMakeRect(0, 0, _ClippedItemsViewWidth, - _ItemBackViewDefaultHeight)]) != nil) + 100)]) != nil) // the correct height will be set by the layout method { [self setBordered: NO]; [[self cell] setHighlightsBy: NSChangeGrayCellMask @@ -195,13 +200,19 @@ static void initSystemExtensionsColors(void) return nil; } +- (void) dealloc +{ + RELEASE(_toolbar); + [super dealloc]; +} + - (void) layout { [self setFrameSize: NSMakeSize([self frame].size.width, - [[_toolbar _toolbarView] _heightFromLayout])]; + [[_toolbar _toolbarView] _heightFromLayout])]; } - (void) mouseDown: (NSEvent *)event { - NSMenu *clippedItemsMenu = [self menuForEvent:event]; + NSMenu *clippedItemsMenu = [self menuForEvent: event]; [super highlight: YES]; @@ -224,9 +235,9 @@ static void initSystemExtensionsColors(void) - (NSMenu *) returnMenu { - // this method cannot be called menu otherwise it would + // this method cannot be called "menu" otherwise it would // override NSResponder method with the same name - NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + NSMenu *menu = [[NSMenu alloc] initWithTitle: @""]; NSEnumerator *e; id item; NSArray *visibleItems; @@ -271,8 +282,10 @@ static void initSystemExtensionsColors(void) return; initSystemExtensionsColors(); - ClassicBackgroundColor = + StandardBackgroundColor = RETAIN([NSColor colorWithCalibratedRed: 0.8 green: 0.8 blue: 0.8 alpha: 1.0]); + // Never released, but that's not a problem because the variable is static and then will be + // deallocated with the class when the application quits. } - (id) initWithFrame: (NSRect)frame @@ -286,7 +299,7 @@ static void initSystemExtensionsColors(void) displayMode: (NSToolbarDisplayMode)displayMode sizeMode: (NSToolbarSizeMode)sizeMode { - if((self = [super initWithFrame: frame]) != nil) + if ((self = [super initWithFrame: frame]) != nil) { float toolbarViewHeight; @@ -309,31 +322,31 @@ static void initSystemExtensionsColors(void) toolbarViewHeight = 0; } - [self setFrame: NSMakeRect( - frame.origin.x, - frame.origin.y, - frame.size.width, - toolbarViewHeight)]; + [self setFrame: NSMakeRect(frame.origin.x, + frame.origin.y, + frame.size.width, + toolbarViewHeight)]; // --- - - _clipView = [[NSClipView alloc] initWithFrame: - NSMakeRect(0, 1, frame.size.width, toolbarViewHeight - 1)]; + + _clipView = [[NSClipView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)]; [_clipView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; - + + [self setBorderMask: GSToolbarViewTopBorder | GSToolbarViewBottomBorder + | GSToolbarViewRightBorder | GSToolbarViewLeftBorder]; // Adjust the clip view frame + [self addSubview: _clipView]; _clippedItemsMark = [[GSToolbarClippedItemsButton alloc] init]; - - _borderMask = GSToolbarViewTopBorder | GSToolbarViewBottomBorder - | GSToolbarViewRightBorder | GSToolbarViewLeftBorder; BackgroundColor = [SystemExtensionsColors colorWithKey: @"toolbarBackgroundColor"]; BorderColor = [SystemExtensionsColors colorWithKey: @"toolbarBorderColor"]; RETAIN(BackgroundColor); RETAIN(BorderColor); + // Never released, but that's not a problem because the variables are static and then will be + // deallocated with the class when the application quits. // --- @@ -377,7 +390,7 @@ static void initSystemExtensionsColors(void) { [NSBezierPath strokeLineFromPoint: NSMakePoint(0, viewFrame.size.height - 0.5) toPoint: NSMakePoint(viewFrame.size.width, - viewFrame.size.height - 0.5)]; + viewFrame.size.height - 0.5)]; } if (_borderMask & GSToolbarViewLeftBorder) { @@ -388,11 +401,10 @@ static void initSystemExtensionsColors(void) { [NSBezierPath strokeLineFromPoint: NSMakePoint(viewFrame.size.width - 0.5,0) toPoint: NSMakePoint(viewFrame.size.width - 0.5, - viewFrame.size.height)]; + viewFrame.size.height)]; } [super drawRect: aRect]; - } - (BOOL) isOpaque @@ -415,16 +427,6 @@ static void initSystemExtensionsColors(void) [self _reload]; } -- (void) viewDidMoveToSuperview -{ - // NSView method called when a view is moved not to a superview - - if (_toolbar != nil) - { - //[self _reload]; - } -} - - (void) viewDidMoveToWindow { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; @@ -433,11 +435,9 @@ static void initSystemExtensionsColors(void) // variable _window) [super viewDidMoveToWindow]; - [nc removeObserver: self name: NSWindowDidResizeNotification object: _window]; + [nc removeObserver: self name: NSWindowDidResizeNotification object: nil]; [nc addObserver: self selector: @selector(windowDidResize:) - name: NSWindowDidResizeNotification object: nil]; - - //[self viewDidMoveToSuperview]; + name: NSWindowDidResizeNotification object: _window]; [nc postNotificationName: @"GSViewDidMoveToWindow" object: self]; } @@ -465,7 +465,51 @@ static void initSystemExtensionsColors(void) - (void) setBorderMask: (unsigned int)borderMask { + NSRect toolbarViewFrame = [self frame]; + NSRect rect = NSMakeRect(0, 0, toolbarViewFrame.size.width, 100); + _borderMask = borderMask; + + // Take in account the size mode + + switch (_sizeMode) + { + case NSToolbarSizeModeDefault: + rect.size.height = _ToolbarViewDefaultHeight; + break; + case NSToolbarSizeModeRegular: + rect.size.height = _ToolbarViewRegularHeight; + break; + case NSToolbarSizeModeSmall: + rect.size.height = _ToolbarViewSmallHeight; + break; + default: + ; // invalid + } + + // Take in account the border + + if (_borderMask & GSToolbarViewBottomBorder) + { + rect = NSMakeRect(rect.origin.x, ++rect.origin.y, rect.size.width, --rect.size.height); + } + + if (_borderMask & GSToolbarViewTopBorder) + { + rect = NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, --rect.size.height); + } + + if (_borderMask & GSToolbarViewLeftBorder) + { + rect = NSMakeRect(++rect.origin.x, rect.origin.y, --rect.size.width, rect.size.height); + } + + if (_borderMask & GSToolbarViewRightBorder) + { + rect = NSMakeRect(rect.origin.x, rect.origin.y, --rect.size.width, rect.size.height); + } + + [_clipView setFrame: rect]; } - (void) setToolbar: (GSToolbar *)toolbar @@ -479,9 +523,9 @@ static void initSystemExtensionsColors(void) [self _setToolbar: toolbar]; } -- (NSColor *) classicBackgroundColor +- (NSColor *) standardBackgroundColor { - return ClassicBackgroundColor; + return StandardBackgroundColor; } // Private methods @@ -496,8 +540,8 @@ static void initSystemExtensionsColors(void) // --- NSArray *subviews = [self subviews]; - _heightFromLayout = 100; - + _heightFromLayout = 0; + while ((item = [e nextObject]) != nil) { itemBackView = [item _backView]; @@ -513,12 +557,12 @@ static void initSystemExtensionsColors(void) itemBackViewFrame = [itemBackView frame]; [itemBackView setFrame: NSMakeRect(x, - itemBackViewFrame.origin.y, - itemBackViewFrame.size.width, - itemBackViewFrame.size.height)]; + itemBackViewFrame.origin.y, + itemBackViewFrame.size.width, + itemBackViewFrame.size.height)]; x += [itemBackView frame].size.width; - if (itemBackViewFrame.size.height < _heightFromLayout) + if (itemBackViewFrame.size.height > _heightFromLayout) _heightFromLayout = itemBackViewFrame.size.height; } @@ -592,7 +636,7 @@ static void initSystemExtensionsColors(void) // Resize the clip view if (lastVisibleBackView != nil) - width = NSMaxX([lastVisibleBackView frame]); + width = NSMaxX([lastVisibleBackView frame]); [_clipView setFrame: NSMakeRect(clipViewFrame.origin.x, clipViewFrame.origin.y, width, @@ -614,14 +658,14 @@ static void initSystemExtensionsColors(void) } else if (([_clippedItemsMark superview] != nil) - && ([visibleBackViews count] >= [backViews count])) + && ([visibleBackViews count] == [backViews count])) { [_clippedItemsMark removeFromSuperview]; [_clipView setFrame: NSMakeRect(clipViewFrame.origin.x, clipViewFrame.origin.y, - [self frame].size.width, - clipViewFrame.size.height)]; + [self frame].size.width, + clipViewFrame.size.height)]; } } @@ -672,7 +716,7 @@ static void initSystemExtensionsColors(void) } if (lengthAvailable < flexibleSpaceItemsNumber) - return; + return; e = [items objectEnumerator]; while ((item = [e nextObject]) != nil) @@ -703,21 +747,31 @@ static void initSystemExtensionsColors(void) - (float) _heightFromLayout { - return _heightFromLayout; + float height = _heightFromLayout; + + if (_borderMask & GSToolbarViewBottomBorder) + { + height++; + } + + if (_borderMask & GSToolbarViewTopBorder) + { + height++; + } + + + return height; } -- (NSArray *) _visibleBackViews +// Will return the visible (not clipped) back views in the toolbar view even when the toolbar is not visible +// May be should be renamed _notClippedBackViews method +- (NSArray *) _visibleBackViews { NSArray *items = [_toolbar items]; NSView *backView; int i, n = [items count]; float backViewsWidth = 0, toolbarWidth = [self frame].size.width; - /* - // _willBeVisible indicates that the toolbar view previously hidden is in - // process to become visible again before the end of current the event loop. - if ([self superview] == nil && ![self _willBeVisible]) - return nil; - */ + [_visibleBackViews release]; _visibleBackViews = [[NSMutableArray alloc] init]; @@ -738,6 +792,18 @@ static void initSystemExtensionsColors(void) } +- (void) _setSizeMode: (NSToolbarSizeMode)sizeMode +{ + _sizeMode = sizeMode; +} + +- (NSToolbarSizeMode) _sizeMode +{ + return _sizeMode; +} + +// _willBeVisible indicates that the toolbar view previously hidden is in +// process to become visible again before the end of current the event loop. - (BOOL) _willBeVisible { return _willBeVisible; @@ -748,4 +814,18 @@ static void initSystemExtensionsColors(void) _willBeVisible = willBeVisible; } +- (void) _setUsesStandardBackgroundColor: (BOOL)standard +{ + if (standard) + { + RELEASE(BackgroundColor); + BackgroundColor = [self standardBackgroundColor]; + } + else + { + BackgroundColor = [NSColor clearColor]; + RETAIN(BackgroundColor); + } +} + @end diff --git a/Source/NSToolbar.m b/Source/NSToolbar.m index 656a1f507..c0e8221ac 100644 --- a/Source/NSToolbar.m +++ b/Source/NSToolbar.m @@ -73,9 +73,13 @@ static const int current_version = 1; @interface GSToolbarView (GNUstepPrivate) - (void) _reload; + +// Accessors - (NSArray *) _visibleBackViews; -- (BOOL) _willBeVisible; +- (void) _setSizeMode: (NSToolbarSizeMode)sizeMode; +- (NSToolbarSizeMode) _sizeMode; - (void) _setWillBeVisible: (BOOL)willBeVisible; +- (BOOL) _willBeVisible; @end @interface NSWindow (ToolbarPrivate) @@ -204,6 +208,7 @@ static const int current_version = 1; broadcast: (BOOL)broadcast { _sizeMode = sizeMode; + [_toolbarView _setSizeMode: _sizeMode]; [_toolbarView _reload]; [_window _adjustToolbarView]; @@ -246,7 +251,8 @@ static const int current_version = 1; - (void)_setWindow: (NSWindow *)window { - ASSIGN(_window, window); + _window = window; + // We don't do an ASSIGN because the toolbar view retains us. // call [NSWindow(Toolbar) setToolbar:] to set the toolbar window } diff --git a/Source/NSToolbarItem.m b/Source/NSToolbarItem.m index 3e3f0a00a..bf60cc316 100644 --- a/Source/NSToolbarItem.m +++ b/Source/NSToolbarItem.m @@ -62,11 +62,16 @@ @end @interface NSToolbarItem (GNUstepPrivate) +- (void) _layout; +// --- - (NSView *) _backView; - (NSMenuItem *) _defaultMenuFormRepresentation; - (BOOL) _isFlexibleSpace; - (BOOL) _isModified; -- (void) _layout; +- (BOOL) _selectable; +- (void) _setSelectable: (BOOL)selectable; +- (BOOL) _selected; +- (void) _setSelected: (BOOL)selected; - (void) _setToolbar: (GSToolbar *)toolbar; @end @@ -85,15 +90,19 @@ - (id) initWithToolbarItem: (NSToolbarItem *)toolbarItem; - (void) layout; -- (void) setToolbarItemAction: (SEL)action; + +// Accessors - (NSToolbarItem *) toolbarItem; - (SEL) toolbarItemAction; +- (void) setToolbarItemAction: (SEL)action; @end @implementation GSToolbarButton - (id) initWithToolbarItem: (NSToolbarItem *)toolbarItem { self = [super initWithFrame: NSMakeRect(_ItemBackViewX, _ItemBackViewY, _ItemBackViewDefaultWidth, _ItemBackViewDefaultHeight)]; + // Frame will be reset by the layout method + if (self != nil) { ASSIGN(_toolbarItem, toolbarItem); @@ -101,15 +110,24 @@ return self; } +- (void) dealloc +{ + RELEASE(_toolbarItem); + + [super dealloc]; +} + - (void) layout { float textWidth, layoutedWidth = -1, layoutedHeight = -1; NSAttributedString *attrStr; NSDictionary *attr; NSFont *font = [NSFont systemFontOfSize: 11]; // [NSFont smallSystemFontSize] or better should NSControlContentFontSize - + unsigned int borderMask = [[[_toolbarItem toolbar] _toolbarView] borderMask]; + NSString *label = [_toolbarItem label]; + // Adjust the layout in accordance with NSToolbarSizeMode - + switch ([[_toolbarItem toolbar] sizeMode]) { case NSToolbarSizeModeDefault: @@ -137,11 +155,27 @@ } [[self cell] setFont: font]; + + // Adjust the layout in accordance with the border + + if (!(borderMask & GSToolbarViewBottomBorder)) + { + layoutedHeight++; + layoutedWidth++; + } + + if (!(borderMask & GSToolbarViewTopBorder)) + { + layoutedHeight++; + layoutedWidth++; + } // Adjust the layout in accordance with the label attr = [NSDictionary dictionaryWithObject: font forKey: @"NSFontAttributeName"]; - attrStr = [[NSAttributedString alloc] initWithString: [_toolbarItem label] attributes: attr]; + if (label == nil || [label isEqualToString: @""]) + label = @"Dummy"; + attrStr = [[NSAttributedString alloc] initWithString: label attributes: attr]; textWidth = [attrStr size].width + 2 * _InsetItemTextX; if (layoutedWidth != -1 && textWidth > layoutedWidth) @@ -176,8 +210,20 @@ } +- (void) mouseDown: (NSEvent *)event +{ + if ([_toolbarItem _selectable] && [self state]) + return; // Abort in case the button is selectable and selected + // HACK: must be improved to handle drag event + + [super mouseDown: event]; +} + - (BOOL) sendAction: (SEL)action to: (id)target { + if ([_toolbarItem _selectable]) + [[_toolbarItem toolbar] setSelectedItemIdentifier: [_toolbarItem itemIdentifier]]; + if (_toolbarItemAction) { return [NSApp sendAction: _toolbarItemAction to: target from: _toolbarItem]; @@ -227,6 +273,7 @@ { self = [super initWithFrame: NSMakeRect(_ItemBackViewX, _ItemBackViewY, _ItemBackViewDefaultWidth, _ItemBackViewDefaultHeight)]; + // Frame will be reset by the layout method if (self != nil) { @@ -236,12 +283,20 @@ return self; } +- (void) dealloc +{ + RELEASE(_toolbarItem); + //RELEASE(_font); + + [super dealloc]; +} + - (void)drawRect: (NSRect)rect { NSAttributedString *attrString; NSDictionary *attr; NSColor *color; - int textX; + float textX; [super drawRect: rect]; // We draw _view which is a subview @@ -258,10 +313,10 @@ { // we draw the label attr = [NSDictionary dictionaryWithObjectsAndKeys: _font, - @"NSFontAttributeName", + @"NSFontAttributeName", color, - @"NSForegroundColorAttributeName", - nil]; + @"NSForegroundColorAttributeName", + nil]; attrString = [[NSAttributedString alloc] initWithString: [_toolbarItem label] attributes: attr]; textX = (([self frame].size.width - _InsetItemTextX) - [attrString size].width) / 2; [attrString drawAtPoint: NSMakePoint(textX, _InsetItemTextY)]; @@ -275,6 +330,8 @@ float textWidth, layoutedWidth = -1, layoutedHeight = -1; NSAttributedString *attrStr; NSDictionary *attr; + unsigned int borderMask = [[[_toolbarItem toolbar] _toolbarView] borderMask]; + NSString *label = [_toolbarItem label]; _font = [NSFont systemFontOfSize: 11]; // [NSFont smallSystemFontSize] or better should be NSControlContentFontSize @@ -307,11 +364,27 @@ default: NSLog(@"Invalid NSToolbarSizeMode"); // invalid } + + // Adjust the layout in accordance with the border + + if (!(borderMask & GSToolbarViewBottomBorder)) + { + layoutedHeight++; + layoutedWidth++; + } + + if (!(borderMask & GSToolbarViewTopBorder)) + { + layoutedHeight++; + layoutedWidth++; + } // Adjust the layout in accordance with the label attr = [NSDictionary dictionaryWithObject: _font forKey: @"NSFontAttributeName"]; - attrStr = [[NSAttributedString alloc] initWithString: [_toolbarItem label] attributes: attr]; + if (label == nil || [label isEqualToString: @""]) + label = @"Dummy"; + attrStr = [[NSAttributedString alloc] initWithString: label attributes: attr]; textWidth = [attrStr size].width + 2 * _InsetItemTextX; if (textWidth > layoutedWidth) @@ -430,7 +503,12 @@ - (void) _layout { - // override the default implementation in order to do nothing + NSView *backView = [self _backView]; + + // override the default implementation + + [(id)backView layout]; + [backView setFrameSize: NSMakeSize(30, [backView frame].size.height)]; } @end @@ -453,11 +531,6 @@ { return nil;// override the default implementation in order to do nothing } - -- (void) _layout -{ - // override the default implementation in order to do nothing -} @end // ---- NSToolbarFlexibleSpaceItemIdentifier @@ -485,6 +558,8 @@ { NSView *backView = [self _backView]; + [(id)backView layout]; + [backView setFrameSize: NSMakeSize(0, [backView frame].size.height)]; // override the default implementation in order to reset the _backView to a zero width @@ -504,7 +579,7 @@ self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; - [self setLabel: @"Colors"]; + [self setLabel: @"Colors"]; // FIX ME: localize // set action... [self setTarget: nil]; // goes to first responder.. @@ -527,7 +602,7 @@ self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; - [self setLabel: @"Fonts"]; + [self setLabel: @"Fonts"]; // FIX ME: localize // set action... [self setTarget: nil]; // goes to first responder.. @@ -550,7 +625,7 @@ self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; - [self setLabel: @"Customize"]; + [self setLabel: @"Customize"]; // FIX ME: localize // set action... [self setTarget: nil]; // goes to first responder.. @@ -573,7 +648,7 @@ self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; - [self setLabel: @"Print..."]; + [self setLabel: @"Print..."]; // FIX ME: localize // set action... [self setTarget: nil]; // goes to first responder.. @@ -696,6 +771,20 @@ return self; } +- (void) dealloc +{ + RELEASE(_itemIdentifier); + RELEASE(_label); + RELEASE(_image); + RELEASE(_menuFormRepresentation); + RELEASE(_paletteLabel); + RELEASE(_toolTip); + RELEASE(_view); + RELEASE(_backView); + + [super dealloc]; +} + - (BOOL) isEnabled { if(_flags._isEnabled) @@ -945,6 +1034,45 @@ return [self isKindOfClass: [GSToolbarFlexibleSpaceItem class]]; } +- (BOOL) _selectable +{ + return _selectable; +} + +- (BOOL) _selected +{ + return [(GSToolbarButton *)_backView state]; +} + +- (void) _setSelected: (BOOL)selected +{ + if (_selectable && ![self _selected] && selected) + { + [(GSToolbarButton *)_backView performClick:self]; + } + else if (!selected) + { + [(GSToolbarButton *)_backView setState: NO]; + } + else if (!_selectable) + { + NSLog(@"The toolbar item %@ is not selectable", self); + } +} + +- (void) _setSelectable: (BOOL)selectable +{ + if ([_backView isKindOfClass: [GSToolbarButton class]]) + { + _selectable = selectable; + [(GSToolbarButton *)_backView setButtonType: NSOnOffButton]; + } + else + { + NSLog(@"The toolbar item %@ is not selectable", self); + } +} + - (void) _setToolbar: (GSToolbar *)toolbar { ASSIGN(_toolbar, toolbar); diff --git a/Source/NSWindow+Toolbar.m b/Source/NSWindow+Toolbar.m index 5dfcd3ca1..7d9c71a93 100644 --- a/Source/NSWindow+Toolbar.m +++ b/Source/NSWindow+Toolbar.m @@ -13,7 +13,7 @@ modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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