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