PageRenderTime 65ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/AppKit/CPView.j

http://github.com/cacaodev/cappuccino
Unknown | 3784 lines | 3031 code | 753 blank | 0 comment | 0 complexity | 189fe62a410f5929b22f78688dde7a7e MD5 | raw file
Possible License(s): LGPL-2.1, MIT
  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. @import <Foundation/CPArray.j>
  23. @import <Foundation/CPObjJRuntime.j>
  24. @import <Foundation/CPSet.j>
  25. @import "CGAffineTransform.j"
  26. @import "CGGeometry.j"
  27. @import "CPColor.j"
  28. @import "CPGraphicsContext.j"
  29. @import "CPResponder.j"
  30. @import "CPTheme.j"
  31. @import "CPWindow_Constants.j"
  32. @import "_CPDisplayServer.j"
  33. @class _CPToolTip
  34. @class CPWindow
  35. @class _CPMenuItemView
  36. @class CPPlatformWindow
  37. @class CPMenu
  38. @class CPClipView
  39. @class CPScrollView
  40. @class CALayer
  41. @global appkit_tag_dom_elements
  42. @typedef _CPViewFullScreenModeState
  43. #if PLATFORM(DOM)
  44. if (typeof(appkit_tag_dom_elements) !== "undefined" && appkit_tag_dom_elements)
  45. {
  46. AppKitTagDOMElement = function(owner, element)
  47. {
  48. element.setAttribute("data-cappuccino-view", [owner className]);
  49. element.setAttribute("data-cappuccino-uid", [owner UID]);
  50. }
  51. }
  52. else
  53. {
  54. AppKitTagDOMElement = function(owner, element)
  55. {
  56. // By default, do nothing.
  57. }
  58. }
  59. #endif
  60. /*
  61. @global
  62. @group CPViewAutoresizingMasks
  63. The default resizingMask, the view will not resize or reposition itself.
  64. */
  65. CPViewNotSizable = 0;
  66. /*
  67. @global
  68. @group CPViewAutoresizingMasks
  69. Allow for flexible space on the left hand side of the view.
  70. */
  71. CPViewMinXMargin = 1;
  72. /*
  73. @global
  74. @group CPViewAutoresizingMasks
  75. The view should grow and shrink horizontally with its parent view.
  76. */
  77. CPViewWidthSizable = 2;
  78. /*
  79. @global
  80. @group CPViewAutoresizingMasks
  81. Allow for flexible space to the right hand side of the view.
  82. */
  83. CPViewMaxXMargin = 4;
  84. /*
  85. @global
  86. @group CPViewAutoresizingMasks
  87. Allow for flexible space above the view.
  88. */
  89. CPViewMinYMargin = 8;
  90. /*
  91. @global
  92. @group CPViewAutoresizingMasks
  93. The view should grow and shrink vertically with its parent view.
  94. */
  95. CPViewHeightSizable = 16;
  96. /*
  97. @global
  98. @group CPViewAutoresizingMasks
  99. Allow for flexible space below the view.
  100. */
  101. CPViewMaxYMargin = 32;
  102. CPViewBoundsDidChangeNotification = @"CPViewBoundsDidChangeNotification";
  103. CPViewFrameDidChangeNotification = @"CPViewFrameDidChangeNotification";
  104. var CachedNotificationCenter = nil,
  105. CachedThemeAttributes = nil;
  106. #if PLATFORM(DOM)
  107. var DOMElementPrototype = nil,
  108. BackgroundTrivialColor = 0,
  109. BackgroundVerticalThreePartImage = 1,
  110. BackgroundHorizontalThreePartImage = 2,
  111. BackgroundNinePartImage = 3,
  112. BackgroundTransparentColor = 4;
  113. #endif
  114. var CPViewFlags = { },
  115. CPViewHasCustomDrawRect = 1 << 0,
  116. CPViewHasCustomLayoutSubviews = 1 << 1;
  117. /*!
  118. @ingroup appkit
  119. @class CPView
  120. <p>CPView is a class which provides facilities for drawing
  121. in a window and receiving events. It is the superclass of many of the visual
  122. elements of the GUI.</p>
  123. <p>In order to display itself, a view must be placed in a window (represented by an
  124. CPWindow object). Within the window is a hierarchy of CPViews,
  125. headed by the window's content view. Every other view in a window is a descendant
  126. of this view.</p>
  127. <p>Subclasses can override \c -drawRect: in order to implement their
  128. appearance. Other methods of CPView and CPResponder can
  129. also be overridden to handle user generated events.
  130. */
  131. @implementation CPView : CPResponder
  132. {
  133. CPWindow _window;
  134. CPView _superview;
  135. CPArray _subviews;
  136. CPGraphicsContext _graphicsContext;
  137. int _tag;
  138. CPString _identifier @accessors(property=identifier);
  139. CGRect _frame;
  140. CGRect _bounds;
  141. CGAffineTransform _boundsTransform;
  142. CGAffineTransform _inverseBoundsTransform;
  143. CPSet _registeredDraggedTypes;
  144. CPArray _registeredDraggedTypesArray;
  145. BOOL _isHidden;
  146. BOOL _hitTests;
  147. BOOL _clipsToBounds;
  148. BOOL _postsFrameChangedNotifications;
  149. BOOL _postsBoundsChangedNotifications;
  150. BOOL _inhibitFrameAndBoundsChangedNotifications;
  151. BOOL _inLiveResize;
  152. BOOL _isSuperviewAClipView;
  153. #if PLATFORM(DOM)
  154. DOMElement _DOMElement;
  155. DOMElement _DOMContentsElement;
  156. CPArray _DOMImageParts;
  157. CPArray _DOMImageSizes;
  158. unsigned _backgroundType;
  159. #endif
  160. CGRect _dirtyRect;
  161. float _opacity;
  162. CPColor _backgroundColor;
  163. BOOL _autoresizesSubviews;
  164. unsigned _autoresizingMask;
  165. CALayer _layer;
  166. BOOL _wantsLayer;
  167. // Full Screen State
  168. BOOL _isInFullScreenMode;
  169. _CPViewFullScreenModeState _fullScreenModeState;
  170. // Zoom Support
  171. BOOL _isScaled;
  172. CGSize _hierarchyScaleSize;
  173. CGSize _scaleSize;
  174. // Layout Support
  175. BOOL _needsLayout;
  176. JSObject _ephemeralSubviews;
  177. // Theming Support
  178. CPTheme _theme;
  179. CPString _themeClass;
  180. JSObject _themeAttributes;
  181. unsigned _themeState;
  182. JSObject _ephemeralSubviewsForNames;
  183. CPSet _ephereralSubviews;
  184. // Key View Support
  185. CPView _nextKeyView;
  186. CPView _previousKeyView;
  187. unsigned _viewClassFlags;
  188. // ToolTips
  189. CPString _toolTip @accessors(getter=toolTip);
  190. Function _toolTipFunctionIn;
  191. Function _toolTipFunctionOut;
  192. BOOL _toolTipInstalled;
  193. BOOL _isObserving;
  194. }
  195. /*
  196. Private method for Objective-J.
  197. @ignore
  198. */
  199. + (void)initialize
  200. {
  201. if (self !== [CPView class])
  202. return;
  203. #if PLATFORM(DOM)
  204. DOMElementPrototype = document.createElement("div");
  205. var style = DOMElementPrototype.style;
  206. style.overflow = "hidden";
  207. style.position = "absolute";
  208. style.visibility = "visible";
  209. style.zIndex = 0;
  210. #endif
  211. CachedNotificationCenter = [CPNotificationCenter defaultCenter];
  212. }
  213. + (Class)_binderClassForBinding:(CPString)aBinding
  214. {
  215. if ([aBinding hasPrefix:CPHiddenBinding])
  216. return [CPMultipleValueOrBinding class];
  217. return [super _binderClassForBinding:aBinding];
  218. }
  219. - (void)_setupViewFlags
  220. {
  221. var theClass = [self class],
  222. classUID = [theClass UID];
  223. if (CPViewFlags[classUID] === undefined)
  224. {
  225. var flags = 0;
  226. if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
  227. flags |= CPViewHasCustomDrawRect;
  228. if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
  229. flags |= CPViewHasCustomLayoutSubviews;
  230. CPViewFlags[classUID] = flags;
  231. }
  232. _viewClassFlags = CPViewFlags[classUID];
  233. }
  234. - (void)_setupToolTipHandlers
  235. {
  236. _toolTipInstalled = NO;
  237. _toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
  238. _toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
  239. }
  240. + (CPSet)keyPathsForValuesAffectingFrame
  241. {
  242. return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
  243. }
  244. + (CPSet)keyPathsForValuesAffectingBounds
  245. {
  246. return [CPSet setWithObjects:@"boundsOrigin", @"boundsSize"];
  247. }
  248. + (CPMenu)defaultMenu
  249. {
  250. return nil;
  251. }
  252. - (id)init
  253. {
  254. return [self initWithFrame:CGRectMakeZero()];
  255. }
  256. /*!
  257. Initializes the receiver for usage with the specified bounding rectangle
  258. @return the initialized view
  259. */
  260. - (id)initWithFrame:(CGRect)aFrame
  261. {
  262. self = [super init];
  263. if (self)
  264. {
  265. var width = CGRectGetWidth(aFrame),
  266. height = CGRectGetHeight(aFrame);
  267. _subviews = [];
  268. _registeredDraggedTypes = [CPSet set];
  269. _registeredDraggedTypesArray = [];
  270. _tag = -1;
  271. _frame = CGRectMakeCopy(aFrame);
  272. _bounds = CGRectMake(0.0, 0.0, width, height);
  273. _autoresizingMask = CPViewNotSizable;
  274. _autoresizesSubviews = YES;
  275. _clipsToBounds = YES;
  276. _opacity = 1.0;
  277. _isHidden = NO;
  278. _hitTests = YES;
  279. _hierarchyScaleSize = CGSizeMake(1.0 , 1.0);
  280. _scaleSize = CGSizeMake(1.0, 1.0);
  281. _isScaled = NO;
  282. _theme = [CPTheme defaultTheme];
  283. _themeState = CPThemeStateNormal;
  284. #if PLATFORM(DOM)
  285. _DOMElement = DOMElementPrototype.cloneNode(false);
  286. AppKitTagDOMElement(self, _DOMElement);
  287. CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(aFrame), CGRectGetMinY(aFrame));
  288. CPDOMDisplayServerSetStyleSize(_DOMElement, width, height);
  289. _DOMImageParts = [];
  290. _DOMImageSizes = [];
  291. #endif
  292. [self _setupToolTipHandlers];
  293. [self _setupViewFlags];
  294. [self _loadThemeAttributes];
  295. }
  296. return self;
  297. }
  298. /*!
  299. Sets the tooltip for the receiver.
  300. @param aToolTip the tooltip
  301. */
  302. - (void)setToolTip:(CPString)aToolTip
  303. {
  304. if (_toolTip == aToolTip)
  305. return;
  306. if (aToolTip && ![aToolTip isKindOfClass:CPString])
  307. aToolTip = [aToolTip description];
  308. _toolTip = aToolTip;
  309. if (_toolTip)
  310. [self _installToolTipEventHandlers];
  311. else
  312. [self _uninstallToolTipEventHandlers];
  313. }
  314. /*! @ignore
  315. Install the handlers for the tooltip
  316. */
  317. - (void)_installToolTipEventHandlers
  318. {
  319. if (_toolTipInstalled)
  320. return;
  321. #if PLATFORM(DOM)
  322. if (_DOMElement.addEventListener)
  323. {
  324. _DOMElement.addEventListener("mouseover", _toolTipFunctionIn, YES);
  325. _DOMElement.addEventListener("keypress", _toolTipFunctionOut, YES);
  326. _DOMElement.addEventListener("mouseout", _toolTipFunctionOut, YES);
  327. }
  328. else if (_DOMElement.attachEvent)
  329. {
  330. _DOMElement.attachEvent("onmouseover", _toolTipFunctionIn);
  331. _DOMElement.attachEvent("onkeypress", _toolTipFunctionOut);
  332. _DOMElement.attachEvent("onmouseout", _toolTipFunctionOut);
  333. }
  334. #endif
  335. _toolTipInstalled = YES;
  336. }
  337. /*! @ignore
  338. Uninstall the handlers for the tooltip
  339. */
  340. - (void)_uninstallToolTipEventHandlers
  341. {
  342. if (!_toolTipInstalled)
  343. return;
  344. #if PLATFORM(DOM)
  345. if (_DOMElement.removeEventListener)
  346. {
  347. _DOMElement.removeEventListener("mouseover", _toolTipFunctionIn, YES);
  348. _DOMElement.removeEventListener("keypress", _toolTipFunctionOut, YES);
  349. _DOMElement.removeEventListener("mouseout", _toolTipFunctionOut, YES);
  350. }
  351. else if (_DOMElement.detachEvent)
  352. {
  353. _DOMElement.detachEvent("onmouseover", _toolTipFunctionIn);
  354. _DOMElement.detachEvent("onkeypress", _toolTipFunctionOut);
  355. _DOMElement.detachEvent("onmouseout", _toolTipFunctionOut);
  356. }
  357. #endif
  358. _toolTipInstalled = NO;
  359. }
  360. /*!
  361. Returns the container view of the receiver
  362. @return the receiver's containing view
  363. */
  364. - (CPView)superview
  365. {
  366. return _superview;
  367. }
  368. /*!
  369. Returns an array of all the views contained as direct children of the receiver
  370. @return an array of CPViews
  371. */
  372. - (CPArray)subviews
  373. {
  374. return [_subviews copy];
  375. }
  376. /*!
  377. Returns the window containing this receiver
  378. */
  379. - (CPWindow)window
  380. {
  381. return _window;
  382. }
  383. /*!
  384. Makes the argument a subview of the receiver.
  385. @param aSubview the CPView to make a subview
  386. */
  387. - (void)addSubview:(CPView)aSubview
  388. {
  389. [self _insertSubview:aSubview atIndex:CPNotFound];
  390. }
  391. /*!
  392. Makes \c aSubview a subview of the receiver. It is positioned relative to \c anotherView
  393. @param aSubview the view to add as a subview
  394. @param anOrderingMode specifies \c aSubview's ordering relative to \c anotherView
  395. @param anotherView \c aSubview will be positioned relative to this argument
  396. */
  397. - (void)addSubview:(CPView)aSubview positioned:(CPWindowOrderingMode)anOrderingMode relativeTo:(CPView)anotherView
  398. {
  399. var index = anotherView ? [_subviews indexOfObjectIdenticalTo:anotherView] : CPNotFound;
  400. // In other words, if no view, then either all the way at the bottom or all the way at the top.
  401. if (index === CPNotFound)
  402. index = (anOrderingMode === CPWindowAbove) ? [_subviews count] : 0;
  403. // else, if we have a view, above if above.
  404. else if (anOrderingMode === CPWindowAbove)
  405. ++index;
  406. [self _insertSubview:aSubview atIndex:index];
  407. }
  408. /* @ignore */
  409. - (void)_insertSubview:(CPView)aSubview atIndex:(int)anIndex
  410. {
  411. if (aSubview === self)
  412. [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"];
  413. #if DEBUG
  414. if (!aSubview._superview && _subviews.indexOf(aSubview) !== CPNotFound)
  415. [CPException raise:CPInvalidArgumentException reason:"can't insert a subview in duplicate (probably partially decoded)"];
  416. #endif
  417. // Notify the subview that it will be moving.
  418. [aSubview viewWillMoveToSuperview:self];
  419. // We will have to adjust the z-index of all views starting at this index.
  420. var count = _subviews.length,
  421. lastWindow;
  422. // Dirty the key view loop, in case the window wants to auto recalculate it
  423. [[self window] _dirtyKeyViewLoop];
  424. // If this is already one of our subviews, remove it.
  425. if (aSubview._superview == self)
  426. {
  427. var index = [_subviews indexOfObjectIdenticalTo:aSubview];
  428. // FIXME: should this be anIndex >= count? (last one)
  429. if (index === anIndex || index === count - 1 && anIndex === count)
  430. return;
  431. [_subviews removeObjectAtIndex:index];
  432. #if PLATFORM(DOM)
  433. CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement);
  434. #endif
  435. if (anIndex > index)
  436. --anIndex;
  437. //We've effectively made the subviews array shorter, so represent that.
  438. --count;
  439. }
  440. else
  441. {
  442. var superview = aSubview._superview;
  443. lastWindow = [superview window];
  444. // Remove the view from its previous superview.
  445. [aSubview _removeFromSuperview];
  446. // Set ourselves as the superview.
  447. aSubview._superview = self;
  448. }
  449. if (anIndex === CPNotFound || anIndex >= count)
  450. {
  451. _subviews.push(aSubview);
  452. #if PLATFORM(DOM)
  453. // Attach the actual node.
  454. CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement);
  455. #endif
  456. }
  457. else
  458. {
  459. _subviews.splice(anIndex, 0, aSubview);
  460. #if PLATFORM(DOM)
  461. // Attach the actual node.
  462. CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement);
  463. #endif
  464. }
  465. [aSubview setNextResponder:self];
  466. [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]];
  467. // If the subview is not hidden and one of its ancestors is hidden,
  468. // notify the subview that it is now hidden.
  469. if (![aSubview isHidden] && [self isHiddenOrHasHiddenAncestor])
  470. [aSubview _notifyViewDidHide];
  471. [aSubview viewDidMoveToSuperview];
  472. // Set the subview's window to our own.
  473. if (_window)
  474. [aSubview _setWindow:_window];
  475. if (!_window && lastWindow)
  476. [aSubview _setWindow:nil];
  477. // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet
  478. // and none of the below matters anyhow.
  479. if (_themeState)
  480. {
  481. if ([self hasThemeState:CPThemeStateFirstResponder])
  482. [aSubview _notifyViewDidBecomeFirstResponder];
  483. else
  484. [aSubview _notifyViewDidResignFirstResponder];
  485. if ([self hasThemeState:CPThemeStateKeyWindow])
  486. [aSubview _notifyWindowDidBecomeKey];
  487. else
  488. [aSubview _notifyWindowDidResignKey];
  489. }
  490. [self didAddSubview:aSubview];
  491. }
  492. /*!
  493. Called when the receiver has added \c aSubview to it's child views.
  494. @param aSubview the view that was added
  495. */
  496. - (void)didAddSubview:(CPView)aSubview
  497. {
  498. }
  499. /*!
  500. Removes the receiver from it's container view and window.
  501. Does nothing if there's no container view.
  502. */
  503. - (void)removeFromSuperview
  504. {
  505. var superview = _superview;
  506. [self viewWillMoveToSuperview:nil];
  507. [self _removeFromSuperview];
  508. [self viewDidMoveToSuperview];
  509. if (superview)
  510. [self _setWindow:nil];
  511. }
  512. - (void)_removeFromSuperview
  513. {
  514. if (!_superview)
  515. return;
  516. // Dirty the key view loop, in case the window wants to auto recalculate it
  517. [[self window] _dirtyKeyViewLoop];
  518. [_superview willRemoveSubview:self];
  519. [_superview._subviews removeObjectIdenticalTo:self];
  520. #if PLATFORM(DOM)
  521. CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement);
  522. #endif
  523. // If the view is not hidden and one of its ancestors is hidden,
  524. // notify the view that it is now unhidden.
  525. if (!_isHidden && [_superview isHiddenOrHasHiddenAncestor])
  526. [self _notifyViewDidUnhide];
  527. [self _notifyWindowDidResignKey];
  528. [self _notifyViewDidResignFirstResponder];
  529. _superview = nil;
  530. }
  531. /*!
  532. Replaces the specified child view with another view
  533. @param aSubview the view to replace
  534. @param aView the replacement view
  535. */
  536. - (void)replaceSubview:(CPView)aSubview with:(CPView)aView
  537. {
  538. if (aSubview._superview !== self || aSubview === aView)
  539. return;
  540. var index = [_subviews indexOfObjectIdenticalTo:aSubview];
  541. [self _insertSubview:aView atIndex:index];
  542. [aSubview removeFromSuperview];
  543. }
  544. - (void)setSubviews:(CPArray)newSubviews
  545. {
  546. if (!newSubviews)
  547. [CPException raise:CPInvalidArgumentException reason:"newSubviews cannot be nil in -[CPView setSubviews:]"];
  548. // Trivial Case 0: Same array somehow
  549. if ([_subviews isEqual:newSubviews])
  550. return;
  551. // Trivial Case 1: No current subviews, simply add all new subviews.
  552. if ([_subviews count] === 0)
  553. {
  554. var index = 0,
  555. count = [newSubviews count];
  556. for (; index < count; ++index)
  557. [self addSubview:newSubviews[index]];
  558. return;
  559. }
  560. // Trivial Case 2: No new subviews, simply remove all current subviews.
  561. if ([newSubviews count] === 0)
  562. {
  563. var count = [_subviews count];
  564. while (count--)
  565. [_subviews[count] removeFromSuperview];
  566. return;
  567. }
  568. // Find out the views that were removed.
  569. var removedSubviews = [CPMutableSet setWithArray:_subviews];
  570. [removedSubviews removeObjectsInArray:newSubviews];
  571. [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
  572. // Find out which views need to be added.
  573. var addedSubviews = [CPMutableSet setWithArray:newSubviews];
  574. [addedSubviews removeObjectsInArray:_subviews];
  575. var addedSubview = nil,
  576. addedSubviewEnumerator = [addedSubviews objectEnumerator];
  577. while ((addedSubview = [addedSubviewEnumerator nextObject]) !== nil)
  578. [self addSubview:addedSubview];
  579. // If the order is fine, no need to reorder.
  580. if ([_subviews isEqual:newSubviews])
  581. return;
  582. _subviews = [newSubviews copy];
  583. #if PLATFORM(DOM)
  584. var index = 0,
  585. count = [_subviews count];
  586. for (; index < count; ++index)
  587. {
  588. var subview = _subviews[index];
  589. CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement);
  590. CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement);
  591. }
  592. #endif
  593. }
  594. /* @ignore */
  595. - (void)_setWindow:(CPWindow)aWindow
  596. {
  597. [[self window] _dirtyKeyViewLoop];
  598. // Clear out first responder if we're the first responder and leaving.
  599. if ([_window firstResponder] === self && _window != aWindow)
  600. [_window makeFirstResponder:nil];
  601. // Notify the view and its subviews
  602. [self viewWillMoveToWindow:aWindow];
  603. // Unregister the drag events from the current window and register
  604. // them in the new window.
  605. if (_registeredDraggedTypes)
  606. {
  607. [_window _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
  608. [aWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
  609. }
  610. _window = aWindow;
  611. var count = [_subviews count];
  612. while (count--)
  613. [_subviews[count] _setWindow:aWindow];
  614. if ([_window isKeyWindow])
  615. [self setThemeState:CPThemeStateKeyWindow];
  616. else
  617. [self unsetThemeState:CPThemeStateKeyWindow];
  618. [self viewDidMoveToWindow];
  619. [[self window] _dirtyKeyViewLoop];
  620. }
  621. /*!
  622. Returns \c YES if the receiver is, or is a descendant of, \c aView.
  623. @param aView the view to test for ancestry
  624. */
  625. - (BOOL)isDescendantOf:(CPView)aView
  626. {
  627. var view = self;
  628. do
  629. {
  630. if (view == aView)
  631. return YES;
  632. } while(view = [view superview])
  633. return NO;
  634. }
  635. /*!
  636. Called when the receiver's superview has changed.
  637. */
  638. - (void)viewDidMoveToSuperview
  639. {
  640. // if (_graphicsContext)
  641. [self setNeedsDisplay:YES];
  642. }
  643. /*!
  644. Called when the receiver has been moved to a new CPWindow.
  645. */
  646. - (void)viewDidMoveToWindow
  647. {
  648. }
  649. /*!
  650. Called when the receiver is about to be moved to a new view.
  651. @param aView the view to which the receiver will be moved
  652. */
  653. - (void)viewWillMoveToSuperview:(CPView)aView
  654. {
  655. _isSuperviewAClipView = [aView isKindOfClass:[CPClipView class]];
  656. [self _removeObservers];
  657. if (aView)
  658. [self _addObservers];
  659. }
  660. /*!
  661. Called when the receiver is about to be moved to a new window.
  662. @param aWindow the window to which the receiver will be moved.
  663. */
  664. - (void)viewWillMoveToWindow:(CPWindow)aWindow
  665. {
  666. }
  667. /*!
  668. Called when the receiver is about to remove one of its subviews.
  669. @param aView the view that will be removed
  670. */
  671. - (void)willRemoveSubview:(CPView)aView
  672. {
  673. }
  674. - (void)_removeObservers
  675. {
  676. if (!_isObserving)
  677. return;
  678. var count = [_subviews count];
  679. while (count--)
  680. [_subviews[count] _removeObservers];
  681. _isObserving = NO;
  682. }
  683. - (void)_addObservers
  684. {
  685. if (_isObserving)
  686. return;
  687. var count = [_subviews count];
  688. while (count--)
  689. [_subviews[count] _addObservers];
  690. _isObserving = YES;
  691. }
  692. /*!
  693. Returns the menu item containing the receiver or one of its ancestor views.
  694. @return a menu item, or \c nil if the view or one of its ancestors wasn't found
  695. */
  696. - (CPMenuItem)enclosingMenuItem
  697. {
  698. var view = self;
  699. while (view && ![view isKindOfClass:[_CPMenuItemView class]])
  700. view = [view superview];
  701. if (view)
  702. return view._menuItem;
  703. return nil;
  704. /* var view = self,
  705. enclosingMenuItem = _enclosingMenuItem;
  706. while (!enclosingMenuItem && (view = view._enclosingMenuItem))
  707. view = [view superview];
  708. return enclosingMenuItem;*/
  709. }
  710. - (void)setTag:(CPInteger)aTag
  711. {
  712. _tag = aTag;
  713. }
  714. - (CPInteger)tag
  715. {
  716. return _tag;
  717. }
  718. - (CPView)viewWithTag:(CPInteger)aTag
  719. {
  720. if ([self tag] == aTag)
  721. return self;
  722. var index = 0,
  723. count = _subviews.length;
  724. for (; index < count; ++index)
  725. {
  726. var view = [_subviews[index] viewWithTag:aTag];
  727. if (view)
  728. return view;
  729. }
  730. return nil;
  731. }
  732. /*!
  733. Returns whether the view is flipped.
  734. @return \c YES if the view is flipped. \c NO, otherwise.
  735. */
  736. - (BOOL)isFlipped
  737. {
  738. return YES;
  739. }
  740. /*!
  741. Sets the frame size of the receiver to the dimensions and origin of the provided rectangle in the coordinate system
  742. of the superview. The method also posts a CPViewFrameDidChangeNotification to the notification
  743. center if the receiver is configured to do so. If the frame is the same as the current frame, the method simply
  744. returns (and no notification is posted).
  745. @param aFrame the rectangle specifying the new origin and size of the receiver
  746. */
  747. - (void)setFrame:(CGRect)aFrame
  748. {
  749. if (CGRectEqualToRect(_frame, aFrame))
  750. return;
  751. _inhibitFrameAndBoundsChangedNotifications = YES;
  752. [self setFrameOrigin:aFrame.origin];
  753. [self setFrameSize:aFrame.size];
  754. _inhibitFrameAndBoundsChangedNotifications = NO;
  755. if (_postsFrameChangedNotifications)
  756. [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
  757. if (_isSuperviewAClipView)
  758. [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
  759. }
  760. /*!
  761. Returns the receiver's frame.
  762. @return a copy of the receiver's frame
  763. */
  764. - (CGRect)frame
  765. {
  766. return CGRectMakeCopy(_frame);
  767. }
  768. - (CGPoint)frameOrigin
  769. {
  770. return CGPointMakeCopy(_frame.origin);
  771. }
  772. - (CGSize)frameSize
  773. {
  774. return CGSizeMakeCopy(_frame.size);
  775. }
  776. /*!
  777. Moves the center of the receiver's frame to the provided point. The point is defined in the superview's coordinate system.
  778. The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
  779. is configured to do so. If the specified origin is the same as the frame's current origin, the method will
  780. simply return (and no notification will be posted).
  781. @param aPoint the new origin point
  782. */
  783. - (void)setCenter:(CGPoint)aPoint
  784. {
  785. [self setFrameOrigin:CGPointMake(aPoint.x - _frame.size.width / 2.0, aPoint.y - _frame.size.height / 2.0)];
  786. }
  787. /*!
  788. Returns the center of the receiver's frame in the superview's coordinate system.
  789. @return CGPoint the center point of the receiver's frame
  790. */
  791. - (CGPoint)center
  792. {
  793. return CGPointMake(_frame.size.width / 2.0 + _frame.origin.x, _frame.size.height / 2.0 + _frame.origin.y);
  794. }
  795. /*!
  796. Sets the receiver's frame origin to the provided point. The point is defined in the superview's coordinate system.
  797. The method posts a CPViewFrameDidChangeNotification to the default notification center if the receiver
  798. is configured to do so. If the specified origin is the same as the frame's current origin, the method will
  799. simply return (and no notification will be posted).
  800. @param aPoint the new origin point
  801. */
  802. - (void)setFrameOrigin:(CGPoint)aPoint
  803. {
  804. var origin = _frame.origin;
  805. if (!aPoint || CGPointEqualToPoint(origin, aPoint))
  806. return;
  807. origin.x = aPoint.x;
  808. origin.y = aPoint.y;
  809. if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
  810. [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
  811. if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
  812. [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
  813. #if PLATFORM(DOM)
  814. var transform = _superview ? _superview._boundsTransform : NULL;
  815. CPDOMDisplayServerSetStyleLeftTop(_DOMElement, transform, origin.x, origin.y);
  816. #endif
  817. }
  818. /*!
  819. Sets the receiver's frame size. If \c aSize is the same as the frame's current dimensions, this
  820. method simply returns. The method posts a CPViewFrameDidChangeNotification to the
  821. default notification center if the receiver is configured to do so.
  822. @param aSize the new size for the frame
  823. */
  824. - (void)setFrameSize:(CGSize)aSize
  825. {
  826. var size = _frame.size;
  827. if (!aSize || CGSizeEqualToSize(size, aSize))
  828. return;
  829. var oldSize = CGSizeMakeCopy(size);
  830. size.width = aSize.width;
  831. size.height = aSize.height;
  832. if (YES)
  833. {
  834. _bounds.size.width = aSize.width * 1 / _scaleSize.width;
  835. _bounds.size.height = aSize.height * 1 / _scaleSize.height;
  836. }
  837. if (_layer)
  838. [_layer _owningViewBoundsChanged];
  839. if (_autoresizesSubviews)
  840. [self resizeSubviewsWithOldSize:oldSize];
  841. [self setNeedsLayout];
  842. [self setNeedsDisplay:YES];
  843. #if PLATFORM(DOM)
  844. [self _setDisplayServerSetStyleSize:size];
  845. if (_DOMContentsElement)
  846. {
  847. CPDOMDisplayServerSetSize(_DOMContentsElement, size.width, size.height);
  848. CPDOMDisplayServerSetStyleSize(_DOMContentsElement, size.width, size.height);
  849. }
  850. if (_backgroundType !== BackgroundTrivialColor)
  851. {
  852. if (_backgroundType === BackgroundTransparentColor)
  853. {
  854. CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
  855. }
  856. else
  857. {
  858. var images = [[_backgroundColor patternImage] imageSlices],
  859. partIndex = 0,
  860. frameSize = aSize;
  861. if (_backgroundType === BackgroundVerticalThreePartImage)
  862. {
  863. var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
  864. bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
  865. // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
  866. if (top)
  867. {
  868. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px");
  869. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, top);
  870. partIndex++;
  871. }
  872. if (_DOMImageSizes[1])
  873. {
  874. var height = frameSize.height - top - bottom;
  875. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px");
  876. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height - top - bottom);
  877. partIndex++;
  878. }
  879. if (bottom)
  880. {
  881. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", bottom + "px");
  882. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, bottom);
  883. }
  884. }
  885. else if (_backgroundType === BackgroundHorizontalThreePartImage)
  886. {
  887. var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
  888. right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
  889. // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
  890. if (left)
  891. {
  892. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px");
  893. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, size.height);
  894. partIndex++;
  895. }
  896. if (_DOMImageSizes[1])
  897. {
  898. var width = (frameSize.width - left - right);
  899. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], width + "px", frameSize.height + "px");
  900. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width - left - right, size.height);
  901. partIndex++;
  902. }
  903. if (right)
  904. {
  905. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], right + "px", frameSize.height + "px");
  906. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, size.height);
  907. }
  908. }
  909. else if (_backgroundType === BackgroundNinePartImage)
  910. {
  911. var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
  912. right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
  913. top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
  914. bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
  915. width = size.width - left - right,
  916. height = size.height - top - bottom;
  917. if (_DOMImageSizes[0])
  918. partIndex++;
  919. if (_DOMImageSizes[1])
  920. {
  921. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, top);
  922. partIndex++;
  923. }
  924. if (_DOMImageSizes[2])
  925. partIndex++;
  926. if (_DOMImageSizes[3])
  927. {
  928. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
  929. partIndex++;
  930. }
  931. if (_DOMImageSizes[4])
  932. {
  933. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
  934. partIndex++;
  935. }
  936. if (_DOMImageSizes[5])
  937. {
  938. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
  939. partIndex++;
  940. }
  941. if (_DOMImageSizes[6])
  942. partIndex++;
  943. if (_DOMImageSizes[7])
  944. {
  945. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
  946. }
  947. }
  948. }
  949. }
  950. #endif
  951. if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
  952. [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self];
  953. if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
  954. [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]];
  955. }
  956. /*!
  957. This method is used to set the width and height of the _DOMElement. It cares about the scale of the view.
  958. When scaling, for instance with a size (0.5, 0.5), the bounds of the view will be multiply by 2. It's why we multiply by the inverse of the scaling.
  959. The view will finally keep the same proportion for the user on the screen.
  960. */
  961. - (void)_setDisplayServerSetStyleSize:(CGSize)aSize
  962. {
  963. #if PLATFORM(DOM)
  964. var scale = [self scaleSize];
  965. CPDOMDisplayServerSetStyleSize(_DOMElement, aSize.width * 1 / scale.width, aSize.height * 1 / scale.height);
  966. #endif
  967. }
  968. /*!
  969. Sets the receiver's bounds. The bounds define the size and location of the receiver inside it's frame. Posts a
  970. CPViewBoundsDidChangeNotification to the default notification center if the receiver is configured to do so.
  971. @param bounds the new bounds
  972. */
  973. - (void)setBounds:(CGRect)bounds
  974. {
  975. if (CGRectEqualToRect(_bounds, bounds))
  976. return;
  977. _inhibitFrameAndBoundsChangedNotifications = YES;
  978. [self setBoundsOrigin:bounds.origin];
  979. [self setBoundsSize:bounds.size];
  980. _inhibitFrameAndBoundsChangedNotifications = NO;
  981. if (_postsBoundsChangedNotifications)
  982. [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
  983. if (_isSuperviewAClipView)
  984. [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
  985. }
  986. /*!
  987. Returns the receiver's bounds. The bounds define the size
  988. and location of the receiver inside its frame.
  989. */
  990. - (CGRect)bounds
  991. {
  992. return CGRectMakeCopy(_bounds);
  993. }
  994. - (CGPoint)boundsOrigin
  995. {
  996. return CGPointMakeCopy(_bounds.origin);
  997. }
  998. - (CGSize)boundsSize
  999. {
  1000. return CGSizeMakeCopy(_bounds.size);
  1001. }
  1002. /*!
  1003. Sets the location of the receiver inside its frame. The method
  1004. posts a CPViewBoundsDidChangeNotification to the
  1005. default notification center if the receiver is configured to do so.
  1006. @param aPoint the new location for the receiver
  1007. */
  1008. - (void)setBoundsOrigin:(CGPoint)aPoint
  1009. {
  1010. var origin = _bounds.origin;
  1011. if (CGPointEqualToPoint(origin, aPoint))
  1012. return;
  1013. origin.x = aPoint.x;
  1014. origin.y = aPoint.y;
  1015. if (origin.x != 0 || origin.y != 0)
  1016. {
  1017. _boundsTransform = CGAffineTransformMakeTranslation(-origin.x, -origin.y);
  1018. _inverseBoundsTransform = CGAffineTransformInvert(_boundsTransform);
  1019. }
  1020. else
  1021. {
  1022. _boundsTransform = nil;
  1023. _inverseBoundsTransform = nil;
  1024. }
  1025. #if PLATFORM(DOM)
  1026. var index = _subviews.length;
  1027. while (index--)
  1028. {
  1029. var view = _subviews[index],
  1030. origin = view._frame.origin;
  1031. CPDOMDisplayServerSetStyleLeftTop(view._DOMElement, _boundsTransform, origin.x, origin.y);
  1032. }
  1033. #endif
  1034. if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
  1035. [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
  1036. if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
  1037. [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
  1038. }
  1039. /*!
  1040. Sets the receiver's size inside its frame. The method posts a
  1041. CPViewBoundsDidChangeNotification to the default
  1042. notification center if the receiver is configured to do so.
  1043. @param aSize the new size for the receiver
  1044. */
  1045. - (void)setBoundsSize:(CGSize)aSize
  1046. {
  1047. var size = _bounds.size;
  1048. if (CGSizeEqualToSize(size, aSize))
  1049. return;
  1050. var frameSize = _frame.size;
  1051. if (!CGSizeEqualToSize(size, frameSize))
  1052. {
  1053. var origin = _bounds.origin;
  1054. origin.x /= size.width / frameSize.width;
  1055. origin.y /= size.height / frameSize.height;
  1056. }
  1057. size.width = aSize.width;
  1058. size.height = aSize.height;
  1059. if (!CGSizeEqualToSize(size, frameSize))
  1060. {
  1061. var origin = _bounds.origin;
  1062. origin.x *= size.width / frameSize.width;
  1063. origin.y *= size.height / frameSize.height;
  1064. }
  1065. if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications)
  1066. [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self];
  1067. if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications)
  1068. [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]];
  1069. }
  1070. /*!
  1071. Notifies subviews that the superview changed size.
  1072. @param aSize the size of the old superview
  1073. */
  1074. - (void)resizeWithOldSuperviewSize:(CGSize)aSize
  1075. {
  1076. var mask = [self autoresizingMask];
  1077. if (mask == CPViewNotSizable)
  1078. return;
  1079. var frame = _superview._frame,
  1080. newFrame = CGRectMakeCopy(_frame),
  1081. dX = frame.size.width - aSize.width,
  1082. dY = frame.size.height - aSize.height,
  1083. evenFractionX = 1.0 / ((mask & CPViewMinXMargin ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
  1084. evenFractionY = 1.0 / ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)),
  1085. baseX = (mask & CPViewMinXMargin ? _frame.origin.x : 0) +
  1086. (mask & CPViewWidthSizable ? _frame.size.width : 0) +
  1087. (mask & CPViewMaxXMargin ? aSize.width - _frame.size.width - _frame.origin.x : 0),
  1088. baseY = (mask & CPViewMinYMargin ? _frame.origin.y : 0) +
  1089. (mask & CPViewHeightSizable ? _frame.size.height : 0) +
  1090. (mask & CPViewMaxYMargin ? aSize.height - _frame.size.height - _frame.origin.y : 0);
  1091. if (mask & CPViewMinXMargin)
  1092. newFrame.origin.x += dX * (baseX > 0 ? _frame.origin.x / baseX : evenFractionX);
  1093. if (mask & CPViewWidthSizable)
  1094. newFrame.size.width += dX * (baseX > 0 ? _frame.size.width / baseX : evenFractionX);
  1095. if (mask & CPViewMinYMargin)
  1096. newFrame.origin.y += dY * (baseY > 0 ? _frame.origin.y / baseY : evenFractionY);
  1097. if (mask & CPViewHeightSizable)
  1098. newFrame.size.height += dY * (baseY > 0 ? _frame.size.height / baseY : evenFractionY);
  1099. [self setFrame:newFrame];
  1100. }
  1101. /*!
  1102. Initiates \c -superviewSizeChanged: messages to subviews.
  1103. @param aSize the size for the subviews
  1104. */
  1105. - (void)resizeSubviewsWithOldSize:(CGSize)aSize
  1106. {
  1107. var count = _subviews.length;
  1108. while (count--)
  1109. [_subviews[count] resizeWithOldSuperviewSize:aSize];
  1110. }
  1111. /*!
  1112. Specifies whether the receiver view should automatically resize its
  1113. subviews when its \c -setFrameSize: method receives a change.
  1114. @param aFlag If \c YES, then subviews will automatically be resized
  1115. when this view is resized. \c NO means the views will not
  1116. be resized automatically.
  1117. */
  1118. - (void)setAutoresizesSubviews:(BOOL)aFlag
  1119. {
  1120. _autoresizesSubviews = !!aFlag;
  1121. }
  1122. /*!
  1123. Reports whether the receiver automatically resizes its subviews when its frame size changes.
  1124. @return \c YES means it resizes its subviews on a frame size change.
  1125. */
  1126. - (BOOL)autoresizesSubviews
  1127. {
  1128. return _autoresizesSubviews;
  1129. }
  1130. /*!
  1131. Determines automatic resizing behavior.
  1132. @param aMask a bit mask with options
  1133. */
  1134. - (void)setAutoresizingMask:(unsigned)aMask
  1135. {
  1136. _autoresizingMask = aMask;
  1137. }
  1138. /*!
  1139. Returns the bit mask options for resizing behavior
  1140. */
  1141. - (unsigned)autoresizingMask
  1142. {
  1143. return _autoresizingMask;
  1144. }
  1145. // Fullscreen Mode
  1146. /*!
  1147. Puts the receiver into full screen mode.
  1148. */
  1149. - (BOOL)enterFullScreenMode
  1150. {
  1151. return [self enterFullScreenMode:nil withOptions:nil];
  1152. }
  1153. /*!
  1154. Puts the receiver into full screen mode.
  1155. @param aScreen the that should be used
  1156. @param options configuration options
  1157. */
  1158. - (BOOL)enterFullScreenMode:(CPScreen)aScreen withOptions:(CPDictionary)options
  1159. {
  1160. _fullScreenModeState = _CPViewFullScreenModeStateMake(self);
  1161. var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask];
  1162. [fullScreenWindow setLevel:CPScreenSaverWindowLevel];
  1163. [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
  1164. var contentView = [fullScreenWindow contentView];
  1165. [contentView setBackgroundColor:[CPColor blackColor]];
  1166. [contentView addSubview:self];
  1167. [self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
  1168. [self setFrame:CGRectMakeCopy([contentView bounds])];
  1169. [fullScreenWindow makeKeyAndOrderFront:self];
  1170. [fullScreenWindow makeFirstResponder:self];
  1171. _isInFullScreenMode = YES;
  1172. return YES;
  1173. }
  1174. /*!
  1175. The receiver should exit full screen mode.
  1176. */
  1177. - (void)exitFullScreenMode
  1178. {
  1179. [self exitFullScreenModeWithOptions:nil];
  1180. }
  1181. /*!
  1182. The receiver should exit full screen mode.
  1183. @param options configurations options
  1184. */
  1185. - (void)exitFullScreenModeWithOptions:(CPDictionary)options
  1186. {
  1187. if (!_isInFullScreenMode)
  1188. return;
  1189. _isInFullScreenMode = NO;
  1190. [self setFrame:_fullScreenModeState.frame];
  1191. [self setAutoresizingMask:_fullScreenModeState.autoresizingMask];
  1192. [_fullScreenModeState.superview _insertSubview:self atIndex:_fullScreenModeState.index];
  1193. [[self window] orderOut:self];
  1194. }
  1195. /*!
  1196. Returns \c YES if the receiver is currently in full screen mode.
  1197. */
  1198. - (BOOL)isInFullScreenMode
  1199. {
  1200. return _isInFullScreenMode;
  1201. }
  1202. /*!
  1203. Sets whether the receiver should be hidden.
  1204. @param aFlag \c YES makes the receiver hidden.
  1205. */
  1206. - (void)setHidden:(BOOL)aFlag
  1207. {
  1208. aFlag = !!aFlag;
  1209. if (_isHidden === aFlag)
  1210. return;
  1211. // FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
  1212. // _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
  1213. _isHidden = aFlag;
  1214. #if PLATFORM(DOM)
  1215. _DOMElement.style.display = _isHidden ? "none" : "block";
  1216. #endif
  1217. if (aFlag)
  1218. {
  1219. var view = [_window firstResponder];
  1220. if ([view isKindOfClass:[CPView class]])
  1221. {
  1222. do
  1223. {
  1224. if (self == view)
  1225. {
  1226. [_window makeFirstResponder:[self nextValidKeyView]];
  1227. break;
  1228. }
  1229. }
  1230. while (view = [view superview]);
  1231. }
  1232. [self _notifyViewDidHide];
  1233. }
  1234. else
  1235. {
  1236. [self setNeedsDisplay:YES];
  1237. [self _notifyViewDidUnhide];
  1238. }
  1239. }
  1240. - (void)_notifyViewDidHide
  1241. {
  1242. [self viewDidHide];
  1243. var count = [_subviews count];
  1244. while (count--)
  1245. [_subviews[count] _notifyViewDidHide];
  1246. }
  1247. - (void)_notifyViewDidUnhide
  1248. {
  1249. [self viewDidUnhide];
  1250. var count = [_subviews count];
  1251. while (count--)
  1252. [_subviews[count] _notifyViewDidUnhide];
  1253. }
  1254. /*!
  1255. Returns \c YES if the receiver is hidden.
  1256. */
  1257. - (BOOL)isHidden
  1258. {
  1259. return _isHidden;
  1260. }
  1261. - (void)setClipsToBounds:(BOOL)shouldClip
  1262. {
  1263. if (_clipsToBounds === shouldClip)
  1264. return;
  1265. _clipsToBounds = shouldClip;
  1266. #if PLATFORM(DOM)
  1267. _DOMElement.style.overflow = _clipsToBounds ? "hidden" : "visible";
  1268. #endif
  1269. }
  1270. - (BOOL)clipsToBounds
  1271. {
  1272. return _clipsToBounds;
  1273. }
  1274. /*!
  1275. Sets the opacity of the receiver. The value must be in the range of 0.0 to 1.0, where 0.0 is
  1276. completely transparent and 1.0 is completely opaque.
  1277. @param anAlphaValue an alpha value ranging from 0.0 to 1.0.
  1278. */
  1279. - (void)setAlphaValue:(float)anAlphaValue
  1280. {
  1281. if (_opacity == anAlphaValue)
  1282. return;
  1283. _opacity = anAlphaValue;
  1284. #if PLATFORM(DOM)
  1285. if (CPFeatureIsCompatible(CPOpacityRequiresFilterFeature))
  1286. {
  1287. if (anAlphaValue === 1.0)
  1288. try { _DOMElement.style.removeAttribute("filter") } catch (anException) { }
  1289. else
  1290. _DOMElement.style.filter = "alpha(opacity=" + anAlphaValue * 100 + ")";
  1291. }
  1292. else
  1293. _DOMElement.style.opacity = anAlphaValue;
  1294. #endif
  1295. }
  1296. /*!
  1297. Returns the alpha value of the receiver. Ranges from 0.0 to
  1298. 1.0, where 0.0 is completely transparent and 1.0 is completely opaque.
  1299. */
  1300. - (float)alphaValue
  1301. {
  1302. return _opacity;
  1303. }
  1304. /*!
  1305. Returns \c YES if the receiver is hidden, or one
  1306. of it's ancestor views is hidden. \c NO, otherwise.
  1307. */
  1308. - (BOOL)isHiddenOrHasHiddenAncestor
  1309. {
  1310. var view = self;
  1311. while (view && ![view isHidden])
  1312. view = [view superview];
  1313. return view !== nil;
  1314. }
  1315. /*!
  1316. Returns YES if the view is not hidden, has no hidden ancestor and doesn't belong to a hidden window.
  1317. */
  1318. - (BOOL)_isVisible
  1319. {
  1320. return ![self isHiddenOrHasHiddenAncestor] && [[self window] isVisible];
  1321. }
  1322. /*!
  1323. Called when the return value of isHiddenOrHasHiddenAncestor becomes YES,
  1324. e.g. when this view becomes hidden due to a setHidden:YES message to
  1325. itself or to one of its superviews.
  1326. Note: in the current implementation, viewDidHide may be called multiple
  1327. times if additional superviews are hidden, even if
  1328. isHiddenOrHasHiddenAncestor was already YES.
  1329. */
  1330. - (void)viewDidHide
  1331. {
  1332. }
  1333. /*!
  1334. Called when the return value of isHiddenOrHasHiddenAncestor becomes NO,
  1335. e.g. when this view stops being hidden due to a setHidden:NO message to
  1336. itself or to one of its superviews.
  1337. Note: in the current implementation, viewDidUnhide may be called multiple
  1338. times if additional superviews are unhidden, even if
  1339. isHiddenOrHasHiddenAncestor was already NO.
  1340. */
  1341. - (void)viewDidUnhide
  1342. {
  1343. }
  1344. /*!
  1345. Returns whether the receiver should be sent a \c -mouseDown: message for \c anEvent.<br/>
  1346. Returns \c NO by default.
  1347. @return \c YES, if the view object accepts first mouse-down event. \c NO, otherwise.
  1348. */
  1349. - (BOOL)acceptsFirstMouse:(CPEvent)anEvent
  1350. {
  1351. return NO;
  1352. }
  1353. /*!
  1354. Returns whether or not the view responds to hit tests.
  1355. @return \c YES if this view listens to \c -hitTest messages, \c NO otherwise.
  1356. */
  1357. - (BOOL)hitTests
  1358. {
  1359. return _hitTests;
  1360. }
  1361. /*!
  1362. Set whether or not the view should respond to hit tests.
  1363. @param shouldHitTest should be \c YES if this view should respond to hit tests, \c NO otherwise.
  1364. */
  1365. - (void)setHitTests:(BOOL)shouldHitTest
  1366. {
  1367. _hitTests = !!shouldHitTest;
  1368. }
  1369. /*!
  1370. Tests whether a point is contained within this view, or one of its subviews.
  1371. @param aPoint the point to test
  1372. @return returns the containing view, or nil if the point is not contained
  1373. */
  1374. - (CPView)hitTest:(CGPoint)aPoint
  1375. {
  1376. if (_isHidden || !_hitTests)
  1377. return nil;
  1378. var frame = _frame,
  1379. sizeScale = [self _hierarchyScaleSize];
  1380. if (_isScaled)
  1381. frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale([_superview _hierarchyScaleSize].width, [_superview _hierarchyScaleSize].height));
  1382. else
  1383. frame = CGRectApplyAffineTransform(_frame, CGAffineTransformMakeScale(sizeScale.width, sizeScale.height));
  1384. if (!CGRectContainsPoint(frame, aPoint))
  1385. return nil;
  1386. var view = nil,
  1387. i = _subviews.length,
  1388. adjustedPoint = CGPointMake(aPoint.x - CGRectGetMinX(frame), aPoint.y - CGRectGetMinY(frame));
  1389. if (_inverseBoundsTransform)
  1390. {
  1391. var affineTransform = CGAffineTransformMakeCopy(_inverseBoundsTransform);
  1392. if (_isScaled)
  1393. {
  1394. affineTransform.tx *= [_superview _hierarchyScaleSize].width;
  1395. affineTransform.ty *= [_superview _hierarchyScaleSize].height;
  1396. }
  1397. else
  1398. {
  1399. affineTransform.tx *= sizeScale.width;
  1400. affineTransform.ty *= sizeScale.height;
  1401. }
  1402. adjustedPoint = CGPointApplyAffineTransform(adjustedPoint, affineTransform);
  1403. }
  1404. while (i--)
  1405. if (view = [_subviews[i] hitTest:adjustedPoint])
  1406. return view;
  1407. return self;
  1408. }
  1409. /*!
  1410. Returns \c YES if this view requires a panel to become key. Normally only text fields, so this returns \c NO.
  1411. */
  1412. - (BOOL)needsPanelToBecomeKey
  1413. {
  1414. return NO;
  1415. }
  1416. /*!
  1417. Returns \c YES if mouse events aren't needed by the receiver and can be sent to the superview. The
  1418. default implementation returns \c NO if the view is opaque.
  1419. */
  1420. - (BOOL)mouseDownCanMoveWindow
  1421. {
  1422. return ![self isOpaque];
  1423. }
  1424. - (void)mouseDown:(CPEvent)anEvent
  1425. {
  1426. if ([self mouseDownCanMoveWindow])
  1427. [super mouseDown:anEvent];
  1428. }
  1429. - (void)rightMouseDown:(CPEvent)anEvent
  1430. {
  1431. var menu = [self menuForEvent:anEvent];
  1432. if (menu)
  1433. [CPMenu popUpContextMenu:menu withEvent:anEvent forView:self];
  1434. else if ([[self nextResponder] isKindOfClass:CPView])
  1435. [super rightMouseDown:anEvent];
  1436. else
  1437. [[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
  1438. }
  1439. - (CPMenu)menuForEvent:(CPEvent)anEvent
  1440. {
  1441. return [self menu] || [[self class] defaultMenu];
  1442. }
  1443. /*!
  1444. Sets the background color of the receiver.
  1445. @param aColor the new color for the receiver's background
  1446. */
  1447. - (void)setBackgroundColor:(CPColor)aColor
  1448. {
  1449. if (_backgroundColor == aColor)
  1450. return;
  1451. if (aColor == [CPNull null])
  1452. aColor = nil;
  1453. _backgroundColor = aColor;
  1454. #if PLATFORM(DOM)
  1455. var patternImage = [_backgroundColor patternImage],
  1456. colorExists = _backgroundColor && ([_backgroundColor patternImage] || [_backgroundColor alphaComponent] > 0.0),
  1457. colorHasAlpha = colorExists && [_backgroundColor alphaComponent] < 1.0,
  1458. supportsRGBA = CPFeatureIsCompatible(CPCSSRGBAFeature),
  1459. colorNeedsDOMElement = colorHasAlpha && !supportsRGBA,
  1460. amount = 0,
  1461. slices;
  1462. if ([patternImage isThreePartImage])
  1463. {
  1464. _backgroundType = [patternImage isVertical] ? BackgroundVerticalThreePartImage : BackgroundHorizontalThreePartImage;
  1465. amount = 3;
  1466. }
  1467. else if ([patternImage isNinePartImage])
  1468. {
  1469. _backgroundType = BackgroundNinePartImage;
  1470. amount = 9;
  1471. }
  1472. else
  1473. {
  1474. _backgroundType = colorNeedsDOMElement ? BackgroundTransparentColor : BackgroundTrivialColor;
  1475. amount = (colorNeedsDOMElement ? 1 : 0) - _DOMImageParts.length;
  1476. }
  1477. // 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.
  1478. if (_backgroundType === BackgroundVerticalThreePartImage || _backgroundType === BackgroundHorizontalThreePartImage || _backgroundType === BackgroundNinePartImage)
  1479. {
  1480. slices = [patternImage imageSlices];
  1481. // We won't need more divs than there are slices.
  1482. amount = MIN(amount, slices.length);
  1483. for (var i = 0, count = slices.length; i < count; i++)
  1484. {
  1485. var image = slices[i],
  1486. size = [image size];
  1487. if (!size || (size.width == 0 && size.height == 0))
  1488. size = nil;
  1489. _DOMImageSizes[i] = size;
  1490. // If there's a nil slice or a slice with no size, it won't need a div.
  1491. if (!size)
  1492. amount--;
  1493. }
  1494. // Now that we know how many divs we really need, compare that to number we actually have.
  1495. amount -= _DOMImageParts.length;
  1496. }
  1497. // Make sure the number of divs we have match our needs.
  1498. if (amount > 0)
  1499. {
  1500. while (amount--)
  1501. {
  1502. var DOMElement = DOMElementPrototype.cloneNode(false);
  1503. DOMElement.style.zIndex = -1000;
  1504. _DOMImageParts.push(DOMElement);
  1505. _DOMElement.appendChild(DOMElement);
  1506. }
  1507. }
  1508. else
  1509. {
  1510. amount = -amount;
  1511. while (amount--)
  1512. _DOMElement.removeChild(_DOMImageParts.pop());
  1513. }
  1514. if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
  1515. {
  1516. var colorCSS = colorExists ? [_backgroundColor cssString] : "";
  1517. if (colorNeedsDOMElement)
  1518. {
  1519. _DOMElement.style.background = "";
  1520. _DOMImageParts[0].style.background = [_backgroundColor cssString];
  1521. if (patternImage)
  1522. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[0], [patternImage size].width + "px", [patternImage size].height + "px");
  1523. if (CPFeatureIsCompatible(CPOpacityRequiresFilterFeature))
  1524. _DOMImageParts[0].style.filter = "alpha(opacity=" + [_backgroundColor alphaComponent] * 100 + ")";
  1525. else
  1526. _DOMImageParts[0].style.opacity = [_backgroundColor alphaComponent];
  1527. var size = [self bounds].size;
  1528. CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
  1529. }
  1530. else
  1531. _DOMElement.style.background = colorCSS;
  1532. if (patternImage)
  1533. CPDOMDisplayServerSetStyleBackgroundSize(_DOMElement, [patternImage size].width + "px", [patternImage size].height + "px");
  1534. }
  1535. else
  1536. {
  1537. var frameSize = _frame.size,
  1538. partIndex = 0;
  1539. for (var i = 0; i < slices.length; i++)
  1540. {
  1541. var size = _DOMImageSizes[i];
  1542. if (!size)
  1543. continue;
  1544. var image = slices[i];
  1545. // // If image was nil, size should have been nil too.
  1546. // assert(image != nil);
  1547. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height);
  1548. _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")";
  1549. if (!supportsRGBA)
  1550. {
  1551. if (CPFeatureIsCompatible(CPOpacityRequiresFilterFeature))
  1552. try { _DOMImageParts[partIndex].style.removeAttribute("filter") } catch (anException) { }
  1553. else
  1554. _DOMImageParts[partIndex].style.opacity = 1.0;
  1555. }
  1556. partIndex++;
  1557. }
  1558. if (_backgroundType == BackgroundNinePartImage)
  1559. {
  1560. var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
  1561. right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0,
  1562. top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
  1563. bottom = _DOMImageSizes[6] ? _DOMImageSizes[6].height : 0,
  1564. width = frameSize.width - left - right,
  1565. height = frameSize.height - top - bottom;
  1566. partIndex = 0;
  1567. if (_DOMImageSizes[0])
  1568. {
  1569. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1570. partIndex++;
  1571. }
  1572. if (_DOMImageSizes[1])
  1573. {
  1574. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
  1575. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[1].height);
  1576. partIndex++;
  1577. }
  1578. if (_DOMImageSizes[2])
  1579. {
  1580. CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1581. partIndex++;
  1582. }
  1583. if (_DOMImageSizes[3])
  1584. {
  1585. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
  1586. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[3].width, height);
  1587. partIndex++;
  1588. }
  1589. if (_DOMImageSizes[4])
  1590. {
  1591. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, top);
  1592. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, height);
  1593. partIndex++;
  1594. }
  1595. if (_DOMImageSizes[5])
  1596. {
  1597. CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, top);
  1598. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], _DOMImageSizes[5].width, height);
  1599. partIndex++;
  1600. }
  1601. if (_DOMImageSizes[6])
  1602. {
  1603. CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1604. partIndex++;
  1605. }
  1606. if (_DOMImageSizes[7])
  1607. {
  1608. CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, left, 0.0);
  1609. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, _DOMImageSizes[7].height);
  1610. partIndex++;
  1611. }
  1612. if (_DOMImageSizes[8])
  1613. {
  1614. CPDOMDisplayServerSetStyleRightBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1615. }
  1616. }
  1617. else if (_backgroundType == BackgroundVerticalThreePartImage)
  1618. {
  1619. var top = _DOMImageSizes[0] ? _DOMImageSizes[0].height : 0,
  1620. bottom = _DOMImageSizes[2] ? _DOMImageSizes[2].height : 0;
  1621. partIndex = 0;
  1622. // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed.
  1623. if (top)
  1624. {
  1625. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px");
  1626. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1627. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, top);
  1628. partIndex++;
  1629. }
  1630. if (_DOMImageSizes[1])
  1631. {
  1632. var height = frameSize.height - top - bottom;
  1633. //_DOMImageParts[partIndex].style.backgroundSize = frameSize.width + "px " + height + "px";
  1634. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px");
  1635. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top);
  1636. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, height);
  1637. partIndex++;
  1638. }
  1639. if (bottom)
  1640. {
  1641. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", bottom + "px");
  1642. CPDOMDisplayServerSetStyleLeftBottom(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1643. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, bottom);
  1644. }
  1645. }
  1646. else if (_backgroundType == BackgroundHorizontalThreePartImage)
  1647. {
  1648. var left = _DOMImageSizes[0] ? _DOMImageSizes[0].width : 0,
  1649. right = _DOMImageSizes[2] ? _DOMImageSizes[2].width : 0;
  1650. partIndex = 0;
  1651. // Make sure to repeat the left and right pieces vertically if they're not the exact height needed.
  1652. if (left)
  1653. {
  1654. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px");
  1655. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1656. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], left, frameSize.height);
  1657. partIndex++;
  1658. }
  1659. if (_DOMImageSizes[1])
  1660. {
  1661. var width = (frameSize.width - left - right);
  1662. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], width + "px", frameSize.height + "px");
  1663. CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, left, 0.0);
  1664. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], width, frameSize.height);
  1665. partIndex++;
  1666. }
  1667. if (right)
  1668. {
  1669. CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], right + "px", frameSize.height + "px");
  1670. CPDOMDisplayServerSetStyleRightTop(_DOMImageParts[partIndex], NULL, 0.0, 0.0);
  1671. CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], right, frameSize.height);
  1672. }
  1673. }
  1674. }
  1675. #endif
  1676. }
  1677. /*!
  1678. Returns the background color of the receiver
  1679. */
  1680. - (CPColor)backgroundColor
  1681. {
  1682. return _backgroundColor;
  1683. }
  1684. // Converting Coordinates
  1685. /*!
  1686. Converts \c aPoint from the coordinate space of \c aView to the coordinate space of the receiver.
  1687. @param aPoint the point to convert
  1688. @param aView the view space to convert from
  1689. @return the converted point
  1690. */
  1691. - (CGPoint)convertPoint:(CGPoint)aPoint fromView:(CPView)aView
  1692. {
  1693. if (aView === self)
  1694. return aPoint;
  1695. return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(aView, self));
  1696. }
  1697. /*!
  1698. Converts the point from the base coordinate system to the receiver’s coordinate system.
  1699. @param aPoint A point specifying a location in the base coordinate system
  1700. @return The point converted to the receiver’s base coordinate system
  1701. */
  1702. - (CGPoint)convertPointFromBase:(CGPoint)aPoint
  1703. {
  1704. return [self convertPoint:aPoint fromView:nil];
  1705. }
  1706. /*!
  1707. Converts \c aPoint from the receiver's coordinate space to the coordinate space of \c aView.
  1708. @param aPoint the point to convert
  1709. @param aView the coordinate space to which the point will be converted
  1710. @return the converted point
  1711. */
  1712. - (CGPoint)convertPoint:(CGPoint)aPoint toView:(CPView)aView
  1713. {
  1714. if (aView === self)
  1715. return aPoint;
  1716. return CGPointApplyAffineTransform(aPoint, _CPViewGetTransform(self, aView));
  1717. }
  1718. /*!
  1719. Converts the point from the receiver’s coordinate system to the base coordinate system.
  1720. @param aPoint A point specifying a location in the coordinate system of the receiver
  1721. @return The point converted to the base coordinate system
  1722. */
  1723. - (CGPoint)convertPointToBase:(CGPoint)aPoint
  1724. {
  1725. return [self convertPoint:aPoint toView:nil];
  1726. }
  1727. /*!
  1728. Convert's \c aSize from \c aView's coordinate space to the receiver's coordinate space.
  1729. @param aSize the size to convert
  1730. @param aView the coordinate space to convert from
  1731. @return the converted size
  1732. */
  1733. - (CGSize)convertSize:(CGSize)aSize fromView:(CPView)aView
  1734. {
  1735. if (aView === self)
  1736. return aSize;
  1737. return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(aView, self));
  1738. }
  1739. /*!
  1740. Convert's \c aSize from the receiver's coordinate space to \c aView's coordinate space.
  1741. @param aSize the size to convert
  1742. @param the coordinate space to which the size will be converted
  1743. @return the converted size
  1744. */
  1745. - (CGSize)convertSize:(CGSize)aSize toView:(CPView)aView
  1746. {
  1747. if (aView === self)
  1748. return aSize;
  1749. return CGSizeApplyAffineTransform(aSize, _CPViewGetTransform(self, aView));
  1750. }
  1751. /*!
  1752. Converts \c aRect from \c aView's coordinate space to the receiver's space.
  1753. @param aRect the rectangle to convert
  1754. @param aView the coordinate space from which to convert
  1755. @return the converted rectangle
  1756. */
  1757. - (CGRect)convertRect:(CGRect)aRect fromView:(CPView)aView
  1758. {
  1759. if (self === aView)
  1760. return aRect;
  1761. return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(aView, self));
  1762. }
  1763. /*!
  1764. Converts the rectangle from the base coordinate system to the receiver’s coordinate system.
  1765. @param aRect A rectangle specifying a location in the base coordinate system
  1766. @return The rectangle converted to the receiver’s base coordinate system
  1767. */
  1768. - (CGRect)convertRectFromBase:(CGRect)aRect
  1769. {
  1770. return [self convertRect:aRect fromView:nil];
  1771. }
  1772. /*!
  1773. Converts \c aRect from the receiver's coordinate space to \c aView's coordinate space.
  1774. @param aRect the rectangle to convert
  1775. @param aView the coordinate space to which the rectangle will be converted
  1776. @return the converted rectangle
  1777. */
  1778. - (CGRect)convertRect:(CGRect)aRect toView:(CPView)aView
  1779. {
  1780. if (self === aView)
  1781. return aRect;
  1782. return CGRectApplyAffineTransform(aRect, _CPViewGetTransform(self, aView));
  1783. }
  1784. /*!
  1785. Converts the rectangle from the receiver’s coordinate system to the base coordinate system.
  1786. @param aRect A rectangle specifying a location in the coordinate system of the receiver
  1787. @return The rectangle converted to the base coordinate system
  1788. */
  1789. - (CGRect)convertRectToBase:(CGRect)aRect
  1790. {
  1791. return [self convertRect:aRect toView:nil];
  1792. }
  1793. /*!
  1794. Sets whether the receiver posts a CPViewFrameDidChangeNotification notification
  1795. to the default notification center when its frame is changed. The default is \c NO.
  1796. Methods that could cause a frame change notification are:
  1797. <pre>
  1798. setFrame:
  1799. setFrameSize:
  1800. setFrameOrigin:
  1801. </pre>
  1802. @param shouldPostFrameChangedNotifications \c YES makes the receiver post
  1803. notifications on frame changes (size or origin)
  1804. */
  1805. - (void)setPostsFrameChangedNotifications:(BOOL)shouldPostFrameChangedNotifications
  1806. {
  1807. shouldPostFrameChangedNotifications = !!shouldPostFrameChangedNotifications;
  1808. if (_postsFrameChangedNotifications === shouldPostFrameChangedNotifications)
  1809. return;
  1810. _postsFrameChangedNotifications = shouldPostFrameChangedNotifications;
  1811. }
  1812. /*!
  1813. Returns \c YES if the receiver posts a CPViewFrameDidChangeNotification if its frame is changed.
  1814. */
  1815. - (BOOL)postsFrameChangedNotifications
  1816. {
  1817. return _postsFrameChangedNotifications;
  1818. }
  1819. /*!
  1820. Sets whether the receiver posts a CPViewBoundsDidChangeNotification notification
  1821. to the default notification center when its bounds is changed. The default is \c NO.
  1822. Methods that could cause a bounds change notification are:
  1823. <pre>
  1824. setBounds:
  1825. setBoundsSize:
  1826. setBoundsOrigin:
  1827. </pre>
  1828. @param shouldPostBoundsChangedNotifications \c YES makes the receiver post
  1829. notifications on bounds changes
  1830. */
  1831. - (void)setPostsBoundsChangedNotifications:(BOOL)shouldPostBoundsChangedNotifications
  1832. {
  1833. shouldPostBoundsChangedNotifications = !!shouldPostBoundsChangedNotifications;
  1834. if (_postsBoundsChangedNotifications === shouldPostBoundsChangedNotifications)
  1835. return;
  1836. _postsBoundsChangedNotifications = shouldPostBoundsChangedNotifications;
  1837. }
  1838. /*!
  1839. Returns \c YES if the receiver posts a
  1840. CPViewBoundsDidChangeNotification when its
  1841. bounds is changed.
  1842. */
  1843. - (BOOL)postsBoundsChangedNotifications
  1844. {
  1845. return _postsBoundsChangedNotifications;
  1846. }
  1847. /*!
  1848. Initiates a drag operation from the receiver to another view that accepts dragged data.
  1849. @param anImage the image to be dragged
  1850. @param aLocation the lower-left corner coordinate of \c anImage
  1851. @param mouseOffset the distance from the \c -mouseDown: location and the current location
  1852. @param anEvent the \c -mouseDown: that triggered the drag
  1853. @param aPasteboard the pasteboard that holds the drag data
  1854. @param aSourceObject the drag operation controller
  1855. @param slideBack Whether the image should 'slide back' if the drag is rejected
  1856. */
  1857. - (void)dragImage:(CPImage)anImage at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
  1858. {
  1859. [_window dragImage:anImage at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
  1860. }
  1861. /*!
  1862. Initiates a drag operation from the receiver to another view that accepts dragged data.
  1863. @param aView the view to be dragged
  1864. @param aLocation the top-left corner coordinate of \c aView
  1865. @param mouseOffset the distance from the \c -mouseDown: location and the current location
  1866. @param anEvent the \c -mouseDown: that triggered the drag
  1867. @param aPasteboard the pasteboard that holds the drag data
  1868. @param aSourceObject the drag operation controller
  1869. @param slideBack Whether the view should 'slide back' if the drag is rejected
  1870. */
  1871. - (void)dragView:(CPView)aView at:(CGPoint)aLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
  1872. {
  1873. [_window dragView:aView at:[self convertPoint:aLocation toView:nil] offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
  1874. }
  1875. /*!
  1876. Sets the receiver's list of acceptable data types for a dragging operation.
  1877. @param pasteboardTypes an array of CPPasteboards
  1878. */
  1879. - (void)registerForDraggedTypes:(CPArray)pasteboardTypes
  1880. {
  1881. if (!pasteboardTypes || ![pasteboardTypes count])
  1882. return;
  1883. var theWindow = [self window];
  1884. [theWindow _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
  1885. [_registeredDraggedTypes addObjectsFromArray:pasteboardTypes];
  1886. [theWindow _noteRegisteredDraggedTypes:_registeredDraggedTypes];
  1887. _registeredDraggedTypesArray = nil;
  1888. }
  1889. /*!
  1890. Returns an array of all types the receiver accepts for dragging operations.
  1891. @return an array of CPPasteBoards
  1892. */
  1893. - (CPArray)registeredDraggedTypes
  1894. {
  1895. if (!_registeredDraggedTypesArray)
  1896. _registeredDraggedTypesArray = [_registeredDraggedTypes allObjects];
  1897. return _registeredDraggedTypesArray;
  1898. }
  1899. /*!
  1900. Resets the array of acceptable data types for a dragging operation.
  1901. */
  1902. - (void)unregisterDraggedTypes
  1903. {
  1904. [[self window] _noteUnregisteredDraggedTypes:_registeredDraggedTypes];
  1905. _registeredDraggedTypes = [CPSet set];
  1906. _registeredDraggedTypesArray = [];
  1907. }
  1908. /*!
  1909. Draws the receiver into \c aRect. This method should be overridden by subclasses.
  1910. @param aRect the area that should be drawn into
  1911. */
  1912. - (void)drawRect:(CGRect)aRect
  1913. {
  1914. }
  1915. // Scaling
  1916. /*!
  1917. Scales the receiver’s coordinate system so that the unit square scales to the specified dimensions.
  1918. The bounds of the receiver will change, for instance if the given size is (0.5, 0.5) the width and height of the bounds will be multiply by 2.
  1919. You must call setNeedsDisplay: to redraw the view.
  1920. @param aSize, the size corresponding the new unit scales
  1921. */
  1922. - (void)scaleUnitSquareToSize:(CGSize)aSize
  1923. {
  1924. if (!aSize)
  1925. return;
  1926. // Reset the bounds
  1927. var bounds = CGRectMakeCopy([self bounds]);
  1928. bounds.size.width *= _scaleSize.width;
  1929. bounds.size.height *= _scaleSize.height;
  1930. [self willChangeValueForKey:@"scaleSize"];
  1931. _scaleSize = CGSizeMakeCopy([self scaleSize]);
  1932. _scaleSize.height *= aSize.height;
  1933. _scaleSize.width *= aSize.width;
  1934. [self didChangeValueForKey:@"scaleSize"];
  1935. _isScaled = YES;
  1936. _hierarchyScaleSize = CGSizeMakeCopy([self _hierarchyScaleSize]);
  1937. _hierarchyScaleSize.height *= aSize.height;
  1938. _hierarchyScaleSize.width *= aSize.width;
  1939. var scaleAffine = CGAffineTransformMakeScale(1.0 / _scaleSize.width, 1.0 / _scaleSize.height),
  1940. newBounds = CGRectApplyAffineTransform(CGRectMakeCopy(bounds), scaleAffine);
  1941. [self setBounds:newBounds];
  1942. [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
  1943. }
  1944. /*!
  1945. @ignore
  1946. Set the _hierarchyScaleSize and call all of the subviews to set their _hierarchyScaleSize
  1947. */
  1948. - (void)_scaleSizeUnitSquareToSize:(CGSize)aSize
  1949. {
  1950. _hierarchyScaleSize = CGSizeMakeCopy([_superview _hierarchyScaleSize]);
  1951. if (_isScaled)
  1952. {
  1953. _hierarchyScaleSize.width *= _scaleSize.width;
  1954. _hierarchyScaleSize.height *= _scaleSize.height;
  1955. }
  1956. [_subviews makeObjectsPerformSelector:@selector(_scaleSizeUnitSquareToSize:) withObject:aSize];
  1957. }
  1958. /*!
  1959. Return the _hierarchyScaleSize, this is a CGSize with the real zoom of the view (depending with his parents)
  1960. */
  1961. - (CGSize)_hierarchyScaleSize
  1962. {
  1963. return _hierarchyScaleSize || CGSizeMake(1.0, 1.0);
  1964. }
  1965. /*!
  1966. Make a zoom in css
  1967. */
  1968. - (void)_applyCSSScalingTranformations
  1969. {
  1970. #if PLATFORM(DOM)
  1971. if (_isScaled)
  1972. {
  1973. var scale = [self scaleSize],
  1974. browserPropertyTransform = CPBrowserStyleProperty(@"transform"),
  1975. browserPropertyTransformOrigin = CPBrowserStyleProperty(@"transformOrigin");
  1976. self._DOMElement.style[browserPropertyTransform] = 'scale(' + scale.width + ', ' + scale.height + ')';
  1977. self._DOMElement.style[browserPropertyTransformOrigin] = '0 0';
  1978. [self _setDisplayServerSetStyleSize:[self frameSize]];
  1979. }
  1980. #endif
  1981. }
  1982. // Displaying
  1983. /*!
  1984. Marks the entire view as dirty, and needing a redraw.
  1985. */
  1986. - (void)setNeedsDisplay:(BOOL)aFlag
  1987. {
  1988. if (aFlag)
  1989. {
  1990. [self _applyCSSScalingTranformations];
  1991. [self setNeedsDisplayInRect:[self bounds]];
  1992. }
  1993. }
  1994. /*!
  1995. Marks the area denoted by \c aRect as dirty, and initiates a redraw on it.
  1996. @param aRect the area that needs to be redrawn
  1997. */
  1998. - (void)setNeedsDisplayInRect:(CGRect)aRect
  1999. {
  2000. if (!(_viewClassFlags & CPViewHasCustomDrawRect))
  2001. return;
  2002. if (CGRectIsEmpty(aRect))
  2003. return;
  2004. if (_dirtyRect && !CGRectIsEmpty(_dirtyRect))
  2005. _dirtyRect = CGRectUnion(aRect, _dirtyRect);
  2006. else
  2007. _dirtyRect = CGRectMakeCopy(aRect);
  2008. _CPDisplayServerAddDisplayObject(self);
  2009. }
  2010. - (BOOL)needsDisplay
  2011. {
  2012. return _dirtyRect && !CGRectIsEmpty(_dirtyRect);
  2013. }
  2014. /*!
  2015. Displays the receiver and any of its subviews that need to be displayed.
  2016. */
  2017. - (void)displayIfNeeded
  2018. {
  2019. if ([self needsDisplay])
  2020. [self displayRect:_dirtyRect];
  2021. }
  2022. /*!
  2023. Draws the entire area of the receiver as defined by its \c -bounds.
  2024. */
  2025. - (void)display
  2026. {
  2027. [self displayRect:[self visibleRect]];
  2028. }
  2029. - (void)displayIfNeededInRect:(CGRect)aRect
  2030. {
  2031. if ([self needsDisplay])
  2032. [self displayRect:aRect];
  2033. }
  2034. /*!
  2035. Draws the receiver into the area defined by \c aRect.
  2036. @param aRect the area to be drawn
  2037. */
  2038. - (void)displayRect:(CGRect)aRect
  2039. {
  2040. [self viewWillDraw];
  2041. [self displayRectIgnoringOpacity:aRect inContext:nil];
  2042. _dirtyRect = NULL;
  2043. }
  2044. - (void)displayRectIgnoringOpacity:(CGRect)aRect inContext:(CPGraphicsContext)aGraphicsContext
  2045. {
  2046. if ([self isHidden])
  2047. return;
  2048. #if PLATFORM(DOM)
  2049. [self lockFocus];
  2050. CGContextClearRect([[CPGraphicsContext currentContext] graphicsPort], aRect);
  2051. [self drawRect:aRect];
  2052. [self unlockFocus];
  2053. #endif
  2054. }
  2055. - (void)viewWillDraw
  2056. {
  2057. }
  2058. /*!
  2059. Locks focus on the receiver, so drawing commands apply to it.
  2060. */
  2061. - (void)lockFocus
  2062. {
  2063. if (!_graphicsContext)
  2064. {
  2065. var graphicsPort = CGBitmapGraphicsContextCreate();
  2066. #if PLATFORM(DOM)
  2067. var width = CGRectGetWidth(_frame),
  2068. height = CGRectGetHeight(_frame);
  2069. _DOMContentsElement = graphicsPort.DOMElement;
  2070. _DOMContentsElement.style.zIndex = -100;
  2071. _DOMContentsElement.style.overflow = "hidden";
  2072. _DOMContentsElement.style.position = "absolute";
  2073. _DOMContentsElement.style.visibility = "visible";
  2074. CPDOMDisplayServerSetSize(_DOMContentsElement, width, height);
  2075. CPDOMDisplayServerSetStyleLeftTop(_DOMContentsElement, NULL, 0.0, 0.0);
  2076. CPDOMDisplayServerSetStyleSize(_DOMContentsElement, width, height);
  2077. // The performance implications of this aren't clear, but without this subviews might not be redrawn when this
  2078. // view moves.
  2079. if (CPPlatformHasBug(CPCanvasParentDrawErrorsOnMovementBug))
  2080. _DOMElement.style.webkitTransform = 'translateX(0)';
  2081. CPDOMDisplayServerAppendChild(_DOMElement, _DOMContentsElement);
  2082. #endif
  2083. _graphicsContext = [CPGraphicsContext graphicsContextWithGraphicsPort:graphicsPort flipped:YES];
  2084. }
  2085. [CPGraphicsContext setCurrentContext:_graphicsContext];
  2086. CGContextSaveGState([_graphicsContext graphicsPort]);
  2087. }
  2088. /*!
  2089. Takes focus away from the receiver, and restores it to the previous view.
  2090. */
  2091. - (void)unlockFocus
  2092. {
  2093. CGContextRestoreGState([_graphicsContext graphicsPort]);
  2094. [CPGraphicsContext setCurrentContext:nil];
  2095. }
  2096. - (void)setNeedsLayout
  2097. {
  2098. if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews))
  2099. return;
  2100. _needsLayout = YES;
  2101. _CPDisplayServerAddLayoutObject(self);
  2102. }
  2103. - (void)layoutIfNeeded
  2104. {
  2105. if (_needsLayout)
  2106. {
  2107. _needsLayout = NO;
  2108. [self layoutSubviews];
  2109. }
  2110. }
  2111. - (void)layoutSubviews
  2112. {
  2113. }
  2114. /*!
  2115. Returns whether the receiver is completely opaque. By default, returns \c NO.
  2116. */
  2117. - (BOOL)isOpaque
  2118. {
  2119. return NO;
  2120. }
  2121. /*!
  2122. Returns the rectangle of the receiver not clipped by its superview.
  2123. */
  2124. - (CGRect)visibleRect
  2125. {
  2126. if (!_superview)
  2127. return _bounds;
  2128. return CGRectIntersection([self convertRect:[_superview visibleRect] fromView:_superview], _bounds);
  2129. }
  2130. // Scrolling
  2131. /* @ignore */
  2132. - (CPScrollView)_enclosingClipView
  2133. {
  2134. var superview = _superview,
  2135. clipViewClass = [CPClipView class];
  2136. while (superview && ![superview isKindOfClass:clipViewClass])
  2137. superview = superview._superview;
  2138. return superview;
  2139. }
  2140. /*!
  2141. Changes the receiver's frame origin to a 'constrained' \c aPoint.
  2142. @param aPoint the proposed frame origin
  2143. */
  2144. - (void)scrollPoint:(CGPoint)aPoint
  2145. {
  2146. var clipView = [self _enclosingClipView];
  2147. if (!clipView)
  2148. return;
  2149. [clipView scrollToPoint:[self convertPoint:aPoint toView:clipView]];
  2150. }
  2151. /*!
  2152. Scrolls the nearest ancestor CPClipView a minimum amount so \c aRect can become visible.
  2153. @param aRect the area to become visible
  2154. @return \c YES if any scrolling occurred, \c NO otherwise.
  2155. */
  2156. - (BOOL)scrollRectToVisible:(CGRect)aRect
  2157. {
  2158. // Make sure we have a rect that exists.
  2159. aRect = CGRectIntersection(aRect, _bounds);
  2160. // If aRect is empty no scrolling required.
  2161. if (CGRectIsEmpty(aRect))
  2162. return NO;
  2163. var enclosingClipView = [self _enclosingClipView];
  2164. // If we're not in a clip view, then there isn't much we can do.
  2165. if (!enclosingClipView)
  2166. return NO;
  2167. var documentView = [enclosingClipView documentView];
  2168. // If the clip view doesn't have a document view, then there isn't much we can do.
  2169. if (!documentView)
  2170. return NO;
  2171. // Get the document view visible rect and convert aRect to the document view's coordinate system
  2172. var documentViewVisibleRect = [documentView visibleRect],
  2173. rectInDocumentView = [self convertRect:aRect toView:documentView];
  2174. // If already visible then no scrolling required.
  2175. if (CGRectContainsRect(documentViewVisibleRect, rectInDocumentView))
  2176. return NO;
  2177. var scrollPoint = CGPointMakeCopy(documentViewVisibleRect.origin);
  2178. // One of the following has to be true since our current visible rect didn't contain aRect.
  2179. if (CGRectGetMinX(rectInDocumentView) < CGRectGetMinX(documentViewVisibleRect))
  2180. scrollPoint.x = CGRectGetMinX(rectInDocumentView);
  2181. else if (CGRectGetMaxX(rectInDocumentView) > CGRectGetMaxX(documentViewVisibleRect))
  2182. scrollPoint.x += CGRectGetMaxX(rectInDocumentView) - CGRectGetMaxX(documentViewVisibleRect);
  2183. if (CGRectGetMinY(rectInDocumentView) < CGRectGetMinY(documentViewVisibleRect))
  2184. scrollPoint.y = CGRectGetMinY(rectInDocumentView);
  2185. else if (CGRectGetMaxY(rectInDocumentView) > CGRectGetMaxY(documentViewVisibleRect))
  2186. scrollPoint.y += CGRectGetMaxY(rectInDocumentView) - CGRectGetMaxY(documentViewVisibleRect);
  2187. [enclosingClipView scrollToPoint:scrollPoint];
  2188. return YES;
  2189. }
  2190. /*
  2191. FIXME Not yet implemented
  2192. */
  2193. - (BOOL)autoscroll:(CPEvent)anEvent
  2194. {
  2195. return [[self superview] autoscroll:anEvent];
  2196. }
  2197. /*!
  2198. Subclasses can override this to modify the visible rectangle after a
  2199. scrolling operation. The default implementation simply returns the provided rectangle.
  2200. @param proposedVisibleRect the rectangle to alter
  2201. @return the same adjusted rectangle
  2202. */
  2203. - (CGRect)adjustScroll:(CGRect)proposedVisibleRect
  2204. {
  2205. return proposedVisibleRect;
  2206. }
  2207. /*!
  2208. Should be overridden by subclasses.
  2209. */
  2210. - (void)scrollRect:(CGRect)aRect by:(float)anAmount
  2211. {
  2212. }
  2213. /*!
  2214. Returns the CPScrollView containing the receiver.
  2215. @return the CPScrollView containing the receiver.
  2216. */
  2217. - (CPScrollView)enclosingScrollView
  2218. {
  2219. var superview = _superview,
  2220. scrollViewClass = [CPScrollView class];
  2221. while (superview && ![superview isKindOfClass:scrollViewClass])
  2222. superview = superview._superview;
  2223. return superview;
  2224. }
  2225. /*!
  2226. Scrolls the clip view to a specified point
  2227. @param the clip view to scroll
  2228. @param the point to scroll to
  2229. */
  2230. - (void)scrollClipView:(CPClipView)aClipView toPoint:(CGPoint)aPoint
  2231. {
  2232. [aClipView scrollToPoint:aPoint];
  2233. }
  2234. /*!
  2235. Notifies the receiver (superview of a CPClipView)
  2236. that the clip view bounds or the document view bounds have changed.
  2237. @param aClipView the clip view of the superview being notified
  2238. */
  2239. - (void)reflectScrolledClipView:(CPClipView)aClipView
  2240. {
  2241. }
  2242. /*!
  2243. Return yes if the receiver is in a live-resize operation.
  2244. */
  2245. - (BOOL)inLiveResize
  2246. {
  2247. return _inLiveResize;
  2248. }
  2249. /*!
  2250. Not implemented.
  2251. A view will be sent this message before a window begins a resize operation. The
  2252. receiver might choose to simplify its drawing operations during a live resize
  2253. for speed.
  2254. Subclasses should call super.
  2255. */
  2256. - (void)viewWillStartLiveResize
  2257. {
  2258. _inLiveResize = YES;
  2259. }
  2260. /*!
  2261. Not implemented.
  2262. A view will be sent this message after a window finishes a resize operation. The
  2263. receiver which simplified its drawing operations in viewWillStartLiveResize might
  2264. stop doing so now. Note the view might no longer be in a window, so use
  2265. [self setNeedsDisplay:YES] if a final non-simplified redraw is required.
  2266. Subclasses should call super.
  2267. */
  2268. - (void)viewDidEndLiveResize
  2269. {
  2270. _inLiveResize = NO;
  2271. }
  2272. @end
  2273. @implementation CPView (KeyView)
  2274. /*!
  2275. Overridden by subclasses to handle a key equivalent.
  2276. If the receiver’s key equivalent is the same as the characters of the key-down event theEvent,
  2277. as returned by \ref CPEvent::charactersIgnoringModifiers "[anEvent charactersIgnoringModifiers]",
  2278. the receiver should take the appropriate action and return \c YES. Otherwise, it should return
  2279. the result of invoking super’s implementation. The default implementation of this method simply
  2280. passes the message down the view hierarchy (from superviews to subviews)
  2281. and returns \c NO if none of the receiver’s subviews responds \c YES.
  2282. @param anEvent An event object that represents the key equivalent pressed
  2283. @return \c YES if theEvent is a key equivalent that the receiver handled,
  2284. \c NO if it is not a key equivalent that it should handle.
  2285. */
  2286. - (BOOL)performKeyEquivalent:(CPEvent)anEvent
  2287. {
  2288. var count = [_subviews count];
  2289. // Is reverse iteration correct here? It matches the other (correct) code like hit testing.
  2290. while (count--)
  2291. if ([_subviews[count] performKeyEquivalent:anEvent])
  2292. return YES;
  2293. return NO;
  2294. }
  2295. - (BOOL)canBecomeKeyView
  2296. {
  2297. return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor];
  2298. }
  2299. - (CPView)nextKeyView
  2300. {
  2301. return _nextKeyView;
  2302. }
  2303. - (CPView)nextValidKeyView
  2304. {
  2305. var result = [self nextKeyView],
  2306. resultUID = [result UID],
  2307. unsuitableResults = {};
  2308. while (result && ![result canBecomeKeyView])
  2309. {
  2310. unsuitableResults[resultUID] = 1;
  2311. result = [result nextKeyView];
  2312. resultUID = [result UID];
  2313. // Did we get back to a key view we already ruled out due to ![result canBecomeKeyView]?
  2314. if (unsuitableResults[resultUID])
  2315. return nil;
  2316. }
  2317. return result;
  2318. }
  2319. - (CPView)previousKeyView
  2320. {
  2321. return _previousKeyView;
  2322. }
  2323. - (CPView)previousValidKeyView
  2324. {
  2325. var result = [self previousKeyView],
  2326. firstResult = result;
  2327. while (result && ![result canBecomeKeyView])
  2328. {
  2329. result = [result previousKeyView];
  2330. // Cycled.
  2331. if (result === firstResult)
  2332. return nil;
  2333. }
  2334. return result;
  2335. }
  2336. - (void)_setPreviousKeyView:(CPView)previous
  2337. {
  2338. if (![previous isEqual:self])
  2339. {
  2340. var previousWindow = [previous window];
  2341. if (!previousWindow || previousWindow === _window)
  2342. {
  2343. _previousKeyView = previous;
  2344. return;
  2345. }
  2346. }
  2347. _previousKeyView = nil;
  2348. }
  2349. - (void)setNextKeyView:(CPView)next
  2350. {
  2351. if (![next isEqual:self])
  2352. {
  2353. var nextWindow = [next window];
  2354. if (!nextWindow || nextWindow === _window)
  2355. {
  2356. _nextKeyView = next;
  2357. [_nextKeyView _setPreviousKeyView:self];
  2358. return;
  2359. }
  2360. }
  2361. _nextKeyView = nil;
  2362. }
  2363. @end
  2364. @implementation CPView (CoreAnimationAdditions)
  2365. /*!
  2366. Sets the core animation layer to be used by this receiver.
  2367. */
  2368. - (void)setLayer:(CALayer)aLayer
  2369. {
  2370. if (_layer == aLayer)
  2371. return;
  2372. if (_layer)
  2373. {
  2374. _layer._owningView = nil;
  2375. #if PLATFORM(DOM)
  2376. _DOMElement.removeChild(_layer._DOMElement);
  2377. #endif
  2378. }
  2379. _layer = aLayer;
  2380. if (_layer)
  2381. {
  2382. var bounds = CGRectMakeCopy([self bounds]);
  2383. [_layer _setOwningView:self];
  2384. #if PLATFORM(DOM)
  2385. _layer._DOMElement.style.zIndex = 100;
  2386. _DOMElement.appendChild(_layer._DOMElement);
  2387. #endif
  2388. }
  2389. }
  2390. /*!
  2391. Returns the core animation layer used by the receiver.
  2392. */
  2393. - (CALayer)layer
  2394. {
  2395. return _layer;
  2396. }
  2397. /*!
  2398. Sets whether the receiver wants a core animation layer.
  2399. @param \c YES means the receiver wants a layer.
  2400. */
  2401. - (void)setWantsLayer:(BOOL)aFlag
  2402. {
  2403. _wantsLayer = !!aFlag;
  2404. }
  2405. /*!
  2406. Returns \c YES if the receiver uses a CALayer
  2407. @returns \c YES if the receiver uses a CALayer
  2408. */
  2409. - (BOOL)wantsLayer
  2410. {
  2411. return _wantsLayer;
  2412. }
  2413. @end
  2414. @implementation CPView (Scaling)
  2415. /*!
  2416. Set the zoom of the view. This will call scaleUnitSquareToSize: and setNeedsDisplay:
  2417. This method doesn't care about the last zoom you set in the view
  2418. @param aSize, the size corresponding the new unit scales
  2419. */
  2420. - (void)setScaleSize:(CGSize)aSize
  2421. {
  2422. if (CGSizeEqualToSize(_scaleSize, aSize))
  2423. return;
  2424. var size = CGSizeMakeZero(),
  2425. scale = CGSizeMakeCopy([self scaleSize]);
  2426. size.height = aSize.height / scale.height;
  2427. size.width = aSize.width / scale.width;
  2428. [self scaleUnitSquareToSize:size];
  2429. [self setNeedsDisplay:YES];
  2430. }
  2431. /*!
  2432. Return the scaleSize of the view, this scaleSize is used to scale in css
  2433. */
  2434. - (CGSize)scaleSize
  2435. {
  2436. return _scaleSize || CGSizeMake(1.0, 1.0);
  2437. }
  2438. @end
  2439. @implementation CPView (Theming)
  2440. #pragma mark Theme States
  2441. - (unsigned)themeState
  2442. {
  2443. return _themeState;
  2444. }
  2445. - (BOOL)hasThemeState:(ThemeState)aState
  2446. {
  2447. if (aState.isa && [aState isKindOfClass:CPArray])
  2448. return _themeState.hasThemeState.apply(_themeState, aState);
  2449. return _themeState.hasThemeState(aState);
  2450. }
  2451. - (BOOL)setThemeState:(ThemeState)aState
  2452. {
  2453. if (aState && aState.isa && [aState isKindOfClass:CPArray])
  2454. aState = CPThemeState.apply(null, aState);
  2455. if (_themeState.hasThemeState(aState))
  2456. return NO;
  2457. _themeState = CPThemeState(_themeState, aState);
  2458. [self setNeedsLayout];
  2459. [self setNeedsDisplay:YES];
  2460. return YES;
  2461. }
  2462. - (BOOL)unsetThemeState:(ThemeState)aState
  2463. {
  2464. if (aState && aState.isa && [aState isKindOfClass:CPArray])
  2465. aState = CPThemeState.apply(null, aState);
  2466. var oldThemeState = _themeState;
  2467. _themeState = _themeState.without(aState);
  2468. if (oldThemeState === _themeState)
  2469. return NO;
  2470. [self setNeedsLayout];
  2471. [self setNeedsDisplay:YES];
  2472. return YES;
  2473. }
  2474. - (BOOL)becomeFirstResponder
  2475. {
  2476. var r = [super becomeFirstResponder];
  2477. if (r)
  2478. [self _notifyViewDidBecomeFirstResponder];
  2479. return r;
  2480. }
  2481. - (void)_notifyViewDidBecomeFirstResponder
  2482. {
  2483. [self setThemeState:CPThemeStateFirstResponder];
  2484. var count = [_subviews count];
  2485. while (count--)
  2486. [_subviews[count] _notifyViewDidBecomeFirstResponder];
  2487. }
  2488. - (BOOL)resignFirstResponder
  2489. {
  2490. var r = [super resignFirstResponder];
  2491. if (r)
  2492. [self _notifyViewDidResignFirstResponder];
  2493. return r;
  2494. }
  2495. - (void)_notifyViewDidResignFirstResponder
  2496. {
  2497. [self unsetThemeState:CPThemeStateFirstResponder];
  2498. var count = [_subviews count];
  2499. while (count--)
  2500. [_subviews[count] _notifyViewDidResignFirstResponder];
  2501. }
  2502. - (void)_notifyWindowDidBecomeKey
  2503. {
  2504. [self setThemeState:CPThemeStateKeyWindow];
  2505. var count = [_subviews count];
  2506. while (count--)
  2507. [_subviews[count] _notifyWindowDidBecomeKey];
  2508. }
  2509. - (void)_notifyWindowDidResignKey
  2510. {
  2511. [self unsetThemeState:CPThemeStateKeyWindow];
  2512. var count = [_subviews count];
  2513. while (count--)
  2514. [_subviews[count] _notifyWindowDidResignKey];
  2515. }
  2516. #pragma mark Theme Attributes
  2517. + (CPString)defaultThemeClass
  2518. {
  2519. return nil;
  2520. }
  2521. - (CPString)themeClass
  2522. {
  2523. if (_themeClass)
  2524. return _themeClass;
  2525. return [[self class] defaultThemeClass];
  2526. }
  2527. - (void)setThemeClass:(CPString)theClass
  2528. {
  2529. _themeClass = theClass;
  2530. [self _loadThemeAttributes];
  2531. [self setNeedsLayout];
  2532. [self setNeedsDisplay:YES];
  2533. }
  2534. + (CPDictionary)themeAttributes
  2535. {
  2536. return nil;
  2537. }
  2538. + (CPArray)_themeAttributes
  2539. {
  2540. if (!CachedThemeAttributes)
  2541. CachedThemeAttributes = {};
  2542. var theClass = [self class],
  2543. CPViewClass = [CPView class],
  2544. attributes = [],
  2545. nullValue = [CPNull null];
  2546. for (; theClass && theClass !== CPViewClass; theClass = [theClass superclass])
  2547. {
  2548. var cachedAttributes = CachedThemeAttributes[class_getName(theClass)];
  2549. if (cachedAttributes)
  2550. {
  2551. attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
  2552. CachedThemeAttributes[[self className]] = attributes;
  2553. break;
  2554. }
  2555. var attributeDictionary = [theClass themeAttributes];
  2556. if (!attributeDictionary)
  2557. continue;
  2558. var attributeKeys = [attributeDictionary allKeys],
  2559. attributeCount = attributeKeys.length;
  2560. while (attributeCount--)
  2561. {
  2562. var attributeName = attributeKeys[attributeCount],
  2563. attributeValue = [attributeDictionary objectForKey:attributeName];
  2564. attributes.push(attributeValue === nullValue ? nil : attributeValue);
  2565. attributes.push(attributeName);
  2566. }
  2567. }
  2568. return attributes;
  2569. }
  2570. - (void)_loadThemeAttributes
  2571. {
  2572. var theClass = [self class],
  2573. attributes = [theClass _themeAttributes],
  2574. count = attributes.length;
  2575. if (!count)
  2576. return;
  2577. var theme = [self theme],
  2578. themeClass = [self themeClass];
  2579. _themeAttributes = {};
  2580. while (count--)
  2581. {
  2582. var attributeName = attributes[count--],
  2583. attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]];
  2584. [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
  2585. _themeAttributes[attributeName] = attribute;
  2586. }
  2587. }
  2588. - (void)setTheme:(CPTheme)aTheme
  2589. {
  2590. if (_theme === aTheme)
  2591. return;
  2592. _theme = aTheme;
  2593. [self viewDidChangeTheme];
  2594. }
  2595. - (void)_setThemeIncludingDescendants:(CPTheme)aTheme
  2596. {
  2597. [self setTheme:aTheme];
  2598. [[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
  2599. }
  2600. - (CPTheme)theme
  2601. {
  2602. return _theme;
  2603. }
  2604. - (void)viewDidChangeTheme
  2605. {
  2606. if (!_themeAttributes)
  2607. return;
  2608. var theme = [self theme],
  2609. themeClass = [self themeClass];
  2610. for (var attributeName in _themeAttributes)
  2611. if (_themeAttributes.hasOwnProperty(attributeName))
  2612. [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
  2613. [self setNeedsLayout];
  2614. [self setNeedsDisplay:YES];
  2615. }
  2616. - (CPDictionary)_themeAttributeDictionary
  2617. {
  2618. var dictionary = @{};
  2619. if (_themeAttributes)
  2620. {
  2621. var theme = [self theme];
  2622. for (var attributeName in _themeAttributes)
  2623. if (_themeAttributes.hasOwnProperty(attributeName))
  2624. [dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
  2625. }
  2626. return dictionary;
  2627. }
  2628. - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
  2629. {
  2630. if (aState.isa && [aState isKindOfClass:CPArray])
  2631. aState = CPThemeState.apply(null, aState);
  2632. if (!_themeAttributes || !_themeAttributes[aName])
  2633. [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
  2634. var currentValue = [self currentValueForThemeAttribute:aName];
  2635. [_themeAttributes[aName] setValue:aValue forState:aState];
  2636. if ([self currentValueForThemeAttribute:aName] === currentValue)
  2637. return;
  2638. [self setNeedsDisplay:YES];
  2639. [self setNeedsLayout];
  2640. }
  2641. - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
  2642. {
  2643. if (!_themeAttributes || !_themeAttributes[aName])
  2644. [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
  2645. var currentValue = [self currentValueForThemeAttribute:aName];
  2646. [_themeAttributes[aName] setValue:aValue];
  2647. if ([self currentValueForThemeAttribute:aName] === currentValue)
  2648. return;
  2649. [self setNeedsDisplay:YES];
  2650. [self setNeedsLayout];
  2651. }
  2652. - (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState
  2653. {
  2654. if (aState.isa && [aState isKindOfClass:CPArray])
  2655. aState = CPThemeState.apply(null, aState);
  2656. if (!_themeAttributes || !_themeAttributes[aName])
  2657. [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
  2658. return [_themeAttributes[aName] valueForState:aState];
  2659. }
  2660. - (id)valueForThemeAttribute:(CPString)aName
  2661. {
  2662. if (!_themeAttributes || !_themeAttributes[aName])
  2663. [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
  2664. return [_themeAttributes[aName] value];
  2665. }
  2666. - (id)currentValueForThemeAttribute:(CPString)aName
  2667. {
  2668. if (!_themeAttributes || !_themeAttributes[aName])
  2669. [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
  2670. return [_themeAttributes[aName] valueForState:_themeState];
  2671. }
  2672. - (BOOL)hasThemeAttribute:(CPString)aName
  2673. {
  2674. return (_themeAttributes && _themeAttributes[aName] !== undefined);
  2675. }
  2676. /*!
  2677. Registers theme values encoded in an array at runtime. The format of the data in the array
  2678. is the same as that used by ThemeDescriptors.j, with the exception that you need to use
  2679. CPColorWithImages() in place of PatternColor(). For more information see the comments
  2680. at the top of ThemeDescriptors.j.
  2681. @param themeValues array of theme values
  2682. */
  2683. - (void)registerThemeValues:(CPArray)themeValues
  2684. {
  2685. for (var i = 0; i < themeValues.length; ++i)
  2686. {
  2687. var attributeValueState = themeValues[i],
  2688. attribute = attributeValueState[0],
  2689. value = attributeValueState[1],
  2690. state = attributeValueState[2];
  2691. if (state)
  2692. [self setValue:value forThemeAttribute:attribute inState:state];
  2693. else
  2694. [self setValue:value forThemeAttribute:attribute];
  2695. }
  2696. }
  2697. /*!
  2698. Registers theme values encoded in an array at runtime. The format of the data in the array
  2699. is the same as that used by ThemeDescriptors.j, with the exception that you need to use
  2700. CPColorWithImages() in place of PatternColor(). The values in \c inheritedValues are
  2701. registered first, then those in \c themeValues override/augment the inherited values.
  2702. For more information see the comments at the top of ThemeDescriptors.j.
  2703. @param themeValues array of base theme values
  2704. @param inheritedValues array of overridden/additional theme values
  2705. */
  2706. - (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
  2707. {
  2708. // Register inherited values first, then override those with the subtheme values.
  2709. if (inheritedValues)
  2710. [self registerThemeValues:inheritedValues];
  2711. if (themeValues)
  2712. [self registerThemeValues:themeValues];
  2713. }
  2714. - (CPView)createEphemeralSubviewNamed:(CPString)aViewName
  2715. {
  2716. return nil;
  2717. }
  2718. - (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
  2719. {
  2720. return CGRectMakeZero();
  2721. }
  2722. - (CPView)layoutEphemeralSubviewNamed:(CPString)aViewName
  2723. positioned:(CPWindowOrderingMode)anOrderingMode
  2724. relativeToEphemeralSubviewNamed:(CPString)relativeToViewName
  2725. {
  2726. if (!_ephemeralSubviewsForNames)
  2727. {
  2728. _ephemeralSubviewsForNames = {};
  2729. _ephemeralSubviews = [CPSet set];
  2730. }
  2731. var frame = [self rectForEphemeralSubviewNamed:aViewName];
  2732. if (frame)
  2733. {
  2734. if (!_ephemeralSubviewsForNames[aViewName])
  2735. {
  2736. _ephemeralSubviewsForNames[aViewName] = [self createEphemeralSubviewNamed:aViewName];
  2737. [_ephemeralSubviews addObject:_ephemeralSubviewsForNames[aViewName]];
  2738. if (_ephemeralSubviewsForNames[aViewName])
  2739. [self addSubview:_ephemeralSubviewsForNames[aViewName] positioned:anOrderingMode relativeTo:_ephemeralSubviewsForNames[relativeToViewName]];
  2740. }
  2741. if (_ephemeralSubviewsForNames[aViewName])
  2742. [_ephemeralSubviewsForNames[aViewName] setFrame:frame];
  2743. }
  2744. else if (_ephemeralSubviewsForNames[aViewName])
  2745. {
  2746. [_ephemeralSubviewsForNames[aViewName] removeFromSuperview];
  2747. [_ephemeralSubviews removeObject:_ephemeralSubviewsForNames[aViewName]];
  2748. delete _ephemeralSubviewsForNames[aViewName];
  2749. }
  2750. return _ephemeralSubviewsForNames[aViewName];
  2751. }
  2752. - (CPView)ephemeralSubviewNamed:(CPString)aViewName
  2753. {
  2754. if (!_ephemeralSubviewsForNames)
  2755. return nil;
  2756. return (_ephemeralSubviewsForNames[aViewName] || nil);
  2757. }
  2758. @end
  2759. var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
  2760. CPViewAutoresizesSubviewsKey = @"CPViewAutoresizesSubviews",
  2761. CPViewBackgroundColorKey = @"CPViewBackgroundColor",
  2762. CPViewBoundsKey = @"CPViewBoundsKey",
  2763. CPViewFrameKey = @"CPViewFrameKey",
  2764. CPViewHitTestsKey = @"CPViewHitTestsKey",
  2765. CPViewToolTipKey = @"CPViewToolTipKey",
  2766. CPViewIsHiddenKey = @"CPViewIsHiddenKey",
  2767. CPViewOpacityKey = @"CPViewOpacityKey",
  2768. CPViewSubviewsKey = @"CPViewSubviewsKey",
  2769. CPViewSuperviewKey = @"CPViewSuperviewKey",
  2770. CPViewTagKey = @"CPViewTagKey",
  2771. CPViewThemeClassKey = @"CPViewThemeClassKey",
  2772. CPViewThemeStateKey = @"CPViewThemeStateKey",
  2773. CPViewWindowKey = @"CPViewWindowKey",
  2774. CPViewNextKeyViewKey = @"CPViewNextKeyViewKey",
  2775. CPViewPreviousKeyViewKey = @"CPViewPreviousKeyViewKey",
  2776. CPReuseIdentifierKey = @"CPReuseIdentifierKey",
  2777. CPViewScaleKey = @"CPViewScaleKey",
  2778. CPViewSizeScaleKey = @"CPViewSizeScaleKey",
  2779. CPViewIsScaledKey = @"CPViewIsScaledKey";
  2780. @implementation CPView (CPCoding)
  2781. /*!
  2782. Initializes the view from an archive.
  2783. @param aCoder the coder from which to initialize
  2784. @return the initialized view
  2785. */
  2786. - (id)initWithCoder:(CPCoder)aCoder
  2787. {
  2788. // We create the DOMElement "early" because there is a chance that we
  2789. // will decode our superview before we are done decoding, at which point
  2790. // we have to have an element to place in the tree. Perhaps there is
  2791. // a more "elegant" way to do this...?
  2792. #if PLATFORM(DOM)
  2793. _DOMElement = DOMElementPrototype.cloneNode(false);
  2794. AppKitTagDOMElement(self, _DOMElement);
  2795. #endif
  2796. // Also decode these "early".
  2797. _frame = [aCoder decodeRectForKey:CPViewFrameKey];
  2798. _bounds = [aCoder decodeRectForKey:CPViewBoundsKey];
  2799. self = [super initWithCoder:aCoder];
  2800. if (self)
  2801. {
  2802. // We have to manually check because it may be 0, so we can't use ||
  2803. _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1;
  2804. _identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey];
  2805. _window = [aCoder decodeObjectForKey:CPViewWindowKey];
  2806. _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey];
  2807. // We have to manually add the subviews so that they will receive
  2808. // viewWillMoveToSuperview: and viewDidMoveToSuperview:
  2809. _subviews = [];
  2810. var subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || [];
  2811. for (var i = 0, count = [subviews count]; i < count; ++i)
  2812. {
  2813. // addSubview won't do anything if the superview is already self, so clear it
  2814. subviews[i]._superview = nil;
  2815. [self addSubview:subviews[i]];
  2816. }
  2817. // FIXME: Should we encode/decode this?
  2818. _registeredDraggedTypes = [CPSet set];
  2819. _registeredDraggedTypesArray = [];
  2820. // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded.
  2821. // We make sure we don't override the value by checking if it was already set.
  2822. if (_autoresizingMask === nil)
  2823. _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable;
  2824. _autoresizesSubviews = ![aCoder containsValueForKey:CPViewAutoresizesSubviewsKey] || [aCoder decodeBoolForKey:CPViewAutoresizesSubviewsKey];
  2825. _hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey];
  2826. [self _setupToolTipHandlers];
  2827. _toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey];
  2828. if (_toolTip)
  2829. [self _installToolTipEventHandlers];
  2830. _scaleSize = [aCoder containsValueForKey:CPViewScaleKey] ? [aCoder decodeSizeForKey:CPViewScaleKey] : CGSizeMake(1.0, 1.0);
  2831. _hierarchyScaleSize = [aCoder containsValueForKey:CPViewSizeScaleKey] ? [aCoder decodeSizeForKey:CPViewSizeScaleKey] : CGSizeMake(1.0, 1.0);
  2832. _isScaled = [aCoder containsValueForKey:CPViewIsScaledKey] ? [aCoder decodeBoolForKey:CPViewIsScaledKey] : NO;
  2833. // DOM SETUP
  2834. #if PLATFORM(DOM)
  2835. _DOMImageParts = [];
  2836. _DOMImageSizes = [];
  2837. CPDOMDisplayServerSetStyleLeftTop(_DOMElement, NULL, CGRectGetMinX(_frame), CGRectGetMinY(_frame));
  2838. [self _setDisplayServerSetStyleSize:_frame.size];
  2839. var index = 0,
  2840. count = _subviews.length;
  2841. for (; index < count; ++index)
  2842. {
  2843. CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement);
  2844. //_subviews[index]._superview = self;
  2845. }
  2846. #endif
  2847. [self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
  2848. if ([aCoder containsValueForKey:CPViewOpacityKey])
  2849. [self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
  2850. else
  2851. _opacity = 1.0;
  2852. [self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
  2853. [self _setupViewFlags];
  2854. _theme = [CPTheme defaultTheme];
  2855. _themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
  2856. _themeState = CPThemeState([aCoder decodeObjectForKey:CPViewThemeStateKey]);
  2857. _themeAttributes = {};
  2858. var theClass = [self class],
  2859. themeClass = [self themeClass],
  2860. attributes = [theClass _themeAttributes],
  2861. count = attributes.length;
  2862. while (count--)
  2863. {
  2864. var attributeName = attributes[count--];
  2865. _themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass);
  2866. }
  2867. [self setNeedsDisplay:YES];
  2868. [self setNeedsLayout];
  2869. }
  2870. return self;
  2871. }
  2872. /*!
  2873. Archives the view to a coder.
  2874. @param aCoder the object into which the view's data will be archived.
  2875. */
  2876. - (void)encodeWithCoder:(CPCoder)aCoder
  2877. {
  2878. [super encodeWithCoder:aCoder];
  2879. if (_tag !== -1)
  2880. [aCoder encodeInt:_tag forKey:CPViewTagKey];
  2881. [aCoder encodeRect:_frame forKey:CPViewFrameKey];
  2882. [aCoder encodeRect:_bounds forKey:CPViewBoundsKey];
  2883. // This will come out nil on the other side with decodeObjectForKey:
  2884. if (_window !== nil)
  2885. [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey];
  2886. var count = [_subviews count],
  2887. encodedSubviews = _subviews;
  2888. if (count > 0 && [_ephemeralSubviews count] > 0)
  2889. {
  2890. encodedSubviews = [encodedSubviews copy];
  2891. while (count--)
  2892. if ([_ephemeralSubviews containsObject:encodedSubviews[count]])
  2893. encodedSubviews.splice(count, 1);
  2894. }
  2895. if (encodedSubviews.length > 0)
  2896. [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey];
  2897. // This will come out nil on the other side with decodeObjectForKey:
  2898. if (_superview !== nil)
  2899. [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey];
  2900. if (_autoresizingMask !== CPViewNotSizable)
  2901. [aCoder encodeInt:_autoresizingMask forKey:CPViewAutoresizingMaskKey];
  2902. if (!_autoresizesSubviews)
  2903. [aCoder encodeBool:_autoresizesSubviews forKey:CPViewAutoresizesSubviewsKey];
  2904. if (_backgroundColor !== nil)
  2905. [aCoder encodeObject:_backgroundColor forKey:CPViewBackgroundColorKey];
  2906. if (_hitTests !== YES)
  2907. [aCoder encodeBool:_hitTests forKey:CPViewHitTestsKey];
  2908. if (_opacity !== 1.0)
  2909. [aCoder encodeFloat:_opacity forKey:CPViewOpacityKey];
  2910. if (_isHidden)
  2911. [aCoder encodeBool:_isHidden forKey:CPViewIsHiddenKey];
  2912. if (_toolTip)
  2913. [aCoder encodeObject:_toolTip forKey:CPViewToolTipKey];
  2914. var nextKeyView = [self nextKeyView];
  2915. if (nextKeyView !== nil && ![nextKeyView isEqual:self])
  2916. [aCoder encodeConditionalObject:nextKeyView forKey:CPViewNextKeyViewKey];
  2917. var previousKeyView = [self previousKeyView];
  2918. if (previousKeyView !== nil && ![previousKeyView isEqual:self])
  2919. [aCoder encodeConditionalObject:previousKeyView forKey:CPViewPreviousKeyViewKey];
  2920. [aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
  2921. [aCoder encodeObject:String(_themeState) forKey:CPViewThemeStateKey];
  2922. for (var attributeName in _themeAttributes)
  2923. if (_themeAttributes.hasOwnProperty(attributeName))
  2924. CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
  2925. if (_identifier)
  2926. [aCoder encodeObject:_identifier forKey:CPReuseIdentifierKey];
  2927. [aCoder encodeSize:[self scaleSize] forKey:CPViewScaleKey];
  2928. [aCoder encodeSize:[self _hierarchyScaleSize] forKey:CPViewSizeScaleKey];
  2929. [aCoder encodeBool:_isScaled forKey:CPViewIsScaledKey];
  2930. }
  2931. @end
  2932. var _CPViewFullScreenModeStateMake = function(aView)
  2933. {
  2934. var superview = aView._superview;
  2935. return { autoresizingMask:aView._autoresizingMask, frame:CGRectMakeCopy(aView._frame), index:(superview ? [superview._subviews indexOfObjectIdenticalTo:aView] : 0), superview:superview };
  2936. };
  2937. var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
  2938. {
  2939. var transform = CGAffineTransformMakeIdentity(),
  2940. sameWindow = YES,
  2941. fromWindow = nil,
  2942. toWindow = nil;
  2943. if (fromView)
  2944. {
  2945. var view = fromView;
  2946. // FIXME: This doesn't handle the case when the outside views are equal.
  2947. // If we have a fromView, "climb up" the view tree until
  2948. // we hit the root node or we hit the toLayer.
  2949. while (view && view != toView)
  2950. {
  2951. var frame = view._frame;
  2952. if (view._isScaled)
  2953. {
  2954. var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
  2955. CGAffineTransformConcatTo(transform, affineZoom, transform);
  2956. }
  2957. transform.tx += CGRectGetMinX(frame);
  2958. transform.ty += CGRectGetMinY(frame);
  2959. if (view._boundsTransform)
  2960. {
  2961. var inverseBoundsTransform = CGAffineTransformMakeCopy(view._boundsTransform);
  2962. if (view._isScaled)
  2963. {
  2964. var affineZoom = CGAffineTransformMakeScale(view._scaleSize.width, view._scaleSize.height);
  2965. CGAffineTransformConcatTo(inverseBoundsTransform, affineZoom, inverseBoundsTransform);
  2966. }
  2967. CGAffineTransformConcatTo(transform, inverseBoundsTransform, transform);
  2968. }
  2969. view = view._superview;
  2970. }
  2971. // If we hit toView, then we're done.
  2972. if (view === toView)
  2973. {
  2974. return transform;
  2975. }
  2976. else if (fromView && toView)
  2977. {
  2978. fromWindow = [fromView window];
  2979. toWindow = [toView window];
  2980. if (fromWindow && toWindow && fromWindow !== toWindow)
  2981. sameWindow = NO;
  2982. }
  2983. }
  2984. // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way.
  2985. var view = toView,
  2986. transform2 = CGAffineTransformMakeIdentity();
  2987. while (view && view != fromView)
  2988. {
  2989. var frame = CGRectMakeCopy(view._frame);
  2990. // FIXME : For now we don't care about rotate transform and so on
  2991. if (view._isScaled)
  2992. {
  2993. transform2.a *= 1 / view._scaleSize.width;
  2994. transform2.d *= 1 / view._scaleSize.height;
  2995. }
  2996. transform2.tx += CGRectGetMinX(frame) * transform2.a;
  2997. transform2.ty += CGRectGetMinY(frame) * transform2.d;
  2998. if (view._boundsTransform)
  2999. {
  3000. var inverseBoundsTransform = CGAffineTransformMakeIdentity();
  3001. inverseBoundsTransform.tx -= view._inverseBoundsTransform.tx * transform2.a;
  3002. inverseBoundsTransform.ty -= view._inverseBoundsTransform.ty * transform2.d;
  3003. CGAffineTransformConcatTo(transform2, inverseBoundsTransform, transform2);
  3004. }
  3005. view = view._superview;
  3006. }
  3007. transform2.tx = -transform2.tx;
  3008. transform2.ty = -transform2.ty;
  3009. if (view === fromView)
  3010. {
  3011. // toView is inside of fromView
  3012. return transform2;
  3013. }
  3014. CGAffineTransformConcatTo(transform, transform2, transform);
  3015. return transform;
  3016. /* var views = [],
  3017. view = toView;
  3018. while (view)
  3019. {
  3020. views.push(view);
  3021. view = view._superview;
  3022. }
  3023. var index = views.length;
  3024. while (index--)
  3025. {
  3026. var frame = views[index]._frame;
  3027. transform.tx -= CGRectGetMinX(frame);
  3028. transform.ty -= CGRectGetMinY(frame);
  3029. }*/
  3030. return transform;
  3031. };