1/** <title>NSDrawer</title> 2 3 <abstract>The drawer class</abstract> 4 5 Copyright (C) 2001 Free Software Foundation, Inc. 6 7 Author: Douglas Simons <doug.simons@testplant.com> 8 Date: 2009 9 Author: Gregory Casamento <greg_casamento@yahoo.com> 10 Date: 2006 11 Author: Fred Kiefer <FredKiefer@gmx.de> 12 Date: 2001 13 14 This file is part of the GNUstep GUI Library. 15 16 This library is free software; you can redistribute it and/or 17 modify it under the terms of the GNU Lesser General Public 18 License as published by the Free Software Foundation; either 19 version 2 of the License, or (at your option) any later version. 20 21 This library is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 Lesser General Public License for more details. 25 26 You should have received a copy of the GNU Lesser General Public 27 License along with this library; see the file COPYING.LIB. 28 If not, see <http://www.gnu.org/licenses/> or write to the 29 Free Software Foundation, 51 Franklin Street, Fifth Floor, 30 Boston, MA 02110-1301, USA. 31*/ 32 33#import <Foundation/NSCoder.h> 34#import <Foundation/NSArchiver.h> 35#import <Foundation/NSKeyedArchiver.h> 36#import <Foundation/NSNotification.h> 37#import <Foundation/NSException.h> 38#import <Foundation/NSThread.h> 39#import <Foundation/NSTimer.h> 40#import "AppKit/NSWindow.h" 41#import "AppKit/NSBox.h" 42#import "AppKit/NSView.h" 43#import "AppKit/NSDrawer.h" 44#import "AppKit/NSGraphics.h" 45 46static NSNotificationCenter *nc = nil; 47 48@interface GSDrawerWindow : NSWindow 49{ 50 NSWindow *_parentWindow; 51 NSWindow *_pendingParentWindow; 52 NSDrawer *_drawer; 53 id _container; 54 NSBox *_borderBox; 55 NSSize _borderSize; 56 NSTimer *_timer; 57 NSRect _latestParentFrame; 58 BOOL _wasOpen; 59} 60- (NSRect) frameFromParentWindowFrameInState:(NSInteger)state; 61 62// open/close 63- (void) openOnEdge; 64- (void) closeOnEdge; 65- (void) slideOpen:(BOOL)opening; 66- (void) startTimer; 67- (void) stopTimer; 68 69// window/drawer properties 70- (void) setParentWindow: (NSWindow *)window; 71- (NSWindow *) parentWindow; 72- (void) setPendingParentWindow: (NSWindow *)window; 73- (NSWindow *) pendingParentWindow; 74- (void) setDrawer: (NSDrawer *)drawer; 75- (NSDrawer *) drawer; 76 77// handle notifications... 78- (void) handleWindowDidBecomeKey: (NSNotification *)notification; 79- (void) handleWindowClose: (NSNotification *)notification; 80- (void) handleWindowMiniaturize: (NSNotification *)notification; 81- (void) handleWindowDeminiaturize: (NSNotification *)notification; 82- (void) handleWindowMove: (NSNotification *)notification; 83@end 84 85@implementation GSDrawerWindow 86+ (void) initialize 87{ 88 if (self == [GSDrawerWindow class]) 89 { 90 nc = [NSNotificationCenter defaultCenter]; 91 [self setVersion: 0]; 92 } 93} 94 95- (id) initWithContentRect: (NSRect)contentRect 96 styleMask: (NSUInteger)aStyle 97 backing: (NSBackingStoreType)bufferingType 98 defer: (BOOL)flag 99{ 100 if(NSIsEmptyRect(contentRect)) 101 { 102 contentRect = NSMakeRect(0,0,100,100); 103 } 104 105 self = [super initWithContentRect: contentRect 106 styleMask: aStyle 107 backing: bufferingType 108 defer: flag]; 109 if (self != nil) 110 { 111 NSRect rect = contentRect; 112 NSRect border = contentRect; 113 NSSize containerContentSize; 114 115 rect.origin.x += 6; 116 rect.origin.y += 6; 117 rect.size.width -= 16; 118 rect.size.height -= 16; 119 120 border.origin.x += 1; 121 border.origin.y += 1; 122 border.size.width -= 2; 123 border.size.height -= 2; 124 125 _borderBox = [[NSBox alloc] initWithFrame: border]; 126 [_borderBox setTitle: @""]; 127 [_borderBox setTitlePosition: NSNoTitle]; 128 [_borderBox setBorderType: NSLineBorder]; 129 [_borderBox setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 130 [_borderBox setContentViewMargins: NSMakeSize(0,0)]; 131 [[super contentView] addSubview: _borderBox]; 132 133 _container = [[NSBox alloc] initWithFrame: rect]; 134 [_container setTitle: @""]; 135 [_container setTitlePosition: NSNoTitle]; 136 [_container setBorderType: NSBezelBorder]; 137 [_container setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 138 [_container setContentViewMargins: NSMakeSize(2,2)]; 139 [_borderBox addSubview: _container]; 140 141 // determine the difference between the container's content size and the window content size 142 containerContentSize = [[_container contentView] frame].size; 143 _borderSize = NSMakeSize(contentRect.size.width - containerContentSize.width, 144 contentRect.size.height - containerContentSize.height); 145 } 146 return self; 147} 148 149- (id) container 150{ 151 return _container; 152} 153 154- (NSRect) frameFromParentWindowFrameInState:(NSInteger)state 155{ 156 NSRect newFrame = [_parentWindow frame]; 157 CGFloat totalOffset = [_drawer leadingOffset] + [_drawer trailingOffset]; 158 NSRectEdge edge = [_drawer preferredEdge]; 159 BOOL opened = (state == NSDrawerOpenState || state == NSDrawerOpeningState); 160 NSSize size = [_parentWindow frame].size; // [_drawer maxContentSize]; 161 NSRect windowContentRect = [[_parentWindow contentView] frame]; 162 CGFloat windowHeightWithoutTitleBar = windowContentRect.origin.y + windowContentRect.size.height; // FIXME: This should probably add the toolbar height too, if the window has a toolbar 163 164 if (edge == NSMinXEdge) // left 165 { 166 if (opened) 167 newFrame.size.width = [_drawer minContentSize].width + _borderSize.width; 168 else 169 newFrame.size.width = 16; 170 171 newFrame.size.height = windowHeightWithoutTitleBar - totalOffset; 172 newFrame.origin.y += [_drawer trailingOffset]; 173 if (opened) 174 newFrame.origin.x -= newFrame.size.width; 175 } 176 else if (edge == NSMinYEdge) // bottom 177 { 178 if (opened) 179 newFrame.size.height = [_drawer minContentSize].height + _borderSize.height; 180 else 181 newFrame.size.height = 16; 182 183 newFrame.size.width -= totalOffset; 184 newFrame.origin.x += [_drawer leadingOffset]; 185 if (opened) 186 newFrame.origin.y -= newFrame.size.height; 187 } 188 else if (edge == NSMaxXEdge) // right 189 { 190 if (opened) 191 newFrame.size.width = [_drawer minContentSize].width + _borderSize.width; 192 else 193 newFrame.size.width = 16; 194 195 newFrame.size.height = windowHeightWithoutTitleBar - totalOffset; 196 newFrame.origin.y += [_drawer trailingOffset]; 197 newFrame.origin.x += size.width; 198 if (!opened) 199 newFrame.origin.x -= newFrame.size.width; 200 } 201 else if (edge == NSMaxYEdge) // top 202 { 203 if (opened) 204 newFrame.size.height = [_drawer minContentSize].height + _borderSize.height; 205 else 206 newFrame.size.height = 16; 207 208 newFrame.size.width -= totalOffset; 209 newFrame.origin.x += [_drawer leadingOffset]; 210 newFrame.origin.y += size.height; // put above the window 211 if (!opened) 212 newFrame.origin.y -= newFrame.size.height; 213 } 214 215 return newFrame; 216} 217 218 219 220- (BOOL) canBecomeKeyWindow 221{ 222 return YES; 223} 224 225- (BOOL) canBecomeMainWindow 226{ 227 return NO; 228} 229 230- (void) becomeKeyWindow 231{ 232 [_parentWindow orderFrontRegardless]; // so clicking on the drawer will bring the parent to the front 233 [super becomeKeyWindow]; 234} 235 236/* 237- (void) orderFront: (id)sender 238{ 239 NSPoint holdOrigin = [self frame].origin; 240 NSPoint newOrigin = NSMakePoint(-10000,-10000); 241 NSRect tempFrame = [self frame]; 242 243 // order the window under the parent... 244 tempFrame.origin = newOrigin; 245 [self setFrame: tempFrame display: NO]; 246 [super orderFront: sender]; 247 // [_parentWindow orderWindow: NSWindowAbove relativeTo: [self windowNumber]]; 248 tempFrame.origin = holdOrigin; 249 // [self setFrame: tempFrame display: YES]; 250} 251*/ 252 253/* 254- (void) orderFront: (id)sender 255{ 256 [super orderWindow:NSWindowBelow relativeTo:[_parentWindow windowNumber]]; 257} 258*/ 259 260- (void) startTimer 261{ 262 NSTimeInterval time = 0.1; 263 _timer = [NSTimer scheduledTimerWithTimeInterval: time 264 target: self 265 selector: @selector(_timedWindowReset) 266 userInfo: nil 267 repeats: YES]; 268} 269 270- (void) stopTimer 271{ 272 [_timer invalidate]; 273 _timer = nil; 274} 275 276- (void) orderFrontRegardless 277{ 278 [self orderFront: self]; 279} 280 281- (void) orderOut: (id)sender 282{ 283 [super orderOut: sender]; 284} 285 286/* 287- (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (int)windowNum 288{ 289 NSLog(@"Ordering window...."); 290 [super orderWindow: place relativeTo: windowNum]; 291} 292*/ 293 294- (void) lockBorderBoxForSliding 295{ 296 // set the _borderBox to not resize during the slide, and attach it to the appropriate edge instead 297 NSRectEdge edge = [_drawer preferredEdge]; 298 NSUInteger resizeMask = 0; 299 if (edge == NSMinXEdge) // left 300 { 301 resizeMask = NSViewMaxXMargin; 302 } 303 else if (edge == NSMinYEdge) // bottom 304 { 305 resizeMask = NSViewMaxYMargin; 306 } 307 else if (edge == NSMaxXEdge) // right 308 { 309 resizeMask = NSViewMinXMargin; 310 } 311 else if (edge == NSMaxYEdge) // top 312 { 313 resizeMask = NSViewMinYMargin; 314 } 315 [_borderBox setAutoresizingMask: resizeMask]; // don't resize -- just give appearance of sliding 316} 317 318- (void) unlockBorderBoxAfterSliding 319{ 320 // set the _borderBox to resize again 321 [_borderBox setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 322} 323 324- (void) openOnEdge 325{ 326 // prepare drawer contents before sliding... 327 NSRect frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState]; 328 [self setFrame:frame display: YES]; // make sure it's the full (open) size before locking the borderBox 329 if ([_parentWindow isVisible]) // don't order front until parent window is visible 330 { 331 [self lockBorderBoxForSliding]; 332 [self orderFront: self]; 333 [self slideOpen:YES]; 334 [self performSelector:@selector(unlockBorderBoxAfterSliding) withObject:nil afterDelay:0.01]; 335 } 336 [self startTimer]; 337} 338 339- (void) closeOnEdge 340{ 341 NSRect frame; 342 343 [self stopTimer]; 344 [self lockBorderBoxForSliding]; 345 [self slideOpen:NO]; 346 [self orderOut: self]; 347 348 frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState]; 349 [self setFrame:frame display: YES]; // make sure it's the full (open) size again (offscreen) before unlocking 350 [self performSelector:@selector(unlockBorderBoxAfterSliding) withObject:nil afterDelay:0.01]; 351 352 if (_pendingParentWindow != nil 353 && _pendingParentWindow != _parentWindow) 354 { 355 [self setParentWindow: _pendingParentWindow]; 356 ASSIGN(_pendingParentWindow, nil); 357 } 358} 359 360- (void) slideOpen:(BOOL)opening 361{ 362 NSRect frame = [self frameFromParentWindowFrameInState:(opening?NSDrawerClosedState:NSDrawerOpenState)]; 363 NSRect newFrame = [self frameFromParentWindowFrameInState:(opening?NSDrawerOpenState:NSDrawerClosedState)]; 364 NSTimeInterval slideDelay = 0.03; 365 NSDate *nextStop = [NSDate dateWithTimeIntervalSinceNow:slideDelay]; 366 int count = 10; 367 CGFloat deltaX = (newFrame.origin.x - frame.origin.x) / count; 368 CGFloat deltaY = (newFrame.origin.y - frame.origin.y) / count; 369 CGFloat deltaW = (newFrame.size.width - frame.size.width) / count; 370 CGFloat deltaH = (newFrame.size.height - frame.size.height) / count; 371 while (count--) 372 { 373 frame.origin.x += deltaX; 374 frame.origin.y += deltaY; 375 frame.size.width += deltaW; 376 frame.size.height += deltaH; 377 [self setFrame: frame display: YES]; 378 [NSThread sleepUntilDate:nextStop]; 379 nextStop = [nextStop addTimeInterval:slideDelay]; 380 } 381 [self setFrame:newFrame display: YES]; 382} 383 384- (void) _resetWindowPosition 385{ 386 if ([_parentWindow isVisible]) // don't set our frame until parent window is visible 387 { 388 NSRect frame = [self frameFromParentWindowFrameInState:[_drawer state]]; 389 [self setFrame: frame display: YES]; 390 if (![self isVisible] && [_drawer state] != NSDrawerClosedState) 391 [self orderFront:self]; 392 } 393 if ([self isVisible] && [_parentWindow isKeyWindow]) // do our best to maintain proper window ordering 394 { 395 [super orderFrontRegardless]; 396 [_parentWindow orderFront:self]; 397 } 398} 399 400- (void) _timedWindowReset 401{ 402 NSRect frame = [_parentWindow frame]; 403 if (!NSEqualRects(frame, _latestParentFrame)) 404 { 405 [self _resetWindowPosition]; 406 _latestParentFrame = frame; 407 } 408} 409 410- (void) handleWindowClose: (NSNotification *)notification 411{ 412 [self stopTimer]; 413 [self close]; 414} 415 416- (void) handleWindowMiniaturize: (NSNotification *)notification 417{ 418 _wasOpen = ([_drawer state] == NSDrawerOpenState); 419 if (_wasOpen) 420 { 421 // It would be nice to call [self closeOnEdge] here, but the parent window is already closing 422 // (with animation) so it doesn't look right to slide the drawer. So we'll do this instead: 423 [self stopTimer]; 424 [self close]; 425 } 426} 427 428- (void) handleWindowDeminiaturize: (NSNotification *)notification 429{ 430 if (_wasOpen) 431 { 432 //[self openOnEdge]; -- this also doesn't work: see comment above 433 _latestParentFrame = NSMakeRect(0,0,0,0); 434 [self startTimer]; 435 } 436} 437 438- (void) handleWindowMove: (NSNotification *)notification 439{ 440 [self _resetWindowPosition]; 441} 442 443- (void) handleWindowDidBecomeKey: (NSNotification *)notification 444{ 445 if([_drawer state] == NSDrawerOpenState) 446 { 447 [self _resetWindowPosition]; 448 } 449} 450 451- (void) setParentWindow: (NSWindow *)window 452{ 453 if (_parentWindow != window) 454 { 455 [super setParentWindow: window]; 456 ASSIGN(_parentWindow, window); 457 [nc removeObserver: self]; 458 459 if (_parentWindow != nil) 460 { 461 [self _resetWindowPosition]; 462 463 // add observers.... 464 [nc addObserver: self 465 selector: @selector(handleWindowClose:) 466 name: NSWindowWillCloseNotification 467 object: _parentWindow]; 468 469 [nc addObserver: self 470 selector: @selector(handleWindowMiniaturize:) 471 name: NSWindowWillMiniaturizeNotification 472 object: _parentWindow]; 473 474 [nc addObserver: self 475 selector: @selector(handleWindowDeminiaturize:) 476 name: NSWindowDidDeminiaturizeNotification 477 object: _parentWindow]; 478 479 [nc addObserver: self 480 selector: @selector(handleWindowMove:) 481 name: NSWindowWillMoveNotification 482 object: _parentWindow]; 483 484 [nc addObserver: self 485 selector: @selector(handleWindowMove:) 486 name: NSWindowDidResizeNotification 487 object: _parentWindow]; 488 489 [nc addObserver: self 490 selector: @selector(handleWindowDidBecomeKey:) 491 name: NSWindowDidBecomeKeyNotification 492 object: _parentWindow]; 493 } 494 } 495} 496 497- (NSWindow *) parentWindow 498{ 499 return _parentWindow; 500} 501 502- (void) setPendingParentWindow: (NSWindow *)window 503{ 504 ASSIGN(_pendingParentWindow, window); 505} 506 507- (NSWindow *) pendingParentWindow 508{ 509 return _pendingParentWindow; 510} 511 512- (void) setDrawer: (NSDrawer *)drawer 513{ 514 // don't retain, since the drawer retains us... 515 _drawer = drawer; 516} 517 518- (NSDrawer *) drawer 519{ 520 return _drawer; 521} 522 523- (void) dealloc 524{ 525 [self stopTimer]; 526 RELEASE(_parentWindow); 527 RELEASE(_borderBox); 528 TEST_RELEASE(_pendingParentWindow); 529 [super dealloc]; 530} 531@end 532 533@implementation NSDrawer 534 535+ (void) initialize 536{ 537 if (self == [NSDrawer class]) 538 { 539 nc = [NSNotificationCenter defaultCenter]; 540 [self setVersion: 0]; 541 } 542} 543 544// Creation 545- (id) init 546{ 547 return [self initWithContentSize: NSMakeSize(100,100) 548 preferredEdge: NSMinXEdge]; 549} 550 551- (id) initWithContentSize: (NSSize)contentSize 552 preferredEdge: (NSRectEdge)edge 553{ 554 self = [super init]; 555 // initialize the drawer window... 556 _drawerWindow = [[GSDrawerWindow alloc] 557 initWithContentRect: NSMakeRect(0, 0, contentSize.width, 558 contentSize.height) 559 styleMask: 0 560 backing: NSBackingStoreBuffered 561 defer: NO]; 562 [_drawerWindow setDrawer: self]; 563 [_drawerWindow setReleasedWhenClosed: NO]; 564 565 _preferredEdge = edge; 566 _currentEdge = edge; 567 _maxContentSize = NSMakeSize(200,200); 568 _minContentSize = contentSize; //NSMakeSize(50,50); 569 if (edge == NSMinXEdge || edge == NSMaxXEdge) { 570 _leadingOffset = 0.0; // for side drawers, top of drawer is immediately below the title bar 571 _trailingOffset = 10.0; 572 } else { 573 _leadingOffset = 10.0; 574 _trailingOffset = 10.0; 575 } 576 _state = NSDrawerClosedState; 577 578 return self; 579} 580 581- (void) dealloc 582{ 583 RELEASE(_drawerWindow); 584 if (_delegate != nil) 585 { 586 [nc removeObserver: _delegate name: nil object: self]; 587 _delegate = nil; 588 } 589 [super dealloc]; 590} 591 592// Opening and Closing 593- (void) close 594{ 595 if (_state != NSDrawerOpenState) 596 return; 597 598 if ((_delegate != nil) 599 && ([_delegate respondsToSelector: @selector(drawerShouldClose:)]) 600 && ![_delegate drawerShouldClose: self]) 601 return; 602 603 _state = NSDrawerClosingState; 604 [nc postNotificationName: NSDrawerWillCloseNotification object: self]; 605 606 [_drawerWindow closeOnEdge]; 607 608 _state = NSDrawerClosedState; 609 [nc postNotificationName: NSDrawerDidCloseNotification object: self]; 610} 611 612- (void) close: (id)sender 613{ 614 [self close]; 615} 616 617- (void) open 618{ 619 [self openOnEdge: _preferredEdge]; 620} 621 622- (void) open: (id)sender 623{ 624 [self open]; 625} 626 627- (void) openOnEdge: (NSRectEdge)edge 628{ 629 if ((_state != NSDrawerClosedState) 630 || ([self parentWindow] == nil)) 631 return; 632 633 if ((_delegate != nil) 634 && ([_delegate respondsToSelector: @selector(drawerShouldOpen:)]) 635 && ![_delegate drawerShouldOpen: self]) 636 return; 637 638 _state = NSDrawerOpeningState; 639 [nc postNotificationName: NSDrawerWillOpenNotification object: self]; 640 641 _currentEdge = edge; 642 [_drawerWindow openOnEdge]; 643 644 _state = NSDrawerOpenState; 645 [nc postNotificationName: NSDrawerDidOpenNotification object: self]; 646} 647 648- (void) toggle: (id)sender 649{ 650 if (_state == NSDrawerClosedState) 651 [self open: sender]; 652 else if (_state == NSDrawerOpenState) 653 [self close: sender]; 654 // Do nothing for inbetween states 655} 656 657// Managing Size 658- (NSSize) contentSize 659{ 660 return [[_drawerWindow contentView] frame].size; 661} 662 663- (CGFloat) leadingOffset 664{ 665 return _leadingOffset; 666} 667 668- (NSSize) maxContentSize 669{ 670 return _maxContentSize; 671} 672 673- (NSSize) minContentSize 674{ 675 return _minContentSize; 676} 677 678- (void) setContentSize: (NSSize)size 679{ 680 // Check with min and max size 681 if (size.width < _minContentSize.width) 682 size.width = _minContentSize.width; 683 if (size.height < _minContentSize.height) 684 size.height = _minContentSize.height; 685 if (size.width > _maxContentSize.width) 686 size.width = _maxContentSize.width; 687 if (size.height > _maxContentSize.height) 688 size.height = _maxContentSize.height; 689 690 // Check with delegate 691 if ((_delegate != nil) 692 && ([_delegate respondsToSelector: 693 @selector(drawerWillResizeContents:toSize:)])) 694 { 695 size = [_delegate drawerWillResizeContents: self 696 toSize: size]; 697 } 698 699 [_drawerWindow setContentSize: size]; 700} 701 702- (void) setLeadingOffset: (CGFloat)offset 703{ 704 _leadingOffset = offset; 705} 706 707- (void) setMaxContentSize: (NSSize)size 708{ 709 _maxContentSize = size; 710} 711 712- (void) setMinContentSize: (NSSize)size 713{ 714 _minContentSize = size; 715} 716 717- (void) setTrailingOffset: (CGFloat)offset 718{ 719 _trailingOffset = offset; 720} 721 722- (CGFloat) trailingOffset 723{ 724 return _trailingOffset; 725} 726 727// Managing Edge 728- (NSRectEdge) edge 729{ 730 return _currentEdge; 731} 732 733- (NSRectEdge) preferredEdge 734{ 735 return _preferredEdge; 736} 737 738- (void) setPreferredEdge: (NSRectEdge)preferredEdge 739{ 740 _preferredEdge = preferredEdge; 741} 742 743// Managing Views 744- (NSView *) contentView 745{ 746 return [[_drawerWindow container] contentView]; 747} 748 749- (NSWindow *) parentWindow 750{ 751 return [_drawerWindow parentWindow]; 752} 753 754- (void) setContentView: (NSView *)aView 755{ 756 [[_drawerWindow container] setContentView: aView]; 757} 758 759- (void) setParentWindow: (NSWindow *)parent 760{ 761 if (_state == NSDrawerClosedState) 762 { 763 [_drawerWindow setParentWindow: parent]; 764 } 765 else 766 { 767 [_drawerWindow setPendingParentWindow: parent]; 768 } 769} 770 771 772// Delegation and State 773- (id) delegate 774{ 775 return _delegate; 776} 777 778- (void) setDelegate: (id)anObject 779{ 780 if (_delegate) 781 { 782 [nc removeObserver: _delegate name: nil object: self]; 783 } 784 785 _delegate = anObject; 786 787#define SET_DELEGATE_NOTIFICATION(notif_name) \ 788 if ([_delegate respondsToSelector: @selector(drawer##notif_name:)]) \ 789 [nc addObserver: _delegate \ 790 selector: @selector(drawer##notif_name:) \ 791 name: NSDrawer##notif_name##Notification object: self] 792 793 SET_DELEGATE_NOTIFICATION(DidClose); 794 SET_DELEGATE_NOTIFICATION(DidOpen); 795 SET_DELEGATE_NOTIFICATION(WillClose); 796 SET_DELEGATE_NOTIFICATION(WillOpen); 797} 798 799- (NSInteger) state 800{ 801 return _state; 802} 803 804/* 805 * NSCoding protocol 806 */ 807- (void) encodeWithCoder: (NSCoder*)aCoder 808{ 809 id parent = [self parentWindow]; 810 811 [super encodeWithCoder: aCoder]; 812 if ([aCoder allowsKeyedCoding]) 813 { 814 [aCoder encodeSize: [self contentSize] forKey: @"NSContentSize"]; 815 816 if (_delegate != nil) 817 { 818 [aCoder encodeObject: _delegate forKey: @"NSDelegate"]; 819 } 820 821 [aCoder encodeFloat: _leadingOffset forKey: @"NSLeadingOffset"]; 822 [aCoder encodeSize: _maxContentSize forKey: @"NSMaxContentSize"]; 823 [aCoder encodeSize: _minContentSize forKey: @"NSMinContentSize"]; 824 825 if (parent != nil) 826 { 827 [aCoder encodeObject: parent forKey: @"NSParentWindow"]; 828 } 829 830 [aCoder encodeInt: _preferredEdge forKey: @"NSPreferredEdge"]; 831 [aCoder encodeFloat: _trailingOffset forKey: @"NSTrailingOffset"]; 832 } 833 else 834 { 835 [aCoder encodeSize: [self contentSize]]; 836 [aCoder encodeObject: _delegate]; 837 [aCoder encodeValueOfObjCType: @encode(float) at: &_leadingOffset]; 838 [aCoder encodeSize: _maxContentSize]; 839 [aCoder encodeSize: _minContentSize]; 840 [aCoder encodeObject: parent]; 841 [aCoder encodeValueOfObjCType: @encode(unsigned) at: &_preferredEdge]; 842 [aCoder encodeValueOfObjCType: @encode(float) at: &_trailingOffset]; 843 } 844} 845 846- (id) initWithCoder: (NSCoder*)aDecoder 847{ 848 if ((self = [super initWithCoder: aDecoder]) != nil) 849 { 850 NSWindow *parentWindow = nil; 851 852 if ([aDecoder allowsKeyedCoding]) 853 { 854 _contentSize = [aDecoder decodeSizeForKey: @"NSContentSize"]; 855 856 if ([aDecoder containsValueForKey: @"NSDelegate"]) 857 { 858 ASSIGN(_delegate, [aDecoder decodeObjectForKey: @"NSDelegate"]); 859 } 860 861 _leadingOffset = [aDecoder decodeFloatForKey: @"NSLeadingOffset"]; 862 _maxContentSize = [aDecoder decodeSizeForKey: @"NSMaxContentSize"]; 863 _minContentSize = [aDecoder decodeSizeForKey: @"NSMinContentSize"]; 864 865 if ([aDecoder containsValueForKey: @"NSParentWindow"]) 866 { 867 parentWindow = [aDecoder decodeObjectForKey: @"NSParentWindow"]; 868 } 869 870 _preferredEdge = [aDecoder decodeIntForKey: @"NSPreferredEdge"]; 871 _trailingOffset = [aDecoder decodeFloatForKey: @"NSTrailingOffset"]; 872 } 873 else 874 { 875 int version = [aDecoder versionForClassName: @"NSDrawer"]; 876 if (version == 0) 877 { 878 _contentSize = [aDecoder decodeSize]; 879 ASSIGN(_delegate, [aDecoder decodeObject]); 880 [aDecoder decodeValueOfObjCType: @encode(float) 881 at: &_leadingOffset]; 882 _maxContentSize = [aDecoder decodeSize]; 883 _minContentSize = [aDecoder decodeSize]; 884 parentWindow = [aDecoder decodeObject]; 885 [aDecoder decodeValueOfObjCType: @encode(unsigned) 886 at: &_preferredEdge]; 887 [aDecoder decodeValueOfObjCType: @encode(float) 888 at: &_trailingOffset]; 889 } 890 else 891 { 892 [NSException raise: NSInternalInconsistencyException 893 format: @"Invalid version of NSDrawer (version = %d).", 894 version]; 895 return nil; // not reached, but keeps gcc happy... 896 } 897 } 898 899 // set up drawer... 900 _drawerWindow = [[GSDrawerWindow alloc] 901 initWithContentRect: 902 NSMakeRect(0, 0,_contentSize.width, 903 _contentSize.height) 904 styleMask: 0 905 backing: NSBackingStoreBuffered 906 defer: NO]; 907 [_drawerWindow setParentWindow: parentWindow]; 908 [_drawerWindow setDrawer: self]; 909 [_drawerWindow setReleasedWhenClosed: NO]; 910 911 // initial state... 912 _state = NSDrawerClosedState; 913 914 } 915 return self; 916} 917 918@end 919