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 "MMVimController.h"
27 #import "MMFullscreenWindow.h"
28 #import <PSMTabBarControl.h>
30 #import "MMTextView.h"
31 #import "MMWindowController.h"
32 #import <Carbon/Carbon.h>
34 // These have to be the same as in option.h
35 #define FUOPT_MAXVERT 0x001
36 #define FUOPT_MAXHORZ 0x002
37 #define FUOPT_BGCOLOR_HLGROUP 0x004
40 static int numFullscreenWindows = 0;
42 @interface MMFullscreenWindow (Private)
43 - (BOOL)isOnPrimaryScreen;
44 - (void)hideDockIfAppropriate;
45 - (void)revealDockIfAppropriate;
48 @implementation MMFullscreenWindow
50 - (MMFullscreenWindow *)initWithWindow:(NSWindow *)t view:(MMVimView *)v
51 backgroundColor:(NSColor *)back
53 NSScreen* screen = [t screen];
55 // XXX: what if screen == nil?
57 // you can't change the style of an existing window in cocoa. create a new
58 // window and move the MMTextView into it.
59 // (another way would be to make the existing window large enough that the
60 // title bar is off screen. but that doesn't work with multiple screens).
61 self = [super initWithContentRect:[screen frame]
62 styleMask:NSBorderlessWindowMask
63 backing:NSBackingStoreBuffered
65 // since we're passing [screen frame] above,
66 // we want the content rect to be relative to
67 // the main screen (ie, pass nil for screen).
76 [self setHasShadow:NO];
77 [self setShowsResizeIndicator:NO];
78 [self setBackgroundColor:back];
79 [self setReleasedWhenClosed:NO];
86 [target release]; target = nil;
87 [view release]; view = nil;
92 - (void)enterFullscreen:(int)fuoptions
94 [self hideDockIfAppropriate];
97 Boolean didBlend = NO;
98 CGDisplayFadeReservationToken token;
99 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
100 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
101 kCGDisplayBlendSolidColor, .0, .0, .0, true);
106 id delegate = [target delegate];
107 [target setDelegate:nil];
109 // make target's window controller believe that it's now controlling us
110 [[target windowController] setWindow:self];
112 oldTabBarStyle = [[view tabBarControl] styleName];
113 [[view tabBarControl] setStyleNamed:@"Unified"];
116 oldPosition = [view frame].origin;
118 [view removeFromSuperviewWithoutNeedingDisplay];
119 [[self contentView] addSubview:view];
120 [self setInitialFirstResponder:[view textView]];
122 // NOTE: Calling setTitle:nil causes an exception to be raised (and it is
123 // possible that 'target' has no title when we get here).
125 [self setTitle:[target title]];
127 [self setOpaque:[target isOpaque]];
129 // don't set this sooner, so we don't get an additional
130 // focus gained message
131 [self setDelegate:delegate];
133 // resize vim view according to fuoptions
134 int currRows, currColumns;
135 [[view textView] getMaxRows:&currRows columns:&currColumns];
137 int fuRows = currRows, fuColumns = currColumns;
139 int maxRows, maxColumns;
140 NSSize size = [[self screen] frame].size;
141 [view constrainRows:&maxRows columns:&maxColumns toSize:size];
143 // Store current pre-fu vim size
144 nonFuRows = currRows;
145 nonFuColumns = currColumns;
147 // Compute current fu size
148 if (fuoptions & FUOPT_MAXVERT)
150 if (fuoptions & FUOPT_MAXHORZ)
151 fuColumns = maxColumns;
153 startFuFlags = fuoptions;
155 // if necessary, resize vim to target fu size
156 if (currRows != fuRows || currColumns != fuColumns) {
157 int newSize[2] = { fuRows, fuColumns };
158 NSData *data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
159 MMVimController *vimController =
160 [[self windowController] vimController];
162 [vimController sendMessage:SetTextDimensionsMsgID data:data];
163 [[view textView] setMaxRows:fuRows columns:fuColumns];
166 startFuRows = fuRows;
167 startFuColumns = fuColumns;
169 // move vim view to the window's center
172 // make us visible and target invisible
173 [target orderOut:self];
174 [self makeKeyAndOrderFront:self];
178 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
179 kCGDisplayBlendNormal, .0, .0, .0, false);
180 CGReleaseDisplayFadeReservation(token);
184 - (void)leaveFullscreen
187 Boolean didBlend = NO;
188 CGDisplayFadeReservationToken token;
189 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
190 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
191 kCGDisplayBlendSolidColor, .0, .0, .0, true);
195 // restore old vim view size
196 int currRows, currColumns;
197 [[view textView] getMaxRows:&currRows columns:&currColumns];
198 int newRows = currRows, newColumns = currColumns;
200 // compute desired non-fu size.
201 // if current fu size is equal to fu size at fu enter time,
202 // restore the old size
204 if (startFuFlags & FUOPT_MAXVERT && startFuRows == currRows)
207 if (startFuFlags & FUOPT_MAXHORZ && startFuColumns == currColumns)
208 newColumns = nonFuColumns;
210 // resize vim if necessary
211 if (currRows != newRows || currColumns != newColumns) {
212 int newSize[2] = { newRows, newColumns };
213 NSData *data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
214 MMVimController *vimController =
215 [[self windowController] vimController];
217 [vimController sendMessage:SetTextDimensionsMsgID data:data];
218 [[view textView] setMaxRows:newRows columns:newColumns];
221 // fix up target controller
222 [self retain]; // NSWindowController releases us once
223 [[self windowController] setWindow:target];
225 [[view tabBarControl] setStyleNamed:oldTabBarStyle];
228 id delegate = [self delegate];
229 [self setDelegate:nil];
231 // move text view back to original window, hide fullscreen window,
232 // show original window
233 // do this _after_ resetting delegate and window controller, so the
234 // window controller doesn't get a focus lost message from the fullscreen
236 [view removeFromSuperviewWithoutNeedingDisplay];
237 [[target contentView] addSubview:view];
239 [view setFrameOrigin:oldPosition];
242 // Set the text view to initial first responder, otherwise the 'plus'
243 // button on the tabline steals the first responder status.
244 [target setInitialFirstResponder:[view textView]];
246 [target makeKeyAndOrderFront:self];
248 // ...but we don't want a focus gained message either, so don't set this
250 [target setDelegate:delegate];
254 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
255 kCGDisplayBlendNormal, .0, .0, .0, false);
256 CGReleaseDisplayFadeReservation(token);
259 [self revealDockIfAppropriate];
261 [self autorelease]; // Balance the above retain
264 // Title-less windows normally don't receive key presses, override this
265 - (BOOL)canBecomeKeyWindow
270 // Title-less windows normally can't become main which means that another
271 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
272 - (BOOL)canBecomeMainWindow
279 NSRect outer = [self frame], inner = [view frame];
280 //NSLog(@"%s %@%@", _cmd, NSStringFromRect(outer), NSStringFromRect(inner));
282 NSPoint origin = NSMakePoint((outer.size.width - inner.size.width)/2,
283 (outer.size.height - inner.size.height)/2);
284 [view setFrameOrigin:origin];
287 - (void)scrollWheel:(NSEvent *)theEvent
289 [[view textView] scrollWheel:theEvent];
292 - (void)performClose:(id)sender
294 id wc = [self windowController];
295 if ([wc respondsToSelector:@selector(performClose:)])
296 [wc performClose:sender];
298 [super performClose:sender];
301 @end // MMFullscreenWindow
306 @implementation MMFullscreenWindow (Private)
308 - (BOOL)isOnPrimaryScreen
310 // The primary screen is the screen the menu bar is on. This is different
311 // from [NSScreen mainScreen] (which returns the screen containing the
313 NSArray *screens = [NSScreen screens];
314 if (screens == nil || [screens count] < 1)
317 return [self screen] == [screens objectAtIndex:0];
320 - (void)hideDockIfAppropriate
322 // Hide menu and dock, both appear on demand.
324 // Don't hide the dock if going fullscreen on a non-primary screen. Also,
325 // if there are several fullscreen windows on the primary screen, only
326 // hide dock and friends for the first fullscreen window (and display
327 // them again after the last fullscreen window has been closed).
329 // Another way to deal with several fullscreen windows would be to hide/
330 // reveal the dock each time a fullscreen window gets/loses focus, but
331 // this way it's less distracting.
333 // XXX: If you have a fullscreen window on a secondary monitor and unplug
334 // the monitor, this will probably not work right.
336 if ([self isOnPrimaryScreen]) {
337 if (numFullscreenWindows == 0) {
338 SetSystemUIMode(kUIModeAllSuppressed, 0); //requires 10.3
340 ++numFullscreenWindows;
344 - (void)revealDockIfAppropriate
346 // order menu and dock back in
347 if ([self isOnPrimaryScreen]) {
348 --numFullscreenWindows;
349 if (numFullscreenWindows == 0) {
350 SetSystemUIMode(kUIModeNormal, 0);
355 @end // MMFullscreenWindow (Private)