API  0.9.6
 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 
26 
27 
28 /*
29  @global
30  @group CPViewAutoresizingMasks
31  The default resizingMask, the view will not resize or reposition itself.
32 */
34 /*
35  @global
36  @group CPViewAutoresizingMasks
37  Allow for flexible space on the left hand side of the view.
38 */
40 /*
41  @global
42  @group CPViewAutoresizingMasks
43  The view should grow and shrink horizontally with its parent view.
44 */
46 /*
47  @global
48  @group CPViewAutoresizingMasks
49  Allow for flexible space to the right hand side of the view.
50 */
52 /*
53  @global
54  @group CPViewAutoresizingMasks
55  Allow for flexible space above the view.
56 */
58 /*
59  @global
60  @group CPViewAutoresizingMasks
61  The view should grow and shrink vertically with its parent view.
62 */
64 /*
65  @global
66  @group CPViewAutoresizingMasks
67  Allow for flexible space below the view.
68 */
70 
71 CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
72 CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
73 
76 
77 #if PLATFORM(DOM)
78 var DOMElementPrototype = nil,
79 
80  BackgroundTrivialColor = 0,
81  BackgroundVerticalThreePartImage = 1,
82  BackgroundHorizontalThreePartImage = 2,
83  BackgroundNinePartImage = 3,
84  BackgroundTransparentColor = 4;
85 #endif
86 
87 var CPViewFlags = { },
90 
91 
109 @implementation CPView : CPResponder
110 {
111  CPWindow _window;
112 
113  CPView _superview;
114  CPArray _subviews;
115 
116  CPGraphicsContext _graphicsContext;
117 
118  int _tag;
119 
120  CGRect _frame;
121  CGRect _bounds;
122  CGAffineTransform _boundsTransform;
123  CGAffineTransform _inverseBoundsTransform;
124 
125  CPSet _registeredDraggedTypes;
126  CPArray _registeredDraggedTypesArray;
127 
128  BOOL _isHidden;
129  BOOL _hitTests;
130  BOOL _clipsToBounds;
131 
132  BOOL _postsFrameChangedNotifications;
133  BOOL _postsBoundsChangedNotifications;
134  BOOL _inhibitFrameAndBoundsChangedNotifications;
135  BOOL _inLiveResize;
136 
137 #if PLATFORM(DOM)
138  DOMElement _DOMElement;
139  DOMElement _DOMContentsElement;
140 
141  CPArray _DOMImageParts;
142  CPArray _DOMImageSizes;
143 
144  unsigned _backgroundType;
145 #endif
146 
147  CGRect _dirtyRect;
148 
149  float _opacity;
150  CPColor _backgroundColor;
151 
152  BOOL _autoresizesSubviews;
153  unsigned _autoresizingMask;
154 
155  CALayer _layer;
156  BOOL _wantsLayer;
157 
158  // Full Screen State
159  BOOL _isInFullScreenMode;
160 
161  _CPViewFullScreenModeState _fullScreenModeState;
162 
163  // Layout Support
164  BOOL _needsLayout;
165  JSObject _ephemeralSubviews;
166 
167  // Theming Support
168  CPTheme _theme;
169  CPString _themeClass;
170  JSObject _themeAttributes;
171  unsigned _themeState;
172 
173  JSObject _ephemeralSubviewsForNames;
174  CPSet _ephereralSubviews;
175 
176  // Key View Support
177  CPView _nextKeyView;
178  CPView _previousKeyView;
179 
180  unsigned _viewClassFlags;
181 
182  // ToolTips
183  CPString _toolTip;
184  Function _toolTipFunctionIn;
185  Function _toolTipFunctionOut;
186  BOOL _toolTipInstalled;
187 }
188 
189 /*
190  Private method for Objective-J.
191  @ignore
192 */
193 + (void)initialize
194 {
195  if (self !== [CPView class])
196  return;
197 
198 #if PLATFORM(DOM)
199  DOMElementPrototype = document.createElement("div");
200 
201  var style = DOMElementPrototype.style;
202 
203  style.overflow = "hidden";
204  style.position = "absolute";
205  style.visibility = "visible";
206  style.zIndex = 0;
207 #endif
208 
210 }
211 
212 - (void)_setupViewFlags
213 {
214  var theClass = [self class],
215  classUID = [theClass UID];
216 
217  if (CPViewFlags[classUID] === undefined)
218  {
219  var flags = 0;
220 
221  if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
222  flags |= CPViewHasCustomDrawRect;
223 
224  if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
226 
227  CPViewFlags[classUID] = flags;
228  }
229 
230  _viewClassFlags = CPViewFlags[classUID];
231 }
232 
233 - (void)_setupToolTipHandlers
234 {
235  _toolTipInstalled = NO;
236  _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
237  _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
238 }
239 
240 + (CPSet)keyPathsForValuesAffectingFrame
241 {
242  return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
243 }
244 
245 + (CPSet)keyPathsForValuesAffectingBounds
246 {
247  return [CPSet setWithObjects:@"boundsOrigin", @"boundsSize"];
248 }
249 
250 + (CPMenu)defaultMenu
251 {
252  return nil;
253 }
254 
255 - (id)init
256 {
257  return [self initWithFrame:CGRectMakeZero()];
258 }
259 
264 - (id)initWithFrame:(CGRect)aFrame
265 {
266  self = [super init];
267 
268  if (self)
269  {
270  var width = _CGRectGetWidth(aFrame),
271  height = _CGRectGetHeight(aFrame);
272 
273  _subviews = [];
274  _registeredDraggedTypes = [CPSet set];
275  _registeredDraggedTypesArray = [];
276 
277  _tag = -1;
278 
279  _frame = _CGRectMakeCopy(aFrame);
280  _bounds = _CGRectMake(0.0, 0.0, width, height);
281 
282  _autoresizingMask = CPViewNotSizable;
283  _autoresizesSubviews = YES;
284  _clipsToBounds = YES;
285 
286  _opacity = 1.0;
287  _isHidden = NO;
288  _hitTests = YES;
289 
290 #if PLATFORM(DOM)
291  _DOMElement = DOMElementPrototype.cloneNode(false);
292 
293  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(aFrame), _CGRectGetMinY(aFrame));
294  CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
295 
296  if (typeof(appkit_tag_dom_elements) !== "undefined" && !!appkit_tag_dom_elements)
297  _DOMElement.setAttribute("data-cappuccino-view", [self className]);
298 
299  _DOMImageParts = [];
300  _DOMImageSizes = [];
301 #endif
302 
303  _theme = [CPTheme defaultTheme];
304  _themeState = CPThemeStateNormal;
305 
306  [self _setupToolTipHandlers];
307  [self _setupViewFlags];
308 
309  [self _loadThemeAttributes];
310  }
311 
312  return self;
313 }
314 
315 
321 - (void)setToolTip:(CPString)aToolTip
322 {
323  if (_toolTip == aToolTip)
324  return;
325 
326  if (aToolTip && ![aToolTip isKindOfClass:CPString])
327  aToolTip = [aToolTip description];
328 
329  _toolTip = aToolTip;
330 
331  if (_toolTip)
332  [self _installToolTipEventHandlers];
333  else
334  [self _uninstallToolTipEventHandlers];
335 }
336 
341 - (void)_installToolTipEventHandlers
342 {
343  if (_toolTipInstalled)
344  return;
345 
346 #if PLATFORM(DOM)
347  if (_DOMElement.addEventListener)
348  {
349  _DOMElement.addEventListener("mouseover", _toolTipFunctionIn, NO);
350  _DOMElement.addEventListener("keypress", _toolTipFunctionOut, NO);
351  _DOMElement.addEventListener("mouseout", _toolTipFunctionOut, NO);
352  }
353  else if (_DOMElement.attachEvent)
354  {
355  _DOMElement.attachEvent("onmouseover", _toolTipFunctionIn);
356  _DOMElement.attachEvent("onkeypress", _toolTipFunctionOut);
357  _DOMElement.attachEvent("onmouseout", _toolTipFunctionOut);
358  }
359 #endif
360 
361  _toolTipInstalled = YES;
362 }
363 
368 - (void)_uninstallToolTipEventHandlers
369 {
370  if (!_toolTipInstalled)
371  return;
372 
373 #if PLATFORM(DOM)
374  if (_DOMElement.removeEventListener)
375  {
376  _DOMElement.removeEventListener("mouseover", _toolTipFunctionIn, NO);
377  _DOMElement.removeEventListener("keypress", _toolTipFunctionOut, NO);
378  _DOMElement.removeEventListener("mouseout", _toolTipFunctionOut, NO);
379  }
380  else if (_DOMElement.detachEvent)
381  {
382  _DOMElement.detachEvent("onmouseover", _toolTipFunctionIn);
383  _DOMElement.detachEvent("onkeypress", _toolTipFunctionOut);
384  _DOMElement.detachEvent("onmouseout", _toolTipFunctionOut);
385  }
386 #endif
387 
388  _toolTipInstalled = NO;
389 }
390 
395 - (CPView)superview
396 {
397  return _superview;
398 }
399 
404 - (CPArray)subviews
405 {
406  return [_subviews copy];
407 }
408 
412 - (CPWindow)window
413 {
414  return _window;
415 }
416 
421 - (void)addSubview:(CPView)aSubview
422 {
423  [self _insertSubview:aSubview atIndex:CPNotFound];
424 }
425 
432 - (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
433 {
434  var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound;
435 
436  // In other words, if no view, then either all the way at the bottom or all the way at the top.
437  if (index === CPNotFound)
438  index = (anOrderingMode === CPWindowAbove) ? [_subviews count] : 0;
439 
440  // else, if we have a view, above if above.
441  else if (anOrderingMode === CPWindowAbove)
442  ++index;
443 
444  [self _insertSubview:aSubview atIndex:index];
445 }
446 
447 /* @ignore */
448 - (void)_insertSubview:(CPView)aSubview atIndex:(int)anIndex
449 {
450  if (aSubview === self)
451  [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"];
452 
453  // We will have to adjust the z-index of all views starting at this index.
454  var count = _subviews.length;
455 
456  // Dirty the key view loop, in case the window wants to auto recalculate it
457  [[self window] _dirtyKeyViewLoop];
458 
459  // If this is already one of our subviews, remove it.
460  if (aSubview._superview == self)
461  {
462  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
463 
464  // FIXME: should this be anIndex >= count? (last one)
465  if (index === anIndex || index === count - 1 && anIndex === count)
466  return;
467 
468  [_subviews removeObjectAtIndex:index];
469 
470 #if PLATFORM(DOM)
471  CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement);
472 #endif
473 
474  if (anIndex > index)
475  --anIndex;
476 
477  //We've effectively made the subviews array shorter, so represent that.
478  --count;
479  }
480  else
481  {
482  // Remove the view from its previous superview.
483  [aSubview removeFromSuperview];
484 
485  // Set the subview's window to our own.
486  [aSubview _setWindow:_window];
487 
488  // Notify the subview that it will be moving.
489  [aSubview viewWillMoveToSuperview:self];
490 
491  // Set ourselves as the superview.
492  aSubview._superview = self;
493  }
494 
495  if (anIndex === CPNotFound || anIndex >= count)
496  {
497  _subviews.push(aSubview);
498 
499 #if PLATFORM(DOM)
500  // Attach the actual node.
501  CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement);
502 #endif
503  }
504  else
505  {
506  _subviews.splice(anIndex, 0, aSubview);
507 
508 #if PLATFORM(DOM)
509  // Attach the actual node.
510  CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement);
511 #endif
512  }
513 
514  [aSubview setNextResponder:self];
515  [aSubview viewDidMoveToSuperview];
516 
517  [self didAddSubview:aSubview];
518 }
519 
524 - (void)didAddSubview:(CPView)aSubview
525 {
526 }
527 
532 - (void)removeFromSuperview
533 {
534  if (!_superview)
535  return;
536 
537  // Dirty the key view loop, in case the window wants to auto recalculate it
538  [[self window] _dirtyKeyViewLoop];
539 
540  [_superview willRemoveSubview:self];
541 
542  [_superview._subviews removeObject:self];
543 
544 #if PLATFORM(DOM)
545  CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
546 #endif
547  _superview = nil;
548 
549  [self _setWindow:nil];
550 }
551 
557 - (void)replaceSubview:(CPView)aSubview with:(CPView)aView
558 {
559  if (aSubview._superview !== self)
560  return;
561 
562  var index = [_subviews indexOfObjectIdenticalTo:aSubview];
563 
564  [aSubview removeFromSuperview];
565 
566  [self _insertSubview:aView atIndex:index];
567 }
568 
569 - (void)setSubviews:(CPArray)newSubviews
570 {
571  if (!newSubviews)
572  [CPException raise:CPInvalidArgumentException reason:"newSubviews cannot be nil in -[CPView setSubviews:]"];
573 
574  // Trivial Case 0: Same array somehow
575  if ([_subviews isEqual:newSubviews])
576  return;
577 
578  // Trivial Case 1: No current subviews, simply add all new subviews.
579  if ([_subviews count] === 0)
580  {
581  var index = 0,
582  count = [newSubviews count];
583 
584  for (; index < count; ++index)
585  [self addSubview:newSubviews[index]];
586 
587  return;
588  }
589 
590  // Trivial Case 2: No new subviews, simply remove all current subviews.
591  if ([newSubviews count] === 0)
592  {
593  var count = [_subviews count];
594 
595  while (count--)
596  [_subviews[count] removeFromSuperview];
597 
598  return;
599  }
600 
601  // Find out the views that were removed.
602  var removedSubviews = [CPMutableSet setWithArray:_subviews];
603 
604  [removedSubviews removeObjectsInArray:newSubviews];
605  [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
606 
607  // Find out which views need to be added.
608  var addedSubviews = [CPMutableSet setWithArray:newSubviews];
609 
610  [addedSubviews removeObjectsInArray:_subviews];
611 
612  var addedSubview = nil,
613  addedSubviewEnumerator = [addedSubviews objectEnumerator];
614 
615  while ((addedSubview = [addedSubviewEnumerator nextObject]) !== nil)
616  [self addSubview:addedSubview];
617 
618  // If the order is fine, no need to reorder.
619  if ([_subviews isEqual:newSubviews])
620  return;
621 
622  _subviews = [newSubviews copy];
623 
624 #if PLATFORM(DOM)
625  var index = 0,
626  count = [_subviews count];
627 
628  for (; index < count; ++index)
629  {
630  var subview = _subviews[index];
631 
632  CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement);
633  CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement);
634  }
635 #endif
636 }
637 
638 /* @ignore */
639 - (void)_setWindow:(CPWindow)aWindow
640 {
641  if (_window === aWindow)
642  return;
643 
644  [[self window] _dirtyKeyViewLoop];
645 
646  // Clear out first responder if we're the first responder and leaving.
647  if ([_window firstResponder] === self)
648  [_window makeFirstResponder:nil];
649 
650  // Notify the view and its subviews
651  [self viewWillMoveToWindow:aWindow];
652 
653  // Unregister the drag events from the current window and register
654  // them in the new window.
655  if (_registeredDraggedTypes)
656  {
657  [_window _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
658  [aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
659  }
660 
661  _window = aWindow;
662 
663  var count = [_subviews count];
664 
665  while (count--)
666  [_subviews[count] _setWindow:aWindow];
667 
668  [self viewDidMoveToWindow];
669 
670  [[self window] _dirtyKeyViewLoop];
671 }
672 
677 - (BOOL)isDescendantOf:(CPView)aView
678 {
679  var view = self;
680 
681  do
682  {
683  if (view == aView)
684  return YES;
685  } while(view = [view superview])
686 
687  return NO;
688 }
689 
693 - (void)viewDidMoveToSuperview
694 {
695 // if (_graphicsContext)
696  [self setNeedsDisplay:YES];
697 }
698 
702 - (void)viewDidMoveToWindow
703 {
704 }
705 
710 - (void)viewWillMoveToSuperview:(CPView)aView
711 {
712 }
713 
718 - (void)viewWillMoveToWindow:(CPWindow)aWindow
719 {
720 }
721 
726 - (void)willRemoveSubview:(CPView)aView
727 {
728 }
729 
734 - (CPMenuItem)enclosingMenuItem
735 {
736  var view = self;
737 
738  while (view && ![view isKindOfClass:[_CPMenuItemView class]])
739  view = [view superview];
740 
741  if (view)
742  return view._menuItem;
743 
744  return nil;
745 /* var view = self,
746  enclosingMenuItem = _enclosingMenuItem;
747 
748  while (!enclosingMenuItem && (view = view._enclosingMenuItem))
749  view = [view superview];
750 
751  return enclosingMenuItem;*/
752 }
753 
754 - (void)setTag:(CPInteger)aTag
755 {
756  _tag = aTag;
757 }
758 
759 - (CPInteger)tag
760 {
761  return _tag;
762 }
763 
764 - (CPView)viewWithTag:(CPInteger)aTag
765 {
766  if ([self tag] == aTag)
767  return self;
768 
769  var index = 0,
770  count = _subviews.length;
771 
772  for (; index < count; ++index)
773  {
774  var view = [_subviews[index] viewWithTag:aTag];
775 
776  if (view)
777  return view;
778  }
779 
780  return nil;
781 }
782 
787 - (BOOL)isFlipped
788 {
789  return YES;
790 }
791 
799 - (void)setFrame:(CGRect)aFrame
800 {
801  if (_CGRectEqualToRect(_frame, aFrame))
802  return;
803 
804  _inhibitFrameAndBoundsChangedNotifications = YES;
805 
806  [self setFrameOrigin:aFrame.origin];
807  [self setFrameSize:aFrame.size];
808 
809  _inhibitFrameAndBoundsChangedNotifications = NO;
810 
811  if (_postsFrameChangedNotifications)
812  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
813 }
814 
819 - (CGRect)frame
820 {
821  return _CGRectMakeCopy(_frame);
822 }
823 
824 - (CGPoint)frameOrigin
825 {
826  return _CGPointMakeCopy(_frame.origin);
827 }
828 
829 - (CGSize)frameSize
830 {
831  return _CGSizeMakeCopy(_frame.size);
832 }
833 
841 - (void)setCenter:(CGPoint)aPoint
842 {
843  [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
844 }
845 
850 - (CGPoint)center
851 {
852  return CGPointMake(_frame.size.width / 2.0 + _frame.origin.x, _frame.size.height / 2.0 + _frame.origin.y);
853 }
854 
862 - (void)setFrameOrigin:(CGPoint)aPoint
863 {
864  var origin = _frame.origin;
865 
866  if (!aPoint || _CGPointEqualToPoint(origin, aPoint))
867  return;
868 
869  origin.x = aPoint.x;
870  origin.y = aPoint.y;
871 
872  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
873  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
874 
875 #if PLATFORM(DOM)
876  var transform = _superview ? _superview._boundsTransform : NULL;
877 
878  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
879 #endif
880 }
881 
888 - (void)setFrameSize:(CGSize)aSize
889 {
890  var size = _frame.size;
891 
892  if (!aSize || _CGSizeEqualToSize(size, aSize))
893  return;
894 
895  var oldSize = _CGSizeMakeCopy(size);
896 
897  size.width = aSize.width;
898  size.height = aSize.height;
899 
900  if (YES)
901  {
902  _bounds.size.width = aSize.width;
903  _bounds.size.height = aSize.height;
904  }
905 
906  if (_layer)
907  [_layer _owningViewBoundsChanged];
908 
909  if (_autoresizesSubviews)
910  [self resizeSubviewsWithOldSize:oldSize];
911 
912  [self setNeedsLayout];
913  [self setNeedsDisplay:YES];
914 
915 #if PLATFORM(DOM)
916  CPDOMDisplayServerSetStyleSize(_DOMElement, size.width, size.height);
917 
918  if (_DOMContentsElement)
919  {
920  CPDOMDisplayServerSetSize(_DOMContentsElement, size.width, size.height);
921  CPDOMDisplayServerSetStyleSize(_DOMContentsElement, size.width, size.height);
922  }
923 
924  if (_backgroundType !== BackgroundTrivialColor)
925  {
926  if (_backgroundType === BackgroundTransparentColor)
927  {
928  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
929  }
930  else
931  {
932  var images = [[_backgroundColor patternImage] imageSlices],
933  partIndex = 0;
934 
935  if (_backgroundType === BackgroundVerticalThreePartImage)
936  {
937  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
938  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
939 
940  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
941  if (top)
942  {
943  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, top);
944  partIndex++;
945  }
946  if (_DOMImageSizes[1])
947  {
948  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height - top - bottom);
949  partIndex++;
950  }
951  if (bottom)
952  {
953  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, bottom);
954  }
955  }
956  else if (_backgroundType === BackgroundHorizontalThreePartImage)
957  {
958  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
959  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
960 
961  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
962  if (left)
963  {
964  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, size.height);
965  partIndex++;
966  }
967  if (_DOMImageSizes[1])
968  {
969  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width - left - right, size.height);
970  partIndex++;
971  }
972  if (right)
973  {
974  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, size.height);
975  }
976  }
977  else if (_backgroundType === BackgroundNinePartImage)
978  {
979  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
980  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
981  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
982  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
983  width = size.width - left - right,
984  height = size.height - top - bottom;
985 
986  if (_DOMImageSizes[0])
987  partIndex++;
988  if (_DOMImageSizes[1])
989  {
990  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, top);
991  partIndex++;
992  }
993  if (_DOMImageSizes[2])
994  partIndex++;
995  if (_DOMImageSizes[3])
996  {
997  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
998  partIndex++;
999  }
1000  if (_DOMImageSizes[4])
1001  {
1002  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1003  partIndex++;
1004  }
1005  if (_DOMImageSizes[5])
1006  {
1007  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1008  partIndex++;
1009  }
1010  if (_DOMImageSizes[6])
1011  partIndex++;
1012  if (_DOMImageSizes[7])
1013  {
1014  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
1015  }
1016  }
1017  }
1018  }
1019 #endif
1020 
1021  if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1022  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
1023 }
1024 
1030 - (void)setBounds:(CGRect)bounds
1031 {
1032  if (_CGRectEqualToRect(_bounds, bounds))
1033  return;
1034 
1035  _inhibitFrameAndBoundsChangedNotifications = YES;
1036 
1037  [self setBoundsOrigin:bounds.origin];
1038  [self setBoundsSize:bounds.size];
1039 
1040  _inhibitFrameAndBoundsChangedNotifications = NO;
1041 
1042  if (_postsBoundsChangedNotifications)
1043  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1044 }
1045 
1050 - (CGRect)bounds
1051 {
1052  return _CGRectMakeCopy(_bounds);
1053 }
1054 
1055 - (CGPoint)boundsOrigin
1056 {
1057  return _CGPointMakeCopy(_bounds.origin);
1058 }
1059 
1060 - (CGSize)boundsSize
1061 {
1062  return _CGSizeMakeCopy(_bounds.size);
1063 }
1064 
1071 - (void)setBoundsOrigin:(CGPoint)aPoint
1072 {
1073  var origin = _bounds.origin;
1074 
1075  if (_CGPointEqualToPoint(origin, aPoint))
1076  return;
1077 
1078  origin.x = aPoint.x;
1079  origin.y = aPoint.y;
1080 
1081  if (origin.x != 0 || origin.y != 0)
1082  {
1083  _boundsTransform = _CGAffineTransformMakeTranslation(-origin.x, -origin.y);
1084  _inverseBoundsTransform = CGAffineTransformInvert(_boundsTransform);
1085  }
1086  else
1087  {
1088  _boundsTransform = nil;
1089  _inverseBoundsTransform = nil;
1090  }
1091 
1092 #if PLATFORM(DOM)
1093  var index = _subviews.length;
1094 
1095  while (index--)
1096  {
1097  var view = _subviews[index],
1098  origin = view._frame.origin;
1099 
1100  CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y);
1101  }
1102 #endif
1103 
1104  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1105  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1106 }
1107 
1114 - (void)setBoundsSize:(CGSize)aSize
1115 {
1116  var size = _bounds.size;
1117 
1118  if (_CGSizeEqualToSize(size, aSize))
1119  return;
1120 
1121  var frameSize = _frame.size;
1122 
1123  if (!_CGSizeEqualToSize(size, frameSize))
1124  {
1125  var origin = _bounds.origin;
1126 
1127  origin.x /= size.width / frameSize.width;
1128  origin.y /= size.height / frameSize.height;
1129  }
1130 
1131  size.width = aSize.width;
1132  size.height = aSize.height;
1133 
1134  if (!_CGSizeEqualToSize(size, frameSize))
1135  {
1136  var origin = _bounds.origin;
1137 
1138  origin.x *= size.width / frameSize.width;
1139  origin.y *= size.height / frameSize.height;
1140  }
1141 
1142  if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
1143  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1144 }
1145 
1146 
1151 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
1152 {
1153  var mask = [self autoresizingMask];
1154 
1155  if (mask == CPViewNotSizable)
1156  return;
1157 
1158  var frame = _superview._frame,
1159  newFrame = _CGRectMakeCopy(_frame),
1160  dX = (_CGRectGetWidth(frame) - aSize.width) /
1161  (((mask & CPViewMinXMargin) ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
1162  dY = (_CGRectGetHeight(frame) - aSize.height) /
1163  ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0));
1164 
1165  if (mask & CPViewMinXMargin)
1166  newFrame.origin.x += dX;
1167  if (mask & CPViewWidthSizable)
1168  newFrame.size.width += dX;
1169 
1170  if (mask & CPViewMinYMargin)
1171  newFrame.origin.y += dY;
1172  if (mask & CPViewHeightSizable)
1173  newFrame.size.height += dY;
1174 
1175  [self setFrame:newFrame];
1176 }
1177 
1182 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
1183 {
1184  var count = _subviews.length;
1185 
1186  while (count--)
1187  [_subviews[count] resizeWithOldSuperviewSize:aSize];
1188 }
1189 
1197 - (void)setAutoresizesSubviews:(BOOL)aFlag
1198 {
1199  _autoresizesSubviews = !!aFlag;
1200 }
1201 
1206 - (BOOL)autoresizesSubviews
1207 {
1208  return _autoresizesSubviews;
1209 }
1210 
1215 - (void)setAutoresizingMask:(unsigned)aMask
1216 {
1217  _autoresizingMask = aMask;
1218 }
1219 
1223 - (unsigned)autoresizingMask
1224 {
1225  return _autoresizingMask;
1226 }
1227 
1228 // Fullscreen Mode
1229 
1233 - (BOOL)enterFullScreenMode
1234 {
1235  return [self enterFullScreenMode:nil withOptions:nil];
1236 }
1237 
1243 - (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options
1244 {
1245  _fullScreenModeState = _CPViewFullScreenModeStateMake(self);
1246 
1247  var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
1248 
1249  [fullScreenWindow setLevel:CPScreenSaverWindowLevel];
1250  [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1251 
1252  var contentView = [fullScreenWindow contentView];
1253 
1254  [contentView setBackgroundColor:[CPColor blackColor]];
1255  [contentView addSubview:self];
1256 
1257  [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
1258  [self setFrame:CGRectMakeCopy([contentView bounds])];
1259 
1260  [fullScreenWindow makeKeyAndOrderFront:self];
1261 
1262  [fullScreenWindow makeFirstResponder:self];
1263 
1264  _isInFullScreenMode = YES;
1265 
1266  return YES;
1267 }
1268 
1272 - (void)exitFullScreenMode
1273 {
1274  [self exitFullScreenModeWithOptions:nil];
1275 }
1276 
1281 - (void)exitFullScreenModeWithOptions:(CPDictionary)options
1282 {
1283  if (!_isInFullScreenMode)
1284  return;
1285 
1286  _isInFullScreenMode = NO;
1287 
1288  [self setFrame:_fullScreenModeState.frame];
1289  [self setAutoresizingMask:_fullScreenModeState.autoresizingMask];
1290  [_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index];
1291 
1292  [[self window] orderOut:self];
1293 }
1294 
1298 - (BOOL)isInFullScreenMode
1299 {
1300  return _isInFullScreenMode;
1301 }
1302 
1307 - (void)setHidden:(BOOL)aFlag
1308 {
1309  aFlag = !!aFlag;
1310 
1311  if (_isHidden === aFlag)
1312  return;
1313 
1314 // FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
1315 // _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
1316  _isHidden = aFlag;
1317 #if PLATFORM(DOM)
1318  _DOMElement.style.display = _isHidden ? "none" : "block";
1319 #endif
1320 
1321  if (aFlag)
1322  {
1323  var view = [_window firstResponder];
1324 
1325  if ([view isKindOfClass:[CPView class]])
1326  {
1327  do
1328  {
1329  if (self == view)
1330  {
1331  [_window makeFirstResponder:[self nextValidKeyView]];
1332  break;
1333  }
1334  }
1335  while (view = [view superview]);
1336  }
1337 
1338  [self _notifyViewDidHide];
1339  }
1340  else
1341  {
1342  [self _notifyViewDidUnhide];
1343  }
1344 }
1345 
1346 - (void)_notifyViewDidHide
1347 {
1348  [self viewDidHide];
1349 
1350  var count = [_subviews count];
1351  while (count--)
1352  [_subviews[count] _notifyViewDidHide];
1353 }
1354 
1355 - (void)_notifyViewDidUnhide
1356 {
1357  [self viewDidUnhide];
1358 
1359  var count = [_subviews count];
1360  while (count--)
1361  [_subviews[count] _notifyViewDidUnhide];
1362 }
1363 
1367 - (BOOL)isHidden
1368 {
1369  return _isHidden;
1370 }
1371 
1372 - (void)setClipsToBounds:(BOOL)shouldClip
1373 {
1374  if (_clipsToBounds === shouldClip)
1375  return;
1376 
1377  _clipsToBounds = shouldClip;
1378 
1379 #if PLATFORM(DOM)
1380  _DOMElement.style.overflow = _clipsToBounds ? "hidden" : "visible";
1381 #endif
1382 }
1383 
1384 - (BOOL)clipsToBounds
1385 {
1386  return _clipsToBounds;
1387 }
1388 
1394 - (void)setAlphaValue:(float)anAlphaValue
1395 {
1396  if (_opacity == anAlphaValue)
1397  return;
1398 
1399  _opacity = anAlphaValue;
1400 
1401 #if PLATFORM(DOM)
1402 
1404  {
1405  if (anAlphaValue === 1.0)
1406  try { _DOMElement.style.removeAttribute("filter") } catch (anException) { }
1407  else
1408  _DOMElement.style.filter = "alpha(opacity=" + anAlphaValue * 100 + ")";
1409  }
1410  else
1411  _DOMElement.style.opacity = anAlphaValue;
1412 
1413 #endif
1414 }
1415 
1420 - (float)alphaValue
1421 {
1422  return _opacity;
1423 }
1424 
1429 - (BOOL)isHiddenOrHasHiddenAncestor
1430 {
1431  var view = self;
1432 
1433  while (view && ![view isHidden])
1434  view = [view superview];
1435 
1436  return view !== nil;
1437 }
1438 
1442 - (BOOL)_isVisible
1443 {
1444  return ![self isHiddenOrHasHiddenAncestor] && [[self window] isVisible];
1445 }
1446 
1456 - (void)viewDidHide
1457 {
1458 
1459 }
1460 
1470 - (void)viewDidUnhide
1471 {
1472 
1473 }
1474 
1480 //FIXME: should be NO by default?
1481 - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
1482 {
1483  return YES;
1484 }
1485 
1490 - (BOOL)hitTests
1491 {
1492  return _hitTests;
1493 }
1494 
1499 - (void)setHitTests:(BOOL)shouldHitTest
1500 {
1501  _hitTests = !!shouldHitTest;
1502 }
1503 
1509 - (CPView)hitTest:(CPPoint)aPoint
1510 {
1511  if (_isHidden || !_hitTests || !_CGRectContainsPoint(_frame, aPoint))
1512  return nil;
1513 
1514  var view = nil,
1515  i = _subviews.length,
1516  adjustedPoint = _CGPointMake(aPoint.x - _CGRectGetMinX(_frame), aPoint.y - _CGRectGetMinY(_frame));
1517 
1518  if (_inverseBoundsTransform)
1519  adjustedPoint = _CGPointApplyAffineTransform(adjustedPoint, _inverseBoundsTransform);
1520 
1521  while (i--)
1522  if (view = [_subviews[i] hitTest:adjustedPoint])
1523  return view;
1524 
1525  return self;
1526 }
1527 
1531 - (BOOL)needsPanelToBecomeKey
1532 {
1533  return NO;
1534 }
1535 
1540 - (BOOL)mouseDownCanMoveWindow
1541 {
1542  return ![self isOpaque];
1543 }
1544 
1545 - (void)mouseDown:(CPEvent)anEvent
1546 {
1547  if ([self mouseDownCanMoveWindow])
1548  [super mouseDown:anEvent];
1549 }
1550 
1551 - (void)rightMouseDown:(CPEvent)anEvent
1552 {
1553  var menu = [self menuForEvent:anEvent];
1554  if (menu)
1555  [CPMenu popUpContextMenu:menu withEvent:anEvent forView:self];
1556  else if ([[self nextResponder] isKindOfClass:CPView])
1557  [super rightMouseDown:anEvent];
1558  else
1559  [[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
1560 }
1561 
1562 - (CPMenu)menuForEvent:(CPEvent)anEvent
1563 {
1564  return [self menu] || [[self class] defaultMenu];
1565 }
1566 
1571 - (void)setBackgroundColor:(CPColor)aColor
1572 {
1573  if (_backgroundColor == aColor)
1574  return;
1575 
1576  if (aColor == [CPNull null])
1577  aColor = nil;
1578 
1579  _backgroundColor = aColor;
1580 
1581 #if PLATFORM(DOM)
1582  var patternImage = [_backgroundColor patternImage],
1583  colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
1584  colorHasAlpha = colorExists && [_backgroundColor alphaComponent] < 1.0,
1585  supportsRGBA = CPFeatureIsCompatible(CPCSSRGBAFeature),
1586  colorNeedsDOMElement = colorHasAlpha && !supportsRGBA,
1587  amount = 0,
1588  slices;
1589 
1590  if ([patternImage isThreePartImage])
1591  {
1592  _backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage;
1593  amount = 3;
1594  }
1595  else if ([patternImage isNinePartImage])
1596  {
1597  _backgroundType = BackgroundNinePartImage;
1598  amount = 9;
1599  }
1600  else
1601  {
1602  _backgroundType = colorNeedsDOMElement ? BackgroundTransparentColor : BackgroundTrivialColor;
1603  amount = (colorNeedsDOMElement ? 1 : 0) - _DOMImageParts.length;
1604  }
1605 
1606  // 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.
1607  if (_backgroundType === BackgroundVerticalThreePartImage || _backgroundType === BackgroundHorizontalThreePartImage || _backgroundType === BackgroundNinePartImage)
1608  {
1609  slices = [patternImage imageSlices];
1610 
1611  // We won't need more divs than there are slices.
1612  amount = MIN(amount, slices.length);
1613 
1614  for (var i = 0, count = slices.length; i < count; i++)
1615  {
1616  var image = slices[i],
1617  size = [image size];
1618 
1619  if (!size || (size.width == 0 && size.height == 0))
1620  size = nil;
1621 
1622  _DOMImageSizes[i] = size;
1623 
1624  // If there's a nil slice or a slice with no size, it won't need a div.
1625  if (!size)
1626  amount--;
1627  }
1628 
1629  // Now that we know how many divs we really need, compare that to number we actually have.
1630  amount -= _DOMImageParts.length;
1631  }
1632 
1633  // Make sure the number of divs we have match our needs.
1634  if (amount > 0)
1635  {
1636  while (amount--)
1637  {
1638  var DOMElement = DOMElementPrototype.cloneNode(false);
1639 
1640  DOMElement.style.zIndex = -1000;
1641 
1642  _DOMImageParts.push(DOMElement);
1643  _DOMElement.appendChild(DOMElement);
1644  }
1645  }
1646  else
1647  {
1648  amount = -amount;
1649  while (amount--)
1650  _DOMElement.removeChild(_DOMImageParts.pop());
1651  }
1652 
1653  if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
1654  {
1655  var colorCSS = colorExists ? [_backgroundColor cssString] : "";
1656 
1657  if (colorNeedsDOMElement)
1658  {
1659  _DOMElement.style.background = "";
1660  _DOMImageParts[0].style.background = [_backgroundColor cssString];
1661 
1663  _DOMImageParts[0].style.filter = "alpha(opacity=" + [_backgroundColor alphaComponent] * 100 + ")";
1664  else
1665  _DOMImageParts[0].style.opacity = [_backgroundColor alphaComponent];
1666 
1667  var size = [self bounds].size;
1668  CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
1669  }
1670  else
1671  _DOMElement.style.background = colorCSS;
1672  }
1673  else
1674  {
1675  var frameSize = _frame.size,
1676  partIndex = 0;
1677 
1678  for (var i = 0; i < slices.length; i++)
1679  {
1680  var size = _DOMImageSizes[i];
1681 
1682  if (!size)
1683  continue;
1684 
1685  var image = slices[i];
1686 
1687  // // If image was nil, size should have been nil too.
1688  // assert(image != nil);
1689 
1690  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height);
1691 
1692  _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")";
1693 
1694  if (!supportsRGBA)
1695  {
1697  try { _DOMImageParts[partIndex].style.removeAttribute("filter") } catch (anException) { }
1698  else
1699  _DOMImageParts[partIndex].style.opacity = 1.0;
1700  }
1701 
1702  partIndex++;
1703  }
1704 
1705  if (_backgroundType == BackgroundNinePartImage)
1706  {
1707  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1708  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
1709  top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1710  bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
1711  width = frameSize.width - left - right,
1712  height = frameSize.height - top - bottom;
1713 
1714  partIndex = 0;
1715 
1716  if (_DOMImageSizes[0])
1717  {
1718  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1719  partIndex++;
1720  }
1721  if (_DOMImageSizes[1])
1722  {
1723  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
1724  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[1].height);
1725  partIndex++;
1726  }
1727  if (_DOMImageSizes[2])
1728  {
1729  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1730  partIndex++;
1731  }
1732  if (_DOMImageSizes[3])
1733  {
1734  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1735  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
1736  partIndex++;
1737  }
1738  if (_DOMImageSizes[4])
1739  {
1740  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, top);
1741  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
1742  partIndex++;
1743  }
1744  if (_DOMImageSizes[5])
1745  {
1746  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1747  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
1748  partIndex++;
1749  }
1750  if (_DOMImageSizes[6])
1751  {
1752  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1753  partIndex++;
1754  }
1755  if (_DOMImageSizes[7])
1756  {
1757  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, left, 0.0);
1758  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
1759  partIndex++;
1760  }
1761  if (_DOMImageSizes[8])
1762  {
1763  CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1764  }
1765  }
1766  else if (_backgroundType == BackgroundVerticalThreePartImage)
1767  {
1768  var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
1769  bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
1770 
1771  partIndex = 0;
1772 
1773  // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
1774  if (top)
1775  {
1776  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1777  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, top);
1778  partIndex++;
1779  }
1780  if (_DOMImageSizes[1])
1781  {
1782  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
1783  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, frameSize.height - top - bottom);
1784  partIndex++;
1785  }
1786  if (bottom)
1787  {
1788  CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1789  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, bottom);
1790  }
1791  }
1792  else if (_backgroundType == BackgroundHorizontalThreePartImage)
1793  {
1794  var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
1795  right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
1796 
1797  partIndex = 0;
1798 
1799  // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
1800  if (left)
1801  {
1802  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1803  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, frameSize.height);
1804  partIndex++;
1805  }
1806  if (_DOMImageSizes[1])
1807  {
1808  CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
1809  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width - left - right, frameSize.height);
1810  partIndex++;
1811  }
1812  if (right)
1813  {
1814  CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
1815  CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, frameSize.height);
1816  }
1817  }
1818  }
1819 #endif
1820 }
1821 
1825 - (CPColor)backgroundColor
1826 {
1827  return _backgroundColor;
1828 }
1829 
1830 // Converting Coordinates
1837 - (CGPoint)convertPoint:(CGPoint)aPoint fromView:(CPView)aView
1838 {
1839  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
1840 }
1841 
1847 - (CGPoint)convertPointFromBase:(CGPoint)aPoint
1848 {
1849  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(nil, self));
1850 }
1851 
1858 - (CGPoint)convertPoint:(CGPoint)aPoint toView:(CPView)aView
1859 {
1860  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
1861 }
1862 
1868 - (CGPoint)convertPointToBase:(CGPoint)aPoint
1869 {
1870  return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, nil));
1871 }
1872 
1879 - (CGSize)convertSize:(CGSize)aSize fromView:(CPView)aView
1880 {
1881  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(aView, self));
1882 }
1883 
1890 - (CGSize)convertSize:(CGSize)aSize toView:(CPView)aView
1891 {
1892  return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(self, aView));
1893 }
1894 
1901 - (CGRect)convertRect:(CGRect)aRect fromView:(CPView)aView
1902 {
1903  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
1904 }
1905 
1911 - (CGRect)convertRectFromBase:(CGRect)aRect
1912 {
1913  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(nil, self));
1914 }
1915 
1922 - (CGRect)convertRect:(CGRect)aRect toView:(CPView)aView
1923 {
1924  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
1925 }
1926 
1932 - (CGRect)convertRectToBase:(CGRect)aRect
1933 {
1934  return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, nil));
1935 }
1936 
1949 - (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications
1950 {
1951  shouldPostFrameChangedNotifications = !!shouldPostFrameChangedNotifications;
1952 
1953  if (_postsFrameChangedNotifications === shouldPostFrameChangedNotifications)
1954  return;
1955 
1956  _postsFrameChangedNotifications = shouldPostFrameChangedNotifications;
1957 
1958  if (_postsFrameChangedNotifications)
1959  [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
1960 }
1961 
1965 - (BOOL)postsFrameChangedNotifications
1966 {
1967  return _postsFrameChangedNotifications;
1968 }
1969 
1982 - (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications
1983 {
1984  shouldPostBoundsChangedNotifications = !!shouldPostBoundsChangedNotifications;
1985 
1986  if (_postsBoundsChangedNotifications === shouldPostBoundsChangedNotifications)
1987  return;
1988 
1989  _postsBoundsChangedNotifications = shouldPostBoundsChangedNotifications;
1990 
1991  if (_postsBoundsChangedNotifications)
1992  [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
1993 }
1994 
2000 - (BOOL)postsBoundsChangedNotifications
2001 {
2002  return _postsBoundsChangedNotifications;
2003 }
2004 
2015 - (void)dragImage:(CPImage)anImage at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2016 {
2017  [_window dragImage:anImage at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2018 }
2019 
2030 - (void)dragView:(CPView)aView at:(CPPoint)aLocation offset:(CPSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
2031 {
2032  [_window dragView:aView at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
2033 }
2034 
2039 - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
2040 {
2041  if (!pasteboardTypes || ![pasteboardTypes count])
2042  return;
2043 
2044  var theWindow = [self window];
2045 
2046  [theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2047  [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
2048  [theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
2049 
2050  _registeredDraggedTypesArray = nil;
2051 }
2052 
2057 - (CPArray)registeredDraggedTypes
2058 {
2059  if (!_registeredDraggedTypesArray)
2060  _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
2061 
2062  return _registeredDraggedTypesArray;
2063 }
2064 
2068 - (void)unregisterDraggedTypes
2069 {
2070  [[self window] _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
2071 
2072  _registeredDraggedTypes = [CPSet set];
2073  _registeredDraggedTypesArray = [];
2074 }
2075 
2080 - (void)drawRect:(CPRect)aRect
2081 {
2082 
2083 }
2084 
2085 // Displaying
2086 
2090 - (void)setNeedsDisplay:(BOOL)aFlag
2091 {
2092  if (aFlag)
2093  [self setNeedsDisplayInRect:[self bounds]];
2094 }
2095 
2100 - (void)setNeedsDisplayInRect:(CPRect)aRect
2101 {
2102  if (!(_viewClassFlags & CPViewHasCustomDrawRect))
2103  return;
2104 
2105  if (_CGRectIsEmpty(aRect))
2106  return;
2107 
2108  if (_dirtyRect && !_CGRectIsEmpty(_dirtyRect))
2109  _dirtyRect = CGRectUnion(aRect, _dirtyRect);
2110  else
2111  _dirtyRect = _CGRectMakeCopy(aRect);
2112 
2113  _CPDisplayServerAddDisplayObject(self);
2114 }
2115 
2116 - (BOOL)needsDisplay
2117 {
2118  return _dirtyRect && !_CGRectIsEmpty(_dirtyRect);
2119 }
2120 
2124 - (void)displayIfNeeded
2125 {
2126  if ([self needsDisplay])
2127  [self displayRect:_dirtyRect];
2128 }
2129 
2133 - (void)display
2134 {
2135  [self displayRect:[self visibleRect]];
2136 }
2137 
2138 - (void)displayIfNeededInRect:(CGRect)aRect
2139 {
2140  if ([self needsDisplay])
2141  [self displayRect:aRect];
2142 }
2143 
2148 - (void)displayRect:(CPRect)aRect
2149 {
2150  [self viewWillDraw];
2151 
2152  [self displayRectIgnoringOpacity:aRect inContext:nil];
2153 
2154  _dirtyRect = NULL;
2155 }
2156 
2157 - (void)displayRectIgnoringOpacity:(CGRect)aRect inContext:(CPGraphicsContext)aGraphicsContext
2158 {
2159  if ([self isHidden])
2160  return;
2161 
2162 #if PLATFORM(DOM)
2163  [self lockFocus];
2164 
2165  CGContextClearRect([[CPGraphicsContext currentContext] graphicsPort], aRect);
2166 
2167  [self drawRect:aRect];
2168  [self unlockFocus];
2169 #endif
2170 }
2171 
2172 - (void)viewWillDraw
2173 {
2174 }
2175 
2179 - (void)lockFocus
2180 {
2181  if (!_graphicsContext)
2182  {
2183  var graphicsPort = CGBitmapGraphicsContextCreate();
2184 
2185  _DOMContentsElement = graphicsPort.DOMElement;
2186 
2187  _DOMContentsElement.style.zIndex = -100;
2188 
2189  _DOMContentsElement.style.overflow = "hidden";
2190  _DOMContentsElement.style.position = "absolute";
2191  _DOMContentsElement.style.visibility = "visible";
2192 
2193  _DOMContentsElement.width = ROUND(_CGRectGetWidth(_frame));
2194  _DOMContentsElement.height = ROUND(_CGRectGetHeight(_frame));
2195 
2196  _DOMContentsElement.style.top = "0px";
2197  _DOMContentsElement.style.left = "0px";
2198  _DOMContentsElement.style.width = ROUND(_CGRectGetWidth(_frame)) + "px";
2199  _DOMContentsElement.style.height = ROUND(_CGRectGetHeight(_frame)) + "px";
2200 
2201 #if PLATFORM(DOM)
2202  // The performance implications of this aren't clear, but without this subviews might not be redrawn when this
2203  // view moves.
2205  _DOMElement.style.webkitTransform = 'translateX(0)';
2206 
2207  CPDOMDisplayServerAppendChild(_DOMElement, _DOMContentsElement);
2208 #endif
2209  _graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:graphicsPort flipped:YES];
2210  }
2211 
2212  [CPGraphicsContext setCurrentContext:_graphicsContext];
2213 
2214  CGContextSaveGState([_graphicsContext graphicsPort]);
2215 }
2216 
2220 - (void)unlockFocus
2221 {
2222  CGContextRestoreGState([_graphicsContext graphicsPort]);
2223 
2225 }
2226 
2227 - (void)setNeedsLayout
2228 {
2229  if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews))
2230  return;
2231 
2232  _needsLayout = YES;
2233 
2234  _CPDisplayServerAddLayoutObject(self);
2235 }
2236 
2237 - (void)layoutIfNeeded
2238 {
2239  if (_needsLayout)
2240  {
2241  _needsLayout = NO;
2242 
2243  [self layoutSubviews];
2244  }
2245 }
2246 
2247 - (void)layoutSubviews
2248 {
2249 }
2250 
2254 - (BOOL)isOpaque
2255 {
2256  return NO;
2257 }
2258 
2262 - (CGRect)visibleRect
2263 {
2264  if (!_superview)
2265  return _bounds;
2266 
2267  return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds);
2268 }
2269 
2270 // Scrolling
2271 /* @ignore */
2272 - (CPScrollView)_enclosingClipView
2273 {
2274  var superview = _superview,
2275  clipViewClass = [CPClipView class];
2276 
2277  while (superview && ![superview isKindOfClass:clipViewClass])
2278  superview = superview._superview;
2279 
2280  return superview;
2281 }
2282 
2287 - (void)scrollPoint:(CGPoint)aPoint
2288 {
2289  var clipView = [self _enclosingClipView];
2290 
2291  if (!clipView)
2292  return;
2293 
2294  [clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]];
2295 }
2296 
2302 - (BOOL)scrollRectToVisible:(CGRect)aRect
2303 {
2304  var visibleRect = [self visibleRect];
2305 
2306  // Make sure we have a rect that exists.
2307  aRect = CGRectIntersection(aRect, _bounds);
2308 
2309  // If aRect is empty or is already visible then no scrolling required.
2310  if (_CGRectIsEmpty(aRect) || CGRectContainsRect(visibleRect, aRect))
2311  return NO;
2312 
2313  var enclosingClipView = [self _enclosingClipView];
2314 
2315  // If we're not in a clip view, then there isn't much we can do.
2316  if (!enclosingClipView)
2317  return NO;
2318 
2319  var scrollPoint = _CGPointMakeCopy(visibleRect.origin);
2320 
2321  // One of the following has to be true since our current visible rect didn't contain aRect.
2322  if (_CGRectGetMinX(aRect) <= _CGRectGetMinX(visibleRect))
2323  scrollPoint.x = _CGRectGetMinX(aRect);
2324  else if (_CGRectGetMaxX(aRect) > _CGRectGetMaxX(visibleRect))
2325  scrollPoint.x += _CGRectGetMaxX(aRect) - _CGRectGetMaxX(visibleRect);
2326 
2327  if (_CGRectGetMinY(aRect) <= _CGRectGetMinY(visibleRect))
2328  scrollPoint.y = CGRectGetMinY(aRect);
2329  else if (_CGRectGetMaxY(aRect) > _CGRectGetMaxY(visibleRect))
2330  scrollPoint.y += _CGRectGetMaxY(aRect) - _CGRectGetMaxY(visibleRect);
2331 
2332  [enclosingClipView scrollToPoint:CGPointMake(scrollPoint.x, scrollPoint.y)];
2333 
2334  return YES;
2335 }
2336 
2337 /*
2338  FIXME Not yet implemented
2339 */
2340 - (BOOL)autoscroll:(CPEvent)anEvent
2341 {
2342  return [[self superview] autoscroll:anEvent];
2343 }
2344 
2351 - (CGRect)adjustScroll:(CGRect)proposedVisibleRect
2352 {
2353  return proposedVisibleRect;
2354 }
2355 
2359 - (void)scrollRect:(CGRect)aRect by:(float)anAmount
2360 {
2361 
2362 }
2363 
2368 - (CPScrollView)enclosingScrollView
2369 {
2370  var superview = _superview,
2371  scrollViewClass = [CPScrollView class];
2372 
2373  while (superview && ![superview isKindOfClass:scrollViewClass])
2374  superview = superview._superview;
2375 
2376  return superview;
2377 }
2378 
2384 - (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
2385 {
2386  [aClipView scrollToPoint:aPoint];
2387 }
2388 
2394 - (void)reflectScrolledClipView:(CPClipView)aClipView
2395 {
2396 }
2397 
2401 - (BOOL)inLiveResize
2402 {
2403  return _inLiveResize;
2404 }
2405 
2415 - (void)viewWillStartLiveResize
2416 {
2417  _inLiveResize = YES;
2418 }
2419 
2430 - (void)viewDidEndLiveResize
2431 {
2432  _inLiveResize = NO;
2433 }
2434 
2435 @end
2436 
2437 @implementation CPView (KeyView)
2438 
2453 - (BOOL)performKeyEquivalent:(CPEvent)anEvent
2454 {
2455  var count = [_subviews count];
2456 
2457  // Is reverse iteration correct here? It matches the other (correct) code like hit testing.
2458  while (count--)
2459  if ([_subviews[count] performKeyEquivalent:anEvent])
2460  return YES;
2461 
2462  return NO;
2463 }
2464 
2465 - (BOOL)canBecomeKeyView
2466 {
2467  return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor];
2468 }
2469 
2470 - (CPView)nextKeyView
2471 {
2472  return _nextKeyView;
2473 }
2474 
2475 - (CPView)nextValidKeyView
2476 {
2477  var result = [self nextKeyView],
2478  firstResult = result;
2479 
2480  while (result && ![result canBecomeKeyView])
2481  {
2482  result = [result nextKeyView];
2483 
2484  // Cycled.
2485  if (result === firstResult)
2486  return nil;
2487  }
2488 
2489  return result;
2490 }
2491 
2492 - (CPView)previousKeyView
2493 {
2494  return _previousKeyView;
2495 }
2496 
2497 - (CPView)previousValidKeyView
2498 {
2499  var result = [self previousKeyView],
2500  firstResult = result;
2501 
2502  while (result && ![result canBecomeKeyView])
2503  {
2504  result = [result previousKeyView];
2505 
2506  // Cycled.
2507  if (result === firstResult)
2508  return nil;
2509  }
2510 
2511  return result;
2512 }
2513 
2514 - (void)_setPreviousKeyView:(CPView)previous
2515 {
2516  if ([previous isEqual:self])
2517  _previousKeyView = nil;
2518  else
2519  _previousKeyView = previous;
2520 }
2521 
2522 - (void)setNextKeyView:(CPView)next
2523 {
2524  if ([next isEqual:self])
2525  _nextKeyView = nil;
2526  else
2527  {
2528  _nextKeyView = next;
2529  [_nextKeyView _setPreviousKeyView:self];
2530  }
2531 }
2532 
2533 @end
2534 
2536 
2540 - (void)setLayer:(CALayer)aLayer
2541 {
2542  if (_layer == aLayer)
2543  return;
2544 
2545  if (_layer)
2546  {
2547  _layer._owningView = nil;
2548 #if PLATFORM(DOM)
2549  _DOMElement.removeChild(_layer._DOMElement);
2550 #endif
2551  }
2552 
2553  _layer = aLayer;
2554 
2555  if (_layer)
2556  {
2557  var bounds = CGRectMakeCopy([self bounds]);
2558 
2559  [_layer _setOwningView:self];
2560 
2561 #if PLATFORM(DOM)
2562  _layer._DOMElement.style.zIndex = 100;
2563 
2564  _DOMElement.appendChild(_layer._DOMElement);
2565 #endif
2566  }
2567 }
2568 
2572 - (CALayer)layer
2573 {
2574  return _layer;
2575 }
2576 
2581 - (void)setWantsLayer:(BOOL)aFlag
2582 {
2583  _wantsLayer = !!aFlag;
2584 }
2585 
2590 - (BOOL)wantsLayer
2591 {
2592  return _wantsLayer;
2593 }
2594 
2595 @end
2596 
2597 @implementation CPView (Theming)
2598 #pragma mark Theme States
2599 
2600 - (unsigned)themeState
2601 {
2602  return _themeState;
2603 }
2604 
2605 - (BOOL)hasThemeState:(CPThemeState)aState
2606 {
2607  // Because CPThemeStateNormal is defined as 0 we need to check for it explicitly here
2608  if (aState === CPThemeStateNormal && _themeState === CPThemeStateNormal)
2609  return YES;
2610 
2611  return !!(_themeState & ((typeof aState === "string") ? CPThemeState(aState) : aState));
2612 }
2613 
2614 - (BOOL)setThemeState:(CPThemeState)aState
2615 {
2616  var newState = (typeof aState === "string") ? CPThemeState(aState) : aState;
2617 
2618  if (_themeState & newState)
2619  return NO;
2620 
2621  _themeState |= newState;
2622 
2623  [self setNeedsLayout];
2624  [self setNeedsDisplay:YES];
2625 
2626  return YES;
2627 }
2628 
2629 - (BOOL)unsetThemeState:(CPThemeState)aState
2630 {
2631  var newState = ((typeof aState === "string") ? CPThemeState(aState) : aState);
2632 
2633  if (!(_themeState & newState))
2634  return NO;
2635 
2636  _themeState &= ~newState;
2637 
2638  [self setNeedsLayout];
2639  [self setNeedsDisplay:YES];
2640 
2641  return YES;
2642 }
2643 
2644 #pragma mark Theme Attributes
2645 
2646 + (CPString)defaultThemeClass
2647 {
2648  return nil;
2649 }
2650 
2651 - (CPString)themeClass
2652 {
2653  if (_themeClass)
2654  return _themeClass;
2655 
2656  return [[self class] defaultThemeClass];
2657 }
2658 
2659 - (void)setThemeClass:(CPString)theClass
2660 {
2661  _themeClass = theClass;
2662 
2663  [self _loadThemeAttributes];
2664 
2665  [self setNeedsLayout];
2666  [self setNeedsDisplay:YES];
2667 }
2668 
2669 + (CPDictionary)themeAttributes
2670 {
2671  return nil;
2672 }
2673 
2674 + (CPArray)_themeAttributes
2675 {
2676  if (!CachedThemeAttributes)
2677  CachedThemeAttributes = {};
2678 
2679  var theClass = [self class],
2680  CPViewClass = [CPView class],
2681  attributes = [],
2682  nullValue = [CPNull null];
2683 
2684  for (; theClass && theClass !== CPViewClass; theClass = [theClass superclass])
2685  {
2686  var cachedAttributes = CachedThemeAttributes[class_getName(theClass)];
2687 
2688  if (cachedAttributes)
2689  {
2690  attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
2691  CachedThemeAttributes[[self className]] = attributes;
2692 
2693  break;
2694  }
2695 
2696  var attributeDictionary = [theClass themeAttributes];
2697 
2698  if (!attributeDictionary)
2699  continue;
2700 
2701  var attributeKeys = [attributeDictionary allKeys],
2702  attributeCount = attributeKeys.length;
2703 
2704  while (attributeCount--)
2705  {
2706  var attributeName = attributeKeys[attributeCount],
2707  attributeValue = [attributeDictionary objectForKey:attributeName];
2708 
2709  attributes.push(attributeValue === nullValue ? nil : attributeValue);
2710  attributes.push(attributeName);
2711  }
2712  }
2713 
2714  return attributes;
2715 }
2716 
2717 - (void)_loadThemeAttributes
2718 {
2719  var theClass = [self class],
2720  attributes = [theClass _themeAttributes],
2721  count = attributes.length;
2722 
2723  if (!count)
2724  return;
2725 
2726  var theme = [self theme],
2727  themeClass = [self themeClass];
2728 
2729  _themeAttributes = {};
2730 
2731  while (count--)
2732  {
2733  var attributeName = attributes[count--],
2734  attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
2735 
2736  [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
2737 
2738  _themeAttributes[attributeName] = attribute;
2739  }
2740 }
2741 
2742 - (void)setTheme:(CPTheme)aTheme
2743 {
2744  if (_theme === aTheme)
2745  return;
2746 
2747  _theme = aTheme;
2748 
2749  [self viewDidChangeTheme];
2750 }
2751 
2752 - (CPTheme)theme
2753 {
2754  return _theme;
2755 }
2756 
2757 - (void)viewDidChangeTheme
2758 {
2759  if (!_themeAttributes)
2760  return;
2761 
2762  var theme = [self theme],
2763  themeClass = [self themeClass];
2764 
2765  for (var attributeName in _themeAttributes)
2766  if (_themeAttributes.hasOwnProperty(attributeName))
2767  [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
2768 
2769  [self setNeedsLayout];
2770  [self setNeedsDisplay:YES];
2771 }
2772 
2773 - (CPDictionary)_themeAttributeDictionary
2774 {
2775  var dictionary = [CPDictionary dictionary];
2776 
2777  if (_themeAttributes)
2778  {
2779  var theme = [self theme];
2780 
2781  for (var attributeName in _themeAttributes)
2782  if (_themeAttributes.hasOwnProperty(attributeName))
2783  [dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
2784  }
2785 
2786  return dictionary;
2787 }
2788 
2789 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(CPThemeState)aState
2790 {
2791  if (!_themeAttributes || !_themeAttributes[aName])
2792  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
2793 
2794  var currentValue = [self currentValueForThemeAttribute:aName];
2795 
2796  [_themeAttributes[aName] setValue:aValue forState:aState];
2797 
2798  if ([self currentValueForThemeAttribute:aName] === currentValue)
2799  return;
2800 
2801  [self setNeedsDisplay:YES];
2802  [self setNeedsLayout];
2803 }
2804 
2805 - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
2806 {
2807  if (!_themeAttributes || !_themeAttributes[aName])
2808  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
2809 
2810  var currentValue = [self currentValueForThemeAttribute:aName];
2811 
2812  [_themeAttributes[aName] setValue:aValue];
2813 
2814  if ([self currentValueForThemeAttribute:aName] === currentValue)
2815  return;
2816 
2817  [self setNeedsDisplay:YES];
2818  [self setNeedsLayout];
2819 }
2820 
2821 - (id)valueForThemeAttribute:(CPString)aName inState:(CPThemeState)aState
2822 {
2823  if (!_themeAttributes || !_themeAttributes[aName])
2824  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
2825 
2826  return [_themeAttributes[aName] valueForState:aState];
2827 }
2828 
2829 - (id)valueForThemeAttribute:(CPString)aName
2830 {
2831  if (!_themeAttributes || !_themeAttributes[aName])
2832  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
2833 
2834  return [_themeAttributes[aName] value];
2835 }
2836 
2837 - (id)currentValueForThemeAttribute:(CPString)aName
2838 {
2839  if (!_themeAttributes || !_themeAttributes[aName])
2840  [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
2841 
2842  return [_themeAttributes[aName] valueForState:_themeState];
2843 }
2844 
2845 - (BOOL)hasThemeAttribute:(CPString)aName
2846 {
2847  return (_themeAttributes && _themeAttributes[aName] !== undefined);
2848 }
2849 
2858 - (void)registerThemeValues:(CPArray)themeValues
2859 {
2860  for (var i = 0; i < themeValues.length; ++i)
2861  {
2862  var attributeValueState = themeValues[i],
2863  attribute = attributeValueState[0],
2864  value = attributeValueState[1],
2865  state = attributeValueState[2];
2866 
2867  if (state)
2868  [self setValue:value forThemeAttribute:attribute inState:state];
2869  else
2870  [self setValue:value forThemeAttribute:attribute];
2871  }
2872 }
2873 
2884 - (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
2885 {
2886  // Register inherited values first, then override those with the subtheme values.
2887  if (inheritedValues)
2888  [self registerThemeValues:inheritedValues];
2889 
2890  if (themeValues)
2891  [self registerThemeValues:themeValues];
2892 }
2893 
2894 - (CPView)createEphemeralSubviewNamed:(CPString)aViewName
2895 {
2896  return nil;
2897 }
2898 
2899 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
2900 {
2901  return _CGRectMakeZero();
2902 }
2903 
2904 - (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
2905  positioned:(CPWindowOrderingMode)anOrderingMode
2906  relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
2907 {
2908  if (!_ephemeralSubviewsForNames)
2909  {
2910  _ephemeralSubviewsForNames = {};
2911  _ephemeralSubviews = [CPSet set];
2912  }
2913 
2914  var frame = [self rectForEphemeralSubviewNamed:aViewName];
2915 
2916  if (frame)
2917  {
2918  if (!_ephemeralSubviewsForNames[aViewName])
2919  {
2920  _ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
2921 
2922  [_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
2923 
2924  if (_ephemeralSubviewsForNames[aViewName])
2925  [self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
2926  }
2927 
2928  if (_ephemeralSubviewsForNames[aViewName])
2929  [_ephemeralSubviewsForNames[aViewName] setFrame:frame];
2930  }
2931  else if (_ephemeralSubviewsForNames[aViewName])
2932  {
2933  [_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
2934 
2935  [_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
2936  delete _ephemeralSubviewsForNames[aViewName];
2937  }
2938 
2939  return _ephemeralSubviewsForNames[aViewName];
2940 }
2941 
2942 - (CPView)ephemeralSubviewNamed:(CPString)aViewName
2943 {
2944  if (!_ephemeralSubviewsForNames)
2945  return nil;
2946 
2947  return (_ephemeralSubviewsForNames[aViewName] || nil);
2948 }
2949 
2950 @end
2951 
2952 var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
2953  CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews",
2954  CPViewBackgroundColorKey = @"CPViewBackgroundColor",
2955  CPViewBoundsKey = @"CPViewBoundsKey",
2956  CPViewFrameKey = @"CPViewFrameKey",
2957  CPViewHitTestsKey = @"CPViewHitTestsKey",
2958  CPViewToolTipKey = @"CPViewToolTipKey",
2959  CPViewIsHiddenKey = @"CPViewIsHiddenKey",
2960  CPViewOpacityKey = @"CPViewOpacityKey",
2961  CPViewSubviewsKey = @"CPViewSubviewsKey",
2962  CPViewSuperviewKey = @"CPViewSuperviewKey",
2963  CPViewTagKey = @"CPViewTagKey",
2964  CPViewThemeClassKey = @"CPViewThemeClassKey",
2965  CPViewThemeStateKey = @"CPViewThemeStateKey",
2966  CPViewWindowKey = @"CPViewWindowKey",
2967  CPViewNextKeyViewKey = @"CPViewNextKeyViewKey",
2968  CPViewPreviousKeyViewKey = @"CPViewPreviousKeyViewKey";
2969 
2970 @implementation CPView (CPCoding)
2971 
2977 - (id)initWithCoder:(CPCoder)aCoder
2978 {
2979  // We create the DOMElement "early" because there is a chance that we
2980  // will decode our superview before we are done decoding, at which point
2981  // we have to have an element to place in the tree. Perhaps there is
2982  // a more "elegant" way to do this...?
2983 #if PLATFORM(DOM)
2984  _DOMElement = DOMElementPrototype.cloneNode(false);
2985 #endif
2986 
2987  // Also decode these "early".
2988  _frame = [aCoder decodeRectForKey:CPViewFrameKey];
2989  _bounds = [aCoder decodeRectForKey:CPViewBoundsKey];
2990 
2991  self = [super initWithCoder:aCoder];
2992 
2993  if (self)
2994  {
2995  // We have to manually check because it may be 0, so we can't use ||
2996  _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
2997 
2998  _window = [aCoder decodeObjectForKey:CPViewWindowKey];
2999  _subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
3000  _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
3001 
3002  // FIXME: Should we encode/decode this?
3003  _registeredDraggedTypes = [CPSet set];
3004  _registeredDraggedTypesArray = [];
3005 
3006  // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded.
3007  // We make sure we don't override the value by checking if it was already set.
3008  if (_autoresizingMask === nil)
3009  _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
3010 
3011  _autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
3012 
3013  _hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey];
3014 
3015  [self _setupToolTipHandlers];
3016  _toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey];
3017 
3018  if (_toolTip)
3019  [self _installToolTipEventHandlers];
3020 
3021  // DOM SETUP
3022 #if PLATFORM(DOM)
3023  _DOMImageParts = [];
3024  _DOMImageSizes = [];
3025 
3026  CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, _CGRectGetMinX(_frame), _CGRectGetMinY(_frame));
3027  CPDOMDisplayServerSetStyleSize(_DOMElement, _CGRectGetWidth(_frame), _CGRectGetHeight(_frame));
3028 
3029  var index = 0,
3030  count = _subviews.length;
3031 
3032  for (; index < count; ++index)
3033  {
3034  CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement);
3035  //_subviews[index]._superview = self;
3036  }
3037 #endif
3038 
3039  [self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
3040 
3041  if ([aCoder containsValueForKey:CPViewOpacityKey])
3042  [self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
3043  else
3044  _opacity = 1.0;
3045 
3046  [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
3047  [self _setupViewFlags];
3048 
3049  _theme = [CPTheme defaultTheme];
3050  _themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
3051  _themeState = CPThemeState([aCoder decodeIntForKey:CPViewThemeStateKey]);
3052  _themeAttributes = {};
3053 
3054  var theClass = [self class],
3055  themeClass = [self themeClass],
3056  attributes = [theClass _themeAttributes],
3057  count = attributes.length;
3058 
3059  while (count--)
3060  {
3061  var attributeName = attributes[count--];
3062 
3063  _themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass);
3064  }
3065 
3066  [self setNeedsDisplay:YES];
3067  [self setNeedsLayout];
3068  }
3069 
3070  return self;
3071 }
3072 
3077 - (void)encodeWithCoder:(CPCoder)aCoder
3078 {
3079  [super encodeWithCoder:aCoder];
3080 
3081  if (_tag !== -1)
3082  [aCoder encodeInt:_tag forKey:CPViewTagKey];
3083 
3084  [aCoder encodeRect:_frame forKey:CPViewFrameKey];
3085  [aCoder encodeRect:_bounds forKey:CPViewBoundsKey];
3086 
3087  // This will come out nil on the other side with decodeObjectForKey:
3088  if (_window !== nil)
3089  [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey];
3090 
3091  var count = [_subviews count],
3092  encodedSubviews = _subviews;
3093 
3094  if (count > 0 && [_ephemeralSubviews count] > 0)
3095  {
3096  encodedSubviews = [encodedSubviews copy];
3097 
3098  while (count--)
3099  if ([_ephemeralSubviews containsObject:encodedSubviews[count]])
3100  encodedSubviews.splice(count, 1);
3101  }
3102 
3103  if (encodedSubviews.length > 0)
3104  [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey];
3105 
3106  // This will come out nil on the other side with decodeObjectForKey:
3107  if (_superview !== nil)
3108  [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey];
3109 
3110  if (_autoresizingMask !== CPViewNotSizable)
3111  [aCoder encodeInt:_autoresizingMask forKey:CPViewAutoresizingMaskKey];
3112 
3113  if (!_autoresizesSubviews)
3114  [aCoder encodeBool:_autoresizesSubviews forKey:CPViewAutoresizesSubviewsKey];
3115 
3116  if (_backgroundColor !== nil)
3117  [aCoder encodeObject:_backgroundColor forKey:CPViewBackgroundColorKey];
3118 
3119  if (_hitTests !== YES)
3120  [aCoder encodeBool:_hitTests forKey:CPViewHitTestsKey];
3121 
3122  if (_opacity !== 1.0)
3123  [aCoder encodeFloat:_opacity forKey:CPViewOpacityKey];
3124 
3125  if (_isHidden)
3126  [aCoder encodeBool:_isHidden forKey:CPViewIsHiddenKey];
3127 
3128  if (_toolTip)
3129  [aCoder encodeObject:_toolTip forKey:CPViewToolTipKey];
3130 
3131  var nextKeyView = [self nextKeyView];
3132 
3133  if (nextKeyView !== nil && ![nextKeyView isEqual:self])
3134  [aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
3135 
3136  var previousKeyView = [self previousKeyView];
3137 
3138  if (previousKeyView !== nil && ![previousKeyView isEqual:self])
3139  [aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
3140 
3141  [aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
3142  [aCoder encodeInt:CPThemeStateName(_themeState) forKey:CPViewThemeStateKey];
3143 
3144  for (var attributeName in _themeAttributes)
3145  if (_themeAttributes.hasOwnProperty(attributeName))
3146  CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
3147 }
3148 
3149 @end
3150 
3151 var _CPViewFullScreenModeStateMake = function(aView)
3152 {
3153  var superview = aView._superview;
3154 
3155  return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview };
3156 };
3157 
3158 var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
3159 {
3160  var transform = CGAffineTransformMakeIdentity(),
3161  sameWindow = YES,
3162  fromWindow = nil,
3163  toWindow = nil;
3164 
3165  if (fromView)
3166  {
3167  var view = fromView;
3168 
3169  // FIXME: This doesn't handle the case when the outside views are equal.
3170  // If we have a fromView, "climb up" the view tree until
3171  // we hit the root node or we hit the toLayer.
3172  while (view && view != toView)
3173  {
3174  var frame = view._frame;
3175 
3176  transform.tx += _CGRectGetMinX(frame);
3177  transform.ty += _CGRectGetMinY(frame);
3178 
3179  if (view._boundsTransform)
3180  {
3181  _CGAffineTransformConcatTo(transform, view._boundsTransform, transform);
3182  }
3183 
3184  view = view._superview;
3185  }
3186 
3187  // If we hit toView, then we're done.
3188  if (view === toView)
3189  return transform;
3190 
3191  else if (fromView && toView)
3192  {
3193  fromWindow = [fromView window];
3194  toWindow = [toView window];
3195 
3196  if (fromWindow && toWindow && fromWindow !== toWindow)
3197  {
3198  sameWindow = NO;
3199 
3200  var frame = [fromWindow frame];
3201 
3202  transform.tx += _CGRectGetMinX(frame);
3203  transform.ty += _CGRectGetMinY(frame);
3204  }
3205  }
3206  }
3207 
3208  // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way.
3209  var view = toView;
3210 
3211  while (view)
3212  {
3213  var frame = view._frame;
3214 
3215  transform.tx -= _CGRectGetMinX(frame);
3216  transform.ty -= _CGRectGetMinY(frame);
3217 
3218  if (view._boundsTransform)
3219  {
3220  _CGAffineTransformConcatTo(transform, view._inverseBoundsTransform, transform);
3221  }
3222 
3223  view = view._superview;
3224  }
3225 
3226  if (!sameWindow)
3227  {
3228  var frame = [toWindow frame];
3229 
3230  transform.tx -= _CGRectGetMinX(frame);
3231  transform.ty -= _CGRectGetMinY(frame);
3232  }
3233 /* var views = [],
3234  view = toView;
3235 
3236  while (view)
3237  {
3238  views.push(view);
3239  view = view._superview;
3240  }
3241 
3242  var index = views.length;
3243 
3244  while (index--)
3245  {
3246  var frame = views[index]._frame;
3247 
3248  transform.tx -= _CGRectGetMinX(frame);
3249  transform.ty -= _CGRectGetMinY(frame);
3250  }*/
3251 
3252  return transform;
3253 };
3254 
3256 
3260 - (CPString)toolTip
3261 {
3262  return _toolTip;
3263 }
3264 
3265 @end