API  0.9.7-1
 All Classes Files Functions Variables 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 @class _CPToolTip
26 @class CPWindow
27 @class _CPMenuItemView
28 @class CPPlatformWindow
29 @class CPMenu
30 @class CPClipView
31 @class CPScrollView
32 
33 @global appkit_tag_dom_elements
34 
35 #if PLATFORM(DOM)
36 
37 if (typeof(appkit_tag_dom_elements) !== "undefined" && appkit_tag_dom_elements)
38 {
39  AppKitTagDOMElement = function(owner, element)
40  {
41  element.setAttribute("data-cappuccino-view", [owner className]);
42  element.setAttribute("data-cappuccino-uid", [owner UID]);
43  }
44 }
45 else
46 {
47  AppKitTagDOMElement = function(owner, element)
48  {
49  // By default, do nothing.
50  }
51 }
52 
53 #endif
54 
55 /*
56  @global
57  @group CPViewAutoresizingMasks
58  The default resizingMask, the view will not resize or reposition itself.
59 */
60 CPViewNotSizable = 0;
61 /*
62  @global
63  @group CPViewAutoresizingMasks
64  Allow for flexible space on the left hand side of the view.
65 */
66 CPViewMinXMargin = 1;
67 /*
68  @global
69  @group CPViewAutoresizingMasks
70  The view should grow and shrink horizontally with its parent view.
71 */
72 CPViewWidthSizable = 2;
73 /*
74  @global
75  @group CPViewAutoresizingMasks
76  Allow for flexible space to the right hand side of the view.
77 */
78 CPViewMaxXMargin = 4;
79 /*
80  @global
81  @group CPViewAutoresizingMasks
82  Allow for flexible space above the view.
83 */
84 CPViewMinYMargin = 8;
85 /*
86  @global
87  @group CPViewAutoresizingMasks
88  The view should grow and shrink vertically with its parent view.
89 */
90 CPViewHeightSizable = 16;
91 /*
92  @global
93  @group CPViewAutoresizingMasks
94  Allow for flexible space below the view.
95 */
96 CPViewMaxYMargin = 32;
97 
98 CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
99 CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
100 
101 var CachedNotificationCenter = nil,
102  CachedThemeAttributes = nil;
103 
104 #if PLATFORM(DOM)
105 var DOMElementPrototype = nil,
106 
107  BackgroundTrivialColor = 0,
108  BackgroundVerticalThreePartImage = 1,
109  BackgroundHorizontalThreePartImage = 2,
110  BackgroundNinePartImage = 3,
111  BackgroundTransparentColor = 4;
112 #endif
113 
114 var CPViewFlags = { },
115  CPViewHasCustomDrawRect = 1 << 0,
116  CPViewHasCustomLayoutSubviews = 1 << 1;
117 
118 
136 @implementation CPView : CPResponder
137 {
138  CPWindow _window;
139 
140  CPView _superview;
141  CPArray _subviews;
142 
143  CPGraphicsContext _graphicsContext;
144 
145  int _tag;
146  CPString _identifier;
147 
148  CGRect _frame;
149  CGRect _bounds;
150  CGAffineTransform _boundsTransform;
151  CGAffineTransform _inverseBoundsTransform;
152 
153  CPSet _registeredDraggedTypes;
154  CPArray _registeredDraggedTypesArray;
155 
156  BOOL _isHidden;
157  BOOL _hitTests;
158  BOOL _clipsToBounds;
159 
160  BOOL _postsFrameChangedNotifications;
161  BOOL _postsBoundsChangedNotifications;
162  BOOL _inhibitFrameAndBoundsChangedNotifications;
163  BOOL _inLiveResize;
164 
165 #if PLATFORM(DOM)
166  DOMElement _DOMElement;
167  DOMElement _DOMContentsElement;
168 
169  CPArray _DOMImageParts;
170  CPArray _DOMImageSizes;
171 
172  unsigned _backgroundType;
173 #endif
174 
175  CGRect _dirtyRect;
176 
177  float _opacity;
178  CPColor _backgroundColor;
179 
180  BOOL _autoresizesSubviews;
181  unsigned _autoresizingMask;
182 
183  CALayer _layer;
184  BOOL _wantsLayer;
185 
186  // Full Screen State
187  BOOL _isInFullScreenMode;
188 
189  _CPViewFullScreenModeState _fullScreenModeState;
190 
191  // Zoom Support
192  BOOL _isScaled;
193  CGSize _hierarchyScaleSize;
194  CGSize _scaleSize;
195 
196  // Layout Support
197  BOOL _needsLayout;
198  JSObject _ephemeralSubviews;
199 
200  // Theming Support
201  CPTheme _theme;
202  CPString _themeClass;
203  JSObject _themeAttributes;
204  unsigned _themeState;
205 
206  JSObject _ephemeralSubviewsForNames;
207  CPSet _ephereralSubviews;
208 
209  // Key View Support
210  CPView _nextKeyView;
211  CPView _previousKeyView;
212 
213  unsigned _viewClassFlags;
214 
215  // ToolTips
216  CPString _toolTip;
217  Function _toolTipFunctionIn;
218  Function _toolTipFunctionOut;
219  BOOL _toolTipInstalled;
220 }
221 
222 /*
223  Private method for Objective-J.
224  @ignore
225 */
226 + (void)initialize
227 {
228  if (self !== [CPView class])
229  return;
230 
231 #if PLATFORM(DOM)
232  DOMElementPrototype = document.createElement("div");
233 
234  var style = DOMElementPrototype.style;
235 
236  style.overflow = "hidden";
237  style.position = "absolute";
238  style.visibility = "visible";
239  style.zIndex = 0;
240 #endif
241 
242  CachedNotificationCenter = [CPNotificationCenter defaultCenter];
243 }
244 
245 + (Class)_binderClassForBinding:(CPString)aBinding
246 {
247  if ([aBinding hasPrefix:CPHiddenBinding])
248  return [CPMultipleValueOrBinding class];
249 
250  return [super _binderClassForBinding:aBinding];
251 }
252 
253 - (void)_setupViewFlags
254 {
255  var theClass = [self class],
256  classUID = [theClass UID];
257 
258  if (CPViewFlags[classUID] === undefined)
259  {
260  var flags = 0;
261 
262  if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
263  flags |= CPViewHasCustomDrawRect;
264 
265  if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
266  flags |= CPViewHasCustomLayoutSubviews;
267 
268  CPViewFlags[classUID] = flags;
269  }
270 
271  _viewClassFlags = CPViewFlags[classUID];
272 }
273 
274 - (void)_setupToolTipHandlers
275 {
276  _toolTipInstalled = NO;
277  _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
278  _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
279 }
280 
281 + (CPSet)keyPathsForValuesAffectingFrame
282 {
283  return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
284 }
285 
286 + (CPSet)keyPathsForValuesAffectingBounds
287 {
288  return [CPSet setWithObjects:@"boundsOrigin", @"boundsSize"];
289 }
290 
291 + (CPMenu)defaultMenu
292 {
293  return nil;
294 }
295 
296 - (id)init
297 {
298  return [self initWithFrame:CGRectMakeZero()];
299 }
300 
305 - (id)initWithFrame:(CGRect)aFrame
306 {
307  self = [super init];
308 
309  if (self)
310  {
311  var width = CGRectGetWidth(aFrame),
312  height = CGRectGetHeight(aFrame);
313 
314  _subviews = [];
315  _registeredDraggedTypes = [CPSet set];
316  _registeredDraggedTypesArray = [];
317 
318  _tag = -1;
319 
320  _frame = CGRectMakeCopy(aFrame);
321  _bounds = CGRectMake(0.0, 0.0, width, height);
322 
323  _autoresizingMask = CPViewNotSizable;
324  _autoresizesSubviews = YES;
325  _clipsToBounds = YES;
326 
327  _opacity = 1.0;
328  _isHidden = NO;
329  _hitTests = YES;
330 
331  _hierarchyScaleSize = CGSizeMake(1.0 , 1.0);
332  _scaleSize = CGSizeMake(1.0, 1.0);
333  _isScaled = NO;
334 
335 #if PLATFORM(DOM)
336  _DOMElement = DOMElementPrototype.cloneNode(false);
337  AppKitTagDOMElement(self, _DOMElement);
338 
339  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(aFrame), CGRectGetMinY(aFrame));
340  CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
341 
342  _DOMImageParts = [];
343  _DOMImageSizes = [];
344 #endif
345 
346  _theme = [CPTheme defaultTheme];
347  _themeState = CPThemeStateNormal;
348 
349  [self _setupToolTipHandlers];
350  [self _setupViewFlags];
351 
352  [self _loadThemeAttributes];
353  }
354 
355  return self;
356 }
357 
358 
364 - (void)setToolTip:(CPString)aToolTip
365 {
366  if (_toolTip == aToolTip)
367  return;
368 
369  if (aToolTip && ![aToolTip isKindOfClass:CPString])
370  aToolTip = [aToolTip description];
371 
372  _toolTip = aToolTip;
373 
374  if (_toolTip)
375  [self _installToolTipEventHandlers];
376  else
377  [self _uninstallToolTipEventHandlers];
378 }
379 
384 - (void)_installToolTipEventHandlers
385 {
386  if (_toolTipInstalled)
387  return;
388 
389 #if PLATFORM(DOM)
390  if (_DOMElement.addEventListener)
391  {
392  _DOMElement.addEventListener("mouseover", _toolTipFunctionIn, YES);
393  _DOMElement.addEventListener("keypress", _toolTipFunctionOut, YES);
394  _DOMElement.addEventListener("mouseout", _toolTipFunctionOut, YES);
395  }
396  else if (_DOMElement.attachEvent)
397  {
398  _DOMElement.attachEvent("onmouseover", _toolTipFunctionIn);
399  _DOMElement.attachEvent("onkeypress", _toolTipFunctionOut);
400  _DOMElement.attachEvent("onmouseout", _toolTipFunctionOut);
401  }
402 #endif
403 
404  _toolTipInstalled = YES;
405 }
406 
411 - (void)_uninstallToolTipEventHandlers
412 {
413  if (!_toolTipInstalled)
414  return;
415 
416 #if PLATFORM(DOM)
417  if (_DOMElement.removeEventListener)
418  {
419  _DOMElement.removeEventListener("mouseover", _toolTipFunctionIn, YES);
420  _DOMElement.removeEventListener("keypress", _toolTipFunctionOut, YES);
421  _DOMElement.removeEventListener("mouseout", _toolTipFunctionOut, YES);
422  }
423  else if (_DOMElement.detachEvent)
424  {
425  _DOMElement.detachEvent("onmouseover", _toolTipFunctionIn);
426  _DOMElement.detachEvent("onkeypress", _toolTipFunctionOut);
427  _DOMElement.detachEvent("onmouseout", _toolTipFunctionOut);
428  }
429 #endif
430 
431  _toolTipInstalled = NO;
432 }
433 
438 - (CPView)superview
439 {
440  return _superview;
441 }
442 
447 - (CPArray)subviews
448 {
449  return [_subviews copy];
450 }
451 
455 - (CPWindow)window
456 {
457  return _window;
458 }
459 
464 - (void)addSubview:(CPView)aSubview
465 {
466  [self _insertSubview:aSubview atIndex:CPNotFound];
467 }
468 
475 - (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
476 {
477  var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound;
478 
479  // In other words, if no view, then either all the way at the bottom or all the way at the top.
480  if (index === CPNotFound)
481  index = (anOrderingMode === CPWindowAbove) ? [_subviews count] : 0;
482 
483  // else, if we have a view, above if above.
484  else if (anOrderingMode === CPWindowAbove)
485  ++index;
486 
487  [self _insertSubview:aSubview atIndex:index];
488 }
489 
490 /* @ignore */
491 - (void)_insertSubview:(CPView)aSubview atIndex:(int)anIndex
492 {
493  if (aSubview === self)
494  [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"];
495 #if DEBUG
496  if (!aSubview._superview && _subviews.indexOf(aSubview) !== CPNotFound)
497  [CPException raise:CPInvalidArgumentException reason:"can't insert a subview in duplicate (probably partially decoded)"];
498 #endif
499 
500  // We will have to adjust the z-index of all views starting at this index.
501  var count = _subviews.length;
502 
503  // Dirty the key view loop, in case the window wants to auto recalculate it
504  [[self window] _dirtyKeyViewLoop];
505 
506  // If this is already one of our subviews, remove it.
507  if (aSubview._superview == self)
508  {
509  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
510 
511  // FIXME: should this be anIndex >= count? (last one)
512  if (index === anIndex || index === count - 1 && anIndex === count)
513  return;
514 
515  [_subviews removeObjectAtIndex:index];
516 
517 #if PLATFORM(DOM)
518  CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement);
519 #endif
520 
521  if (anIndex > index)
522  --anIndex;
523 
524  //We've effectively made the subviews array shorter, so represent that.
525  --count;
526  }
527  else
528  {
529  // Remove the view from its previous superview.
530  [aSubview removeFromSuperview];
531 
532  // Set the subview's window to our own.
533  [aSubview _setWindow:_window];
534 
535  // Notify the subview that it will be moving.
536  [aSubview viewWillMoveToSuperview:self];
537 
538  // Set ourselves as the superview.
539  aSubview._superview = self;
540  }
541 
542  if (anIndex === CPNotFound || anIndex >= count)
543  {
544  _subviews.push(aSubview);
545 
546 #if PLATFORM(DOM)
547  // Attach the actual node.
548  CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement);
549 #endif
550  }
551  else
552  {
553  _subviews.splice(anIndex, 0, aSubview);
554 
555 #if PLATFORM(DOM)
556  // Attach the actual node.
557  CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement);
558 #endif
559  }
560 
561  [aSubview setNextResponder:self];
562  [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]];
563 
564  // If the subview is not hidden and one of its ancestors is hidden,
565  // notify the subview that it is now hidden.
566  if (![aSubview isHidden] && [self isHiddenOrHasHiddenAncestor])
567  [aSubview _notifyViewDidHide];
568 
569  [aSubview viewDidMoveToSuperview];
570 
571  [self didAddSubview:aSubview];
572 }
573 
578 - (void)didAddSubview:(CPView)aSubview
579 {
580 }
581 
586 - (void)removeFromSuperview
587 {
588  if (!_superview)
589  return;
590 
591  // Dirty the key view loop, in case the window wants to auto recalculate it
592  [[self window] _dirtyKeyViewLoop];
593 
594  [_superview willRemoveSubview:self];
595 
596  [_superview._subviews removeObjectIdenticalTo:self];
597 
598 #if PLATFORM(DOM)
599  CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
600 #endif
601 
602  // If the view is not hidden and one of its ancestors is hidden,
603  // notify the view that it is now unhidden.
604  if (!_isHidden && [_superview isHiddenOrHasHiddenAncestor])
605  [self _notifyViewDidUnhide];
606 
607  _superview = nil;
608 
609  [self _setWindow:nil];
610 }
611 
617 - (void)replaceSubview:(CPView)aSubview with:(CPView)aView
618 {
619  if (aSubview._superview !== self)
620  return;
621 
622  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
623 
624  [aSubview removeFromSuperview];
625 
626  [self _insertSubview:aView atIndex:index];
627 }
628 
629 - (void)setSubviews:(CPArray)newSubviews
630 {
631  if (!newSubviews)
632  [CPException raise:CPInvalidArgumentException reason:"newSubviews cannot be nil in -[CPView setSubviews:]"];
633 
634  // Trivial Case 0: Same array somehow
635  if ([_subviews isEqual:newSubviews])
636  return;
637 
638  // Trivial Case 1: No current subviews, simply add all new subviews.
639  if ([_subviews count] === 0)
640  {
641  var index = 0,
642  count = [newSubviews count];
643 
644  for (; index < count; ++index)
645  [self addSubview:newSubviews[index]];
646 
647  return;
648  }
649 
650  // Trivial Case 2: No new subviews, simply remove all current subviews.
651  if ([newSubviews count] === 0)
652  {
653  var count = [_subviews count];
654 
655  while (count--)
656  [_subviews[count] removeFromSuperview];
657 
658  return;
659  }
660 
661  // Find out the views that were removed.
662  var removedSubviews = [CPMutableSet setWithArray:_subviews];
663 
664  [removedSubviews removeObjectsInArray:newSubviews];
665  [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
666 
667  // Find out which views need to be added.
668  var addedSubviews = [CPMutableSet setWithArray:newSubviews];
669 
670  [addedSubviews removeObjectsInArray:_subviews];
671 
672  var addedSubview = nil,
673  addedSubviewEnumerator = [addedSubviews objectEnumerator];
674 
675  while ((addedSubview = [addedSubviewEnumerator nextObject]) !== nil)
676  [self addSubview:addedSubview];
677 
678  // If the order is fine, no need to reorder.
679  if ([_subviews isEqual:newSubviews])
680  return;
681 
682  _subviews = [newSubviews copy];
683 
684 #if PLATFORM(DOM)
685  var index = 0,
686  count = [_subviews count];
687 
688  for (; index < count; ++index)
689  {
690  var subview = _subviews[index];
691 
692  CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement);
693  CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement);
694  }
695 #endif
696 }
697 
698 /* @ignore */
699 - (void)_setWindow:(CPWindow)aWindow
700 {
701  if (_window === aWindow)
702  return;
703 
704  [[self window] _dirtyKeyViewLoop];
705 
706  // Clear out first responder if we're the first responder and leaving.
707  if ([_window firstResponder] === self)
708  [_window makeFirstResponder:nil];
709 
710  // Notify the view and its subviews
711  [self viewWillMoveToWindow:aWindow];
712 
713  // Unregister the drag events from the current window and register
714  // them in the new window.
715  if (_registeredDraggedTypes)
716  {
717  [_window _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
718  [aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
719  }
720 
721  _window = aWindow;
722 
723  var count = [_subviews count];
724 
725  while (count--)
726  [_subviews[count] _setWindow:aWindow];
727 
728  [self viewDidMoveToWindow];
729 
730  [[self window] _dirtyKeyViewLoop];
731 }
732 
737 - (BOOL)isDescendantOf:(CPView)aView
738 {
739  var view = self;
740 
741  do
742  {
743  if (view == aView)
744  return YES;
745  } while(view = [view superview])
746 
747  return NO;
748 }
749 
753 - (void)viewDidMoveToSuperview
754 {
755 // if (_graphicsContext)
756  [self setNeedsDisplay:YES];
757 }
758 
762 - (void)viewDidMoveToWindow
763 {
764 }
765 
770 - (void)viewWillMoveToSuperview:(CPView)aView
771 {
772 }
773 
778 - (void)viewWillMoveToWindow:(CPWindow)aWindow
779 {
780 }
781 
786 - (void)willRemoveSubview:(CPView)aView
787 {
788 }
789 
794 - (CPMenuItem)enclosingMenuItem
795 {
796  var view = self;
797 
798  while (view && ![view isKindOfClass:[_CPMenuItemView class]])
799  view = [view superview];
800 
801  if (view)
802  return view._menuItem;
803 
804  return nil;
805 /* var view = self,
806  enclosingMenuItem = _enclosingMenuItem;
807 
808  while (!enclosingMenuItem && (view = view._enclosingMenuItem))
809  view = [view superview];
810 
811  return enclosingMenuItem;*/
812 }
813 
814 - (void)setTag:(CPInteger)aTag
815 {
816  _tag = aTag;
817 }
818 
819 - (CPInteger)tag
820 {
821  return _tag;
822 }
823 
824 - (CPView)viewWithTag:(CPInteger)aTag
825 {
826  if ([self tag] == aTag)
827  return self;
828 
829  var index = 0,
830  count = _subviews.length;
831 
832  for (; index < count; ++index)
833  {
834  var view = [_subviews[index] viewWithTag:aTag];
835 
836  if (view)
837  return view;
838  }
839 
840  return nil;
841 }
842 
847 - (BOOL)isFlipped
848 {
849  return YES;
850 }
851 
859 - (void)setFrame:(CGRect)aFrame
860 {
861  if (CGRectEqualToRect(_frame, aFrame))
862  return;
863 
864  _inhibitFrameAndBoundsChangedNotifications = YES;
865 
866  [self setFrameOrigin:aFrame.origin];
867  [self setFrameSize:aFrame.size];
868 
869  _inhibitFrameAndBoundsChangedNotifications = NO;
870 
871  if (_postsFrameChangedNotifications)
872  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
873 }
874 
879 - (CGRect)frame
880 {
881  return CGRectMakeCopy(_frame);
882 }
883 
884 - (CGPoint)frameOrigin
885 {
886  return CGPointMakeCopy(_frame.origin);
887 }
888 
889 - (CGSize)frameSize
890 {
891  return CGSizeMakeCopy(_frame.size);
892 }
893 
901 - (void)setCenter:(CGPoint)aPoint
902 {
903  [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
904 }
905 
910 - (CGPoint)center
911 {
912  return CGPointMake(_frame.size.width / 2.0 + _frame.origin.x, _frame.size.height / 2.0 + _frame.origin.y);
913 }
914 
922 - (void)setFrameOrigin:(CGPoint)aPoint
923 {
924  var origin = _frame.origin;
925 
926  if (!aPoint || CGPointEqualToPoint(origin, aPoint))
927  return;
928 
929  origin.x = aPoint.x;
930  origin.y = aPoint.y;
931 
932  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
933  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
934 
935 #if PLATFORM(DOM)
936  var transform = _superview ? _superview._boundsTransform : NULL;
937 
938  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
939 #endif
940 }
941 
948 - (void)setFrameSize:(CGSize)aSize
949 {
950  var size = _frame.size;
951 
952  if (!aSize || CGSizeEqualToSize(size, aSize))
953  return;
954 
955  var oldSize = CGSizeMakeCopy(size);
956 
957  size.width = aSize.width;
958  size.height = aSize.height;
959 
960  if (YES)
961  {
962  _bounds.size.width = aSize.width * 1 / _scaleSize.width;
963  _bounds.size.height = aSize.height * 1 / _scaleSize.height;
964  }
965 
966  if (_layer)
967  [_layer _owningViewBoundsChanged];
968 
969  if (_autoresizesSubviews)
970  [self resizeSubviewsWithOldSize:oldSize];
971 
972  [self setNeedsLayout];
973  [self setNeedsDisplay:YES];
974 
975 #if PLATFORM(DOM)
976  [self _setDisplayServerSetStyleSize:size];
977 
978  if (_DOMContentsElement)
979  {
980  CPDOMDisplayServerSetSize(_DOMContentsElement, size.width, size.height);
981  CPDOMDisplayServerSetStyleSize(_DOMContentsElement, size.width, size.height);
982  }
983 
984  if (_backgroundType !== BackgroundTrivialColor)
985  {
986  if (_backgroundType === BackgroundTransparentColor)
987  {
988  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
989  }
990  else
991  {
992  var images = [[_backgroundColor patternImage] imageSlices],
993  partIndex = 0;
994 
995  if (_backgroundType === BackgroundVerticalThreePartImage)
996  {
997  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
998  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
999 
1000  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
1001  if (top)
1002  {
1003  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, top);
1004  partIndex++;
1005  }
1006  if (_DOMImageSizes[1])
1007  {
1008  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height - top - bottom);
1009  partIndex++;
1010  }
1011  if (bottom)
1012  {
1013  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, bottom);
1014  }
1015  }
1016  else if (_backgroundType === BackgroundHorizontalThreePartImage)
1017  {
1018  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1019  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
1020 
1021  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
1022  if (left)
1023  {
1024  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, size.height);
1025  partIndex++;
1026  }
1027  if (_DOMImageSizes[1])
1028  {
1029  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width - left - right, size.height);
1030  partIndex++;
1031  }
1032  if (right)
1033  {
1034  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, size.height);
1035  }
1036  }
1037  else if (_backgroundType === BackgroundNinePartImage)
1038  {
1039  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1040  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
1041  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1042  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
1043  width = size.width - left - right,
1044  height = size.height - top - bottom;
1045 
1046  if (_DOMImageSizes[0])
1047  partIndex++;
1048  if (_DOMImageSizes[1])
1049  {
1050  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, top);
1051  partIndex++;
1052  }
1053  if (_DOMImageSizes[2])
1054  partIndex++;
1055  if (_DOMImageSizes[3])
1056  {
1057  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
1058  partIndex++;
1059  }
1060  if (_DOMImageSizes[4])
1061  {
1062  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1063  partIndex++;
1064  }
1065  if (_DOMImageSizes[5])
1066  {
1067  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1068  partIndex++;
1069  }
1070  if (_DOMImageSizes[6])
1071  partIndex++;
1072  if (_DOMImageSizes[7])
1073  {
1074  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
1075  }
1076  }
1077  }
1078  }
1079 #endif
1080 
1081  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1082  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
1083 }
1084 
1090 - (void)_setDisplayServerSetStyleSize:(CGSize)aSize
1091 {
1092 #if PLATFORM(DOM)
1093  var scale = [self scaleSize];
1094  CPDOMDisplayServerSetStyleSize(_DOMElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
1095 #endif
1096 }
1097 
1103 - (void)setBounds:(CGRect)bounds
1104 {
1105  if (CGRectEqualToRect(_bounds, bounds))
1106  return;
1107 
1108  _inhibitFrameAndBoundsChangedNotifications = YES;
1109 
1110  [self setBoundsOrigin:bounds.origin];
1111  [self setBoundsSize:bounds.size];
1112 
1113  _inhibitFrameAndBoundsChangedNotifications = NO;
1114 
1115  if (_postsBoundsChangedNotifications)
1116  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1117 }
1118 
1123 - (CGRect)bounds
1124 {
1125  return CGRectMakeCopy(_bounds);
1126 }
1127 
1128 - (CGPoint)boundsOrigin
1129 {
1130  return CGPointMakeCopy(_bounds.origin);
1131 }
1132 
1133 - (CGSize)boundsSize
1134 {
1135  return CGSizeMakeCopy(_bounds.size);
1136 }
1137 
1144 - (void)setBoundsOrigin:(CGPoint)aPoint
1145 {
1146  var origin = _bounds.origin;
1147 
1148  if (CGPointEqualToPoint(origin, aPoint))
1149  return;
1150 
1151  origin.x = aPoint.x;
1152  origin.y = aPoint.y;
1153 
1154  if (origin.x != 0 || origin.y != 0)
1155  {
1156  _boundsTransform = CGAffineTransformMakeTranslation(-origin.x, -origin.y);
1157  _inverseBoundsTransform = CGAffineTransformInvert(_boundsTransform);
1158  }
1159  else
1160  {
1161  _boundsTransform = nil;
1162  _inverseBoundsTransform = nil;
1163  }
1164 
1165 #if PLATFORM(DOM)
1166  var index = _subviews.length;
1167 
1168  while (index--)
1169  {
1170  var view = _subviews[index],
1171  origin = view._frame.origin;
1172 
1173  CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y);
1174  }
1175 #endif
1176 
1177  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1178  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1179 }
1180 
1187 - (void)setBoundsSize:(CGSize)aSize
1188 {
1189  var size = _bounds.size;
1190 
1191  if (CGSizeEqualToSize(size, aSize))
1192  return;
1193 
1194  var frameSize = _frame.size;
1195 
1196  if (!CGSizeEqualToSize(size, frameSize))
1197  {
1198  var origin = _bounds.origin;
1199 
1200  origin.x /= size.width / frameSize.width;
1201  origin.y /= size.height / frameSize.height;
1202  }
1203 
1204  size.width = aSize.width;
1205  size.height = aSize.height;
1206 
1207  if (!CGSizeEqualToSize(size, frameSize))
1208  {
1209  var origin = _bounds.origin;
1210 
1211  origin.x *= size.width / frameSize.width;
1212  origin.y *= size.height / frameSize.height;
1213  }
1214 
1215  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1216  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1217 }
1218 
1219 
1224 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
1225 {
1226  var mask = [self autoresizingMask];
1227 
1228  if (mask == CPViewNotSizable)
1229  return;
1230 
1231  var frame = _superview._frame,
1232  newFrame = CGRectMakeCopy(_frame),
1233  dX = frame.size.width - aSize.width,
1234  dY = frame.size.height - aSize.height,
1235  evenFractionX = 1.0 / ((mask & CPViewMinXMargin ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
1236  evenFractionY = 1.0 / ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)),
1237  baseX = (mask & CPViewMinXMargin ? _frame.origin.x : 0) +
1238  (mask & CPViewWidthSizable ? _frame.size.width : 0) +
1239  (mask & CPViewMaxXMargin ? aSize.width - _frame.size.width - _frame.origin.x : 0),
1240  baseY = (mask & CPViewMinYMargin ? _frame.origin.y : 0) +
1241  (mask & CPViewHeightSizable ? _frame.size.height : 0) +
1242  (mask & CPViewMaxYMargin ? aSize.height - _frame.size.height - _frame.origin.y : 0);
1243 
1244  if (mask & CPViewMinXMargin)
1245  newFrame.origin.x += dX * (baseX > 0 ? _frame.origin.x / baseX : evenFractionX);
1246 
1247  if (mask & CPViewWidthSizable)
1248  newFrame.size.width += dX * (baseX > 0 ? _frame.size.width / baseX : evenFractionX);
1249 
1250  if (mask & CPViewMinYMargin)
1251  newFrame.origin.y += dY * (baseY > 0 ? _frame.origin.y / baseY : evenFractionY);
1252 
1253  if (mask & CPViewHeightSizable)
1254  newFrame.size.height += dY * (baseY > 0 ? _frame.size.height / baseY : evenFractionY);
1255 
1256  [self setFrame:newFrame];
1257 }
1258 
1263 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
1264 {
1265  var count = _subviews.length;
1266 
1267  while (count--)
1268  [_subviews[count] resizeWithOldSuperviewSize:aSize];
1269 }
1270 
1278 - (void)setAutoresizesSubviews:(BOOL)aFlag
1279 {
1280  _autoresizesSubviews = !!aFlag;
1281 }
1282 
1287 - (BOOL)autoresizesSubviews
1288 {
1289  return _autoresizesSubviews;
1290 }
1291 
1296 - (void)setAutoresizingMask:(unsigned)aMask
1297 {
1298  _autoresizingMask = aMask;
1299 }
1300 
1304 - (unsigned)autoresizingMask
1305 {
1306  return _autoresizingMask;
1307 }
1308 
1309 // Fullscreen Mode
1310 
1314 - (BOOL)enterFullScreenMode
1315 {
1316  return [self enterFullScreenMode:nil withOptions:nil];
1317 }
1318 
1324 - (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options
1325 {
1326  _fullScreenModeState = _CPViewFullScreenModeStateMake(self);
1327 
1328  var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
1329 
1330  [fullScreenWindow setLevel:CPScreenSaverWindowLevel];
1331  [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1332 
1333  var contentView = [fullScreenWindow contentView];
1334 
1335  [contentView setBackgroundColor:[CPColor blackColor]];
1336  [contentView addSubview:self];
1337 
1338  [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1339  [self setFrame:CGRectMakeCopy([contentView bounds])];
1340 
1341  [fullScreenWindow makeKeyAndOrderFront:self];
1342 
1343  [fullScreenWindow makeFirstResponder:self];
1344 
1345  _isInFullScreenMode = YES;
1346 
1347  return YES;
1348 }
1349 
1353 - (void)exitFullScreenMode
1354 {
1355  [self exitFullScreenModeWithOptions:nil];
1356 }
1357 
1362 - (void)exitFullScreenModeWithOptions:(CPDictionary)options
1363 {
1364  if (!_isInFullScreenMode)
1365  return;
1366 
1367  _isInFullScreenMode = NO;
1368 
1369  [self setFrame:_fullScreenModeState.frame];
1370  [self setAutoresizingMask:_fullScreenModeState.autoresizingMask];
1371  [_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index];
1372 
1373  [[self window] orderOut:self];
1374 }
1375 
1379 - (BOOL)isInFullScreenMode
1380 {
1381  return _isInFullScreenMode;
1382 }
1383 
1388 - (void)setHidden:(BOOL)aFlag
1389 {
1390  aFlag = !!aFlag;
1391 
1392  if (_isHidden === aFlag)
1393  return;
1394 
1395 // FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
1396 // _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
1397  _isHidden = aFlag;
1398 
1399 #if PLATFORM(DOM)
1400  _DOMElement.style.display = _isHidden ? "none" : "block";
1401 #endif
1402 
1403  if (aFlag)
1404  {
1405  var view = [_window firstResponder];
1406 
1407  if ([view isKindOfClass:[CPView class]])
1408  {
1409  do
1410  {
1411  if (self == view)
1412  {
1413  [_window makeFirstResponder:[self nextValidKeyView]];
1414  break;
1415  }
1416  }
1417  while (view = [view superview]);
1418  }
1419 
1420  [self _notifyViewDidHide];
1421  }
1422  else
1423  {
1424  [self setNeedsDisplay:YES];
1425  [self _notifyViewDidUnhide];
1426  }
1427 }
1428 
1429 - (void)_notifyViewDidHide
1430 {
1431  [self viewDidHide];
1432 
1433  var count = [_subviews count];
1434 
1435  while (count--)
1436  [_subviews[count] _notifyViewDidHide];
1437 }
1438 
1439 - (void)_notifyViewDidUnhide
1440 {
1441  [self viewDidUnhide];
1442 
1443  var count = [_subviews count];
1444 
1445  while (count--)
1446  [_subviews[count] _notifyViewDidUnhide];
1447 }
1448 
1452 - (BOOL)isHidden
1453 {
1454  return _isHidden;
1455 }
1456 
1457 - (void)setClipsToBounds:(BOOL)shouldClip
1458 {
1459  if (_clipsToBounds === shouldClip)
1460  return;
1461 
1462  _clipsToBounds = shouldClip;
1463 
1464 #if PLATFORM(DOM)
1465  _DOMElement.style.overflow = _clipsToBounds ? "hidden" : "visible";
1466 #endif
1467 }
1468 
1469 - (BOOL)clipsToBounds
1470 {
1471  return _clipsToBounds;
1472 }
1473 
1479 - (void)setAlphaValue:(float)anAlphaValue
1480 {
1481  if (_opacity == anAlphaValue)
1482  return;
1483 
1484  _opacity = anAlphaValue;
1485 
1486 #if PLATFORM(DOM)
1487 
1489  {
1490  if (anAlphaValue === 1.0)
1491  try { _DOMElement.style.removeAttribute("filter") } catch (anException) { }
1492  else
1493  _DOMElement.style.filter = "alpha(opacity=" + anAlphaValue * 100 + ")";
1494  }
1495  else
1496  _DOMElement.style.opacity = anAlphaValue;
1497 
1498 #endif
1499 }
1500 
1505 - (float)alphaValue
1506 {
1507  return _opacity;
1508 }
1509 
1514 - (BOOL)isHiddenOrHasHiddenAncestor
1515 {
1516  var view = self;
1517 
1518  while (view && ![view isHidden])
1519  view = [view superview];
1520 
1521  return view !== nil;
1522 }
1523 
1527 - (BOOL)_isVisible
1528 {
1529  return ![self isHiddenOrHasHiddenAncestor] && [[self window] isVisible];
1530 }
1531 
1541 - (void)viewDidHide
1542 {
1543 
1544 }
1545 
1555 - (void)viewDidUnhide
1556 {
1557 
1558 }
1559 
1565 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
1566 {
1567  return NO;
1568 }
1569 
1574 - (BOOL)hitTests
1575 {
1576  return _hitTests;
1577 }
1578 
1583 - (void)setHitTests:(BOOL)shouldHitTest
1584 {
1585  _hitTests = !!shouldHitTest;
1586 }
1587 
1593 - (CPView)hitTest:(CGPoint)aPoint
1594 {
1595  if (_isHidden || !_hitTests)
1596  return nil;
1597 
1598  var frame = _frame,
1599  sizeScale = [self _hierarchyScaleSize];
1600 
1601  if (_isScaled)
1602  frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale([_superview _hierarchyScaleSize].width, [_superview _hierarchyScaleSize].height));
1603  else
1604  frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale(sizeScale.width, sizeScale.height));
1605 
1606  if (!CGRectContainsPoint(frame, aPoint))
1607  return nil;
1608 
1609  var view = nil,
1610  i = _subviews.length,
1611  adjustedPoint = CGPointMake(aPoint.x - CGRectGetMinX(frame), aPoint.y - CGRectGetMinY(frame));
1612 
1613  if (_inverseBoundsTransform)
1614  {
1615  var affineTransform = CGAffineTransformMakeCopy(_inverseBoundsTransform);
1616 
1617  if (_isScaled)
1618  {
1619  affineTransform.tx *= [_superview _hierarchyScaleSize].width;
1620  affineTransform.ty *= [_superview _hierarchyScaleSize].height;
1621  }
1622  else
1623  {
1624  affineTransform.tx *= sizeScale.width;
1625  affineTransform.ty *= sizeScale.height;
1626  }
1627 
1628  adjustedPoint = CGPointApplyAffineTransform(adjustedPoint, affineTransform);
1629  }
1630 
1631 
1632  while (i--)
1633  if (view = [_subviews[i] hitTest:adjustedPoint])
1634  return view;
1635 
1636  return self;
1637 }
1638 
1642 - (BOOL)needsPanelToBecomeKey
1643 {
1644  return NO;
1645 }
1646 
1651 - (BOOL)mouseDownCanMoveWindow
1652 {
1653  return ![self isOpaque];
1654 }
1655 
1656 - (void)mouseDown:(CPEvent)anEvent
1657 {
1658  if ([self mouseDownCanMoveWindow])
1659  [super mouseDown:anEvent];
1660 }
1661 
1662 - (void)rightMouseDown:(CPEvent)anEvent
1663 {
1664  var menu = [self menuForEvent:anEvent];
1665 
1666  if (menu)
1667  [CPMenu popUpContextMenu:menu withEvent:anEvent forView:self];
1668  else if ([[self nextResponder] isKindOfClass:CPView])
1669  [super rightMouseDown:anEvent];
1670  else
1671  [[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
1672 }
1673 
1674 - (CPMenu)menuForEvent:(CPEvent)anEvent
1675 {
1676  return [self menu] || [[self class] defaultMenu];
1677 }
1678 
1683 - (void)setBackgroundColor:(CPColor)aColor
1684 {
1685  if (_backgroundColor == aColor)
1686  return;
1687 
1688  if (aColor == [CPNull null])
1689  aColor = nil;
1690 
1691  _backgroundColor = aColor;
1692 
1693 #if PLATFORM(DOM)
1694  var patternImage = [_backgroundColor patternImage],
1695  colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
1696  colorHasAlpha = colorExists && [_backgroundColor alphaComponent] < 1.0,
1697  supportsRGBA = CPFeatureIsCompatible(CPCSSRGBAFeature),
1698  colorNeedsDOMElement = colorHasAlpha && !supportsRGBA,
1699  amount = 0,
1700  slices;
1701 
1702  if ([patternImage isThreePartImage])
1703  {
1704  _backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage;
1705  amount = 3;
1706  }
1707  else if ([patternImage isNinePartImage])
1708  {
1709  _backgroundType = BackgroundNinePartImage;
1710  amount = 9;
1711  }
1712  else
1713  {
1714  _backgroundType = colorNeedsDOMElement ? BackgroundTransparentColor : BackgroundTrivialColor;
1715  amount = (colorNeedsDOMElement ? 1 : 0) - _DOMImageParts.length;
1716  }
1717 
1718  // 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.
1719  if (_backgroundType === BackgroundVerticalThreePartImage || _backgroundType === BackgroundHorizontalThreePartImage || _backgroundType === BackgroundNinePartImage)
1720  {
1721  slices = [patternImage imageSlices];
1722 
1723  // We won't need more divs than there are slices.
1724  amount = MIN(amount, slices.length);
1725 
1726  for (var i = 0, count = slices.length; i < count; i++)
1727  {
1728  var image = slices[i],
1729  size = [image size];
1730 
1731  if (!size || (size.width == 0 && size.height == 0))
1732  size = nil;
1733 
1734  _DOMImageSizes[i] = size;
1735 
1736  // If there's a nil slice or a slice with no size, it won't need a div.
1737  if (!size)
1738  amount--;
1739  }
1740 
1741  // Now that we know how many divs we really need, compare that to number we actually have.
1742  amount -= _DOMImageParts.length;
1743  }
1744 
1745  // Make sure the number of divs we have match our needs.
1746  if (amount > 0)
1747  {
1748  while (amount--)
1749  {
1750  var DOMElement = DOMElementPrototype.cloneNode(false);
1751 
1752  DOMElement.style.zIndex = -1000;
1753 
1754  _DOMImageParts.push(DOMElement);
1755  _DOMElement.appendChild(DOMElement);
1756  }
1757  }
1758  else
1759  {
1760  amount = -amount;
1761  while (amount--)
1762  _DOMElement.removeChild(_DOMImageParts.pop());
1763  }
1764 
1765  if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
1766  {
1767  var colorCSS = colorExists ? [_backgroundColor cssString] : "";
1768 
1769  if (colorNeedsDOMElement)
1770  {
1771  _DOMElement.style.background = "";
1772  _DOMImageParts[0].style.background = [_backgroundColor cssString];
1773 
1775  _DOMImageParts[0].style.filter = "alpha(opacity=" + [_backgroundColor alphaComponent] * 100 + ")";
1776  else
1777  _DOMImageParts[0].style.opacity = [_backgroundColor alphaComponent];
1778 
1779  var size = [self bounds].size;
1780  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
1781  }
1782  else
1783  _DOMElement.style.background = colorCSS;
1784  }
1785  else
1786  {
1787  var frameSize = _frame.size,
1788  partIndex = 0;
1789 
1790  for (var i = 0; i < slices.length; i++)
1791  {
1792  var size = _DOMImageSizes[i];
1793 
1794  if (!size)
1795  continue;
1796 
1797  var image = slices[i];
1798 
1799  // // If image was nil, size should have been nil too.
1800  // assert(image != nil);
1801 
1802  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height);
1803 
1804  _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")";
1805 
1806  if (!supportsRGBA)
1807  {
1809  try { _DOMImageParts[partIndex].style.removeAttribute("filter") } catch (anException) { }
1810  else
1811  _DOMImageParts[partIndex].style.opacity = 1.0;
1812  }
1813 
1814  partIndex++;
1815  }
1816 
1817  if (_backgroundType == BackgroundNinePartImage)
1818  {
1819  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1820  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
1821  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1822  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
1823  width = frameSize.width - left - right,
1824  height = frameSize.height - top - bottom;
1825 
1826  partIndex = 0;
1827 
1828  if (_DOMImageSizes[0])
1829  {
1830  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1831  partIndex++;
1832  }
1833  if (_DOMImageSizes[1])
1834  {
1835  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
1836  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[1].height);
1837  partIndex++;
1838  }
1839  if (_DOMImageSizes[2])
1840  {
1841  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1842  partIndex++;
1843  }
1844  if (_DOMImageSizes[3])
1845  {
1846  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1847  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
1848  partIndex++;
1849  }
1850  if (_DOMImageSizes[4])
1851  {
1852  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, top);
1853  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1854  partIndex++;
1855  }
1856  if (_DOMImageSizes[5])
1857  {
1858  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1859  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1860  partIndex++;
1861  }
1862  if (_DOMImageSizes[6])
1863  {
1864  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1865  partIndex++;
1866  }
1867  if (_DOMImageSizes[7])
1868  {
1869  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, left, 0.0);
1870  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
1871  partIndex++;
1872  }
1873  if (_DOMImageSizes[8])
1874  {
1875  CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1876  }
1877  }
1878  else if (_backgroundType == BackgroundVerticalThreePartImage)
1879  {
1880  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1881  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
1882 
1883  partIndex = 0;
1884 
1885  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
1886  if (top)
1887  {
1888  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1889  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, top);
1890  partIndex++;
1891  }
1892  if (_DOMImageSizes[1])
1893  {
1894  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1895  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, frameSize.height - top - bottom);
1896  partIndex++;
1897  }
1898  if (bottom)
1899  {
1900  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1901  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, bottom);
1902  }
1903  }
1904  else if (_backgroundType == BackgroundHorizontalThreePartImage)
1905  {
1906  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1907  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
1908 
1909  partIndex = 0;
1910 
1911  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
1912  if (left)
1913  {
1914  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1915  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, frameSize.height);
1916  partIndex++;
1917  }
1918  if (_DOMImageSizes[1])
1919  {
1920  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
1921  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width - left - right, frameSize.height);
1922  partIndex++;
1923  }
1924  if (right)
1925  {
1926  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1927  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, frameSize.height);
1928  }
1929  }
1930  }
1931 #endif
1932 }
1933 
1937 - (CPColor)backgroundColor
1938 {
1939  return _backgroundColor;
1940 }
1941 
1942 // Converting Coordinates
1949 - (CGPoint)convertPoint:(CGPoint)aPoint fromView:(CPView)aView
1950 {
1951  if (aView === self)
1952  return aPoint;
1953 
1954  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
1955 }
1956 
1962 - (CGPoint)convertPointFromBase:(CGPoint)aPoint
1963 {
1964  return [self convertPoint:aPoint fromView:nil];
1965 }
1966 
1973 - (CGPoint)convertPoint:(CGPoint)aPoint toView:(CPView)aView
1974 {
1975  if (aView === self)
1976  return aPoint;
1977 
1978  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
1979 }
1980 
1981 
1987 - (CGPoint)convertPointToBase:(CGPoint)aPoint
1988 {
1989  return [self convertPoint:aPoint toView:nil];
1990 }
1991 
1998 - (CGSize)convertSize:(CGSize)aSize fromView:(CPView)aView
1999 {
2000  if (aView === self)
2001  return aSize;
2002 
2003  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(aView, self));
2004 }
2005 
2012 - (CGSize)convertSize:(CGSize)aSize toView:(CPView)aView
2013 {
2014  if (aView === self)
2015  return aSize;
2016 
2017  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(self, aView));
2018 }
2019 
2026 - (CGRect)convertRect:(CGRect)aRect fromView:(CPView)aView
2027 {
2028  if (self === aView)
2029  return aRect;
2030 
2031  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
2032 }
2033 
2039 - (CGRect)convertRectFromBase:(CGRect)aRect
2040 {
2041  return [self convertRect:aRect fromView:nil];
2042 }
2043 
2050 - (CGRect)convertRect:(CGRect)aRect toView:(CPView)aView
2051 {
2052  if (self === aView)
2053  return aRect;
2054 
2055  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
2056 }
2057 
2063 - (CGRect)convertRectToBase:(CGRect)aRect
2064 {
2065  return [self convertRect:aRect toView:nil];
2066 }
2067 
2080 - (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications
2081 {
2082  shouldPostFrameChangedNotifications = !!shouldPostFrameChangedNotifications;
2083 
2084  if (_postsFrameChangedNotifications === shouldPostFrameChangedNotifications)
2085  return;
2086 
2087  _postsFrameChangedNotifications = shouldPostFrameChangedNotifications;
2088 }
2089 
2093 - (BOOL)postsFrameChangedNotifications
2094 {
2095  return _postsFrameChangedNotifications;
2096 }
2097 
2110 - (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications
2111 {
2112  shouldPostBoundsChangedNotifications = !!shouldPostBoundsChangedNotifications;
2113 
2114  if (_postsBoundsChangedNotifications === shouldPostBoundsChangedNotifications)
2115  return;
2116 
2117  _postsBoundsChangedNotifications = shouldPostBoundsChangedNotifications;
2118 }
2119 
2125 - (BOOL)postsBoundsChangedNotifications
2126 {
2127  return _postsBoundsChangedNotifications;
2128 }
2129 
2140 - (void)dragImage:(CPImage)anImage at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2141 {
2142  [_window dragImage:anImage at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2143 }
2144 
2155 - (void)dragView:(CPView)aView at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2156 {
2157  [_window dragView:aView at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2158 }
2159 
2164 - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
2165 {
2166  if (!pasteboardTypes || ![pasteboardTypes count])
2167  return;
2168 
2169  var theWindow = [self window];
2170 
2171  [theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2172  [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
2173  [theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
2174 
2175  _registeredDraggedTypesArray = nil;
2176 }
2177 
2182 - (CPArray)registeredDraggedTypes
2183 {
2184  if (!_registeredDraggedTypesArray)
2185  _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
2186 
2187  return _registeredDraggedTypesArray;
2188 }
2189 
2193 - (void)unregisterDraggedTypes
2194 {
2195  [[self window] _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2196 
2197  _registeredDraggedTypes = [CPSet set];
2198  _registeredDraggedTypesArray = [];
2199 }
2200 
2205 - (void)drawRect:(CGRect)aRect
2206 {
2207 
2208 }
2209 
2210 // Scaling
2211 
2218 - (void)scaleUnitSquareToSize:(CGSize)aSize
2219 {
2220  if (!aSize)
2221  return;
2222 
2223  // Reset the bounds
2224  var bounds = CGRectMakeCopy([self bounds]);
2225  bounds.size.width *= _scaleSize.width;
2226  bounds.size.height *= _scaleSize.height;
2227 
2228  [self willChangeValueForKey:@"scaleSize"];
2229  _scaleSize = CGSizeMakeCopy([self scaleSize]);
2230  _scaleSize.height *= aSize.height;
2231  _scaleSize.width *= aSize.width;
2232  [self didChangeValueForKey:@"scaleSize"];
2233  _isScaled = YES;
2234 
2235  _hierarchyScaleSize = CGSizeMakeCopy([self _hierarchyScaleSize]);
2236  _hierarchyScaleSize.height *= aSize.height;
2237  _hierarchyScaleSize.width *= aSize.width;
2238 
2239  var scaleAffine = CGAffineTransformMakeScale(1.0 / _scaleSize.width, 1.0 / _scaleSize.height),
2240  newBounds = CGRectApplyAffineTransform(CGRectMakeCopy(bounds), scaleAffine);
2241 
2242  [self setBounds:newBounds];
2243 
2244  [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
2245 }
2246 
2251 - (void)_scaleSizeUnitSquareToSize:(CGSize)aSize
2252 {
2253  _hierarchyScaleSize = CGSizeMakeCopy([_superview _hierarchyScaleSize]);
2254 
2255  if (_isScaled)
2256  {
2257  _hierarchyScaleSize.width *= _scaleSize.width;
2258  _hierarchyScaleSize.height *= _scaleSize.height;
2259  }
2260 
2261  [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
2262 }
2263 
2267 - (CGSize)_hierarchyScaleSize
2268 {
2269  return _hierarchyScaleSize || CGSizeMake(1.0, 1.0);
2270 }
2271 
2275 - (void)_applyCSSScalingTranformations
2276 {
2277 #if PLATFORM(DOM)
2278  if (_isScaled)
2279  {
2280  var scale = [self scaleSize],
2281  browserPropertyTransform = CPBrowserStyleProperty(@"transform"),
2282  browserPropertyTransformOrigin = CPBrowserStyleProperty(@"transformOrigin");
2283 
2284  self._DOMElement.style[browserPropertyTransform] = 'scale(' + scale.width + ', ' + scale.height + ')';
2285  self._DOMElement.style[browserPropertyTransformOrigin] = '0 0';
2286 
2287  [self _setDisplayServerSetStyleSize:[self frameSize]];
2288  }
2289 #endif
2290 }
2291 
2292 // Displaying
2293 
2297 - (void)setNeedsDisplay:(BOOL)aFlag
2298 {
2299  if (aFlag)
2300  {
2301  [self _applyCSSScalingTranformations];
2302  [self setNeedsDisplayInRect:[self bounds]];
2303  }
2304 }
2305 
2310 - (void)setNeedsDisplayInRect:(CGRect)aRect
2311 {
2312  if (!(_viewClassFlags & CPViewHasCustomDrawRect))
2313  return;
2314 
2315  if (CGRectIsEmpty(aRect))
2316  return;
2317 
2318  if (_dirtyRect && !CGRectIsEmpty(_dirtyRect))
2319  _dirtyRect = CGRectUnion(aRect, _dirtyRect);
2320  else
2321  _dirtyRect = CGRectMakeCopy(aRect);
2322 
2323  _CPDisplayServerAddDisplayObject(self);
2324 }
2325 
2326 - (BOOL)needsDisplay
2327 {
2328  return _dirtyRect && !CGRectIsEmpty(_dirtyRect);
2329 }
2330 
2334 - (void)displayIfNeeded
2335 {
2336  if ([self needsDisplay])
2337  [self displayRect:_dirtyRect];
2338 }
2339 
2343 - (void)display
2344 {
2345  [self displayRect:[self visibleRect]];
2346 }
2347 
2348 - (void)displayIfNeededInRect:(CGRect)aRect
2349 {
2350  if ([self needsDisplay])
2351  [self displayRect:aRect];
2352 }
2353 
2358 - (void)displayRect:(CGRect)aRect
2359 {
2360  [self viewWillDraw];
2361 
2362  [self displayRectIgnoringOpacity:aRect inContext:nil];
2363 
2364  _dirtyRect = NULL;
2365 }
2366 
2367 - (void)displayRectIgnoringOpacity:(CGRect)aRect inContext:(CPGraphicsContext)aGraphicsContext
2368 {
2369  if ([self isHidden])
2370  return;
2371 
2372 #if PLATFORM(DOM)
2373  [self lockFocus];
2374 
2375  CGContextClearRect([[CPGraphicsContext currentContext] graphicsPort], aRect);
2376 
2377  [self drawRect:aRect];
2378  [self unlockFocus];
2379 #endif
2380 }
2381 
2382 - (void)viewWillDraw
2383 {
2384 }
2385 
2389 - (void)lockFocus
2390 {
2391  if (!_graphicsContext)
2392  {
2393  var graphicsPort = CGBitmapGraphicsContextCreate();
2394 
2395 #if PLATFORM(DOM)
2396  var width = CGRectGetWidth(_frame),
2397  height = CGRectGetHeight(_frame);
2398 
2399  _DOMContentsElement = graphicsPort.DOMElement;
2400 
2401  _DOMContentsElement.style.zIndex = -100;
2402 
2403  _DOMContentsElement.style.overflow = "hidden";
2404  _DOMContentsElement.style.position = "absolute";
2405  _DOMContentsElement.style.visibility = "visible";
2406 
2407  CPDOMDisplayServerSetSize(_DOMContentsElement, width, height);
2408 
2409  CPDOMDisplayServerSetStyleLeftTop(_DOMContentsElement, NULL, 0.0, 0.0);
2410  CPDOMDisplayServerSetStyleSize(_DOMContentsElement, width, height);
2411 
2412  // The performance implications of this aren't clear, but without this subviews might not be redrawn when this
2413  // view moves.
2415  _DOMElement.style.webkitTransform = 'translateX(0)';
2416 
2417  CPDOMDisplayServerAppendChild(_DOMElement, _DOMContentsElement);
2418 #endif
2419  _graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:graphicsPort flipped:YES];
2420  }
2421 
2422  [CPGraphicsContext setCurrentContext:_graphicsContext];
2423 
2424  CGContextSaveGState([_graphicsContext graphicsPort]);
2425 }
2426 
2430 - (void)unlockFocus
2431 {
2432  CGContextRestoreGState([_graphicsContext graphicsPort]);
2433 
2435 }
2436 
2437 - (void)setNeedsLayout
2438 {
2439  if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews))
2440  return;
2441 
2442  _needsLayout = YES;
2443 
2444  _CPDisplayServerAddLayoutObject(self);
2445 }
2446 
2447 - (void)layoutIfNeeded
2448 {
2449  if (_needsLayout)
2450  {
2451  _needsLayout = NO;
2452 
2453  [self layoutSubviews];
2454  }
2455 }
2456 
2457 - (void)layoutSubviews
2458 {
2459 }
2460 
2464 - (BOOL)isOpaque
2465 {
2466  return NO;
2467 }
2468 
2472 - (CGRect)visibleRect
2473 {
2474  if (!_superview)
2475  return _bounds;
2476 
2477  return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds);
2478 }
2479 
2480 // Scrolling
2481 /* @ignore */
2482 - (CPScrollView)_enclosingClipView
2483 {
2484  var superview = _superview,
2485  clipViewClass = [CPClipView class];
2486 
2487  while (superview && ![superview isKindOfClass:clipViewClass])
2488  superview = superview._superview;
2489 
2490  return superview;
2491 }
2492 
2497 - (void)scrollPoint:(CGPoint)aPoint
2498 {
2499  var clipView = [self _enclosingClipView];
2500 
2501  if (!clipView)
2502  return;
2503 
2504  [clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]];
2505 }
2506 
2512 - (BOOL)scrollRectToVisible:(CGRect)aRect
2513 {
2514  // Make sure we have a rect that exists.
2515  aRect = CGRectIntersection(aRect, _bounds);
2516 
2517  // If aRect is empty no scrolling required.
2518  if (CGRectIsEmpty(aRect))
2519  return NO;
2520 
2521  var enclosingClipView = [self _enclosingClipView];
2522 
2523  // If we're not in a clip view, then there isn't much we can do.
2524  if (!enclosingClipView)
2525  return NO;
2526 
2527  var documentView = [enclosingClipView documentView];
2528 
2529  // If the clip view doesn't have a document view, then there isn't much we can do.
2530  if (!documentView)
2531  return NO;
2532 
2533  // Get the document view visible rect and convert aRect to the document view's coordinate system
2534  var documentViewVisibleRect = [documentView visibleRect],
2535  rectInDocumentView = [self convertRect:aRect toView:documentView];
2536 
2537  // If already visible then no scrolling required.
2538  if (CGRectContainsRect(documentViewVisibleRect, rectInDocumentView))
2539  return NO;
2540 
2541  var scrollPoint = CGPointMakeCopy(documentViewVisibleRect.origin);
2542 
2543  // One of the following has to be true since our current visible rect didn't contain aRect.
2544  if (CGRectGetMinX(rectInDocumentView) < CGRectGetMinX(documentViewVisibleRect))
2545  scrollPoint.x = CGRectGetMinX(rectInDocumentView);
2546  else if (CGRectGetMaxX(rectInDocumentView) > CGRectGetMaxX(documentViewVisibleRect))
2547  scrollPoint.x += CGRectGetMaxX(rectInDocumentView) - CGRectGetMaxX(documentViewVisibleRect);
2548 
2549  if (CGRectGetMinY(rectInDocumentView) < CGRectGetMinY(documentViewVisibleRect))
2550  scrollPoint.y = CGRectGetMinY(rectInDocumentView);
2551  else if (CGRectGetMaxY(rectInDocumentView) > CGRectGetMaxY(documentViewVisibleRect))
2552  scrollPoint.y += CGRectGetMaxY(rectInDocumentView) - CGRectGetMaxY(documentViewVisibleRect);
2553 
2554  [enclosingClipView scrollToPoint:scrollPoint];
2555 
2556  return YES;
2557 }
2558 
2559 /*
2560  FIXME Not yet implemented
2561 */
2562 - (BOOL)autoscroll:(CPEvent)anEvent
2563 {
2564  return [[self superview] autoscroll:anEvent];
2565 }
2566 
2573 - (CGRect)adjustScroll:(CGRect)proposedVisibleRect
2574 {
2575  return proposedVisibleRect;
2576 }
2577 
2581 - (void)scrollRect:(CGRect)aRect by:(float)anAmount
2582 {
2583 
2584 }
2585 
2590 - (CPScrollView)enclosingScrollView
2591 {
2592  var superview = _superview,
2593  scrollViewClass = [CPScrollView class];
2594 
2595  while (superview && ![superview isKindOfClass:scrollViewClass])
2596  superview = superview._superview;
2597 
2598  return superview;
2599 }
2600 
2606 - (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
2607 {
2608  [aClipView scrollToPoint:aPoint];
2609 }
2610 
2616 - (void)reflectScrolledClipView:(CPClipView)aClipView
2617 {
2618 }
2619 
2623 - (BOOL)inLiveResize
2624 {
2625  return _inLiveResize;
2626 }
2627 
2637 - (void)viewWillStartLiveResize
2638 {
2639  _inLiveResize = YES;
2640 }
2641 
2652 - (void)viewDidEndLiveResize
2653 {
2654  _inLiveResize = NO;
2655 }
2656 
2657 @end
2658 
2659 @implementation CPView (KeyView)
2660 
2675 - (BOOL)performKeyEquivalent:(CPEvent)anEvent
2676 {
2677  var count = [_subviews count];
2678 
2679  // Is reverse iteration correct here? It matches the other (correct) code like hit testing.
2680  while (count--)
2681  if ([_subviews[count] performKeyEquivalent:anEvent])
2682  return YES;
2683 
2684  return NO;
2685 }
2686 
2687 - (BOOL)canBecomeKeyView
2688 {
2689  return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor];
2690 }
2691 
2692 - (CPView)nextKeyView
2693 {
2694  return _nextKeyView;
2695 }
2696 
2697 - (CPView)nextValidKeyView
2698 {
2699  var result = [self nextKeyView],
2700  resultUID = [result UID],
2701  unsuitableResults = {};
2702 
2703  while (result && ![result canBecomeKeyView])
2704  {
2705  unsuitableResults[resultUID] = 1;
2706  result = [result nextKeyView];
2707 
2708  resultUID = [result UID];
2709 
2710  // Did we get back to a key view we already ruled out due to ![result canBecomeKeyView]?
2711  if (unsuitableResults[resultUID])
2712  return nil;
2713  }
2714 
2715  return result;
2716 }
2717 
2718 - (CPView)previousKeyView
2719 {
2720  return _previousKeyView;
2721 }
2722 
2723 - (CPView)previousValidKeyView
2724 {
2725  var result = [self previousKeyView],
2726  firstResult = result;
2727 
2728  while (result && ![result canBecomeKeyView])
2729  {
2730  result = [result previousKeyView];
2731 
2732  // Cycled.
2733  if (result === firstResult)
2734  return nil;
2735  }
2736 
2737  return result;
2738 }
2739 
2740 - (void)_setPreviousKeyView:(CPView)previous
2741 {
2742  if (![previous isEqual:self])
2743  {
2744  var previousWindow = [previous window];
2745 
2746  if (!previousWindow || previousWindow === _window)
2747  {
2748  _previousKeyView = previous;
2749  return;
2750  }
2751  }
2752 
2753  _previousKeyView = nil;
2754 }
2755 
2756 - (void)setNextKeyView:(CPView)next
2757 {
2758  if (![next isEqual:self])
2759  {
2760  var nextWindow = [next window];
2761 
2762  if (!nextWindow || nextWindow === _window)
2763  {
2764  _nextKeyView = next;
2765  [_nextKeyView _setPreviousKeyView:self];
2766  return;
2767  }
2768  }
2769 
2770  _nextKeyView = nil;
2771 }
2772 
2773 @end
2774 
2775 @implementation CPView (CoreAnimationAdditions)
2776 
2780 - (void)setLayer:(CALayer)aLayer
2781 {
2782  if (_layer == aLayer)
2783  return;
2784 
2785  if (_layer)
2786  {
2787  _layer._owningView = nil;
2788 #if PLATFORM(DOM)
2789  _DOMElement.removeChild(_layer._DOMElement);
2790 #endif
2791  }
2792 
2793  _layer = aLayer;
2794 
2795  if (_layer)
2796  {
2797  var bounds = CGRectMakeCopy([self bounds]);
2798 
2799  [_layer _setOwningView:self];
2800 
2801 #if PLATFORM(DOM)
2802  _layer._DOMElement.style.zIndex = 100;
2803 
2804  _DOMElement.appendChild(_layer._DOMElement);
2805 #endif
2806  }
2807 }
2808 
2812 - (CALayer)layer
2813 {
2814  return _layer;
2815 }
2816 
2821 - (void)setWantsLayer:(BOOL)aFlag
2822 {
2823  _wantsLayer = !!aFlag;
2824 }
2825 
2830 - (BOOL)wantsLayer
2831 {
2832  return _wantsLayer;
2833 }
2834 
2835 @end
2836 
2837 
2838 @implementation CPView (Scaling)
2839 
2845 - (void)setScaleSize:(CGSize)aSize
2846 {
2847  if (CGSizeEqualToSize(_scaleSize, aSize))
2848  return;
2849 
2850  var size = CGSizeMakeZero(),
2851  scale = CGSizeMakeCopy([self scaleSize]);
2852 
2853  size.height = aSize.height / scale.height;
2854  size.width = aSize.width / scale.width;
2855 
2856  [self scaleUnitSquareToSize:size];
2857  [self setNeedsDisplay:YES];
2858 }
2859 
2860 
2864 - (CGSize)scaleSize
2865 {
2866  return _scaleSize || CGSizeMake(1.0, 1.0);
2867 }
2868 
2869 @end
2870 
2871 @implementation CPView (Theming)
2872 #pragma mark Theme States
2873 
2874 - (unsigned)themeState
2875 {
2876  return _themeState;
2877 }
2878 
2879 - (BOOL)hasThemeState:(CPThemeState)aState
2880 {
2881  // Because CPThemeStateNormal is defined as 0 we need to check for it explicitly here
2882  if (aState === CPThemeStateNormal && _themeState === CPThemeStateNormal)
2883  return YES;
2884 
2885  return !!(_themeState & ((typeof aState === "string") ? CPThemeState(aState) : aState));
2886 }
2887 
2888 - (BOOL)setThemeState:(CPThemeState)aState
2889 {
2890  var newState = (typeof aState === "string") ? CPThemeState(aState) : aState;
2891 
2892  if (_themeState & newState)
2893  return NO;
2894 
2895  _themeState |= newState;
2896 
2897  [self setNeedsLayout];
2898  [self setNeedsDisplay:YES];
2899 
2900  return YES;
2901 }
2902 
2903 - (BOOL)unsetThemeState:(CPThemeState)aState
2904 {
2905  var newState = ((typeof aState === "string") ? CPThemeState(aState) : aState);
2906 
2907  if (!(_themeState & newState))
2908  return NO;
2909 
2910  _themeState &= ~newState;
2911 
2912  [self setNeedsLayout];
2913  [self setNeedsDisplay:YES];
2914 
2915  return YES;
2916 }
2917 
2918 #pragma mark Theme Attributes
2919 
2920 + (CPString)defaultThemeClass
2921 {
2922  return nil;
2923 }
2924 
2925 - (CPString)themeClass
2926 {
2927  if (_themeClass)
2928  return _themeClass;
2929 
2930  return [[self class] defaultThemeClass];
2931 }
2932 
2933 - (void)setThemeClass:(CPString)theClass
2934 {
2935  _themeClass = theClass;
2936 
2937  [self _loadThemeAttributes];
2938 
2939  [self setNeedsLayout];
2940  [self setNeedsDisplay:YES];
2941 }
2942 
2943 + (CPDictionary)themeAttributes
2944 {
2945  return nil;
2946 }
2947 
2948 + (CPArray)_themeAttributes
2949 {
2950  if (!CachedThemeAttributes)
2951  CachedThemeAttributes = {};
2952 
2953  var theClass = [self class],
2954  CPViewClass = [CPView class],
2955  attributes = [],
2956  nullValue = [CPNull null];
2957 
2958  for (; theClass && theClass !== CPViewClass; theClass = [theClass superclass])
2959  {
2960  var cachedAttributes = CachedThemeAttributes[class_getName(theClass)];
2961 
2962  if (cachedAttributes)
2963  {
2964  attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
2965  CachedThemeAttributes[[self className]] = attributes;
2966 
2967  break;
2968  }
2969 
2970  var attributeDictionary = [theClass themeAttributes];
2971 
2972  if (!attributeDictionary)
2973  continue;
2974 
2975  var attributeKeys = [attributeDictionary allKeys],
2976  attributeCount = attributeKeys.length;
2977 
2978  while (attributeCount--)
2979  {
2980  var attributeName = attributeKeys[attributeCount],
2981  attributeValue = [attributeDictionary objectForKey:attributeName];
2982 
2983  attributes.push(attributeValue === nullValue ? nil : attributeValue);
2984  attributes.push(attributeName);
2985  }
2986  }
2987 
2988  return attributes;
2989 }
2990 
2991 - (void)_loadThemeAttributes
2992 {
2993  var theClass = [self class],
2994  attributes = [theClass _themeAttributes],
2995  count = attributes.length;
2996 
2997  if (!count)
2998  return;
2999 
3000  var theme = [self theme],
3001  themeClass = [self themeClass];
3002 
3003  _themeAttributes = {};
3004 
3005  while (count--)
3006  {
3007  var attributeName = attributes[count--],
3008  attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
3009 
3010  [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
3011 
3012  _themeAttributes[attributeName] = attribute;
3013  }
3014 }
3015 
3016 - (void)setTheme:(CPTheme)aTheme
3017 {
3018  if (_theme === aTheme)
3019  return;
3020 
3021  _theme = aTheme;
3022 
3023  [self viewDidChangeTheme];
3024 }
3025 
3026 - (void)_setThemeIncludingDescendants:(CPTheme)aTheme
3027 {
3028  [self setTheme:aTheme];
3029  [[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
3030 }
3031 
3032 - (CPTheme)theme
3033 {
3034  return _theme;
3035 }
3036 
3037 - (void)viewDidChangeTheme
3038 {
3039  if (!_themeAttributes)
3040  return;
3041 
3042  var theme = [self theme],
3043  themeClass = [self themeClass];
3044 
3045  for (var attributeName in _themeAttributes)
3046  if (_themeAttributes.hasOwnProperty(attributeName))
3047  [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
3048 
3049  [self setNeedsLayout];
3050  [self setNeedsDisplay:YES];
3051 }
3052 
3053 - (CPDictionary)_themeAttributeDictionary
3054 {
3055  var dictionary = @{};
3056 
3057  if (_themeAttributes)
3058  {
3059  var theme = [self theme];
3060 
3061  for (var attributeName in _themeAttributes)
3062  if (_themeAttributes.hasOwnProperty(attributeName))
3063  [dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
3064  }
3065 
3066  return dictionary;
3067 }
3068 
3069 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(CPThemeState)aState
3070 {
3071  if (!_themeAttributes || !_themeAttributes[aName])
3072  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3073 
3074  var currentValue = [self currentValueForThemeAttribute:aName];
3075 
3076  [_themeAttributes[aName] setValue:aValue forState:aState];
3077 
3078  if ([self currentValueForThemeAttribute:aName] === currentValue)
3079  return;
3080 
3081  [self setNeedsDisplay:YES];
3082  [self setNeedsLayout];
3083 }
3084 
3085 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
3086 {
3087  if (!_themeAttributes || !_themeAttributes[aName])
3088  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3089 
3090  var currentValue = [self currentValueForThemeAttribute:aName];
3091 
3092  [_themeAttributes[aName] setValue:aValue];
3093 
3094  if ([self currentValueForThemeAttribute:aName] === currentValue)
3095  return;
3096 
3097  [self setNeedsDisplay:YES];
3098  [self setNeedsLayout];
3099 }
3100 
3101 - (id)valueForThemeAttribute:(CPString)aName inState:(CPThemeState)aState
3102 {
3103  if (!_themeAttributes || !_themeAttributes[aName])
3104  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3105 
3106  return [_themeAttributes[aName] valueForState:aState];
3107 }
3108 
3109 - (id)valueForThemeAttribute:(CPString)aName
3110 {
3111  if (!_themeAttributes || !_themeAttributes[aName])
3112  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3113 
3114  return [_themeAttributes[aName] value];
3115 }
3116 
3117 - (id)currentValueForThemeAttribute:(CPString)aName
3118 {
3119  if (!_themeAttributes || !_themeAttributes[aName])
3120  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
3121 
3122  return [_themeAttributes[aName] valueForState:_themeState];
3123 }
3124 
3125 - (BOOL)hasThemeAttribute:(CPString)aName
3126 {
3127  return (_themeAttributes && _themeAttributes[aName] !== undefined);
3128 }
3129 
3138 - (void)registerThemeValues:(CPArray)themeValues
3139 {
3140  for (var i = 0; i < themeValues.length; ++i)
3141  {
3142  var attributeValueState = themeValues[i],
3143  attribute = attributeValueState[0],
3144  value = attributeValueState[1],
3145  state = attributeValueState[2];
3146 
3147  if (state)
3148  [self setValue:value forThemeAttribute:attribute inState:state];
3149  else
3150  [self setValue:value forThemeAttribute:attribute];
3151  }
3152 }
3153 
3164 - (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
3165 {
3166  // Register inherited values first, then override those with the subtheme values.
3167  if (inheritedValues)
3168  [self registerThemeValues:inheritedValues];
3169 
3170  if (themeValues)
3171  [self registerThemeValues:themeValues];
3172 }
3173 
3174 - (CPView)createEphemeralSubviewNamed:(CPString)aViewName
3175 {
3176  return nil;
3177 }
3178 
3179 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
3180 {
3181  return CGRectMakeZero();
3182 }
3183 
3184 - (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
3185  positioned:(CPWindowOrderingMode)anOrderingMode
3186  relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
3187 {
3188  if (!_ephemeralSubviewsForNames)
3189  {
3190  _ephemeralSubviewsForNames = {};
3191  _ephemeralSubviews = [CPSet set];
3192  }
3193 
3194  var frame = [self rectForEphemeralSubviewNamed:aViewName];
3195 
3196  if (frame)
3197  {
3198  if (!_ephemeralSubviewsForNames[aViewName])
3199  {
3200  _ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
3201 
3202  [_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
3203 
3204  if (_ephemeralSubviewsForNames[aViewName])
3205  [self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
3206  }
3207 
3208  if (_ephemeralSubviewsForNames[aViewName])
3209  [_ephemeralSubviewsForNames[aViewName] setFrame:frame];
3210  }
3211  else if (_ephemeralSubviewsForNames[aViewName])
3212  {
3213  [_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
3214 
3215  [_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
3216  delete _ephemeralSubviewsForNames[aViewName];
3217  }
3218 
3219  return _ephemeralSubviewsForNames[aViewName];
3220 }
3221 
3222 - (CPView)ephemeralSubviewNamed:(CPString)aViewName
3223 {
3224  if (!_ephemeralSubviewsForNames)
3225  return nil;
3226 
3227  return (_ephemeralSubviewsForNames[aViewName] || nil);
3228 }
3229 
3230 @end
3231 
3232 var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
3233  CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews",
3234  CPViewBackgroundColorKey = @"CPViewBackgroundColor",
3235  CPViewBoundsKey = @"CPViewBoundsKey",
3236  CPViewFrameKey = @"CPViewFrameKey",
3237  CPViewHitTestsKey = @"CPViewHitTestsKey",
3238  CPViewToolTipKey = @"CPViewToolTipKey",
3239  CPViewIsHiddenKey = @"CPViewIsHiddenKey",
3240  CPViewOpacityKey = @"CPViewOpacityKey",
3241  CPViewSubviewsKey = @"CPViewSubviewsKey",
3242  CPViewSuperviewKey = @"CPViewSuperviewKey",
3243  CPViewTagKey = @"CPViewTagKey",
3244  CPViewThemeClassKey = @"CPViewThemeClassKey",
3245  CPViewThemeStateKey = @"CPViewThemeStateKey",
3246  CPViewWindowKey = @"CPViewWindowKey",
3247  CPViewNextKeyViewKey = @"CPViewNextKeyViewKey",
3248  CPViewPreviousKeyViewKey = @"CPViewPreviousKeyViewKey",
3249  CPReuseIdentifierKey = @"CPReuseIdentifierKey",
3250  CPViewScaleKey = @"CPViewScaleKey",
3251  CPViewSizeScaleKey = @"CPViewSizeScaleKey",
3252  CPViewIsScaledKey = @"CPViewIsScaledKey";
3253 
3254 @implementation CPView (CPCoding)
3255 
3261 - (id)initWithCoder:(CPCoder)aCoder
3262 {
3263  // We create the DOMElement "early" because there is a chance that we
3264  // will decode our superview before we are done decoding, at which point
3265  // we have to have an element to place in the tree. Perhaps there is
3266  // a more "elegant" way to do this...?
3267 #if PLATFORM(DOM)
3268  _DOMElement = DOMElementPrototype.cloneNode(false);
3269  AppKitTagDOMElement(self, _DOMElement);
3270 #endif
3271 
3272  // Also decode these "early".
3273  _frame = [aCoder decodeRectForKey:CPViewFrameKey];
3274  _bounds = [aCoder decodeRectForKey:CPViewBoundsKey];
3275 
3276  self = [super initWithCoder:aCoder];
3277 
3278  if (self)
3279  {
3280  // We have to manually check because it may be 0, so we can't use ||
3281  _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
3282  _identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey];
3283 
3284  _window = [aCoder decodeObjectForKey:CPViewWindowKey];
3285  _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
3286 
3287  // We have to manually add the subviews so that they will receive
3288  // viewWillMoveToSuperview: and viewDidMoveToSuperview:
3289  _subviews = [];
3290 
3291  var subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
3292 
3293  for (var i = 0, count = [subviews count]; i < count; ++i)
3294  {
3295  // addSubview won't do anything if the superview is already self, so clear it
3296  subviews[i]._superview = nil;
3297  [self addSubview:subviews[i]];
3298  }
3299 
3300  // FIXME: Should we encode/decode this?
3301  _registeredDraggedTypes = [CPSet set];
3302  _registeredDraggedTypesArray = [];
3303 
3304  // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded.
3305  // We make sure we don't override the value by checking if it was already set.
3306  if (_autoresizingMask === nil)
3307  _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
3308 
3309  _autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
3310 
3311  _hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey];
3312 
3313  [self _setupToolTipHandlers];
3314  _toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey];
3315 
3316  if (_toolTip)
3317  [self _installToolTipEventHandlers];
3318 
3319  _scaleSize = [aCoder containsValueForKey:CPViewScaleKey] ? [aCoder decodeSizeForKey:CPViewScaleKey] : CGSizeMake(1.0, 1.0);
3320  _hierarchyScaleSize = [aCoder containsValueForKey:CPViewSizeScaleKey] ? [aCoder decodeSizeForKey:CPViewSizeScaleKey] : CGSizeMake(1.0, 1.0);
3321  _isScaled = [aCoder containsValueForKey:CPViewIsScaledKey] ? [aCoder decodeBoolForKey:CPViewIsScaledKey] : NO;
3322 
3323  // DOM SETUP
3324 #if PLATFORM(DOM)
3325  _DOMImageParts = [];
3326  _DOMImageSizes = [];
3327 
3328  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(_frame), CGRectGetMinY(_frame));
3329  [self _setDisplayServerSetStyleSize:_frame.size];
3330 
3331  var index = 0,
3332  count = _subviews.length;
3333 
3334  for (; index < count; ++index)
3335  {
3336  CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement);
3337  //_subviews[index]._superview = self;
3338  }
3339 #endif
3340 
3341  [self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
3342 
3343  if ([aCoder containsValueForKey:CPViewOpacityKey])
3344  [self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
3345  else
3346  _opacity = 1.0;
3347 
3348  [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
3349  [self _setupViewFlags];
3350 
3351  _theme = [CPTheme defaultTheme];
3352  _themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
3353  _themeState = CPThemeState([aCoder decodeIntForKey:CPViewThemeStateKey]);
3354  _themeAttributes = {};
3355 
3356  var theClass = [self class],
3357  themeClass = [self themeClass],
3358  attributes = [theClass _themeAttributes],
3359  count = attributes.length;
3360 
3361  while (count--)
3362  {
3363  var attributeName = attributes[count--];
3364 
3365  _themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass);
3366  }
3367 
3368  [self setNeedsDisplay:YES];
3369  [self setNeedsLayout];
3370  }
3371 
3372  return self;
3373 }
3374 
3379 - (void)encodeWithCoder:(CPCoder)aCoder
3380 {
3381  [super encodeWithCoder:aCoder];
3382 
3383  if (_tag !== -1)
3384  [aCoder encodeInt:_tag forKey:CPViewTagKey];
3385 
3386  [aCoder encodeRect:_frame forKey:CPViewFrameKey];
3387  [aCoder encodeRect:_bounds forKey:CPViewBoundsKey];
3388 
3389  // This will come out nil on the other side with decodeObjectForKey:
3390  if (_window !== nil)
3391  [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey];
3392 
3393  var count = [_subviews count],
3394  encodedSubviews = _subviews;
3395 
3396  if (count > 0 && [_ephemeralSubviews count] > 0)
3397  {
3398  encodedSubviews = [encodedSubviews copy];
3399 
3400  while (count--)
3401  if ([_ephemeralSubviews containsObject:encodedSubviews[count]])
3402  encodedSubviews.splice(count, 1);
3403  }
3404 
3405  if (encodedSubviews.length > 0)
3406  [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey];
3407 
3408  // This will come out nil on the other side with decodeObjectForKey:
3409  if (_superview !== nil)
3410  [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey];
3411 
3412  if (_autoresizingMask !== CPViewNotSizable)
3413  [aCoder encodeInt:_autoresizingMask forKey:CPViewAutoresizingMaskKey];
3414 
3415  if (!_autoresizesSubviews)
3416  [aCoder encodeBool:_autoresizesSubviews forKey:CPViewAutoresizesSubviewsKey];
3417 
3418  if (_backgroundColor !== nil)
3419  [aCoder encodeObject:_backgroundColor forKey:CPViewBackgroundColorKey];
3420 
3421  if (_hitTests !== YES)
3422  [aCoder encodeBool:_hitTests forKey:CPViewHitTestsKey];
3423 
3424  if (_opacity !== 1.0)
3425  [aCoder encodeFloat:_opacity forKey:CPViewOpacityKey];
3426 
3427  if (_isHidden)
3428  [aCoder encodeBool:_isHidden forKey:CPViewIsHiddenKey];
3429 
3430  if (_toolTip)
3431  [aCoder encodeObject:_toolTip forKey:CPViewToolTipKey];
3432 
3433  var nextKeyView = [self nextKeyView];
3434 
3435  if (nextKeyView !== nil && ![nextKeyView isEqual:self])
3436  [aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
3437 
3438  var previousKeyView = [self previousKeyView];
3439 
3440  if (previousKeyView !== nil && ![previousKeyView isEqual:self])
3441  [aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
3442 
3443  [aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
3444  [aCoder encodeInt:CPThemeStateName(_themeState) forKey:CPViewThemeStateKey];
3445 
3446  for (var attributeName in _themeAttributes)
3447  if (_themeAttributes.hasOwnProperty(attributeName))
3448  CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
3449 
3450  if (_identifier)
3451  [aCoder encodeObject:_identifier forKey:CPReuseIdentifierKey];
3452 
3453  [aCoder encodeSize:[self scaleSize] forKey:CPViewScaleKey];
3454  [aCoder encodeSize:[self _hierarchyScaleSize] forKey:CPViewSizeScaleKey];
3455  [aCoder encodeBool:_isScaled forKey:CPViewIsScaledKey];
3456 }
3457 
3458 @end
3459 
3460 var _CPViewFullScreenModeStateMake = function(aView)
3461 {
3462  var superview = aView._superview;
3463 
3464  return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview };
3465 };
3466 
3467 var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
3468 {
3469  var transform = CGAffineTransformMakeIdentity(),
3470  sameWindow = YES,
3471  fromWindow = nil,
3472  toWindow = nil;
3473 
3474  if (fromView)
3475  {
3476  var view = fromView;
3477 
3478  // FIXME: This doesn't handle the case when the outside views are equal.
3479  // If we have a fromView, "climb up" the view tree until
3480  // we hit the root node or we hit the toLayer.
3481  while (view && view != toView)
3482  {
3483  var frame = view._frame;
3484 
3485  if (view._isScaled)
3486  {
3487  var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
3488  CGAffineTransformConcatTo(transform, affineZoom, transform);
3489  }
3490 
3491  transform.tx += CGRectGetMinX(frame);
3492  transform.ty += CGRectGetMinY(frame);
3493 
3494  if (view._boundsTransform)
3495  {
3496  var inverseBoundsTransform = CGAffineTransformMakeCopy(view._boundsTransform);
3497 
3498  if (view._isScaled)
3499  {
3500  var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
3501  CGAffineTransformConcatTo(inverseBoundsTransform, affineZoom, inverseBoundsTransform);
3502  }
3503 
3504  CGAffineTransformConcatTo(transform, inverseBoundsTransform, transform);
3505  }
3506 
3507  view = view._superview;
3508  }
3509 
3510  // If we hit toView, then we're done.
3511  if (view === toView)
3512  {
3513  return transform;
3514  }
3515  else if (fromView && toView)
3516  {
3517  fromWindow = [fromView window];
3518  toWindow = [toView window];
3519 
3520  if (fromWindow && toWindow && fromWindow !== toWindow)
3521  sameWindow = NO;
3522  }
3523  }
3524 
3525  // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way.
3526  var view = toView,
3527  transform2 = CGAffineTransformMakeIdentity();
3528 
3529  while (view && view != fromView)
3530  {
3531  var frame = CGRectMakeCopy(view._frame);
3532 
3533  // FIXME : For now we don't care about rotate transform and so on
3534  if (view._isScaled)
3535  {
3536  transform2.a *= 1 / view._scaleSize.width;
3537  transform2.d *= 1 / view._scaleSize.height;
3538  }
3539 
3540  transform2.tx += CGRectGetMinX(frame) * transform2.a;
3541  transform2.ty += CGRectGetMinY(frame) * transform2.d;
3542 
3543  if (view._boundsTransform)
3544  {
3545  var inverseBoundsTransform = CGAffineTransformMakeIdentity();
3546  inverseBoundsTransform.tx -= view._inverseBoundsTransform.tx * transform2.a;
3547  inverseBoundsTransform.ty -= view._inverseBoundsTransform.ty * transform2.d;
3548 
3549  CGAffineTransformConcatTo(transform2, inverseBoundsTransform, transform2);
3550  }
3551 
3552  view = view._superview;
3553  }
3554 
3555  transform2.tx = -transform2.tx;
3556  transform2.ty = -transform2.ty;
3557 
3558  if (view === fromView)
3559  {
3560  // toView is inside of fromView
3561  return transform2;
3562  }
3563 
3564  CGAffineTransformConcatTo(transform, transform2, transform);
3565 
3566  return transform;
3567 
3568 
3569 
3570 /* var views = [],
3571  view = toView;
3572 
3573  while (view)
3574  {
3575  views.push(view);
3576  view = view._superview;
3577  }
3578 
3579  var index = views.length;
3580 
3581  while (index--)
3582  {
3583  var frame = views[index]._frame;
3584 
3585  transform.tx -= CGRectGetMinX(frame);
3586  transform.ty -= CGRectGetMinY(frame);
3587  }*/
3588 
3589  return transform;
3590 };
3591 
3592 @implementation CPView (CPSynthesizedAccessors)
3593 
3597 - (CPString)identifier
3598 {
3599  return _identifier;
3600 }
3601 
3605 - (void)setIdentifier:(CPString)aValue
3606 {
3607  _identifier = aValue;
3608 }
3609 
3613 - (CPString)toolTip
3614 {
3615  return _toolTip;
3616 }
3617 
3618 @end