Toolbar improvements... Fixed several major and minor bugs, rewritten the validation support, lot of memory leaks eliminated and other changes

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@19689 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Quentin Mathe 2004-07-07 21:25:42 +00:00
parent e2178bf8f1
commit e428e69f82
8 changed files with 492 additions and 378 deletions

View file

@ -1,3 +1,34 @@
2004-07-07 Quentin Mathe <qmathe@club-internet.fr>
* Source/GSToolbar.m: Fixed several segmentation faults which could
occur when windows with a toolbar have been closed and new similar
windows are initialized (bug #9535), rewritten validation support to
be more clearer and efficient, and the toolbar notifications now
correctly bundles the toolbar items in the notification dictionary
(Patch by Ludovic Marcotte).
* Source/GSToolbarView:
* Source/NSToolbar.m:
* Source/NSWindow+Toolbar.m:
Related changes and lot of memory leaks fixed permitting to have now
the toolbar system properly deallocated when the toolbar is not anymore
retained by the user and the toolbar view.
Note: In order to observe the toolbar system being deallocated when you close
a window, you need to have the option "Window released when closed"
checked.
Actual retain logic is strictly :
- Superview in the view hierachy retains toolbar view
- Toolbar view retains toolbar
- Toolbar view retains toolbar items view
- Toolbar retains toolbar items
And when a toolbar is based on NSToolbar class :
- Windows retains toolbar view.
* Source/NSView.m: Added a missing method call -viewWillMoveToSuperview:
in -removeSubview: method (this missing method is used by the toolbar
view).
* Headers/Additions/GNUstepGUI/GSToolbar.h:
* Headers/AppKit/NSToolbar.h:
Moved the _window ivar from GSToolbar class to NSToolbar class.
2004-07-07 01:23 Alexander Malmberg <alexander@malmberg.org>
* Source/NSWindow+Toolbar.m: Properly convert window frames to

View file

@ -79,7 +79,6 @@ APPKIT_EXPORT NSString *NSToolbarWillAddItemNotification;
NSString *_selectedItemIdentifier;
NSMutableArray *_items;
GSToolbarView *_toolbarView;
NSWindow *_window;
BOOL _build;
}

View file

@ -35,6 +35,7 @@
@interface NSToolbar : GSToolbar
{
NSWindow *_window;
BOOL _visible;
}

View file

