/** NSDrawer The drawer class Copyright (C) 2001 Free Software Foundation, Inc. Author: Gregory Casamento Date: 2006 Author: Fred Kiefer Date: 2001 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "AppKit/NSWindow.h" #include "AppKit/NSBox.h" #include "AppKit/NSView.h" #include "AppKit/NSDrawer.h" static NSNotificationCenter *nc = nil; @interface GSDrawerWindow : NSWindow { NSWindow *_parentWindow; NSWindow *_pendingParentWindow; NSDrawer *_drawer; NSBox *_box; } - (NSRect) frameFromParentWindowFrame; // open/close - (void) openOnEdge; - (void) closeOnEdge; - (void) slide; // window/drawer properties - (void) setParentWindow: (NSWindow *)window; - (NSWindow *) parentWindow; - (void) setPendingParentWindow: (NSWindow *)window; - (NSWindow *) pendingParentWindow; - (void) setDrawer: (NSDrawer *)drawer; - (NSDrawer *) drawer; // handle notifications... - (void) handleWindowClose: (NSNotification *)notification; - (void) handleWindowMiniaturize: (NSNotification *)notification; - (void) handleWindowMove: (NSNotification *)notification; @end @implementation GSDrawerWindow + (void) initialize { if (self == [GSDrawerWindow class]) { nc = [NSNotificationCenter defaultCenter]; [self setVersion: 0]; } } - (id) initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle backing: (NSBackingStoreType)bufferingType defer: (BOOL)flag screen: (NSScreen*)aScreen { self = [super initWithContentRect: contentRect styleMask: aStyle backing: bufferingType defer: flag screen: aScreen]; if (self != nil) { /* _box = [[NSBox alloc] init]; [_box setTitle: @""]; [_box setTitlePosition: NSNoTitle]; [_box setBorderType: NSBezelBorder]; [_box setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; [[super contentView] addSubview: _box]; */ } return self; } /* - (void) setContentView: (NSView *)view { [_box setContentView: view]; } - (NSView *) contentView { return [_box contentView]; } */ - (NSRect) frameFromParentWindowFrame { NSRect newFrame = [_parentWindow frame]; float total = [_drawer leadingOffset] + [_drawer trailingOffset]; NSRectEdge edge = [_drawer preferredEdge]; int state = [_drawer state]; BOOL opened = (state == NSDrawerOpenState); NSSize size = [_drawer maxContentSize]; if (edge == NSMinXEdge) // left { newFrame.size.height -= total; newFrame.origin.y += [_drawer trailingOffset]; newFrame.origin.x -= (opened)?size.width:0; } else if (edge == NSMinYEdge) // bottom { newFrame.size.width -= total; newFrame.origin.x += [_drawer leadingOffset]; newFrame.origin.y -= (opened)?size.height:0; } else if (edge == NSMaxXEdge) // right { newFrame.size.height -= total; newFrame.origin.y += [_drawer trailingOffset]; newFrame.origin.x += (opened)?size.width:0; } else if (edge == NSMaxYEdge) // top { newFrame.size.width -= total; newFrame.origin.x += [_drawer leadingOffset]; newFrame.origin.y += (opened)?size.height:0; } return newFrame; } - (BOOL) canBecomeKeyWindow { return NO; } - (BOOL) canBecomeMainWindow { return NO; } - (void) orderFront: (id)sender { NSPoint holdOrigin = [self frame].origin; NSPoint newOrigin = NSMakePoint(-10000,-10000); NSRect tempFrame = [self frame]; // order the window under the parent... tempFrame.origin = newOrigin; [self setFrame: tempFrame display: NO]; [super orderFront: sender]; [_parentWindow orderWindow: NSWindowAbove relativeTo: [self windowNumber]]; tempFrame.origin = holdOrigin; [self setFrame: tempFrame display: YES]; } - (void) openOnEdge { [self orderFront: self]; [self slide]; } - (void) closeOnEdge { NSRect frame = [self frameFromParentWindowFrame]; // : [_parentWindow frame]]; // slide the drawer closed.... [self slide]; [self setFrame: frame display: YES]; [self orderOut: self]; if (_pendingParentWindow != nil && _pendingParentWindow != _parentWindow) { [self setParentWindow: _pendingParentWindow]; ASSIGN(_pendingParentWindow, nil); } } - (void) slide { NSRect frame = [self frame]; float i; NSRectEdge edge = [_drawer preferredEdge]; NSSize size = [_drawer maxContentSize]; float factor = 1.0; // if it's open, then slide it closed. if ([_drawer state] == NSDrawerClosingState) { factor = -factor; } else if ([_drawer state] == NSDrawerOpeningState) { factor = 1.0; } if (edge == NSMinXEdge) // left { // slide left... for (i = 0; i < size.width; i++) { frame.origin.x -= factor; [self setFrame: frame display: YES]; } } else if (edge == NSMinYEdge) // bottom { // slide down... for (i = 0; i < size.height; i++) { frame.origin.y -= factor; [self setFrame: frame display: YES]; } } else if (edge == NSMaxXEdge) // right { // slide right... for (i = 0; i < size.width; i++) { frame.origin.x += factor; [self setFrame: frame display: YES]; } } else if (edge == NSMaxYEdge) // top { // slide up... for (i = 0; i < size.height; i++) { frame.origin.y += factor; [self setFrame: frame display: YES]; } } } - (void) handleWindowClose: (NSNotification *)notification { [self close]; } - (void) handleWindowMiniaturize: (NSNotification *)notification { [self close]; } - (void) handleWindowMove: (NSNotification *)notification { NSRect frame = [self frameFromParentWindowFrame]; // : [obj frame]]; NSLog(@"%@",NSStringFromRect(frame)); [self setFrame: frame display: NO]; } - (void) setParentWindow: (NSWindow *)window { if (_parentWindow != window) { ASSIGN(_parentWindow, window); [nc removeObserver: self]; if (_parentWindow != nil) { NSRect frame = [self frameFromParentWindowFrame]; [self setFrame: frame display: YES]; // add observers.... [nc addObserver: self selector: @selector(handleWindowClose:) name: NSWindowWillCloseNotification object: _parentWindow]; [nc addObserver: self selector: @selector(handleWindowMiniaturize:) name: NSWindowWillMiniaturizeNotification object: _parentWindow]; [nc addObserver: self selector: @selector(handleWindowMove:) name: NSWindowDidMoveNotification object: _parentWindow]; [nc addObserver: self selector: @selector(handleWindowMove:) name: NSWindowDidResizeNotification object: _parentWindow]; } } } - (NSWindow *) parentWindow { return _parentWindow; } - (void) setPendingParentWindow: (NSWindow *)window { ASSIGN(_pendingParentWindow, window); } - (NSWindow *) pendingParentWindow { return _pendingParentWindow; } - (void) setDrawer: (NSDrawer *)drawer { // don't retain, since the drawer retains us... _drawer = drawer; } - (NSDrawer *) drawer { return _drawer; } - (void) dealloc { RELEASE(_parentWindow); TEST_RELEASE(_pendingParentWindow); [super dealloc]; } @end @implementation NSDrawer + (void) initialize { if (self == [NSDrawer class]) { nc = [NSNotificationCenter defaultCenter]; [self setVersion: 0]; } } // Creation - (id) init { return [self initWithContentSize: NSZeroSize preferredEdge: NSMinXEdge]; } - (id) initWithContentSize: (NSSize)contentSize preferredEdge: (NSRectEdge)edge { self = [super init]; // initialize the drawer window... _drawerWindow = [[GSDrawerWindow alloc] initWithContentRect: NSMakeRect(0, 0, contentSize.width, contentSize.height) styleMask: 0 backing: NSBackingStoreBuffered defer: NO]; [_drawerWindow setDrawer: self]; [_drawerWindow setReleasedWhenClosed: NO]; _preferredEdge = edge; _currentEdge = edge; _maxContentSize = NSMakeSize(200,200); _minContentSize = NSMakeSize(50,50); _state = NSDrawerClosedState; _leadingOffset = 10.0; _trailingOffset = 10.0; return self; } - (void) dealloc { RELEASE(_drawerWindow); if (_delegate != nil) { [nc removeObserver: _delegate name: nil object: self]; _delegate = nil; } [super dealloc]; } // Opening and Closing - (void) close { if (_state != NSDrawerOpenState) return; if ((_delegate != nil) && ([_delegate respondsToSelector: @selector(drawerShouldClose:)]) && ![_delegate drawerShouldClose: self]) return; _state = NSDrawerClosingState; [nc postNotificationName: NSDrawerWillCloseNotification object: self]; [_drawerWindow closeOnEdge]; _state = NSDrawerClosedState; [nc postNotificationName: NSDrawerDidCloseNotification object: self]; } - (void) close: (id)sender { [self close]; } - (void) open { [self openOnEdge: _preferredEdge]; } - (void) open: (id)sender { [self open]; } - (void) openOnEdge: (NSRectEdge)edge { if ((_state != NSDrawerClosedState) || ([self parentWindow] == nil)) return; if ((_delegate != nil) && ([_delegate respondsToSelector: @selector(drawerShouldOpen:)]) && ![_delegate drawerShouldOpen: self]) return; _state = NSDrawerOpeningState; [nc postNotificationName: NSDrawerWillOpenNotification object: self]; _currentEdge = edge; [_drawerWindow openOnEdge]; _state = NSDrawerOpenState; [nc postNotificationName: NSDrawerDidOpenNotification object: self]; } - (void) toggle: (id)sender { if (_state == NSDrawerClosedState) [self open: sender]; else if (_state == NSDrawerOpenState) [self close: sender]; // Do nothing for inbetween states } // Managing Size - (NSSize) contentSize { return [[_drawerWindow contentView] frame].size; } - (float) leadingOffset { return _leadingOffset; } - (NSSize) maxContentSize { return _maxContentSize; } - (NSSize) minContentSize { return _minContentSize; } - (void) setContentSize: (NSSize)size { // Check with min and max size if (size.width < _minContentSize.width) size.width = _minContentSize.width; if (size.height < _minContentSize.height) size.height = _minContentSize.height; if (size.width > _maxContentSize.width) size.width = _maxContentSize.width; if (size.height > _maxContentSize.height) size.height = _maxContentSize.height; // Check with delegate if ((_delegate != nil) && ([_delegate respondsToSelector: @selector(drawerWillResizeContents:toSize:)])) { size = [_delegate drawerWillResizeContents: self toSize: size]; } [_drawerWindow setContentSize: size]; } - (void) setLeadingOffset: (float)offset { _leadingOffset = offset; } - (void) setMaxContentSize: (NSSize)size { _maxContentSize = size; } - (void) setMinContentSize: (NSSize)size { _minContentSize = size; } - (void) setTrailingOffset: (float)offset { _trailingOffset = offset; } - (float) trailingOffset { return _trailingOffset; } // Managing Edge - (NSRectEdge) edge { return _currentEdge; } - (NSRectEdge) preferredEdge { return _preferredEdge; } - (void) setPreferredEdge: (NSRectEdge)preferredEdge { _preferredEdge = preferredEdge; } // Managing Views - (NSView *) contentView { return [_drawerWindow contentView]; } - (NSWindow *) parentWindow { return [_drawerWindow parentWindow]; } - (void) setContentView: (NSView *)aView { [_drawerWindow setContentView: aView]; } - (void) setParentWindow: (NSWindow *)parent { if (_state == NSDrawerClosedState) { [_drawerWindow setParentWindow: parent]; } else { [_drawerWindow setPendingParentWindow: parent]; } } // Delegation and State - (id) delegate { return _delegate; } - (void) setDelegate: (id)anObject { if (_delegate) { [nc removeObserver: _delegate name: nil object: self]; } _delegate = anObject; #define SET_DELEGATE_NOTIFICATION(notif_name) \ if ([_delegate respondsToSelector: @selector(drawer##notif_name:)]) \ [nc addObserver: _delegate \ selector: @selector(drawer##notif_name:) \ name: NSDrawer##notif_name##Notification object: self] SET_DELEGATE_NOTIFICATION(DidClose); SET_DELEGATE_NOTIFICATION(DidOpen); SET_DELEGATE_NOTIFICATION(WillClose); SET_DELEGATE_NOTIFICATION(WillOpen); } - (int) state { return _state; } /* * NSCoding protocol */ - (void) encodeWithCoder: (NSCoder*)aCoder { id parent = [self parentWindow]; [super encodeWithCoder: aCoder]; if ([aCoder allowsKeyedCoding]) { [aCoder encodeSize: [self contentSize] forKey: @"NSContentSize"]; if (_delegate != nil) { [aCoder encodeObject: _delegate forKey: @"NSDelegate"]; } [aCoder encodeFloat: _leadingOffset forKey: @"NSLeadingOffset"]; [aCoder encodeSize: _maxContentSize forKey: @"NSMaxContentSize"]; [aCoder encodeSize: _minContentSize forKey: @"NSMinContentSize"]; if (parent != nil) { [aCoder encodeObject: parent forKey: @"NSParentWindow"]; } [aCoder encodeInt: _preferredEdge forKey: @"NSPreferredEdge"]; [aCoder encodeFloat: _trailingOffset forKey: @"NSTrailingOffset"]; } else { [aCoder encodeSize: [self contentSize]]; [aCoder encodeObject: _delegate]; [aCoder encodeValueOfObjCType: @encode(float) at: &_leadingOffset]; [aCoder encodeSize: _maxContentSize]; [aCoder encodeSize: _minContentSize]; [aCoder encodeObject: parent]; [aCoder encodeValueOfObjCType: @encode(unsigned) at: &_preferredEdge]; [aCoder encodeValueOfObjCType: @encode(float) at: &_trailingOffset]; } } - (id) initWithCoder: (NSCoder*)aDecoder { if ((self = [super initWithCoder: aDecoder]) != nil) { NSWindow *parentWindow = nil; if ([aDecoder allowsKeyedCoding]) { _contentSize = [aDecoder decodeSizeForKey: @"NSContentSize"]; if ([aDecoder containsValueForKey: @"NSDelegate"]) { ASSIGN(_delegate, [aDecoder decodeObjectForKey: @"NSDelegate"]); } _leadingOffset = [aDecoder decodeFloatForKey: @"NSLeadingOffset"]; _maxContentSize = [aDecoder decodeSizeForKey: @"NSMaxContentSize"]; _minContentSize = [aDecoder decodeSizeForKey: @"NSMinContentSize"]; if ([aDecoder containsValueForKey: @"NSParentWindow"]) { parentWindow = [aDecoder decodeObjectForKey: @"NSParentWindow"]; } _preferredEdge = [aDecoder decodeIntForKey: @"NSPreferredEdge"]; _trailingOffset = [aDecoder decodeFloatForKey: @"NSTrailingOffset"]; } else { int version = [aDecoder versionForClassName: @"NSDrawer"]; if (version == 0) { _contentSize = [aDecoder decodeSize]; ASSIGN(_delegate, [aDecoder decodeObject]); [aDecoder decodeValueOfObjCType: @encode(float) at: &_leadingOffset]; _maxContentSize = [aDecoder decodeSize]; _minContentSize = [aDecoder decodeSize]; parentWindow = [aDecoder decodeObject]; [aDecoder decodeValueOfObjCType: @encode(unsigned) at: &_preferredEdge]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_trailingOffset]; } else { [NSException raise: NSInternalInconsistencyException format: @"Invalid version of NSDrawer (version = %d).", version]; return nil; // not reached, but keeps gcc happy... } } // set up drawer... _drawerWindow = [[GSDrawerWindow alloc] initWithContentRect: NSMakeRect(0, 0,_contentSize.width, _contentSize.height) styleMask: 0 backing: NSBackingStoreBuffered defer: NO]; [_drawerWindow setParentWindow: parentWindow]; [_drawerWindow setDrawer: self]; [_drawerWindow setReleasedWhenClosed: NO]; // initial state... _state = NSDrawerClosedState; } return self; } @end