API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
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 @class CPMenu
29 
30 @global CPApp
31 
32 var CPWindowSaveImage = nil,
33 
34  CPWindowResizeTime = 0.2,
35  CPWindowResizeStyleGlobalChangeNotification = @"CPWindowResizeStyleGlobalChangeNotification",
36 
37  CPWindowMinVisibleHorizontalMargin = 40,
38  CPWindowMinVisibleVerticalMargin = 2;
39 
40 /*
41  Keys for which action messages will be sent by default when unhandled, e.g. complete:.
42 */
43 var CPWindowActionMessageKeys = [
53  ];
54 
111 @implementation CPWindow : CPResponder
112 {
113  CPPlatformWindow _platformWindow;
114 
115  int _windowNumber;
116  unsigned _styleMask;
117  CGRect _frame;
118  int _level;
119  BOOL _isVisible;
120  BOOL _hasBeenOrderedIn;
121  BOOL _isMiniaturized;
122  BOOL _isAnimating;
123  BOOL _hasShadow;
124  BOOL _isMovableByWindowBackground;
125  BOOL _isMovable;
126  BOOL _constrainsToUsableScreen;
127  unsigned _shadowStyle;
128  BOOL _showsResizeIndicator;
129 
130  int _positioningMask;
131  CGRect _positioningScreenRect;
132 
133  BOOL _isDocumentEdited;
134  BOOL _isDocumentSaving;
135 
136  CPImageView _shadowView;
137 
138  CPView _windowView;
139  CPView _contentView;
140  CPView _toolbarView;
141 
142  CPArray _mouseEnteredStack;
143  CPView _leftMouseDownView;
144  CPView _rightMouseDownView;
145 
146  CPToolbar _toolbar;
147  CPResponder _firstResponder;
148  CPResponder _initialFirstResponder;
149  BOOL _hasBecomeKeyWindow;
150  id _delegate;
151 
152  CPString _title;
153 
154  BOOL _acceptsMouseMovedEvents;
155  BOOL _ignoresMouseEvents;
156 
157  CPWindowController _windowController;
158 
159  CGSize _minSize;
160  CGSize _maxSize;
161 
162  CPUndoManager _undoManager;
163  CPURL _representedURL;
164 
165  CPSet _registeredDraggedTypes;
166  CPArray _registeredDraggedTypesArray;
167  CPCountedSet _inclusiveRegisteredDraggedTypes;
168 
169  CPButton _defaultButton;
170  BOOL _defaultButtonEnabled;
171 
172  BOOL _autorecalculatesKeyViewLoop;
173  BOOL _keyViewLoopIsDirty;
174 
175  BOOL _sharesChromeWithPlatformWindow;
176 
177  // Bridge Support
178 #if PLATFORM(DOM)
179  DOMElement _DOMElement;
180 #endif
181 
182  unsigned _autoresizingMask;
183 
184  BOOL _delegateRespondsToWindowWillReturnUndoManagerSelector;
185 
186  BOOL _isFullPlatformWindow;
187  _CPWindowFullPlatformWindowSession _fullPlatformWindowSession;
188 
189  CPWindow _parentWindow;
190  CPArray _childWindows;
191  CPWindowOrderingMode _childOrdering;
192 
193  CPDictionary _sheetContext;
194  CPWindow _parentView;
195  BOOL _isSheet;
196  _CPWindowFrameAnimation _frameAnimation;
197 }
198 
199 + (Class)_binderClassForBinding:(CPString)aBinding
200 {
201  if ([aBinding hasPrefix:CPDisplayPatternTitleBinding])
202  return [CPTitleWithPatternBinding class];
203 
204  return [super _binderClassForBinding:aBinding];
205 }
206 
207 - (id)init
208 {
209  return [self initWithContentRect:CGRectMakeZero() styleMask:CPTitledWindowMask];
210 }
211 
227 - (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask
228 {
229  self = [super init];
230 
231  if (self)
232  {
233  var windowViewClass = [[self class] _windowViewClassForStyleMask:aStyleMask];
234 
235  _frame = [windowViewClass frameRectForContentRect:aContentRect];
236  _constrainsToUsableScreen = YES;
237 
238  [self _setSharesChromeWithPlatformWindow:![CPPlatform isBrowser]];
239 
240  if ([CPPlatform isBrowser])
241  [self setPlatformWindow:[CPPlatformWindow primaryPlatformWindow]];
242  else
243  {
244  // give zero sized borderless bridge windows a default size if we're not in the browser so they show up in NativeHost.
245  if ((aStyleMask & CPBorderlessBridgeWindowMask) && aContentRect.size.width === 0 && aContentRect.size.height === 0)
246  {
247  var visibleFrame = [[[CPScreen alloc] init] visibleFrame];
248  _frame.size.height = MIN(768.0, visibleFrame.size.height);
249  _frame.size.width = MIN(1024.0, visibleFrame.size.width);
250  _frame.origin.x = (visibleFrame.size.width - _frame.size.width) / 2;
251  _frame.origin.y = (visibleFrame.size.height - _frame.size.height) / 2;
252  }
253 
254  [self setPlatformWindow:[[CPPlatformWindow alloc] initWithContentRect:_frame]];
255  [self platformWindow]._only = self;
256  }
257 
258  _isFullPlatformWindow = NO;
259  _registeredDraggedTypes = [CPSet set];
260  _registeredDraggedTypesArray = [];
261  _acceptsMouseMovedEvents = YES;
262  _isMovable = YES;
263  _hasBeenOrderedIn = NO;
264 
265  _parentWindow = nil;
266  _childWindows = [];
267  _childOrdering = CPWindowOut;
268 
269  _isSheet = NO;
270  _sheetContext = nil;
271  _parentView = nil;
272 
273  // Set up our window number.
274  _windowNumber = [CPApp._windows count];
275  CPApp._windows[_windowNumber] = self;
276 
277  _styleMask = aStyleMask;
278 
279  [self setLevel:CPNormalWindowLevel];
280 
281  // Create our border view which is the actual root of our view hierarchy.
282  _windowView = [[windowViewClass alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) styleMask:aStyleMask];
283 
284  [_windowView _setWindow:self];
285  [_windowView setNextResponder:self];
286 
287  // Size calculation needs _windowView
288  _minSize = [self _calculateMinSizeForProposedSize:CGSizeMake(0.0, 0.0)];
289  _maxSize = CGSizeMake(1000000.0, 1000000.0);
290 
291  [self setMovableByWindowBackground:aStyleMask & CPHUDBackgroundWindowMask];
292 
293  // Create a generic content view.
294  [self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
295 
296  _firstResponder = self;
297 
298 #if PLATFORM(DOM)
299  _DOMElement = document.createElement("div");
300 
301  _DOMElement.style.position = "absolute";
302  _DOMElement.style.visibility = "visible";
303  _DOMElement.style.zIndex = 0;
304 
305  if (![self _sharesChromeWithPlatformWindow])
306  {
307  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(_frame), CGRectGetMinY(_frame));
308  }
309 
310  CPDOMDisplayServerSetStyleSize(_DOMElement, 1, 1);
311  CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement);
312 #endif
313 
314  [self setNextResponder:CPApp];
315 
316  [self setHasShadow:aStyleMask !== CPBorderlessWindowMask];
317 
318  if (aStyleMask & CPBorderlessBridgeWindowMask)
319  [self setFullPlatformWindow:YES];
320 
321  _autorecalculatesKeyViewLoop = NO;
322  _defaultButtonEnabled = YES;
323  _keyViewLoopIsDirty = NO;
324  _hasBecomeKeyWindow = NO;
325 
326  [self setShowsResizeIndicator:_styleMask & CPResizableWindowMask];
327 
330  name:CPWindowResizeStyleGlobalChangeNotification
331  object:nil];
332  }
333 
334  return self;
335 }
336 
337 - (CPPlatformWindow)platformWindow
338 {
339  return _platformWindow;
340 }
341 
347 - (void)setPlatformWindow:(CPPlatformWindow)aPlatformWindow
348 {
349  var wasVisible = [self isVisible];
350 
351  // we have to close it first, otherwise we get a DOM exception.
352  if (wasVisible)
353  [self close];
354 
355  _platformWindow = aPlatformWindow;
356  [_platformWindow _setTitle:_title window:self];
357 
358  if (wasVisible)
359  [self orderFront:self];
360 }
361 
362 
366 + (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask
367 {
368  if (aStyleMask & CPHUDBackgroundWindowMask)
369  return _CPHUDWindowView;
370 
371  else if (aStyleMask === CPBorderlessWindowMask)
372  return _CPBorderlessWindowView;
373 
374  else if (aStyleMask & CPDocModalWindowMask)
375  return _CPDocModalWindowView;
376 
377  else if (aStyleMask & _CPModalWindowMask)
378  return _CPModalWindowView;
379 
380  return _CPStandardWindowView;
381 }
382 
383 + (Class)_windowViewClassForFullPlatformWindowStyleMask:(unsigned)aStyleMask
384 {
385  return _CPBorderlessBridgeWindowView;
386 }
387 
388 - (void)awakeFromCib
389 {
390  // At this time we know the final screen (or browser) size
391  // and can apply the positioning mask, if any, from the nib.
392  if (_positioningScreenRect)
393  {
394  var actualScreenRect = [CPPlatform isBrowser] ? [_platformWindow contentBounds] : [[self screen] visibleFrame],
395  frame = [self frame],
396  origin = frame.origin;
397 
398  if (actualScreenRect)
399  {
400  if ((_positioningMask & CPWindowPositionFlexibleLeft) && (_positioningMask & CPWindowPositionFlexibleRight))
401  {
402  // Proportional Horizontal.
403  origin.x *= (actualScreenRect.size.width / _positioningScreenRect.size.width);
404  }
405  else if (_positioningMask & CPWindowPositionFlexibleLeft)
406  {
407  // Fixed from Right
408  origin.x += actualScreenRect.size.width - _positioningScreenRect.size.width;
409  }
410  else if (_positioningMask & CPWindowPositionFlexibleRight)
411  {
412  // Fixed from Left
413  }
414 
415  if ((_positioningMask & CPWindowPositionFlexibleTop) && (_positioningMask & CPWindowPositionFlexibleBottom))
416  {
417  // Proportional Vertical.
418  origin.y *= (actualScreenRect.size.height / _positioningScreenRect.size.height);
419  }
420  else if (_positioningMask & CPWindowPositionFlexibleTop)
421  {
422  // Fixed from Bottom
423  origin.y += actualScreenRect.size.height - _positioningScreenRect.size.height;
424  }
425  else if (_positioningMask & CPWindowPositionFlexibleBottom)
426  {
427  // Fixed from Top
428  }
429 
430  [self setFrameOrigin:origin];
431  }
432  }
433 
434  /*
435  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.
436  */
437  if ([self _hasKeyViewLoop:[_contentView subviews]])
438  {
439  var views = [self _viewsSortedByPosition],
440  count = [views count];
441 
442  // The first view is the content view.
443  // Find the first subview that has a next key view.
444  for (var i = 1; i < count; ++i)
445  {
446  var view = views[i];
447 
448  if ([view nextKeyView])
449  {
450  [_contentView setNextKeyView:view];
451  break;
452  }
453  }
454  }
455  else
456  {
457  // Cooca does NOT call the public method recalculateKeyViewLoop for nibs,
458  // but it does calculate the loop.
459  [self _doRecalculateKeyViewLoop];
460  }
461 }
462 
463 - (void)_setWindowView:(CPView)aWindowView
464 {
465  if (_windowView === aWindowView)
466  return;
467 
468  var oldWindowView = _windowView;
469 
470  _windowView = aWindowView;
471 
472  if (oldWindowView)
473  {
474  [oldWindowView _setWindow:nil];
475  [oldWindowView noteToolbarChanged];
476 
477 #if PLATFORM(DOM)
478  CPDOMDisplayServerRemoveChild(_DOMElement, oldWindowView._DOMElement);
479 #endif
480  }
481 
482  if (_windowView)
483  {
484 #if PLATFORM(DOM)
485  CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement);
486 #endif
487 
488  var contentRect = [_contentView convertRect:[_contentView bounds] toView:nil];
489 
490  contentRect.origin = [self convertBaseToGlobal:contentRect.origin];
491 
492  [_windowView _setWindow:self];
493  [_windowView setNextResponder:self];
494  [_windowView addSubview:_contentView];
495  [_windowView setTitle:_title];
496  [_windowView noteToolbarChanged];
497  [_windowView setShowsResizeIndicator:[self showsResizeIndicator]];
498 
499  [self setFrame:[self frameRectForContentRect:contentRect]];
500  }
501 }
502 
509 - (void)setFullPlatformWindow:(BOOL)shouldBeFullPlatformWindow
510 {
511  if (![_platformWindow supportsFullPlatformWindows])
512  return;
513 
514  shouldBeFullPlatformWindow = !!shouldBeFullPlatformWindow;
515 
516  if (_isFullPlatformWindow === shouldBeFullPlatformWindow)
517  return;
518 
519  _isFullPlatformWindow = shouldBeFullPlatformWindow;
520 
521  if (_isFullPlatformWindow)
522  {
523  _fullPlatformWindowSession = _CPWindowFullPlatformWindowSessionMake(_windowView, [self contentRectForFrameRect:[self frame]], [self hasShadow], [self level]);
524 
525  var fullPlatformWindowViewClass = [[self class] _windowViewClassForFullPlatformWindowStyleMask:_styleMask],
526  windowView = [[fullPlatformWindowViewClass alloc] initWithFrame:CGRectMakeZero() styleMask:_styleMask];
527 
528  [self _setWindowView:windowView];
529 
530  [self setLevel:CPBackgroundWindowLevel];
531  [self setHasShadow:NO];
532  [self setAutoresizingMask:CPWindowWidthSizable | CPWindowHeightSizable];
533  [self setFrame:[_platformWindow visibleFrame]];
534  }
535  else
536  {
537  var windowView = _fullPlatformWindowSession.windowView;
538 
539  [self _setWindowView:windowView];
540 
541  [self setLevel:_fullPlatformWindowSession.level];
542  [self setHasShadow:_fullPlatformWindowSession.hasShadow];
543  [self setAutoresizingMask:CPWindowNotSizable];
544 
545  [self setFrame:[windowView frameRectForContentRect:_fullPlatformWindowSession.contentRect]];
546  }
547 }
548 
552 - (BOOL)isFullPlatformWindow
553 {
554  return _isFullPlatformWindow;
555 }
556 
560 - (unsigned)styleMask
561 {
562  return _styleMask;
563 }
564 
583 + (CGRect)frameRectForContentRect:(CGRect)aContentRect styleMask:(unsigned)aStyleMask
584 {
585  return [[[self class] _windowViewClassForStyleMask:aStyleMask] frameRectForContentRect:aContentRect];
586 }
587 
592 - (CGRect)contentRectForFrameRect:(CGRect)aFrame
593 {
594  return [_windowView contentRectForFrameRect:aFrame];
595 }
596 
602 - (CGRect)frameRectForContentRect:(CGRect)aContentRect
603 {
604  return [_windowView frameRectForContentRect:aContentRect];
605 }
606 
610 - (CGRect)frame
611 {
612  return CGRectMakeCopy(_frame);
613 }
614 
622 - (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
623 {
624  [self _setFrame:aFrame display:shouldDisplay animate:shouldAnimate constrainWidth:NO constrainHeight:YES];
625 }
626 
627 - (void)_setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate constrainWidth:(BOOL)shouldConstrainWidth constrainHeight:(BOOL)shouldConstrainHeight
628 {
629  var frame = CGRectMakeCopy(aFrame),
630  value = frame.origin.x,
631  delta = value - FLOOR(value);
632 
633  if (delta)
634  frame.origin.x = value > 0.879 ? CEIL(value) : FLOOR(value);
635 
636  value = frame.origin.y;
637  delta = value - FLOOR(value);
638 
639  if (delta)
640  frame.origin.y = value > 0.879 ? CEIL(value) : FLOOR(value);
641 
642  value = frame.size.width;
643  delta = value - FLOOR(value);
644 
645  if (delta)
646  frame.size.width = value > 0.15 ? CEIL(value) : FLOOR(value);
647 
648  value = frame.size.height;
649  delta = value - FLOOR(value);
650 
651  if (delta)
652  frame.size.height = value > 0.15 ? CEIL(value) : FLOOR(value);
653 
654  frame = [self _constrainFrame:frame toUsableScreenWidth:shouldConstrainWidth andHeight:shouldConstrainHeight];
655 
656  if (shouldAnimate)
657  {
658  [_frameAnimation stopAnimation];
659  _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:frame];
660 
661  [_frameAnimation startAnimation];
662  }
663  else
664  {
665  var origin = _frame.origin,
666  newOrigin = frame.origin,
667  originMoved = !CGPointEqualToPoint(origin, newOrigin);
668 
669  if (originMoved)
670  {
671  delta = CGPointMake(newOrigin.x - origin.x, newOrigin.y - origin.y);
672  origin.x = newOrigin.x;
673  origin.y = newOrigin.y;
674 
675 #if PLATFORM(DOM)
676  if (![self _sharesChromeWithPlatformWindow])
677  {
678  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, origin.x, origin.y);
679  }
680 #endif
681 
682  // reposition sheet
683  if ([self attachedSheet])
684  [self _setAttachedSheetFrameOrigin];
685 
686  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidMoveNotification object:self];
687  }
688 
689  var size = _frame.size,
690  newSize = frame.size;
691 
692  if (!CGSizeEqualToSize(size, newSize))
693  {
694  size.width = newSize.width;
695  size.height = newSize.height;
696 
697  [_windowView setFrameSize:size];
698 
699  if (_hasShadow)
700  [_shadowView setNeedsLayout];
701 
702  if (!_isAnimating)
703  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidResizeNotification object:self];
704  }
705 
706  if ([self _sharesChromeWithPlatformWindow])
707  [_platformWindow setContentRect:_frame];
708 
709  if (originMoved)
710  [self _moveChildWindows:delta];
711  }
712 }
713 
714 /*
715  Constrain a frame so that the window remains at least partially visible on screen,
716  moving or resizing the frame as necessary.
717 */
718 - (CGRect)_constrainFrame:(CGRect)aFrame toUsableScreenWidth:(BOOL)constrainWidth andHeight:(BOOL)constrainHeight
719 {
720  var frame = CGRectMakeCopy(aFrame);
721 
722  if (!_constrainsToUsableScreen || !_isVisible)
723  return frame;
724 
725  var usableRect = [_platformWindow usableContentFrame];
726 
727  if (constrainWidth)
728  {
729  // First move the frame right to ensure the left side is within the usable rect.
730  frame.origin.x = MAX(frame.origin.x, usableRect.origin.x);
731 
732  // Now move the frame left so that the right side is within the usable rect.
733  var maxX = MIN(CGRectGetMaxX(frame), CGRectGetMaxX(usableRect));
734  frame.origin.x = maxX - frame.size.width;
735 
736  // Finally, adjust the left + width to ensure the left side is within the usable rect.
737  var usableWidth = CGRectGetWidth(usableRect);
738 
739  if (CGRectGetWidth(frame) > usableWidth)
740  {
741  frame.origin.x = CGRectGetMinX(usableRect);
742  frame.size.width = MAX(usableWidth, _minSize.width);
743  }
744  }
745 
746  if (constrainHeight)
747  {
748  // First move the frame down to ensure the top is within the usable rect.
749  frame.origin.y = MAX(frame.origin.y, usableRect.origin.y);
750 
751  // Now move the frame up so that the bottom is within the usable rect.
752  var maxY = MIN(CGRectGetMaxY(frame), CGRectGetMaxY(usableRect));
753  frame.origin.y = maxY - frame.size.height;
754 
755  // Finally, adjust the top + height to ensure the top is within the usable rect.
756  var usableHeight = CGRectGetHeight(usableRect);
757 
758  if (CGRectGetHeight(frame) > usableHeight)
759  {
760  frame.origin.y = CGRectGetMinY(usableRect);
761  frame.size.height = MAX(usableHeight, _minSize.height);
762  }
763  }
764 
765  return frame;
766 }
767 
768 /*
769  Constrain the origin of a frame such that:
770 
771  - The window view's minimum resize width is kept onscreen at the left/right of the window.
772  - The top of the window is kept below the top of the usable content.
773  - The top of the contentView + CPWindowMinVisibleVerticalMargin is kept above the bottom of the usable content.
774 */
775 - (CGRect)_constrainOriginOfFrame:(CGRect)aFrame
776 {
777  var frame = CGRectMakeCopy(aFrame);
778 
779  if (!_constrainsToUsableScreen || !_isVisible)
780  return frame;
781 
782  var usableRect = [_platformWindow usableContentFrame],
783  minimumSize = [_windowView _minimumResizeSize];
784 
785  // First constrain x so that at least CPWindowMinVisibleHorizontalMargin is visible on the right
786  frame.origin.x = MAX(frame.origin.x, CGRectGetMinX(usableRect) + minimumSize.width - CGRectGetWidth(frame));
787 
788  // Now constrain x so that at least CPWindowMinVisibleHorizontalMargin is visible on the left
789  frame.origin.x = MIN(frame.origin.x, CGRectGetMaxX(usableRect) - minimumSize.width);
790 
791  // Now constrain y so that it is below the top of the usable content
792  frame.origin.y = MAX(frame.origin.y, CGRectGetMinY(usableRect));
793 
794  // Finally constrain y so that at least CPWindowMinVisibleHorizontalMargin is visible at the bottom
795  frame.origin.y = MIN(frame.origin.y, CGRectGetMaxY(usableRect) - CGRectGetMinY([_contentView frame]) - CPWindowMinVisibleVerticalMargin);
796 
797  return frame;
798 }
799 
800 - (void)_moveChildWindows:(CGPoint)delta
801 {
802  [_childWindows enumerateObjectsUsingBlock:function(childWindow)
803  {
804  var origin = [childWindow frame].origin;
805 
806  [childWindow setFrameOrigin:CGPointMake(origin.x + delta.x, origin.y + delta.y)];
807  }
808  ];
809 }
810 
816 - (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay
817 {
818  [self setFrame:aFrame display:shouldDisplay animate:NO];
819 }
820 
825 - (void)setFrame:(CGRect)aFrame
826 {
827  [self setFrame:aFrame display:YES animate:NO];
828 }
829 
834 - (void)setFrameOrigin:(CGPoint)anOrigin
835 {
836  var frame = [self _constrainOriginOfFrame:CGRectMake(anOrigin.x, anOrigin.y, _frame.size.width, _frame.size.height)];
837  [self _setFrame:frame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
838 }
839 
844 - (void)setFrameSize:(CGSize)aSize
845 {
846  [self setFrame:CGRectMake(CGRectGetMinX(_frame), CGRectGetMinY(_frame), aSize.width, aSize.height) display:YES animate:NO];
847 }
848 
853 - (void)orderFront:(id)aSender
854 {
855  [self orderWindow:CPWindowAbove relativeTo:0];
856 }
857 
858 - (void)_orderFront
859 {
860 #if PLATFORM(DOM)
861  // -dw- if a sheet is clicked, the parent window should come up too
862  if (_isSheet)
863  [_parentView orderFront:self];
864 
865  if (!_isVisible)
866  [self _setFrame:_frame display:YES animate:NO constrainWidth:YES constrainHeight:YES];
867 
868  [_platformWindow orderFront:self];
869  [_platformWindow order:CPWindowAbove window:self relativeTo:nil];
870 #endif
871 
872  if (!CPApp._keyWindow)
873  [self makeKeyWindow];
874 
875  if ([self isKeyWindow] && (_firstResponder === self || !_firstResponder))
876  [self makeFirstResponder:_initialFirstResponder];
877 
878  if (!CPApp._mainWindow)
879  [self makeMainWindow];
880 }
881 
882 /*
883  Called when a parent window orders in a child window directly.
884  without going through the ordering methods in CPWindow.
885 */
886 - (void)_parentDidOrderInChild
887 {
888 }
889 
890 /*
891  Makes the receiver the last window in the screen ordering.
892  @param aSender the object that requested this
893  @ignore
894 */
895 - (void)orderBack:(id)aSender
896 {
897  [self orderWindow:CPWindowBelow relativeTo:0];
898 }
899 
900 - (void)_orderBack
901 {
902  // FIXME: Implement this
903 }
904 
909 - (void)orderOut:(id)aSender
910 {
911  [self orderWindow:CPWindowOut relativeTo:0];
912 }
913 
914 - (void)_orderOutRecursively:(BOOL)recursive
915 {
916  if (!_isVisible)
917  return;
918 
919  if ([self isSheet])
920  {
921  // -dw- as in Cocoa, orderOut: detaches the sheet and animates out
922  [self._parentView _detachSheetWindow];
923  return;
924  }
925 
926  if (recursive)
927  [_childWindows makeObjectsPerformSelector:@selector(_orderOutRecursively:) withObject:recursive];
928 
929 #if PLATFORM(DOM)
930  if ([self _sharesChromeWithPlatformWindow])
931  [_platformWindow orderOut:self];
932 
933  [_platformWindow order:CPWindowOut window:self relativeTo:nil];
934 #endif
935 
936  [self _updateMainAndKeyWindows];
937 }
938 
944 - (void)orderWindow:(CPWindowOrderingMode)orderingMode relativeTo:(int)otherWindowNumber
945 {
946  if (orderingMode === CPWindowOut)
947  {
948  // Directly ordering out will detach a child window
949  [_parentWindow removeChildWindow:self];
950 
951  // In Cocoa, a window orders out its child windows only if it has no parent
952  [self _orderOutRecursively:!_parentWindow];
953  }
954  else if (orderingMode === CPWindowAbove && otherWindowNumber === 0)
955  [self _orderFront];
956  else if (orderingMode === CPWindowBelow && otherWindowNumber === 0)
957  [self _orderBack];
958 #if PLATFORM(DOM)
959  else
960  [_platformWindow order:orderingMode window:self relativeTo:CPApp._windows[otherWindowNumber]];
961 #endif
962 }
963 
968 - (void)setLevel:(int)aLevel
969 {
970  if (aLevel === _level)
971  return;
972 
973  [_platformWindow moveWindow:self fromLevel:_level toLevel:aLevel];
974 
975  _level = aLevel;
976  [_childWindows makeObjectsPerformSelector:@selector(setLevel:) withObject:_level];
977 
978  if ([self _sharesChromeWithPlatformWindow])
979  [_platformWindow setLevel:aLevel];
980 }
981 
985 - (int)level
986 {
987  return _level;
988 }
989 
993 - (BOOL)isVisible
994 {
995  return _isVisible;
996 }
997 
1003 + (void)setGlobalResizeStyle:(int)aStyle
1004 {
1005  if (CPWindowResizeStyle === aStyle)
1006  return;
1007 
1008  CPWindowResizeStyle = aStyle;
1009  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowResizeStyleGlobalChangeNotification object:nil];
1010 }
1011 
1016 + (void)setConstrainWindowsToUsableScreen:(BOOL)shouldConstrain
1017 {
1018  CPWindowConstrainToScreen = shouldConstrain;
1019 }
1020 
1024 + (BOOL)constrainWindowsToUsableScreen
1025 {
1027 }
1028 
1029 - (void)_didReceiveResizeStyleChange:(CPNotification)aNotification
1030 {
1031  [_windowView setShowsResizeIndicator:_styleMask & CPResizableWindowMask];
1032 }
1033 
1037 + (int)globalResizeStyle
1038 {
1039  return CPWindowResizeStyle;
1040 }
1041 
1045 - (BOOL)showsResizeIndicator
1046 {
1047  return _showsResizeIndicator;
1048 }
1049 
1054 - (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
1055 {
1056  shouldShowResizeIndicator = !!shouldShowResizeIndicator;
1057 
1058  if (_showsResizeIndicator === shouldShowResizeIndicator)
1059  return;
1060 
1061  _showsResizeIndicator = shouldShowResizeIndicator;
1062  [_windowView setShowsResizeIndicator:[self showsResizeIndicator]];
1063 }
1064 
1068 - (CGSize)resizeIndicatorOffset
1069 {
1070  return [_windowView resizeIndicatorOffset];
1071 }
1072 
1077 - (void)setResizeIndicatorOffset:(CGSize)anOffset
1078 {
1079  [_windowView setResizeIndicatorOffset:anOffset];
1080 }
1081 
1087 - (void)setContentView:(CPView)aView
1088 {
1089  if (_contentView)
1090  [_contentView removeFromSuperview];
1091 
1092  var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
1093 
1094  _contentView = aView;
1095  [_contentView setFrame:[self contentRectForFrameRect:bounds]];
1096 
1097  [_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1098  [_windowView addSubview:_contentView];
1099 
1100  /*
1101  If the initial first responder has been set to something other than
1102  the window, set it to the window because it will no longer be valid.
1103  */
1104  if (_initialFirstResponder && _initialFirstResponder !== self)
1105  _initialFirstResponder = self;
1106 }
1107 
1111 - (CPView)contentView
1112 {
1113  return _contentView;
1114 }
1115 
1120 - (void)setAlphaValue:(float)aValue
1121 {
1122  [_windowView setAlphaValue:aValue];
1123 }
1124 
1128 - (float)alphaValue
1129 {
1130  return [_windowView alphaValue];
1131 }
1132 
1137 - (void)setBackgroundColor:(CPColor)aColor
1138 {
1139  [_windowView setBackgroundColor:aColor];
1140 }
1141 
1145 - (CPColor)backgroundColor
1146 {
1147  return [_windowView backgroundColor];
1148 }
1149 
1157 - (void)setMinSize:(CGSize)aSize
1158 {
1159  if (CGSizeEqualToSize(_minSize, aSize))
1160  return;
1161 
1162  _minSize = [self _calculateMinSizeForProposedSize:aSize];
1163 
1164  var size = CGSizeMakeCopy([self frame].size),
1165  needsFrameChange = NO;
1166 
1167  if (size.width < _minSize.width)
1168  {
1169  size.width = _minSize.width;
1170  needsFrameChange = YES;
1171  }
1172 
1173  if (size.height < _minSize.height)
1174  {
1175  size.height = _minSize.height;
1176  needsFrameChange = YES;
1177  }
1178 
1179  if (needsFrameChange)
1180  [self setFrameSize:size];
1181 }
1182 
1186 - (CGSize)minSize
1187 {
1188  return _minSize;
1189 }
1190 
1192 - (CGSize)_calculateMinSizeForProposedSize:(CGSize)proposedSize
1193 {
1194  var contentFrame = [self contentRectForFrameRect:_frame],
1195  minHeight = CGRectGetHeight(_frame) - CGRectGetHeight(contentFrame);
1196 
1197  return CGSizeMake(MAX(proposedSize.width, 0), MAX(proposedSize.height, minHeight));
1198 }
1199 
1206 - (void)setMaxSize:(CGSize)aSize
1207 {
1208  if (CGSizeEqualToSize(_maxSize, aSize))
1209  return;
1210 
1211  _maxSize = CGSizeMakeCopy(aSize);
1212 
1213  var size = CGSizeMakeCopy([self frame].size),
1214  needsFrameChange = NO;
1215 
1216  if (size.width > _maxSize.width)
1217  {
1218  size.width = _maxSize.width;
1219  needsFrameChange = YES;
1220  }
1221 
1222  if (size.height > _maxSize.height)
1223  {
1224  size.height = _maxSize.height;
1225  needsFrameChange = YES;
1226  }
1227 
1228  if (needsFrameChange)
1229  [self setFrameSize:size];
1230 }
1231 
1235 - (CGSize)maxSize
1236 {
1237  return _maxSize;
1238 }
1239 
1243 - (BOOL)hasShadow
1244 {
1245  return _hasShadow;
1246 }
1247 
1248 - (void)_updateShadow
1249 {
1250  if ([self _sharesChromeWithPlatformWindow])
1251  {
1252  if (_shadowView)
1253  {
1254 #if PLATFORM(DOM)
1255  CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
1256 #endif
1257  _shadowView = nil;
1258  }
1259 
1260  [_platformWindow setHasShadow:_hasShadow];
1261 
1262  return;
1263  }
1264 
1265  if (_hasShadow && !_shadowView)
1266  {
1267  _shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
1268 
1269  [_shadowView setWindowView:_windowView];
1270  [_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1271  [_shadowView setNeedsLayout];
1272 
1273 #if PLATFORM(DOM)
1274  CPDOMDisplayServerInsertBefore(_DOMElement, _shadowView._DOMElement, _windowView._DOMElement);
1275 #endif
1276  }
1277  else if (!_hasShadow && _shadowView)
1278  {
1279 #if PLATFORM(DOM)
1280  CPDOMDisplayServerRemoveChild(_DOMElement, _shadowView._DOMElement);
1281 #endif
1282  _shadowView = nil;
1283  }
1284 }
1285 
1290 - (void)setHasShadow:(BOOL)shouldHaveShadow
1291 {
1292  if (_hasShadow === shouldHaveShadow)
1293  return;
1294 
1295  _hasShadow = shouldHaveShadow;
1296 
1297  [self _updateShadow];
1298 }
1299 
1311 - (void)setShadowStyle:(unsigned)aStyle
1312 {
1313  _shadowStyle = aStyle;
1314 
1315  [[self platformWindow] setShadowStyle:_shadowStyle];
1316 }
1317 
1322 - (void)setDelegate:(id)aDelegate
1323 {
1324  var defaultCenter = [CPNotificationCenter defaultCenter];
1325 
1326  [defaultCenter removeObserver:_delegate name:CPWindowDidResignKeyNotification object:self];
1327  [defaultCenter removeObserver:_delegate name:CPWindowDidBecomeKeyNotification object:self];
1328  [defaultCenter removeObserver:_delegate name:CPWindowDidBecomeMainNotification object:self];
1329  [defaultCenter removeObserver:_delegate name:CPWindowDidResignMainNotification object:self];
1330  [defaultCenter removeObserver:_delegate name:CPWindowDidMoveNotification object:self];
1331  [defaultCenter removeObserver:_delegate name:CPWindowDidResizeNotification object:self];
1332  [defaultCenter removeObserver:_delegate name:CPWindowWillBeginSheetNotification object:self];
1333  [defaultCenter removeObserver:_delegate name:CPWindowDidEndSheetNotification object:self];
1334 
1335  _delegate = aDelegate;
1336  _delegateRespondsToWindowWillReturnUndoManagerSelector = [_delegate respondsToSelector:@selector(windowWillReturnUndoManager:)];
1337 
1338  if ([_delegate respondsToSelector:@selector(windowDidResignKey:)])
1339  [defaultCenter
1340  addObserver:_delegate
1341  selector:@selector(windowDidResignKey:)
1342  name:CPWindowDidResignKeyNotification
1343  object:self];
1344 
1345  if ([_delegate respondsToSelector:@selector(windowDidBecomeKey:)])
1346  [defaultCenter
1347  addObserver:_delegate
1348  selector:@selector(windowDidBecomeKey:)
1349  name:CPWindowDidBecomeKeyNotification
1350  object:self];
1351 
1352  if ([_delegate respondsToSelector:@selector(windowDidBecomeMain:)])
1353  [defaultCenter
1354  addObserver:_delegate
1355  selector:@selector(windowDidBecomeMain:)
1356  name:CPWindowDidBecomeMainNotification
1357  object:self];
1358 
1359  if ([_delegate respondsToSelector:@selector(windowDidResignMain:)])
1360  [defaultCenter
1361  addObserver:_delegate
1362  selector:@selector(windowDidResignMain:)
1363  name:CPWindowDidResignMainNotification
1364  object:self];
1365 
1366  if ([_delegate respondsToSelector:@selector(windowDidMove:)])
1367  [defaultCenter
1368  addObserver:_delegate
1369  selector:@selector(windowDidMove:)
1370  name:CPWindowDidMoveNotification
1371  object:self];
1372 
1373  if ([_delegate respondsToSelector:@selector(windowDidResize:)])
1374  [defaultCenter
1375  addObserver:_delegate
1376  selector:@selector(windowDidResize:)
1377  name:CPWindowDidResizeNotification
1378  object:self];
1379 
1380  if ([_delegate respondsToSelector:@selector(windowWillBeginSheet:)])
1381  [defaultCenter
1382  addObserver:_delegate
1383  selector:@selector(windowWillBeginSheet:)
1384  name:CPWindowWillBeginSheetNotification
1385  object:self];
1386 
1387  if ([_delegate respondsToSelector:@selector(windowDidEndSheet:)])
1388  [defaultCenter
1389  addObserver:_delegate
1390  selector:@selector(windowDidEndSheet:)
1391  name:CPWindowDidEndSheetNotification
1392  object:self];
1393 }
1394 
1398 - (id)delegate
1399 {
1400  return _delegate;
1401 }
1402 
1407 - (void)setWindowController:(CPWindowController)aWindowController
1408 {
1409  _windowController = aWindowController;
1410 }
1411 
1415 - (CPWindowController)windowController
1416 {
1417  return _windowController;
1418 }
1419 
1420 - (void)doCommandBySelector:(SEL)aSelector
1421 {
1422  if ([_delegate respondsToSelector:aSelector])
1423  [_delegate performSelector:aSelector];
1424  else
1425  [super doCommandBySelector:aSelector];
1426 }
1427 
1428 - (BOOL)acceptsFirstResponder
1429 {
1430  return NO;
1431 }
1432 
1433 - (CPView)initialFirstResponder
1434 {
1435  return _initialFirstResponder;
1436 }
1437 
1438 - (void)setInitialFirstResponder:(CPView)aView
1439 {
1440  _initialFirstResponder = aView;
1441 }
1442 
1443 - (void)_setupFirstResponder
1444 {
1445  /*
1446  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.
1447  */
1448  if (!_hasBecomeKeyWindow)
1449  {
1450  if (_firstResponder === self)
1451  {
1452  if (_initialFirstResponder)
1453  [self makeFirstResponder:_initialFirstResponder];
1454  else
1455  {
1456  // Make the first valid key view the first responder
1457  var view = [_contentView nextValidKeyView];
1458 
1459  if (view)
1460  [self makeFirstResponder:view];
1461  }
1462 
1463  return;
1464  }
1465  }
1466 
1467  if (_firstResponder)
1468  [self makeFirstResponder:_firstResponder];
1469 }
1470 
1478 - (BOOL)makeFirstResponder:(CPResponder)aResponder
1479 {
1480  if (_firstResponder === aResponder)
1481  return YES;
1482 
1483  if (![_firstResponder resignFirstResponder])
1484  return NO;
1485 
1486  if (!aResponder || ![aResponder acceptsFirstResponder] || ![aResponder becomeFirstResponder])
1487  {
1488  _firstResponder = self;
1489 
1490  return NO;
1491  }
1492 
1493  _firstResponder = aResponder;
1494 
1495  [[CPNotificationCenter defaultCenter] postNotificationName:_CPWindowDidChangeFirstResponderNotification object:self];
1496 
1497  return YES;
1498 }
1499 
1503 - (CPResponder)firstResponder
1504 {
1505  return _firstResponder;
1506 }
1507 
1508 - (BOOL)acceptsMouseMovedEvents
1509 {
1510  return _acceptsMouseMovedEvents;
1511 }
1512 
1513 - (void)setAcceptsMouseMovedEvents:(BOOL)shouldAcceptMouseMovedEvents
1514 {
1515  _acceptsMouseMovedEvents = shouldAcceptMouseMovedEvents;
1516 }
1517 
1518 - (BOOL)ignoresMouseEvents
1519 {
1520  return _ignoresMouseEvents;
1521 }
1522 
1523 - (void)setIgnoresMouseEvents:(BOOL)shouldIgnoreMouseEvents
1524 {
1525  _ignoresMouseEvents = shouldIgnoreMouseEvents;
1526 }
1527 
1528 - (void)_mouseExitedResizeRect
1529 {
1530  [[CPCursor arrowCursor] set];
1531 }
1532 
1533 // Managing Titles
1534 
1538 - (CPString)title
1539 {
1540  return _title;
1541 }
1542 
1546 - (void)setTitle:(CPString)aTitle
1547 {
1548  _title = aTitle;
1549 
1550  [_windowView setTitle:aTitle];
1551  [_platformWindow _setTitle:_title window:self];
1552 }
1553 
1557 - (void)setTitleWithRepresentedFilename:(CPString)aFilePath
1558 {
1559  [self setRepresentedFilename:aFilePath];
1560  [self setTitle:[aFilePath lastPathComponent]];
1561 }
1562 
1566 - (void)setRepresentedFilename:(CPString)aFilePath
1567 {
1568  // FIXME: urls vs filepaths and all.
1569  [self setRepresentedURL:[CPURL URLWithString:aFilePath]];
1570 }
1571 
1575 - (CPString)representedFilename
1576 {
1577  return [_representedURL absoluteString];
1578 }
1579 
1583 - (void)setRepresentedURL:(CPURL)aURL
1584 {
1585  _representedURL = aURL;
1586 }
1587 
1591 - (CPURL)representedURL
1592 {
1593  return _representedURL;
1594 }
1595 
1596 - (CPScreen)screen
1597 {
1598  return [[CPScreen alloc] init];
1599 }
1600 
1601 // Moving
1602 
1607 - (void)setMovableByWindowBackground:(BOOL)shouldBeMovableByWindowBackground
1608 {
1609  _isMovableByWindowBackground = shouldBeMovableByWindowBackground;
1610 }
1611 
1615 - (BOOL)isMovableByWindowBackground
1616 {
1617  return _isMovableByWindowBackground;
1618 }
1619 
1624 - (void)setMovable:(BOOL)shouldBeMovable
1625 {
1626  _isMovable = shouldBeMovable;
1627 }
1628 
1632 - (void)isMovable
1633 {
1634  return _isMovable;
1635 }
1636 
1640 - (void)center
1641 {
1642  if (_isFullPlatformWindow)
1643  return;
1644 
1645  var size = [self frame].size,
1646  containerSize = [CPPlatform isBrowser] ? [_platformWindow contentBounds].size : [[self screen] visibleFrame].size;
1647 
1648  var origin = CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0);
1649 
1650  if (origin.x < 0.0)
1651  origin.x = 0.0;
1652 
1653  if (origin.y < 0.0)
1654  origin.y = 0.0;
1655 
1656  [self setFrameOrigin:origin];
1657 }
1658 
1663 - (void)sendEvent:(CPEvent)anEvent
1664 {
1665  var type = [anEvent type],
1666  sheet = [self attachedSheet];
1667 
1668  // If a sheet is attached events get filtered here.
1669  // It is not clear what events should be passed to the view, perhaps all?
1670  // CPLeftMouseDown is needed for window moving and resizing to work.
1671  // CPMouseMoved is needed for rollover effects on title bar buttons.
1672 
1673  if (sheet)
1674  {
1675  switch (type)
1676  {
1677  case CPLeftMouseDown:
1678 
1679  // This is needed when a doubleClick occurs when the sheet is closing or opening
1680  if (!_parentWindow)
1681  return;
1682 
1683  [_windowView mouseDown:anEvent];
1684 
1685  // -dw- if the window is clicked, the sheet should come to front, and become key,
1686  // and the window should be immediately behind
1687  [sheet makeKeyAndOrderFront:self];
1688 
1689  return;
1690 
1691  case CPMouseMoved:
1692  // Allow these through to the parent
1693  break;
1694 
1695  default:
1696  // Everything else is filtered
1697  return;
1698  }
1699  }
1700 
1701  var point = [anEvent locationInWindow];
1702 
1703  switch (type)
1704  {
1705  case CPFlagsChanged:
1706  return [[self firstResponder] flagsChanged:anEvent];
1707 
1708  case CPKeyUp:
1709  return [[self firstResponder] keyUp:anEvent];
1710 
1711  case CPKeyDown:
1712  if ([anEvent charactersIgnoringModifiers] === CPTabCharacter)
1713  {
1714  if ([anEvent modifierFlags] & CPShiftKeyMask)
1715  [self selectPreviousKeyView:self];
1716  else
1717  [self selectNextKeyView:self];
1718 #if PLATFORM(DOM)
1719  // Make sure the browser doesn't try to do its own tab handling.
1720  // This is important or the browser might blur the shared text field or token field input field,
1721  // even that we just moved it to a new first responder.
1722  [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
1723 #endif
1724  return;
1725  }
1726  else if ([anEvent charactersIgnoringModifiers] === CPBackTabCharacter)
1727  {
1728  var didTabBack = [self selectPreviousKeyView:self];
1729 
1730  if (didTabBack)
1731  {
1732 #if PLATFORM(DOM)
1733  // Make sure the browser doesn't try to do its own tab handling.
1734  // This is important or the browser might blur the shared text field or token field input field,
1735  // even that we just moved it to a new first responder.
1736  [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO]
1737 #endif
1738  }
1739 
1740  return didTabBack;
1741  }
1742 
1743  [[self firstResponder] keyDown:anEvent];
1744 
1745  // Trigger the default button if needed
1746  // FIXME: Is this only applicable in a sheet? See isse: #722.
1747  if (![self disableKeyEquivalentForDefaultButton])
1748  {
1749  var defaultButton = [self defaultButton],
1750  keyEquivalent = [defaultButton keyEquivalent],
1751  modifierMask = [defaultButton keyEquivalentModifierMask];
1752 
1753  if ([anEvent _triggersKeyEquivalent:keyEquivalent withModifierMask:modifierMask])
1754  [[self defaultButton] performClick:self];
1755  }
1756 
1757  return;
1758 
1759  case CPScrollWheel:
1760  return [[_windowView hitTest:point] scrollWheel:anEvent];
1761 
1762  case CPLeftMouseUp:
1763  case CPRightMouseUp:
1764  var hitTestedView = _leftMouseDownView,
1765  selector = type == CPRightMouseUp ? @selector(rightMouseUp:) : @selector(mouseUp:);
1766 
1767  if (!hitTestedView)
1768  hitTestedView = [_windowView hitTest:point];
1769 
1770  [hitTestedView performSelector:selector withObject:anEvent];
1771 
1772  _leftMouseDownView = nil;
1773 
1774  return;
1775 
1776  case CPLeftMouseDown:
1777  case CPRightMouseDown:
1778  // This will return _windowView if it is within a resize region
1779  _leftMouseDownView = [_windowView hitTest:point];
1780 
1781  if (_leftMouseDownView !== _firstResponder && [_leftMouseDownView acceptsFirstResponder])
1782  [self makeFirstResponder:_leftMouseDownView];
1783 
1784  [CPApp activateIgnoringOtherApps:YES];
1785 
1786  var theWindow = [anEvent window],
1787  selector = type == CPRightMouseDown ? @selector(rightMouseDown:) : @selector(mouseDown:);
1788 
1789  if ([theWindow isKeyWindow] || ([theWindow becomesKeyOnlyIfNeeded] && ![_leftMouseDownView needsPanelToBecomeKey]))
1790  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1791  else
1792  {
1793  // FIXME: delayed ordering?
1794  [self makeKeyAndOrderFront:self];
1795 
1796  if ([_leftMouseDownView acceptsFirstMouse:anEvent])
1797  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1798  }
1799  break;
1800 
1801  case CPLeftMouseDragged:
1802  case CPRightMouseDragged:
1803  if (!_leftMouseDownView)
1804  return [[_windowView hitTest:point] mouseDragged:anEvent];
1805 
1806  var selector;
1807 
1808  if (type == CPRightMouseDragged)
1809  {
1810  selector = @selector(rightMouseDragged:)
1811  if (![_leftMouseDownView respondsToSelector:selector])
1812  selector = nil;
1813  }
1814 
1815  if (!selector)
1816  selector = @selector(mouseDragged:)
1817 
1818  return [_leftMouseDownView performSelector:selector withObject:anEvent];
1819 
1820  case CPMouseMoved:
1821  [_windowView setCursorForLocation:point resizing:NO];
1822 
1823  // Ignore mouse moves for parents of sheets
1824  if (!_acceptsMouseMovedEvents || sheet)
1825  return;
1826 
1827  if (!_mouseEnteredStack)
1828  _mouseEnteredStack = [];
1829 
1830  var hitTestView = [_windowView hitTest:point];
1831 
1832  if ([_mouseEnteredStack count] && [_mouseEnteredStack lastObject] === hitTestView)
1833  return [hitTestView mouseMoved:anEvent];
1834 
1835  var view = hitTestView,
1836  mouseEnteredStack = [];
1837 
1838  while (view)
1839  {
1840  mouseEnteredStack.unshift(view);
1841 
1842  view = [view superview];
1843  }
1844 
1845  var deviation = MIN(_mouseEnteredStack.length, mouseEnteredStack.length);
1846 
1847  while (deviation--)
1848  if (_mouseEnteredStack[deviation] === mouseEnteredStack[deviation])
1849  break;
1850 
1851  var index = deviation + 1,
1852  count = _mouseEnteredStack.length;
1853 
1854  if (index < count)
1855  {
1856  var event = [CPEvent mouseEventWithType:CPMouseExited location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
1857 
1858  for (; index < count; ++index)
1859  [_mouseEnteredStack[index] mouseExited:event];
1860  }
1861 
1862  index = deviation + 1;
1863  count = mouseEnteredStack.length;
1864 
1865  if (index < count)
1866  {
1867  var event = [CPEvent mouseEventWithType:CPMouseEntered location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
1868 
1869  for (; index < count; ++index)
1870  [mouseEnteredStack[index] mouseEntered:event];
1871  }
1872 
1873  _mouseEnteredStack = mouseEnteredStack;
1874 
1875  [hitTestView mouseMoved:anEvent];
1876  }
1877 }
1878 
1882 - (int)windowNumber
1883 {
1884  return _windowNumber;
1885 }
1886 
1892 - (void)becomeKeyWindow
1893 {
1894  CPApp._keyWindow = self;
1895 
1896  if (_firstResponder !== self && [_firstResponder respondsToSelector:@selector(becomeKeyWindow)])
1897  [_firstResponder becomeKeyWindow];
1898 
1899  if (!_hasBecomeKeyWindow)
1900  {
1901  // The first time a window is loaded, if it does not have a key view loop
1902  // established, calculate it now.
1903  if (![self _hasKeyViewLoop:[_contentView subviews]])
1904  [self recalculateKeyViewLoop];
1905  }
1906 
1907  [self _setupFirstResponder];
1908  _hasBecomeKeyWindow = YES;
1909 
1910  [_windowView noteKeyWindowStateChanged];
1911 
1913  postNotificationName:CPWindowDidBecomeKeyNotification
1914  object:self];
1915 }
1916 
1921 - (BOOL)canBecomeKeyWindow
1922 {
1923  /*
1924  In Cocoa only titled windows return YES here by default. But the main browser
1925  window in Cappuccino doesn't have a title bar even that it's both titled and
1926  resizable, so we return YES when isFullPlatformWindow too.
1927 
1928  Note that Cocoa will return NO for a non-titled, resizable window. The Cocoa documention
1929  says it will return YES if there is a "resize bar", but in practice
1930  that is not the same as the resizable mask.
1931  */
1932  return (_styleMask & CPTitledWindowMask) || [self isFullPlatformWindow] || _isSheet;
1933 }
1934 
1938 - (BOOL)isKeyWindow
1939 {
1940  return [CPApp keyWindow] == self;
1941 }
1942 
1947 - (void)makeKeyAndOrderFront:(id)aSender
1948 {
1949  [self orderFront:self];
1950 
1951  [self makeKeyWindow];
1952  [self makeMainWindow];
1953 }
1954 
1958 - (void)makeKeyWindow
1959 {
1960  if ([CPApp keyWindow] === self || ![self canBecomeKeyWindow])
1961  return;
1962 
1963  [[CPApp keyWindow] resignKeyWindow];
1964  [self becomeKeyWindow];
1965 }
1966 
1970 - (void)resignKeyWindow
1971 {
1972  if (_firstResponder !== self && [_firstResponder respondsToSelector:@selector(resignKeyWindow)])
1973  [_firstResponder resignKeyWindow];
1974 
1975  if (CPApp._keyWindow === self)
1976  CPApp._keyWindow = nil;
1977 
1978  [_windowView noteKeyWindowStateChanged];
1979 
1980  [[CPNotificationCenter defaultCenter]
1981  postNotificationName:CPWindowDidResignKeyNotification
1982  object:self];
1983 }
1984 
1995 - (void)dragImage:(CPImage)anImage at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
1996 {
1997  [[CPDragServer sharedDragServer] dragImage:anImage fromWindow:self at:[self convertBaseToGlobal:imageLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
1998 }
1999 
2000 - (void)_noteRegisteredDraggedTypes:(CPSet)pasteboardTypes
2001 {
2002  if (!pasteboardTypes)
2003  return;
2004 
2005  if (!_inclusiveRegisteredDraggedTypes)
2006  _inclusiveRegisteredDraggedTypes = [CPCountedSet set];
2007 
2008  [_inclusiveRegisteredDraggedTypes unionSet:pasteboardTypes];
2009 }
2010 
2011 - (void)_noteUnregisteredDraggedTypes:(CPSet)pasteboardTypes
2012 {
2013  if (!pasteboardTypes)
2014  return;
2015 
2016  [_inclusiveRegisteredDraggedTypes minusSet:pasteboardTypes];
2017 
2018  if ([_inclusiveRegisteredDraggedTypes count] === 0)
2019  _inclusiveRegisteredDraggedTypes = nil;
2020 }
2021 
2032 - (void)dragView:(CPView)aView at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2033 {
2034  [[CPDragServer sharedDragServer] dragView:aView fromWindow:self at:[self convertBaseToGlobal:viewLocation] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2035 }
2036 
2041 - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
2042 {
2043  if (!pasteboardTypes)
2044  return;
2045 
2046  [self _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2047  [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
2048  [self _noteRegisteredDraggedTypes:_registeredDraggedTypes];
2049 
2050  _registeredDraggedTypesArray = nil;
2051 }
2052 
2057 - (CPArray)registeredDraggedTypes
2058 {
2059  if (!_registeredDraggedTypesArray)
2060  _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
2061 
2062  return _registeredDraggedTypesArray;
2063 }
2064 
2068 - (void)unregisterDraggedTypes
2069 {
2070  [self _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2071 
2072  _registeredDraggedTypes = [CPSet set];
2073  _registeredDraggedTypesArray = [];
2074 }
2075 
2076 // Accessing Editing Status
2077 
2082 - (void)setDocumentEdited:(BOOL)isDocumentEdited
2083 {
2084  if (_isDocumentEdited == isDocumentEdited)
2085  return;
2086 
2087  _isDocumentEdited = isDocumentEdited;
2088 
2089  [CPMenu _setMenuBarIconImageAlphaValue:_isDocumentEdited ? 0.5 : 1.0];
2090 
2091  [_windowView setDocumentEdited:isDocumentEdited];
2092 }
2093 
2097 - (BOOL)isDocumentEdited
2098 {
2099  return _isDocumentEdited;
2100 }
2101 
2102 - (void)setDocumentSaving:(BOOL)isDocumentSaving
2103 {
2104  if (_isDocumentSaving == isDocumentSaving)
2105  return;
2106 
2107  _isDocumentSaving = isDocumentSaving;
2108 
2109  [self _synchronizeSaveMenuWithDocumentSaving];
2110 
2111  [_windowView windowDidChangeDocumentSaving];
2112 }
2113 
2114 - (BOOL)isDocumentSaving
2115 {
2116  return _isDocumentSaving;
2117 }
2118 
2119 /* @ignore */
2120 - (void)_synchronizeSaveMenuWithDocumentSaving
2121 {
2122  if (![self isMainWindow])
2123  return;
2124 
2125  var mainMenu = [CPApp mainMenu],
2126  index = [mainMenu indexOfItemWithTitle:_isDocumentSaving ? @"Save" : @"Saving..."];
2127 
2128  if (index == CPNotFound)
2129  return;
2130 
2131  var item = [mainMenu itemAtIndex:index];
2132 
2133  if (_isDocumentSaving)
2134  {
2135  CPWindowSaveImage = [item image];
2136 
2137  [item setTitle:@"Saving..."];
2138  [item setImage:[[CPTheme defaultTheme] valueForAttributeWithName:@"spinning-regular-gif" forClass:CPProgressIndicator]];
2139  [item setEnabled:NO];
2140  }
2141  else
2142  {
2143  [item setTitle:@"Save"];
2144  [item setImage:CPWindowSaveImage];
2145  [item setEnabled:YES];
2146  }
2147 }
2148 
2149 // Minimizing Windows
2150 
2155 - (void)performMiniaturize:(id)aSender
2156 {
2157  //FIXME show stuff
2158  [self miniaturize:aSender];
2159 }
2160 
2165 - (void)miniaturize:(id)sender
2166 {
2167  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillMiniaturizeNotification object:self];
2168 
2169  [[self platformWindow] miniaturize:sender];
2170 
2171  [self _updateMainAndKeyWindows];
2172 
2173  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidMiniaturizeNotification object:self];
2174 
2175  _isMiniaturized = YES;
2176 }
2177 
2181 - (void)deminiaturize:(id)sender
2182 {
2183  [[self platformWindow] deminiaturize:sender];
2184 
2185  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidDeminiaturizeNotification object:self];
2186 
2187  _isMiniaturized = NO;
2188 }
2189 
2193 - (void)isMiniaturized
2194 {
2195  return _isMiniaturized;
2196 }
2197 
2198 // Closing Windows
2199 
2204 - (void)performClose:(id)aSender
2205 {
2206  if (!(_styleMask & CPClosableWindowMask))
2207  return;
2208 
2209  if ([self isFullPlatformWindow])
2210  {
2211  var event = [CPApp currentEvent];
2212 
2213  if ([event type] === CPKeyDown && [event characters] === "w" && ([event modifierFlags] & CPPlatformActionKeyMask))
2214  {
2215  [[self platformWindow] _propagateCurrentDOMEvent:YES];
2216  return;
2217  }
2218  }
2219 
2220  // The Cocoa docs say that if both the delegate and the window implement
2221  // windowShouldClose:, only the delegate receives the message.
2222  if ([_delegate respondsToSelector:@selector(windowShouldClose:)])
2223  {
2224  if (![_delegate windowShouldClose:self])
2225  return;
2226  }
2227  else if ([self respondsToSelector:@selector(windowShouldClose:)])
2228  {
2229  if (![self windowShouldClose:self])
2230  return;
2231  }
2232 
2233  var documents = [_windowController documents];
2234 
2235  if ([documents count])
2236  {
2237  var index = [documents indexOfObject:[_windowController document]];
2238 
2239  [documents[index] shouldCloseWindowController:_windowController
2240  delegate:self
2241  shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:)
2242  contextInfo:{documents:[documents copy], visited:0, index:index}];
2243  }
2244  else
2245  [self close];
2246 }
2247 
2248 - (void)_windowControllerContainingDocument:(CPDocument)document shouldClose:(BOOL)shouldClose contextInfo:(Object)context
2249 {
2250  if (shouldClose)
2251  {
2252  var windowController = [self windowController],
2253  documents = context.documents,
2254  count = [documents count],
2255  visited = ++context.visited,
2256  index = ++context.index % count;
2257 
2258  [document removeWindowController:windowController];
2259 
2260  if (visited < count)
2261  {
2262  [windowController setDocument:documents[index]];
2263 
2264  [documents[index] shouldCloseWindowController:_windowController
2265  delegate:self
2266  shouldCloseSelector:@selector(_windowControllerContainingDocument:shouldClose:contextInfo:)
2267  contextInfo:context];
2268  }
2269  else
2270  [self close];
2271  }
2272 }
2273 
2278 - (void)close
2279 {
2280  if ([_delegate respondsToSelector:@selector(windowWillClose:)])
2281  [_delegate windowWillClose:self];
2282 
2283  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self];
2284 
2285  [_parentWindow removeChildWindow:self];
2286  [self _orderOutRecursively:NO];
2287  [self _detachFromChildrenClosing:!_parentWindow];
2288 }
2289 
2290 - (void)_detachFromChildrenClosing:(BOOL)shouldCloseChildren
2291 {
2292  // When a window is closed, it must detach itself from all children
2293  [_childWindows enumerateObjectsUsingBlock:function(child)
2294  {
2295  [child setParentWindow:nil];
2296  }
2297  ];
2298 
2299  if (shouldCloseChildren)
2300  {
2301  // Cocoa does NOT call close or orderOut when closing child windows,// they are summarily closed.[_childWindows enumerateObjectsUsingBlock:function(child)
2302  {
2303 
2304 
2305  [child _orderOutRecursively:NO];
2306  [child _detachFromChildrenClosing:![child parentWindow]];
2307  }
2308  ];
2309  }
2310 
2311  _childWindows = [];
2312 }
2313 
2314 // Managing Main Status
2318 - (BOOL)isMainWindow
2319 {
2320  return [CPApp mainWindow] === self;
2321 }
2322 
2326 - (BOOL)canBecomeMainWindow
2327 {
2328  // Note that the Cocoa documentation says that this method returns YES if
2329  // the window is visible and has a title bar or a "resize mechanism". It turns
2330  // out a "resize mechanism" is not the same as having the resize mask set.
2331  // In practice a window must have a title bar to become main, but we make
2332  // an exception for a full platform window.
2333  return ([self isVisible] && ((_styleMask & CPTitledWindowMask) || _isFullPlatformWindow));
2334 }
2335 
2339 - (void)makeMainWindow
2340 {
2341  // Sheets cannot be main. Their parent window becomes main.
2342  if (_isSheet)
2343  {
2344  [_parentView makeMainWindow];
2345  return;
2346  }
2347 
2348  if ([CPApp mainWindow] === self || ![self canBecomeMainWindow])
2349  return;
2350 
2351  [[CPApp mainWindow] resignMainWindow];
2352  [self becomeMainWindow];
2353 }
2354 
2358 - (void)becomeMainWindow
2359 {
2360  CPApp._mainWindow = self;
2361 
2362  [self _synchronizeSaveMenuWithDocumentSaving];
2363 
2364  [_windowView noteMainWindowStateChanged];
2365 
2367  postNotificationName:CPWindowDidBecomeMainNotification
2368  object:self];
2369 }
2370 
2374 - (void)resignMainWindow
2375 {
2377  postNotificationName:CPWindowDidResignMainNotification
2378  object:self];
2379 
2380  if (CPApp._mainWindow === self)
2381  CPApp._mainWindow = nil;
2382 
2383  [_windowView noteMainWindowStateChanged];
2384 }
2385 
2386 - (void)_updateMainAndKeyWindows
2387 {
2388  var allWindows = [CPApp orderedWindows],
2389  windowCount = [allWindows count];
2390 
2391  if ([self isKeyWindow])
2392  {
2393  var keyWindow = [CPApp keyWindow];
2394  [self resignKeyWindow];
2395 
2396  if (keyWindow && keyWindow !== self && [keyWindow canBecomeKeyWindow])
2397  [keyWindow makeKeyWindow];
2398  else
2399  {
2400  var mainMenu = [CPApp mainMenu],
2401  menuBarClass = objj_getClass("_CPMenuBarWindow"),
2402  menuWindow;
2403 
2404  for (var i = 0; i < windowCount; i++)
2405  {
2406  var currentWindow = allWindows[i];
2407 
2408  if ([currentWindow isKindOfClass:menuBarClass])
2409  menuWindow = currentWindow;
2410 
2411  if (currentWindow === self || currentWindow === menuWindow)
2412  continue;
2413 
2414  if ([currentWindow isVisible] && [currentWindow canBecomeKeyWindow])
2415  {
2416  [currentWindow makeKeyWindow];
2417  break;
2418  }
2419  }
2420 
2421  if (![CPApp keyWindow])
2422  [menuWindow makeKeyWindow];
2423  }
2424  }
2425 
2426  if ([self isMainWindow])
2427  {
2428  var mainWindow = [CPApp mainWindow];
2429  [self resignMainWindow];
2430 
2431  if (mainWindow && mainWindow !== self && [mainWindow canBecomeMainWindow])
2432  [mainWindow makeMainWindow];
2433  else
2434  {
2435  var mainMenu = [CPApp mainMenu],
2436  menuBarClass = objj_getClass("_CPMenuBarWindow"),
2437  menuWindow;
2438 
2439  for (var i = 0; i < windowCount; i++)
2440  {
2441  var currentWindow = allWindows[i];
2442 
2443  if ([currentWindow isKindOfClass:menuBarClass])
2444  menuWindow = currentWindow;
2445 
2446  if (currentWindow === self || currentWindow === menuWindow)
2447  continue;
2448 
2449  if ([currentWindow isVisible] && [currentWindow canBecomeMainWindow])
2450  {
2451  [currentWindow makeMainWindow];
2452  break;
2453  }
2454  }
2455  }
2456  }
2457 }
2458 
2459 // Managing Toolbars
2463 - (CPToolbar)toolbar
2464 {
2465  return _toolbar;
2466 }
2467 
2472 - (void)setToolbar:(CPToolbar)aToolbar
2473 {
2474  if (_toolbar === aToolbar)
2475  return;
2476 
2477  // If this has an owner, dump it!
2478  [[aToolbar _window] setToolbar:nil];
2479 
2480  // This is no longer our toolbar.
2481  [_toolbar _setWindow:nil];
2482 
2483  _toolbar = aToolbar;
2484 
2485  // THIS is our toolbar.
2486  [_toolbar _setWindow:self];
2487 
2488  [self _noteToolbarChanged];
2489 }
2490 
2491 - (void)toggleToolbarShown:(id)aSender
2492 {
2493  var toolbar = [self toolbar];
2494 
2495  [toolbar setVisible:![toolbar isVisible]];
2496 }
2497 
2498 - (void)_noteToolbarChanged
2499 {
2500  var frame = CGRectMakeCopy([self frame]),
2501  newFrame;
2502 
2503  [_windowView noteToolbarChanged];
2504 
2505  if (_isFullPlatformWindow)
2506  newFrame = [_platformWindow visibleFrame];
2507  else
2508  {
2509  newFrame = CGRectMakeCopy([self frame]);
2510 
2511  newFrame.origin = frame.origin;
2512  }
2513 
2514  [self setFrame:newFrame];
2515  /*
2516  [_windowView setAnimatingToolbar:YES];
2517  [self setFrame:frame];
2518  [self setFrame:newFrame display:YES animate:YES];
2519  [_windowView setAnimatingToolbar:NO];
2520  */
2521 }
2522 
2526 - (CPArray)childWindows
2527 {
2528  return _childWindows;
2529 }
2530 
2531 - (void)addChildWindow:(CPWindow)childWindow ordered:(CPWindowOrderingMode)orderingMode
2532 {
2533  // Don't add the child if it is already in our list
2534  if ([_childWindows indexOfObject:childWindow] >= 0)
2535  return;
2536 
2537  if (orderingMode === CPWindowAbove || orderingMode === CPWindowBelow)
2538  [_childWindows addObject:childWindow];
2539  else
2540  [CPException raise:CPInvalidArgumentException
2541  reason:_cmd + @" unrecognized ordering mode " + orderingMode];
2542 
2543  [childWindow setParentWindow:self];
2544  [childWindow _setChildOrdering:orderingMode];
2545  [childWindow setLevel:[self level]];
2546 
2547  if ([self isVisible] && ![childWindow isVisible])
2548  [childWindow orderWindow:orderingMode relativeTo:_windowNumber];
2549 }
2550 
2551 - (void)removeChildWindow:(CPWindow)childWindow
2552 {
2553  var index = [_childWindows indexOfObject:childWindow];
2554 
2555  if (index === CPNotFound)
2556  return;
2557 
2558  [_childWindows removeObjectAtIndex:index];
2559  [childWindow setParentWindow:nil];
2560 }
2561 
2562 - (CPWindow)parentWindow
2563 {
2564  return _parentWindow;
2565 }
2566 
2572 - (BOOL)_hasAncestorWindow:(CPWindow)anAncestor
2573 {
2574  if (!_parentWindow || !anAncestor)
2575  return NO;
2576 
2577  if (anAncestor === _parentWindow)
2578  return YES;
2579 
2580  return [_parentWindow _hasAncestorWindow:anAncestor];
2581 }
2582 
2583 - (CPWindow)setParentWindow:(CPWindow)parentWindow
2584 {
2585  _parentWindow = parentWindow;
2586 }
2587 
2588 - (void)_setFrame:(CGRect)aFrame delegate:(id)delegate duration:(int)duration curve:(CPAnimationCurve)curve
2589 {
2590  [_frameAnimation stopAnimation];
2591  _frameAnimation = [[_CPWindowFrameAnimation alloc] initWithWindow:self targetFrame:aFrame];
2592  [_frameAnimation setDelegate:delegate];
2593  [_frameAnimation setAnimationCurve:curve];
2594  [_frameAnimation setDuration:duration];
2595  [_frameAnimation startAnimation];
2596 }
2597 
2598 - (CPTimeInterval)animationResizeTime:(CGRect)newWindowFrame
2599 {
2600  return CPWindowResizeTime;
2601 }
2602 
2603 - (void)_setAttachedSheetFrameOrigin
2604 {
2605  // Position the sheet above the contentRect.
2606  var attachedSheet = [self attachedSheet],
2607  contentRect = [_contentView frame],
2608  sheetFrame = CGRectMakeCopy([attachedSheet frame]);
2609 
2610  sheetFrame.origin.y = CGRectGetMinY(_frame) + CGRectGetMinY(contentRect);
2611  sheetFrame.origin.x = CGRectGetMinX(_frame) + FLOOR((CGRectGetWidth(_frame) - CGRectGetWidth(sheetFrame)) / 2.0);
2612 
2613  [attachedSheet setFrame:sheetFrame display:YES animate:NO];
2614 }
2615 
2616 /*
2617  Starting point for sheet session, called from CPApplication beginSheet:
2618 */
2619 - (void)_attachSheet:(CPWindow)aSheet modalDelegate:(id)aModalDelegate
2620  didEndSelector:(SEL)didEndSelector contextInfo:(id)contextInfo
2621 {
2622  if (_sheetContext)
2623  {
2624  [CPException raise:CPInternalInconsistencyException
2625  reason:@"The target window of beginSheet: already has a sheet, did you forget orderOut: ?"];
2626  return;
2627  }
2628 
2629  _sheetContext = {
2630  "sheet": aSheet,
2631  "modalDelegate": aModalDelegate,
2632  "endSelector": didEndSelector,
2633  "contextInfo": contextInfo,
2634  "returnCode": -1,
2635  "opened": NO,
2636  "isAttached": YES,
2637  "savedConstrains": aSheet._constrainsToUsableScreen
2638  };
2639 
2640  // Sheets are not constrained, they are controlled by their parent
2641  aSheet._constrainsToUsableScreen = NO;
2642 
2643  // A timer seems to be necessary for the animation to work correctly
2645  target:self
2646  selector:@selector(_sheetShouldAnimateIn:)
2647  userInfo:nil
2648  repeats:NO];
2649 }
2650 
2651 /*
2652  Called to end the sheet. Note that orderOut: is needed to animate the sheet out, as in Cocoa.
2653  The sheet isn't completely gone until _cleanupSheetWindow gets called.
2654 */
2655 - (void)_endSheet
2656 {
2657  var delegate = _sheetContext["modalDelegate"],
2658  endSelector = _sheetContext["endSelector"];
2659 
2660  // If the sheet has been ordered out, defer didEndSelector until after sheet animates out.
2661  // This must be done since we cannot block and wait for the animation to complete.
2662  if (delegate && endSelector)
2663  {
2664  if (_sheetContext["isAttached"])
2665  objj_msgSend(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
2666  _sheetContext["contextInfo"]);
2667  else
2668  _sheetContext["deferDidEndSelector"] = YES;
2669  }
2670 }
2671 
2672 /*
2673  Called to animate the sheet out. If called while animating in, schedules an animate
2674  out at completion
2675 */
2676 - (void)_detachSheetWindow
2677 {
2678  _sheetContext["isAttached"] = NO;
2679 
2680  // A timer seems to be necessary for the animation to work correctly.
2681  // It would be ideal to block here and spin the event loop, until attach is complete.
2683  target:self
2684  selector:@selector(_sheetShouldAnimateOut:)
2685  userInfo:nil
2686  repeats:NO];
2687 }
2688 
2689 /*
2690  Called to cleanup sheet, when we are definitely done with it
2691 */
2692 - (void)_cleanupSheetWindow
2693 {
2694  var sheet = _sheetContext["sheet"],
2695  deferDidEnd = _sheetContext["deferDidEndSelector"];
2696 
2697  // If the parent window is modal, the sheet started its own modal session
2698  if (sheet._isModal)
2699  [CPApp stopModal];
2700 
2701  [self _removeClipForSheet:sheet];
2702 
2703  // Restore the state of window before it was sheetified
2704  sheet._isSheet = NO;
2705  [sheet._windowView _enableSheet:NO inWindow:self];
2706  sheet._constrainsToUsableScreen = _sheetContext["savedConstrains"];
2707 
2708  // Close it
2709  [sheet orderOut:self];
2710 
2711  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidEndSheetNotification object:self];
2712 
2713  if (deferDidEnd)
2714  {
2715  var delegate = _sheetContext["modalDelegate"],
2716  selector = _sheetContext["endSelector"],
2717  returnCode = _sheetContext["returnCode"],
2718  contextInfo = _sheetContext["contextInfo"];
2719 
2720  // Context must be destroyed, since didEnd might want to attach another sheet
2721  _sheetContext = nil;
2722  sheet._parentView = nil;
2723 
2724  objj_msgSend(delegate, selector, sheet, returnCode, contextInfo);
2725  }
2726  else
2727  {
2728  _sheetContext = nil;
2729  sheet._parentView = nil;
2730  }
2731 }
2732 
2733 /* @ignore */
2734 - (void)animationDidEnd:(id)anim
2735 {
2736  var sheet = _sheetContext["sheet"];
2737 
2738  if (anim._window != sheet)
2739  return;
2740 
2742  target:self
2743  selector:@selector(_sheetAnimationDidEnd:)
2744  userInfo:nil
2745  repeats:NO];
2746 }
2747 
2748 - (void)_sheetShouldAnimateIn:(CPTimer)timer
2749 {
2750  // Can't open sheet while opening or closing animation is going on
2751  if (_sheetContext["isOpening"] || _sheetContext["isClosing"])
2752  return;
2753 
2754  var sheet = _sheetContext["sheet"];
2755  sheet._isSheet = YES;
2756  sheet._parentView = self;
2757 
2758  [[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillBeginSheetNotification object:self];
2759 
2760  // If sheet is attached to a modal window, the sheet runs
2761  // as if itself and the parent window are modal
2762  sheet._isModal = NO;
2763 
2764  if ([CPApp modalWindow] === self)
2765  {
2766  [CPApp runModalForWindow:sheet];
2767  sheet._isModal = YES;
2768  }
2769 
2770  // The sheet starts hidden just above the top of a clip rect
2771  // TODO : Make properly for the -1 in endY
2772  var sheetFrame = [sheet frame],
2773  sheetShadowFrame = sheet._hasShadow ? [sheet._shadowView frame] : sheetFrame,
2774  frame = [self frame],
2775  originX = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
2776  startFrame = CGRectMake(originX, -sheetShadowFrame.size.height, sheetFrame.size.width, sheetFrame.size.height),
2777  endY = -1 + [_windowView bodyOffset] - [[self contentView] frame].origin.y,
2778  endFrame = CGRectMake(originX, endY, sheetFrame.size.width, sheetFrame.size.height);
2779 
2780  if (_toolbar && [_windowView showsToolbar] && [self isFullPlatformWindow])
2781  {
2782  endY += [[_toolbar _toolbarView] frameSize].height;
2783  endFrame = CGRectMake(originX, endY, sheetFrame.size.width, sheetFrame.size.height);
2784  }
2785 
2786  // Move the sheet offscreen before ordering front so it doesn't appear briefly
2787  [sheet setFrameOrigin:CGPointMake(0, -13000)];
2788 
2789  // Because clipping does funny thing with the DOM, we have to orderFront before clipping
2790  [sheet orderFront:self];
2791  [self _clipSheet:sheet];
2792 
2793  [sheet setFrame:startFrame display:YES animate:NO];
2794 
2795  _sheetContext["opened"] = YES;
2796  _sheetContext["shouldClose"] = NO;
2797  _sheetContext["isOpening"] = YES;
2798 
2799  [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseOut];
2800 }
2801 
2802 - (void)_sheetShouldAnimateOut:(CPTimer)timer
2803 {
2804  if (_sheetContext["isOpening"])
2805  {
2806  // Allow sheet to be closed while opening, it will close when animate in completes
2807  _sheetContext["shouldClose"] = YES;
2808  return;
2809  }
2810 
2811  if (_sheetContext["isClosing"])
2812  return;
2813 
2814  _sheetContext["opened"] = NO;
2815  _sheetContext["isClosing"] = YES;
2816 
2817  // The parent window can be orderedOut to disable the sheet animate out, as in Cocoa
2818  if ([self isVisible])
2819  {
2820  var sheet = _sheetContext["sheet"],
2821  sheetFrame = [sheet frame],
2822  fullHeight = sheet._hasShadow ? [sheet._shadowView frame].size.height : sheetFrame.size.height,
2823  endFrame = CGRectMakeCopy(sheetFrame),
2824  contentOrigin = [self convertBaseToGlobal:[[self contentView] frame].origin];
2825 
2826  // Don't constrain sheets, they are controlled by the parent
2827  sheet._constrainsToUsableScreen = NO;
2828 
2829  [sheet setFrameOrigin:CGPointMake(sheetFrame.origin.x, sheetFrame.origin.y - contentOrigin.y)];
2830  [self _clipSheet:sheet];
2831 
2832  endFrame.origin.y = -fullHeight;
2833  [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseIn];
2834  }
2835  else
2836  {
2837  [self _sheetAnimationDidEnd:nil];
2838  }
2839 }
2840 
2841 - (void)_sheetAnimationDidEnd:(CPTimer)timer
2842 {
2843  var sheet = _sheetContext["sheet"];
2844 
2845  _sheetContext["isOpening"] = NO;
2846  _sheetContext["isClosing"] = NO;
2847 
2848  if (_sheetContext["opened"] === YES)
2849  {
2850  var sheetFrame = [sheet frame],
2851  sheetOrigin = CGPointMakeCopy(sheetFrame.origin);
2852 
2853  [self _removeClipForSheet:sheet];
2854  [sheet setFrameOrigin:CGPointMake(sheetOrigin.x, [sheet frame].origin.y + sheetOrigin.y)];
2855 
2856  // we wanted to close the sheet while it animated in, do that now
2857  if (_sheetContext["shouldClose"] === YES)
2858  [self _detachSheetWindow];
2859  else
2860  [sheet makeKeyWindow];
2861  }
2862  else
2863  {
2864  // sheet is closed and not visible
2865  [self _cleanupSheetWindow];
2866  }
2867 }
2868 
2869 - (void)_clipSheet:(CPWindow)aSheet
2870 {
2871  var clipRect = [_platformWindow contentBounds];
2872  clipRect.origin.y = [self frame].origin.y + [[self contentView] frame].origin.y;
2873 
2874  [[_platformWindow layerAtLevel:_level create:NO] clipWindow:aSheet toRect:clipRect];
2875 }
2876 
2877 - (void)_removeClipForSheet:(CPWindow)aSheet
2878 {
2879  [[_platformWindow layerAtLevel:_level create:NO] removeClipForWindow:aSheet];
2880 }
2881 
2885 - (CPWindow)attachedSheet
2886 {
2887  if (_sheetContext === nil)
2888  return nil;
2889 
2890  return _sheetContext["sheet"];
2891 }
2892 
2896 - (BOOL)isSheet
2897 {
2898  return _isSheet;
2899 }
2900 
2901 //
2902 /*
2903  Used privately.
2904  @ignore
2905 */
2906 - (BOOL)becomesKeyOnlyIfNeeded
2907 {
2908  return NO;
2909 }
2910 
2915 - (BOOL)worksWhenModal
2916 {
2917  return NO;
2918 }
2919 
2920 - (BOOL)performKeyEquivalent:(CPEvent)anEvent
2921 {
2922  // FIXME: should we be starting at the root, in other words _windowView?
2923  // The evidence seems to point to no...
2924  return [_contentView performKeyEquivalent:anEvent];
2925 }
2926 
2927 - (void)keyDown:(CPEvent)anEvent
2928 {
2929  // It's not clear why we do performKeyEquivalent again here...
2930  // Perhaps to allow something to happen between sendEvent: and keyDown:?
2931  if ([anEvent _couldBeKeyEquivalent] && [self performKeyEquivalent:anEvent])
2932  return;
2933 
2934  // Apple's documentation is inconsistent with their behavior here. According to the docs
2935  // an event going of the responder chain is passed to the input system as a last resort.
2936  // However, the only methods I could get Cocoa to call automatically are
2937  // moveUp: moveDown: moveLeft: moveRight: pageUp: pageDown: and complete:
2938  // Unhandled events just travel further up the responder chain _past_ the window.
2939  if (![self _processKeyboardUIKey:anEvent])
2940  [super keyDown:anEvent];
2941 }
2942 
2943 /*
2944  @ignore
2945  Interprets the key event for action messages and sends the action message down the responder chain
2946  Cocoa only sends moveDown:, moveUp:, moveLeft:, moveRight:, pageUp:, pageDown: and complete: messages.
2947  We deviate from this by sending (the default) scrollPageUp:, scrollPageDown:, scrollToBeginningOfDocument: and scrollToEndOfDocument: for pageUp, pageDown, home and end keys.
2948  @param anEvent the event to handle.
2949  @return YES if the key event was handled, NO if no responder handled the key event
2950 */
2951 - (BOOL)_processKeyboardUIKey:(CPEvent)anEvent
2952 {
2953  var character = [anEvent charactersIgnoringModifiers];
2954 
2955  if (![CPWindowActionMessageKeys containsObject:character])
2956  return NO;
2957 
2958  var selectors = [CPKeyBinding selectorsForKey:character modifierFlags:0];
2959 
2960  if ([selectors count] <= 0)
2961  return NO;
2962 
2963  if (character !== CPEscapeFunctionKey)
2964  {
2965  var selector = [selectors objectAtIndex:0];
2966  return [[self firstResponder] tryToPerform:selector with:self];
2967  }
2968  else
2969  {
2970  /*
2971  Cocoa sends complete: for the escape key (instead of the default cancelOperation:). This is also the only action that is not sent directly to the first responder, but through doCommandBySelector. The difference is that doCommandBySelector: will also send the action to the window and application delegates.
2972  */
2973  [[self firstResponder] doCommandBySelector:@selector(complete:)];
2974  }
2975 
2976  return NO;
2977 }
2978 
2979 - (void)_dirtyKeyViewLoop
2980 {
2981  if (_autorecalculatesKeyViewLoop)
2982  _keyViewLoopIsDirty = YES;
2983 }
2984 
2985 /*
2986  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.
2987 
2988  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.
2989 */
2990 - (BOOL)_hasKeyViewLoop:(CPArray)theViews
2991 {
2992  var i,
2993  count = [theViews count];
2994 
2995  for (i = 0; i < count; ++i)
2996  {
2997  var view = theViews[i];
2998 
2999  if ([view nextKeyView] || [view previousKeyView])
3000  return YES;
3001  }
3002 
3003  for (i = 0; i < count; ++i)
3004  {
3005  var subviews = [theViews[i] subviews];
3006 
3007  if ([subviews count] && [self _hasKeyViewLoop:subviews])
3008  return YES;
3009  }
3010 
3011  return NO;
3012 }
3013 
3021 - (void)recalculateKeyViewLoop
3022 {
3023  [self _doRecalculateKeyViewLoop];
3024 }
3025 
3026 - (CPArray)_viewsSortedByPosition
3027 {
3028  var views = [CPArray arrayWithObject:_contentView];
3029 
3030  views = views.concat([self _subviewsSortedByPosition:[_contentView subviews]]);
3031 
3032  return views;
3033 }
3034 
3035 - (CPArray)_subviewsSortedByPosition:(CPArray)theSubviews
3036 {
3037  /*
3038  We first sort the subviews according to geometric order.
3039  Then we go through each subview, and if it has subviews,
3040  they are sorted and inserted after the superview. This
3041  is done recursively.
3042  */
3043  theSubviews = [theSubviews copy];
3044  [theSubviews sortUsingFunction:keyViewComparator context:nil];
3045 
3046  var sortedViews = [];
3047 
3048  for (var i = 0, count = [theSubviews count]; i < count; ++i)
3049  {
3050  var view = theSubviews[i],
3051  subviews = [view subviews];
3052 
3053  sortedViews.push(view);
3054 
3055  if ([subviews count])
3056  sortedViews = sortedViews.concat([self _subviewsSortedByPosition:subviews]);
3057  }
3058 
3059  return sortedViews;
3060 }
3061 
3062 - (void)_doRecalculateKeyViewLoop
3063 {
3064  var views = [self _viewsSortedByPosition];
3065 
3066  for (var index = 0, count = [views count]; index < count; ++index)
3067  [views[index] setNextKeyView:views[(index + 1) % count]];
3068 
3069  _keyViewLoopIsDirty = NO;
3070 }
3071 
3072 - (void)setAutorecalculatesKeyViewLoop:(BOOL)shouldRecalculate
3073 {
3074  if (_autorecalculatesKeyViewLoop === shouldRecalculate)
3075  return;
3076 
3077  _autorecalculatesKeyViewLoop = shouldRecalculate;
3078 }
3079 
3080 - (BOOL)autorecalculatesKeyViewLoop
3081 {
3082  return _autorecalculatesKeyViewLoop;
3083 }
3084 
3085 - (void)selectNextKeyView:(id)sender
3086 {
3087  if (_keyViewLoopIsDirty)
3088  [self _doRecalculateKeyViewLoop];
3089 
3090  var nextValidKeyView = nil;
3091 
3092  if ([_firstResponder isKindOfClass:[CPView class]])
3093  nextValidKeyView = [_firstResponder nextValidKeyView];
3094 
3095  if (!nextValidKeyView)
3096  {
3097  if ([_initialFirstResponder acceptsFirstResponder])
3098  nextValidKeyView = _initialFirstResponder;
3099  else
3100  nextValidKeyView = [_initialFirstResponder nextValidKeyView];
3101  }
3102 
3103  if (nextValidKeyView)
3104  [self makeFirstResponder:nextValidKeyView];
3105 }
3106 
3107 - (void)selectPreviousKeyView:(id)sender
3108 {
3109  if (_keyViewLoopIsDirty)
3110  [self _doRecalculateKeyViewLoop];
3111 
3112  var previousValidKeyView = nil;
3113 
3114  if ([_firstResponder isKindOfClass:[CPView class]])
3115  previousValidKeyView = [_firstResponder previousValidKeyView];
3116 
3117  if (!previousValidKeyView)
3118  {
3119  if ([_initialFirstResponder acceptsFirstResponder])
3120  previousValidKeyView = _initialFirstResponder;
3121  else
3122  previousValidKeyView = [_initialFirstResponder previousValidKeyView];
3123  }
3124 
3125  if (previousValidKeyView)
3126  [self makeFirstResponder:previousValidKeyView];
3127 }
3128 
3129 - (void)selectKeyViewFollowingView:(CPView)aView
3130 {
3131  if (_keyViewLoopIsDirty)
3132  [self _doRecalculateKeyViewLoop];
3133 
3134  var nextValidKeyView = [aView nextValidKeyView];
3135 
3136  if ([nextValidKeyView isKindOfClass:[CPView class]])
3137  [self makeFirstResponder:nextValidKeyView];
3138 }
3139 
3140 - (void)selectKeyViewPrecedingView:(CPView)aView
3141 {
3142  if (_keyViewLoopIsDirty)
3143  [self _doRecalculateKeyViewLoop];
3144 
3145  var previousValidKeyView = [aView previousValidKeyView];
3146 
3147  if ([previousValidKeyView isKindOfClass:[CPView class]])
3148  [self makeFirstResponder:previousValidKeyView];
3149 }
3150 
3156 - (void)setDefaultButtonCell:(CPButton)aButton
3157 {
3158  [self setDefaultButton:aButton];
3159 }
3160 
3165 - (CPButton)defaultButtonCell
3166 {
3167  return [self defaultButton];
3168 }
3169 
3176 - (void)setDefaultButton:(CPButton)aButton
3177 {
3178  if (_defaultButton === aButton)
3179  return;
3180 
3181  if ([_defaultButton keyEquivalent] === CPCarriageReturnCharacter)
3182  [_defaultButton setKeyEquivalent:nil];
3183 
3184  _defaultButton = aButton;
3185 
3186  if ([_defaultButton keyEquivalent] !== CPCarriageReturnCharacter)
3187  [_defaultButton setKeyEquivalent:CPCarriageReturnCharacter];
3188 }
3189 
3193 - (CPButton)defaultButton
3194 {
3195  return _defaultButton;
3196 }
3197 
3201 - (void)enableKeyEquivalentForDefaultButton
3202 {
3203  _defaultButtonEnabled = YES;
3204 }
3205 
3210 - (void)enableKeyEquivalentForDefaultButtonCell
3211 {
3212  [self enableKeyEquivalentForDefaultButton];
3213 }
3214 
3218 - (void)disableKeyEquivalentForDefaultButton
3219 {
3220  _defaultButtonEnabled = NO;
3221 }
3222 
3227 - (void)disableKeyEquivalentForDefaultButtonCell
3228 {
3229  [self disableKeyEquivalentForDefaultButton];
3230 }
3231 
3232 - (void)setValue:(id)aValue forKey:(CPString)aKey
3233 {
3234  if (aKey === CPDisplayPatternTitleBinding)
3235  [self setTitle:aValue || @""];
3236  else
3237  [super setValue:aValue forKey:aKey];
3238 }
3239 
3240 @end
3241 
3242 var keyViewComparator = function(lhs, rhs, context)
3243 {
3244  var lhsBounds = [lhs convertRect:[lhs bounds] toView:nil],
3245  rhsBounds = [rhs convertRect:[rhs bounds] toView:nil],
3246  lhsY = CGRectGetMinY(lhsBounds),
3247  rhsY = CGRectGetMinY(rhsBounds),
3248  lhsX = CGRectGetMinX(lhsBounds),
3249  rhsX = CGRectGetMinX(rhsBounds),
3250  intersectsVertically = MIN(CGRectGetMaxY(lhsBounds), CGRectGetMaxY(rhsBounds)) - MAX(lhsY, rhsY);
3251 
3252  // If two views are "on the same line" (intersect vertically), then rely on the x comparison.
3253  if (intersectsVertically > 0)
3254  {
3255  if (lhsX < rhsX)
3256  return CPOrderedAscending;
3257 
3258  if (lhsX === rhsX)
3259  return CPOrderedSame;
3260 
3261  return CPOrderedDescending;
3262  }
3263 
3264  if (lhsY < rhsY)
3265  return CPOrderedAscending;
3266 
3267  if (lhsY === rhsY)
3268  return CPOrderedSame;
3269 
3270  return CPOrderedDescending;
3271 };
3272 
3273 @implementation CPWindow (BridgeSupport)
3274 
3275 /*
3276  @ignore
3277 */
3278 - (void)resizeWithOldPlatformWindowSize:(CGSize)aSize
3279 {
3280  if ([self isFullPlatformWindow])
3281  return [self setFrame:[_platformWindow visibleFrame]];
3282 
3283  // If this window is constrainable and we are globally ignoring constraining, ignore the platform resize
3284  if ((_constrainsToUsableScreen && !CPWindowConstrainToScreen) || _autoresizingMask === CPWindowNotSizable)
3285  return;
3286 
3287  var frame = [_platformWindow contentBounds],
3288  newFrame = CGRectMakeCopy(_frame),
3289  dX = (CGRectGetWidth(frame) - aSize.width) /
3290  (((_autoresizingMask & CPWindowMinXMargin) ? 1 : 0) + (_autoresizingMask & CPWindowWidthSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxXMargin ? 1 : 0)),
3291  dY = (CGRectGetHeight(frame) - aSize.height) /
3292  ((_autoresizingMask & CPWindowMinYMargin ? 1 : 0) + (_autoresizingMask & CPWindowHeightSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxYMargin ? 1 : 0));
3293 
3294  if (_autoresizingMask & CPWindowMinXMargin)
3295  newFrame.origin.x += dX;
3296 
3297  if (_autoresizingMask & CPWindowWidthSizable)
3298  newFrame.size.width += dX;
3299 
3300  if (_autoresizingMask & CPWindowMinYMargin)
3301  newFrame.origin.y += dY;
3302 
3303  if (_autoresizingMask & CPWindowHeightSizable)
3304  newFrame.size.height += dY;
3305 
3306  [self _setFrame:newFrame display:YES animate:NO constrainWidth:YES constrainHeight:YES];
3307 }
3308 
3309 /*
3310  @ignore
3311 */
3312 - (void)setAutoresizingMask:(unsigned)anAutoresizingMask
3313 {
3314  _autoresizingMask = anAutoresizingMask;
3315 }
3316 
3317 /*
3318  @ignore
3319 */
3320 - (unsigned)autoresizingMask
3321 {
3322  return _autoresizingMask;
3323 }
3324 
3328 - (CGPoint)convertBaseToGlobal:(CGPoint)aPoint
3329 {
3330  return [CPPlatform isBrowser] ? [self convertBaseToPlatformWindow:aPoint] : [self convertBaseToScreen:aPoint];
3331 }
3332 
3336 - (CGPoint)convertGlobalToBase:(CGPoint)aPoint
3337 {
3338  return [CPPlatform isBrowser] ? [self convertPlatformWindowToBase:aPoint] : [self convertScreenToBase:aPoint];
3339 }
3340 
3344 - (CGPoint)convertBaseToPlatformWindow:(CGPoint)aPoint
3345 {
3346  if ([self _sharesChromeWithPlatformWindow])
3347  return CGPointMakeCopy(aPoint);
3348 
3349  var origin = [self frame].origin;
3350 
3351  return CGPointMake(aPoint.x + origin.x, aPoint.y + origin.y);
3352 }
3353 
3357 - (CGPoint)convertPlatformWindowToBase:(CGPoint)aPoint
3358 {
3359  if ([self _sharesChromeWithPlatformWindow])
3360  return CGPointMakeCopy(aPoint);
3361 
3362  var origin = [self frame].origin;
3363 
3364  return CGPointMake(aPoint.x - origin.x, aPoint.y - origin.y);
3365 }
3366 
3367 - (CGPoint)convertScreenToBase:(CGPoint)aPoint
3368 {
3369  return [self convertPlatformWindowToBase:[_platformWindow convertScreenToBase:aPoint]];
3370 }
3371 
3372 - (CGPoint)convertBaseToScreen:(CGPoint)aPoint
3373 {
3374  return [_platformWindow convertBaseToScreen:[self convertBaseToPlatformWindow:aPoint]];
3375 }
3376 
3377 - (void)_setSharesChromeWithPlatformWindow:(BOOL)shouldShareFrameWithPlatformWindow
3378 {
3379  // We canna' do it captain! We just don't have the power!
3380  if (shouldShareFrameWithPlatformWindow && [CPPlatform isBrowser])
3381  return;
3382 
3383  _sharesChromeWithPlatformWindow = shouldShareFrameWithPlatformWindow;
3384 
3385  [self _updateShadow];
3386 }
3387 
3388 - (BOOL)_sharesChromeWithPlatformWindow
3389 {
3390  return _sharesChromeWithPlatformWindow;
3391 }
3392 
3393 // Undo and Redo Support
3397 - (CPUndoManager)undoManager
3398 {
3399  // If we've ever created an undo manager, return it.
3400  if (_undoManager)
3401  return _undoManager;
3402 
3403  // If not, check to see if the document has one.
3404  var documentUndoManager = [[_windowController document] undoManager];
3405 
3406  if (documentUndoManager)
3407  return documentUndoManager;
3408 
3409  // If not, check to see if the delegate has one.
3410  if (_delegateRespondsToWindowWillReturnUndoManagerSelector)
3411  return [_delegate windowWillReturnUndoManager:self];
3412 
3413  // If not, create one.
3414  if (!_undoManager)
3415  _undoManager = [[CPUndoManager alloc] init];
3416 
3417  return _undoManager;
3418 }
3419 
3424 - (void)undo:(id)aSender
3425 {
3426  [[self undoManager] undo];
3427 }
3428 
3433 - (void)redo:(id)aSender
3434 {
3435  [[self undoManager] redo];
3436 }
3437 
3438 - (BOOL)containsPoint:(CGPoint)aPoint
3439 {
3440  return CGRectContainsPoint(_frame, aPoint);
3441 }
3442 
3443 /* aPoint should be global */
3444 - (BOOL)_isValidMousePoint:(CGPoint)aPoint
3445 {
3446  // If we are using the new resizing mode, mouse events are valid
3447  // outside the window's frame for non-full platform windows.
3448  var mouseFrame = (!_isFullPlatformWindow && (_styleMask & CPResizableWindowMask) && (CPWindowResizeStyle === CPWindowResizeStyleModern)) ? CGRectInset(_frame, -_CPWindowViewResizeSlop, -_CPWindowViewResizeSlop) : _frame;
3449 
3450  return CGRectContainsPoint(mouseFrame, aPoint);
3451 }
3452 
3453 @end
3454 
3455 @implementation CPWindow (Deprecated)
3460 - (void)setFullBridge:(BOOL)shouldBeFullBridge
3461 {
3462  _CPReportLenientDeprecation([self class], _cmd, @selector(setFullPlatformWindow:));
3463 
3464  [self setFullPlatformWindow:shouldBeFullBridge];
3465 }
3466 
3471 - (BOOL)isFullBridge
3472 {
3473  return [self isFullPlatformWindow];
3474 }
3475 
3476 /*
3477  @ignore
3478 */
3479 - (CGPoint)convertBaseToBridge:(CGPoint)aPoint
3480 {
3481  return [self convertBaseToPlatformWindow:aPoint];
3482 }
3483 
3484 /*
3485  @ignore
3486 */
3487 - (CGPoint)convertBridgeToBase:(CGPoint)aPoint
3488 {
3489  return [self convertPlatformWindowToBase:aPoint];
3490 }
3491 
3492 @end
3493 
3494 var interpolate = function(fromValue, toValue, progress)
3495 {
3496  return fromValue + (toValue - fromValue) * progress;
3497 };
3498 
3499 /* @ignore */
3500 @implementation _CPWindowFrameAnimation : CPAnimation
3501 {
3502  CPWindow _window;
3503 
3504  CGRect _startFrame;
3505  CGRect _targetFrame;
3506 }
3507 
3508 - (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame
3509 {
3510  self = [super initWithDuration:[aWindow animationResizeTime:aTargetFrame] animationCurve:CPAnimationLinear];
3511 
3512  if (self)
3513  {
3514  _window = aWindow;
3515 
3516  _targetFrame = CGRectMakeCopy(aTargetFrame);
3517  _startFrame = CGRectMakeCopy([_window frame]);
3518  }
3519 
3520  return self;
3521 }
3522 
3523 - (void)startAnimation
3524 {
3525  [super startAnimation];
3526 
3527  _window._isAnimating = YES;
3528 }
3529 
3530 - (void)setCurrentProgress:(float)aProgress
3531 {
3532  [super setCurrentProgress:aProgress];
3533 
3534  var value = [self currentValue];
3535 
3536  if (value == 1.0)
3537  _window._isAnimating = NO;
3538 
3539  var newFrame = CGRectMake(
3540  interpolate(CGRectGetMinX(_startFrame), CGRectGetMinX(_targetFrame), value),
3541  interpolate(CGRectGetMinY(_startFrame), CGRectGetMinY(_targetFrame), value),
3542  interpolate(CGRectGetWidth(_startFrame), CGRectGetWidth(_targetFrame), value),
3543  interpolate(CGRectGetHeight(_startFrame), CGRectGetHeight(_targetFrame), value));
3544 
3545  [_window setFrame:newFrame display:YES animate:NO];
3546 }
3547 
3548 @end
3549 
3550 
3551 @implementation CPWindow (CPDraggingAdditions)
3552 
3553 /* @ignore */
3554 - (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
3555 {
3556  // If none of our views or ourselves has registered for drag events...
3557  if (!_inclusiveRegisteredDraggedTypes)
3558  return nil;
3559 
3560 // We don't need to do this because the only place this gets called
3561 // -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
3562 // be safe?
3563 // if (![self containsPoint:aPoint])
3564 // return nil;
3565 
3566  var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
3567  hitView = [_windowView hitTest:adjustedPoint];
3568 
3569  while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
3570  hitView = [hitView superview];
3571 
3572  if (hitView)
3573  return hitView;
3574 
3575  if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
3576  return self;
3577 
3578  return nil;
3579 }
3580 
3581 @end
3582 
3583 function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasShadow, aLevel)
3584 {
3585  return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel };
3586 }
3587 
3588 @implementation CPWindow (CPSynthesizedAccessors)
3589 
3593 - (void)_setChildOrdering:(CPWindowOrderingMode)aValue
3594 {
3595  _childOrdering = aValue;
3596 }
3597 
3598 @end