Exit full-screen if the window moves
[MacVim.git] / src / MacVim / DBPrefsWindowController.m
blobb58696fe1bc8df529253133beab87429f255ca32
1 //
2 //  DBPrefsWindowController.m
3 //
5 #import "DBPrefsWindowController.h"
8 static DBPrefsWindowController *_sharedPrefsWindowController = nil;
11 @implementation DBPrefsWindowController
16 #pragma mark -
17 #pragma mark Class Methods
20 + (DBPrefsWindowController *)sharedPrefsWindowController
22         if (!_sharedPrefsWindowController) {
23                 _sharedPrefsWindowController = [[self alloc] initWithWindowNibName:[self nibName]];
24         }
25         return _sharedPrefsWindowController;
31 + (NSString *)nibName
32         // Subclasses can override this to use a nib with a different name.
34    return @"Preferences";
40 #pragma mark -
41 #pragma mark Setup & Teardown
44 - (id)initWithWindow:(NSWindow *)window
45   // -initWithWindow: is the designated initializer for NSWindowController.
47         self = [super initWithWindow:nil];
48         if (self != nil) {
49                         // Set up an array and some dictionaries to keep track
50                         // of the views we'll be displaying.
51                 toolbarIdentifiers = [[NSMutableArray alloc] init];
52                 toolbarViews = [[NSMutableDictionary alloc] init];
53                 toolbarItems = [[NSMutableDictionary alloc] init];
55                         // Set up an NSViewAnimation to animate the transitions.
56                 viewAnimation = [[NSViewAnimation alloc] init];
57                 [viewAnimation setAnimationBlockingMode:NSAnimationNonblocking];
58                 [viewAnimation setAnimationCurve:NSAnimationEaseInOut];
59                 [viewAnimation setDelegate:self];
60                 
61                 [self setCrossFade:YES]; 
62                 [self setShiftSlowsAnimation:YES];
63         }
64         return self;
66         (void)window;  // To prevent compiler warnings.
72 - (void)windowDidLoad
74                 // Create a new window to display the preference views.
75                 // If the developer attached a window to this controller
76                 // in Interface Builder, it gets replaced with this one.
77         NSPanel *window = [[[NSPanel alloc] initWithContentRect:NSMakeRect(0,0,1000,1000)
78                                                                                                     styleMask:(NSTitledWindowMask |
79                                                                                                                            NSClosableWindowMask)
80                                                                                                           backing:NSBackingStoreBuffered
81                                                                                                             defer:YES] autorelease];
82         [window setHidesOnDeactivate:NO];
83         [self setWindow:window];
84         contentSubview = [[[NSView alloc] initWithFrame:[[[self window] contentView] frame]] autorelease];
85         [contentSubview setAutoresizingMask:(NSViewMinYMargin | NSViewWidthSizable)];
86         [[[self window] contentView] addSubview:contentSubview];
87         [[self window] setShowsToolbarButton:NO];
93 - (void) dealloc {
94         [toolbarIdentifiers release];
95         [toolbarViews release];
96         [toolbarItems release];
97         [viewAnimation release];
98         [super dealloc];
104 #pragma mark -
105 #pragma mark Configuration
108 - (void)setupToolbar
110         // Subclasses must override this method to add items to the
111         // toolbar by calling -addView:label: or -addView:label:image:.
117 - (void)addView:(NSView *)view label:(NSString *)label
119         [self addView:view
120                         label:label
121                         image:[NSImage imageNamed:label]];
127 - (void)addView:(NSView *)view label:(NSString *)label image:(NSImage *)image
129         NSAssert (view != nil,
130                           @"Attempted to add a nil view when calling -addView:label:image:.");
131         
132         NSString *identifier = [[label copy] autorelease];
133         
134         [toolbarIdentifiers addObject:identifier];
135         [toolbarViews setObject:view forKey:identifier];
136         
137         NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier:identifier] autorelease];
138         [item setLabel:label];
139         [item setImage:image];
140         [item setTarget:self];
141         [item setAction:@selector(toggleActivePreferenceView:)];
142         
143         [toolbarItems setObject:item forKey:identifier];
149 #pragma mark -
150 #pragma mark Accessor Methods
153 - (BOOL)crossFade
155         return _crossFade;
161 - (void)setCrossFade:(BOOL)fade
163         _crossFade = fade;
169 - (BOOL)shiftSlowsAnimation
171         return _shiftSlowsAnimation;
177 - (void)setShiftSlowsAnimation:(BOOL)slows
179         _shiftSlowsAnimation = slows;
185 - (NSString *)currentPaneIdentifier
186         // Subclasses can override this to persist the current preference pane.
188         return currentPaneIdentifier;
194 - (void)setCurrentPaneIdentifier:(NSString *)identifier
195         // Subclasses can override this to persist the current preference pane.
197         currentPaneIdentifier = identifier;
203 #pragma mark -
204 #pragma mark Overriding Methods
207 - (IBAction)showWindow:(id)sender 
209                 // This forces the resources in the nib to load.
210         (void)[self window];
212                 // Clear the last setup and get a fresh one.
213         [toolbarIdentifiers removeAllObjects];
214         [toolbarViews removeAllObjects];
215         [toolbarItems removeAllObjects];
216         [self setupToolbar];
218         NSAssert (([toolbarIdentifiers count] > 0),
219                           @"No items were added to the toolbar in -setupToolbar.");
220         
221         if ([[self window] toolbar] == nil) {
222                 NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"DBPreferencesToolbar"];
223                 [toolbar setAllowsUserCustomization:NO];
224                 [toolbar setAutosavesConfiguration:NO];
225                 [toolbar setSizeMode:NSToolbarSizeModeDefault];
226                 [toolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel];
227                 [toolbar setDelegate:self];
228                 [[self window] setToolbar:toolbar];
229                 [toolbar release];
230         }
231         
232         if ([toolbarItems objectForKey:[self currentPaneIdentifier]] == nil) {
233                 [self setCurrentPaneIdentifier:[toolbarIdentifiers objectAtIndex:0]];
234         }
235         [[[self window] toolbar]
236                 setSelectedItemIdentifier:[self currentPaneIdentifier]];
237         [self displayViewForIdentifier:[self currentPaneIdentifier] animate:NO];
238         
239         [[self window] center];
241         [super showWindow:sender];
247 #pragma mark -
248 #pragma mark Toolbar
251 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
253         return toolbarIdentifiers;
255         (void)toolbar;
261 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar 
263         return toolbarIdentifiers;
265         (void)toolbar;
271 - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
273         return toolbarIdentifiers;
274         (void)toolbar;
280 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)identifier willBeInsertedIntoToolbar:(BOOL)willBeInserted 
282         return [toolbarItems objectForKey:identifier];
283         (void)toolbar;
284         (void)willBeInserted;
290 - (void)toggleActivePreferenceView:(NSToolbarItem *)toolbarItem
292         [self displayViewForIdentifier:[toolbarItem itemIdentifier] animate:YES];
293         [self setCurrentPaneIdentifier:[toolbarItem itemIdentifier]];
299 - (void)displayViewForIdentifier:(NSString *)identifier animate:(BOOL)animate
300 {       
301                 // Find the view we want to display.
302         NSView *newView = [toolbarViews objectForKey:identifier];
304                 // See if there are any visible views.
305         NSView *oldView = nil;
306         if ([[contentSubview subviews] count] > 0) {
307                         // Get a list of all of the views in the window. Usually at this
308                         // point there is just one visible view. But if the last fade
309                         // hasn't finished, we need to get rid of it now before we move on.
310                 NSEnumerator *subviewsEnum = [[contentSubview subviews] reverseObjectEnumerator];
311                 
312                         // The first one (last one added) is our visible view.
313                 oldView = [subviewsEnum nextObject];
314                 
315                         // Remove any others.
316                 NSView *reallyOldView = nil;
317                 while ((reallyOldView = [subviewsEnum nextObject]) != nil) {
318                         [reallyOldView removeFromSuperviewWithoutNeedingDisplay];
319                 }
320         }
321         
322         if (![newView isEqualTo:oldView]) {             
323                 NSRect frame = [newView bounds];
324                 frame.origin.y = NSHeight([contentSubview frame]) - NSHeight([newView bounds]);
325                 [newView setFrame:frame];
326                 [contentSubview addSubview:newView];
327                 [[self window] setInitialFirstResponder:newView];
329                 if (animate && [self crossFade])
330                         [self crossFadeView:oldView withView:newView];
331                 else {
332                         [oldView removeFromSuperviewWithoutNeedingDisplay];
333                         [newView setHidden:NO];
334                         [[self window] setFrame:[self frameForView:newView] display:YES animate:animate];
335                 }
336                 
337                 [[self window] setTitle:[[toolbarItems objectForKey:identifier] label]];
338         }
344 #pragma mark -
345 #pragma mark Cross-Fading Methods
348 - (void)crossFadeView:(NSView *)oldView withView:(NSView *)newView
350         [viewAnimation stopAnimation];
351         
352         if ([self shiftSlowsAnimation] && [[[self window] currentEvent] modifierFlags] & NSShiftKeyMask)
353                 [viewAnimation setDuration:1.25];
354         else
355                 [viewAnimation setDuration:0.25];
356         
357         NSDictionary *fadeOutDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
358                 oldView, NSViewAnimationTargetKey,
359                 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
360                 nil];
362         NSDictionary *fadeInDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
363                 newView, NSViewAnimationTargetKey,
364                 NSViewAnimationFadeInEffect, NSViewAnimationEffectKey,
365                 nil];
367         NSDictionary *resizeDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
368                 [self window], NSViewAnimationTargetKey,
369                 [NSValue valueWithRect:[[self window] frame]], NSViewAnimationStartFrameKey,
370                 [NSValue valueWithRect:[self frameForView:newView]], NSViewAnimationEndFrameKey,
371                 nil];
372         
373         NSArray *animationArray = [NSArray arrayWithObjects:
374                 fadeOutDictionary,
375                 fadeInDictionary,
376                 resizeDictionary,
377                 nil];
378         
379         [viewAnimation setViewAnimations:animationArray];
380         [viewAnimation startAnimation];
386 - (void)animationDidEnd:(NSAnimation *)animation
388         NSView *subview;
389         
390                 // Get a list of all of the views in the window. Hopefully
391                 // at this point there are two. One is visible and one is hidden.
392         NSEnumerator *subviewsEnum = [[contentSubview subviews] reverseObjectEnumerator];
393         
394                 // This is our visible view. Just get past it.
395         subview = [subviewsEnum nextObject];
397                 // Remove everything else. There should be just one, but
398                 // if the user does a lot of fast clicking, we might have
399                 // more than one to remove.
400         while ((subview = [subviewsEnum nextObject]) != nil) {
401                 [subview removeFromSuperviewWithoutNeedingDisplay];
402         }
404                 // This is a work-around that prevents the first
405                 // toolbar icon from becoming highlighted.
406         [[self window] makeFirstResponder:nil];
408         (void)animation;
414 - (NSRect)frameForView:(NSView *)view
415         // Calculate the window size for the new view.
417         NSRect windowFrame = [[self window] frame];
418         NSRect contentRect = [[self window] contentRectForFrameRect:windowFrame];
419         float windowTitleAndToolbarHeight = NSHeight(windowFrame) - NSHeight(contentRect);
421         windowFrame.size.height = NSHeight([view frame]) + windowTitleAndToolbarHeight;
422         windowFrame.size.width = NSWidth([view frame]);
423         windowFrame.origin.y = NSMaxY([[self window] frame]) - NSHeight(windowFrame);
424         
425         return windowFrame;
431 @end