API  0.9.8
 All Classes Files Functions Variables Typedefs Macros Groups Pages
CPView.j
Go to the documentation of this file.
1 /*
2  * CPView.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 
25 
26 @global appkit_tag_dom_elements
27 
28 @typedef _CPViewFullScreenModeState
29 
30 #if PLATFORM(DOM)
31 
32 if (typeof(appkit_tag_dom_elements) !== "undefined" && appkit_tag_dom_elements)
33 {
34  AppKitTagDOMElement = function(owner, element)
35  {
36  element.setAttribute("data-cappuccino-view", [owner className]);
37  element.setAttribute("data-cappuccino-uid", [owner UID]);
38  }
39 }
40 else
41 {
42  AppKitTagDOMElement = function(owner, element)
43  {
44  // By default, do nothing.
45  }
46 }
47 
48 #endif
49 
50 /*
51  @global
52  @group CPViewAutoresizingMasks
53  The default resizingMask, the view will not resize or reposition itself.
54 */
56 /*
57  @global
58  @group CPViewAutoresizingMasks
59  Allow for flexible space on the left hand side of the view.
60 */
62 /*
63  @global
64  @group CPViewAutoresizingMasks
65  The view should grow and shrink horizontally with its parent view.
66 */
68 /*
69  @global
70  @group CPViewAutoresizingMasks
71  Allow for flexible space to the right hand side of the view.
72 */
74 /*
75  @global
76  @group CPViewAutoresizingMasks
77  Allow for flexible space above the view.
78 */
80 /*
81  @global
82  @group CPViewAutoresizingMasks
83  The view should grow and shrink vertically with its parent view.
84 */
86 /*
87  @global
88  @group CPViewAutoresizingMasks
89  Allow for flexible space below the view.
90 */
92 
93 CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
94 CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
95 
98 
99 #if PLATFORM(DOM)
100 var DOMElementPrototype = nil,
101 
102  BackgroundTrivialColor = 0,
103  BackgroundVerticalThreePartImage = 1,
104  BackgroundHorizontalThreePartImage = 2,
105  BackgroundNinePartImage = 3,
106  BackgroundTransparentColor = 4;
107 #endif
108 
109 var CPViewFlags = { },
112 
114 
115 
132 @implementation CPView : CPResponder
133 {
134  CPWindow _window;
135 
136  CPView _superview;
137  CPArray _subviews;
138 
139  CPGraphicsContext _graphicsContext;
140 
141  int _tag;
142  CPString _identifier;
143 
144  CGRect _frame;
145  CGRect _bounds;
146  CGAffineTransform _boundsTransform;
147  CGAffineTransform _inverseBoundsTransform;
148 
149  CPSet _registeredDraggedTypes;
150  CPArray _registeredDraggedTypesArray;
151 
152  BOOL _isHidden;
153  BOOL _hitTests;
154  BOOL _clipsToBounds;
155 
156  BOOL _postsFrameChangedNotifications;
157  BOOL _postsBoundsChangedNotifications;
158  BOOL _inhibitFrameAndBoundsChangedNotifications;
159  BOOL _inLiveResize;
160  BOOL _isSuperviewAClipView;
161 
162 #if PLATFORM(DOM)
163  DOMElement _DOMElement;
164  DOMElement _DOMContentsElement;
165 
166  CPArray _DOMImageParts;
167  CPArray _DOMImageSizes;
168 
169  unsigned _backgroundType;
170 #endif
171 
172  CGRect _dirtyRect;
173 
174  float _opacity;
175  CPColor _backgroundColor;
176 
177  BOOL _autoresizesSubviews;
178  unsigned _autoresizingMask;
179 
180  CALayer _layer;
181  BOOL _wantsLayer;
182 
183  // Full Screen State
184  BOOL _isInFullScreenMode;
185 
186  _CPViewFullScreenModeState _fullScreenModeState;
187 
188  // Zoom Support
189  BOOL _isScaled;
190  CGSize _hierarchyScaleSize;
191  CGSize _scaleSize;
192 
193  // Drawing high DPI
194  BOOL _needToSetTransformMatrix;
195  float _highDPIRatio;
196 
197  // Layout Support
198  BOOL _needsLayout;
199  JSObject _ephemeralSubviews;
200 
201  // Theming Support
202  CPTheme _theme;
203  CPString _themeClass;
204  JSObject _themeAttributes;
205  unsigned _themeState;
206 
207  JSObject _ephemeralSubviewsForNames;
208  CPSet _ephereralSubviews;
209 
210  // Key View Support
211  CPView _nextKeyView;
212  CPView _previousKeyView;
213 
214  unsigned _viewClassFlags;
215 
216  // ToolTips
217  CPString _toolTip;
218  Function _toolTipFunctionIn;
219  Function _toolTipFunctionOut;
220  BOOL _toolTipInstalled;
221 
222  BOOL _isObserving;
223 }
224 
225 /*
226  Private method for Objective-J.
227  @ignore
228 */
229 + (void)initialize
230 {
231  if (self !== [CPView class])
232  return;
233 
234 #if PLATFORM(DOM)
235  DOMElementPrototype = document.createElement("div");
236 
237  var style = DOMElementPrototype.style;
238 
239  style.overflow = "hidden";
240  style.position = "absolute";
241  style.visibility = "visible";
242  style.zIndex = 0;
243 #endif
244 
245  CachedNotificationCenter = [CPNotificationCenter defaultCenter];
246 }
247 
248 + (Class)_binderClassForBinding:(CPString)aBinding
249 {
250  if ([aBinding hasPrefix:CPHiddenBinding])
251  return [CPMultipleValueOrBinding class];
252 
253  return [super _binderClassForBinding:aBinding];
254 }
255 
260 + (void)setHighDPIDrawingEnabled:(BOOL)isEnabled
261 {
262  CPViewHighDPIDrawingEnabled = isEnabled;
263 }
264 
269 + (BOOL)isHighDPIDrawingEnabled
270 {
272 }
273 
274 - (void)_setupViewFlags
275 {
276  var theClass = [self class],
277  classUID = [theClass UID];
278 
279  if (CPViewFlags[classUID] === undefined)
280  {
281  var flags = 0;
282 
283  if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
284  flags |= CPViewHasCustomDrawRect;
285 
286  if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
288 
289  CPViewFlags[classUID] = flags;
290  }
291 
292  _viewClassFlags = CPViewFlags[classUID];
293 }
294 
295 + (CPSet)keyPathsForValuesAffectingFrame
296 {
297  return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
298 }
299 
300 + (CPSet)keyPathsForValuesAffectingBounds
301 {
302  return [CPSet setWithObjects:@"boundsOrigin", @"boundsSize"];
303 }
304 
305 + (CPMenu)defaultMenu
306 {
307  return nil;
308 }
309 
310 - (id)init
311 {
312  return [self initWithFrame:CGRectMakeZero()];
313 }
314 
319 - (id)initWithFrame:(CGRect)aFrame
320 {
321  self = [super init];
322 
323  if (self)
324  {
325  var width = CGRectGetWidth(aFrame),
326  height = CGRectGetHeight(aFrame);
327 
328  _subviews = [];
329  _registeredDraggedTypes = [CPSet set];
330  _registeredDraggedTypesArray = [];
331 
332  _tag = -1;
333 
334  _frame = CGRectMakeCopy(aFrame);
335  _bounds = CGRectMake(0.0, 0.0, width, height);
336 
337  _autoresizingMask = CPViewNotSizable;
338  _autoresizesSubviews = YES;
339  _clipsToBounds = YES;
340 
341  _opacity = 1.0;
342  _isHidden = NO;
343  _hitTests = YES;
344 
345  _hierarchyScaleSize = CGSizeMake(1.0 , 1.0);
346  _scaleSize = CGSizeMake(1.0, 1.0);
347  _isScaled = NO;
348 
349  _theme = [CPTheme defaultTheme];
350  _themeState = CPThemeStateNormal;
351 
352 #if PLATFORM(DOM)
353  _DOMElement = DOMElementPrototype.cloneNode(false);
354  AppKitTagDOMElement(self, _DOMElement);
355 
356  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(aFrame), CGRectGetMinY(aFrame));
357  CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
358 
359  _DOMImageParts = [];
360  _DOMImageSizes = [];
361 #endif
362 
363  [self _setupViewFlags];
364 
365  [self _loadThemeAttributes];
366  }
367 
368  return self;
369 }
370 
371 
377 - (void)setToolTip:(CPString)aToolTip
378 {
379  if (_toolTip == aToolTip)
380  return;
381 
382  if (aToolTip && ![aToolTip isKindOfClass:CPString])
383  aToolTip = [aToolTip description];
384 
385  _toolTip = aToolTip;
386 
387  [self _manageToolTipInstallation];
388 }
389 
390 - (void)_manageToolTipInstallation
391 {
392  if ([self window] && _toolTip)
393  [self _installToolTipEventHandlers];
394  else
395  [self _uninstallToolTipEventHandlers];
396 }
401 - (void)_installToolTipEventHandlers
402 {
403  if (_toolTipInstalled)
404  return;
405 
406  if (!_toolTipFunctionIn)
407  _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
408 
409  if (!_toolTipFunctionOut)
410  _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
411 
412 #if PLATFORM(DOM)
413  if (_DOMElement.addEventListener)
414  {
415  _DOMElement.addEventListener("mouseover", _toolTipFunctionIn, YES);
416  _DOMElement.addEventListener("keypress", _toolTipFunctionOut, YES);
417  _DOMElement.addEventListener("mouseout", _toolTipFunctionOut, YES);
418  }
419  else if (_DOMElement.attachEvent)
420  {
421  _DOMElement.attachEvent("onmouseover", _toolTipFunctionIn);
422  _DOMElement.attachEvent("onkeypress", _toolTipFunctionOut);
423  _DOMElement.attachEvent("onmouseout", _toolTipFunctionOut);
424  }
425 #endif
426 
427  _toolTipInstalled = YES;
428 }
429 
434 - (void)_uninstallToolTipEventHandlers
435 {
436  if (!_toolTipInstalled)
437  return;
438 
439 #if PLATFORM(DOM)
440  if (_DOMElement.removeEventListener)
441  {
442  _DOMElement.removeEventListener("mouseover", _toolTipFunctionIn, YES);
443  _DOMElement.removeEventListener("keypress", _toolTipFunctionOut, YES);
444  _DOMElement.removeEventListener("mouseout", _toolTipFunctionOut, YES);
445  }
446  else if (_DOMElement.detachEvent)
447  {
448  _DOMElement.detachEvent("onmouseover", _toolTipFunctionIn);
449  _DOMElement.detachEvent("onkeypress", _toolTipFunctionOut);
450  _DOMElement.detachEvent("onmouseout", _toolTipFunctionOut);
451  }
452 #endif
453 
454  _toolTipFunctionIn = nil;
455  _toolTipFunctionOut = nil;
456 
457  _toolTipInstalled = NO;
458 }
459 
464 - (CPView)superview
465 {
466  return _superview;
467 }
468 
473 - (CPArray)subviews
474 {
475  return [_subviews copy];
476 }
477 
481 - (CPWindow)window
482 {
483  return _window;
484 }
485 
490 - (void)addSubview:(CPView)aSubview
491 {
492  [self _insertSubview:aSubview atIndex:CPNotFound];
493 }
494 
501 - (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
502 {
503  var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound;
504 
505  // In other words, if no view, then either all the way at the bottom or all the way at the top.
506  if (index === CPNotFound)
507  index = (anOrderingMode === CPWindowAbove) ? [_subviews count] : 0;
508 
509  // else, if we have a view, above if above.
510  else if (anOrderingMode === CPWindowAbove)
511  ++index;
512 
513  [self _insertSubview:aSubview atIndex:index];
514 }
515 
516 /* @ignore */
517 - (void)_insertSubview:(CPView)aSubview atIndex:(int)anIndex
518 {
519  if (aSubview === self)
520  [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"];
521 #if DEBUG
522  if (!aSubview._superview && _subviews.indexOf(aSubview) !== CPNotFound)
523  [CPException raise:CPInvalidArgumentException reason:"can't insert a subview in duplicate (probably partially decoded)"];
524 #endif
525 
526  // Notify the subview that it will be moving.
527  [aSubview viewWillMoveToSuperview:self];
528 
529  // We will have to adjust the z-index of all views starting at this index.
530  var count = _subviews.length,
531  lastWindow;
532 
533  // Dirty the key view loop, in case the window wants to auto recalculate it
534  [[self window] _dirtyKeyViewLoop];
535 
536  // If this is already one of our subviews, remove it.
537  if (aSubview._superview == self)
538  {
539  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
540 
541  // FIXME: should this be anIndex >= count? (last one)
542  if (index === anIndex || index === count - 1 && anIndex === count)
543  return;
544 
545  [_subviews removeObjectAtIndex:index];
546 
547 #if PLATFORM(DOM)
548  CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement);
549 #endif
550 
551  if (anIndex > index)
552  --anIndex;
553 
554  //We've effectively made the subviews array shorter, so represent that.
555  --count;
556  }
557  else
558  {
559  var superview = aSubview._superview;
560 
561  lastWindow = [superview window];
562 
563  // Remove the view from its previous superview.
564  [aSubview _removeFromSuperview];
565 
566  // Set ourselves as the superview.
567  aSubview._superview = self;
568  }
569 
570  if (anIndex === CPNotFound || anIndex >= count)
571  {
572  _subviews.push(aSubview);
573 
574 #if PLATFORM(DOM)
575  // Attach the actual node.
576  CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement);
577 #endif
578  }
579  else
580  {
581  _subviews.splice(anIndex, 0, aSubview);
582 
583 #if PLATFORM(DOM)
584  // Attach the actual node.
585  CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement);
586 #endif
587  }
588 
589  [aSubview setNextResponder:self];
590  [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]];
591 
592  // If the subview is not hidden and one of its ancestors is hidden,
593  // notify the subview that it is now hidden.
594  if (![aSubview isHidden] && [self isHiddenOrHasHiddenAncestor])
595  [aSubview _notifyViewDidHide];
596 
597  [aSubview viewDidMoveToSuperview];
598 
599  // Set the subview's window to our own.
600  if (_window)
601  [aSubview _setWindow:_window];
602 
603  if (!_window && lastWindow)
604  [aSubview _setWindow:nil];
605 
606  // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet
607  // and none of the below matters anyhow.
608  if (_themeState)
609  {
610  if ([self hasThemeState:CPThemeStateFirstResponder])
611  [aSubview _notifyViewDidBecomeFirstResponder];
612  else
613  [aSubview _notifyViewDidResignFirstResponder];
614 
615  if ([self hasThemeState:CPThemeStateKeyWindow])
616  [aSubview _notifyWindowDidBecomeKey];
617  else
618  [aSubview _notifyWindowDidResignKey];
619  }
620 
621  [self didAddSubview:aSubview];
622 }
623 
628 - (void)didAddSubview:(CPView)aSubview
629 {
630 }
631 
636 - (void)removeFromSuperview
637 {
638  var superview = _superview;
639 
640  [self viewWillMoveToSuperview:nil];
641  [self _removeFromSuperview];
642  [self viewDidMoveToSuperview];
643 
644  if (superview)
645  [self _setWindow:nil];
646 }
647 
648 - (void)_removeFromSuperview
649 {
650  if (!_superview)
651  return;
652 
653  // Dirty the key view loop, in case the window wants to auto recalculate it
654  [[self window] _dirtyKeyViewLoop];
655 
656  [_superview willRemoveSubview:self];
657 
658  [_superview._subviews removeObjectIdenticalTo:self];
659 
660 #if PLATFORM(DOM)
661  CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
662 #endif
663 
664  // If the view is not hidden and one of its ancestors is hidden,
665  // notify the view that it is now unhidden.
666  if (!_isHidden && [_superview isHiddenOrHasHiddenAncestor])
667  [self _notifyViewDidUnhide];
668 
669  [self _notifyWindowDidResignKey];
670  [self _notifyViewDidResignFirstResponder];
671 
672  _superview = nil;
673 }
674 
680 - (void)replaceSubview:(CPView)aSubview with:(CPView)aView
681 {
682  if (aSubview._superview !== self || aSubview === aView)
683  return;
684 
685  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
686 
687  [self _insertSubview:aView atIndex:index];
688 
689  [aSubview removeFromSuperview];
690 }
691 
692 - (void)setSubviews:(CPArray)newSubviews
693 {
694  if (!newSubviews)
695  [CPException raise:CPInvalidArgumentException reason:"newSubviews cannot be nil in -[CPView setSubviews:]"];
696 
697  // Trivial Case 0: Same array somehow
698  if ([_subviews isEqual:newSubviews])
699  return;
700 
701  // Trivial Case 1: No current subviews, simply add all new subviews.
702  if ([_subviews count] === 0)
703  {
704  var index = 0,
705  count = [newSubviews count];
706 
707  for (; index < count; ++index)
708  [self addSubview:newSubviews[index]];
709 
710  return;
711  }
712 
713  // Trivial Case 2: No new subviews, simply remove all current subviews.
714  if ([newSubviews count] === 0)
715  {
716  var count = [_subviews count];
717 
718  while (count--)
719  [_subviews[count] removeFromSuperview];
720 
721  return;
722  }
723 
724  // Find out the views that were removed.
725  var removedSubviews = [CPMutableSet setWithArray:_subviews];
726 
727  [removedSubviews removeObjectsInArray:newSubviews];
728  [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
729 
730  // Find out which views need to be added.
731  var addedSubviews = [CPMutableSet setWithArray:newSubviews];
732 
733  [addedSubviews removeObjectsInArray:_subviews];
734 
735  var addedSubview = nil,
736  addedSubviewEnumerator = [addedSubviews objectEnumerator];
737 
738  while ((addedSubview = [addedSubviewEnumerator nextObject]) !== nil)
739  [self addSubview:addedSubview];
740 
741  // If the order is fine, no need to reorder.
742  if ([_subviews isEqual:newSubviews])
743  return;
744 
745  _subviews = [newSubviews copy];
746 
747 #if PLATFORM(DOM)
748  var index = 0,
749  count = [_subviews count];
750 
751  for (; index < count; ++index)
752  {
753  var subview = _subviews[index];
754 
755  CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement);
756  CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement);
757  }
758 #endif
759 }
760 
761 /* @ignore */
762 - (void)_setWindow:(CPWindow)aWindow
763 {
764  [[self window] _dirtyKeyViewLoop];
765 
766  // Clear out first responder if we're the first responder and leaving.
767  if ([_window firstResponder] === self && _window != aWindow)
768  [_window makeFirstResponder:nil];
769 
770  // Notify the view and its subviews
771  [self viewWillMoveToWindow:aWindow];
772 
773  // Unregister the drag events from the current window and register
774  // them in the new window.
775  if (_registeredDraggedTypes)
776  {
777  [_window _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
778  [aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
779  }
780 
781  _window = aWindow;
782 
783  var count = [_subviews count];
784 
785  while (count--)
786  [_subviews[count] _setWindow:aWindow];
787 
788  if ([_window isKeyWindow])
789  [self setThemeState:CPThemeStateKeyWindow];
790  else
791  [self unsetThemeState:CPThemeStateKeyWindow];
792 
793  [self viewDidMoveToWindow];
794 
795  [self _manageToolTipInstallation];
796 
797  [[self window] _dirtyKeyViewLoop];
798 }
799 
804 - (BOOL)isDescendantOf:(CPView)aView
805 {
806  var view = self;
807 
808  do
809  {
810  if (view == aView)
811  return YES;
812  } while(view = [view superview])
813 
814  return NO;
815 }
816 
820 - (void)viewDidMoveToSuperview
821 {
822 // if (_graphicsContext)
823  [self setNeedsDisplay:YES];
824 
825 }
826 
830 - (void)viewDidMoveToWindow
831 {
832 }
833 
838 - (void)viewWillMoveToSuperview:(CPView)aView
839 {
840  _isSuperviewAClipView = [aView isKindOfClass:[CPClipView class]];
841 
842  [self _removeObservers];
843 
844  if (aView)
845  [self _addObservers];
846 }
847 
852 - (void)viewWillMoveToWindow:(CPWindow)aWindow
853 {
854 }
855 
860 - (void)willRemoveSubview:(CPView)aView
861 {
862 }
863 
864 - (void)_removeObservers
865 {
866  if (!_isObserving)
867  return;
868 
869  var count = [_subviews count];
870 
871  while (count--)
872  [_subviews[count] _removeObservers];
873 
874  _isObserving = NO;
875 }
876 
877 - (void)_addObservers
878 {
879  if (_isObserving)
880  return;
881 
882  var count = [_subviews count];
883 
884  while (count--)
885  [_subviews[count] _addObservers];
886 
887  _isObserving = YES;
888 }
889 
894 - (CPMenuItem)enclosingMenuItem
895 {
896  var view = self;
897 
898  while (view && ![view isKindOfClass:[_CPMenuItemView class]])
899  view = [view superview];
900 
901  if (view)
902  return view._menuItem;
903 
904  return nil;
905 /* var view = self,
906  enclosingMenuItem = _enclosingMenuItem;
907 
908  while (!enclosingMenuItem && (view = view._enclosingMenuItem))
909  view = [view superview];
910 
911  return enclosingMenuItem;*/
912 }
913 
914 - (void)setTag:(CPInteger)aTag
915 {
916  _tag = aTag;
917 }
918 
919 - (CPInteger)tag
920 {
921  return _tag;
922 }
923 
924 - (CPView)viewWithTag:(CPInteger)aTag
925 {
926  if ([self tag] == aTag)
927  return self;
928 
929  var index = 0,
930  count = _subviews.length;
931 
932  for (; index < count; ++index)
933  {
934  var view = [_subviews[index] viewWithTag:aTag];
935 
936  if (view)
937  return view;
938  }
939 
940  return nil;
941 }
942 
947 - (BOOL)isFlipped
948 {
949  return YES;
950 }
951 
959 - (void)setFrame:(CGRect)aFrame
960 {
961  if (CGRectEqualToRect(_frame, aFrame))
962  return;
963 
964  _inhibitFrameAndBoundsChangedNotifications = YES;
965 
966  [self setFrameOrigin:aFrame.origin];
967  [self setFrameSize:aFrame.size];
968 
969  _inhibitFrameAndBoundsChangedNotifications = NO;
970 
971  if (_postsFrameChangedNotifications)
972  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
973 
974  if (_isSuperviewAClipView)
975  [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
976 }
977 
982 - (CGRect)frame
983 {
984  return CGRectMakeCopy(_frame);
985 }
986 
987 - (CGPoint)frameOrigin
988 {
989  return CGPointMakeCopy(_frame.origin);
990 }
991 
992 - (CGSize)frameSize
993 {
994  return CGSizeMakeCopy(_frame.size);
995 }
996 
1004 - (void)setCenter:(CGPoint)aPoint
1005 {
1006  [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
1007 }
1008 
1013 - (CGPoint)center
1014 {
1015  return CGPointMake(_frame.size.width / 2.0 + _frame.origin.x, _frame.size.height / 2.0 + _frame.origin.y);
1016 }
1017 
1025 - (void)setFrameOrigin:(CGPoint)aPoint
1026 {
1027  var origin = _frame.origin;
1028 
1029  if (!aPoint || CGPointEqualToPoint(origin, aPoint))
1030  return;
1031 
1032  origin.x = aPoint.x;
1033  origin.y = aPoint.y;
1034 
1035  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1036  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
1037 
1038  if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
1039  [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
1040 
1041 #if PLATFORM(DOM)
1042  var transform = _superview ? _superview._boundsTransform : NULL;
1043 
1044  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
1045 #endif
1046 }
1047 
1054 - (void)setFrameSize:(CGSize)aSize
1055 {
1056  var size = _frame.size;
1057 
1058  if (!aSize || CGSizeEqualToSize(size, aSize))
1059  return;
1060 
1061  var oldSize = CGSizeMakeCopy(size);
1062 
1063  size.width = aSize.width;
1064  size.height = aSize.height;
1065 
1066  if (YES)
1067  {
1068  _bounds.size.width = aSize.width * 1 / _scaleSize.width;
1069  _bounds.size.height = aSize.height * 1 / _scaleSize.height;
1070  }
1071 
1072  if (_layer)
1073  [_layer _owningViewBoundsChanged];
1074 
1075  if (_autoresizesSubviews)
1076  [self resizeSubviewsWithOldSize:oldSize];
1077 
1078  [self setNeedsLayout];
1079  [self setNeedsDisplay:YES];
1080 
1081 #if PLATFORM(DOM)
1082  [self _setDisplayServerSetStyleSize:size];
1083 
1084  if (_backgroundType !== BackgroundTrivialColor)
1085  {
1086  if (_backgroundType === BackgroundTransparentColor)
1087  {
1088  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
1089  }
1090  else
1091  {
1092  var images = [[_backgroundColor patternImage] imageSlices],
1093  partIndex = 0,
1094  frameSize = aSize;
1095 
1096  if (_backgroundType === BackgroundVerticalThreePartImage)
1097  {
1098  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1099  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
1100 
1101  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
1102  if (top)
1103  {
1104  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px");
1105  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, top);
1106  partIndex++;
1107  }
1108  if (_DOMImageSizes[1])
1109  {
1110  var height = frameSize.height - top - bottom;
1111 
1112  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px");
1113  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height - top - bottom);
1114  partIndex++;
1115  }
1116  if (bottom)
1117  {
1118  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", bottom + "px");
1119  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, bottom);
1120  }
1121  }
1122  else if (_backgroundType === BackgroundHorizontalThreePartImage)
1123  {
1124  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1125  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
1126 
1127  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
1128  if (left)
1129  {
1130  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px");
1131  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, size.height);
1132  partIndex++;
1133  }
1134  if (_DOMImageSizes[1])
1135  {
1136  var width = (frameSize.width - left - right);
1137 
1138  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], width + "px", frameSize.height + "px");
1139  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width - left - right, size.height);
1140  partIndex++;
1141  }
1142  if (right)
1143  {
1144  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], right + "px", frameSize.height + "px");
1145  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, size.height);
1146  }
1147  }
1148  else if (_backgroundType === BackgroundNinePartImage)
1149  {
1150  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1151  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
1152  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1153  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
1154  width = size.width - left - right,
1155  height = size.height - top - bottom;
1156 
1157  if (_DOMImageSizes[0])
1158  partIndex++;
1159  if (_DOMImageSizes[1])
1160  {
1161  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, top);
1162  partIndex++;
1163  }
1164  if (_DOMImageSizes[2])
1165  partIndex++;
1166  if (_DOMImageSizes[3])
1167  {
1168  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
1169  partIndex++;
1170  }
1171  if (_DOMImageSizes[4])
1172  {
1173  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1174  partIndex++;
1175  }
1176  if (_DOMImageSizes[5])
1177  {
1178  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1179  partIndex++;
1180  }
1181  if (_DOMImageSizes[6])
1182  partIndex++;
1183  if (_DOMImageSizes[7])
1184  {
1185  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
1186  }
1187  }
1188  }
1189  }
1190 #endif
1191 
1192  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1193  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
1194 
1195  if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
1196  [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
1197 }
1198 
1204 - (void)_setDisplayServerSetStyleSize:(CGSize)aSize
1205 {
1206 #if PLATFORM(DOM)
1207  var scale = [self scaleSize];
1208 
1209  CPDOMDisplayServerSetStyleSize(_DOMElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
1210 
1211  if (_DOMContentsElement)
1212  {
1213  CPDOMDisplayServerSetSize(_DOMContentsElement, aSize.width * _highDPIRatio * 1 / scale.width, aSize.height * _highDPIRatio * 1 / scale.height);
1214  CPDOMDisplayServerSetStyleSize(_DOMContentsElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
1215 
1216  _needToSetTransformMatrix = YES;
1217  }
1218 #endif
1219 }
1220 
1226 - (void)setBounds:(CGRect)bounds
1227 {
1228  if (CGRectEqualToRect(_bounds, bounds))
1229  return;
1230 
1231  _inhibitFrameAndBoundsChangedNotifications = YES;
1232 
1233  [self setBoundsOrigin:bounds.origin];
1234  [self setBoundsSize:bounds.size];
1235 
1236  _inhibitFrameAndBoundsChangedNotifications = NO;
1237 
1238  if (_postsBoundsChangedNotifications)
1239  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1240 
1241  if (_isSuperviewAClipView)
1242  [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
1243 }
1244 
1249 - (CGRect)bounds
1250 {
1251  return CGRectMakeCopy(_bounds);
1252 }
1253 
1254 - (CGPoint)boundsOrigin
1255 {
1256  return CGPointMakeCopy(_bounds.origin);
1257 }
1258 
1259 - (CGSize)boundsSize
1260 {
1261  return CGSizeMakeCopy(_bounds.size);
1262 }
1263 
1270 - (void)setBoundsOrigin:(CGPoint)aPoint
1271 {
1272  var origin = _bounds.origin;
1273 
1274  if (CGPointEqualToPoint(origin, aPoint))
1275  return;
1276 
1277  origin.x = aPoint.x;
1278  origin.y = aPoint.y;
1279 
1280  if (origin.x != 0 || origin.y != 0)
1281  {
1282  _boundsTransform = CGAffineTransformMakeTranslation(-origin.x, -origin.y);
1283  _inverseBoundsTransform = CGAffineTransformInvert(_boundsTransform);
1284  }
1285  else
1286  {
1287  _boundsTransform = nil;
1288  _inverseBoundsTransform = nil;
1289  }
1290 
1291 #if PLATFORM(DOM)
1292  var index = _subviews.length;
1293 
1294  while (index--)
1295  {
1296  var view = _subviews[index],
1297  origin = view._frame.origin;
1298 
1299  CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y);
1300  }
1301 #endif
1302 
1303  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1304  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1305 
1306  if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
1307  [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
1308 }
1309 
1316 - (void)setBoundsSize:(CGSize)aSize
1317 {
1318  var size = _bounds.size;
1319 
1320  if (CGSizeEqualToSize(size, aSize))
1321  return;
1322 
1323  var frameSize = _frame.size;
1324 
1325  if (!CGSizeEqualToSize(size, frameSize))
1326  {
1327  var origin = _bounds.origin;
1328 
1329  origin.x /= size.width / frameSize.width;
1330  origin.y /= size.height / frameSize.height;
1331  }
1332 
1333  size.width = aSize.width;
1334  size.height = aSize.height;
1335 
1336  if (!CGSizeEqualToSize(size, frameSize))
1337  {
1338  var origin = _bounds.origin;
1339 
1340  origin.x *= size.width / frameSize.width;
1341  origin.y *= size.height / frameSize.height;
1342  }
1343 
1344  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1345  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1346 
1347  if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
1348  [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
1349 }
1350 
1351 
1356 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
1357 {
1358  var mask = [self autoresizingMask];
1359 
1360  if (mask == CPViewNotSizable)
1361  return;
1362 
1363  var frame = _superview._frame,
1364  newFrame = CGRectMakeCopy(_frame),
1365  dX = frame.size.width - aSize.width,
1366  dY = frame.size.height - aSize.height,
1367  evenFractionX = 1.0 / ((mask & CPViewMinXMargin ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
1368  evenFractionY = 1.0 / ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)),
1369  baseX = (mask & CPViewMinXMargin ? _frame.origin.x : 0) +
1370  (mask & CPViewWidthSizable ? _frame.size.width : 0) +
1371  (mask & CPViewMaxXMargin ? aSize.width - _frame.size.width - _frame.origin.x : 0),
1372  baseY = (mask & CPViewMinYMargin ? _frame.origin.y : 0) +
1373  (mask & CPViewHeightSizable ? _frame.size.height : 0) +
1374  (mask & CPViewMaxYMargin ? aSize.height - _frame.size.height - _frame.origin.y : 0);
1375 
1376  if (mask & CPViewMinXMargin)
1377  newFrame.origin.x += dX * (baseX > 0 ? _frame.origin.x / baseX : evenFractionX);
1378 
1379  if (mask & CPViewWidthSizable)
1380  newFrame.size.width += dX * (baseX > 0 ? _frame.size.width / baseX : evenFractionX);
1381 
1382  if (mask & CPViewMinYMargin)
1383  newFrame.origin.y += dY * (baseY > 0 ? _frame.origin.y / baseY : evenFractionY);
1384 
1385  if (mask & CPViewHeightSizable)
1386  newFrame.size.height += dY * (baseY > 0 ? _frame.size.height / baseY : evenFractionY);
1387 
1388  [self setFrame:newFrame];
1389 }
1390 
1395 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
1396 {
1397  var count = _subviews.length;
1398 
1399  while (count--)
1400  [_subviews[count] resizeWithOldSuperviewSize:aSize];
1401 }
1402 
1410 - (void)setAutoresizesSubviews:(BOOL)aFlag
1411 {
1412  _autoresizesSubviews = !!aFlag;
1413 }
1414 
1419 - (BOOL)autoresizesSubviews
1420 {
1421  return _autoresizesSubviews;
1422 }
1423 
1428 - (void)setAutoresizingMask:(unsigned)aMask
1429 {
1430  _autoresizingMask = aMask;
1431 }
1432 
1436 - (unsigned)autoresizingMask
1437 {
1438  return _autoresizingMask;
1439 }
1440 
1441 // Fullscreen Mode
1442 
1446 - (BOOL)enterFullScreenMode
1447 {
1448  return [self enterFullScreenMode:nil withOptions:nil];
1449 }
1450 
1456 - (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options
1457 {
1458  _fullScreenModeState = _CPViewFullScreenModeStateMake(self);
1459 
1460  var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
1461 
1462  [fullScreenWindow setLevel:CPScreenSaverWindowLevel];
1463  [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1464 
1465  var contentView = [fullScreenWindow contentView];
1466 
1467  [contentView setBackgroundColor:[CPColor blackColor]];
1468  [contentView addSubview:self];
1469 
1470  [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1471  [self setFrame:CGRectMakeCopy([contentView bounds])];
1472 
1473  [fullScreenWindow makeKeyAndOrderFront:self];
1474 
1475  [fullScreenWindow makeFirstResponder:self];
1476 
1477  _isInFullScreenMode = YES;
1478 
1479  return YES;
1480 }
1481 
1485 - (void)exitFullScreenMode
1486 {
1487  [self exitFullScreenModeWithOptions:nil];
1488 }
1489 
1494 - (void)exitFullScreenModeWithOptions:(CPDictionary)options
1495 {
1496  if (!_isInFullScreenMode)
1497  return;
1498 
1499  _isInFullScreenMode = NO;
1500 
1501  [self setFrame:_fullScreenModeState.frame];
1502  [self setAutoresizingMask:_fullScreenModeState.autoresizingMask];
1503  [_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index];
1504 
1505  [[self window] orderOut:self];
1506 }
1507 
1511 - (BOOL)isInFullScreenMode
1512 {
1513  return _isInFullScreenMode;
1514 }
1515 
1520 - (void)setHidden:(BOOL)aFlag
1521 {
1522  aFlag = !!aFlag;
1523 
1524  if (_isHidden === aFlag)
1525  return;
1526 
1527 // FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
1528 // _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
1529  _isHidden = aFlag;
1530 
1531 #if PLATFORM(DOM)
1532  _DOMElement.style.display = _isHidden ? "none" : "block";
1533 #endif
1534 
1535  if (aFlag)
1536  {
1537  var view = [_window firstResponder];
1538 
1539  if ([view isKindOfClass:[CPView class]])
1540  {
1541  do
1542  {
1543  if (self == view)
1544  {
1545  [_window makeFirstResponder:[self nextValidKeyView]];
1546  break;
1547  }
1548  }
1549  while (view = [view superview]);
1550  }
1551 
1552  [self _notifyViewDidHide];
1553  }
1554  else
1555  {
1556  [self setNeedsDisplay:YES];
1557  [self _notifyViewDidUnhide];
1558  }
1559 }
1560 
1561 - (void)_notifyViewDidHide
1562 {
1563  [self viewDidHide];
1564 
1565  var count = [_subviews count];
1566 
1567  while (count--)
1568  [_subviews[count] _notifyViewDidHide];
1569 }
1570 
1571 - (void)_notifyViewDidUnhide
1572 {
1573  [self viewDidUnhide];
1574 
1575  var count = [_subviews count];
1576 
1577  while (count--)
1578  [_subviews[count] _notifyViewDidUnhide];
1579 }
1580 
1584 - (BOOL)isHidden
1585 {
1586  return _isHidden;
1587 }
1588 
1589 - (void)setClipsToBounds:(BOOL)shouldClip
1590 {
1591  if (_clipsToBounds === shouldClip)
1592  return;
1593 
1594  _clipsToBounds = shouldClip;
1595 
1596 #if PLATFORM(DOM)
1597  _DOMElement.style.overflow = _clipsToBounds ? "hidden" : "visible";
1598 #endif
1599 }
1600 
1601 - (BOOL)clipsToBounds
1602 {
1603  return _clipsToBounds;
1604 }
1605 
1611 - (void)setAlphaValue:(float)anAlphaValue
1612 {
1613  if (_opacity == anAlphaValue)
1614  return;
1615 
1616  _opacity = anAlphaValue;
1617 
1618 #if PLATFORM(DOM)
1619 
1621  {
1622  if (anAlphaValue === 1.0)
1623  try { _DOMElement.style.removeAttribute("filter") } catch (anException) { }
1624  else
1625  _DOMElement.style.filter = "alpha(opacity=" + anAlphaValue * 100 + ")";
1626  }
1627  else
1628  _DOMElement.style.opacity = anAlphaValue;
1629 
1630 #endif
1631 }
1632 
1637 - (float)alphaValue
1638 {
1639  return _opacity;
1640 }
1641 
1646 - (BOOL)isHiddenOrHasHiddenAncestor
1647 {
1648  var view = self;
1649 
1650  while (view && ![view isHidden])
1651  view = [view superview];
1652 
1653  return view !== nil;
1654 }
1655 
1659 - (BOOL)_isVisible
1660 {
1661  return ![self isHiddenOrHasHiddenAncestor] && [[self window] isVisible];
1662 }
1663 
1673 - (void)viewDidHide
1674 {
1675 
1676 }
1677 
1687 - (void)viewDidUnhide
1688 {
1689 
1690 }
1691 
1697 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
1698 {
1699  return NO;
1700 }
1701 
1706 - (BOOL)hitTests
1707 {
1708  return _hitTests;
1709 }
1710 
1715 - (void)setHitTests:(BOOL)shouldHitTest
1716 {
1717  _hitTests = !!shouldHitTest;
1718 }
1719 
1725 - (CPView)hitTest:(CGPoint)aPoint
1726 {
1727  if (_isHidden || !_hitTests)
1728  return nil;
1729 
1730  var frame = _frame,
1731  sizeScale = [self _hierarchyScaleSize];
1732 
1733  if (_isScaled)
1734  frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale([_superview _hierarchyScaleSize].width, [_superview _hierarchyScaleSize].height));
1735  else
1736  frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale(sizeScale.width, sizeScale.height));
1737 
1738  if (!CGRectContainsPoint(frame, aPoint))
1739  return nil;
1740 
1741  var view = nil,
1742  i = _subviews.length,
1743  adjustedPoint = CGPointMake(aPoint.x - CGRectGetMinX(frame), aPoint.y - CGRectGetMinY(frame));
1744 
1745  if (_inverseBoundsTransform)
1746  {
1747  var affineTransform = CGAffineTransformMakeCopy(_inverseBoundsTransform);
1748 
1749  if (_isScaled)
1750  {
1751  affineTransform.tx *= [_superview _hierarchyScaleSize].width;
1752  affineTransform.ty *= [_superview _hierarchyScaleSize].height;
1753  }
1754  else
1755  {
1756  affineTransform.tx *= sizeScale.width;
1757  affineTransform.ty *= sizeScale.height;
1758  }
1759 
1760  adjustedPoint = CGPointApplyAffineTransform(adjustedPoint, affineTransform);
1761  }
1762 
1763 
1764  while (i--)
1765  if (view = [_subviews[i] hitTest:adjustedPoint])
1766  return view;
1767 
1768  return self;
1769 }
1770 
1774 - (BOOL)needsPanelToBecomeKey
1775 {
1776  return NO;
1777 }
1778 
1783 - (BOOL)mouseDownCanMoveWindow
1784 {
1785  return ![self isOpaque];
1786 }
1787 
1788 - (void)mouseDown:(CPEvent)anEvent
1789 {
1790  if ([self mouseDownCanMoveWindow])
1791  [super mouseDown:anEvent];
1792 }
1793 
1794 - (void)rightMouseDown:(CPEvent)anEvent
1795 {
1796  var menu = [self menuForEvent:anEvent];
1797 
1798  if (menu)
1799  [CPMenu popUpContextMenu:menu withEvent:anEvent forView:self];
1800  else if ([[self nextResponder] isKindOfClass:CPView])
1801  [super rightMouseDown:anEvent];
1802  else
1803  [[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
1804 }
1805 
1806 - (CPMenu)menuForEvent:(CPEvent)anEvent
1807 {
1808  return [self menu] || [[self class] defaultMenu];
1809 }
1810 
1815 - (void)setBackgroundColor:(CPColor)aColor
1816 {
1817  if (_backgroundColor == aColor)
1818  return;
1819 
1820  if (aColor == [CPNull null])
1821  aColor = nil;
1822 
1823  _backgroundColor = aColor;
1824 
1825 #if PLATFORM(DOM)
1826  var patternImage = [_backgroundColor patternImage],
1827  colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
1828  colorHasAlpha = colorExists && [_backgroundColor alphaComponent] < 1.0,
1829  supportsRGBA = CPFeatureIsCompatible(CPCSSRGBAFeature),
1830  colorNeedsDOMElement = colorHasAlpha && !supportsRGBA,
1831  amount = 0,
1832  slices;
1833 
1834  if ([patternImage isThreePartImage])
1835  {
1836  _backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage;
1837  amount = 3;
1838  }
1839  else if ([patternImage isNinePartImage])
1840  {
1841  _backgroundType = BackgroundNinePartImage;
1842  amount = 9;
1843  }
1844  else
1845  {
1846  _backgroundType = colorNeedsDOMElement ? BackgroundTransparentColor : BackgroundTrivialColor;
1847  amount = (colorNeedsDOMElement ? 1 : 0) - _DOMImageParts.length;
1848  }
1849 
1850  // Prepare multipart image data and reduce number of required DOM parts by number of empty slices in the multipart image to save needless DOM elements.
1851  if (_backgroundType === BackgroundVerticalThreePartImage || _backgroundType === BackgroundHorizontalThreePartImage || _backgroundType === BackgroundNinePartImage)
1852  {
1853  slices = [patternImage imageSlices];
1854 
1855  // We won't need more divs than there are slices.
1856  amount = MIN(amount, slices.length);
1857 
1858  for (var i = 0, count = slices.length; i < count; i++)
1859  {
1860  var image = slices[i],
1861  size = [image size];
1862 
1863  if (!size || (size.width == 0 && size.height == 0))
1864  size = nil;
1865 
1866  _DOMImageSizes[i] = size;
1867 
1868  // If there's a nil slice or a slice with no size, it won't need a div.
1869  if (!size)
1870  amount--;
1871  }
1872 
1873  // Now that we know how many divs we really need, compare that to number we actually have.
1874  amount -= _DOMImageParts.length;
1875  }
1876 
1877  // Make sure the number of divs we have match our needs.
1878  if (amount > 0)
1879  {
1880  while (amount--)
1881  {
1882  var DOMElement = DOMElementPrototype.cloneNode(false);
1883 
1884  DOMElement.style.zIndex = -1000;
1885 
1886  _DOMImageParts.push(DOMElement);
1887  _DOMElement.appendChild(DOMElement);
1888  }
1889  }
1890  else
1891  {
1892  amount = -amount;
1893  while (amount--)
1894  _DOMElement.removeChild(_DOMImageParts.pop());
1895  }
1896 
1897  if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
1898  {
1899  var colorCSS = colorExists ? [_backgroundColor cssString] : "";
1900 
1901  if (colorNeedsDOMElement)
1902  {
1903  _DOMElement.style.background = "";
1904  _DOMImageParts[0].style.background = [_backgroundColor cssString];
1905 
1906  if (patternImage)
1907  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[0], [patternImage size].width + "px", [patternImage size].height + "px");
1908 
1910  _DOMImageParts[0].style.filter = "alpha(opacity=" + [_backgroundColor alphaComponent] * 100 + ")";
1911  else
1912  _DOMImageParts[0].style.opacity = [_backgroundColor alphaComponent];
1913 
1914  var size = [self bounds].size;
1915  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
1916  }
1917  else
1918  _DOMElement.style.background = colorCSS;
1919 
1920  if (patternImage)
1921  CPDOMDisplayServerSetStyleBackgroundSize(_DOMElement, [patternImage size].width + "px", [patternImage size].height + "px");
1922  }
1923  else
1924  {
1925  var frameSize = _frame.size,
1926  partIndex = 0;
1927 
1928  for (var i = 0; i < slices.length; i++)
1929  {
1930  var size = _DOMImageSizes[i];
1931 
1932  if (!size)
1933  continue;
1934 
1935  var image = slices[i];
1936 
1937  // // If image was nil, size should have been nil too.
1938  // assert(image != nil);
1939 
1940  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height);
1941 
1942  _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")";
1943 
1944  if (!supportsRGBA)
1945  {
1947  try { _DOMImageParts[partIndex].style.removeAttribute("filter") } catch (anException) { }
1948  else
1949  _DOMImageParts[partIndex].style.opacity = 1.0;
1950  }
1951 
1952  partIndex++;
1953  }
1954 
1955  if (_backgroundType == BackgroundNinePartImage)
1956  {
1957  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1958  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
1959  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1960  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
1961  width = frameSize.width - left - right,
1962  height = frameSize.height - top - bottom;
1963 
1964  partIndex = 0;
1965 
1966  if (_DOMImageSizes[0])
1967  {
1968  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1969  partIndex++;
1970  }
1971  if (_DOMImageSizes[1])
1972  {
1973  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
1974  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[1].height);
1975  partIndex++;
1976  }
1977  if (_DOMImageSizes[2])
1978  {
1979  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1980  partIndex++;
1981  }
1982  if (_DOMImageSizes[3])
1983  {
1984  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1985  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
1986  partIndex++;
1987  }
1988  if (_DOMImageSizes[4])
1989  {
1990  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, top);
1991  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1992  partIndex++;
1993  }
1994  if (_DOMImageSizes[5])
1995  {
1996  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1997  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1998  partIndex++;
1999  }
2000  if (_DOMImageSizes[6])
2001  {
2002  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2003  partIndex++;
2004  }
2005  if (_DOMImageSizes[7])
2006  {
2007  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, left, 0.0);
2008  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
2009  partIndex++;
2010  }
2011  if (_DOMImageSizes[8])
2012  {
2013  CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2014  }
2015  }
2016  else if (_backgroundType == BackgroundVerticalThreePartImage)
2017  {
2018  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
2019  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
2020 
2021  partIndex = 0;
2022 
2023  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
2024  if (top)
2025  {
2026  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px");
2027  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2028  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, top);
2029  partIndex++;
2030  }
2031  if (_DOMImageSizes[1])
2032  {
2033  var height = frameSize.height - top - bottom;
2034 
2035  //_DOMImageParts[partIndex].style.backgroundSize = frameSize.width + "px " + height + "px";
2036  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px");
2037  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
2038  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, height);
2039  partIndex++;
2040  }
2041  if (bottom)
2042  {
2043  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", bottom + "px");
2044  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2045  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, bottom);
2046  }
2047  }
2048  else if (_backgroundType == BackgroundHorizontalThreePartImage)
2049  {
2050  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
2051  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
2052 
2053  partIndex = 0;
2054 
2055  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
2056  if (left)
2057  {
2058  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px");
2059  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2060  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, frameSize.height);
2061  partIndex++;
2062  }
2063  if (_DOMImageSizes[1])
2064  {
2065  var width = (frameSize.width - left - right);
2066 
2067  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], width + "px", frameSize.height + "px");
2068  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
2069  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, frameSize.height);
2070  partIndex++;
2071  }
2072  if (right)
2073  {
2074  CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], right + "px", frameSize.height + "px");
2075  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
2076  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, frameSize.height);
2077  }
2078  }
2079  }
2080 #endif
2081 }
2082 
2087 {
2088  return _backgroundColor;
2089 }
2090 
2091 // Converting Coordinates
2098 - (CGPoint)convertPoint:(CGPoint)aPoint fromView:(CPView)aView
2099 {
2100  if (aView === self)
2101  return aPoint;
2102 
2103  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
2104 }
2105 
2111 - (CGPoint)convertPointFromBase:(CGPoint)aPoint
2112 {
2113  return [self convertPoint:aPoint fromView:nil];
2114 }
2115 
2122 - (CGPoint)convertPoint:(CGPoint)aPoint toView:(CPView)aView
2123 {
2124  if (aView === self)
2125  return aPoint;
2126 
2127  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
2128 }
2129 
2130 
2136 - (CGPoint)convertPointToBase:(CGPoint)aPoint
2137 {
2138  return [self convertPoint:aPoint toView:nil];
2139 }
2140 
2147 - (CGSize)convertSize:(CGSize)aSize fromView:(CPView)aView
2148 {
2149  if (aView === self)
2150  return aSize;
2151 
2152  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(aView, self));
2153 }
2154 
2161 - (CGSize)convertSize:(CGSize)aSize toView:(CPView)aView
2162 {
2163  if (aView === self)
2164  return aSize;
2165 
2166  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(self, aView));
2167 }
2168 
2175 - (CGRect)convertRect:(CGRect)aRect fromView:(CPView)aView
2176 {
2177  if (self === aView)
2178  return aRect;
2179 
2180  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
2181 }
2182 
2188 - (CGRect)convertRectFromBase:(CGRect)aRect
2189 {
2190  return [self convertRect:aRect fromView:nil];
2191 }
2192 
2199 - (CGRect)convertRect:(CGRect)aRect toView:(CPView)aView
2200 {
2201  if (self === aView)
2202  return aRect;
2203 
2204  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
2205 }
2206 
2212 - (CGRect)convertRectToBase:(CGRect)aRect
2213 {
2214  return [self convertRect:aRect toView:nil];
2215 }
2216 
2229 - (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications
2230 {
2231  shouldPostFrameChangedNotifications = !!shouldPostFrameChangedNotifications;
2232 
2233  if (_postsFrameChangedNotifications === shouldPostFrameChangedNotifications)
2234  return;
2235 
2236  _postsFrameChangedNotifications = shouldPostFrameChangedNotifications;
2237 }
2238 
2242 - (BOOL)postsFrameChangedNotifications
2243 {
2244  return _postsFrameChangedNotifications;
2245 }
2246 
2259 - (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications
2260 {
2261  shouldPostBoundsChangedNotifications = !!shouldPostBoundsChangedNotifications;
2262 
2263  if (_postsBoundsChangedNotifications === shouldPostBoundsChangedNotifications)
2264  return;
2265 
2266  _postsBoundsChangedNotifications = shouldPostBoundsChangedNotifications;
2267 }
2268 
2274 - (BOOL)postsBoundsChangedNotifications
2275 {
2276  return _postsBoundsChangedNotifications;
2277 }
2278 
2289 - (void)dragImage:(CPImage)anImage at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2290 {
2291  [_window dragImage:anImage at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2292 }
2293 
2304 - (void)dragView:(CPView)aView at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2305 {
2306  [_window dragView:aView at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2307 }
2308 
2313 - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
2314 {
2315  if (!pasteboardTypes || ![pasteboardTypes count])
2316  return;
2317 
2318  var theWindow = [self window];
2319 
2320  [theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2321  [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
2322  [theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
2323 
2324  _registeredDraggedTypesArray = nil;
2325 }
2326 
2331 - (CPArray)registeredDraggedTypes
2332 {
2333  if (!_registeredDraggedTypesArray)
2334  _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
2335 
2336  return _registeredDraggedTypesArray;
2337 }
2338 
2342 - (void)unregisterDraggedTypes
2343 {
2344  [[self window] _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2345 
2346  _registeredDraggedTypes = [CPSet set];
2347  _registeredDraggedTypesArray = [];
2348 }
2349 
2354 - (void)drawRect:(CGRect)aRect
2355 {
2356 
2357 }
2358 
2359 // Scaling
2360 
2367 - (void)scaleUnitSquareToSize:(CGSize)aSize
2368 {
2369  if (!aSize)
2370  return;
2371 
2372  // Reset the bounds
2373  var bounds = CGRectMakeCopy([self bounds]);
2374  bounds.size.width *= _scaleSize.width;
2375  bounds.size.height *= _scaleSize.height;
2376 
2377  [self willChangeValueForKey:@"scaleSize"];
2378  _scaleSize = CGSizeMakeCopy([self scaleSize]);
2379  _scaleSize.height *= aSize.height;
2380  _scaleSize.width *= aSize.width;
2381  [self didChangeValueForKey:@"scaleSize"];
2382  _isScaled = YES;
2383 
2384  _hierarchyScaleSize = CGSizeMakeCopy([self _hierarchyScaleSize]);
2385  _hierarchyScaleSize.height *= aSize.height;
2386  _hierarchyScaleSize.width *= aSize.width;
2387 
2388  var scaleAffine = CGAffineTransformMakeScale(1.0 / _scaleSize.width, 1.0 / _scaleSize.height),
2389  newBounds = CGRectApplyAffineTransform(CGRectMakeCopy(bounds), scaleAffine);
2390 
2391  [self setBounds:newBounds];
2392 
2393  [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
2394 }
2395 
2400 - (void)_scaleSizeUnitSquareToSize:(CGSize)aSize
2401 {
2402  _hierarchyScaleSize = CGSizeMakeCopy([_superview _hierarchyScaleSize]);
2403 
2404  if (_isScaled)
2405  {
2406  _hierarchyScaleSize.width *= _scaleSize.width;
2407  _hierarchyScaleSize.height *= _scaleSize.height;
2408  }
2409 
2410  [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
2411 }
2412 
2416 - (CGSize)_hierarchyScaleSize
2417 {
2418  return _hierarchyScaleSize || CGSizeMake(1.0, 1.0);
2419 }
2420 
2424 - (void)_applyCSSScalingTranformations
2425 {
2426 #if PLATFORM(DOM)
2427  if (_isScaled)
2428  {
2429  var scale = [self scaleSize],
2430  browserPropertyTransform = CPBrowserStyleProperty(@"transform"),
2431  browserPropertyTransformOrigin = CPBrowserStyleProperty(@"transformOrigin");
2432 
2433  self._DOMElement.style[browserPropertyTransform] = 'scale(' + scale.width + ', ' + scale.height + ')';
2434  self._DOMElement.style[browserPropertyTransformOrigin] = '0 0';
2435 
2436  [self _setDisplayServerSetStyleSize:[self frameSize]];
2437  }
2438 #endif
2439 }
2440 
2441 // Displaying
2442 
2446 - (void)setNeedsDisplay:(BOOL)aFlag
2447 {
2448  if (aFlag)
2449  {
2450  [self _applyCSSScalingTranformations];
2451  [self setNeedsDisplayInRect:[self bounds]];
2452  }
2453 }
2454 
2459 - (void)setNeedsDisplayInRect:(CGRect)aRect
2460 {
2461  if (!(_viewClassFlags & CPViewHasCustomDrawRect))
2462  return;
2463 
2464  if (CGRectIsEmpty(aRect))
2465  return;
2466 
2467  if (_dirtyRect && !CGRectIsEmpty(_dirtyRect))
2468  _dirtyRect = CGRectUnion(aRect, _dirtyRect);
2469  else
2470  _dirtyRect = CGRectMakeCopy(aRect);
2471 
2472  _CPDisplayServerAddDisplayObject(self);
2473 }
2474 
2475 - (BOOL)needsDisplay
2476 {
2477  return _dirtyRect && !CGRectIsEmpty(_dirtyRect);
2478 }
2479 
2483 - (void)displayIfNeeded
2484 {
2485  if ([self needsDisplay])
2486  [self displayRect:_dirtyRect];
2487 }
2488 
2492 - (void)display
2493 {
2494  [self displayRect:[self visibleRect]];
2495 }
2496 
2497 - (void)displayIfNeededInRect:(CGRect)aRect
2498 {
2499  if ([self needsDisplay])
2500  [self displayRect:aRect];
2501 }
2502 
2507 - (void)displayRect:(CGRect)aRect
2508 {
2509  [self viewWillDraw];
2510 
2511  [self displayRectIgnoringOpacity:aRect inContext:nil];
2512 
2513  _dirtyRect = NULL;
2514 }
2515 
2516 - (void)displayRectIgnoringOpacity:(CGRect)aRect inContext:(CPGraphicsContext)aGraphicsContext
2517 {
2518  if ([self isHidden])
2519  return;
2520 
2521 #if PLATFORM(DOM)
2522  [self lockFocus];
2523 
2524  CGContextClearRect([[CPGraphicsContext currentContext] graphicsPort], aRect);
2525 
2526  [self drawRect:aRect];
2527  [self unlockFocus];
2528 #endif
2529 }
2530 
2531 - (void)viewWillDraw
2532 {
2533 }
2534 
2538 - (void)lockFocus
2539 {
2540  if (!_graphicsContext)
2541  {
2542  var graphicsPort = CGBitmapGraphicsContextCreate();
2543 
2544 #if PLATFORM(DOM)
2545  var width = CGRectGetWidth(_frame),
2546  height = CGRectGetHeight(_frame),
2547  devicePixelRatio = window.devicePixelRatio || 1,
2548  backingStoreRatio = CPBrowserBackingStorePixelRatio(graphicsPort);
2549 
2550  _highDPIRatio = CPViewHighDPIDrawingEnabled ? (devicePixelRatio / backingStoreRatio) : 1;
2551 
2552  _DOMContentsElement = graphicsPort.DOMElement;
2553 
2554  _DOMContentsElement.style.zIndex = -100;
2555 
2556  _DOMContentsElement.style.overflow = "hidden";
2557  _DOMContentsElement.style.position = "absolute";
2558  _DOMContentsElement.style.visibility = "visible";
2559 
2560  CPDOMDisplayServerSetSize(_DOMContentsElement, width * _highDPIRatio, height * _highDPIRatio);
2561 
2562  CPDOMDisplayServerSetStyleLeftTop(_DOMContentsElement, NULL, 0.0, 0.0);
2563  CPDOMDisplayServerSetStyleSize(_DOMContentsElement, width, height);
2564 
2565  // The performance implications of this aren't clear, but without this subviews might not be redrawn when this
2566  // view moves.
2568  _DOMElement.style.webkitTransform = 'translateX(0)';
2569 
2570  CPDOMDisplayServerAppendChild(_DOMElement, _DOMContentsElement);
2571 #endif
2572  _graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:graphicsPort flipped:YES];
2573  _needToSetTransformMatrix = YES;
2574  }
2575 
2576 #if PLATFORM(DOM)
2577  if (_needToSetTransformMatrix)
2578  [_graphicsContext graphicsPort].setTransform(_highDPIRatio, 0, 0 , _highDPIRatio, 0, 0);
2579 #endif
2580 
2581  _needToSetTransformMatrix = NO;
2582  [CPGraphicsContext setCurrentContext:_graphicsContext];
2583 
2584  CGContextSaveGState([_graphicsContext graphicsPort]);
2585 }
2586 
2590 - (void)unlockFocus
2591 {
2592  CGContextRestoreGState([_graphicsContext graphicsPort]);
2593 
2595 }
2596 
2597 - (void)setNeedsLayout
2598 {
2599  [self setNeedsLayout:YES];
2600 }
2601 
2602 - (void)setNeedsLayout:(BOOL)needLayout
2603 {
2604  if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews) || !needLayout)
2605  {
2606  _needsLayout = NO;
2607  return;
2608  }
2609 
2610  _needsLayout = YES;
2611 
2612  _CPDisplayServerAddLayoutObject(self);
2613 }
2614 
2615 - (BOOL)needsLayout
2616 {
2617  return _needsLayout;
2618 }
2619 
2620 - (void)layoutIfNeeded
2621 {
2622  if (_needsLayout)
2623  {
2624  _needsLayout = NO;
2625 
2626  [self layoutSubviews];
2627  }
2628 }
2629 
2630 - (void)layoutSubviews
2631 {
2632 }
2633 
2637 - (BOOL)isOpaque
2638 {
2639  return NO;
2640 }
2641 
2645 - (CGRect)visibleRect
2646 {
2647  if (!_superview)
2648  return _bounds;
2649 
2650  return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds);
2651 }
2652 
2653 // Scrolling
2654 /* @ignore */
2655 - (CPScrollView)_enclosingClipView
2656 {
2657  var superview = _superview,
2658  clipViewClass = [CPClipView class];
2659 
2660  while (superview && ![superview isKindOfClass:clipViewClass])
2661  superview = superview._superview;
2662 
2663  return superview;
2664 }
2665 
2670 - (void)scrollPoint:(CGPoint)aPoint
2671 {
2672  var clipView = [self _enclosingClipView];
2673 
2674  if (!clipView)
2675  return;
2676 
2677  [clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]];
2678 }
2679 
2685 - (BOOL)scrollRectToVisible:(CGRect)aRect
2686 {
2687  // Make sure we have a rect that exists.
2688  aRect = CGRectIntersection(aRect, _bounds);
2689 
2690  // If aRect is empty no scrolling required.
2691  if (CGRectIsEmpty(aRect))
2692  return NO;
2693 
2694  var enclosingClipView = [self _enclosingClipView];
2695 
2696  // If we're not in a clip view, then there isn't much we can do.
2697  if (!enclosingClipView)
2698  return NO;
2699 
2700  var documentView = [enclosingClipView documentView];
2701 
2702  // If the clip view doesn't have a document view, then there isn't much we can do.
2703  if (!documentView)
2704  return NO;
2705 
2706  // Get the document view visible rect and convert aRect to the document view's coordinate system
2707  var documentViewVisibleRect = [documentView visibleRect],
2708  rectInDocumentView = [self convertRect:aRect toView:documentView];
2709 
2710  // If already visible then no scrolling required.
2711  if (CGRectContainsRect(documentViewVisibleRect, rectInDocumentView))
2712  return NO;
2713 
2714  var scrollPoint = CGPointMakeCopy(documentViewVisibleRect.origin);
2715 
2716  // One of the following has to be true since our current visible rect didn't contain aRect.
2717  if (CGRectGetMinX(rectInDocumentView) < CGRectGetMinX(documentViewVisibleRect))
2718  scrollPoint.x = CGRectGetMinX(rectInDocumentView);
2719  else if (CGRectGetMaxX(rectInDocumentView) > CGRectGetMaxX(documentViewVisibleRect))
2720  scrollPoint.x += CGRectGetMaxX(rectInDocumentView) - CGRectGetMaxX(documentViewVisibleRect);
2721 
2722  if (CGRectGetMinY(rectInDocumentView) < CGRectGetMinY(documentViewVisibleRect))
2723  scrollPoint.y = CGRectGetMinY(rectInDocumentView);
2724  else if (CGRectGetMaxY(rectInDocumentView) > CGRectGetMaxY(documentViewVisibleRect))
2725  scrollPoint.y += CGRectGetMaxY(rectInDocumentView) - CGRectGetMaxY(documentViewVisibleRect);
2726 
2727  [enclosingClipView scrollToPoint:scrollPoint];
2728 
2729  return YES;
2730 }
2731 
2732 /*
2733  FIXME Not yet implemented
2734 */
2735 - (BOOL)autoscroll:(CPEvent)anEvent
2736 {
2737  return [[self superview] autoscroll:anEvent];
2738 }
2739 
2746 - (CGRect)adjustScroll:(CGRect)proposedVisibleRect
2747 {
2748  return proposedVisibleRect;
2749 }
2750 
2754 - (void)scrollRect:(CGRect)aRect by:(float)anAmount
2755 {
2756 
2757 }
2758 
2763 - (CPScrollView)enclosingScrollView
2764 {
2765  var superview = _superview,
2766  scrollViewClass = [CPScrollView class];
2767 
2768  while (superview && ![superview isKindOfClass:scrollViewClass])
2769  superview = superview._superview;
2770 
2771  return superview;
2772 }
2773 
2779 - (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
2780 {
2781  [aClipView scrollToPoint:aPoint];
2782 }
2783 
2789 - (void)reflectScrolledClipView:(CPClipView)aClipView
2790 {
2791 }
2792 
2796 - (BOOL)inLiveResize
2797 {
2798  return _inLiveResize;
2799 }
2800 
2810 - (void)viewWillStartLiveResize
2811 {
2812  _inLiveResize = YES;
2813 }
2814 
2825 - (void)viewDidEndLiveResize
2826 {
2827  _inLiveResize = NO;
2828 }
2829 
2830 @end
2831 
2832 @implementation CPView (KeyView)
2833 
2848 - (BOOL)performKeyEquivalent:(CPEvent)anEvent
2849 {
2850  var count = [_subviews count];
2851 
2852  // Is reverse iteration correct here? It matches the other (correct) code like hit testing.
2853  while (count--)
2854  if ([_subviews[count] performKeyEquivalent:anEvent])
2855  return YES;
2856 
2857  return NO;
2858 }
2859 
2860 - (BOOL)canBecomeKeyView
2861 {
2862  return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor];
2863 }
2864 
2865 - (CPView)nextKeyView
2866 {
2867  return _nextKeyView;
2868 }
2869 
2870 - (CPView)nextValidKeyView
2871 {
2872  var result = [self nextKeyView],
2873  resultUID = [result UID],
2874  unsuitableResults = {};
2875 
2876  while (result && ![result canBecomeKeyView])
2877  {
2878  unsuitableResults[resultUID] = 1;
2879  result = [result nextKeyView];
2880 
2881  resultUID = [result UID];
2882 
2883  // Did we get back to a key view we already ruled out due to ![result canBecomeKeyView]?
2884  if (unsuitableResults[resultUID])
2885  return nil;
2886  }
2887 
2888  return result;
2889 }
2890 
2891 - (CPView)previousKeyView
2892 {
2893  return _previousKeyView;
2894 }
2895 
2896 - (CPView)previousValidKeyView
2897 {
2898  var result = [self previousKeyView],
2899  firstResult = result;
2900 
2901  while (result && ![result canBecomeKeyView])
2902  {
2903  result = [result previousKeyView];
2904 
2905  // Cycled.
2906  if (result === firstResult)
2907  return nil;
2908  }
2909 
2910  return result;
2911 }
2912 
2913 - (void)_setPreviousKeyView:(CPView)previous
2914 {
2915  if (![previous isEqual:self])
2916  {
2917  var previousWindow = [previous window];
2918 
2919  if (!previousWindow || previousWindow === _window)
2920  {
2921  _previousKeyView = previous;
2922  return;
2923  }
2924  }
2925 
2926  _previousKeyView = nil;
2927 }
2928 
2929 - (void)setNextKeyView:(CPView)next
2930 {
2931  if (![next isEqual:self])
2932  {
2933  var nextWindow = [next window];
2934 
2935  if (!nextWindow || nextWindow === _window)
2936  {
2937  _nextKeyView = next;
2938  [_nextKeyView _setPreviousKeyView:self];
2939  return;
2940  }
2941  }
2942 
2943  _nextKeyView = nil;
2944 }
2945 
2946 @end
2947 
2949 
2953 - (void)setLayer:(CALayer)aLayer
2954 {
2955  if (_layer == aLayer)
2956  return;
2957 
2958  if (_layer)
2959  {
2960  _layer._owningView = nil;
2961 #if PLATFORM(DOM)
2962  _DOMElement.removeChild(_layer._DOMElement);
2963 #endif
2964  }
2965 
2966  _layer = aLayer;
2967 
2968  if (_layer)
2969  {
2970  var bounds = CGRectMakeCopy([self bounds]);
2971 
2972  [_layer _setOwningView:self];
2973 
2974 #if PLATFORM(DOM)
2975  _layer._DOMElement.style.zIndex = 100;
2976 
2977  _DOMElement.appendChild(_layer._DOMElement);
2978 #endif
2979  }
2980 }
2981 
2986 {
2987  return _layer;
2988 }
2989 
2994 - (void)setWantsLayer:(BOOL)aFlag
2995 {
2996  _wantsLayer = !!aFlag;
2997 }
2998 
3003 - (BOOL)wantsLayer
3004 {
3005  return _wantsLayer;
3006 }
3007 
3008 @end
3009 
3010 
3011 @implementation CPView (Scaling)
3012 
3018 - (void)setScaleSize:(CGSize)aSize
3019 {
3020  if (CGSizeEqualToSize(_scaleSize, aSize))
3021  return;
3022 
3023  var size = CGSizeMakeZero(),
3024  scale = CGSizeMakeCopy([self scaleSize]);
3025 
3026  size.height = aSize.height / scale.height;
3027  size.width = aSize.width / scale.width;
3028 
3029  [self scaleUnitSquareToSize:size];
3030  [self setNeedsDisplay:YES];
3031 }
3032 
3033 
3037 - (CGSize)scaleSize
3038 {
3039  return _scaleSize || CGSizeMake(1.0, 1.0);
3040 }
3041 
3042 @end
3043 
3044 @implementation CPView (Theming)
3045 #pragma mark Theme States
3046 
3047 - (unsigned)themeState
3048 {
3049  return _themeState;
3050 }
3051 
3052 - (BOOL)hasThemeState:(ThemeState)aState
3053 {
3054  if (aState.isa && [aState isKindOfClass:CPArray])
3055  return _themeState.hasThemeState.apply(_themeState, aState);
3056 
3057  return _themeState.hasThemeState(aState);
3058 }
3059 
3060 - (BOOL)setThemeState:(ThemeState)aState
3061 {
3062  if (aState && aState.isa && [aState isKindOfClass:CPArray])
3063  aState = CPThemeState.apply(null, aState);
3064 
3065  if (_themeState.hasThemeState(aState))
3066  return NO;
3067 
3068  _themeState = CPThemeState(_themeState, aState);
3069 
3070  [self setNeedsLayout];
3071  [self setNeedsDisplay:YES];
3072 
3073  return YES;
3074 }
3075 
3076 - (BOOL)unsetThemeState:(ThemeState)aState
3077 {
3078  if (aState && aState.isa && [aState isKindOfClass:CPArray])
3079  aState = CPThemeState.apply(null, aState);
3080 
3081  var oldThemeState = _themeState;
3082  _themeState = _themeState.without(aState);
3083 
3084  if (oldThemeState === _themeState)
3085  return NO;
3086 
3087  [self setNeedsLayout];
3088  [self setNeedsDisplay:YES];
3089 
3090  return YES;
3091 }
3092 
3093 - (BOOL)becomeFirstResponder
3094 {
3095  var r = [super becomeFirstResponder];
3096  if (r)
3097  [self _notifyViewDidBecomeFirstResponder];
3098  return r;
3099 }
3100 
3101 - (void)_notifyViewDidBecomeFirstResponder
3102 {
3103  [self setThemeState:CPThemeStateFirstResponder];
3104 
3105  var count = [_subviews count];
3106  while (count--)
3107  [_subviews[count] _notifyViewDidBecomeFirstResponder];
3108 }
3109 
3110 - (BOOL)resignFirstResponder
3111 {
3112  var r = [super resignFirstResponder];
3113  if (r)
3114  [self _notifyViewDidResignFirstResponder];
3115  return r;
3116 }
3117 
3118 - (void)_notifyViewDidResignFirstResponder
3119 {
3120  [self unsetThemeState:CPThemeStateFirstResponder];
3121 
3122  var count = [_subviews count];
3123  while (count--)
3124  [_subviews[count] _notifyViewDidResignFirstResponder];
3125 }
3126 
3127 - (void)_notifyWindowDidBecomeKey
3128 {
3129  [self setThemeState:CPThemeStateKeyWindow];
3130 
3131  var count = [_subviews count];
3132  while (count--)
3133  [_subviews[count] _notifyWindowDidBecomeKey];
3134 }
3135 
3136 - (void)_notifyWindowDidResignKey
3137 {
3138  [self unsetThemeState:CPThemeStateKeyWindow];
3139 
3140  var count = [_subviews count];
3141  while (count--)
3142  [_subviews[count] _notifyWindowDidResignKey];
3143 }
3144 
3145 #pragma mark Theme Attributes
3146 
3147 + (CPString)defaultThemeClass
3148 {
3149  return nil;
3150 }
3151 
3152 - (CPString)themeClass
3153 {
3154  if (_themeClass)
3155  return _themeClass;
3156 
3157  return [[self class] defaultThemeClass];
3158 }
3159 
3160 - (void)setThemeClass:(CPString)theClass
3161 {
3162  _themeClass = theClass;
3163 
3164  [self _loadThemeAttributes];
3165 
3166  [self setNeedsLayout];
3167  [self setNeedsDisplay:YES];
3168 }
3169 
3170 + (CPDictionary)themeAttributes
3171 {
3172  return nil;
3173 }
3174 
3175 + (CPArray)_themeAttributes
3176 {
3177  if (!CachedThemeAttributes)
3178  CachedThemeAttributes = {};
3179 
3180  var theClass = [self class],
3181  CPViewClass = [CPView class],
3182  attributes = [],
3183  nullValue = [CPNull null];
3184 
3185  for (; theClass && theClass !== CPViewClass; theClass = [theClass superclass])
3186  {
3187  var cachedAttributes = CachedThemeAttributes[class_getName(theClass)];
3188 
3189  if (cachedAttributes)
3190  {
3191  attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
3192  CachedThemeAttributes[[self className]] = attributes;
3193 
3194  break;
3195  }
3196 
3197  var attributeDictionary = [theClass themeAttributes];
3198 
3199  if (!attributeDictionary)
3200  continue;
3201 
3202  var attributeKeys = [attributeDictionary allKeys],
3203  attributeCount = attributeKeys.length;
3204 
3205  while (attributeCount--)
3206  {
3207  var attributeName = attributeKeys[attributeCount],
3208  attributeValue = [attributeDictionary objectForKey:attributeName];
3209 
3210  attributes.push(attributeValue === nullValue ? nil : attributeValue);
3211  attributes.push(attributeName);
3212  }
3213  }
3214 
3215  return attributes;
3216 }
3217 
3218 - (void)_loadThemeAttributes
3219 {
3220  var theClass = [self class],
3221  attributes = [theClass _themeAttributes],
3222  count = attributes.length;
3223 
3224  if (!count)
3225  return;
3226 
3227  var theme = [self theme],
3228  themeClass = [self themeClass];
3229 
3230  _themeAttributes = {};
3231 
3232  while (count--)
3233  {
3234  var attributeName = attributes[count--],
3235  attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
3236 
3237  [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
3238 
3239  _themeAttributes[attributeName] = attribute;
3240  }
3241 }
3242 
3243 - (void)setTheme:(CPTheme)aTheme
3244 {
3245  if (_theme === aTheme)
3246  return;
3247 
3248  _theme = aTheme;
3249 
3250  [self viewDidChangeTheme];
3251 }
3252 
3253 - (void)_setThemeIncludingDescendants:(CPTheme)aTheme
3254 {
3255  [self setTheme:aTheme];
3256  [[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
3257 }
3258 
3259 - (CPTheme)theme
3260 {
3261  return _theme;
3262 }
3263 
3264 - (void)viewDidChangeTheme
3265 {
3266  if (!_themeAttributes)
3267  return;
3268 
3269  var theme = [self theme],
3270  themeClass = [self themeClass];
3271 
3272  for (var attributeName in _themeAttributes)
3273  if (_themeAttributes.hasOwnProperty(attributeName))
3274  [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
3275 
3276  [self setNeedsLayout];
3277  [self setNeedsDisplay:YES];
3278 }
3279 
3280 - (CPDictionary)_themeAttributeDictionary
3281 {
3282  var dictionary = @{};
3283 
3284  if (_themeAttributes)
3285  {
3286  var theme = [self theme];
3287 
3288  for (var attributeName in _themeAttributes)
3289  if (_themeAttributes.hasOwnProperty(attributeName))
3290  [dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
3291  }
3292 
3293  return dictionary;
3294 }
3295 
3296 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
3297 {
3298  if (aState.isa && [aState isKindOfClass:CPArray])
3299  aState = CPThemeState.apply(null, aState);
3300 
3301  if (!_themeAttributes || !_themeAttributes[aName])
3302  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3303 
3304  var currentValue = [self currentValueForThemeAttribute:aName];
3305 
3306  [_themeAttributes[aName] setValue:aValue forState:aState];
3307 
3308  if ([self currentValueForThemeAttribute:aName] === currentValue)
3309  return;
3310 
3311  [self setNeedsDisplay:YES];
3312  [self setNeedsLayout];
3313 }
3314 
3315 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
3316 {
3317  if (!_themeAttributes || !_themeAttributes[aName])
3318  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3319 
3320  var currentValue = [self currentValueForThemeAttribute:aName];
3321 
3322  [_themeAttributes[aName] setValue:aValue];
3323 
3324  if ([self currentValueForThemeAttribute:aName] === currentValue)
3325  return;
3326 
3327  [self setNeedsDisplay:YES];
3328  [self setNeedsLayout];
3329 }
3330 
3331 - (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState
3332 {
3333  if (aState.isa && [aState isKindOfClass:CPArray])
3334  aState = CPThemeState.apply(null, aState);
3335 
3336  if (!_themeAttributes || !_themeAttributes[aName])
3337  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3338 
3339  return [_themeAttributes[aName] valueForState:aState];
3340 }
3341 
3342 - (id)valueForThemeAttribute:(CPString)aName
3343 {
3344  if (!_themeAttributes || !_themeAttributes[aName])
3345  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3346 
3347  return [_themeAttributes[aName] value];
3348 }
3349 
3350 - (id)currentValueForThemeAttribute:(CPString)aName
3351 {
3352  if (!_themeAttributes || !_themeAttributes[aName])
3353  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3354 
3355  return [_themeAttributes[aName] valueForState:_themeState];
3356 }
3357 
3358 - (BOOL)hasThemeAttribute:(CPString)aName
3359 {
3360  return (_themeAttributes && _themeAttributes[aName] !== undefined);
3361 }
3362 
3371 - (void)registerThemeValues:(CPArray)themeValues
3372 {
3373  for (var i = 0; i < themeValues.length; ++i)
3374  {
3375  var attributeValueState = themeValues[i],
3376  attribute = attributeValueState[0],
3377  value = attributeValueState[1],
3378  state = attributeValueState[2];
3379 
3380  if (state)
3381  [self setValue:value forThemeAttribute:attribute inState:state];
3382  else
3383  [self setValue:value forThemeAttribute:attribute];
3384  }
3385 }
3386 
3397 - (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
3398 {
3399  // Register inherited values first, then override those with the subtheme values.
3400  if (inheritedValues)
3401  [self registerThemeValues:inheritedValues];
3402 
3403  if (themeValues)
3404  [self registerThemeValues:themeValues];
3405 }
3406 
3407 - (CPView)createEphemeralSubviewNamed:(CPString)aViewName
3408 {
3409  return nil;
3410 }
3411 
3412 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
3413 {
3414  return CGRectMakeZero();
3415 }
3416 
3417 - (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
3418  positioned:(CPWindowOrderingMode)anOrderingMode
3419  relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
3420 {
3421  if (!_ephemeralSubviewsForNames)
3422  {
3423  _ephemeralSubviewsForNames = {};
3424  _ephemeralSubviews = [CPSet set];
3425  }
3426 
3427  var frame = [self rectForEphemeralSubviewNamed:aViewName];
3428 
3429  if (frame)
3430  {
3431  if (!_ephemeralSubviewsForNames[aViewName])
3432  {
3433  _ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
3434 
3435  [_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
3436 
3437  if (_ephemeralSubviewsForNames[aViewName])
3438  [self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
3439  }
3440 
3441  if (_ephemeralSubviewsForNames[aViewName])
3442  [_ephemeralSubviewsForNames[aViewName] setFrame:frame];
3443  }
3444  else if (_ephemeralSubviewsForNames[aViewName])
3445  {
3446  [_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
3447 
3448  [_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
3449  delete _ephemeralSubviewsForNames[aViewName];
3450  }
3451 
3452  return _ephemeralSubviewsForNames[aViewName];
3453 }
3454 
3455 - (CPView)ephemeralSubviewNamed:(CPString)aViewName
3456 {
3457  if (!_ephemeralSubviewsForNames)
3458  return nil;
3459 
3460  return (_ephemeralSubviewsForNames[aViewName] || nil);
3461 }
3462 
3463 @end
3464 
3465 var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
3466  CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews",
3467  CPViewBackgroundColorKey = @"CPViewBackgroundColor",
3468  CPViewBoundsKey = @"CPViewBoundsKey",
3469  CPViewFrameKey = @"CPViewFrameKey",
3470  CPViewHitTestsKey = @"CPViewHitTestsKey",
3471  CPViewToolTipKey = @"CPViewToolTipKey",
3472  CPViewIsHiddenKey = @"CPViewIsHiddenKey",
3473  CPViewOpacityKey = @"CPViewOpacityKey",
3474  CPViewSubviewsKey = @"CPViewSubviewsKey",
3475  CPViewSuperviewKey = @"CPViewSuperviewKey",
3476  CPViewTagKey = @"CPViewTagKey",
3477  CPViewThemeClassKey = @"CPViewThemeClassKey",
3478  CPViewThemeStateKey = @"CPViewThemeStateKey",
3479  CPViewWindowKey = @"CPViewWindowKey",
3480  CPViewNextKeyViewKey = @"CPViewNextKeyViewKey",
3481  CPViewPreviousKeyViewKey = @"CPViewPreviousKeyViewKey",
3482  CPReuseIdentifierKey = @"CPReuseIdentifierKey",
3483  CPViewScaleKey = @"CPViewScaleKey",
3484  CPViewSizeScaleKey = @"CPViewSizeScaleKey",
3485  CPViewIsScaledKey = @"CPViewIsScaledKey";
3486 
3487 @implementation CPView (CPCoding)
3488 
3494 - (id)initWithCoder:(CPCoder)aCoder
3495 {
3496  // We create the DOMElement "early" because there is a chance that we
3497  // will decode our superview before we are done decoding, at which point
3498  // we have to have an element to place in the tree. Perhaps there is
3499  // a more "elegant" way to do this...?
3500 #if PLATFORM(DOM)
3501  _DOMElement = DOMElementPrototype.cloneNode(false);
3502  AppKitTagDOMElement(self, _DOMElement);
3503 #endif
3504 
3505  // Also decode these "early".
3506  _frame = [aCoder decodeRectForKey:CPViewFrameKey];
3507  _bounds = [aCoder decodeRectForKey:CPViewBoundsKey];
3508 
3509  self = [super initWithCoder:aCoder];
3510 
3511  if (self)
3512  {
3513  // We have to manually check because it may be 0, so we can't use ||
3514  _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
3515  _identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey];
3516 
3517  _window = [aCoder decodeObjectForKey:CPViewWindowKey];
3518  _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
3519 
3520  // We have to manually add the subviews so that they will receive
3521  // viewWillMoveToSuperview: and viewDidMoveToSuperview:
3522  _subviews = [];
3523 
3524  var subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
3525 
3526  for (var i = 0, count = [subviews count]; i < count; ++i)
3527  {
3528  // addSubview won't do anything if the superview is already self, so clear it
3529  subviews[i]._superview = nil;
3530  [self addSubview:subviews[i]];
3531  }
3532 
3533  // FIXME: Should we encode/decode this?
3534  _registeredDraggedTypes = [CPSet set];
3535  _registeredDraggedTypesArray = [];
3536 
3537  // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded.
3538  // We make sure we don't override the value by checking if it was already set.
3539  if (_autoresizingMask === nil)
3540  _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
3541 
3542  _autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
3543 
3544  _hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey];
3545 
3546  _toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey];
3547 
3548  if (_toolTip)
3549  [self _installToolTipEventHandlers];
3550 
3551  _scaleSize = [aCoder containsValueForKey:CPViewScaleKey] ? [aCoder decodeSizeForKey:CPViewScaleKey] : CGSizeMake(1.0, 1.0);
3552  _hierarchyScaleSize = [aCoder containsValueForKey:CPViewSizeScaleKey] ? [aCoder decodeSizeForKey:CPViewSizeScaleKey] : CGSizeMake(1.0, 1.0);
3553  _isScaled = [aCoder containsValueForKey:CPViewIsScaledKey] ? [aCoder decodeBoolForKey:CPViewIsScaledKey] : NO;
3554 
3555  // DOM SETUP
3556 #if PLATFORM(DOM)
3557  _DOMImageParts = [];
3558  _DOMImageSizes = [];
3559 
3560  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(_frame), CGRectGetMinY(_frame));
3561  [self _setDisplayServerSetStyleSize:_frame.size];
3562 
3563  var index = 0,
3564  count = _subviews.length;
3565 
3566  for (; index < count; ++index)
3567  {
3568  CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement);
3569  //_subviews[index]._superview = self;
3570  }
3571 #endif
3572 
3573  [self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
3574 
3575  if ([aCoder containsValueForKey:CPViewOpacityKey])
3576  [self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
3577  else
3578  _opacity = 1.0;
3579 
3580  [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
3581  [self _setupViewFlags];
3582 
3583  _theme = [CPTheme defaultTheme];
3584  _themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
3585  _themeState = CPThemeState([aCoder decodeObjectForKey:CPViewThemeStateKey]);
3586  _themeAttributes = {};
3587 
3588  var theClass = [self class],
3589  themeClass = [self themeClass],
3590  attributes = [theClass _themeAttributes],
3591  count = attributes.length;
3592 
3593  while (count--)
3594  {
3595  var attributeName = attributes[count--];
3596 
3597  _themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass);
3598  }
3599 
3600  [self setNeedsDisplay:YES];
3601  [self setNeedsLayout];
3602  }
3603 
3604  return self;
3605 }
3606 
3611 - (void)encodeWithCoder:(CPCoder)aCoder
3612 {
3613  [super encodeWithCoder:aCoder];
3614 
3615  if (_tag !== -1)
3616  [aCoder encodeInt:_tag forKey:CPViewTagKey];
3617 
3618  [aCoder encodeRect:_frame forKey:CPViewFrameKey];
3619  [aCoder encodeRect:_bounds forKey:CPViewBoundsKey];
3620 
3621  // This will come out nil on the other side with decodeObjectForKey:
3622  if (_window !== nil)
3623  [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey];
3624 
3625  var count = [_subviews count],
3626  encodedSubviews = _subviews;
3627 
3628  if (count > 0 && [_ephemeralSubviews count] > 0)
3629  {
3630  encodedSubviews = [encodedSubviews copy];
3631 
3632  while (count--)
3633  if ([_ephemeralSubviews containsObject:encodedSubviews[count]])
3634  encodedSubviews.splice(count, 1);
3635  }
3636 
3637  if (encodedSubviews.length > 0)
3638  [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey];
3639 
3640  // This will come out nil on the other side with decodeObjectForKey:
3641  if (_superview !== nil)
3642  [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey];
3643 
3644  if (_autoresizingMask !== CPViewNotSizable)
3645  [aCoder encodeInt:_autoresizingMask forKey:CPViewAutoresizingMaskKey];
3646 
3647  if (!_autoresizesSubviews)
3648  [aCoder encodeBool:_autoresizesSubviews forKey:CPViewAutoresizesSubviewsKey];
3649 
3650  if (_backgroundColor !== nil)
3651  [aCoder encodeObject:_backgroundColor forKey:CPViewBackgroundColorKey];
3652 
3653  if (_hitTests !== YES)
3654  [aCoder encodeBool:_hitTests forKey:CPViewHitTestsKey];
3655 
3656  if (_opacity !== 1.0)
3657  [aCoder encodeFloat:_opacity forKey:CPViewOpacityKey];
3658 
3659  if (_isHidden)
3660  [aCoder encodeBool:_isHidden forKey:CPViewIsHiddenKey];
3661 
3662  if (_toolTip)
3663  [aCoder encodeObject:_toolTip forKey:CPViewToolTipKey];
3664 
3665  var nextKeyView = [self nextKeyView];
3666 
3667  if (nextKeyView !== nil && ![nextKeyView isEqual:self])
3668  [aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
3669 
3670  var previousKeyView = [self previousKeyView];
3671 
3672  if (previousKeyView !== nil && ![previousKeyView isEqual:self])
3673  [aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
3674 
3675  [aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
3676  [aCoder encodeObject:String(_themeState) forKey:CPViewThemeStateKey];
3677 
3678  for (var attributeName in _themeAttributes)
3679  if (_themeAttributes.hasOwnProperty(attributeName))
3680  CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
3681 
3682  if (_identifier)
3683  [aCoder encodeObject:_identifier forKey:CPReuseIdentifierKey];
3684 
3685  [aCoder encodeSize:[self scaleSize] forKey:CPViewScaleKey];
3686  [aCoder encodeSize:[self _hierarchyScaleSize] forKey:CPViewSizeScaleKey];
3687  [aCoder encodeBool:_isScaled forKey:CPViewIsScaledKey];
3688 }
3689 
3690 @end
3691 
3692 var _CPViewFullScreenModeStateMake = function(aView)
3693 {
3694  var superview = aView._superview;
3695 
3696  return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview };
3697 };
3698 
3699 var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
3700 {
3701  var transform = CGAffineTransformMakeIdentity(),
3702  sameWindow = YES,
3703  fromWindow = nil,
3704  toWindow = nil;
3705 
3706  if (fromView)
3707  {
3708  var view = fromView;
3709 
3710  // FIXME: This doesn't handle the case when the outside views are equal.
3711  // If we have a fromView, "climb up" the view tree until
3712  // we hit the root node or we hit the toLayer.
3713  while (view && view != toView)
3714  {
3715  var frame = view._frame;
3716 
3717  if (view._isScaled)
3718  {
3719  var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
3720  CGAffineTransformConcatTo(transform, affineZoom, transform);
3721  }
3722 
3723  transform.tx += CGRectGetMinX(frame);
3724  transform.ty += CGRectGetMinY(frame);
3725 
3726  if (view._boundsTransform)
3727  {
3728  var inverseBoundsTransform = CGAffineTransformMakeCopy(view._boundsTransform);
3729 
3730  if (view._isScaled)
3731  {
3732  var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
3733  CGAffineTransformConcatTo(inverseBoundsTransform, affineZoom, inverseBoundsTransform);
3734  }
3735 
3736  CGAffineTransformConcatTo(transform, inverseBoundsTransform, transform);
3737  }
3738 
3739  view = view._superview;
3740  }
3741 
3742  // If we hit toView, then we're done.
3743  if (view === toView)
3744  {
3745  return transform;
3746  }
3747  else if (fromView && toView)
3748  {
3749  fromWindow = [fromView window];
3750  toWindow = [toView window];
3751 
3752  if (fromWindow && toWindow && fromWindow !== toWindow)
3753  sameWindow = NO;
3754  }
3755  }
3756 
3757  // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way.
3758  var view = toView,
3759  transform2 = CGAffineTransformMakeIdentity();
3760 
3761  while (view && view != fromView)
3762  {
3763  var frame = CGRectMakeCopy(view._frame);
3764 
3765  // FIXME : For now we don't care about rotate transform and so on
3766  if (view._isScaled)
3767  {
3768  transform2.a *= 1 / view._scaleSize.width;
3769  transform2.d *= 1 / view._scaleSize.height;
3770  }
3771 
3772  transform2.tx += CGRectGetMinX(frame) * transform2.a;
3773  transform2.ty += CGRectGetMinY(frame) * transform2.d;
3774 
3775  if (view._boundsTransform)
3776  {
3777  var inverseBoundsTransform = CGAffineTransformMakeIdentity();
3778  inverseBoundsTransform.tx -= view._inverseBoundsTransform.tx * transform2.a;
3779  inverseBoundsTransform.ty -= view._inverseBoundsTransform.ty * transform2.d;
3780 
3781  CGAffineTransformConcatTo(transform2, inverseBoundsTransform, transform2);
3782  }
3783 
3784  view = view._superview;
3785  }
3786 
3787  transform2.tx = -transform2.tx;
3788  transform2.ty = -transform2.ty;
3789 
3790  if (view === fromView)
3791  {
3792  // toView is inside of fromView
3793  return transform2;
3794  }
3795 
3796  CGAffineTransformConcatTo(transform, transform2, transform);
3797 
3798  return transform;
3799 
3800 
3801 
3802 /* var views = [],
3803  view = toView;
3804 
3805  while (view)
3806  {
3807  views.push(view);
3808  view = view._superview;
3809  }
3810 
3811  var index = views.length;
3812 
3813  while (index--)
3814  {
3815  var frame = views[index]._frame;
3816 
3817  transform.tx -= CGRectGetMinX(frame);
3818  transform.ty -= CGRectGetMinY(frame);
3819  }*/
3820 
3821  return transform;
3822 };
3823 
3825 
3829 - (CPString)identifier
3830 {
3831  return _identifier;
3832 }
3833 
3837 - (void)setIdentifier:(CPString)aValue
3838 {
3839  _identifier = aValue;
3840 }
3841 
3845 - (CPString)toolTip
3846 {
3847  return _toolTip;
3848 }
3849 
3850 @end