1/** <title>GSStandardWindowDecorationView</title> 2 3 Copyright (C) 2004 Free Software Foundation, Inc. 4 5 Author: Alexander Malmberg <alexander@malmberg.org> 6 Date: 2004-03-24 7 8 This file is part of the GNUstep GUI Library. 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; see the file COPYING.LIB. 22 If not, see <http://www.gnu.org/licenses/> or write to the 23 Free Software Foundation, 51 Franklin Street, Fifth Floor, 24 Boston, MA 02110-1301, USA. 25*/ 26 27#import <Foundation/NSException.h> 28#import <Foundation/NSNotification.h> 29 30#import "AppKit/NSApplication.h" 31#import "AppKit/NSAttributedString.h" 32#import "AppKit/NSButton.h" 33#import "AppKit/NSEvent.h" 34#import "AppKit/NSImage.h" 35#import "AppKit/NSParagraphStyle.h" 36#import "AppKit/NSScreen.h" 37#import "AppKit/NSStringDrawing.h" 38#import "AppKit/NSWindow.h" 39#import "AppKit/PSOperators.h" 40#import "GNUstepGUI/GSDisplayServer.h" 41#import "GNUstepGUI/GSTheme.h" 42 43#import <GNUstepGUI/GSWindowDecorationView.h> 44 45@interface GSStandardWindowDecorationView (GSTheme) 46- (void) _themeDidActivate: (NSNotification*)notification; 47@end 48 49@implementation GSStandardWindowDecorationView 50 51+ (void) offsets: (float *)l : (float *)r : (float *)t : (float *)b 52 forStyleMask: (NSUInteger)style 53{ 54 GSTheme *theme = [GSTheme theme]; 55 56 if (style 57 & (NSTitledWindowMask | NSClosableWindowMask 58 | NSMiniaturizableWindowMask | NSResizableWindowMask)) 59 { 60 *l = *r = *t = *b = 1.0; 61 } 62 else 63 { 64 *l = *r = *t = *b = 0.0; 65 } 66 67 if (style 68 & (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)) 69 { 70 *t = [theme titlebarHeight]; 71 } 72 if (style & NSResizableWindowMask) 73 { 74 *b = [theme resizebarHeight]; 75 } 76} 77 78+ (CGFloat) minFrameWidthWithTitle: (NSString *)aTitle 79 styleMask: (NSUInteger)aStyle 80{ 81 float l, r, t, b, width; 82 83 [self offsets: &l : &r : &t : &b forStyleMask: aStyle]; 84 85 width = l + r; 86 87 if (aStyle & NSTitledWindowMask) 88 { 89 width += [aTitle sizeWithAttributes: nil].width; 90 } 91 return width; 92} 93 94- (void) updateRects 95{ 96 GSTheme *theme = [GSTheme theme]; 97 98 if (hasTitleBar) 99 { 100 CGFloat titleHeight = [theme titlebarHeight]; 101 102 titleBarRect = NSMakeRect(0.0, [self bounds].size.height - titleHeight, 103 [self bounds].size.width, titleHeight); 104 } 105 if (hasResizeBar) 106 { 107 resizeBarRect = NSMakeRect(0.0, 0.0, [self bounds].size.width, [theme resizebarHeight]); 108 } 109 if (hasCloseButton) 110 { 111 closeButtonRect = NSMakeRect([self bounds].size.width - [theme titlebarButtonSize] - 112 [theme titlebarPaddingRight], [self bounds].size.height - 113 [theme titlebarButtonSize] - [theme titlebarPaddingTop], 114 [theme titlebarButtonSize], [theme titlebarButtonSize]); 115 [closeButton setFrame: closeButtonRect]; 116 } 117 118 if (hasMiniaturizeButton) 119 { 120 miniaturizeButtonRect = NSMakeRect([theme titlebarPaddingLeft], [self bounds].size.height - 121 [theme titlebarButtonSize] - [theme titlebarPaddingTop], 122 [theme titlebarButtonSize], [theme titlebarButtonSize]); 123 [miniaturizeButton setFrame: miniaturizeButtonRect]; 124 } 125} 126 127- (id) initWithFrame: (NSRect)frame 128 window: (NSWindow *)w 129{ 130 NSUInteger styleMask; 131 132 self = [super initWithFrame: frame window: w]; 133 if (!self) return nil; 134 135 styleMask = [w styleMask]; 136 if (styleMask 137 & (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)) 138 { 139 hasTitleBar = YES; 140 } 141 if (styleMask & NSTitledWindowMask) 142 { 143 isTitled = YES; 144 } 145 if (styleMask & NSClosableWindowMask) 146 { 147 hasCloseButton = YES; 148 149 closeButton = [NSWindow standardWindowButton: NSWindowCloseButton 150 forStyleMask: styleMask]; 151 [closeButton setTarget: window]; 152 [self addSubview: closeButton]; 153 } 154 if (styleMask & NSMiniaturizableWindowMask) 155 { 156 hasMiniaturizeButton = YES; 157 158 miniaturizeButton = [NSWindow standardWindowButton: NSWindowMiniaturizeButton 159 forStyleMask: styleMask]; 160 [miniaturizeButton setTarget: window]; 161 [self addSubview: miniaturizeButton]; 162 } 163 if (styleMask & NSResizableWindowMask) 164 { 165 hasResizeBar = YES; 166 } 167 [self updateRects]; 168 169 [[NSNotificationCenter defaultCenter] 170 addObserver: self 171 selector: @selector(_themeDidActivate:) 172 name: GSThemeDidActivateNotification 173 object: nil]; 174 return self; 175} 176 177- (void) dealloc 178{ 179 [[NSNotificationCenter defaultCenter] removeObserver: self]; 180 [super dealloc]; 181} 182 183- (void) drawRect: (NSRect)rect 184{ 185 [[GSTheme theme] drawWindowBorder: rect 186 withFrame: [self bounds] 187 forStyleMask: [window styleMask] 188 state: inputState 189 andTitle: [window title]]; 190 191 [super drawRect: rect]; 192} 193 194- (void) setTitle: (NSString *)newTitle 195{ 196 if (isTitled) 197 [self setNeedsDisplayInRect: titleBarRect]; 198 [super setTitle: newTitle]; 199} 200 201- (void) setInputState: (int)state 202{ 203 NSAssert(state >= 0 && state <= 2, @"Invalid state!"); 204 [super setInputState: state]; 205 if (hasTitleBar) 206 [self setNeedsDisplayInRect: titleBarRect]; 207} 208 209- (void) setDocumentEdited: (BOOL)flag 210{ 211 if (flag) 212 { 213 [closeButton setImage: [NSImage imageNamed: @"common_CloseBroken"]]; 214 [closeButton setAlternateImage: 215 [NSImage imageNamed: @"common_CloseBrokenH"]]; 216 } 217 else 218 { 219 [closeButton setImage: [NSImage imageNamed: @"common_Close"]]; 220 [closeButton setAlternateImage: 221 [NSImage imageNamed: @"common_CloseH"]]; 222 } 223 [super setDocumentEdited: flag]; 224} 225 226- (NSPoint) mouseLocationOnScreenOutsideOfEventStream 227{ 228 int screen = [[window screen] screenNumber]; 229 return [GSServerForWindow(window) mouseLocationOnScreen: screen 230 window: NULL]; 231} 232 233- (void) moveWindowStartingWithEvent: (NSEvent *)event 234{ 235 NSUInteger mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask; 236 NSEvent *currentEvent = event; 237 NSDate *distantPast = [NSDate distantPast]; 238 NSPoint delta, point; 239 240 delta = [event locationInWindow]; 241 242 [window _captureMouse: nil]; 243 do 244 { 245 while (currentEvent && [currentEvent type] != NSLeftMouseUp) 246 { 247 currentEvent = [_window nextEventMatchingMask: mask 248 untilDate: distantPast 249 inMode: NSEventTrackingRunLoopMode 250 dequeue: YES]; 251 } 252 253 point = [self mouseLocationOnScreenOutsideOfEventStream]; 254 [window setFrameOrigin: NSMakePoint(point.x - delta.x, 255 point.y - delta.y)]; 256 257 if (currentEvent && [currentEvent type] == NSLeftMouseUp) 258 break; 259 260 currentEvent = [_window nextEventMatchingMask: mask 261 untilDate: [NSDate distantFuture] 262 inMode: NSEventTrackingRunLoopMode 263 dequeue: YES]; 264 } while ([currentEvent type] != NSLeftMouseUp); 265 [window _releaseMouse: nil]; 266} 267 268 269static NSRect 270calc_new_frame(NSRect frame, NSPoint point, NSPoint firstPoint, 271 int mode, NSSize minSize, NSSize maxSize) 272{ 273 NSRect newFrame = frame; 274 newFrame.origin.y = point.y - firstPoint.y; 275 newFrame.size.height = NSMaxY(frame) - newFrame.origin.y; 276 if (newFrame.size.height < minSize.height) 277 { 278 newFrame.size.height = minSize.height; 279 newFrame.origin.y = NSMaxY(frame) - newFrame.size.height; 280 } 281 282 if (mode == 0) 283 { 284 newFrame.origin.x = point.x - firstPoint.x; 285 newFrame.size.width = NSMaxX(frame) - newFrame.origin.x; 286 287 if (newFrame.size.width < minSize.width) 288 { 289 newFrame.size.width = minSize.width; 290 newFrame.origin.x = NSMaxX(frame) - newFrame.size.width; 291 } 292 } 293 else if (mode == 1) 294 { 295 newFrame.size.width = point.x - frame.origin.x + frame.size.width 296 - firstPoint.x; 297 298 if (newFrame.size.width < minSize.width) 299 { 300 newFrame.size.width = minSize.width; 301 newFrame.origin.x = frame.origin.x; 302 } 303 } 304 return newFrame; 305} 306 307- (void) resizeWindowStartingWithEvent: (NSEvent *)event 308{ 309 NSUInteger mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask | NSPeriodicMask; 310 NSEvent *currentEvent = event; 311 NSDate *distantPast = [NSDate distantPast]; 312 NSDate *distantFuture = [NSDate distantFuture]; 313 NSPoint firstPoint, point; 314 NSRect newFrame, frame; 315 NSSize minSize, maxSize; 316 int num = 0; 317 318 /* 319 0 drag lower left corner 320 1 drag lower right corner 321 2 drag lower edge 322 */ 323 int mode; 324 325 firstPoint = [event locationInWindow]; 326 if (resizeBarRect.size.width < 30 * 2 327 && firstPoint.x < resizeBarRect.size.width / 2) 328 mode = 0; 329 else if (firstPoint.x > resizeBarRect.size.width - 29) 330 mode = 1; 331 else if (firstPoint.x < 29) 332 mode = 0; 333 else 334 mode = 2; 335 336 frame = [window frame]; 337 minSize = [window minSize]; 338 maxSize = [window maxSize]; 339 340 [window _captureMouse: nil]; 341 [NSEvent startPeriodicEventsAfterDelay: 0.1 withPeriod: 0.1]; 342 do 343 { 344 while (currentEvent && [currentEvent type] != NSLeftMouseUp) 345 { 346 currentEvent = [_window nextEventMatchingMask: mask 347 untilDate: distantPast 348 inMode: NSEventTrackingRunLoopMode 349 dequeue: YES]; 350 } 351 352 point = [self mouseLocationOnScreenOutsideOfEventStream]; 353 newFrame 354 = calc_new_frame(frame, point, firstPoint, mode, minSize, maxSize); 355 356 if (currentEvent && [currentEvent type] == NSLeftMouseUp) 357 break; 358 359 num++; 360 if (num == 5) 361 { 362 [window setFrame: newFrame display: YES]; 363 num = 0; 364 } 365 366 currentEvent = [_window nextEventMatchingMask: mask 367 untilDate: distantFuture 368 inMode: NSEventTrackingRunLoopMode 369 dequeue: YES]; 370 } while ([currentEvent type] != NSLeftMouseUp); 371 [NSEvent stopPeriodicEvents]; 372 [window _releaseMouse: nil]; 373 374 [window setFrame: newFrame display: YES]; 375} 376 377- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent 378{ 379 return YES; 380} 381 382- (void) mouseDown: (NSEvent *)event 383{ 384 NSPoint p = [self convertPoint: [event locationInWindow] fromView: nil]; 385 386 if (NSPointInRect(p, contentRect)) 387 { 388 [super mouseDown: event]; 389 return; 390 } 391 392 if (NSPointInRect(p, titleBarRect)) 393 { 394 [self moveWindowStartingWithEvent: event]; 395 return; 396 } 397 398 if (NSPointInRect(p, resizeBarRect)) 399 { 400 [self resizeWindowStartingWithEvent: event]; 401 return; 402 } 403 404 [super mouseDown: event]; 405} 406 407- (void) setFrame: (NSRect)frameRect 408{ 409 [super setFrame: frameRect]; 410 [self updateRects]; 411} 412 413@end 414 415@implementation GSStandardWindowDecorationView (GSTheme) 416 417- (void) _themeDidActivate: (NSNotification*)notification 418{ 419 [self updateRects]; 420 [self setNeedsDisplay: YES]; 421} 422 423@end 424