Improved method to start Vim processes in a login shell
[MacVim.git] / src / MacVim / MMFullscreenWindow.m
blobf4b0e5c9c1631dd049284eafbd43d03eaa7fa376
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
11  * MMFullscreenWindow
12  *
13  * A window without any decorations which covers an entire screen.
14  *
15  * When entering full-screen mode the window controller is set to control an
16  * instance of this class instead of an MMWindow.  (This seems to work fine
17  * even though the Apple docs state that it is generally a better idea to
18  * create a separate window controller for each window.)
19  *
20  * Most of the full-screen logic is currently in this class although it might
21  * move to the window controller in the future.
22  *
23  * Author: Nico Weber
24  */
26 #import "MMFullscreenWindow.h"
27 #import <PSMTabBarControl.h>
28 #import "MMVimView.h"
29 #import "MMTextView.h"
30 #import "MMWindowController.h"
31 #import <Carbon/Carbon.h>
34 static int numFullscreenWindows = 0;
36 @interface MMFullscreenWindow (Private)
37 - (BOOL)isOnPrimaryScreen;
38 - (void)hideDockIfAppropriate;
39 - (void)revealDockIfAppropriate;
40 @end
42 @implementation MMFullscreenWindow
44 - (MMFullscreenWindow *)initWithWindow:(NSWindow *)t view:(MMVimView *)v
46     NSScreen* screen = [t screen];
48     // XXX: what if screen == nil?
50     // you can't change the style of an existing window in cocoa. create a new
51     // window and move the MMTextView into it.
52     // (another way would be to make the existing window large enough that the
53     // title bar is off screen. but that doesn't work with multiple screens).  
54     self = [super initWithContentRect:[screen frame]
55                             styleMask:NSBorderlessWindowMask
56                               backing:NSBackingStoreBuffered
57                                 defer:YES
58                                // since we're passing [screen frame] above,
59                                // we want the content rect to be relative to
60                                // the main screen (ie, pass nil for screen).
61                                screen:nil];
62       
63     if (self == nil)
64         return nil;
66     target = [t retain];
67     view = [v retain];
69     [self setHasShadow:NO];
70     [self setShowsResizeIndicator:NO];
71     [self setBackgroundColor:[NSColor blackColor]];
72     [self setReleasedWhenClosed:NO];
74     return self;
77 - (void)dealloc
79     [target release];  target = nil;
80     [view release];  view = nil;
82     [super dealloc];
85 - (void)enterFullscreen
87     [self hideDockIfAppropriate];
89     // fade to black
90     Boolean didBlend = NO;
91     CGDisplayFadeReservationToken token;
92     if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
93         CGDisplayFade(token, .25, kCGDisplayBlendNormal,
94             kCGDisplayBlendSolidColor, .0, .0, .0, true);
95         didBlend = YES;
96     }
97     
98     // fool delegate
99     id delegate = [target delegate];
100     [target setDelegate:nil];
101     
102     // make target's window controller believe that it's now controlling us
103     [[target windowController] setWindow:self];
105     oldTabBarStyle = [[view tabBarControl] styleName];
106     [[view tabBarControl] setStyleNamed:@"Unified"];
108     // add text view
109     oldPosition = [view frame].origin;
111     [view removeFromSuperviewWithoutNeedingDisplay];
112     [[self contentView] addSubview:view];
113     [self setInitialFirstResponder:[view textView]];
114     
115     // NOTE: Calling setTitle:nil causes an exception to be raised (and it is
116     // possible that 'target' has no title when we get here).
117     if ([target title])
118         [self setTitle:[target title]];
120     [self setOpaque:[target isOpaque]];
122     // don't set this sooner, so we don't get an additional
123     // focus gained message  
124     [self setDelegate:delegate];
126     // move vim view to the window's center
127     [self centerView];
129     // make us visible and target invisible
130     [target orderOut:self];
131     [self makeKeyAndOrderFront:self];
133     // fade back in
134     if (didBlend) {
135         CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
136             kCGDisplayBlendNormal, .0, .0, .0, false);
137         CGReleaseDisplayFadeReservation(token);
138     }
141 - (void)leaveFullscreen
143     // fade to black
144     Boolean didBlend = NO;
145     CGDisplayFadeReservationToken token;
146     if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
147         CGDisplayFade(token, .25, kCGDisplayBlendNormal,
148             kCGDisplayBlendSolidColor, .0, .0, .0, true);
149         didBlend = YES;
150     }
152     // fix up target controller
153     [self retain];  // NSWindowController releases us once
154     [[self windowController] setWindow:target];
156     [[view tabBarControl] setStyleNamed:oldTabBarStyle];
158     // fix delegate
159     id delegate = [self delegate];
160     [self setDelegate:nil];
161     
162     // move text view back to original window, hide fullscreen window,
163     // show original window
164     // do this _after_ resetting delegate and window controller, so the
165     // window controller doesn't get a focus lost message from the fullscreen
166     // window.
167     [view removeFromSuperviewWithoutNeedingDisplay];
168     [[target contentView] addSubview:view];
169     [view setFrameOrigin:oldPosition];
170     [self close];
172     // Set the text view to initial first responder, otherwise the 'plus'
173     // button on the tabline steals the first responder status.
174     [target setInitialFirstResponder:[view textView]];
176     [target makeKeyAndOrderFront:self];
178     // ...but we don't want a focus gained message either, so don't set this
179     // sooner
180     [target setDelegate:delegate];
182     // fade back in  
183     if (didBlend) {
184         CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
185             kCGDisplayBlendNormal, .0, .0, .0, false);
186         CGReleaseDisplayFadeReservation(token);
187     }
188     
189     [self revealDockIfAppropriate];
191     [self autorelease]; // Balance the above retain
194 // Title-less windows normally don't receive key presses, override this
195 - (BOOL)canBecomeKeyWindow
197     return YES;
200 // Title-less windows normally can't become main which means that another
201 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
202 - (BOOL)canBecomeMainWindow
204     return YES;
207 - (void)centerView
209     NSRect outer = [self frame], inner = [view frame];
210     //NSLog(@"%s %@%@", _cmd, NSStringFromRect(outer), NSStringFromRect(inner));
212     NSPoint origin = NSMakePoint((outer.size.width - inner.size.width)/2,
213                                  (outer.size.height - inner.size.height)/2);
214     [view setFrameOrigin:origin];
217 - (void)scrollWheel:(NSEvent *)theEvent
219     [[view textView] scrollWheel:theEvent];
222 - (void)performClose:(id)sender
224     id wc = [self windowController];
225     if ([wc respondsToSelector:@selector(performClose:)])
226         [wc performClose:sender];
227     else
228         [super performClose:sender];
231 @end // MMFullscreenWindow
236 @implementation MMFullscreenWindow (Private)
238 - (BOOL)isOnPrimaryScreen
240     // The primary screen is the screen the menu bar is on. This is different
241     // from [NSScreen mainScreen] (which returns the screen containing the
242     // key window).
243     NSArray *screens = [NSScreen screens];
244     if (screens == nil || [screens count] < 1)
245         return NO;
247     return [self screen] == [screens objectAtIndex:0];
250 - (void)hideDockIfAppropriate
252     // Hide menu and dock, both appear on demand.
253     //
254     // Don't hide the dock if going fullscreen on a non-primary screen. Also,
255     // if there are several fullscreen windows on the primary screen, only
256     // hide dock and friends for the first fullscreen window (and display
257     // them again after the last fullscreen window has been closed).
258     //
259     // Another way to deal with several fullscreen windows would be to hide/
260     // reveal the dock each time a fullscreen window gets/loses focus, but
261     // this way it's less distracting.
263     // XXX: If you have a fullscreen window on a secondary monitor and unplug
264     // the monitor, this will probably not work right.
266     if ([self isOnPrimaryScreen]) {
267         if (numFullscreenWindows == 0) {
268             SetSystemUIMode(kUIModeAllSuppressed, 0); //requires 10.3
269         }
270         ++numFullscreenWindows;
271     }
274 - (void)revealDockIfAppropriate
276      // order menu and dock back in
277     if ([self isOnPrimaryScreen]) {
278         --numFullscreenWindows;
279         if (numFullscreenWindows == 0) {
280             SetSystemUIMode(kUIModeNormal, 0);
281         }
282     }
285 @end // MMFullscreenWindow (Private)