1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
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.
13 * A window without any decorations which covers an entire screen.
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.)
20 * Most of the full-screen logic is currently in this class although it might
21 * move to the window controller in the future.
26 #import "MMFullscreenWindow.h"
27 #import "MMTextView.h"
28 #import "MMVimController.h"
30 #import "MMWindowController.h"
31 #import "Miscellaneous.h"
32 #import <Carbon/Carbon.h>
33 #import <PSMTabBarControl.h>
35 // These have to be the same as in option.h
36 #define FUOPT_MAXVERT 0x001
37 #define FUOPT_MAXHORZ 0x002
38 #define FUOPT_BGCOLOR_HLGROUP 0x004
41 static int numFullscreenWindows = 0;
43 @interface MMFullscreenWindow (Private)
44 - (BOOL)isOnPrimaryScreen;
45 - (void)hideDockIfAppropriate;
46 - (void)revealDockIfAppropriate;
49 @implementation MMFullscreenWindow
51 - (MMFullscreenWindow *)initWithWindow:(NSWindow *)t view:(MMVimView *)v
52 backgroundColor:(NSColor *)back
54 NSScreen* screen = [t screen];
56 // XXX: what if screen == nil?
58 // you can't change the style of an existing window in cocoa. create a new
59 // window and move the MMTextView into it.
60 // (another way would be to make the existing window large enough that the
61 // title bar is off screen. but that doesn't work with multiple screens).
62 self = [super initWithContentRect:[screen frame]
63 styleMask:NSBorderlessWindowMask
64 backing:NSBackingStoreBuffered
66 // since we're passing [screen frame] above,
67 // we want the content rect to be relative to
68 // the main screen (ie, pass nil for screen).
77 [self setHasShadow:NO];
78 [self setShowsResizeIndicator:NO];
79 [self setBackgroundColor:back];
80 [self setReleasedWhenClosed:NO];
89 [target release]; target = nil;
90 [view release]; view = nil;
95 - (void)enterFullscreen:(int)fuoptions
97 [self hideDockIfAppropriate];
100 Boolean didBlend = NO;
101 CGDisplayFadeReservationToken token;
102 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
103 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
104 kCGDisplayBlendSolidColor, .0, .0, .0, true);
109 id delegate = [target delegate];
110 [target setDelegate:nil];
112 // make target's window controller believe that it's now controlling us
113 [[target windowController] setWindow:self];
115 oldTabBarStyle = [[view tabBarControl] styleName];
116 [[view tabBarControl] setStyleNamed:@"Unified"];
119 oldPosition = [view frame].origin;
121 [view removeFromSuperviewWithoutNeedingDisplay];
122 [[self contentView] addSubview:view];
123 [self setInitialFirstResponder:[view textView]];
125 // NOTE: Calling setTitle:nil causes an exception to be raised (and it is
126 // possible that 'target' has no title when we get here).
128 [self setTitle:[target title]];
130 [self setOpaque:[target isOpaque]];
132 // don't set this sooner, so we don't get an additional
133 // focus gained message
134 [self setDelegate:delegate];
136 // resize vim view according to fuoptions
137 int currRows, currColumns;
138 [[view textView] getMaxRows:&currRows columns:&currColumns];
140 int fuRows = currRows, fuColumns = currColumns;
142 int maxRows, maxColumns;
143 NSSize size = [[self screen] visibleFrame].size;
144 [view constrainRows:&maxRows columns:&maxColumns toSize:size];
146 // Store current pre-fu vim size
147 nonFuRows = currRows;
148 nonFuColumns = currColumns;
150 // Compute current fu size
151 if (fuoptions & FUOPT_MAXVERT)
153 if (fuoptions & FUOPT_MAXHORZ)
154 fuColumns = maxColumns;
156 startFuFlags = fuoptions;
158 // if necessary, resize vim to target fu size
159 if (currRows != fuRows || currColumns != fuColumns) {
161 // The size sent here is queued and sent to vim when it's in
162 // event processing mode again. Make sure to only send the values we
163 // care about, as they override any changes that were made to 'lines'
164 // and 'columns' after 'fu' was set but before the event loop is run.
167 if (currRows != fuRows && currColumns != fuColumns) {
168 int newSize[2] = { fuRows, fuColumns };
169 data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
170 msgid = SetTextDimensionsMsgID;
171 } else if (currRows != fuRows) {
172 data = [NSData dataWithBytes:&fuRows length:sizeof(int)];
173 msgid = SetTextRowsMsgID;
174 } else if (currColumns != fuColumns) {
175 data = [NSData dataWithBytes:&fuColumns length:sizeof(int)];
176 msgid = SetTextColumnsMsgID;
178 NSParameterAssert(data != nil && msgid != 0);
180 MMVimController *vimController =
181 [[self windowController] vimController];
183 [vimController sendMessage:msgid data:data];
184 [[view textView] setMaxRows:fuRows columns:fuColumns];
187 startFuRows = fuRows;
188 startFuColumns = fuColumns;
190 // move vim view to the window's center
193 // make us visible and target invisible
194 [target orderOut:self];
195 [self makeKeyAndOrderFront:self];
199 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
200 kCGDisplayBlendNormal, .0, .0, .0, false);
201 CGReleaseDisplayFadeReservation(token);
205 - (void)leaveFullscreen
208 Boolean didBlend = NO;
209 CGDisplayFadeReservationToken token;
210 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
211 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
212 kCGDisplayBlendSolidColor, .0, .0, .0, true);
216 // restore old vim view size
217 int currRows, currColumns;
218 [[view textView] getMaxRows:&currRows columns:&currColumns];
219 int newRows = currRows, newColumns = currColumns;
221 // compute desired non-fu size.
222 // if current fu size is equal to fu size at fu enter time,
223 // restore the old size
225 if (startFuFlags & FUOPT_MAXVERT && startFuRows == currRows)
228 if (startFuFlags & FUOPT_MAXHORZ && startFuColumns == currColumns)
229 newColumns = nonFuColumns;
231 // resize vim if necessary
232 if (currRows != newRows || currColumns != newColumns) {
233 int newSize[2] = { newRows, newColumns };
234 NSData *data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
235 MMVimController *vimController =
236 [[self windowController] vimController];
238 [vimController sendMessage:SetTextDimensionsMsgID data:data];
239 [[view textView] setMaxRows:newRows columns:newColumns];
242 // fix up target controller
243 [self retain]; // NSWindowController releases us once
244 [[self windowController] setWindow:target];
246 [[view tabBarControl] setStyleNamed:oldTabBarStyle];
249 id delegate = [self delegate];
250 [self setDelegate:nil];
252 // move text view back to original window, hide fullscreen window,
253 // show original window
254 // do this _after_ resetting delegate and window controller, so the
255 // window controller doesn't get a focus lost message from the fullscreen
257 [view removeFromSuperviewWithoutNeedingDisplay];
258 [[target contentView] addSubview:view];
260 [view setFrameOrigin:oldPosition];
263 // Set the text view to initial first responder, otherwise the 'plus'
264 // button on the tabline steals the first responder status.
265 [target setInitialFirstResponder:[view textView]];
267 [target makeKeyAndOrderFront:self];
269 // ...but we don't want a focus gained message either, so don't set this
271 [target setDelegate:delegate];
275 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
276 kCGDisplayBlendNormal, .0, .0, .0, false);
277 CGReleaseDisplayFadeReservation(token);
280 [self revealDockIfAppropriate];
282 [self autorelease]; // Balance the above retain
285 // Title-less windows normally don't receive key presses, override this
286 - (BOOL)canBecomeKeyWindow
291 // Title-less windows normally can't become main which means that another
292 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
293 - (BOOL)canBecomeMainWindow
300 NSRect outer = [self frame], inner = [view frame];
301 //NSLog(@"%s %@%@", _cmd, NSStringFromRect(outer), NSStringFromRect(inner));
303 NSPoint origin = NSMakePoint((outer.size.width - inner.size.width)/2,
304 (outer.size.height - inner.size.height)/2);
305 [view setFrameOrigin:origin];
308 - (void)scrollWheel:(NSEvent *)theEvent
310 [[view textView] scrollWheel:theEvent];
313 - (void)performClose:(id)sender
315 id wc = [self windowController];
316 if ([wc respondsToSelector:@selector(performClose:)])
317 [wc performClose:sender];
319 [super performClose:sender];
322 - (BOOL)validateMenuItem:(NSMenuItem *)item
324 if ([item action] == @selector(vimMenuItemAction:)
325 || [item action] == @selector(performClose:))
331 @end // MMFullscreenWindow
336 @implementation MMFullscreenWindow (Private)
338 - (BOOL)isOnPrimaryScreen
340 // The primary screen is the screen the menu bar is on. This is different
341 // from [NSScreen mainScreen] (which returns the screen containing the
343 NSArray *screens = [NSScreen screens];
344 if (screens == nil || [screens count] < 1)
347 return [self screen] == [screens objectAtIndex:0];
350 - (void)hideDockIfAppropriate
352 // Hide menu and dock, both appear on demand.
354 // Don't hide the dock if going fullscreen on a non-primary screen. Also,
355 // if there are several fullscreen windows on the primary screen, only
356 // hide dock and friends for the first fullscreen window (and display
357 // them again after the last fullscreen window has been closed).
359 // Another way to deal with several fullscreen windows would be to hide/
360 // reveal the dock each time a fullscreen window gets/loses focus, but
361 // this way it's less distracting.
363 // XXX: If you have a fullscreen window on a secondary monitor and unplug
364 // the monitor, this will probably not work right.
366 if ([self isOnPrimaryScreen]) {
367 if (numFullscreenWindows == 0) {
368 SetSystemUIMode(kUIModeAllSuppressed, 0); //requires 10.3
370 ++numFullscreenWindows;
374 - (void)revealDockIfAppropriate
376 // order menu and dock back in
377 if ([self isOnPrimaryScreen]) {
378 --numFullscreenWindows;
379 if (numFullscreenWindows == 0) {
380 SetSystemUIMode(kUIModeNormal, 0);
385 @end // MMFullscreenWindow (Private)