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/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 @interface MMFullscreenWindow (Private)
42 - (BOOL)isOnPrimaryScreen;
43 - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notification;
44 - (void)handleWindowDidResignMainNotification:(NSNotification *)notification;
47 @implementation MMFullscreenWindow
49 - (MMFullscreenWindow *)initWithWindow:(NSWindow *)t view:(MMVimView *)v
50 backgroundColor:(NSColor *)back
52 NSScreen* screen = [t screen];
54 // XXX: what if screen == nil?
56 // you can't change the style of an existing window in cocoa. create a new
57 // window and move the MMTextView into it.
58 // (another way would be to make the existing window large enough that the
59 // title bar is off screen. but that doesn't work with multiple screens).
60 self = [super initWithContentRect:[screen frame]
61 styleMask:NSBorderlessWindowMask
62 backing:NSBackingStoreBuffered
64 // since we're passing [screen frame] above,
65 // we want the content rect to be relative to
66 // the main screen (ie, pass nil for screen).
75 [self setHasShadow:NO];
76 [self setShowsResizeIndicator:NO];
77 [self setBackgroundColor:back];
78 [self setReleasedWhenClosed:NO];
80 [[NSNotificationCenter defaultCenter]
82 selector:@selector(handleWindowDidBecomeMainNotification:)
83 name:NSWindowDidBecomeMainNotification
86 [[NSNotificationCenter defaultCenter]
88 selector:@selector(handleWindowDidResignMainNotification:)
89 name:NSWindowDidResignMainNotification
99 [[NSNotificationCenter defaultCenter] removeObserver:self];
101 [target release]; target = nil;
102 [view release]; view = nil;
107 - (void)enterFullscreen:(int)fuoptions
110 Boolean didBlend = NO;
111 CGDisplayFadeReservationToken token;
112 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
113 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
114 kCGDisplayBlendSolidColor, .0, .0, .0, true);
119 id delegate = [target delegate];
120 [target setDelegate:nil];
122 // make target's window controller believe that it's now controlling us
123 [[target windowController] setWindow:self];
125 oldTabBarStyle = [[view tabBarControl] styleName];
126 [[view tabBarControl] setStyleNamed:@"Unified"];
129 oldPosition = [view frame].origin;
131 [view removeFromSuperviewWithoutNeedingDisplay];
132 [[self contentView] addSubview:view];
133 [self setInitialFirstResponder:[view textView]];
135 // NOTE: Calling setTitle:nil causes an exception to be raised (and it is
136 // possible that 'target' has no title when we get here).
138 [self setTitle:[target title]];
140 [self setOpaque:[target isOpaque]];
142 // don't set this sooner, so we don't get an additional
143 // focus gained message
144 [self setDelegate:delegate];
146 // resize vim view according to fuoptions
147 int currRows, currColumns;
148 [[view textView] getMaxRows:&currRows columns:&currColumns];
150 int fuRows = currRows, fuColumns = currColumns;
152 // NOTE: Do not use [NSScreen visibleFrame] when determining the screen
153 // size since it compensates for menu and dock.
154 int maxRows, maxColumns;
155 NSSize size = [[self screen] frame].size;
156 [view constrainRows:&maxRows columns:&maxColumns toSize:size];
158 // Store current pre-fu vim size
159 nonFuRows = currRows;
160 nonFuColumns = currColumns;
162 // Compute current fu size
163 if (fuoptions & FUOPT_MAXVERT)
165 if (fuoptions & FUOPT_MAXHORZ)
166 fuColumns = maxColumns;
168 startFuFlags = fuoptions;
170 // if necessary, resize vim to target fu size
171 if (currRows != fuRows || currColumns != fuColumns) {
173 // The size sent here is queued and sent to vim when it's in
174 // event processing mode again. Make sure to only send the values we
175 // care about, as they override any changes that were made to 'lines'
176 // and 'columns' after 'fu' was set but before the event loop is run.
179 if (currRows != fuRows && currColumns != fuColumns) {
180 int newSize[2] = { fuRows, fuColumns };
181 data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
182 msgid = SetTextDimensionsMsgID;
183 } else if (currRows != fuRows) {
184 data = [NSData dataWithBytes:&fuRows length:sizeof(int)];
185 msgid = SetTextRowsMsgID;
186 } else if (currColumns != fuColumns) {
187 data = [NSData dataWithBytes:&fuColumns length:sizeof(int)];
188 msgid = SetTextColumnsMsgID;
190 NSParameterAssert(data != nil && msgid != 0);
192 MMVimController *vimController =
193 [[self windowController] vimController];
195 [vimController sendMessage:msgid data:data];
196 [[view textView] setMaxRows:fuRows columns:fuColumns];
199 startFuRows = fuRows;
200 startFuColumns = fuColumns;
202 // move vim view to the window's center
205 // make us visible and target invisible
206 [target orderOut:self];
207 [self makeKeyAndOrderFront:self];
211 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
212 kCGDisplayBlendNormal, .0, .0, .0, false);
213 CGReleaseDisplayFadeReservation(token);
217 - (void)leaveFullscreen
220 Boolean didBlend = NO;
221 CGDisplayFadeReservationToken token;
222 if (CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) {
223 CGDisplayFade(token, .25, kCGDisplayBlendNormal,
224 kCGDisplayBlendSolidColor, .0, .0, .0, true);
228 // restore old vim view size
229 int currRows, currColumns;
230 [[view textView] getMaxRows:&currRows columns:&currColumns];
231 int newRows = currRows, newColumns = currColumns;
233 // compute desired non-fu size.
234 // if current fu size is equal to fu size at fu enter time,
235 // restore the old size
237 if (startFuFlags & FUOPT_MAXVERT && startFuRows == currRows)
240 if (startFuFlags & FUOPT_MAXHORZ && startFuColumns == currColumns)
241 newColumns = nonFuColumns;
243 // resize vim if necessary
244 if (currRows != newRows || currColumns != newColumns) {
245 int newSize[2] = { newRows, newColumns };
246 NSData *data = [NSData dataWithBytes:newSize length:2*sizeof(int)];
247 MMVimController *vimController =
248 [[self windowController] vimController];
250 [vimController sendMessage:SetTextDimensionsMsgID data:data];
251 [[view textView] setMaxRows:newRows columns:newColumns];
254 // fix up target controller
255 [self retain]; // NSWindowController releases us once
256 [[self windowController] setWindow:target];
258 [[view tabBarControl] setStyleNamed:oldTabBarStyle];
261 id delegate = [self delegate];
262 [self setDelegate:nil];
264 // move text view back to original window, hide fullscreen window,
265 // show original window
266 // do this _after_ resetting delegate and window controller, so the
267 // window controller doesn't get a focus lost message from the fullscreen
269 [view removeFromSuperviewWithoutNeedingDisplay];
270 [[target contentView] addSubview:view];
272 [view setFrameOrigin:oldPosition];
275 // Set the text view to initial first responder, otherwise the 'plus'
276 // button on the tabline steals the first responder status.
277 [target setInitialFirstResponder:[view textView]];
279 [target makeKeyAndOrderFront:self];
281 // ...but we don't want a focus gained message either, so don't set this
283 [target setDelegate:delegate];
287 CGDisplayFade(token, .25, kCGDisplayBlendSolidColor,
288 kCGDisplayBlendNormal, .0, .0, .0, false);
289 CGReleaseDisplayFadeReservation(token);
292 [self autorelease]; // Balance the above retain
295 // Title-less windows normally don't receive key presses, override this
296 - (BOOL)canBecomeKeyWindow
301 // Title-less windows normally can't become main which means that another
302 // non-fullscreen window will have the "active" titlebar in expose. Bad, fix it.
303 - (BOOL)canBecomeMainWindow
310 NSRect outer = [self frame], inner = [view frame];
312 // NOTE! Make sure the origin coordinates are integral or very strange
313 // rendering issues may arise (screen looks blurry, each redraw clears the
314 // entire window, etc.).
315 NSPoint origin = { floor((outer.size.width - inner.size.width)/2),
316 floor((outer.size.height - inner.size.height)/2) };
318 [view setFrameOrigin:origin];
321 - (void)scrollWheel:(NSEvent *)theEvent
323 [[view textView] scrollWheel:theEvent];
326 - (void)performClose:(id)sender
328 id wc = [self windowController];
329 if ([wc respondsToSelector:@selector(performClose:)])
330 [wc performClose:sender];
332 [super performClose:sender];
335 - (BOOL)validateMenuItem:(NSMenuItem *)item
337 if ([item action] == @selector(vimMenuItemAction:)
338 || [item action] == @selector(performClose:))
344 @end // MMFullscreenWindow
349 @implementation MMFullscreenWindow (Private)
351 - (BOOL)isOnPrimaryScreen
353 // The primary screen is the screen the menu bar is on. This is different
354 // from [NSScreen mainScreen] (which returns the screen containing the
356 NSArray *screens = [NSScreen screens];
357 if (screens == nil || [screens count] < 1)
360 return [self screen] == [screens objectAtIndex:0];
363 - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notification
365 // Hide menu and dock, both appear on demand.
367 // Another way to deal with several fullscreen windows would be to hide/
368 // reveal the dock only when the first fullscreen window is created and
369 // show it again after the last one has been closed, but toggling on each
370 // focus gain/loss works better with Spaces. The downside is that the
371 // menu bar flashes shortly when switching between two fullscreen windows.
373 // XXX: If you have a fullscreen window on a secondary monitor and unplug
374 // the monitor, this will probably not work right.
376 if ([self isOnPrimaryScreen]) {
377 SetSystemUIMode(kUIModeAllSuppressed, 0); //requires 10.3
381 - (void)handleWindowDidResignMainNotification:(NSNotification *)notification
383 // order menu and dock back in
384 if ([self isOnPrimaryScreen]) {
385 SetSystemUIMode(kUIModeNormal, 0);
389 @end // MMFullscreenWindow (Private)