API  0.9.9
CPWindow.j
Go to the documentation of this file.
1 /*
2  * CPWindow.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 #if PLATFORM(BROWSER)
25 #endif
26 
27 
28 @global CPApp
29 @global _CPPlatformWindowWillCloseNotification
30 
31 @typedef _CPWindowFullPlatformWindowSession
32 
33 
35 
36 @optional
37 - (BOOL)windowShouldClose:(CPWindow)aWindow;
38 - (CGSize)windowWillResize:(CPWindow)sender toSize:(CGSize)aSize;
39 - (CPUndoManager)windowWillReturnUndoManager:(CPWindow)window;
40 - (void)windowDidBecomeKey:(CPNotification)aNotification;
41 - (void)windowDidBecomeMain:(CPNotification)aNotification;
42 - (void)windowDidDeminiaturize:(CPNotification)notification;
43 - (void)windowDidEndSheet:(CPNotification)aNotification;
44 - (void)windowDidMiniaturize:(CPNotification)notification;
45 - (void)windowDidMove:(CPNotification)aNotification;
46 - (void)windowDidResignKey:(CPNotification)aNotification;
47 - (void)windowDidResignMain:(CPNotification)aNotification;
48 - (void)windowDidResize:(CPNotification)aNotification;
49 - (void)windowWillMiniaturize:(CPNotification)notification;
50 - (void)windowWillBeginSheet:(CPNotification)aNotification;
51 - (void)windowWillClose:(CPWindow)aWindow;
52 
53 @end
54 
59 
60 
62 
64  CPWindowResizeStyleGlobalChangeNotification = @"CPWindowResizeStyleGlobalChangeNotification",
65 
68 
69 /*
70  Keys for which action messages will be sent by default when unhandled, e.g. complete:.
71 */
73  CPLeftArrowFunctionKey,
74  CPRightArrowFunctionKey,
75  CPUpArrowFunctionKey,
76  CPDownArrowFunctionKey,
77  CPPageUpFunctionKey,
78  CPPageDownFunctionKey,
79  CPHomeFunctionKey,
80  CPEndFunctionKey,
81  CPEscapeFunctionKey
82  ];
83 
139 @implementation CPWindow : CPResponder
140 {
141  CPPlatformWindow _platformWindow;
142 
143  int _windowNumber;
144  unsigned _styleMask;
145  CGRect _frame;
146  int _level;
147  BOOL _isVisible;
148  BOOL _hasBeenOrderedIn;
149  BOOL _isMiniaturized;
150  BOOL _isAnimating;
151  BOOL _hasShadow;
152  BOOL _isMovableByWindowBackground;
153  BOOL _isMovable;
154  BOOL _constrainsToUsableScreen;
155  unsigned _shadowStyle;
156  BOOL _showsResizeIndicator;
157 
158  int _positioningMask;
159  CGRect _positioningScreenRect;
160 
161  BOOL _isDocumentEdited;
162  BOOL _isDocumentSaving;
163 
164  CPImageView _shadowView;
165 
166  CPView _windowView;
167  CPView _contentView;
168  CPView _toolbarView;
169 
170  BOOL _handlingTrackingAreaEvent;
171  BOOL _restartHandlingTrackingAreaEvent;
172  CPArray _previousMouseEnteredStack;
173  CPArray _previousCursorUpdateStack;
174  CPArray _mouseEnteredStack;
175  CPArray _cursorUpdateStack;
176  CPArray _queuedEvents;
177  CPArray _trackingAreaViews;
178  id _activeCursorTrackingArea;
179  CPArray _queuedTrackingEvents;
180  CPView _leftMouseDownView;
181  CPView _rightMouseDownView;
182 
183  CPToolbar _toolbar;
184  CPResponder _firstResponder;
185  CPResponder _initialFirstResponder;
186  BOOL _hasBecomeKeyWindow;
187  id <CPWindowDelegate> _delegate;
188  unsigned _implementedDelegateMethods;
189 
190  CPString _title;
191 
192  BOOL _acceptsMouseMovedEvents;
193  BOOL _ignoresMouseEvents;
194 
195  CPWindowController _windowController;
196 
197  CGSize _minSize;
198  CGSize _maxSize;
199 
200  CPUndoManager _undoManager;
201  CPURL _representedURL;
202 
203  CPSet _registeredDraggedTypes;
204  CPArray _registeredDraggedTypesArray;
205  CPCountedSet _inclusiveRegisteredDraggedTypes;
206 
207  CPButton _defaultButton;
208  BOOL _defaultButtonEnabled;
209 
210  BOOL _autorecalculatesKeyViewLoop;
211  BOOL _keyViewLoopIsDirty;
212 
213  BOOL _sharesChromeWithPlatformWindow;
214 
215  // Bridge Support
216 #if PLATFORM(DOM)
217  DOMElement _DOMElement;
218 #endif
219 
220  unsigned _autoresizingMask;
221 
222  BOOL _isFullPlatformWindow;
223  _CPWindowFullPlatformWindowSession _fullPlatformWindowSession;
224 
225  CPWindow _parentWindow;
226  CPArray _childWindows;
227  CPWindowOrderingMode _childOrdering;
228 
229  CPDictionary _sheetContext;
230  CPWindow _parentView;
231  BOOL _isSheet;
232  _CPWindowFrameAnimation _frameAnimation;
233 }
234 
235 + (Class)_binderClassForBinding:(CPString)aBinding
236 {
237  if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
238  return [CPTitleWithPatternBinding class];
239 
240  return [super _binderClassForBinding:aBinding];
241 }
242 
243 - (id)init
244 {
245  return [self initWithContentRect:CGRectMakeZero() styleMask:CPTitledWindowMask];
246 }
247 
263 - (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask
264 {
265  self = [super init];
266 
267  if (self)
268  {
269  var windowViewClass = [[self class] _windowViewClassForStyleMask:aStyleMask];
270 
271  _frame = [windowViewClass frameRectForContentRect:aContentRect];
272  _constrainsToUsableScreen = YES;
273 
274  [self _setSharesChromeWithPlatformWindow:![CPPlatform isBrowser]];
275 
276  if ([CPPlatform isBrowser])
278  else
279  {
280  // give zero sized borderless bridge windows a default size if we're not in the browser so they show up in NativeHost.
281  if ((aStyleMask & CPBorderlessBridgeWindowMask) && aContentRect.size.width === 0 && aContentRect.size.height === 0)
282  {
283  var visibleFrame = [[[CPScreen alloc] init] visibleFrame];
284  _frame.size.height = MIN(768.0, visibleFrame.size.height);
285  _frame.size.width = MIN(1024.0, visibleFrame.size.width);
286  _frame.origin.x = (visibleFrame.size.width - _frame.size.width) / 2;
287  _frame.origin.y = (visibleFrame.size.height - _frame.size.height) / 2;
288  }
289 
291  [self platformWindow]._only = self;
292  }
293 
294  _isFullPlatformWindow = NO;
295  _registeredDraggedTypes = [CPSet set];
296  _registeredDraggedTypesArray = [];
297  _acceptsMouseMovedEvents = YES;
298  _isMovable = YES;
299  _hasBeenOrderedIn = NO;
300 
301  _parentWindow = nil;
302  _childWindows = [];
303  _childOrdering = CPWindowOut;
304 
305  _isSheet = NO;
306  _sheetContext = nil;
307  _parentView = nil;
308 
309  // Set up our window number.
310  _windowNumber = [CPApp._windows count];
311  CPApp._windows[_windowNumber] = self;
312 
313  _styleMask = aStyleMask;
314 
315  [self setLevel:CPNormalWindowLevel];
316 
317  _handlingTrackingAreaEvent = NO;
318  _restartHandlingTrackingAreaEvent = NO;
319  _trackingAreaViews = [];
320  _previousMouseEnteredStack = [];
321  _previousCursorUpdateStack = [];
322  _mouseEnteredStack = [];
323  _cursorUpdateStack = [];
324  _queuedEvents = [];
325  _queuedTrackingEvents = [];
326  _activeCursorTrackingArea = nil;
327 
328  // Create our border view which is the actual root of our view hierarchy.
329  _windowView = [[windowViewClass alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) styleMask:aStyleMask];
330 
331  [_windowView _setWindow:self];
332  [_windowView setNextResponder:self];
333 
334  // Size calculation needs _windowView
335  _minSize = [self _calculateMinSizeForProposedSize:CGSizeMake(0.0, 0.0)];
336  _maxSize = CGSizeMake(1000000.0, 1000000.0);
337 
338  [self setMovableByWindowBackground:aStyleMask & CPHUDBackgroundWindowMask];
339 
340  // Create a generic content view.
341  [self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
342 
343  _firstResponder = self;
344 
345 #if PLATFORM(DOM)
346  _DOMElement = document.createElement("div");
347 
348  _DOMElement.style.position = "absolute";
349  _DOMElement.style.visibility = "visible";
350  _DOMElement.style.zIndex = 0;
351 
352  if (![self _sharesChromeWithPlatformWindow])
353  {
354  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(_frame), CGRectGetMinY(_frame));
355  }
356 
357  CPDOMDisplayServerSetStyleSize(_DOMElement, 1, 1);
358  CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement);
359 #endif
360 
361  [self setNextResponder:CPApp];
362 
363  [self setHasShadow:aStyleMask !== CPBorderlessWindowMask];
364 
365  if (aStyleMask & CPBorderlessBridgeWindowMask)
366  [self setFullPlatformWindow:YES];
367 
368  _autorecalculatesKeyViewLoop = NO;
369  _defaultButtonEnabled = YES;
370  _keyViewLoopIsDirty = NO;
371  _hasBecomeKeyWindow = NO;
372 
373  [self setShowsResizeIndicator:_styleMask & CPResizableWindowMask];
374 
377  name:CPWindowResizeStyleGlobalChangeNotification
378  object:nil];
379  }
380 
381  return self;
382 }
383 
385 {
386  return _platformWindow;
387 }
388 
394 - (void)setPlatformWindow:(CPPlatformWindow)aPlatformWindow
395 {
396  var wasVisible = [self isVisible];
397 
398  // we have to close it first, otherwise we get a DOM exception.
399  if (wasVisible)
400  [self close];
401 
402  _platformWindow = aPlatformWindow;
403  [_platformWindow _setTitle:_title window:self];
404 
405  if (wasVisible)
406  [self orderFront:self];
407 }
408 
409 
413 + (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask
414 {
415  if (aStyleMask & CPHUDBackgroundWindowMask)
416  return _CPHUDWindowView;
417 
418  else if (aStyleMask === CPBorderlessWindowMask)
419  return _CPBorderlessWindowView;
420 
421  else if (aStyleMask & CPDocModalWindowMask)
422  return _CPDocModalWindowView;
423 
424  else if (aStyleMask & _CPModalWindowMask)
425  return _CPModalWindowView;
426 
427  return _CPStandardWindowView;
428 }
429 
430 + (Class)_windowViewClassForFullPlatformWindowStyleMask:(unsigned)aStyleMask
431 {
432  return _CPBorderlessBridgeWindowView;
433 }
434 
436 {
437  // At this time we know the final screen (or browser) size
438  // and can apply the positioning mask, if any, from the nib.
439  if (_positioningScreenRect)
440  {
441  var actualScreenRect = [CPPlatform isBrowser] ? [_platformWindow contentBounds] : [[self screen] visibleFrame],
442  frame = [self frame],
443  origin = frame.origin;
444 
445  if (actualScreenRect)
446  {
447  if ((_positioningMask & CPWindowPositionFlexibleLeft) && (_positioningMask & CPWindowPositionFlexibleRight))
448  {
449  // Proportional Horizontal.
450  origin.x *= (actualScreenRect.size.width / _positioningScreenRect.size.width);
451  }
452  else if (_positioningMask & CPWindowPositionFlexibleLeft)
453  {
454  // Fixed from Right
455  origin.x += actualScreenRect.size.width - _positioningScreenRect.size.width;
456  }
457  else if (_positioningMask & CPWindowPositionFlexibleRight)
458  {
459  // Fixed from Left
460  }
461 
462  if ((_positioningMask & CPWindowPositionFlexibleTop) && (_positioningMask & CPWindowPositionFlexibleBottom))
463  {
464  // Proportional Vertical.
465  origin.y *= (actualScreenRect.size.height / _positioningScreenRect.size.height);
466  }
467  else if (_positioningMask & CPWindowPositionFlexibleTop)
468  {
469  // Fixed from Bottom
470  origin.y += actualScreenRect.size.height - _positioningScreenRect.size.height;
471  }
472  else if (_positioningMask & CPWindowPositionFlexibleBottom)
473  {
474  // Fixed from Top
475  }
476 
477  [self setFrameOrigin:origin];
478  }
479  }
480 
481  /*
482  Calculate the key view loop if necessary. Note that Cocoa does not call recalculateKeyViewLoop when awaking a nib. If a key view loop was set in the cib, we have to chain it to the content view.
483  */
484  if ([self _hasKeyViewLoop:[_contentView subviews]])
485  {
486  var views = [self _viewsSortedByPosition],
487  count = [views count];
488 
489  // The first view is the content view.
490  // Find the first subview that has a next key view.
491  for (var i = 1; i < count; ++i)
492  {
493  var view = views[i];
494 
495  if ([view nextKeyView])
496  {
497  [_contentView setNextKeyView:view];
498  break;
499  }
500  }
501  }
502  else
503  {
504  // Cooca does NOT call the public method recalculateKeyViewLoop for nibs,
505  // but it does calculate the loop.
506  [self _doRecalculateKeyViewLoop];
507  }
508 }
509 
510 - (void)_setWindowView:(CPView)aWindowView
511 {
512  if (_windowView === aWindowView)
513  return;
514 
515  var oldWindowView = _windowView;
516 
517  _windowView = aWindowView;
518 
519  if (oldWindowView)
520  {
521  [oldWindowView _setWindow:nil];
522  [oldWindowView noteToolbarChanged];
523 
524 #if PLATFORM(DOM)
525  CPDOMDisplayServerRemoveChild(_DOMElement, oldWindowView._DOMElement);
526 #endif
527  }
528 
529  if (_windowView)
530  {
531 #if PLATFORM(DOM)
532  CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement);
533 #endif
534 
535  var contentRect = [_contentView convertRect:[_contentView bounds] toView:nil];
536 
537  contentRect.origin = [self convertBaseToGlobal:contentRect.origin];
538 
539  [_windowView _setWindow:self];
540  [_windowView setNextResponder:self];
541  [_windowView addSubview:_contentView];
542  [_windowView setTitle:_title];
543  [_windowView noteToolbarChanged];
544  [_windowView setShowsResizeIndicator:[self showsResizeIndicator]];
545 
546  [self setFrame:[self frameRectForContentRect:contentRect]];
547  }
548 }
549 
550 - (CPView)_windowView
551 {
552  return _windowView;
553 }
554 
561 - (void)setFullPlatformWindow:(BOOL)shouldBeFullPlatformWindow
562 {
563  if (![_platformWindow supportsFullPlatformWindows])
564  return;
565 
566  shouldBeFullPlatformWindow = !!shouldBeFullPlatformWindow;
567 
568  if (_isFullPlatformWindow === shouldBeFullPlatformWindow)
569  return;
570 
571  _isFullPlatformWindow = shouldBeFullPlatformWindow;
572 
573  if (_isFullPlatformWindow)
574  {
575  _fullPlatformWindowSession = _CPWindowFullPlatformWindowSessionMake(_windowView, [self contentRectForFrameRect:[self frame]], [self hasShadow], [self level]);
576 
577  var fullPlatformWindowViewClass = [[self class] _windowViewClassForFullPlatformWindowStyleMask:_styleMask],
578  windowView = [[fullPlatformWindowViewClass alloc] initWithFrame:CGRectMakeZero() styleMask:_styleMask];
579 
580  if (_platformWindow != [CPPlatformWindow primaryPlatformWindow] && [_platformWindow _hasInitializeInstanceWithWindow])
581  [_platformWindow setContentRect:[self frame]];
582 
583  [self _setWindowView:windowView];
584 
585  [self setLevel:CPBackgroundWindowLevel];
586  [self setHasShadow:NO];
587  [self setAutoresizingMask:CPWindowWidthSizable | CPWindowHeightSizable];
588  [self setFrame:[_platformWindow visibleFrame]];
589  }
590  else
591  {
592  var windowView = _fullPlatformWindowSession.windowView;
593 
594  [self _setWindowView:windowView];
595 
596  [self setLevel:_fullPlatformWindowSession.level];
597  [self setHasShadow:_fullPlatformWindowSession.hasShadow];
598  [self setAutoresizingMask:CPWindowNotSizable];
599 
600  [self setFrame:[windowView frameRectForContentRect:_fullPlatformWindowSession.contentRect]];
601  }
602 }
603 
608 {
609  return _isFullPlatformWindow;
610 }
611 
615 - (unsigned)styleMask
616 {
617  return _styleMask;
618 }
619 
638 + (CGRect)frameRectForContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask
639 {
640  return [[[self class] _windowViewClassForStyleMask:aStyleMask] frameRectForContentRect:aContentRect];
641 }
642 
647 - (CGRect)contentRectForFrameRect:(CGRect)aFrame
648 {
649  return [_windowView contentRectForFrameRect:aFrame];
650 }
651 
657 - (CGRect)frameRectForContentRect:(CGRect)aContentRect
658 {
659  return [_windowView frameRectForContentRect:aContentRect];
660 }
661 
665 - (CGRect)frame
666 {
667  return CGRectMakeCopy(_frame);
668 }
669 
677 - (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
678 {
679  [self _setFrame:aFrame display:shouldDisplay animate:shouldAnimate constrainWidth:NO constrainHeight:YES];
680 }
681 
682 - (void)_setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate constrainWidth:(BOOL)shouldConstrainWidth constrainHeight:(BOOL)shouldConstrainHeight
683 {
684  var frame = CGRectMakeCopy(aFrame),
685  value = frame.origin.x,
686  delta = value - FLOOR(value);
687 
688  if (delta)
689  frame.origin.x = value > 0.879 ? CEIL(value) : FLOOR(value);
690 
691  value = frame.origin.y;
692  delta = value - FLOOR(value);
693 
694  if (delta)
695  frame.origin.y = value > 0.879 ? CEIL(value) : FLOOR(value);
696 
697  value = frame.size.width;
698  delta = value - FLOOR(value);
699 
700  if (delta)
701  frame.size.width = value > 0.15 ? CEIL(value) : FLOOR(value);
702 
703  value = frame.size.height;
704  delta = value - FLOOR(value);
705 
706  if (delta)
707  frame.size.height = value > 0.15 ? CEIL(value) : FLOOR(value);
708 
709  frame = [self _constrainFrame:frame toUsableScreenWidth:shouldConstrainWidth andHeight:shouldConstrainHeight];
710 
711  if (shouldAnimate)
712  {
713  [_frameAnimation stopAnimation];
714  _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:frame];
715 
716  [_frameAnimation startAnimation];
717  }
718  else
719  {
720  var origin = _frame.origin,
721  newOrigin = frame.origin,
722  originMoved = !CGPointEqualToPoint(origin, newOrigin);
723 
724  if (originMoved)
725  {
726  delta = CGPointMake(newOrigin.x - origin.x, newOrigin.y - origin.y);
727  origin.x = newOrigin.x;
728  origin.y = newOrigin.y;
729 
730 #if PLATFORM(DOM)
731  if (![self _sharesChromeWithPlatformWindow])
732  {
733  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, origin.x, origin.y);
734  }
735 #endif
736 
737  // reposition sheet
738  if ([self attachedSheet])
739  [self _setAttachedSheetFrameOrigin];
740 
741  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidMoveNotification object:self];
742  }
743 
744  var size = _frame.size,
745  newSize = frame.size;
746 
747  if (!CGSizeEqualToSize(size, newSize))
748  {
749  size.width = newSize.width;
750  size.height = newSize.height;
751 
752  if (!_isAnimating)
753  size = [self _sendDelegateWindowWillResizeToSize:size];
754 
755  [_windowView setFrameSize:size];
756 
757  if (_hasShadow)
758  [_shadowView setNeedsLayout];
759 
760  if (!_isAnimating)
761  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidResizeNotification object:self];
762  }
763 
764  if ([self _sharesChromeWithPlatformWindow])
765  [_platformWindow setContentRect:_frame];
766 
767  if (originMoved)
768  [self _moveChildWindows:delta];
769  }
770 
771  if ([_platformWindow _canUpdateContentRect] && _isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
772  [_platformWindow setContentRect:aFrame];
773 }
774 
775 /*
776  Constrain a frame so that the window remains at least partially visible on screen,
777  moving or resizing the frame as necessary.
778 */
779 - (CGRect)_constrainFrame:(CGRect)aFrame toUsableScreenWidth:(BOOL)constrainWidth andHeight:(BOOL)constrainHeight
780 {
781  var frame = CGRectMakeCopy(aFrame);
782 
783  if (!_constrainsToUsableScreen || !_isVisible)
784  return frame;
785 
786  var usableRect = [_platformWindow usableContentFrame];
787 
788  if (constrainWidth)
789  {
790  // First move the frame right to ensure the left side is within the usable rect.
791  frame.origin.x = MAX(frame.origin.x, usableRect.origin.x);
792 
793  // Now move the frame left so that the right side is within the usable rect.
794  var maxX = MIN(CGRectGetMaxX(frame), CGRectGetMaxX(usableRect));
795  frame.origin.x = maxX - frame.size.width;
796 
797  // Finally, adjust the left + width to ensure the left side is within the usable rect.
798  var usableWidth = CGRectGetWidth(usableRect);
799 
800  if (CGRectGetWidth(frame) > usableWidth)
801  {
802  frame.origin.x = CGRectGetMinX(usableRect);
803  frame.size.width = MAX(usableWidth, _minSize.width);
804  }
805  }
806 
807  if (constrainHeight)
808  {
809  // First move the frame down to ensure the top is within the usable rect.
810  frame.origin.y = MAX(frame.origin.y, usableRect.origin.y);
811 
812  // Now move the frame up so that the bottom is within the usable rect.
813  var maxY = MIN(CGRectGetMaxY(frame), CGRectGetMaxY(usableRect));
814  frame.origin.y = maxY - frame.size.height;
815 
816  // Finally, adjust the top + height to ensure the top is within the usable rect.
817  var usableHeight = CGRectGetHeight(usableRect);
818 
819  if (CGRectGetHeight(frame) > usableHeight)
820  {
821  frame.origin.y = CGRectGetMinY(usableRect);
822  frame.size.height = MAX(usableHeight, _minSize.height);
823  }
824  }
825 
826  return frame;
827 }
828 
829 /*
830  Constrain the origin of a frame such that:
831 
832  - The window view's minimum resize width is kept onscreen at the left/right of the window.
833  - The top of the window is kept below the top of the usable content.
834  - The top of the contentView + CPWindowMinVisibleVerticalMargin is kept above the bottom of the usable content.
835 */
836 - (CGRect)_constrainOriginOfFrame:(CGRect)aFrame
837 {
838  var frame = CGRectMakeCopy(aFrame);
839 
840  if (!_constrainsToUsableScreen || !_isVisible)
841  return frame;
842 
843  var usableRect = [_platformWindow usableContentFrame],
844  minimumSize = [_windowView _minimumResizeSize];
845 
846  // First constrain x so that at least CPWindowMinVisibleHorizontalMargin is visible on the right
847  frame.origin.x = MAX(frame.origin.x, CGRectGetMinX(usableRect) + minimumSize.width - CGRectGetWidth(frame));
848 
849  // Now constrain x so that at least CPWindowMinVisibleHorizontalMargin is visible on the left
850  frame.origin.x = MIN(frame.origin.x, CGRectGetMaxX(usableRect) - minimumSize.width);
851 
852  // Now constrain y so that it is below the top of the usable content
853  frame.origin.y = MAX(frame.origin.y, CGRectGetMinY(usableRect));
854 
855  // Finally constrain y so that at least CPWindowMinVisibleHorizontalMargin is visible at the bottom
856  frame.origin.y = MIN(frame.origin.y, CGRectGetMaxY(usableRect) - CGRectGetMinY([_contentView frame]) - CPWindowMinVisibleVerticalMargin);
857 
858  return frame;
859 }
860 
861 - (void)_moveChildWindows:(CGPoint)delta
862 {
863  [_childWindows enumerateObjectsUsingBlock:function(childWindow)
864  {
865  var origin = [childWindow frame].origin;
866 
867  [childWindow setFrameOrigin:CGPointMake(origin.x + delta.x, origin.y + delta.y)];
868  }
869  ];
870 }
871 
877 - (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay
878 {
879  [self setFrame:aFrame display:shouldDisplay animate:NO];
880 }
881 
886 - (void)setFrame:(CGRect)aFrame
887 {
888  [self setFrame:aFrame display:YES animate:NO];
889 }
890 
895 - (void)setFrameOrigin:(CGPoint)anOrigin
896 {
897  var frame = [self _constrainOriginOfFrame:CGRectMake(anOrigin.x, anOrigin.y, _frame.size.width, _frame.size.height)];
898  [self _setFrame:frame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
899 }
900 
905 - (void)setFrameSize:(CGSize)aSize
906 {
907  [self setFrame:CGRectMake(CGRectGetMinX(_frame), CGRectGetMinY(_frame), aSize.width, aSize.height) display:YES animate:NO];
908 }
909 
914 - (void)orderFront:(id)aSender
915 {
916  [self orderWindow:CPWindowAbove relativeTo:0];
917 }
918 
919 - (void)_orderFront
920 {
921 
922 #if PLATFORM(DOM)
923 
924  if (!_isVisible)
925  [_platformWindow _setShouldUpdateContentRect:NO];
926 
927  // -dw- if a sheet is clicked, the parent window should come up too
928  if (_isSheet)
929  [_parentView orderFront:self];
930 
931  // Save the boolean since it will be updated in the method order:window:relativeTo:
932  var wasVisible = _isVisible;
933 
934  [_platformWindow orderFront:self];
935  [_platformWindow order:CPWindowAbove window:self relativeTo:nil];
936 
937  // setFrame is set after ordering the window as this method can send some notifications
938  if (!wasVisible)
939  [self _setFrame:_frame display:YES animate:NO constrainWidth:YES constrainHeight:YES];
940 #endif
941 
942  if (!CPApp._keyWindow)
943  [self makeKeyWindow];
944 
945  if ([self isKeyWindow] && (_firstResponder === self || !_firstResponder))
946  [self makeFirstResponder:_initialFirstResponder];
947 
948  if (!CPApp._mainWindow)
949  [self makeMainWindow];
950 
951  [_platformWindow _setShouldUpdateContentRect:YES];
952 }
953 
954 /*
955  Called when a parent window orders in a child window directly.
956  without going through the ordering methods in CPWindow.
957 */
958 - (void)_parentDidOrderInChild
959 {
960 }
961 
962 /*
963  Called when the window is displayed in the DOM
964 */
965 - (void)_windowWillBeAddedToTheDOM
966 {
969  name:_CPPlatformWindowWillCloseNotification
970  object:_platformWindow];
971 
972  [[self contentView] _addObservers];
973 }
974 
975 /*
976  Called when the window is removed in the DOM
977 */
978 - (void)_windowWillBeRemovedFromTheDOM
979 {
980  [[CPNotificationCenter defaultCenter] removeObserver:self name:_CPPlatformWindowWillCloseNotification object:nil];
981 
982  [[self contentView] _removeObservers];
983  _hasBecomeKeyWindow = NO;
984 }
985 
986 
987 /*
988  Makes the receiver the last window in the screen ordering.
989  @param aSender the object that requested this
990  @ignore
991 */
992 - (void)orderBack:(id)aSender
993 {
994  [self orderWindow:CPWindowBelow relativeTo:0];
995 }
996 
997 - (void)_orderBack
998 {
999  // FIXME: Implement this
1000 }
1001 
1006 - (void)orderOut:(id)aSender
1007 {
1008  [self orderWindow:CPWindowOut relativeTo:0];
1009 }
1010 
1011 - (void)_orderOutRecursively:(BOOL)recursive
1012 {
1013  if (!_isVisible)
1014  return;
1015 
1016  if ([self isSheet])
1017  {
1018  // -dw- as in Cocoa, orderOut: detaches the sheet and animates out
1019  [self._parentView _detachSheetWindow];
1020  return;
1021  }
1022 
1023  if (recursive)
1024  [_childWindows makeObjectsPerformSelector:@selector(_orderOutRecursively:) withObject:recursive];
1025 
1026 #if PLATFORM(DOM)
1027  if ([self _sharesChromeWithPlatformWindow])
1028  [_platformWindow orderOut:self];
1029 
1030  if (_isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
1031  [_platformWindow orderOut:self];
1032 
1033  [_platformWindow order:CPWindowOut window:self relativeTo:nil];
1034 #endif
1035 
1036  [self makeFirstResponder:nil];
1037  [self _updateMainAndKeyWindows];
1038 }
1039 
1045 - (void)orderWindow:(CPWindowOrderingMode)orderingMode relativeTo:(int)otherWindowNumber
1046 {
1047  if (orderingMode === CPWindowOut)
1048  {
1049  // Directly ordering out will detach a child window
1050  [_parentWindow removeChildWindow:self];
1051 
1052  // In Cocoa, a window orders out its child windows only if it has no parent
1053  [self _orderOutRecursively:!_parentWindow];
1054  }
1055  else if (orderingMode === CPWindowAbove && otherWindowNumber === 0)
1056  [self _orderFront];
1057  else if (orderingMode === CPWindowBelow && otherWindowNumber === 0)
1058  [self _orderBack];
1059 #if PLATFORM(DOM)
1060  else
1061  [_platformWindow order:orderingMode window:self relativeTo:CPApp._windows[otherWindowNumber]];
1062 #endif
1063 }
1064 
1069 - (void)setLevel:(int)aLevel
1070 {
1071  if (aLevel === _level)
1072  return;
1073 
1074  [_platformWindow moveWindow:self fromLevel:_level toLevel:aLevel];
1075 
1076  _level = aLevel;
1077  [_childWindows makeObjectsPerformSelector:@selector(setLevel:) withObject:_level];
1078 
1079  if ([self _sharesChromeWithPlatformWindow])
1080  [_platformWindow setLevel:aLevel];
1081 }
1082 
1086 - (int)level
1087 {
1088  return _level;
1089 }
1090 
1094 - (BOOL)isVisible
1095 {
1096  return _isVisible;
1097 }
1098 
1104 + (void)setGlobalResizeStyle:(int)aStyle
1105 {
1106  if (CPWindowResizeStyle === aStyle)
1107  return;
1108 
1109  CPWindowResizeStyle = aStyle;
1110  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowResizeStyleGlobalChangeNotification object:nil];
1111 }
1112 
1117 + (void)setConstrainWindowsToUsableScreen:(BOOL)shouldConstrain
1118 {
1119  CPWindowConstrainToScreen = shouldConstrain;
1120 }
1121 
1126 {
1128 }
1129 
1130 - (void)_didReceiveResizeStyleChange:(CPNotification)aNotification
1131 {
1132  [_windowView setShowsResizeIndicator:_styleMask & CPResizableWindowMask];
1133 }
1134 
1139 {
1140  return CPWindowResizeStyle;
1141 }
1142 
1147 {
1148  return _showsResizeIndicator;
1149 }
1150 
1155 - (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
1156 {
1157  shouldShowResizeIndicator = !!shouldShowResizeIndicator;
1158 
1159  if (_showsResizeIndicator === shouldShowResizeIndicator)
1160  return;
1161 
1162  _showsResizeIndicator = shouldShowResizeIndicator;
1163  [_windowView setShowsResizeIndicator:[self showsResizeIndicator]];
1164 }
1165 
1170 {
1171  return [_windowView resizeIndicatorOffset];
1172 }
1173 
1178 - (void)setResizeIndicatorOffset:(CGSize)anOffset
1179 {
1180  [_windowView setResizeIndicatorOffset:anOffset];
1181 }
1182 
1188 - (void)setContentView:(CPView)aView
1189 {
1190  if (_contentView && _contentView !== aView)
1191  [_contentView removeFromSuperview];
1192 
1193  var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
1194 
1195  _contentView = aView;
1196  [_contentView setFrame:[self contentRectForFrameRect:bounds]];
1197 
1198  [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1199  [_windowView addSubview:_contentView];
1200 
1201  /*
1202  If the initial first responder has been set to something other than
1203  the window, set it to the window because it will no longer be valid.
1204  */
1205  if (_initialFirstResponder && _initialFirstResponder !== self)
1206  _initialFirstResponder = self;
1207 }
1208 
1213 {
1214  return _contentView;
1215 }
1216 
1221 - (void)setAlphaValue:(float)aValue
1222 {
1223  [_windowView setAlphaValue:aValue];
1224 }
1225 
1229 - (float)alphaValue
1230 {
1231  return [_windowView alphaValue];
1232 }
1233 
1238 - (void)setBackgroundColor:(CPColor)aColor
1239 {
1240  [_windowView setBackgroundColor:aColor];
1241 }
1242 
1247 {
1248  return [_windowView backgroundColor];
1249 }
1250 
1258 - (void)setMinSize:(CGSize)aSize
1259 {
1260  if (CGSizeEqualToSize(_minSize, aSize))
1261  return;
1262 
1263  _minSize = [self _calculateMinSizeForProposedSize:aSize];
1264 
1265  var size = CGSizeMakeCopy([self frame].size),
1266  needsFrameChange = NO;
1267 
1268  if (size.width < _minSize.width)
1269  {
1270  size.width = _minSize.width;
1271  needsFrameChange = YES;
1272  }
1273 
1274  if (size.height < _minSize.height)
1275  {
1276  size.height = _minSize.height;
1277  needsFrameChange = YES;
1278  }
1279 
1280  if (needsFrameChange)
1281  [self setFrameSize:size];
1282 }
1283 
1287 - (CGSize)minSize
1288 {
1289  return _minSize;
1290 }
1291 
1293 - (CGSize)_calculateMinSizeForProposedSize:(CGSize)proposedSize
1294 {
1295  var contentFrame = [self contentRectForFrameRect:_frame],
1296  minHeight = CGRectGetHeight(_frame) - CGRectGetHeight(contentFrame);
1297 
1298  return CGSizeMake(MAX(proposedSize.width, 0), MAX(proposedSize.height, minHeight));
1299 }
1300 
1307 - (void)setMaxSize:(CGSize)aSize
1308 {
1309  if (CGSizeEqualToSize(_maxSize, aSize))
1310  return;
1311 
1312  _maxSize = CGSizeMakeCopy(aSize);
1313 
1314  var size = CGSizeMakeCopy([self frame].size),
1315  needsFrameChange = NO;
1316 
1317  if (size.width > _maxSize.width)
1318  {
1319  size.width = _maxSize.width;
1320  needsFrameChange = YES;
1321  }
1322 
1323  if (size.height > _maxSize.height)
1324  {
1325  size.height = _maxSize.height;
1326  needsFrameChange = YES;
1327  }
1328 
1329  if (needsFrameChange)
1330  [self setFrameSize:size];
1331 }
1332 
1336 - (CGSize)maxSize
1337 {
1338  return _maxSize;
1339 }
1340 
1344 - (BOOL)hasShadow
1345 {
1346  return _hasShadow;
1347 }
1348 
1349 - (void)_updateShadow
1350 {
1351  if ([self _sharesChromeWithPlatformWindow])
1352  {
1353  if (_shadowView)
1354  {
1355 #if PLATFORM(DOM)
1356  CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
1357 #endif
1358  _shadowView = nil;
1359  }
1360 
1361  [_platformWindow setHasShadow:_hasShadow];
1362 
1363  return;
1364  }
1365 
1366  if (_hasShadow && !_shadowView)
1367  {
1368  _shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
1369 
1370  [_shadowView setWindowView:_windowView];
1371  [_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1372  [_shadowView setNeedsLayout];
1373 
1374 #if PLATFORM(DOM)
1375  CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement);
1376 #endif
1377  }
1378  else if (!_hasShadow && _shadowView)
1379  {
1380 #if PLATFORM(DOM)
1381  CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
1382 #endif
1383  _shadowView = nil;
1384  }
1385 }
1386 
1391 - (void)setHasShadow:(BOOL)shouldHaveShadow
1392 {
1393  if (_hasShadow === shouldHaveShadow)
1394  return;
1395 
1396  _hasShadow = shouldHaveShadow;
1397 
1398  [self _updateShadow];
1399 }
1400 
1412 - (void)setShadowStyle:(unsigned)aStyle
1413 {
1414  _shadowStyle = aStyle;
1415 
1416  [[self platformWindow] setShadowStyle:_shadowStyle];
1417 }
1418 
1423 - (void)setDelegate:(id <CPWindowDelegate>)aDelegate
1424 {
1425  if (_delegate === aDelegate)
1426  return;
1427 
1428  var defaultCenter = [CPNotificationCenter defaultCenter];
1429 
1430  [defaultCenter removeObserver:_delegate name:CPWindowDidResignKeyNotification object:self];
1431  [defaultCenter removeObserver:_delegate name:CPWindowDidBecomeKeyNotification object:self];
1432  [defaultCenter removeObserver:_delegate name:CPWindowDidBecomeMainNotification object:self];
1433  [defaultCenter removeObserver:_delegate name:CPWindowDidResignMainNotification object:self];
1434  [defaultCenter removeObserver:_delegate name:CPWindowDidMoveNotification object:self];
1435  [defaultCenter removeObserver:_delegate name:CPWindowDidResizeNotification object:self];
1436  [defaultCenter removeObserver:_delegate name:CPWindowWillBeginSheetNotification object:self];
1437  [defaultCenter removeObserver:_delegate name:CPWindowDidEndSheetNotification object:self];
1438  [defaultCenter removeObserver:_delegate name:CPWindowDidMiniaturizeNotification object:self];
1439  [defaultCenter removeObserver:_delegate name:CPWindowDidDeminiaturizeNotification object:self];
1440  [defaultCenter removeObserver:_delegate name:CPWindowWillMiniaturizeNotification object:self];
1441 
1442  _delegate = aDelegate;
1443  _implementedDelegateMethods = 0;
1444 
1445  if ([_delegate respondsToSelector:@selector(windowShouldClose:)])
1446  _implementedDelegateMethods |= CPWindowDelegate_windowShouldClose_;
1447 
1448  if ([_delegate respondsToSelector:@selector(windowWillReturnUndoManager:)])
1449  _implementedDelegateMethods |= CPWindowDelegate_windowWillReturnUndoManager_;
1450 
1451  if ([_delegate respondsToSelector:@selector(windowWillClose:)])
1452  _implementedDelegateMethods |= CPWindowDelegate_windowWillClose_;
1453 
1454  if ([_delegate respondsToSelector:@selector(windowWillResize:toSize:)])
1455  _implementedDelegateMethods |= CPWindowDelegate_windowWillResize_toSize_;
1456 
1457  if ([_delegate respondsToSelector:@selector(windowDidResignKey:)])
1458  [defaultCenter
1459  addObserver:_delegate
1460  selector:@selector(windowDidResignKey:)
1461  name:CPWindowDidResignKeyNotification
1462  object:self];
1463 
1464  if ([_delegate respondsToSelector:@selector(windowDidBecomeKey:)])
1465  [defaultCenter
1466  addObserver:_delegate
1467  selector:@selector(windowDidBecomeKey:)
1468  name:CPWindowDidBecomeKeyNotification
1469  object:self];
1470 
1471  if ([_delegate respondsToSelector:@selector(windowDidBecomeMain:)])
1472  [defaultCenter
1473  addObserver:_delegate
1474  selector:@selector(windowDidBecomeMain:)
1475  name:CPWindowDidBecomeMainNotification
1476  object:self];
1477 
1478  if ([_delegate respondsToSelector:@selector(windowDidResignMain:)])
1479  [defaultCenter
1480  addObserver:_delegate
1481  selector:@selector(windowDidResignMain:)
1482  name:CPWindowDidResignMainNotification
1483  object:self];
1484 
1485  if ([_delegate respondsToSelector:@selector(windowDidMove:)])
1486  [defaultCenter
1487  addObserver:_delegate
1488  selector:@selector(windowDidMove:)
1489  name:CPWindowDidMoveNotification
1490  object:self];
1491 
1492  if ([_delegate respondsToSelector:@selector(windowDidResize:)])
1493  [defaultCenter
1494  addObserver:_delegate
1495  selector:@selector(windowDidResize:)
1496  name:CPWindowDidResizeNotification
1497  object:self];
1498 
1499  if ([_delegate respondsToSelector:@selector(windowWillBeginSheet:)])
1500  [defaultCenter
1501  addObserver:_delegate
1502  selector:@selector(windowWillBeginSheet:)
1503  name:CPWindowWillBeginSheetNotification
1504  object:self];
1505 
1506  if ([_delegate respondsToSelector:@selector(windowDidEndSheet:)])
1507  [defaultCenter
1508  addObserver:_delegate
1509  selector:@selector(windowDidEndSheet:)
1510  name:CPWindowDidEndSheetNotification
1511  object:self];
1512 
1513  if ([_delegate respondsToSelector:@selector(windowDidMiniaturize:)])
1514  [defaultCenter
1515  addObserver:_delegate
1516  selector:@selector(windowDidMiniaturize:)
1517  name:CPWindowDidMiniaturizeNotification
1518  object:self];
1519 
1520  if ([_delegate respondsToSelector:@selector(windowWillMiniaturize:)])
1521  [defaultCenter
1522  addObserver:_delegate
1523  selector:@selector(windowWillMiniaturize:)
1524  name:CPWindowWillMiniaturizeNotification
1525  object:self];
1526 
1527  if ([_delegate respondsToSelector:@selector(windowDidDeminiaturize:)])
1528  [defaultCenter
1529  addObserver:_delegate
1530  selector:@selector(windowDidDeminiaturize:)
1531  name:CPWindowDidDeminiaturizeNotification
1532  object:self];
1533 }
1534 
1539 {
1540  return _delegate;
1541 }
1542 
1547 - (void)setWindowController:(CPWindowController)aWindowController
1548 {
1549  _windowController = aWindowController;
1550 }
1551 
1556 {
1557  return _windowController;
1558 }
1559 
1560 - (void)doCommandBySelector:(SEL)aSelector
1561 {
1562  if ([_delegate respondsToSelector:aSelector])
1563  [_delegate performSelector:aSelector];
1564  else
1565  [super doCommandBySelector:aSelector];
1566 }
1567 
1569 {
1570  return NO;
1571 }
1572 
1574 {
1575  return _initialFirstResponder;
1576 }
1577 
1578 - (void)setInitialFirstResponder:(CPView)aView
1579 {
1580  _initialFirstResponder = aView;
1581 }
1582 
1583 - (void)_setupFirstResponder
1584 {
1585  /*
1586  When the window is first made the key window, if the first responder is the window, use the initial first responder if there is one. If there is a first responder and it is not the window, ignore the initial first responder.
1587  */
1588  if (!_hasBecomeKeyWindow)
1589  {
1590  if (_firstResponder === self)
1591  {
1592  if (_initialFirstResponder)
1593  [self makeFirstResponder:_initialFirstResponder];
1594  else
1595  {
1596  // Make the first valid key view the first responder
1597  var view = [_contentView nextValidKeyView];
1598 
1599  if (view)
1600  [self makeFirstResponder:view];
1601  }
1602 
1603  return;
1604  }
1605  }
1606 
1607  if (_firstResponder)
1608  [self makeFirstResponder:_firstResponder];
1609 }
1610 
1618 - (BOOL)makeFirstResponder:(CPResponder)aResponder
1619 {
1620  if (_firstResponder === aResponder)
1621  return YES;
1622 
1623  if (![_firstResponder resignFirstResponder])
1624  return NO;
1625 
1626  if (!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder])
1627  {
1628  _firstResponder = self;
1629 
1630  return NO;
1631  }
1632 
1633  _firstResponder = aResponder;
1634 
1635  [[CPNotificationCenter defaultCenter] postNotificationName:_CPWindowDidChangeFirstResponderNotification object:self];
1636 
1637  return YES;
1638 }
1639 
1644 {
1645  return _firstResponder;
1646 }
1647 
1649 {
1650  return _acceptsMouseMovedEvents;
1651 }
1652 
1653 - (void)setAcceptsMouseMovedEvents:(BOOL)shouldAcceptMouseMovedEvents
1654 {
1655  _acceptsMouseMovedEvents = shouldAcceptMouseMovedEvents;
1656 }
1657 
1659 {
1660  return _ignoresMouseEvents;
1661 }
1662 
1663 - (void)setIgnoresMouseEvents:(BOOL)shouldIgnoreMouseEvents
1664 {
1665  _ignoresMouseEvents = shouldIgnoreMouseEvents;
1666 }
1667 
1668 - (void)_mouseExitedResizeRect
1669 {
1670  [[CPCursor arrowCursor] set];
1671 }
1672 
1673 // Managing Titles
1674 
1679 {
1680  return _title;
1681 }
1682 
1686 - (void)setTitle:(CPString)aTitle
1687 {
1688  _title = aTitle;
1689 
1690  [_windowView setTitle:aTitle];
1691  [_platformWindow _setTitle:_title window:self];
1692 }
1693 
1697 - (void)setTitleWithRepresentedFilename:(CPString)aFilePath
1698 {
1699  [self setRepresentedFilename:aFilePath];
1700  [self setTitle:[aFilePath lastPathComponent]];
1701 }
1702 
1706 - (void)setRepresentedFilename:(CPString)aFilePath
1707 {
1708  // FIXME: urls vs filepaths and all.
1709  [self setRepresentedURL:[CPURL URLWithString:aFilePath]];
1710 }
1711 
1716 {
1717  return [_representedURL absoluteString];
1718 }
1719 
1723 - (void)setRepresentedURL:(CPURL)aURL
1724 {
1725  _representedURL = aURL;
1726 }
1727 
1732 {
1733  return _representedURL;
1734 }
1735 
1737 {
1738  return [[CPScreen alloc] init];
1739 }
1740 
1741 // Moving
1742 
1747 - (void)setMovableByWindowBackground:(BOOL)shouldBeMovableByWindowBackground
1748 {
1749  _isMovableByWindowBackground = shouldBeMovableByWindowBackground;
1750 }
1751 
1756 {
1757  return _isMovableByWindowBackground;
1758 }
1759 
1764 - (void)setMovable:(BOOL)shouldBeMovable
1765 {
1766  _isMovable = shouldBeMovable;
1767 }
1768 
1772 - (void)isMovable
1773 {
1774  return _isMovable;
1775 }
1776 
1780 - (void)center
1781 {
1782  if (_isFullPlatformWindow)
1783  return;
1784 
1785  var size = [self frame].size,
1786  containerSize = [CPPlatform isBrowser] ? [_platformWindow contentBounds].size : [[self screen] visibleFrame].size;
1787 
1788  var origin = CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0);
1789 
1790  if (origin.x < 0.0)
1791  origin.x = 0.0;
1792 
1793  if (origin.y < 0.0)
1794  origin.y = 0.0;
1795 
1796  [self setFrameOrigin:origin];
1797 }
1798 
1803 - (void)sendEvent:(CPEvent)anEvent
1804 {
1805  var type = [anEvent type],
1806  sheet = [self attachedSheet];
1807 
1808  // If a sheet is attached events get filtered here.
1809  // It is not clear what events should be passed to the view, perhaps all?
1810  // CPLeftMouseDown is needed for window moving and resizing to work.
1811  // CPMouseMoved is needed for rollover effects on title bar buttons.
1812 
1813  if (sheet)
1814  {
1815  switch (type)
1816  {
1817  case CPLeftMouseDown:
1818 
1819  // This is needed when a doubleClick occurs when the sheet is closing or opening
1820  if (!_parentWindow)
1821  return;
1822 
1823  [_windowView mouseDown:anEvent];
1824 
1825  // -dw- if the window is clicked, the sheet should come to front, and become key,
1826  // and the window should be immediately behind
1827  [sheet makeKeyAndOrderFront:self];
1828 
1829  return;
1830 
1831  case CPMouseMoved:
1832  // Allow these through to the parent
1833  break;
1834 
1835  default:
1836  // Everything else is filtered
1837  return;
1838  }
1839  }
1840 
1841  var point = [anEvent locationInWindow];
1842 
1843  switch (type)
1844  {
1845  case CPAppKitDefined:
1846  return [CPApp activateIgnoringOtherApps:YES];
1847 
1848  case CPFlagsChanged:
1849  return [[self firstResponder] flagsChanged:anEvent];
1850 
1851  case CPKeyUp:
1852  return [[self firstResponder] keyUp:anEvent];
1853 
1854  case CPKeyDown:
1855  if ([anEvent charactersIgnoringModifiers] === CPTabCharacter)
1856  {
1857  if ([anEvent modifierFlags] & CPShiftKeyMask)
1858  [self selectPreviousKeyView:self];
1859  else
1860  [self selectNextKeyView:self];
1861 #if PLATFORM(DOM)
1862  // Make sure the browser doesn't try to do its own tab handling.
1863  // This is important or the browser might blur the shared text field or token field input field,
1864  // even that we just moved it to a new first responder.
1865  [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
1866 #endif
1867  return;
1868  }
1869  else if ([anEvent charactersIgnoringModifiers] === CPBackTabCharacter)
1870  {
1871  var didTabBack = [self selectPreviousKeyView:self];
1872 
1873  if (didTabBack)
1874  {
1875 #if PLATFORM(DOM)
1876  // Make sure the browser doesn't try to do its own tab handling.
1877  // This is important or the browser might blur the shared text field or token field input field,
1878  // even that we just moved it to a new first responder.
1879  [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
1880 #endif
1881 
1882  }
1883  return didTabBack;
1884  }
1885  else if ([anEvent charactersIgnoringModifiers] == CPEscapeFunctionKey && [self _processKeyboardUIKey:anEvent])
1886  {
1887  return;
1888  }
1889 
1890  [[self firstResponder] keyDown:anEvent];
1891 
1892  // Trigger the default button if needed
1893  // FIXME: Is this only applicable in a sheet? See isse: #722.
1895  {
1896  var defaultButton = [self defaultButton],
1897  keyEquivalent = [defaultButton keyEquivalent],
1898  modifierMask = [defaultButton keyEquivalentModifierMask];
1899 
1900  if ([anEvent _triggersKeyEquivalent:keyEquivalent withModifierMask:modifierMask])
1901  [[self defaultButton] performClick:self];
1902  }
1903 
1904  return;
1905 
1906  case CPScrollWheel:
1907  return [[_windowView hitTest:point] scrollWheel:anEvent];
1908 
1909  case CPLeftMouseUp:
1910  case CPRightMouseUp:
1911  var hitTestedView = _leftMouseDownView,
1912  selector = type == CPRightMouseUp ? @selector(rightMouseUp:) : @selector(mouseUp:);
1913 
1914  if (!hitTestedView)
1915  hitTestedView = [_windowView hitTest:point];
1916 
1917  [hitTestedView performSelector:selector withObject:anEvent];
1918 
1919  _leftMouseDownView = nil;
1920 
1921  // If mouseUp ends a drag operation, send delayed events for tracking views under the mouse, then flush delayed events
1922  [self _flushTrackingEventQueueForMouseAt:point];
1923 
1924  return;
1925 
1926  case CPLeftMouseDown:
1927  case CPRightMouseDown:
1928  // This will return _windowView if it is within a resize region
1929  _leftMouseDownView = [_windowView hitTest:point];
1930 
1931  if (_leftMouseDownView !== _firstResponder && [_leftMouseDownView acceptsFirstResponder])
1932  [self makeFirstResponder:_leftMouseDownView];
1933 
1934  var keyWindow = [CPApp keyWindow];
1935 
1936  // This is only when we move from a platform to another one
1937  if ([keyWindow platformWindow] != [self platformWindow])
1938  [self makeKeyAndOrderFront:self];
1939 
1940  [CPApp activateIgnoringOtherApps:YES];
1941 
1942  var theWindow = [anEvent window],
1943  selector = type == CPRightMouseDown ? @selector(rightMouseDown:) : @selector(mouseDown:);
1944 
1945  if ([theWindow isKeyWindow] || ([theWindow becomesKeyOnlyIfNeeded] && ![_leftMouseDownView needsPanelToBecomeKey]))
1946  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1947  else
1948  {
1949  // FIXME: delayed ordering?
1950  [self makeKeyAndOrderFront:self];
1951 
1952  if ([_leftMouseDownView acceptsFirstMouse:anEvent])
1953  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1954  }
1955  break;
1956 
1957  case CPLeftMouseDragged:
1958  case CPRightMouseDragged:
1959  // First, we search for any tracking area requesting CPTrackingEnabledDuringMouseDrag.
1960  // At the same time, we update the entered stack.
1961  [self _handleTrackingAreaEvent:anEvent];
1962 
1963  // Normal mouseDragged workflow
1964  if (!_leftMouseDownView)
1965  return [[_windowView hitTest:point] mouseDragged:anEvent];
1966 
1967  var selector;
1968 
1969  if (type == CPRightMouseDragged)
1970  {
1971  selector = @selector(rightMouseDragged:)
1972  if (![_leftMouseDownView respondsToSelector:selector])
1973  selector = nil;
1974  }
1975 
1976  if (!selector)
1977  selector = @selector(mouseDragged:)
1978 
1979  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1980 
1981  case CPMouseMoved:
1982 
1983  // Ignore mouse moves for parents of sheets
1984  if (!_acceptsMouseMovedEvents || sheet)
1985  return;
1986 
1987  [self _handleTrackingAreaEvent:anEvent];
1988  }
1989 }
1990 
1995 {
1996  return _windowNumber;
1997 }
1998 
2005 {
2006  CPApp._keyWindow = self;
2007 
2008  if (_firstResponder !== self && [_firstResponder respondsToSelector:@selector(becomeKeyWindow)])
2009  [_firstResponder becomeKeyWindow];
2010 
2011  if (!_hasBecomeKeyWindow)
2012  {
2013  // The first time a window is loaded, if it does not have a key view loop
2014  // established, calculate it now.
2015  if (![self _hasKeyViewLoop:[_contentView subviews]])
2016  [self recalculateKeyViewLoop];
2017  }
2018 
2019  [self _setupFirstResponder];
2020  _hasBecomeKeyWindow = YES;
2021  _platformWindow._currentKeyWindow = self;
2022 
2023  [_windowView noteKeyWindowStateChanged];
2024  [_contentView _notifyWindowDidBecomeKey];
2025 
2027  postNotificationName:CPWindowDidBecomeKeyNotification
2028  object:self];
2029 }
2030 
2036 {
2037  /*
2038  In Cocoa only titled windows return YES here by default. But the main browser
2039  window in Cappuccino doesn't have a title bar even that it's both titled and
2040  resizable, so we return YES when isFullPlatformWindow too.
2041 
2042  Note that Cocoa will return NO for a non-titled, resizable window. The Cocoa documention
2043  says it will return YES if there is a "resize bar", but in practice
2044  that is not the same as the resizable mask.
2045  */
2046  return (_styleMask & CPTitledWindowMask) || [self isFullPlatformWindow] || _isSheet;
2047 }
2048 
2053 {
2054  return [CPApp keyWindow] == self;
2055 }
2056 
2061 - (void)makeKeyAndOrderFront:(id)aSender
2062 {
2063  [self orderFront:self];
2064 
2065  [self makeKeyWindow];
2066  [self makeMainWindow];
2067 }
2068 
2073 {
2074  if ([CPApp keyWindow] === self || ![self canBecomeKeyWindow])
2075  return;
2076 
2077  [[CPApp keyWindow] resignKeyWindow];
2078  [self becomeKeyWindow];
2079 }
2080 
2085 {
2086  if (_firstResponder !== self && [_firstResponder respondsToSelector:@selector(resignKeyWindow)])
2087  [_firstResponder resignKeyWindow];
2088 
2089  if (CPApp._keyWindow === self)
2090  CPApp._keyWindow = nil;
2091 
2092  _platformWindow._currentKeyWindow = nil;
2093  [_windowView noteKeyWindowStateChanged];
2094  [_contentView _notifyWindowDidResignKey];
2095 
2097  postNotificationName:CPWindowDidResignKeyNotification
2098  object:self];
2099 }
2100 
2111 - (void)dragImage:(CPImage)anImage at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2112 {
2113  [[CPDragServer sharedDragServer] dragImage:anImage fromWindow:self at:[self convertBaseToGlobal:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2114 }
2115 
2116 - (void)_noteRegisteredDraggedTypes:(CPSet)pasteboardTypes
2117 {
2118  if (!pasteboardTypes)
2119  return;
2120 
2121  if (!_inclusiveRegisteredDraggedTypes)
2122  _inclusiveRegisteredDraggedTypes = [CPCountedSet set];
2123 
2124  [_inclusiveRegisteredDraggedTypes unionSet:pasteboardTypes];
2125 }
2126 
2127 - (void)_noteUnregisteredDraggedTypes:(CPSet)pasteboardTypes
2128 {
2129  if (!pasteboardTypes)
2130  return;
2131 
2132  [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes];
2133 
2134  if ([_inclusiveRegisteredDraggedTypes count] === 0)
2135  _inclusiveRegisteredDraggedTypes = nil;
2136 }
2137 
2148 - (void)dragView:(CPView)aView at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2149 {
2150  [[CPDragServer sharedDragServer] dragView:aView fromWindow:self at:[self convertBaseToGlobal:viewLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2151 }
2152 
2157 - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
2158 {
2159  if (!pasteboardTypes)
2160  return;
2161 
2162  [self _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2163  [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
2164  [self _noteRegisteredDraggedTypes:_registeredDraggedTypes];
2165 
2166  _registeredDraggedTypesArray = nil;
2167 }
2168 
2174 {
2175  if (!_registeredDraggedTypesArray)
2176  _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
2177 
2178  return _registeredDraggedTypesArray;
2179 }
2180 
2185 {
2186  [self _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2187 
2188  _registeredDraggedTypes = [CPSet set];
2189  _registeredDraggedTypesArray = [];
2190 }
2191 
2192 // Accessing Editing Status
2193 
2198 - (void)setDocumentEdited:(BOOL)isDocumentEdited
2199 {
2200  if (_isDocumentEdited == isDocumentEdited)
2201  return;
2202 
2203  _isDocumentEdited = isDocumentEdited;
2204 
2205  [CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0];
2206 
2207  [_windowView setDocumentEdited:isDocumentEdited];
2208 }
2209 
2214 {
2215  return _isDocumentEdited;
2216 }
2217 
2218 - (void)setDocumentSaving:(BOOL)isDocumentSaving
2219 {
2220  if (_isDocumentSaving == isDocumentSaving)
2221  return;
2222 
2223  _isDocumentSaving = isDocumentSaving;
2224 
2225  [self _synchronizeSaveMenuWithDocumentSaving];
2226 
2227  [_windowView windowDidChangeDocumentSaving];
2228 }
2229 
2231 {
2232  return _isDocumentSaving;
2233 }
2234 
2235 /* @ignore */
2236 - (void)_synchronizeSaveMenuWithDocumentSaving
2237 {
2238  if (![self isMainWindow])
2239  return;
2240 
2241  var mainMenu = [CPApp mainMenu],
2242  index = [mainMenu indexOfItemWithTitle:_isDocumentSaving ? @"Save" : @"Saving..."];
2243 
2244  if (index == CPNotFound)
2245  return;
2246 
2247  var item = [mainMenu itemAtIndex:index];
2248 
2249  if (_isDocumentSaving)
2250  {
2251  CPWindowSaveImage = [item image];
2252 
2253  [item setTitle:@"Saving..."];
2254  [item setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"spinning-regular-gif" forClass:CPProgressIndicator]];
2255  [item setEnabled:NO];
2256  }
2257  else
2258  {
2259  [item setTitle:@"Save"];
2260  [item setImage:CPWindowSaveImage];
2261  [item setEnabled:YES];
2262  }
2263 }
2264 
2265 // Minimizing Windows
2266 
2271 - (void)performMiniaturize:(id)aSender
2272 {
2273  //FIXME show stuff
2274  [self miniaturize:aSender];
2275 }
2276 
2281 - (void)miniaturize:(id)sender
2282 {
2283  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillMiniaturizeNotification object:self];
2284 
2285  [[self platformWindow] miniaturize:sender];
2286 
2287  [self _updateMainAndKeyWindows];
2288 
2289  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidMiniaturizeNotification object:self];
2290 
2291  _isMiniaturized = YES;
2292 }
2293 
2297 - (void)deminiaturize:(id)sender
2298 {
2299  [[self platformWindow] deminiaturize:sender];
2300 
2301  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidDeminiaturizeNotification object:self];
2302 
2303  _isMiniaturized = NO;
2304 }
2305 
2310 {
2311  return _isMiniaturized;
2312 }
2313 
2314 // Closing Windows
2315 
2320 - (void)performClose:(id)aSender
2321 {
2322  if (!(_styleMask & CPClosableWindowMask))
2323  return;
2324 
2325  if ([self isFullPlatformWindow])
2326  {
2327  var event = [CPApp currentEvent];
2328 
2329  if ([event type] === CPKeyDown && [event characters] === "w" && ([event modifierFlags] & CPPlatformActionKeyMask))
2330  {
2331  [[self platformWindow] _propagateCurrentDOMEvent:YES];
2332  return;
2333  }
2334  }
2335 
2336  // The Cocoa docs say that if both the delegate and the window implement
2337  // windowShouldClose:, only the delegate receives the message.
2338  if ([self _delegateRespondsToWindowShouldClose])
2339  {
2340  if (![self _sendDelegateWindowShouldClose])
2341  return;
2342  }
2343  else if ([self respondsToSelector:@selector(windowShouldClose:)])
2344  {
2345  if (![self windowShouldClose:self])
2346  return;
2347  }
2348 
2349  var documents = [_windowController documents];
2350 
2351  if ([documents count])
2352  {
2353  var index = [documents indexOfObject:[_windowController document]];
2354 
2355  [documents[index] shouldCloseWindowController:_windowController
2356  delegate:self
2357  shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:)
2358  contextInfo:{documents:[documents copy], visited:0, index:index}];
2359  }
2360  else
2361  [self close];
2362 }
2363 
2364 - (void)_windowControllerContainingDocument:(CPDocument)document shouldClose:(BOOL)shouldClose contextInfo:(Object)context
2365 {
2366  if (shouldClose)
2367  {
2368  var windowController = [self windowController],
2369  documents = context.documents,
2370  count = [documents count],
2371  visited = ++context.visited,
2372  index = ++context.index % count;
2373 
2374  [document removeWindowController:windowController];
2375 
2376  if (visited < count)
2377  {
2378  [windowController setDocument:documents[index]];
2379 
2380  [documents[index] shouldCloseWindowController:_windowController
2381  delegate:self
2382  shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:)
2383  contextInfo:context];
2384  }
2385  else
2386  [self close];
2387  }
2388 }
2389 
2394 - (void)close
2395 {
2396  [self _sendDelegateWindowWillClose];
2397 
2398  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self];
2399 
2400  [_parentWindow removeChildWindow:self];
2401  [self _orderOutRecursively:NO];
2402  [self _detachFromChildrenClosing:!_parentWindow];
2403 }
2404 
2405 - (void)_detachFromChildrenClosing:(BOOL)shouldCloseChildren
2406 {
2407  // When a window is closed, it must detach itself from all children
2408  [_childWindows enumerateObjectsUsingBlock:function(child)
2409  {
2410  [child setParentWindow:nil];
2411  }
2412  ];
2413 
2414  if (shouldCloseChildren)
2415  {
2416  // Cocoa does NOT call close or orderOut when closing child windows,// they are summarily closed.[_childWindows enumerateObjectsUsingBlock:function(child)
2417  {
2418 
2419 
2420  [child _orderOutRecursively:NO];
2421  [child _detachFromChildrenClosing:![child parentWindow]];
2422  }
2423  ];
2424  }
2425 
2426  _childWindows = [];
2427 }
2428 
2429 // Managing Main Status
2434 {
2435  return [CPApp mainWindow] === self;
2436 }
2437 
2442 {
2443  // Note that the Cocoa documentation says that this method returns YES if
2444  // the window is visible and has a title bar or a "resize mechanism". It turns
2445  // out a "resize mechanism" is not the same as having the resize mask set.
2446  // In practice a window must have a title bar to become main, but we make
2447  // an exception for a full platform window.
2448  return ([self isVisible] && ((_styleMask & CPTitledWindowMask) || _isFullPlatformWindow));
2449 }
2450 
2455 {
2456  // Sheets cannot be main. Their parent window becomes main.
2457  if (_isSheet)
2458  {
2459  [_parentView makeMainWindow];
2460  return;
2461  }
2462 
2463  if ([CPApp mainWindow] === self || ![self canBecomeMainWindow])
2464  return;
2465 
2466  [[CPApp mainWindow] resignMainWindow];
2467  [self becomeMainWindow];
2468 }
2469 
2474 {
2475  CPApp._mainWindow = self;
2476  _platformWindow._currentMainWindow = self;
2477 
2478  [self _synchronizeSaveMenuWithDocumentSaving];
2479 
2480  [_windowView noteMainWindowStateChanged];
2481 
2483  postNotificationName:CPWindowDidBecomeMainNotification
2484  object:self];
2485 }
2486 
2491 {
2493  postNotificationName:CPWindowDidResignMainNotification
2494  object:self];
2495 
2496  if (CPApp._mainWindow === self)
2497  CPApp._mainWindow = nil;
2498 
2499  _platformWindow._currentMainWindow = nil;
2500  [_windowView noteMainWindowStateChanged];
2501 }
2502 
2503 - (void)_updateMainAndKeyWindows
2504 {
2505  var allWindows = [CPApp orderedWindows],
2506  windowCount = [allWindows count];
2507 
2508  if ([self isKeyWindow])
2509  {
2510  var keyWindow = [CPApp keyWindow];
2511  [self resignKeyWindow];
2512 
2513  if (keyWindow && keyWindow !== self && [keyWindow canBecomeKeyWindow])
2514  [keyWindow makeKeyWindow];
2515  else
2516  {
2517  var mainMenu = [CPApp mainMenu],
2518  menuBarClass = objj_getClass("_CPMenuBarWindow"),
2519  menuWindow;
2520 
2521  for (var i = 0; i < windowCount; i++)
2522  {
2523  var currentWindow = allWindows[i];
2524 
2525  if ([currentWindow isKindOfClass:menuBarClass])
2526  menuWindow = currentWindow;
2527 
2528  if (currentWindow === self || currentWindow === menuWindow)
2529  continue;
2530 
2531  if ([currentWindow isVisible] && [currentWindow canBecomeKeyWindow] && [currentWindow platformWindow] == [keyWindow platformWindow])
2532  {
2533  [currentWindow makeKeyWindow];
2534  break;
2535  }
2536  }
2537 
2538  if (![CPApp keyWindow])
2539  [menuWindow makeKeyWindow];
2540  }
2541  }
2542 
2543  if ([self isMainWindow])
2544  {
2545  var mainWindow = [CPApp mainWindow];
2546  [self resignMainWindow];
2547 
2548  if (mainWindow && mainWindow !== self && [mainWindow canBecomeMainWindow])
2549  [mainWindow makeMainWindow];
2550  else
2551  {
2552  var mainMenu = [CPApp mainMenu],
2553  menuBarClass = objj_getClass("_CPMenuBarWindow"),
2554  menuWindow;
2555 
2556  for (var i = 0; i < windowCount; i++)
2557  {
2558  var currentWindow = allWindows[i];
2559 
2560  if ([currentWindow isKindOfClass:menuBarClass])
2561  menuWindow = currentWindow;
2562 
2563  if (currentWindow === self || currentWindow === menuWindow)
2564  continue;
2565 
2566  if ([currentWindow isVisible] && [currentWindow canBecomeMainWindow])
2567  {
2568  [currentWindow makeMainWindow];
2569  break;
2570  }
2571  }
2572  }
2573  }
2574 }
2575 
2576 // Managing Toolbars
2581 {
2582  return _toolbar;
2583 }
2584 
2589 - (void)setToolbar:(CPToolbar)aToolbar
2590 {
2591  if (_toolbar === aToolbar)
2592  return;
2593 
2594  // If this has an owner, dump it!
2595  [[aToolbar _window] setToolbar:nil];
2596 
2597  // This is no longer our toolbar.
2598  [_toolbar _setWindow:nil];
2599 
2600  _toolbar = aToolbar;
2601 
2602  // THIS is our toolbar.
2603  [_toolbar _setWindow:self];
2604 
2605  [self _noteToolbarChanged];
2606 }
2607 
2608 - (void)toggleToolbarShown:(id)aSender
2609 {
2610  var toolbar = [self toolbar];
2611 
2613 }
2614 
2615 - (void)_noteToolbarChanged
2616 {
2617  var frame = CGRectMakeCopy([self frame]),
2618  newFrame;
2619 
2620  [_windowView noteToolbarChanged];
2621 
2622  if (_isFullPlatformWindow)
2623  newFrame = [_platformWindow visibleFrame];
2624  else
2625  {
2626  newFrame = CGRectMakeCopy([self frame]);
2627 
2628  newFrame.origin = frame.origin;
2629  }
2630 
2631  [self setFrame:newFrame];
2632  /*
2633  [_windowView setAnimatingToolbar:YES];
2634  [self setFrame:frame];
2635  [self setFrame:newFrame display:YES animate:YES];
2636  [_windowView setAnimatingToolbar:NO];
2637  */
2638 }
2639 
2643 - (CPArray)childWindows
2644 {
2645  return _childWindows;
2646 }
2647 
2648 - (void)addChildWindow:(CPWindow)childWindow ordered:(CPWindowOrderingMode)orderingMode
2649 {
2650  // Don't add the child if it is already in our list
2651  if ([_childWindows indexOfObject:childWindow] >= 0)
2652  return;
2653 
2654  if (orderingMode === CPWindowAbove || orderingMode === CPWindowBelow)
2655  [_childWindows addObject:childWindow];
2656  else
2657  [CPException raise:CPInvalidArgumentException
2658  reason:_cmd + @" unrecognized ordering mode " + orderingMode];
2659 
2660  [childWindow setParentWindow:self];
2661  [childWindow _setChildOrdering:orderingMode];
2662  [childWindow setLevel:[self level]];
2663 
2664  if ([self isVisible] && ![childWindow isVisible])
2665  [childWindow orderWindow:orderingMode relativeTo:_windowNumber];
2666 }
2667 
2668 - (void)removeChildWindow:(CPWindow)childWindow
2669 {
2670  var index = [_childWindows indexOfObject:childWindow];
2671 
2672  if (index === CPNotFound)
2673  return;
2674 
2675  [_childWindows removeObjectAtIndex:index];
2676  [childWindow setParentWindow:nil];
2677 }
2678 
2680 {
2681  return _parentWindow;
2682 }
2683 
2689 - (BOOL)_hasAncestorWindow:(CPWindow)anAncestor
2690 {
2691  if (!_parentWindow || !anAncestor)
2692  return NO;
2693 
2694  if (anAncestor === _parentWindow)
2695  return YES;
2696 
2697  return [_parentWindow _hasAncestorWindow:anAncestor];
2698 }
2699 
2700 - (CPWindow)setParentWindow:(CPWindow)parentWindow
2701 {
2702  _parentWindow = parentWindow;
2703 }
2704 
2705 - (void)_setFrame:(CGRect)aFrame delegate:(id)delegate duration:(int)duration curve:(CPAnimationCurve)curve
2706 {
2707  [_frameAnimation stopAnimation];
2708  _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
2709  [_frameAnimation setDelegate:delegate];
2710  [_frameAnimation setAnimationCurve:curve];
2711  [_frameAnimation setDuration:duration];
2712  [_frameAnimation startAnimation];
2713 }
2714 
2715 - (CPTimeInterval)animationResizeTime:(CGRect)newWindowFrame
2716 {
2717  return CPWindowResizeTime;
2718 }
2719 
2720 - (void)_setAttachedSheetFrameOrigin
2721 {
2722  // Position the sheet above the contentRect.
2723  var attachedSheet = [self attachedSheet],
2724  contentRect = [_contentView frame],
2725  sheetFrame = CGRectMakeCopy([attachedSheet frame]);
2726 
2727  sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect);
2728  sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0);
2729 
2730  [attachedSheet setFrame:sheetFrame display:YES animate:NO];
2731 }
2732 
2733 - (void)_previousSheetIsClosedNotification:(CPNotification)aNotification
2734 {
2735  [[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidEndSheetNotification object:self];
2736 
2737  var sheet = _sheetContext[@"nextSheet"],
2738  modalDelegate = _sheetContext[@"nextModalDelegate"],
2739  endSelector = _sheetContext[@"nextEndSelector"],
2740  contextInfo = _sheetContext[@"nextContextInfo"];
2741 
2742  // Needed, because when the notification CPWindowDidEndSheetNotification is sent, the sheetContext is not up to date...
2743  setTimeout(function()
2744  {
2745  [sheet._windowView _enableSheet:YES inWindow:self];
2746  [self _attachSheet:sheet modalDelegate:modalDelegate didEndSelector:endSelector contextInfo:contextInfo];
2747  }, 0)
2748 }
2749 
2750 /*
2751  Starting point for sheet session, called from CPApplication beginSheet:
2752 */
2753 - (void)_attachSheet:(CPWindow)aSheet modalDelegate:(id)aModalDelegate
2754  didEndSelector:(SEL)didEndSelector contextInfo:(id)contextInfo
2755 {
2756  if (_sheetContext)
2757  {
2758  // Here we wait till the current sheet is closed
2759  if (_sheetContext[@"isClosing"])
2760  {
2761  // Here we save the next sheet to open
2762  _sheetContext[@"nextSheet"] = aSheet;
2763  _sheetContext[@"nextModalDelegate"] = aModalDelegate;
2764  _sheetContext[@"nextEndSelector"] = didEndSelector;
2765  _sheetContext[@"nextContextInfo"] = contextInfo;
2766 
2767  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_previousSheetIsClosedNotification:) name:CPWindowDidEndSheetNotification object:self];
2768  return;
2769  }
2770  else
2771  {
2772  [CPException raise:CPInternalInconsistencyException
2773  reason:@"The target window of beginSheet: already has a sheet, did you forget orderOut: ?"];
2774  return;
2775  }
2776  }
2777 
2778  _sheetContext = {
2779  "sheet": aSheet,
2780  "modalDelegate": aModalDelegate,
2781  "endSelector": didEndSelector,
2782  "contextInfo": contextInfo,
2783  "returnCode": -1,
2784  "opened": NO,
2785  "isAttached": YES,
2786  "savedConstrains": aSheet._constrainsToUsableScreen
2787  };
2788 
2789  // Sheets are not constrained, they are controlled by their parent
2790  aSheet._constrainsToUsableScreen = NO;
2791 
2792  // A timer seems to be necessary for the animation to work correctly
2794  target:self
2795  selector:@selector(_sheetShouldAnimateIn:)
2796  userInfo:nil
2797  repeats:NO];
2798 }
2799 
2800 /*
2801  Called to end the sheet. Note that orderOut: is needed to animate the sheet out, as in Cocoa.
2802  The sheet isn't completely gone until _cleanupSheetWindow gets called.
2803 */
2804 - (void)_endSheet
2805 {
2806  var delegate = _sheetContext["modalDelegate"],
2807  endSelector = _sheetContext["endSelector"];
2808 
2809  // If the sheet has been ordered out, defer didEndSelector until after sheet animates out.
2810  // This must be done since we cannot block and wait for the animation to complete.
2811  if (delegate && endSelector)
2812  {
2813  if (_sheetContext["isAttached"])
2814  delegate.isa.objj_msgSend3(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
2815  _sheetContext["contextInfo"]);
2816  else
2817  _sheetContext["deferDidEndSelector"] = YES;
2818  }
2819 }
2820 
2821 /*
2822  Called to animate the sheet out. If called while animating in, schedules an animate
2823  out at completion
2824 */
2825 - (void)_detachSheetWindow
2826 {
2827  if (_sheetContext["isClosing"])
2828  return;
2829 
2830  _sheetContext["isAttached"] = NO;
2831  _sheetContext["isClosing"] = YES;
2832  _sheetContext["opened"] = NO;
2833 
2834  // A timer seems to be necessary for the animation to work correctly.
2835  // It would be ideal to block here and spin the event loop, until attach is complete.
2837  target:self
2838  selector:@selector(_sheetShouldAnimateOut:)
2839  userInfo:nil
2840  repeats:NO];
2841 }
2842 
2843 /*
2844  Called to cleanup sheet, when we are definitely done with it
2845 */
2846 - (void)_cleanupSheetWindow
2847 {
2848  var sheet = _sheetContext["sheet"],
2849  deferDidEnd = _sheetContext["deferDidEndSelector"];
2850 
2851  // If the parent window is modal, the sheet started its own modal session
2852  if (sheet._isModal)
2853  [CPApp stopModal];
2854 
2855  [self _removeClipForSheet:sheet];
2856 
2857  // Restore the state of window before it was sheetified
2858  sheet._isSheet = NO;
2859  [sheet._windowView _enableSheet:NO inWindow:self];
2860  sheet._constrainsToUsableScreen = _sheetContext["savedConstrains"];
2861 
2862  // Close it
2863  [sheet orderOut:self];
2864 
2865  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidEndSheetNotification object:self];
2866 
2867  if (deferDidEnd)
2868  {
2869  var delegate = _sheetContext["modalDelegate"],
2870  selector = _sheetContext["endSelector"],
2871  returnCode = _sheetContext["returnCode"],
2872  contextInfo = _sheetContext["contextInfo"];
2873 
2874  // Context must be destroyed, since didEnd might want to attach another sheet
2875  _sheetContext = nil;
2876  sheet._parentView = nil;
2877 
2878  if (delegate != null)
2879  delegate.isa.objj_msgSend3(delegate, selector, sheet, returnCode, contextInfo);
2880  }
2881  else
2882  {
2883  _sheetContext = nil;
2884  sheet._parentView = nil;
2885  }
2886 }
2887 
2888 /* @ignore */
2889 - (void)animationDidEnd:(id)anim
2890 {
2891  var sheet = _sheetContext["sheet"];
2892 
2893  if (anim._window != sheet)
2894  return;
2895 
2897  target:self
2898  selector:@selector(_sheetAnimationDidEnd:)
2899  userInfo:nil
2900  repeats:NO];
2901 }
2902 
2903 - (void)_sheetShouldAnimateIn:(CPTimer)timer
2904 {
2905  // Can't open sheet while opening or closing animation is going on
2906  if (_sheetContext["isOpening"] || _sheetContext["isClosing"])
2907  return;
2908 
2909  var sheet = _sheetContext["sheet"];
2910  sheet._isSheet = YES;
2911  sheet._parentView = self;
2912 
2913  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillBeginSheetNotification object:self];
2914 
2915  // If sheet is attached to a modal window, the sheet runs
2916  // as if itself and the parent window are modal
2917  sheet._isModal = NO;
2918 
2919  if ([CPApp modalWindow] === self)
2920  {
2921  [CPApp runModalForWindow:sheet];
2922  sheet._isModal = YES;
2923  }
2924 
2925  // The sheet starts hidden just above the top of a clip rect
2926  // TODO : Make properly for the -1 in endY
2927  var sheetFrame = [sheet frame],
2928  sheetShadowFrame = sheet._hasShadow ? [sheet._shadowView frame] : sheetFrame,
2929  frame = [self frame],
2930  originX = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
2931  startFrame = CGRectMake(originX, -sheetShadowFrame.size.height, sheetFrame.size.width, sheetFrame.size.height),
2932  endY = -1 + [_windowView bodyOffset] - [[self contentView] frame].origin.y,
2933  endFrame = CGRectMake(originX, endY, sheetFrame.size.width, sheetFrame.size.height);
2934 
2935  if (_toolbar && [_windowView showsToolbar] && [self isFullPlatformWindow])
2936  {
2937  endY += [[_toolbar _toolbarView] frameSize].height;
2938  endFrame = CGRectMake(originX, endY, sheetFrame.size.width, sheetFrame.size.height);
2939  }
2940 
2941  // Move the sheet offscreen before ordering front so it doesn't appear briefly
2942  [sheet setFrameOrigin:CGPointMake(0, -13000)];
2943 
2944  // Because clipping does funny thing with the DOM, we have to orderFront before clipping
2945  [sheet orderFront:self];
2946  [self _clipSheet:sheet];
2947 
2948  [sheet setFrame:startFrame display:YES animate:NO];
2949 
2950  _sheetContext["opened"] = YES;
2951  _sheetContext["shouldClose"] = NO;
2952  _sheetContext["isOpening"] = YES;
2953 
2954  [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseOut];
2955 }
2956 
2957 - (void)_sheetShouldAnimateOut:(CPTimer)timer
2958 {
2959  if (_sheetContext["isOpening"])
2960  {
2961  // Allow sheet to be closed while opening, it will close when animate in completes
2962  _sheetContext["shouldClose"] = YES;
2963  return;
2964  }
2965 
2966  // The parent window can be orderedOut to disable the sheet animate out, as in Cocoa
2967  if ([self isVisible])
2968  {
2969  var sheet = _sheetContext["sheet"],
2970  sheetFrame = [sheet frame],
2971  fullHeight = sheet._hasShadow ? [sheet._shadowView frame].size.height : sheetFrame.size.height,
2972  endFrame = CGRectMakeCopy(sheetFrame),
2973  contentOrigin = [self convertBaseToGlobal:[[self contentView] frame].origin];
2974 
2975  // Don't constrain sheets, they are controlled by the parent
2976  sheet._constrainsToUsableScreen = NO;
2977 
2978  [sheet setFrameOrigin:CGPointMake(sheetFrame.origin.x, sheetFrame.origin.y - contentOrigin.y)];
2979  [self _clipSheet:sheet];
2980 
2981  endFrame.origin.y = -fullHeight;
2982  [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseIn];
2983  }
2984  else
2985  {
2986  [self _sheetAnimationDidEnd:nil];
2987  }
2988 }
2989 
2990 - (void)_sheetAnimationDidEnd:(CPTimer)timer
2991 {
2992  var sheet = _sheetContext["sheet"];
2993 
2994  _sheetContext["isOpening"] = NO;
2995  _sheetContext["isClosing"] = NO;
2996 
2997  if (_sheetContext["opened"] === YES)
2998  {
2999  var sheetFrame = [sheet frame],
3000  sheetOrigin = CGPointMakeCopy(sheetFrame.origin);
3001 
3002  [self _removeClipForSheet:sheet];
3003  [sheet setFrameOrigin:CGPointMake(sheetOrigin.x, [sheet frame].origin.y + sheetOrigin.y)];
3004 
3005  // we wanted to close the sheet while it animated in, do that now
3006  if (_sheetContext["shouldClose"] === YES)
3007  [self _detachSheetWindow];
3008  else
3009  [sheet makeKeyWindow];
3010  }
3011  else
3012  {
3013  // sheet is closed and not visible
3014  [self _cleanupSheetWindow];
3015  }
3016 }
3017 
3018 - (void)_clipSheet:(CPWindow)aSheet
3019 {
3020  var clipRect = [_platformWindow contentBounds];
3021  clipRect.origin.y = [self frame].origin.y + [[self contentView] frame].origin.y;
3022 
3023  [[_platformWindow layerAtLevel:_level create:NO] clipWindow:aSheet toRect:clipRect];
3024 }
3025 
3026 - (void)_removeClipForSheet:(CPWindow)aSheet
3027 {
3028  [[_platformWindow layerAtLevel:_level create:NO] removeClipForWindow:aSheet];
3029 }
3030 
3035 {
3036  if (_sheetContext === nil)
3037  return nil;
3038 
3039  return _sheetContext["sheet"];
3040 }
3041 
3045 - (BOOL)isSheet
3046 {
3047  return _isSheet;
3048 }
3049 
3050 //
3051 /*
3052  Used privately.
3053  @ignore
3054 */
3056 {
3057  return NO;
3058 }
3059 
3065 {
3066  return NO;
3067 }
3068 
3069 - (BOOL)performKeyEquivalent:(CPEvent)anEvent
3070 {
3071  // FIXME: should we be starting at the root, in other words _windowView?
3072  // The evidence seems to point to no...
3073  return [_contentView performKeyEquivalent:anEvent];
3074 }
3075 
3076 - (void)keyDown:(CPEvent)anEvent
3077 {
3078  // It's not clear why we do performKeyEquivalent again here...
3079  // Perhaps to allow something to happen between sendEvent: and keyDown:?
3080  if ([anEvent _couldBeKeyEquivalent] && [self performKeyEquivalent:anEvent])
3081  return;
3082 
3083  // Apple's documentation is inconsistent with their behavior here. According to the docs
3084  // an event going of the responder chain is passed to the input system as a last resort.
3085  // However, the only methods I could get Cocoa to call automatically are
3086  // moveUp: moveDown: moveLeft: moveRight: pageUp: pageDown: and complete:
3087  // Unhandled events just travel further up the responder chain _past_ the window.
3088  if (![self _processKeyboardUIKey:anEvent])
3089  [super keyDown:anEvent];
3090 }
3091 
3092 /*
3093  @ignore
3094  Interprets the key event for action messages and sends the action message down the responder chain
3095  Cocoa only sends moveDown:, moveUp:, moveLeft:, moveRight:, pageUp:, pageDown: and complete: messages.
3096  We deviate from this by sending (the default) scrollPageUp:, scrollPageDown:, scrollToBeginningOfDocument: and scrollToEndOfDocument: for pageUp, pageDown, home and end keys.
3097  @param anEvent the event to handle.
3098  @return YES if the key event was handled, NO if no responder handled the key event
3099 */
3100 - (BOOL)_processKeyboardUIKey:(CPEvent)anEvent
3101 {
3102  var character = [anEvent charactersIgnoringModifiers];
3103 
3104  if (![CPWindowActionMessageKeys containsObject:character])
3105  return NO;
3106 
3107  var selectors = [CPKeyBinding selectorsForKey:character modifierFlags:0];
3108 
3109  if ([selectors count] <= 0)
3110  return NO;
3111 
3112  var selector = [selectors objectAtIndex:0];
3113 
3114  return [[self firstResponder] tryToPerform:selector with:self];
3115 }
3116 
3117 - (void)_dirtyKeyViewLoop
3118 {
3119  if (_autorecalculatesKeyViewLoop)
3120  _keyViewLoopIsDirty = YES;
3121 }
3122 
3123 /*
3124  Recursively traverse an array of views (depth last) until we find one that has a next or previous key view set. Return nil if none can be found.
3125 
3126  We don't use _viewsSortedByPosition here because it is wasteful to enumerate the entire view hierarchy when we will probably find a key view at the top level.
3127 */
3128 - (BOOL)_hasKeyViewLoop:(CPArray)theViews
3129 {
3130  var i,
3131  count = [theViews count];
3132 
3133  for (i = 0; i < count; ++i)
3134  {
3135  var view = theViews[i];
3136 
3137  if ([view nextKeyView] || [view previousKeyView])
3138  return YES;
3139  }
3140 
3141  for (i = 0; i < count; ++i)
3142  {
3143  var subviews = [theViews[i] subviews];
3144 
3145  if ([subviews count] && [self _hasKeyViewLoop:subviews])
3146  return YES;
3147  }
3148 
3149  return NO;
3150 }
3151 
3160 {
3161  [self _doRecalculateKeyViewLoop];
3162 }
3163 
3164 - (CPArray)_viewsSortedByPosition
3165 {
3166  var views = [CPArray arrayWithObject:_contentView];
3167 
3168  views = views.concat([self _subviewsSortedByPosition:[_contentView subviews]]);
3169 
3170  return views;
3171 }
3172 
3173 - (CPArray)_subviewsSortedByPosition:(CPArray)theSubviews
3174 {
3175  /*
3176  We first sort the subviews according to geometric order.
3177  Then we go through each subview, and if it has subviews,
3178  they are sorted and inserted after the superview. This
3179  is done recursively.
3180  */
3181  theSubviews = [theSubviews copy];
3182  [theSubviews sortUsingFunction:keyViewComparator context:nil];
3183 
3184  var sortedViews = [];
3185 
3186  for (var i = 0, count = [theSubviews count]; i < count; ++i)
3187  {
3188  var view = theSubviews[i],
3189  subviews = [view subviews];
3190 
3191  sortedViews.push(view);
3192 
3193  if ([subviews count])
3194  sortedViews = sortedViews.concat([self _subviewsSortedByPosition:subviews]);
3195  }
3196 
3197  return sortedViews;
3198 }
3199 
3200 - (void)_doRecalculateKeyViewLoop
3201 {
3202  var views = [self _viewsSortedByPosition];
3203 
3204  for (var index = 0, count = [views count]; index < count; ++index)
3205  [views[index] setNextKeyView:views[(index + 1) % count]];
3206 
3207  _keyViewLoopIsDirty = NO;
3208 }
3209 
3210 - (void)setAutorecalculatesKeyViewLoop:(BOOL)shouldRecalculate
3211 {
3212  if (_autorecalculatesKeyViewLoop === shouldRecalculate)
3213  return;
3214 
3215  _autorecalculatesKeyViewLoop = shouldRecalculate;
3216 }
3217 
3219 {
3220  return _autorecalculatesKeyViewLoop;
3221 }
3222 
3223 - (void)selectNextKeyView:(id)sender
3224 {
3225  if (_keyViewLoopIsDirty)
3226  [self _doRecalculateKeyViewLoop];
3227 
3228  var nextValidKeyView = nil;
3229 
3230  if ([_firstResponder isKindOfClass:[CPView class]])
3231  nextValidKeyView = [_firstResponder nextValidKeyView];
3232 
3233  if (!nextValidKeyView)
3234  {
3235  if ([_initialFirstResponder acceptsFirstResponder])
3236  nextValidKeyView = _initialFirstResponder;
3237  else
3238  nextValidKeyView = [_initialFirstResponder nextValidKeyView];
3239  }
3240 
3241  if (nextValidKeyView)
3242  [self makeFirstResponder:nextValidKeyView];
3243 }
3244 
3245 - (void)selectPreviousKeyView:(id)sender
3246 {
3247  if (_keyViewLoopIsDirty)
3248  [self _doRecalculateKeyViewLoop];
3249 
3250  var previousValidKeyView = nil;
3251 
3252  if ([_firstResponder isKindOfClass:[CPView class]])
3253  previousValidKeyView = [_firstResponder previousValidKeyView];
3254 
3255  if (!previousValidKeyView)
3256  {
3257  if ([_initialFirstResponder acceptsFirstResponder])
3258  previousValidKeyView = _initialFirstResponder;
3259  else
3260  previousValidKeyView = [_initialFirstResponder previousValidKeyView];
3261  }
3262 
3263  if (previousValidKeyView)
3264  [self makeFirstResponder:previousValidKeyView];
3265 }
3266 
3267 - (void)selectKeyViewFollowingView:(CPView)aView
3268 {
3269  if (_keyViewLoopIsDirty)
3270  [self _doRecalculateKeyViewLoop];
3271 
3272  var nextValidKeyView = [aView nextValidKeyView];
3273 
3274  if ([nextValidKeyView isKindOfClass:[CPView class]])
3275  [self makeFirstResponder:nextValidKeyView];
3276 }
3277 
3278 - (void)selectKeyViewPrecedingView:(CPView)aView
3279 {
3280  if (_keyViewLoopIsDirty)
3281  [self _doRecalculateKeyViewLoop];
3282 
3283  var previousValidKeyView = [aView previousValidKeyView];
3284 
3285  if ([previousValidKeyView isKindOfClass:[CPView class]])
3286  [self makeFirstResponder:previousValidKeyView];
3287 }
3288 
3294 - (void)setDefaultButtonCell:(CPButton)aButton
3295 {
3296  [self setDefaultButton:aButton];
3297 }
3298 
3304 {
3305  return [self defaultButton];
3306 }
3307 
3314 - (void)setDefaultButton:(CPButton)aButton
3315 {
3316  if (_defaultButton === aButton)
3317  return;
3318 
3319  if ([_defaultButton keyEquivalent] === CPCarriageReturnCharacter)
3320  [_defaultButton setKeyEquivalent:nil];
3321 
3322  _defaultButton = aButton;
3323 
3324  if ([_defaultButton keyEquivalent] !== CPCarriageReturnCharacter)
3325  [_defaultButton setKeyEquivalent:CPCarriageReturnCharacter];
3326 }
3327 
3332 {
3333  return _defaultButton;
3334 }
3335 
3340 {
3341  _defaultButtonEnabled = YES;
3342 }
3343 
3349 {
3351 }
3352 
3357 {
3358  _defaultButtonEnabled = NO;
3359 }
3360 
3366 {
3368 }
3369 
3370 - (void)setValue:(id)aValue forKey:(CPString)aKey
3371 {
3372  if (aKey === CPDisplayPatternTitleBinding)
3373  [self setTitle:aValue || @""];
3374  else
3375  [super setValue:aValue forKey:aKey];
3376 }
3377 
3378 - (void)_didReceivePlatformWindowWillCloseNotification:(CPNotification)aNotification
3379 {
3380  if ([aNotification object] != _platformWindow)
3381  return;
3382 
3383  [self close];
3384 }
3385 
3386 @end
3387 
3388 var keyViewComparator = function(lhs, rhs, context)
3389 {
3390  var lhsBounds = [lhs convertRect:[lhs bounds] toView:nil],
3391  rhsBounds = [rhs convertRect:[rhs bounds] toView:nil],
3392  lhsY = CGRectGetMinY(lhsBounds),
3393  rhsY = CGRectGetMinY(rhsBounds),
3394  lhsX = CGRectGetMinX(lhsBounds),
3395  rhsX = CGRectGetMinX(rhsBounds),
3396  intersectsVertically = MIN(CGRectGetMaxY(lhsBounds), CGRectGetMaxY(rhsBounds)) - MAX(lhsY, rhsY);
3397 
3398  // If two views are "on the same line" (intersect vertically), then rely on the x comparison.
3399  if (intersectsVertically > 0)
3400  {
3401  if (lhsX < rhsX)
3402  return CPOrderedAscending;
3403 
3404  if (lhsX === rhsX)
3405  return CPOrderedSame;
3406 
3407  return CPOrderedDescending;
3408  }
3409 
3410  if (lhsY < rhsY)
3411  return CPOrderedAscending;
3412 
3413  if (lhsY === rhsY)
3414  return CPOrderedSame;
3415 
3416  return CPOrderedDescending;
3417 };
3418 
3419 
3421 
3426 - (BOOL)_delegateRespondsToWindowWillUndoManager
3427 {
3428  return _implementedDelegateMethods & CPWindowDelegate_windowWillReturnUndoManager_;
3429 }
3430 
3435 - (BOOL)_delegateRespondsToWindowShouldClose
3436 {
3437  return _implementedDelegateMethods & CPWindowDelegate_windowShouldClose_
3438 }
3439 
3444 - (BOOL)_sendDelegateWindowShouldClose
3445 {
3446  if (!(_implementedDelegateMethods & CPWindowDelegate_windowShouldClose_))
3447  return YES;
3448 
3449  return [_delegate windowShouldClose:self];
3450 }
3451 
3456 - (BOOL)_sendDelegateWindowWillReturnUndoManager
3457 {
3458  if (!(_implementedDelegateMethods & CPWindowDelegate_windowWillReturnUndoManager_))
3459  return nil;
3460 
3461  return [_delegate windowWillReturnUndoManager:self];
3462 }
3463 
3468 - (void)_sendDelegateWindowWillClose
3469 {
3470  if (!(_implementedDelegateMethods & CPWindowDelegate_windowWillClose_))
3471  return;
3472 
3473  [_delegate windowWillClose:self];
3474 }
3475 
3480 - (CGSize)_sendDelegateWindowWillResizeToSize:(CGSize)aSize
3481 {
3482  if (!(_implementedDelegateMethods & CPWindowDelegate_windowWillResize_toSize_))
3483  return aSize;
3484 
3485  return [_delegate windowWillResize:self toSize:aSize];
3486 }
3487 
3488 @end
3489 
3490 
3492 
3493 /*
3494  @ignore
3495 */
3496 - (void)resizeWithOldPlatformWindowSize:(CGSize)aSize
3497 {
3498  if ([self isFullPlatformWindow])
3499  return [self setFrame:[_platformWindow visibleFrame]];
3500 
3501  // If this window is constrainable and we are globally ignoring constraining, ignore the platform resize
3502  if ((_constrainsToUsableScreen && !CPWindowConstrainToScreen) || _autoresizingMask === CPWindowNotSizable)
3503  return;
3504 
3505  var frame = [_platformWindow contentBounds],
3506  newFrame = CGRectMakeCopy(_frame),
3507  dX = (CGRectGetWidth(frame) - aSize.width) /
3508  (((_autoresizingMask & CPWindowMinXMargin) ? 1 : 0) + (_autoresizingMask & CPWindowWidthSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxXMargin ? 1 : 0)),
3509  dY = (CGRectGetHeight(frame) - aSize.height) /
3510  ((_autoresizingMask & CPWindowMinYMargin ? 1 : 0) + (_autoresizingMask & CPWindowHeightSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxYMargin ? 1 : 0));
3511 
3512  if (_autoresizingMask & CPWindowMinXMargin)
3513  newFrame.origin.x += dX;
3514 
3515  if (_autoresizingMask & CPWindowWidthSizable)
3516  newFrame.size.width += dX;
3517 
3518  if (_autoresizingMask & CPWindowMinYMargin)
3519  newFrame.origin.y += dY;
3520 
3521  if (_autoresizingMask & CPWindowHeightSizable)
3522  newFrame.size.height += dY;
3523 
3524  [self _setFrame:newFrame display:YES animate:NO constrainWidth:YES constrainHeight:YES];
3525 }
3526 
3527 /*
3528  @ignore
3529 */
3530 - (void)setAutoresizingMask:(unsigned)anAutoresizingMask
3531 {
3532  _autoresizingMask = anAutoresizingMask;
3533 }
3534 
3535 /*
3536  @ignore
3537 */
3538 - (unsigned)autoresizingMask
3539 {
3540  return _autoresizingMask;
3541 }
3542 
3546 - (CGPoint)convertBaseToGlobal:(CGPoint)aPoint
3547 {
3548  return [CPPlatform isBrowser] ? [self convertBaseToPlatformWindow:aPoint] : [self convertBaseToScreen:aPoint];
3549 }
3550 
3554 - (CGPoint)convertGlobalToBase:(CGPoint)aPoint
3555 {
3556  return [CPPlatform isBrowser] ? [self convertPlatformWindowToBase:aPoint] : [self convertScreenToBase:aPoint];
3557 }
3558 
3562 - (CGPoint)convertBaseToPlatformWindow:(CGPoint)aPoint
3563 {
3564  if ([self _sharesChromeWithPlatformWindow])
3565  return CGPointMakeCopy(aPoint);
3566 
3567  var origin = [self frame].origin;
3568 
3569  return CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y);
3570 }
3571 
3575 - (CGPoint)convertPlatformWindowToBase:(CGPoint)aPoint
3576 {
3577  if ([self _sharesChromeWithPlatformWindow])
3578  return CGPointMakeCopy(aPoint);
3579 
3580  var origin = [self frame].origin;
3581 
3582  return CGPointMake(aPoint.x - origin.x, aPoint.y - origin.y);
3583 }
3584 
3585 - (CGPoint)convertScreenToBase:(CGPoint)aPoint
3586 {
3587  return [self convertPlatformWindowToBase:[_platformWindow convertScreenToBase:aPoint]];
3588 }
3589 
3590 - (CGPoint)convertBaseToScreen:(CGPoint)aPoint
3591 {
3592  return [_platformWindow convertBaseToScreen:[self convertBaseToPlatformWindow:aPoint]];
3593 }
3594 
3595 - (void)_setSharesChromeWithPlatformWindow:(BOOL)shouldShareFrameWithPlatformWindow
3596 {
3597  // We canna' do it captain! We just don't have the power!
3598  if (shouldShareFrameWithPlatformWindow && [CPPlatform isBrowser])
3599  return;
3600 
3601  _sharesChromeWithPlatformWindow = shouldShareFrameWithPlatformWindow;
3602 
3603  [self _updateShadow];
3604 }
3605 
3606 - (BOOL)_sharesChromeWithPlatformWindow
3607 {
3608  return _sharesChromeWithPlatformWindow;
3609 }
3610 
3611 // Undo and Redo Support
3616 {
3617  // If we've ever created an undo manager, return it.
3618  if (_undoManager)
3619  return _undoManager;
3620 
3621  // If not, check to see if the document has one.
3622  var documentUndoManager = [[_windowController document] undoManager];
3623 
3624  if (documentUndoManager)
3625  return documentUndoManager;
3626 
3627  // If not, check to see if the delegate has one.
3628  if ([self _delegateRespondsToWindowWillUndoManager])
3629  return [self _sendDelegateWindowWillReturnUndoManager];
3630 
3631  // If not, create one.
3632  if (!_undoManager)
3633  _undoManager = [[CPUndoManager alloc] init];
3634 
3635  return _undoManager;
3636 }
3637 
3642 - (void)undo:(id)aSender
3643 {
3644  [[self undoManager] undo];
3645 }
3646 
3651 - (void)redo:(id)aSender
3652 {
3653  [[self undoManager] redo];
3654 }
3655 
3656 - (BOOL)containsPoint:(CGPoint)aPoint
3657 {
3658  return CGRectContainsPoint(_frame, aPoint);
3659 }
3660 
3661 /* aPoint should be global */
3662 - (BOOL)_isValidMousePoint:(CGPoint)aPoint
3663 {
3664  // If we are using the new resizing mode, mouse events are valid
3665  // outside the window's frame for non-full platform windows.
3666  var mouseFrame = (!_isFullPlatformWindow && (_styleMask & CPResizableWindowMask) && (CPWindowResizeStyle === CPWindowResizeStyleModern)) ? CGRectInset(_frame, -_CPWindowViewResizeSlop, -_CPWindowViewResizeSlop) : _frame;
3667 
3668  return CGRectContainsPoint(mouseFrame, aPoint);
3669 }
3670 
3671 @end
3672 
3673 @implementation CPWindow (Deprecated)
3678 - (void)setFullBridge:(BOOL)shouldBeFullBridge
3679 {
3680  _CPReportLenientDeprecation([self class], _cmd, @selector(setFullPlatformWindow:));
3681 
3682  [self setFullPlatformWindow:shouldBeFullBridge];
3683 }
3684 
3690 {
3691  return [self isFullPlatformWindow];
3692 }
3693 
3694 /*
3695  @ignore
3696 */
3697 - (CGPoint)convertBaseToBridge:(CGPoint)aPoint
3698 {
3699  return [self convertBaseToPlatformWindow:aPoint];
3700 }
3701 
3702 /*
3703  @ignore
3704 */
3705 - (CGPoint)convertBridgeToBase:(CGPoint)aPoint
3706 {
3707  return [self convertPlatformWindowToBase:aPoint];
3708 }
3709 
3710 @end
3711 
3712 var interpolate = function(fromValue, toValue, progress)
3713 {
3714  return fromValue + (toValue - fromValue) * progress;
3715 };
3716 
3717 /* @ignore */
3718 @implementation _CPWindowFrameAnimation : CPAnimation
3719 {
3720  CPWindow _window;
3721 
3722  CGRect _startFrame;
3723  CGRect _targetFrame;
3724 }
3725 
3726 - (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame
3727 {
3728  self = [super initWithDuration:[aWindow animationResizeTime:aTargetFrame] animationCurve:CPAnimationLinear];
3729 
3730  if (self)
3731  {
3732  _window = aWindow;
3733 
3734  _targetFrame = CGRectMakeCopy(aTargetFrame);
3735  _startFrame = CGRectMakeCopy([_window frame]);
3736  }
3737 
3738  return self;
3739 }
3740 
3741 - (void)startAnimation
3742 {
3743  [super startAnimation];
3744 
3745  _window._isAnimating = YES;
3746 }
3747 
3748 - (void)setCurrentProgress:(float)aProgress
3749 {
3750  [super setCurrentProgress:aProgress];
3751 
3752  var value = [self currentValue];
3753 
3754  if (value == 1.0)
3755  _window._isAnimating = NO;
3756 
3757  var newFrame = CGRectMake(
3758  interpolate(CGRectGetMinX(_startFrame), CGRectGetMinX(_targetFrame), value),
3759  interpolate(CGRectGetMinY(_startFrame), CGRectGetMinY(_targetFrame), value),
3760  interpolate(CGRectGetWidth(_startFrame), CGRectGetWidth(_targetFrame), value),
3761  interpolate(CGRectGetHeight(_startFrame), CGRectGetHeight(_targetFrame), value));
3762 
3763  [_window setFrame:newFrame display:YES animate:NO];
3764 }
3765 
3766 @end
3767 
3768 
3770 
3771 /* @ignore */
3772 - (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
3773 {
3774  // If none of our views or ourselves has registered for drag events...
3775  if (!_inclusiveRegisteredDraggedTypes)
3776  return nil;
3777 
3778 // We don't need to do this because the only place this gets called
3779 // -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
3780 // be safe?
3781 // if (![self containsPoint:aPoint])
3782 // return nil;
3783 
3784  var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
3785  hitView = [_windowView hitTest:adjustedPoint];
3786 
3787  while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
3788  hitView = [hitView superview];
3789 
3790  if (hitView)
3791  return hitView;
3792 
3793  if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
3794  return self;
3795 
3796  return nil;
3797 }
3798 
3799 @end
3800 
3802 
3803 - (void)_addTrackingAreaView:(CPView)aView
3804 {
3805  var trackingAreas = [aView trackingAreas];
3806 
3807  for (var i = 0; i < trackingAreas.length; i++)
3808  [self _addTrackingArea:trackingAreas[i]];
3809 }
3810 
3811 - (void)_removeTrackingAreaView:(CPView)aView
3812 {
3813  var trackingAreas = [aView trackingAreas];
3814 
3815  for (var i = 0; i < trackingAreas.length; i++)
3816  [self _removeTrackingArea:trackingAreas[i]];
3817 }
3818 
3819 - (void)_addTrackingArea:(CPTrackingArea)trackingArea
3820 {
3821  var trackingAreaView = [trackingArea view];
3822 
3823  if (![_trackingAreaViews containsObjectIdenticalTo:trackingAreaView])
3824  [_trackingAreaViews addObject:trackingAreaView];
3825 
3826  // If CPTrackingAssumeInside option is set, insert the tracking area in the events management system
3827  // in order to have the first event sent only when mouse leaves the tracking area
3828 
3829  [self _insertTrackingArea:trackingArea assumeInside:([trackingArea options] & CPTrackingAssumeInside)];
3830 }
3831 
3832 - (void)_removeTrackingArea:(CPTrackingArea)trackingArea
3833 {
3834  // If mouse is in the tracking area, we remove it from the stack to avoid to fire a future mouseExited event
3835 
3836  [self _purgeTrackingArea:trackingArea];
3837 
3838  var trackingAreaView = [trackingArea view];
3839 
3840  [_trackingAreaViews removeObjectIdenticalTo:trackingAreaView];
3841 }
3842 
3843 - (void)_insertTrackingArea:(CPTrackingArea)trackingArea assumeInside:(BOOL)assumeInside
3844 {
3845  if (_handlingTrackingAreaEvent)
3846  _restartHandlingTrackingAreaEvent = YES;
3847 
3848  if (assumeInside)
3849  {
3850  if (_handlingTrackingAreaEvent)
3851  [_mouseEnteredStack addObject:trackingArea];
3852  else
3853  [_previousMouseEnteredStack addObject:trackingArea];
3854  }
3855 }
3856 
3857 - (void)_purgeTrackingArea:(CPTrackingArea)trackingArea
3858 {
3859  if (_handlingTrackingAreaEvent)
3860  {
3861  [_mouseEnteredStack removeObjectIdenticalTo:trackingArea];
3862 
3863  var i = _queuedEvents.length;
3864 
3865  while (i--)
3866  if ([_queuedEvents[i] trackingArea] === trackingArea)
3867  [_queuedEvents removeObjectAtIndex:i];
3868 
3869  _cursorUpdateStack = [];
3870  _activeCursorTrackingArea = nil;
3871  }
3872  else
3873  {
3874  [_previousMouseEnteredStack removeObjectIdenticalTo:trackingArea];
3875  [_previousCursorUpdateStack removeObjectIdenticalTo:trackingArea];
3876  }
3877 }
3878 
3879 - (void)_handleTrackingAreaEvent:(CPEvent)anEvent
3880 {
3881  _handlingTrackingAreaEvent = YES;
3882 
3883  var point = [anEvent locationInWindow],
3884  dragging = ([anEvent type] !== CPMouseMoved);
3885 
3886  do
3887  {
3888  // Initialize this run
3889  _restartHandlingTrackingAreaEvent = NO;
3890 
3891  _mouseEnteredStack = [];
3892  _cursorUpdateStack = [];
3893 
3894  // Important remark: we must queue events to avoid running conditions when a view uses a mouse event to modify view hierarchy
3895 
3896  _queuedEvents = [];
3897 
3898  // Handle mouse entering tracking areas (and calc _mouseEnteredStack and _cursorUpdateStack)
3899 
3900  [self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging];
3901 
3902  // Handle mouse exiting tracking areas
3903 
3904  [self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging];
3905 
3906  // Cursor update
3907 
3908  if (_cursorUpdateStack.length > 0)
3909  {
3910  [self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging];
3911  }
3912  else if (!dragging)
3913  {
3914  // Here, we are outside the window content view tracking area, so let _windowView set the cursor (resize cursor, ...)
3915 
3916  [_windowView setCursorForLocation:point resizing:NO];
3917  _activeCursorTrackingArea = nil;
3918  }
3919 
3920  // Send all queued events
3921  // Important remark : as an event can modify the view hierarchy, the events queue could be modified while processing it
3922 
3923  while (_queuedEvents.length > 0)
3924  {
3925  var queuedEvent = _queuedEvents[0],
3926  trackingArea = [queuedEvent trackingArea],
3927  trackingOwner = [trackingArea owner];
3928 
3929  switch ([queuedEvent type])
3930  {
3931  case CPMouseEntered:
3932  [trackingOwner mouseEntered:queuedEvent];
3933  break;
3934 
3935  case CPMouseExited:
3936  [trackingOwner mouseExited:queuedEvent];
3937  break;
3938 
3939  case CPCursorUpdate:
3940  [trackingOwner cursorUpdate:queuedEvent];
3941  break;
3942  }
3943 
3944  if (queuedEvent === _queuedEvents[0])
3945  [_queuedEvents removeObjectAtIndex:0];
3946  }
3947 
3948  // Prepare for next call
3949 
3950  _previousMouseEnteredStack = _mouseEnteredStack;
3951  _previousCursorUpdateStack = _cursorUpdateStack;
3952  }
3953  while (_restartHandlingTrackingAreaEvent)
3954 
3955  _handlingTrackingAreaEvent = NO;
3956 }
3957 
3958 - (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
3959 {
3960  var isKeyWindow = [self isKeyWindow];
3961 
3962  for (var i = 0; i < _trackingAreaViews.length; i++)
3963  {
3964  var aView = _trackingAreaViews[i],
3965  trackingAreas = [aView trackingAreas];
3966 
3967  if ([aView isHidden])
3968  continue;
3969 
3970  for (var j = 0; j < trackingAreas.length; j++)
3971  {
3972  var aTrackingArea = trackingAreas[j],
3973  trackingOptions = [aTrackingArea options],
3974  trackingImplementedMethods = [aTrackingArea implementedOwnerMethods];
3975 
3976  if (!(((trackingOptions & CPTrackingActiveAlways) ||
3977  (trackingOptions & CPTrackingActiveInActiveApp) ||
3978  ((trackingOptions & CPTrackingActiveInKeyWindow) && isKeyWindow) ||
3979  ((trackingOptions & CPTrackingActiveWhenFirstResponder) && isKeyWindow && (_firstResponder === aView))) &&
3980  (CGRectContainsPoint([aTrackingArea windowRect], point))))
3981  {
3982  continue;
3983  }
3984 
3985  [_mouseEnteredStack addObject:aTrackingArea];
3986 
3987  if ([_previousMouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
3988  {
3989  // Mouse was already in this rect so it's a mouseMoved
3990 
3991  if (!dragging && (trackingOptions & CPTrackingMouseMoved) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseMoved))
3992  [[aTrackingArea owner] mouseMoved:anEvent];
3993  }
3994  else if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered))
3995  {
3996  var mouseEnteredEvent = [CPEvent enterExitEventWithType:CPMouseEntered
3997  location:point
3998  modifierFlags:[anEvent modifierFlags]
3999  timestamp:[anEvent timestamp]
4000  windowNumber:_windowNumber
4001  context:nil
4002  eventNumber:-1
4003  trackingArea:aTrackingArea];
4004 
4005  if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
4006  [self _queueTrackingEvent:mouseEnteredEvent];
4007  else
4008  [_queuedEvents addObject:mouseEnteredEvent];
4009  }
4010 
4011  if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate))
4012  [_cursorUpdateStack addObject:aTrackingArea];
4013  }
4014  }
4015 }
4016 
4017 - (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
4018 {
4019  // Search for exited views (were in _previousMouseEnteredStack but no more in _mouseEnteredStack)
4020 
4021  for (var i = 0; i < _previousMouseEnteredStack.length; i++)
4022  {
4023  var aTrackingArea = _previousMouseEnteredStack[i],
4024  trackingOptions = [aTrackingArea options];
4025 
4026  if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
4027  continue;
4028 
4029  // Mouse is no more in this area so it's a mouseExited
4030 
4031  if ((trackingOptions & CPTrackingMouseEnteredAndExited) && ([aTrackingArea implementedOwnerMethods] & CPTrackingOwnerImplementsMouseExited))
4032  {
4033  var theView = [aTrackingArea owner],
4034  mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited
4035  location:point
4036  modifierFlags:[anEvent modifierFlags]
4037  timestamp:[anEvent timestamp]
4038  windowNumber:_windowNumber
4039  context:nil
4040  eventNumber:-1
4041  trackingArea:aTrackingArea];
4042 
4043  if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
4044  [self _queueTrackingEvent:mouseExitedEvent];
4045  else
4046  [_queuedEvents addObject:mouseExitedEvent];
4047  }
4048 
4049  // If this is the active cursor area, we reset _previousCursorUpdateStack so a new active area will be computed
4050 
4051  if (aTrackingArea === _activeCursorTrackingArea)
4052  {
4053  _previousCursorUpdateStack = [];
4054  _activeCursorTrackingArea = nil;
4055  }
4056  }
4057 }
4058 
4059 - (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging
4060 {
4061  var overlappingTrackingAreas = [];
4062 
4063  for (var i = 0; i < _cursorUpdateStack.length; i++)
4064  {
4065  var aTrackingArea = _cursorUpdateStack[i];
4066 
4067  if ((![_previousCursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea))
4068  [overlappingTrackingAreas addObject:aTrackingArea];
4069  }
4070 
4071  var frontmostTrackingArea = overlappingTrackingAreas[0],
4072  frontmostView = [frontmostTrackingArea view];
4073 
4074  for (var i = 1; i < overlappingTrackingAreas.length; i++)
4075  {
4076  var aTrackingArea = overlappingTrackingAreas[i],
4077  aView = [aTrackingArea view];
4078 
4079  // First, if aView is _windowView, skip to next overlapping tracking area
4080  // as _windowView can't be the frontmost view if there's multiple overlapping tracking areas.
4081 
4082  if (aView === _windowView)
4083  continue;
4084 
4085  // Then, if frontmostView is _windowView, aView must become frontmostView
4086 
4087  if (frontmostView === _windowView)
4088  {
4089  frontmostTrackingArea = aTrackingArea;
4090  frontmostView = aView;
4091 
4092  continue;
4093  }
4094 
4095  // Next verify if aView is a subview of frontmostView
4096  // If so, it's our new frontmost view
4097 
4098  var searchingView = aView;
4099 
4100  while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView))
4101  searchingView = [searchingView superview];
4102 
4103  if (searchingView !== _contentView)
4104  {
4105  frontmostTrackingArea = aTrackingArea;
4106  frontmostView = aView;
4107 
4108  continue;
4109  }
4110 
4111  // aView is not a subview of frontmostView
4112  // Search in view hierarchy which one will be over the other
4113  // (this is done by comparing their draw order)
4114 
4115  var firstView = frontmostView,
4116  firstSuperview = [firstView superview];
4117 
4118  while (firstView !== _contentView)
4119  {
4120  var secondView = aView,
4121  secondSuperview = [secondView superview];
4122 
4123  while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview))
4124  {
4125  secondView = secondSuperview;
4126  secondSuperview = [secondView superview];
4127  }
4128 
4129  if (firstSuperview === secondSuperview)
4130  break;
4131 
4132  firstView = firstSuperview;
4133  firstSuperview = [firstView superview];
4134  }
4135 
4136  if (firstSuperview !== secondSuperview)
4137  [CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"];
4138 
4139  var firstSuperviewSubviews = [firstSuperview subviews],
4140  firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView],
4141  secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView];
4142 
4143  if (secondViewIndex > firstViewIndex)
4144  {
4145  frontmostTrackingArea = aTrackingArea;
4146  frontmostView = aView;
4147  }
4148  }
4149 
4150  if (frontmostTrackingArea !== _activeCursorTrackingArea)
4151  {
4152  var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate
4153  location:point
4154  modifierFlags:[anEvent modifierFlags]
4155  timestamp:[anEvent timestamp]
4156  windowNumber:_windowNumber
4157  context:nil
4158  eventNumber:-1
4159  trackingArea:frontmostTrackingArea];
4160 
4161  if (dragging)
4162  [self _queueTrackingEvent:cursorUpdateEvent];
4163  else
4164  [_queuedEvents addObject:cursorUpdateEvent];
4165 
4166  _activeCursorTrackingArea = frontmostTrackingArea;
4167  }
4168 }
4169 
4170 - (void)_queueTrackingEvent:(CPEvent)anEvent
4171 {
4172  // This will put a tracking event in the _queuedTrackingEvents queue.
4173  //
4174  // We optimize this queue with this policy :
4175  // - if mouseEntered, search if queue contains a previous mouseExited for the same tracking area. If so, discard both.
4176  // - if mouseExited, search if queue contains a previous mouseEntered for the same tracking area. If so, discard both.
4177  //
4178  // This is not Cocoa way of doing as it would send every event.
4179  // But final result should be the same.
4180 
4181  var eventType = [anEvent type],
4182  trackingArea = [anEvent trackingArea];
4183 
4184  switch ([anEvent type])
4185  {
4186  case CPMouseEntered:
4187  for (var i = 0; i < _queuedTrackingEvents.length; i++)
4188  {
4189  var queuedEvent = _queuedTrackingEvents[i];
4190 
4191  if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseExited))
4192  {
4193  [_queuedTrackingEvents removeObjectAtIndex:i];
4194  return;
4195  }
4196  }
4197 
4198  [_queuedTrackingEvents addObject:anEvent];
4199  break;
4200 
4201  case CPMouseExited:
4202  for (var i = 0; i < _queuedTrackingEvents.length; i++)
4203  {
4204  var queuedEvent = _queuedTrackingEvents[i];
4205 
4206  if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseEntered))
4207  {
4208  [_queuedTrackingEvents removeObjectAtIndex:i];
4209  return;
4210  }
4211  }
4212 
4213  [_queuedTrackingEvents addObject:anEvent];
4214  break;
4215 
4216  case CPCursorUpdate:
4217  [_queuedTrackingEvents addObject:anEvent];
4218  break;
4219  }
4220 }
4221 
4222 - (void)_flushTrackingEventQueueForMouseAt:(CGPoint)point
4223 {
4224  for (var i = 0; i < _queuedTrackingEvents.length; i++)
4225  {
4226  var queuedEvent = _queuedTrackingEvents[i],
4227  trackingArea = [queuedEvent trackingArea],
4228  trackingOwner = [trackingArea owner];
4229 
4230  switch ([queuedEvent type])
4231  {
4232  case CPMouseEntered:
4233  [trackingOwner mouseEntered:queuedEvent];
4234  break;
4235 
4236  case CPMouseExited:
4237  [trackingOwner mouseExited:queuedEvent];
4238  break;
4239 
4240  case CPCursorUpdate:
4241  [trackingOwner updateTrackingAreas];
4242 
4243  if (CGRectContainsPoint([trackingArea windowRect], point))
4244  [trackingOwner cursorUpdate:queuedEvent];
4245 
4246  break;
4247  }
4248  }
4249 
4250  _queuedTrackingEvents = [];
4251 }
4252 
4253 @end
4254 
4255 function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasShadow, aLevel)
4256 {
4257  return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel };
4258 }
4259 
4261 
4265 - (void)_setChildOrdering:(CPWindowOrderingMode)aValue
4266 {
4267  _childOrdering = aValue;
4268 }
4269 
4270 @end
void isMiniaturized()
Definition: CPWindow.j:2309
void setHasShadow:(BOOL shouldHaveShadow)
Definition: CPWindow.j:1391
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
BOOL makeFirstResponder:(CPResponder aResponder)
Definition: CPWindow.j:1618
void setFrame:(CGRect aFrame)
Definition: CPWindow.j:886
CPOrderedAscending
Definition: CPObjJRuntime.j:48
CPToolbar toolbar()
Definition: CPWindow.j:2580
BOOL isMovableByWindowBackground()
Definition: CPWindow.j:1755
Definition: CPMenu.h:2
BOOL isHidden()
Definition: CALayer.j:597
var CPWindowResizeTime
Definition: CPWindow.j:63
CPWindow setParentWindow:(CPWindow parentWindow)
Definition: CPWindow.j:2700
CPView previousValidKeyView()
Definition: CPView.j:2995
A CALayer is similar to a CPView
Definition: CALayer.j:46
void center()
Definition: CPWindow.j:1780
CPDisplayPatternTitleBinding
CGPoint convertBaseToPlatformWindow:(CGPoint aPoint)
Definition: CPWindow.j:3562
CPBorderlessWindowMask
BOOL tryToPerform:with:(SEL aSelector, [with] id anObject)
Definition: CPResponder.j:319
var CPWindowDelegate_windowShouldClose_
Definition: CPWindow.j:55
CPWindowPositionFlexibleTop
id delegate()
Definition: CPWindow.j:1538
CPWindowMinXMargin
CPWindow parentWindow()
Definition: CPWindow.j:2679
CPWindowController windowController()
Definition: CPWindow.j:1555
void makeMainWindow()
Definition: CPWindow.j:2454
CPOrderedSame
Definition: CPObjJRuntime.j:54
float alphaValue()
Definition: CPWindow.j:1229
CPTrackingOwnerImplementsCursorUpdate
CPWindowResizeStyleModern
CPCursor arrowCursor()
Definition: CPCursor.j:192
var CPWindowSaveImage
Definition: CPWindow.j:61
CPWindowPositionFlexibleRight
void enableKeyEquivalentForDefaultButtonCell()
Definition: CPWindow.j:3348
CPMouseExited
void setRepresentedFilename:(CPString aFilePath)
Definition: CPWindow.j:1706
CPTrackingMouseMoved
void becomeMainWindow()
Definition: CPWindow.j:2473
BOOL isVisible()
Definition: CPToolbar.j:207
void setShowsResizeIndicator:(BOOL shouldShowResizeIndicator)
Definition: CPWindow.j:1155
CPColor backgroundColor()
Definition: CPWindow.j:1246
CPArray childWindows()
Definition: CPWindow.j:2643
CGPoint convertBaseToScreen:(CGPoint aPoint)
Definition: CPWindow.j:3590
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
CPView initialFirstResponder()
Definition: CPWindow.j:1573
void setTitle:(CPString aTitle)
Definition: CPWindow.j:1686
CPScreen screen()
Definition: CPWindow.j:1736
CPResponder firstResponder()
Definition: CPWindow.j:1643
CGPoint locationInWindow()
Definition: CPEvent.j:287
id initWithContentRect:(CGRect aRect)
var CPWindowActionMessageKeys
Definition: CPWindow.j:72
CPWindowBelow
void setPlatformWindow:(CPPlatformWindow aPlatformWindow)
Definition: CPWindow.j:394
var CPWindowResizeStyleGlobalChangeNotification
Definition: CPWindow.j:64
CPWindowMaxYMargin
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void awakeFromCib()
Definition: CPWindow.j:435
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
CPBorderlessBridgeWindowMask
CPString charactersIgnoringModifiers()
Definition: CPEvent.j:381
CPEventType type()
Definition: CPEvent.j:322
var CPWindowMinVisibleHorizontalMargin
Definition: CPWindow.j:66
void miniaturize:(id sender)
CPArray selectorsForKey:modifierFlags:(CPString aKey, [modifierFlags] unsigned aFlag)
Definition: CPKeyBinding.j:180
CPWindow attachedSheet()
Definition: CPWindow.j:3034
CPTrackingActiveInKeyWindow
unsigned modifierFlags()
Definition: CPEvent.j:306
BOOL canBecomeMainWindow()
Definition: CPWindow.j:2441
void keyDown:(CPEvent anEvent)
Definition: CPResponder.j:222
int globalResizeStyle()
Definition: CPWindow.j:1138
BOOL isSheet()
Definition: CPWindow.j:3045
BOOL isFullPlatformWindow()
Definition: CPWindow.j:607
CGRect visibleFrame()
Definition: CPScreen.j:42
CPTrackingActiveInActiveApp
CPWindowHeightSizable
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
void dragImage:fromWindow:at:offset:event:pasteboard:source:slideBack:(CPImage anImage, [fromWindow] CPWindow aWindow, [at] CGPoint imageLocation, [offset] CGSize mouseOffset, [event] CPEvent anEvent, [pasteboard] CPPasteboard aPasteboard, [source] id aSourceObject, [slideBack] BOOL slideBack)
Definition: CPDragServer.j:427
CPTrackingActiveWhenFirstResponder
id init()
Definition: CPWindow.j:243
CPWindowResizeStyle
void setShadowStyle:(int aStyle)
void setFrameSize:(CGSize aSize)
Definition: CPWindow.j:905
BOOL autorecalculatesKeyViewLoop()
Definition: CPWindow.j:3218
CPTitledWindowMask
void setFrame:display:animate:(CGRect aFrame, [display] BOOL shouldDisplay, [animate] BOOL shouldAnimate)
Definition: CPWindow.j:677
CPCursorUpdate
CGRect bounds()
Definition: CALayer.j:203
CPButton defaultButtonCell()
Definition: CPWindow.j:3303
void display()
Definition: CALayer.j:488
CPMouseMoved
id initWithContentRect:styleMask:(CGRect aContentRect, [styleMask] unsigned aStyleMask)
Definition: CPWindow.j:263
void orderFront:(id aSender)
Definition: CPWindow.j:914
void close()
Definition: CPWindow.j:2394
CPWindowPositionFlexibleLeft
CPTabCharacter
Definition: CPText.j:25
CPMouseEntered
int windowNumber()
Definition: CPWindow.j:1994
BOOL ignoresMouseEvents()
Definition: CPWindow.j:1658
void flagsChanged:(CPEvent anEvent)
Definition: CPResponder.j:240
An immutable string (collection of characters).
Definition: CPString.h:2
CPString title()
Definition: CPWindow.j:1678
CPUndoManager undoManager()
Definition: CPWindow.j:3615
CGSize maxSize()
Definition: CPWindow.j:1336
unsigned autoresizingMask()
Definition: CPWindow.j:3538
void unregisterDraggedTypes()
Definition: CPWindow.j:2184
Definition: CPImage.h:2
void isMovable()
Definition: CPWindow.j:1772
CPRightMouseDragged
CPOrderedDescending
Definition: CPObjJRuntime.j:60
BOOL isDocumentSaving()
Definition: CPWindow.j:2230
CPEscapeFunctionKey
CPFlagsChanged
CGPoint convertBaseToGlobal:(CGPoint aPoint)
Definition: CPWindow.j:3546
CPView nextValidKeyView()
Definition: CPView.j:2969
void selectNextKeyView:(id sender)
Definition: CPWindow.j:3223
CPWindowConstrainToScreen
CPTrackingOwnerImplementsMouseMoved
void miniaturize:(id sender)
Definition: CPWindow.j:2281
CPKeyDown
CPWindowNotSizable
var keyViewComparator
Definition: CPWindow.j:3388
BOOL isMainWindow()
Definition: CPWindow.j:2433
CGPoint convertPlatformWindowToBase:(CGPoint aPoint)
Definition: CPWindow.j:3575
CPDragServer sharedDragServer()
Definition: CPDragServer.j:142
void doCommandBySelector:(SEL aSelector)
Definition: CPResponder.j:304
void makeKeyAndOrderFront:(id aSender)
Definition: CPWindow.j:2061
void disableKeyEquivalentForDefaultButton()
Definition: CPWindow.j:3356
CPResizableWindowMask
BOOL isVisible()
Definition: CPWindow.j:1094
CPString representedFilename()
Definition: CPWindow.j:1715
CPWindow window()
Definition: CPEvent.j:330
CGRect frameRectForContentRect:(CGRect aContentRect)
Definition: CPWindow.j:657
CPBackTabCharacter
Definition: CPText.j:29
BOOL worksWhenModal()
Definition: CPWindow.j:3064
CPLeftMouseUp
CPShiftKeyMask
void performClick:(id sender)
Definition: CPButton.j:860
CPTrackingOwnerImplementsMouseExited
CPURL representedURL()
Definition: CPWindow.j:1731
void dragView:fromWindow:at:offset:event:pasteboard:source:slideBack:(CPView aView, [fromWindow] CPWindow aWindow, [at] CGPoint viewLocation, [offset] CGSize mouseOffset, [event] CPEvent mouseDownEvent, [pasteboard] CPPasteboard aPasteboard, [source] id aSourceObject, [slideBack] BOOL slideBack)
Definition: CPDragServer.j:346
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:58
void disableKeyEquivalentForDefaultButtonCell()
Definition: CPWindow.j:3365
void setDefaultButton:(CPButton aButton)
Definition: CPWindow.j:3314
void recalculateKeyViewLoop()
Definition: CPWindow.j:3159
CPTheme defaultTheme()
Definition: CPTheme.j:44
void setAutoresizingMask:(unsigned anAutoresizingMask)
Definition: CPWindow.j:3530
BOOL acceptsFirstResponder()
Definition: CPWindow.j:1568
CPClosableWindowMask
void setVisible:(BOOL aFlag)
Definition: CPToolbar.j:216
void becomeKeyWindow()
Definition: CPWindow.j:2004
CPTimeInterval timestamp()
Definition: CPEvent.j:314
An mutable collection which may contain a specific object numerous times.
Definition: CPCountedSet.h:2
CGRect frame()
Definition: CPWindow.j:665
id enterExitEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:trackingArea:(CPEventType anEventType, [location] CGPoint aPoint, [modifierFlags] unsigned modifierFlags, [timestamp] CPTimeInterval aTimestamp, [windowNumber] int aWindowNumber, [context] CPGraphicsContext aGraphicsContext, [eventNumber] int anEventNumber, [trackingArea] CPTrackingArea aTrackingArea)
Definition: CPEvent.j:149
void setFrameOrigin:(CGPoint anOrigin)
Definition: CPWindow.j:895
BOOL becomesKeyOnlyIfNeeded()
Definition: CPWindow.j:3055
BOOL canBecomeKeyWindow()
Definition: CPWindow.j:2035
CGSize minSize()
Definition: CPWindow.j:1287
A general mechanism for user action "undo".
Definition: CPUndoManager.h:2
CPString lastPathComponent()
Definition: CPString.j:824
CPPlatformActionKeyMask
BOOL isKeyWindow()
Definition: CPWindow.j:2052
A timer object that can send a message after the given time interval.
Definition: CPTimer.h:2
CPLeftMouseDragged
void enableKeyEquivalentForDefaultButton()
Definition: CPWindow.j:3339
void setLevel:(int aLevel)
Definition: CPWindow.j:1069
void keyUp:(CPEvent anEvent)
Definition: CPResponder.j:231
BOOL resignFirstResponder()
Definition: CPResponder.j:73
CPNotFound
Definition: CPObjJRuntime.j:62
void setMovableByWindowBackground:(BOOL shouldBeMovableByWindowBackground)
Definition: CPWindow.j:1747
CPUndoManager undoManager()
Definition: CPWindow.j:3615
BOOL constrainWindowsToUsableScreen()
Definition: CPWindow.j:1125
void setValue:forKey:(id aValue, [forKey] CPString aKey)
id init()
Definition: CPObject.j:145
CPTrackingAreaOptions CPTrackingMouseEnteredAndExited
Sends messages (CPNotification) between objects.
void selectPreviousKeyView:(id sender)
Definition: CPWindow.j:3245
Definition: CPTheme.h:2
void makeKeyWindow()
Definition: CPWindow.j:2072
CPWindowMinYMargin
var CPWindowMinVisibleVerticalMargin
Definition: CPWindow.j:67
CPRightMouseUp
CPDocModalWindowMask
Definition: CPPanel.j:29
void setFullPlatformWindow:(BOOL shouldBeFullPlatformWindow)
Definition: CPWindow.j:561
var CPWindowDelegate_windowWillClose_
Definition: CPWindow.j:57
CPWindowWidthSizable
void setNextResponder:(CPResponder aResponder)
Definition: CPResponder.j:83
void resignMainWindow()
Definition: CPWindow.j:2490
CPLeftMouseDown
int level()
Definition: CPWindow.j:1086
BOOL isDocumentEdited()
Definition: CPWindow.j:2213
BOOL acceptsMouseMovedEvents()
Definition: CPWindow.j:1648
CPCarriageReturnCharacter
Definition: CPText.j:28
void deminiaturize:(id sender)
void removeObserver:name:object:(id anObserver, [name] CPString aNotificationName, [object] id anObject)
void set()
Definition: CPCursor.j:122
CPTrackingEnabledDuringMouseDrag
Definition: CPEvent.h:2
Class class()
Definition: CPObject.j:179
CPView contentView()
Definition: CPWindow.j:1212
void orderWindow:relativeTo:(CPWindowOrderingMode orderingMode, [relativeTo] int otherWindowNumber)
Definition: CPWindow.j:1045
CGRect contentRectForFrameRect:(CGRect aFrame)
Definition: CPWindow.j:647
CPPlatformWindow platformWindow()
Definition: CPWindow.j:384
CGRect frame()
Definition: CPView.j:1011
BOOL isBrowser()
Definition: CPPlatform.j:33
id URLWithString:(CPString URLString)
Definition: CPURL.j:78
CPArray registeredDraggedTypes()
Definition: CPWindow.j:2173
BOOL hasShadow()
Definition: CPWindow.j:1344
CPAppKitDefined
id valueForAttributeWithName:forClass:(CPString aName, [forClass] id aClass)
Definition: CPTheme.j:218
void setRepresentedURL:(CPURL aURL)
Definition: CPWindow.j:1723
CGPoint convertScreenToBase:(CGPoint aPoint)
Definition: CPWindow.j:3585
CPTrackingOwnerImplementsMouseEntered
Definition: CPURL.h:2
var interpolate
Definition: CPWindow.j:3712
CPWindowOrderingMode CPWindowAbove
BOOL becomeFirstResponder()
Definition: CPResponder.j:64
CPRightMouseDown
CPTrackingArea trackingArea()
Definition: CPEvent.j:620
BOOL showsResizeIndicator()
Definition: CPWindow.j:1146
void resignKeyWindow()
Definition: CPWindow.j:2084
CPButton defaultButton()
Definition: CPWindow.j:3331
CPWindowMaxXMargin
var CPWindowDelegate_windowWillResize_toSize_
Definition: CPWindow.j:58
CPWindowPositionFlexibleBottom
void setContentView:(CPView aView)
Definition: CPWindow.j:1188
CPPlatformWindow primaryPlatformWindow()
CPTimeInterval animationResizeTime:(CGRect newWindowFrame)
Definition: CPWindow.j:2715
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:131
CPTrackingCursorUpdate
CGSize resizeIndicatorOffset()
Definition: CPWindow.j:1169
var CPWindowDelegate_windowWillReturnUndoManager_
Definition: CPWindow.j:56
unsigned styleMask()
Definition: CPWindow.j:615
CPScrollWheel
CPHUDBackgroundWindowMask
CPTrackingActiveAlways