@ -56,15 +56,16 @@ static const int current_version = 1;
static NSMutableArray *toolbars;
// Validation stuff
static const unsigned int ValidationInterval = 10;
static id validationCenter;
static const unsigned int ValidationInterval = 4;
@class GSValidationCenter; // Mandatory because the interface is declared later
static GSValidationCenter *vc;
// Extensions
@implementation NSArray (ObjectsWithValueForKey)
- (NSArray *) objectsWithValue: (id)value forKey: (NSString *)key
{
NSMutableArray *result = [[NSMutableArray alloc] init];
NSMutableArray *result = [NSMutableArray array];
NSArray *keys = [self valueForKey: key];
int i, n = 0;
@ -84,381 +85,377 @@ static id validationCenter;
if ([result count] == 0)
return nil;
AUTORELEASE(result);
return result;
}
@end
// Validation support
/*
* Validation support
*
* Validation support is architectured around a shared validation center, which
* is our public interface to handle the validation, behind the scene each
* window has an associated validation object created when an observer is added
* to the validation center.
* A validation object calls the _validate: method on the observer when the
* mouse is inside the observed window and only in the case this window is
* updated or in the case the mouse stays inside more than four seconds, then
* the action will be reiterated every four seconds until the mouse exits.
* A validation object owns a window to observe, a tracking rect attached to
* the window root view to know when the mouse is inside, a timer to be able to
* send the _validate: message periodically, and one ore more observers, then it
* is necessary to supply with each registered observer an associated window to
* observe.
* In the case, an object would observe several windows, the _validate: has a
* parameter observedWindow to let us know where the message is coming from.
* Because we cannot know surely when a validation object is deallocated, a
* method named clean has been added which permits to invalidate a validation
* object which must not be used anymore, not calling it would let segmentation
* faults occurs.
*/
@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;
NSMutableArray *_observers;
NSTimer *_validationTimer;
BOOL _inside;
BOOL _validating;
}
- (id) initWithWindow: (NSWindow *)window;
- (NSMutableArray *) observers;
- (void) setObservers: (NSMutableArray *)observers;
- (NSWindow *) window;
- (void) setWindow: (NSWindow *)window;
- (void) validate;
- (void) scheduledValidate;
- (void) clean;
- (void) invalidate;
// Tracking rect methods
- (void) mouseEntered: (NSEvent *)event;
- (void) mouseExited: (NSEvent *)event;
@end
@interface GSValidationCenter : NSObject
{
NSMutableArray *_validationManagers;
NSMutableArray *_validationObjects;
NSWindow *_prevWindow;
NSMutableArray *_vobjs;
}
+ (GSValidationCenter *) sharedValidationCenter;
- (id) init;
- (void) viewWillMove: (NSNotification *)notification;
- (void) viewDidMove: (NSNotification *)notification;
- (void) unregisterValidationObject: (GSValidationObject *)validationObject;
- (GSValidationObject *) validationObjectWithView: (NSView *)view;
+ (id) sharedValidationCenter;
- (NSArray *) observersWindow: (NSWindow *)window;
- (void) addObserver: (id)observer window: (NSWindow *)window;
- (void) removeObserver: (id)observer window: (NSWindow *)window;
// Private methods
- (GSValidationManager *) _validationManagerWithWindow: (NSWindow *)window;
- (NSArray *) _validationObjectsWithWindow: (NSWindow *)window;
@end
// Validation mechanism
@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
@implementation GSValidationObject
- (id) initWithWindow: (NSWindow *)window
{
if ((self = [super init]) != nil)
{
NSView *vw;
vw = [window _windowView];
_observers = [[NSMutableArray alloc] init];
ASSIGN(_window, window);
[nc addObserver: self selector: @selector(windowDidUpdate:)
name: NSWindowDidUpdateNotification object: _window];
[nc addObserver: self selector: @selector(windowWillClose:)
name: NSWindowWillCloseNotification object: _window];
name: NSWindowDidUpdateNotification
object: window];
[nc addObserver: vc
selector: @selector(windowWillClose:)
name: NSWindowWillCloseNotification
object: window];
ASSIGN(_trackingRectView, vw);
_trackingRect
= [_trackingRectView addTrackingRect: [_trackingRectView bounds]
owner: self
userData: nil
assumeInside: NO];
_trackingRectView = [window _windowView];
_trackingRect
= [_trackingRectView addTrackingRect: [_trackingRectView bounds]
owner: self
userData: nil
assumeInside: NO];
_window = window;
}
return self;
}
- (void) invalidate
{
[_trackingRectView removeTrackingRect: _trackingRect];
// tracking rect retains us, that is normal ?
[_validationTimer invalidate]; // timer idem
}
- (void) dealloc
{
RELEASE(_trackingRectView);
RELEASE(_window);
// NSLog(@"vobj dealloc");
// [_trackingRectView removeTrackingRect: _trackingRect];
// Not here because the tracking rect retains us, then when the tracking rect
// would be deallocated that would create a loop and a segmentation fault.
// See next method.
RELEASE(_observers);
[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)
- (void) clean
{
if ([_validationTimer isValid])
{
[vc unregisterValidationObject: vo];
[_validationTimer invalidate];
_validationTimer = nil;
}
[nc removeObserver: vc
name: NSWindowWillCloseNotification
object: _window];
[nc removeObserver: self
name: NSWindowDidUpdateNotification
object: _window];
[self setWindow: nil];
// Needed because the validation timer can retain us and by this way retain also the toolbar which is
// currently observing.
[self setObservers: nil]; // To release observers
[_trackingRectView removeTrackingRect: _trackingRect];
// We can safely remove the tracking rect here, because it will never call
// this method unlike dealloc.
}
/*
* FIXME: Replace the deprecated method which follows by this one when -base
* NSObject will implement it.
*
- (id) valueForUndefinedKey: (NSString *)key
{
if ([key isEqualToString: @"window"] || [key isEqualToString: @"_window"])
return nil;
return [super valueForUndefinedKey: key];
}
*/
- (id) handleQueryWithUnboundKey: (NSString *)key
{
if ([key isEqualToString: @"window"] || [key isEqualToString: @"_window"])
return [NSNull null];
return [super handleQueryWithUnboundKey: key];
}
- (NSMutableArray *) observers
{
return _observers;
}
- (void) setObservers: (NSMutableArray *)observers
{
ASSIGN(_observers, observers);
}
- (NSWindow *) window
{
return _window;
}
- (void) setWindow: (NSWindow *)window
{
_window = window;
}
- (void) validate
{
_validating = YES;
// NSLog(@"vobj validate");
[_observers makeObjectsPerformSelector: @selector(_validate:)
withObject: _window];
_validating = NO;
}
- (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];
}
[self scheduledValidate];
}
- (void) mouseExited: (NSEvent *)event
{
_inside = NO;
[_validationTimer invalidate];
_validationTimer = nil;
}
- (void) validate
{
[[[GSValidationCenter sharedValidationCenter]
_validationObjectsWithWindow: _window]
makeObjectsPerformSelector: @selector(validate)];
}
- (void) windowDidUpdate: (NSNotification *)notification
{
if ([[NSApp currentEvent] type] == NSLeftMouseUp)
if ([_validationTimer isValid])
{
GSValidationManager *validationManager =
[[GSValidationCenter sharedValidationCenter]
_validationManagerWithWindow: [notification object]];
[validationManager performSelector: @selector(validate) withObject: nil];
[_validationTimer invalidate];
_validationTimer = nil;
}
}
- (void) windowDidUpdate: (NSNotification *)notification
{
// NSLog(@"Window update %d", [[NSApp currentEvent] type]);
if (!_inside || _validating || [[NSApp currentEvent] type] == NSMouseMoved)
return;
// _validating permits in the case the UI/window is refreshed by a validation to
// avoid have windowDidUpdate called, which would cause a loop like that :
// validate -> view update -> windowDidUpdate -> validate etc.
[self validate];
}
- (void) scheduledValidate
{
if (!_inside)
return;
[self validate];
_validationTimer =
[NSTimer timerWithTimeInterval: ValidationInterval
target: self
selector: @selector(scheduledValidate)
userInfo: nil
repeats: NO];
[[NSRunLoop currentRunLoop] addTimer: _validationTimer
forMode: NSDefaultRunLoopMode];
}
@end
@implementation GSValidationCenter
+ (GSValidationCenter *) sharedValidationCenter
{
if (validationCenter == nil)
validationCenter = [[GSValidationCenter alloc] init];
if (vc == nil)
{
if ((vc = [[GSValidationCenter alloc] init]) != nil)
{
// Nothing special
}
}
return validationCenter;
return vc;
}
- (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];
_vobjs = [[NSMutableArray alloc] init];
}
return self;
}
- (void) viewWillMove: (NSNotification *)notification
- (void) dealloc
{
_prevWindow = [[notification object] window];
RETAIN(_prevWindow);
[nc removeObserver: self];
RELEASE(_vobjs);
[super dealloc];
}
- (void) viewDidMove: (NSNotification *)notification
- (NSArray *) observersWindow: (NSWindow *)window
{
NSWindow *window = [[notification object] window];
GSValidationManager *validationManager =
[self _validationManagerWithWindow : _prevWindow];
int i;
NSArray *observersArray;
NSMutableArray *result;
if (validationManager != nil
&& [[self _validationObjectsWithWindow: _prevWindow] count] == 0)
if (window == nil)
{
[validationManager invalidate];
[_validationManagers removeObject: validationManager];
result = [NSMutableArray array];
observersArray = [_vobjs valueForKey: @"_observers"];
for (i = 0; i < [observersArray count]; i++)
{
[result addObjectsFromArray: [observersArray objectAtIndex: i]];
}
return result;
}
else
{
result = [[[_vobjs objectsWithValue: window forKey: @"_window"]
objectAtIndex: 0] observers];
return result;
}
}
- (void) addObserver: (id)observer window: (NSWindow *)window
{
GSValidationObject *vobj =
[[_vobjs objectsWithValue: window forKey: @"_window"] objectAtIndex: 0];
NSMutableArray *observersWindow = nil;
if (window == nil)
return;
validationManager = [self _validationManagerWithWindow: window];
if (validationManager == nil)
if (vobj != nil)
{
validationManager = [[GSValidationManager alloc] initWithWindow: window];
[_validationManagers addObject: validationManager];
RELEASE(validationManager);
observersWindow = [vobj observers];
}
RELEASE(_prevWindow);
else
{
vobj = [[GSValidationObject alloc] initWithWindow: window];
[_vobjs addObject: vobj];
RELEASE(vobj);
observersWindow = [NSMutableArray array];
[vobj setObservers: observersWindow];
}
[observersWindow addObject: observer];
}
/* 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
- (void) removeObserver: (id)observer window: (NSWindow *)window
{
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];
}
}
}
GSValidationObject *vobj;
NSMutableArray *observersWindow;
NSMutableArray *windows;
NSEnumerator *e;
NSWindow *w;
/* 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)
if (window == nil)
{
NSDebugLog(@"Validation object cannot be created because the view is \
nil");
return nil;
windows = [_vobjs valueForKey: @"_window"];
}
else
{
windows = [NSArray arrayWithObject: window];
}
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)
e = [windows objectEnumerator];
while ((w = [e nextObject]) != nil)
{
vobj = [[_vobjs objectsWithValue: w forKey: @"_window"] objectAtIndex: 0];
observersWindow = [vobj observers];
if (observersWindow != nil && [observersWindow containsObject: observer])
{
validationManager =
[[GSValidationManager alloc] initWithWindow: window];
[_validationManagers addObject: validationManager];
RELEASE(validationManager);
[observersWindow removeObject: observer];
if ([observersWindow count] == 0)
{
[vobj clean];
[_vobjs removeObjectIdenticalTo: vobj];
}
}
}
return validationObject;
}
// Private methods
- (GSValidationManager *) _validationManagerWithWindow: (NSWindow *)window
- (void) windowWillClose: (NSNotification *)notification
{
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)
GSValidationObject *vobj;
// NSLog(@"Window will close");
vobj = [[_vobjs objectsWithValue: [notification object] forKey: @"_window"]
objectAtIndex: 0];
if (vobj != nil)
{
if ([[validationObject view] window] == window)
[array addObject: validationObject];
[vobj clean];
[_vobjs removeObjectIdenticalTo: vobj];
}
return array;
}
@end
@ -491,7 +488,8 @@ static id validationCenter;
- (void) _loadConfig;
- (NSToolbarItem *) _toolbarItemForIdentifier: (NSString *)itemIdent;
- (GSToolbar *) _toolbarModel;
- (void) _validate;
- (void) _validate: (NSWindow *)observedWindow;
- (void) _toolbarViewWillMoveToSuperview: (NSView *)newSuperview;
// Accessors
- (void) _setToolbarView: (GSToolbarView *)toolbarView;
@ -532,6 +530,7 @@ static id validationCenter;
{
[self setVersion: current_version];
nc = [NSNotificationCenter defaultCenter];
vc = [GSValidationCenter sharedValidationCenter];
toolbars = [[NSMutableArray alloc] init];
}
}
@ -579,10 +578,10 @@ static id validationCenter;
if ([toolbarModel displayMode] != displayMode
&& [toolbarModel sizeMode] != sizeMode)
{
// raise an exception.
// Raise an exception.
}
//[self _loadConfig];
// [self _loadConfig];
}
else
{
@ -591,7 +590,7 @@ static id validationCenter;
_autosavesConfiguration = NO;
_configurationDictionary = nil;
//[self _loadConfig];
// [self _loadConfig];
_delegate = nil;
}
@ -605,8 +604,12 @@ static id validationCenter;
}
- (void) dealloc
{
// use DESTROY ?
{
NSLog(@"Toolbar dealloc %@", self);
[vc removeObserver: self window: nil];
// Use DESTROY ?
RELEASE(_identifier);
RELEASE(_selectedItemIdentifier);
RELEASE(_configurationDictionary);
@ -621,6 +624,40 @@ static id validationCenter;
[super dealloc];
}
- (void) release
{
// We currently only worry about when our toolbar view is deallocated.
// Views which belongs to a window which is deallocated, are released.
// In such case, it's necessary to remove the toolbar which belongs to this
// view from the master list when nobody else still retains us, so that it
// doesn't cause a memory leak.
if ([self retainCount] == 2)
[toolbars removeObjectIdenticalTo: self];
[super release];
}
/*
* FIXME: Replace the deprecated method which follows by this one when -base
* NSObject will implement it.
*
- (id) valueForUndefinedKey: (NSString *)key
{
if ([key isEqualToString: @"window"] || [key isEqualToString: @"_window"])
return nil;
return [super valueForUndefinedKey: key];
}
*/
- (id) handleQueryWithUnboundKey: (NSString *)key
{
if ([key isEqualToString: @"window"] || [key isEqualToString: @"_window"])
return [NSNull null];
return [super handleQueryWithUnboundKey: key];
}
- (void) insertItemWithItemIdentifier: (NSString *)itemIdentifier
atIndex: (int)index
{
@ -768,14 +805,14 @@ static id validationCenter;
}
else
{
NSLog(@"toolbar delegate does not respond to %@",
NSLog(@"Toolbar delegate does not respond to %@",
@selector(toolbarSelectableItemIdentifiers:));
return;
}
if (selectableIdentifiers == nil)
{
NSLog(@"toolbar delegate returns no such selectable item identifiers");
NSLog(@"Toolbar delegate returns no such selectable item identifiers");
return;
}
@ -798,7 +835,7 @@ static id validationCenter;
}
else
{
NSLog(@"toolbar delegate returns no such selectable item identifiers");
NSLog(@"Toolbar delegate returns no such selectable item identifiers");
}
}
@ -822,7 +859,7 @@ static id validationCenter;
- (void) _build
{
/*
* toolbar build :
* Toolbar build :
* will use the delegate when there is no toolbar model
*/
@ -886,7 +923,7 @@ static id validationCenter;
{
NSArray *linked;
id toolbar;
linked = [toolbars objectsWithValue: [self identifier]
forKey: @"_identifier"];
@ -894,10 +931,12 @@ static id validationCenter;
{
toolbar = [linked objectAtIndex: 0];
// toolbar model class must be identical to self class :
// 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;
else
return nil;
}
return nil;
@ -939,7 +978,7 @@ static id validationCenter;
{ \
if (toolbar != self && [toolbar isMemberOfClass: [self class]]) \
[toolbar signature]; \
}
} \
- (void) _insertItemWithItemIdentifier: (NSString *)itemIdentifier
atIndex: (int)index
@ -972,7 +1011,8 @@ static id validationCenter;
}
[nc postNotificationName: NSToolbarWillAddItemNotification
object: self];
object: self
userInfo: [NSDictionary dictionaryWithObject: item forKey: @"item"]];
[item _setToolbar: self];
[_items insertObject: item atIndex: index];
@ -994,10 +1034,15 @@ static id validationCenter;
- (void) _removeItemAtIndex: (int)index broadcast: (BOOL)broadcast
{
id item;
item = RETAIN([_items objectAtIndex: index]);
[_items removeObjectAtIndex: index];
[_toolbarView _reload];
[nc postNotificationName: NSToolbarDidRemoveItemNotification object: self];
[nc postNotificationName: NSToolbarDidRemoveItemNotification
object: self
userInfo: [NSDictionary dictionaryWithObject: item forKey: @"item"]];
RELEASE(item);
if (broadcast)
{
@ -1041,8 +1086,8 @@ static id validationCenter;
- (void) _setDelegate: (id)delegate broadcast: (BOOL)broadcast
{
if(_delegate)
[nc removeObserver: _delegate name: nil object: self];
//if(_delegate)
// [nc removeObserver: _delegate name: nil object: self];
if (_delegate == delegate
|| (broadcast == NO && [_delegate isMemberOfClass: [delegate class]]))
@ -1053,7 +1098,9 @@ static id validationCenter;
* instance of the nib owner are created with each new window (see
* MiniController.m in the toolbar example application).
*/
if(_delegate)
[nc removeObserver: _delegate name: nil object: self];
#define CHECK_REQUIRED_METHOD(selector_name) \
if (![delegate respondsToSelector: @selector(selector_name)]) \
@ -1065,7 +1112,7 @@ static id validationCenter;
CHECK_REQUIRED_METHOD(toolbarAllowedItemIdentifiers:);
CHECK_REQUIRED_METHOD(toolbarDefaultItemIdentifiers:);
// assign the delegate...
// Assign the delegate...
_delegate = delegate;
#define SET_DELEGATE_NOTIFICATION(notif_name) \
@ -1081,7 +1128,7 @@ static id validationCenter;
if (_toolbarView != nil)
[_toolbarView _reload];
// broadcast now...
// Broadcast now...
if (broadcast)
{
@ -1093,34 +1140,28 @@ static id validationCenter;
- (void) _setToolbarView: (GSToolbarView *)toolbarView
{
GSValidationObject *validationObject = nil;
GSValidationCenter *vc = [GSValidationCenter sharedValidationCenter];
GSToolbar *toolbarModel = [self _toolbarModel];
if (_toolbarView != nil)
{
validationObject = [vc validationObjectWithView: _toolbarView];
if (validationObject != nil)
{
[validationObject removeObserver: self];
if ([[validationObject observers] count] == 0)
[vc unregisterValidationObject: validationObject];
}
[vc removeObserver: self window: nil];
}
if (toolbarView != nil)
{
[vc addObserver: self window: [toolbarView window]];
// In the case the window parameter is a nil value, nothing happens.
}
ASSIGN(_toolbarView, toolbarView);
// Don't do an ASSIGN here, the toolbar itself retains us.
_toolbarView = toolbarView;
if (_toolbarView == nil)
return;
validationObject = [vc validationObjectWithView: _toolbarView];
if (validationObject != nil)
[validationObject addObserver: self];
/* In the case, the user hasn't set a delegate until now, we set it.
* Why ?
* We don't set it before when the toolbar is inited, to do only one toolbar
* content load.
* We don't set it before when the toolbar is initialized, to do only one
* toolbar content load.
* ...
* 1 toolbar = [[GSToolbar alloc] initWithIdentifier: @"blabla"];
* 2 [toolbar setDelegate: myDelegate];
@ -1139,8 +1180,24 @@ static id validationCenter;
return _toolbarView;
}
- (void) _validate
- (void) _toolbarViewWillMoveToSuperview: (NSView *)newSuperview
{
// Must synchronize the validation system
// _toolbarView should never be nil here
// We don't handle synchronization when the toolbar view is added to a superview not
// binded to a window, such superview being later moved to a window. (FIX ME ?)
// NSLog(@"Moving to window %@", [newSuperview window]);
[vc removeObserver: self window: nil];
if (newSuperview != nil)
[vc addObserver: self window: [newSuperview window]];
}
- (void) _validate: (NSWindow *)observedWindow
{
// We observe only one window, then we ignore observedWindow.
[self validateVisibleItems];
}

View file

@ -130,6 +130,7 @@ static void initSystemExtensionsColors(void)
*/
@interface GSToolbar (GNUstepPrivate)
- (void) _build;
- (void) _toolbarViewWillMoveToSuperview: (NSView *)newSuperview;
// Accessors
- (void) _setToolbarView: (GSToolbarView *)toolbarView;
@ -158,6 +159,8 @@ static void initSystemExtensionsColors(void)
- (NSArray *) _visibleBackViews;
- (void) _setSizeMode: (NSToolbarSizeMode)sizeMode;
- (NSToolbarSizeMode) _sizeMode;
- (BOOL) _usesStandardBackgroundColor;
- (void) _setUsesStandardBackgroundColor: (BOOL)standard;
- (void) _setWillBeVisible: (BOOL)willBeVisible;
- (BOOL) _willBeVisible;
@end
@ -172,8 +175,8 @@ static void initSystemExtensionsColors(void)
// Accessors
- (NSMenu *) returnMenu;
// this method cannot be called "menu" otherwise it would override NSResponder
// method with the same name
// This method cannot be called "menu" otherwise it would override NSResponder
// method with the same name.
- (void) layout;
- (void) setToolbar: (GSToolbar *)toolbar;
@ -185,7 +188,7 @@ static void initSystemExtensionsColors(void)
NSImage *image = [NSImage imageNamed: @"common_ToolbarClippedItemsMark"];
if ((self = [super initWithFrame: NSMakeRect(0, 0, _ClippedItemsViewWidth,
100)]) != nil) // the correct height will be set by the layout method
100)]) != nil) // The correct height will be set by the layout method
{
[self setBordered: NO];
[[self cell] setHighlightsBy: NSChangeGrayCellMask
@ -193,19 +196,13 @@ static void initSystemExtensionsColors(void)
[self setAutoresizingMask: NSViewNotSizable];
[self setImagePosition: NSImageOnly];
[image setScalesWhenResized: YES];
//[image setSize: NSMakeSize(20, 20)];
// [image setSize: NSMakeSize(20, 20)];
[self setImage: image];
return self;
}
return nil;
}
- (void) dealloc
{
RELEASE(_toolbar);
[super dealloc];
}
- (void) layout {
[self setFrameSize: NSMakeSize([self frame].size.width,
[[_toolbar _toolbarView] _heightFromLayout])];
@ -235,7 +232,7 @@ 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: @""];
NSEnumerator *e;
@ -269,7 +266,8 @@ static void initSystemExtensionsColors(void)
- (void) setToolbar: (GSToolbar *)toolbar
{
ASSIGN(_toolbar, toolbar);
// Don't do an ASSIGN here, the toolbar view retains us.
_toolbar = toolbar;
}
@end
@ -318,7 +316,7 @@ static void initSystemExtensionsColors(void)
toolbarViewHeight = _ToolbarViewSmallHeight;
break;
default:
// raise exception
// Raise exception
toolbarViewHeight = 0;
}
@ -356,8 +354,17 @@ static void initSystemExtensionsColors(void)
return nil;
}
- (id) retain
{
return [super retain];
}
- (void) dealloc
{
NSLog(@"Toolbar view dealloc");
[[NSNotificationCenter defaultCenter] removeObserver: self];
RELEASE(_toolbar);
RELEASE(_clippedItemsMark);
RELEASE(_clipView);
@ -427,6 +434,14 @@ static void initSystemExtensionsColors(void)
[self _reload];
}
- (void) viewWillMoveToSuperview: (NSView *)newSuperview
{
[super viewWillMoveToSuperview: newSuperview];
[_toolbar _toolbarViewWillMoveToSuperview: newSuperview];
// Allow to update the validation system which is window specific
}
- (void) viewDidMoveToWindow
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
@ -437,18 +452,8 @@ static void initSystemExtensionsColors(void)
[nc removeObserver: self name: NSWindowDidResizeNotification object: nil];
[nc addObserver: self selector: @selector(windowDidResize:)
name: NSWindowDidResizeNotification object: _window];
[nc postNotificationName: @"GSViewDidMoveToWindow" object: self];
}
- (void) viewWillMoveToWindow: (NSWindow *)newWindow
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[super viewWillMoveToWindow: newWindow];
[nc postNotificationName: @"GSViewWillMoveToWindow" object: self];
name: NSWindowDidResizeNotification
object: _window];
}
// More methods... Accessors
@ -484,7 +489,7 @@ static void initSystemExtensionsColors(void)
rect.size.height = _ToolbarViewSmallHeight;
break;
default:
; // invalid
; // Invalid
}
// Take in account the border
@ -611,7 +616,7 @@ static void initSystemExtensionsColors(void)
// We add each backView associated with an added toolbar item
e = [backViews objectEnumerator];
subviews = [_clipView subviews];
subviews = [_clipView subviews];
while ((backView = [e nextObject]) != nil)
{
@ -679,7 +684,7 @@ static void initSystemExtensionsColors(void)
- (void) _setToolbar: (GSToolbar *)toolbar
{
if ([toolbar sizeMode] != _sizeMode)
; // FIXME : raise exception here
; // FIXME: Raise exception here
[toolbar _setToolbarView: self]; // We set the toolbar view on the new toolbar
[_toolbar _setToolbarView: nil]; // We unset the toolbar view from the previous toolbar
@ -763,8 +768,9 @@ static void initSystemExtensionsColors(void)
return height;
}
// 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
// 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];
@ -814,6 +820,11 @@ static void initSystemExtensionsColors(void)
_willBeVisible = willBeVisible;
}
- (BOOL) _usesStandardBackgroundColor
{
return [BackgroundColor isEqual: [self standardBackgroundColor]];
}
- (void) _setUsesStandardBackgroundColor: (BOOL)standard
{
if (standard)

View file

@ -30,6 +30,7 @@
#include <Foundation/NSObject.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSException.h>
#include <Foundation/NSNotification.h>
@ -137,7 +138,8 @@ static const int current_version = 1;
- (void) dealloc
{
// NSLog(@"Dummy NSToolbar dealloc");
[super dealloc];
}
@ -188,7 +190,7 @@ static const int current_version = 1;
{ \
if (toolbar != self && [self isMemberOfClass: [self class]]) \
[toolbar signature]; \
}
} \
- (void) _setDisplayMode: (NSToolbarDisplayMode)displayMode
broadcast: (BOOL)broadcast
@ -247,40 +249,43 @@ static const int current_version = 1;
}
}
// handle notifications
// Notifications
- (void) handleNotification: (NSNotification *)notification
{
NSMutableArray *toolbars = [GSToolbar _toolbars];
// We currently only worry about when our window closes.
// It's necessary to remove the toolbar which belongs to this
// window from the master list, so that it doesn't cause a
// memory leak.
[toolbars removeObjectIdenticalTo: self];
// It's necessary to set the _window ivar in master list to nil when it is
// closed, so that it doesn't cause a segmentation fault when we looks at
// _window ivar with KVC in -[NSWindow(Toolbar) toolbar].
[self _setWindow: nil];
if ([_toolbarView superview] == nil)
RELEASE(_toolbarView);
// We release the toolbar view in such case because NSWindow(Toolbar) retains
// it when its superview value is nil.
}
// Private Accessors
- (void)_setWindow: (NSWindow *)window
- (void) _setWindow: (NSWindow *)window
{
if(_window != window)
if (_window != window)
{
if(_window)
{
[nc removeObserver: _window];
}
if (_window)
{
[nc removeObserver: self];
}
if(window)
{
// watch for this window closing....
[nc addObserver: self
selector: @selector(handleNotification:)
name: NSWindowWillCloseNotification
object: window];
}
if (window)
{
// Watch for this window closing....
[nc addObserver: self
selector: @selector(handleNotification:)
name: NSWindowWillCloseNotification
object: window];
}
}
// We don't do an ASSIGN because the toolbar view retains us.
// call [NSWindow(Toolbar) setToolbar:] to set the toolbar window
_window = window;

View file

@ -620,6 +620,7 @@ GSSetDragTypes(NSView* obj, NSArray *types)
[self willRemoveSubview: aView];
aView->_super_view = nil;
[aView viewWillMoveToWindow: nil];
[aView viewWillMoveToSuperview: nil];
[aView setNextResponder: nil];
RETAIN(aView);
[_sub_views removeObjectIdenticalTo: aView];

View file

@ -27,7 +27,7 @@
#include <Foundation/NSDebug.h>
#include <Foundation/NSException.h>
#include <Foundation/NSAutoreleasePool.h>
#include "AppKit/NSWindow+Toolbar.h"
#include "AppKit/NSView.h"
#include "AppKit/NSToolbar.h"
@ -98,7 +98,11 @@
if (toolbar != nil && [toolbar isVisible])
{
NSArray *subviews = [_contentView subviews];
NSArray *subviews = [_contentView subviews];
// Take in account this method call returns an array which is an
// autoreleased copy.
// By side effect, this increments the toolbar view retain count until the
// autorelease pool is cleared.
NSView *subview;
int i, n = [subviews count];
GSToolbarView *toolbarView = [toolbar _toolbarView];
@ -140,7 +144,7 @@
return (index == NSNotFound) ? nil : [toolbars objectAtIndex: index];
}
// user oriented method
// User oriented method
- (void) setContentViewWithoutToolbar: (NSView *)contentViewWithoutToolbar
{
NSToolbar *toolbar = [self toolbar];
@ -160,7 +164,13 @@
- (void) setToolbar: (NSToolbar*)toolbar
{
NSToolbar *lastToolbar = [self toolbar];
GSToolbarView *toolbarView = nil;
GSToolbarView *toolbarView = [toolbar _toolbarView];
if (toolbarView != nil)
{
NSLog(@"Error: the new toolbar is still owned by a toolbar view");
return;
}
if (lastToolbar != nil)
{
@ -178,18 +188,16 @@
return;
}
// -----
// ELSE
// -----
/*
* Else we do
*/
// The window want to know which toolbar is binded
[toolbar _setWindow : self];
// Instantiate or retrieve the toolbar view (we create this view when the toolbar hasn't such
// view)...
// Instantiate the toolbar view
toolbarView = [toolbar _toolbarView];
if (toolbarView == nil)
{
toolbarView = [[GSToolbarView alloc] initWithFrame: NSMakeRect(0, 0,
@ -274,10 +282,10 @@
contentViewWithoutToolbar = _contentView;
// Switch the content view
RETAIN(contentViewWithoutToolbar);
[self setContentView:
[[NSView alloc] initWithFrame: [_contentView frame]]];
AUTORELEASE([[NSView alloc] initWithFrame: [_contentView frame]])];
// Resize the window
@ -298,9 +306,9 @@
contentViewWithoutToolbarFrame.size.height,
contentViewWithoutToolbarFrame.size.width,
newToolbarViewHeight)];
[_contentView addSubview: toolbarView];
RELEASE(toolbarView);
// Insert the previous content view
/* We want contentViewWithoutToolbarFrame at the origin of our new
@ -319,7 +327,7 @@
contentViewWithoutToolbar = [self contentViewWithoutToolbar];
// Unplug the toolbar view
RETAIN(toolbarView);
[toolbarView removeFromSuperview];
// Resize the window
@ -339,9 +347,10 @@
// Switch the content view
RETAIN(contentViewWithoutToolbar);
// because setContentView: will release the parent view (aka _contentView) and
// Because setContentView: will release the parent view (aka _contentView) and
// their subviews and actually contentViewWithoutToolbar is a subview of _contentView
[contentViewWithoutToolbar removeFromSuperviewWithoutNeedingDisplay];
[self setContentView: contentViewWithoutToolbar];
RELEASE(contentViewWithoutToolbar);