API  1.0.0
CPSplitView.j
Go to the documentation of this file.
1 /*
2  * CPSplitView.j
3  * AppKit
4  *
5  * Created by Thomas Robinson.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 #include "../Foundation/Foundation.h"
24 
25 
26 @global CPApp
27 
28 @protocol CPSplitViewDelegate <CPObject>
29 
30 @optional
31 - (BOOL)splitView:(CPSplitView)splitView canCollapseSubview:(CPView)subview;
32 - (BOOL)splitView:(CPSplitView)splitView shouldAdjustSizeOfSubview:(CPView)subview;
33 - (BOOL)splitView:(CPSplitView)splitView shouldCollapseSubview:(CPView)subview forDoubleClickOnDividerAtIndex:(CPInteger)dividerIndex;
34 - (CGRect)splitView:(CPSplitView)splitView additionalEffectiveRectOfDividerAtIndex:(CPInteger)dividerIndex;
35 - (CGRect)splitView:(CPSplitView)splitView effectiveRect:(CGRect)proposedEffectiveRect forDrawnRect:(CGRect)drawnRect ofDividerAtIndex:(CPInteger)dividerIndex;
36 - (float)splitView:(CPSplitView)splitView constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex;
37 - (float)splitView:(CPSplitView)splitView constrainMinCoordinate:(float)proposedMin ofSubviewAt:(CPInteger)dividerIndex;
38 - (float)splitView:(CPSplitView)splitView constrainSplitPosition:(float)proposedPosition ofSubviewAt:(CPInteger)dividerIndex;
39 - (void)splitView:(CPSplitView)splitView resizeSubviewsWithOldSize:(CGSize)oldSize;
40 - (void)splitViewDidResizeSubviews:(CPNotification)aNotification;
41 - (void)splitViewWillResizeSubviews:(CPNotification)aNotification;
42 
43 @end
44 
56 
57 #define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE() \
58  if ((_suppressResizeNotificationsMask & DidPostWillResizeNotification) === 0) \
59  { \
60  [self _postNotificationWillResize]; \
61  _suppressResizeNotificationsMask |= DidPostWillResizeNotification; \
62  }
63 
64 #define SPLIT_VIEW_MAYBE_POST_DID_RESIZE() \
65  if ((_suppressResizeNotificationsMask & ShouldSuppressResizeNotifications) !== 0) \
66  _suppressResizeNotificationsMask |= DidSuppressResizeNotification; \
67  else \
68  [self _postNotificationDidResize];
69 
70 #define SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION() \
71  ((_suppressResizeNotificationsMask & DidSuppressResizeNotification) !== 0)
72 
73 #define SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(shouldSuppress) \
74  if (shouldSuppress) \
75  _suppressResizeNotificationsMask |= ShouldSuppressResizeNotifications; \
76  else \
77  _suppressResizeNotificationsMask = 0;
78 
79 CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
80 CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
81 
85 
97 @implementation CPSplitView : CPView
98 {
99  id <CPSplitViewDelegate> _delegate;
100  BOOL _isVertical;
101  BOOL _isPaneSplitter;
102 
103  int _currentDivider;
104  float _initialOffset;
105  CPDictionary _preCollapsePositions;
106 
107  CPString _originComponent;
108  CPString _sizeComponent;
109 
110  CPArray _DOMDividerElements;
111  CPString _dividerImagePath;
112  int _drawingDivider;
113  BOOL _isTracking;
114 
115  CPString _autosaveName;
116  BOOL _shouldAutosave;
117  CGSize _shouldRestoreFromAutosaveUnlessFrameSize;
118 
119  BOOL _needsResizeSubviews;
120  int _suppressResizeNotificationsMask;
121 
122  CPArray _buttonBars;
123 
124  unsigned _implementedDelegateMethods;
125 }
126 
127 + (CPString)defaultThemeClass
128 {
129  return @"splitview";
130 }
131 
132 + (CPDictionary)themeAttributes
133 {
134  return @{
135  @"divider-thickness": 1.0,
136  @"pane-divider-thickness": 10.0,
137  @"pane-divider-color": [CPColor grayColor],
138  @"horizontal-divider-color": [CPNull null],
139  @"vertical-divider-color": [CPNull null],
140  };
141 }
142 
143 - (id)initWithFrame:(CGRect)aFrame
144 {
145  if (self = [super initWithFrame:aFrame])
146  {
147  _suppressResizeNotificationsMask = 0;
148  _preCollapsePositions = [CPMutableDictionary new];
149  _currentDivider = CPNotFound;
150 
151  _DOMDividerElements = [];
152  _buttonBars = [];
153 
154  _shouldAutosave = YES;
155  _isTracking = NO;
156 
157  [self _setVertical:YES];
158  }
159 
160  return self;
161 }
162 
167 - (float)dividerThickness
168 {
169  return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
170 }
171 
176 - (BOOL)isVertical
177 {
178  return _isVertical;
179 }
180 
185 - (void)setVertical:(BOOL)shouldBeVertical
186 {
187  if (![self _setVertical:shouldBeVertical])
188  return;
189 
190  // Just re-adjust evenly.
191  var frame = [self frame],
192  dividerThickness = [self dividerThickness];
193 
194  [self _postNotificationWillResize];
195 
196  var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
197  index = 0,
198  count = _subviews.length;
199 
200  if ([self isVertical])
201  {
202  for (; index < count; ++index)
203  [_subviews[index] setFrame:CGRectMake(ROUND((eachSize + dividerThickness) * index), 0, eachSize, frame.size.height)];
204  }
205  else
206  {
207  for (; index < count; ++index)
208  [_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
209  }
210 
211  if (_DOMDividerElements[_drawingDivider])
212  [self _setupDOMDivider];
213 
214  [self setNeedsDisplay:YES];
215  [self _postNotificationDidResize];
216 
217 }
218 
219 - (BOOL)_setVertical:(BOOL)shouldBeVertical
220 {
221  var changed = (_isVertical != shouldBeVertical);
222 
223  _isVertical = shouldBeVertical;
224 
225  _originComponent = [self isVertical] ? "x" : "y";
226  _sizeComponent = [self isVertical] ? "width" : "height";
227  _dividerImagePath = [self isVertical] ? [[self valueForThemeAttribute:@"vertical-divider-color"] filename] : [[self valueForThemeAttribute:@"horizontal-divider-color"] filename];
228 
229  return changed;
230 }
231 
237 - (BOOL)isPaneSplitter
238 {
239  return _isPaneSplitter;
240 }
241 
247 - (void)setIsPaneSplitter:(BOOL)shouldBePaneSplitter
248 {
249  if (_isPaneSplitter == shouldBePaneSplitter)
250  return;
251 
252  _isPaneSplitter = shouldBePaneSplitter;
253 
254  if (_DOMDividerElements[_drawingDivider])
255  [self _setupDOMDivider];
256 
257  // The divider changes size when pane splitter mode is toggled, so the
258  // subviews need to change size too.
259  _needsResizeSubviews = YES;
260  [self setNeedsDisplay:YES];
261 }
262 
263 - (void)didAddSubview:(CPView)aSubview
264 {
265  _needsResizeSubviews = YES;
266 }
267 
273 - (BOOL)isSubviewCollapsed:(CPView)subview
274 {
275  return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
276 }
277 
284 - (CGRect)rectOfDividerAtIndex:(int)aDivider
285 {
286  var frame = [_subviews[aDivider] frame],
287  rect = CGRectMakeZero();
288 
289  rect.size = [self frame].size;
290  rect.size[_sizeComponent] = [self dividerThickness];
291  rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
292 
293  return rect;
294 }
295 
302 - (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
303 {
304  var realRect = [self rectOfDividerAtIndex:aDivider],
305  padding = 2;
306 
307  realRect.size[_sizeComponent] += padding * 2;
308  realRect.origin[_originComponent] -= padding;
309 
310  return realRect;
311 }
312 
313 - (void)drawRect:(CGRect)rect
314 {
315  var count = [_subviews count] - 1;
316 
317  while ((count--) > 0)
318  {
319  _drawingDivider = count;
320  [self drawDividerInRect:[self rectOfDividerAtIndex:count]];
321  }
322 }
323 
329 - (void)willRemoveSubview:(CPView)aView
330 {
331 #if PLATFORM(DOM)
332  var dividerToRemove = _DOMDividerElements.pop();
333 
334  // The divider may not exist if we never rendered out the DOM.
335  if (dividerToRemove)
336  CPDOMDisplayServerRemoveChild(_DOMElement, dividerToRemove);
337 #endif
338 
339  _needsResizeSubviews = YES;
340  [self setNeedsLayout];
341  [self setNeedsDisplay:YES];
342 }
343 
344 - (void)layoutSubviews
345 {
346  [self _adjustSubviewsWithCalculatedSize]
347 }
348 
353 - (void)drawDividerInRect:(CGRect)aRect
354 {
355 #if PLATFORM(DOM)
356  if (!_DOMDividerElements[_drawingDivider])
357  {
358  _DOMDividerElements[_drawingDivider] = document.createElement("div");
359 
360  _DOMDividerElements[_drawingDivider].style.position = "absolute";
361  _DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
362 
363  CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
364  [self _setupDOMDivider];
365  }
366 
367  CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
368  CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], CGRectGetWidth(aRect), CGRectGetHeight(aRect));
369 #endif
370 }
371 
372 - (void)_setupDOMDivider
373 {
374  if (_isPaneSplitter)
375  {
376  _DOMDividerElements[_drawingDivider].style.backgroundColor = "";
377  _DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
378  }
379  else
380  {
381  _DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
382  _DOMDividerElements[_drawingDivider].style.backgroundImage = "";
383  }
384 }
385 
386 - (void)viewWillDraw
387 {
388  [self _adjustSubviewsWithCalculatedSize];
389 }
390 
391 - (void)_adjustSubviewsWithCalculatedSize
392 {
393  if (!_needsResizeSubviews)
394  return;
395 
396  _needsResizeSubviews = NO;
397 
398  [self resizeSubviewsWithOldSize:[self _calculateSize]];
399 }
400 
401 - (CGSize)_calculateSize
402 {
403  var subviews = [self subviews],
404  count = subviews.length,
405  size = CGSizeMakeZero();
406 
407  if ([self isVertical])
408  {
409  size.width += [self dividerThickness] * (count - 1);
410  size.height = CGRectGetHeight([self frame]);
411  }
412  else
413  {
414  size.width = CGRectGetWidth([self frame]);
415  size.height += [self dividerThickness] * (count - 1);
416  }
417 
418  while (count--)
419  size[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
420 
421  return size;
422 }
423 
424 - (BOOL)cursorAtPoint:(CGPoint)aPoint hitDividerAtIndex:(int)anIndex
425 {
426  var frame = [_subviews[anIndex] frame],
427  startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
428  effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
429  buttonBar = _buttonBars[anIndex],
430  buttonBarRect = null,
431  additionalRect = null;
432 
433  if (buttonBar != null)
434  {
435  buttonBarRect = [buttonBar resizeControlFrame];
436  buttonBarRect.origin = [self convertPoint:buttonBarRect.origin fromView:buttonBar];
437  }
438 
439  effectiveRect = [self _sendDelegateSplitViewEffectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
440  additionalRect = [self _sendDelegateSplitViewAdditionalEffectiveRectOfDividerAtIndex:anIndex];
441 
442  return CGRectContainsPoint(effectiveRect, aPoint) ||
443  (additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
444  (buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
445 }
446 
447 - (CPView)hitTest:(CGPoint)aPoint
448 {
449  if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
450  return nil;
451 
452  var point = [self convertPoint:aPoint fromView:[self superview]],
453  dividerIndex = [self _dividerAtPoint:point];
454 
455  if (dividerIndex !== CPNotFound)
456  return self;
457 
458  return [super hitTest:aPoint];
459 }
460 
461 - (CPInteger)_dividerAtPoint:(CGPoint)aPoint
462 {
463  var count = [_subviews count] - 1;
464 
465  for (var i = 0; i < count; i++)
466  {
467  if ([self cursorAtPoint:aPoint hitDividerAtIndex:i])
468  return i;
469  }
470 
471  return CPNotFound;
472 }
473 /*
474  Tracks the divider.
475  @param anEvent the input event
476 */
477 - (void)trackDivider:(CPEvent)anEvent
478 {
479  var type = [anEvent type];
480 
481  if (type == CPLeftMouseUp)
482  {
483  if ([anEvent clickCount] == 2 || (_isTracking && _currentDivider != CPNotFound))
484  {
485  // We disabled autosaving during tracking.
486  _shouldAutosave = YES;
487  _currentDivider = CPNotFound;
488  _isTracking = NO;
489 
490  [self _autosave];
491  [self _updateResizeCursor:anEvent];
492  }
493 
494  return;
495  }
496 
497  if (type == CPLeftMouseDown)
498  {
499  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
500  _currentDivider = [self _dividerAtPoint:point];
501 
502  var frame = [_subviews[_currentDivider] frame],
503  startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
504 
505  if ([anEvent clickCount] == 2 &&
506  [self _delegateRespondsToSplitViewCanCollapseSubview] &&
507  [self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
508  {
509  var minPosition = [self minPossiblePositionOfDividerAtIndex:_currentDivider],
510  maxPosition = [self maxPossiblePositionOfDividerAtIndex:_currentDivider],
511  preCollapsePosition = [_preCollapsePositions objectForKey:"" + _currentDivider] || 0;
512 
513  if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider] forDoubleClickOnDividerAtIndex:_currentDivider])
514  {
515  if ([self isSubviewCollapsed:_subviews[_currentDivider]])
516  [self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
517  else
518  [self setPosition:minPosition ofDividerAtIndex:_currentDivider];
519  }
520  else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider + 1] forDoubleClickOnDividerAtIndex:_currentDivider])
521  {
522  if ([self isSubviewCollapsed:_subviews[_currentDivider + 1]])
523  [self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
524  else
525  [self setPosition:maxPosition ofDividerAtIndex:_currentDivider];
526  }
527  }
528  else
529  {
530  _initialOffset = startPosition - point[_originComponent];
531  // Don't autosave during a resize. We'll wait until it's done.
532  _shouldAutosave = NO;
533  [self _postNotificationWillResize];
534  }
535 
536  }
537  else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
538  {
539  if (!_isTracking)
540  {
541  // Don't autosave during a resize. We'll wait until it's done.
542  _shouldAutosave = NO;
543  [self _postNotificationWillResize];
544 
545  _isTracking = YES;
546  }
547 
548  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
549 
550  [self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
551  // Cursor might change if we reach a resize limit.
552  [self _updateResizeCursor:anEvent];
553  }
554 
555  [CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
556 }
557 
558 - (void)mouseDown:(CPEvent)anEvent
559 {
560  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
561  dividerIndex = [self _dividerAtPoint:point];
562 
563  if (dividerIndex !== CPNotFound)
564  [self trackDivider:anEvent];
565 }
566 
567 - (void)viewDidMoveToWindow
568 {
569  // Enable split view resize cursors. Commented out pending CPTrackingArea implementation.
570  //[[self window] setAcceptsMouseMovedEvents:YES];
571 }
572 
573 - (void)_updateResizeCursor:(CPEvent)anEvent
574 {
575  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
576 
577  if ([anEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents])
578  {
580  return;
581  }
582 
583  for (var i = 0, count = [_subviews count] - 1; i < count; i++)
584  {
585  // If we are currently tracking, keep the resize cursor active even outside of hit areas.
586  if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
587  {
588  var frameA = [_subviews[i] frame],
589  sizeA = frameA.size[_sizeComponent],
590  startPosition = frameA.origin[_originComponent] + sizeA,
591  frameB = [_subviews[i + 1] frame],
592  sizeB = frameB.size[_sizeComponent],
593  canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
594  canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
595  cursor = [CPCursor arrowCursor];
596 
597  if (sizeA === 0)
598  canGrow = YES; // Subview is collapsed.
599  else if (!canShrink && [self _sendDelegateSplitViewCanCollapseSubview:_subviews[i]])
600  canShrink = YES; // Subview is collapsible.
601 
602  if (sizeB === 0)
603  {
604  // Right/lower subview is collapsed.
605  canGrow = NO;
606  // It's safe to assume it can always be uncollapsed.
607  canShrink = YES;
608  }
609  else if (!canGrow && [self _sendDelegateSplitViewCanCollapseSubview:_subviews[i + 1]])
610  {
611  canGrow = YES; // Right/lower subview is collapsible.
612  }
613 
614  if (_isVertical && canShrink && canGrow)
615  cursor = [CPCursor resizeLeftRightCursor];
616  else if (_isVertical && canShrink)
617  cursor = [CPCursor resizeLeftCursor];
618  else if (_isVertical && canGrow)
619  cursor = [CPCursor resizeRightCursor];
620  else if (canShrink && canGrow)
621  cursor = [CPCursor resizeUpDownCursor];
622  else if (canShrink)
623  cursor = [CPCursor resizeUpCursor];
624  else if (canGrow)
625  cursor = [CPCursor resizeDownCursor];
626 
627  [cursor set];
628  return;
629  }
630  }
631 
633 }
634 
640 - (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
641 {
642  var frame = [_subviews[dividerIndex + 1] frame];
643 
644  if (dividerIndex + 1 < [_subviews count] - 1)
645  return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
646  else
647  return [self frame].size[_sizeComponent] - [self dividerThickness];
648 }
649 
655 - (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
656 {
657  if (dividerIndex > 0)
658  {
659  var frame = [_subviews[dividerIndex - 1] frame];
660 
661  return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
662  }
663  else
664  return 0;
665 }
666 
667 - (int)_realPositionForPosition:(float)position ofDividerAtIndex:(int)dividerIndex
668 {
669  // not sure where this should override other positions?
670  var proposedPosition = [self _sendDelegateSplitViewConstrainSplitPosition:position ofSubviewAt:dividerIndex];
671 
672  // Silently ignore bad positions which could result from odd delegate responses. We don't want these
673  // bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
674  // its subviews.
675  if (_IS_NUMERIC(proposedPosition))
676  position = proposedPosition;
677 
678  var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
679  proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
680  actualMax = proposedMax,
681  actualMin = proposedMin,
682  proposedActualMin = [self _sendDelegateSplitViewConstrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex],
683  proposedActualMax = [self _sendDelegateSplitViewConstrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
684 
685  if (_IS_NUMERIC(proposedActualMin))
686  actualMin = proposedActualMin;
687 
688  if (_IS_NUMERIC(proposedActualMax))
689  actualMax = proposedActualMax;
690 
691  var viewA = _subviews[dividerIndex],
692  viewB = _subviews[dividerIndex + 1],
693  realPosition = MAX(MIN(position, actualMax), actualMin);
694 
695  // Is this position past the halfway point to collapse?
696  if ((position < proposedMin + (actualMin - proposedMin) / 2) && [self _sendDelegateSplitViewCanCollapseSubview:viewA])
697  realPosition = proposedMin;
698 
699  // We can also collapse to the right.
700  if ((position > proposedMax - (proposedMax - actualMax) / 2) && [self _sendDelegateSplitViewCanCollapseSubview:viewB])
701  realPosition = proposedMax;
702 
703  return realPosition;
704 }
705 
711 - (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
712 {
713  // Any manual changes to the divider position should override anything we are restoring from
714  // autosave.
715  _shouldRestoreFromAutosaveUnlessFrameSize = nil;
716 
718  [self _adjustSubviewsWithCalculatedSize];
719 
720  var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex],
721  viewA = _subviews[dividerIndex],
722  frameA = [viewA frame],
723  viewB = _subviews[dividerIndex + 1],
724  frameB = [viewB frame],
725  preCollapsePosition = 0,
726  preSize = frameA.size[_sizeComponent];
727 
728  frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
729 
730  if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
731  preCollapsePosition = preSize;
732 
733  if (preSize !== frameA.size[_sizeComponent])
734  {
736  [_subviews[dividerIndex] setFrame:frameA];
738  }
739 
740  preSize = frameB.size[_sizeComponent];
741 
742  var preOrigin = frameB.origin[_originComponent];
743  frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
744 
745  if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
746  preCollapsePosition = frameB.origin[_originComponent];
747 
748  frameB.origin[_originComponent] = realPosition + [self dividerThickness];
749 
750  if (preSize !== frameB.size[_sizeComponent] || preOrigin !== frameB.origin[_originComponent])
751  {
753  [_subviews[dividerIndex + 1] setFrame:frameB];
755  }
756 
757  if (preCollapsePosition)
758  [_preCollapsePositions setObject:preCollapsePosition forKey:"" + dividerIndex];
759 
760  [self setNeedsDisplay:YES];
761 
763  [self _postNotificationDidResize];
764 
766 }
767 
768 - (void)setFrameSize:(CGSize)aSize
769 {
770  if (_shouldRestoreFromAutosaveUnlessFrameSize)
771  _shouldAutosave = NO;
772  else
773  [self _adjustSubviewsWithCalculatedSize];
774 
775  [super setFrameSize:aSize];
776 
777  if (_shouldRestoreFromAutosaveUnlessFrameSize)
778  _shouldAutosave = YES;
779 
780  [self setNeedsDisplay:YES];
781 }
782 
783 - (void)resizeSubviewsWithOldSize:(CGSize)oldSize
784 {
785  if ([self _delegateRespondsToSplitViewResizeSubviewsWithOldSize])
786  {
787  [self _sendDelegateSplitViewResizeSubviewsWithOldSize:oldSize];
788  return;
789  }
790 
791  [self adjustSubviews];
792 }
793 
794 - (void)adjustSubviews
795 {
796  var count = [_subviews count];
797 
798  if (!count)
799  return;
800 
802  [self _postNotificationWillResize];
803 
804  var index = 0,
805  bounds = [self bounds],
806  boundsSize = bounds.size[_sizeComponent],
807  oldSize = [self _calculateSize],
808  dividerThickness = [self dividerThickness],
809  totalDividers = count - 1,
810  oldFlexibleSpace = 0,
811  totalSizablePanes = 0,
812  isSizableMap = {},
813  viewSizes = [];
814 
815  // What we want to do is to preserve non resizable sizes first, and then to preserve the ratio of size to available
816  // non fixed space for every other subview. E.g. assume fixed space was 20 pixels initially, view 1 was 20 and
817  // view 2 was 30 pixels, for a total of 70 pixels. Then the new total size becomes 140 pixels. Now we want the fixed
818  // space to still be 20 pixels, view 1 to be 48 pixels and view 2 to be 72 pixels. This way the relative size of
819  // view 1 to view 2 remains the same - view 1 was 66% of view 2 initially and after the resize view 1 is still
820  // 66% of view 2's size.
821  //
822  // For this calculation, we can consider the dividers themselves to also be fixed size areas - they should remain
823  // the same size before and after.
824 
825  // How much flexible size do we have in pre-resize pixels?
826  for (index = 0; index < count; ++index)
827  {
828  var view = _subviews[index],
829  isSizable = [self _sendDelegateSplitViewShouldAdjustSizeOfSubview:view],
830  size = [view frame].size[_sizeComponent];
831 
832  isSizableMap[index] = isSizable;
833  viewSizes.push(size);
834 
835  if (isSizable)
836  {
837  oldFlexibleSpace += size;
838  totalSizablePanes++;
839  }
840  }
841 
842  // nonSizableSpace is the number of fixed pixels in pre-resize terms and the desired number post-resize.
843  var nonSizableSpace = oldSize[_sizeComponent] - oldFlexibleSpace,
844  newFlexibleSpace = boundsSize - nonSizableSpace,
845  remainingFixedPixelsToRemove = 0;
846 
847  if (newFlexibleSpace < 0)
848  {
849  remainingFixedPixelsToRemove = -newFlexibleSpace;
850  newFlexibleSpace = 0;
851  }
852 
853  var remainingFixedPanes = count - totalSizablePanes;
854 
855  for (index = 0; index < count; ++index)
856  {
857  var view = _subviews[index],
858  viewFrame = CGRectMakeCopy(bounds),
859  isSizable = isSizableMap[index],
860  targetSize = 0;
861 
862  // The last area must take up exactly the remaining space, fixed or not.
863  if (index + 1 === count)
864  targetSize = boundsSize - viewFrame.origin[_originComponent];
865  // Try to keep fixed size areas the same size.
866  else if (!isSizable)
867  {
868  var removedFixedPixels = MIN(remainingFixedPixelsToRemove / remainingFixedPanes, viewSizes[index]);
869  targetSize = viewSizes[index] - removedFixedPixels;
870  remainingFixedPixelsToRemove -= removedFixedPixels;
871  remainingFixedPanes--;
872  }
873  // (new size / flexible size available) == (old size / old flexible size available)
874  else if (oldFlexibleSpace > 0)
875  targetSize = newFlexibleSpace * viewSizes[index] / oldFlexibleSpace;
876  // oldFlexibleSpace <= 0 so all flexible areas were crushed. When we get space, allocate it evenly.
877  // totalSizablePanes cannot be 0 since isSizable.
878  else
879  targetSize = newFlexibleSpace / totalSizablePanes;
880 
881  targetSize = MAX(0, ROUND(targetSize));
882  viewFrame.size[_sizeComponent] = targetSize;
883  [view setFrame:viewFrame];
884  bounds.origin[_originComponent] += targetSize + dividerThickness;
885  }
886 
888 }
889 
955 - (void)setDelegate:(id <CPSplitViewDelegate>)aDelegate
956 {
957  if (_delegate === aDelegate)
958  return;
959 
960  _delegate = aDelegate;
961  _implementedDelegateMethods = 0;
962 
963  if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
964  _implementedDelegateMethods |= CPSplitViewDelegate_splitViewWillResizeSubviews_;
965 
966  if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
967  _implementedDelegateMethods |= CPSplitViewDelegate_splitViewDidResizeSubviews_;
968 
969  if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
970  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_canCollapseSubview_;
971 
972  if ([_delegate respondsToSelector:@selector(splitView:shouldAdjustSizeOfSubview:)])
973  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_;
974 
975  if ([_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
977 
978  if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
980 
981  if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
983 
984  if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
986 
987  if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
989 
990  if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
992 
993  if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
994  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_;
995 
996 }
997 
1013 // FIXME Should be renamed to setButtonBar:ofDividerAtIndex:.
1014 - (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(CPUInteger)dividerIndex
1015 {
1016  if (!aButtonBar)
1017  {
1018  _buttonBars[dividerIndex] = nil;
1019  return;
1020  }
1021 
1022  var view = [aButtonBar superview],
1023  subview = aButtonBar;
1024 
1025  while (view && view !== self)
1026  {
1027  subview = view;
1028  view = [view superview];
1029  }
1030 
1031  if (view !== self)
1032  [CPException raise:CPInvalidArgumentException
1033  reason:@"CPSplitView button bar must be a subview of the split view."];
1034 
1035  var viewIndex = [[self subviews] indexOfObject:subview];
1036 
1037  [aButtonBar setHasResizeControl:YES];
1038  [aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
1039 
1040  _buttonBars[dividerIndex] = aButtonBar;
1041 }
1042 
1043 - (void)_postNotificationWillResize
1044 {
1045  [self _sendDelegateSplitViewWillResizeSubviews];
1046 }
1047 
1048 - (void)_postNotificationDidResize
1049 {
1050  [self _sendDelegateSplitViewDidResizeSubviews];
1051 
1052  // TODO Cocoa always autosaves on "viewDidEndLiveResize". If Cappuccino adds support for this we
1053  // should do the same.
1054  [self _autosave];
1055 }
1056 
1062 - (void)setAutosaveName:(CPString)autosaveName
1063 {
1064  if (_autosaveName == autosaveName)
1065  return;
1066 
1067  _autosaveName = autosaveName;
1068 }
1069 
1075 - (CPString)autosaveName
1076 {
1077  return _autosaveName;
1078 }
1079 
1083 - (void)_autosave
1084 {
1085  if (_shouldRestoreFromAutosaveUnlessFrameSize || !_shouldAutosave || !_autosaveName)
1086  return;
1087 
1088  var userDefaults = [CPUserDefaults standardUserDefaults],
1089  autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
1090  autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
1091  count = [_subviews count],
1092  positions = [CPMutableArray new],
1093  preCollapseArray = [CPMutableArray new];
1094 
1095  for (var i = 0; i < count; i++)
1096  {
1097  var frame = [_subviews[i] frame];
1098  [positions addObject:CGStringFromRect(frame)];
1099  [preCollapseArray addObject:[_preCollapsePositions objectForKey:"" + i]];
1100  }
1101 
1102  [userDefaults setObject:positions forKey:autosaveName];
1103  [userDefaults setObject:preCollapseArray forKey:autosavePrecollapseName];
1104 }
1105 
1112 - (void)_restoreFromAutosaveIfNeeded
1113 {
1114  if (_shouldRestoreFromAutosaveUnlessFrameSize && !CGSizeEqualToSize([self frameSize], _shouldRestoreFromAutosaveUnlessFrameSize))
1115  {
1116  [self _restoreFromAutosave];
1117  }
1118 
1119  _shouldRestoreFromAutosaveUnlessFrameSize = nil;
1120 }
1121 
1125 - (void)_restoreFromAutosave
1126 {
1127  if (!_autosaveName)
1128  return;
1129 
1130  var autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
1131  autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
1132  userDefaults = [CPUserDefaults standardUserDefaults],
1133  frames = [userDefaults objectForKey:autosaveName],
1134  preCollapseArray = [userDefaults objectForKey:autosavePrecollapseName];
1135 
1136  if (frames)
1137  {
1138  var dividerThickness = [self dividerThickness],
1139  position = 0;
1140 
1141  _shouldAutosave = NO;
1142 
1143  for (var i = 0, count = [frames count] - 1; i < count; i++)
1144  {
1145  var frame = CGRectFromString(frames[i]);
1146  position += frame.size[_sizeComponent];
1147 
1148  [self setPosition:position ofDividerAtIndex:i];
1149 
1150  position += dividerThickness;
1151  }
1152 
1153  _shouldAutosave = YES;
1154  }
1155 
1156  if (preCollapseArray)
1157  {
1158  _preCollapsePositions = [CPMutableDictionary new];
1159 
1160  for (var i = 0, count = [preCollapseArray count]; i < count; i++)
1161  {
1162  var item = preCollapseArray[i];
1163 
1164  if (item === nil)
1165  [_preCollapsePositions removeObjectForKey:String(i)];
1166  else
1167  [_preCollapsePositions setObject:item forKey:String(i)];
1168  }
1169  }
1170 }
1171 
1175 - (CPString)_framesKeyForAutosaveName:(CPString)theAutosaveName
1176 {
1177  if (!theAutosaveName)
1178  return nil;
1179 
1180  return @"CPSplitView Subview Frames " + theAutosaveName;
1181 }
1182 
1186 - (CPString)_precollapseKeyForAutosaveName:(CPString)theAutosaveName
1187 {
1188  if (!theAutosaveName)
1189  return nil;
1190 
1191  return @"CPSplitView Subview Precollapse Positions " + theAutosaveName;
1192 }
1193 
1194 @end
1195 
1197 {
1198  CPMutableArray _splitViewTrackingAreas;
1199 }
1200 
1201 - (void)updateTrackingAreas
1202 {
1203  if (_splitViewTrackingAreas)
1204  {
1205  for (var i = 0, count = [_splitViewTrackingAreas count]; i < count; i++)
1206  [self removeTrackingArea:_splitViewTrackingAreas[i]];
1207 
1208  _splitViewTrackingAreas = nil;
1209  }
1210 
1212 
1213  _splitViewTrackingAreas = @[];
1214 
1215  for (var i = 0; i < _subviews.length - 1; i++)
1216  {
1217  [_splitViewTrackingAreas addObject:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
1218  options:options
1219  owner:self
1220  userInfo:nil]];
1221 
1222  [self addTrackingArea:_splitViewTrackingAreas[i]];
1223  }
1224 
1225  [super updateTrackingAreas];
1226 }
1227 
1228 - (void)cursorUpdate:(CPEvent)anEvent
1229 {
1230  if (_currentDivider === CPNotFound)
1231  [self _updateResizeCursor:anEvent];
1232 }
1233 
1234 @end
1235 
1236 
1238 
1243 - (BOOL)_delegateRespondsToSplitViewResizeSubviewsWithOldSize
1244 {
1245  return _implementedDelegateMethods & CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_;
1246 }
1247 
1252 - (BOOL)_delegateRespondsToSplitViewCanCollapseSubview
1253 {
1254  return _implementedDelegateMethods & CPSplitViewDelegate_splitView_canCollapseSubview_;
1255 }
1256 
1261 - (BOOL)_delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex
1262 {
1264 }
1265 
1266 
1271 - (BOOL)_sendDelegateSplitViewCanCollapseSubview:(CPView)aView
1272 {
1273  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_canCollapseSubview_))
1274  return NO;
1275 
1276  return [_delegate splitView:self canCollapseSubview:aView];
1277 }
1278 
1283 - (BOOL)_sendDelegateSplitViewShouldAdjustSizeOfSubview:(CPView)aView
1284 {
1285  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_))
1286  return YES;
1287 
1288  return [_delegate splitView:self shouldAdjustSizeOfSubview:aView];
1289 }
1290 
1295 - (BOOL)_sendDelegateSplitViewShouldCollapseSubview:(CPView)aView forDoubleClickOnDividerAtIndex:(int)anIndex
1296 {
1298  return NO;
1299 
1300  return [_delegate splitView:self shouldCollapseSubview:aView forDoubleClickOnDividerAtIndex:anIndex];
1301 }
1302 
1307 - (CGRect)_sendDelegateSplitViewAdditionalEffectiveRectOfDividerAtIndex:(int)anIndex
1308 {
1310  return nil;
1311 
1312  return [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
1313 }
1314 
1319 - (CGRect)_sendDelegateSplitViewEffectiveRect:(CGRect)proposedEffectiveRect forDrawnRect:(CGRect)drawnRect ofDividerAtIndex:(CPInteger)dividerIndex
1320 {
1322  return proposedEffectiveRect;
1323 
1324  return [_delegate splitView:self effectiveRect:proposedEffectiveRect forDrawnRect:drawnRect ofDividerAtIndex:dividerIndex];
1325 }
1326 
1331 - (float)_sendDelegateSplitViewConstrainMaxCoordinate:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex
1332 {
1333  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_))
1334  return nil;
1335 
1336  return [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
1337 }
1338 
1343 - (float)_sendDelegateSplitViewConstrainMinCoordinate:(float)proposedMin ofSubviewAt:(CPInteger)dividerIndex
1344 {
1345  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_))
1346  return nil;
1347 
1348  return [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
1349 }
1350 
1355 - (float)_sendDelegateSplitViewConstrainSplitPosition:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex
1356 {
1357  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_))
1358  return nil;
1359 
1360  return [_delegate splitView:self constrainSplitPosition:proposedMax ofSubviewAt:dividerIndex];
1361 }
1362 
1367 - (void)_sendDelegateSplitViewResizeSubviewsWithOldSize:(CGSize)oldSize
1368 {
1369  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_))
1370  return;
1371 
1372  [_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
1373 }
1374 
1379 - (void)_sendDelegateSplitViewWillResizeSubviews
1380 {
1381  var userInfo = nil;
1382 
1383  if (_currentDivider !== CPNotFound)
1384  userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
1385 
1386  if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewWillResizeSubviews_)
1387  [_delegate splitViewWillResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo]];
1388 
1389  [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo];
1390 }
1391 
1396 - (void)_sendDelegateSplitViewDidResizeSubviews
1397 {
1398  var userInfo = nil;
1399 
1400  if (_currentDivider !== CPNotFound)
1401  userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
1402 
1403  if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewDidResizeSubviews_)
1404  [_delegate splitViewDidResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo]];
1405 
1406  [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo];
1407 
1408  [self updateTrackingAreas];
1409 }
1410 
1411 @end
1412 
1413 
1414 var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
1415  CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
1416  CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
1417  CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey",
1418  CPSplitViewAutosaveNameKey = "CPSplitViewAutosaveNameKey";
1419 
1420 @implementation CPSplitView (CPCoding)
1421 
1422 /*
1423  Initializes the split view by unarchiving data from \c aCoder.
1424  @param aCoder the coder containing the archived CPSplitView.
1425 */
1426 - (id)initWithCoder:(CPCoder)aCoder
1427 {
1428  // We need to restore this property before calling super's initWithCoder:.
1429  _autosaveName = [aCoder decodeObjectForKey:CPSplitViewAutosaveNameKey];
1430 
1431  /*
1432 
1433  It is common for the main window of a Cappuccino app window to be resized to match the browser
1434  window size at the end of the UI being loaded from a cib. But at decoding time (now) whatever
1435  window size was originally saved will be in place, so if we try to restore the autosaved divider
1436  positions now they might be constrained to the wrong positions due to the difference in frame size,
1437  and in addition they might move later when the window is resized.
1438 
1439  The workaround is to restore the position once now (so it's approximately correct during loading),
1440  and then once more in the next runloop cycle when any `setFullPlatformWindow` calls are done.
1441 
1442  (However if the frame size doesn't change before the next cycle, we should not restore the position
1443  again because that would overwrite any changes the app developer might have made in user code.)
1444 
1445  The other consideration is that any parent split views need to be restored before any child
1446  subviews, otherwise the parent restore will also change the positioning of the child.
1447 
1448  */
1449  if (_autosaveName)
1450  {
1451  // Schedule /before/ [super initWithCoder:]. This way this instance's _restoreFromAutosaveIfNeeded
1452  // will happen before that of any subviews loaded by [super initWithCoder:].
1453  [[CPRunLoop currentRunLoop] performSelector:@selector(_restoreFromAutosaveIfNeeded) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
1454  }
1455 
1456  self = [super initWithCoder:aCoder];
1457 
1458  if (self)
1459  {
1460  _suppressResizeNotificationsMask = 0;
1461  _preCollapsePositions = [CPMutableDictionary new];
1462 
1463  _currentDivider = CPNotFound;
1464  _shouldAutosave = YES;
1465  _isTracking = NO;
1466 
1467  _DOMDividerElements = [];
1468 
1469  _buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
1470 
1471  [self setDelegate:[aCoder decodeObjectForKey:CPSplitViewDelegateKey]];
1472 
1473  _isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
1474  [self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
1475 
1476  if (_autosaveName)
1477  {
1478  [self _restoreFromAutosave];
1479  // Remember the frame size we had at this point so that we can restore again if it changes
1480  // before the next runloop cycle. See above notes.
1481  _shouldRestoreFromAutosaveUnlessFrameSize = [self frameSize];
1482  }
1483  }
1484 
1485  return self;
1486 }
1487 
1488 /*
1489  Archives this split view into the provided coder.
1490  @param aCoder the coder to which the button's instance data will be written.
1491 */
1492 - (void)encodeWithCoder:(CPCoder)aCoder
1493 {
1494  [super encodeWithCoder:aCoder];
1495 
1496  //FIXME how should we handle this?
1497  //[aCoder encodeObject:_buttonBars forKey:CPSplitViewButtonBarsKey];
1498 
1499  [aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
1500 
1501  [aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
1502  [aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
1503 
1504  [aCoder encodeObject:_autosaveName forKey:CPSplitViewAutosaveNameKey];
1505 }
1506 
1507 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
BOOL isHidden()
Definition: CALayer.j:597
CGRect frame
#define SPLIT_VIEW_MAYBE_POST_DID_RESIZE()
Definition: CPSplitView.j:64
CGPoint position()
Definition: CALayer.j:225
void postNotificationName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
An object representation of nil.
Definition: CPNull.h:2
id initWithCoder:(CPCoder aCoder)
Definition: CPView.j:3696
CPCursor arrowCursor()
Definition: CPCursor.j:246
var CPSplitViewDelegate_splitViewWillResizeSubviews_
Definition: CPSplitView.j:55
The main run loop for the application.
Definition: CPRunLoop.h:2
var DidSuppressResizeNotification
Definition: CPSplitView.j:84
void performSelector:target:argument:order:modes:(SEL aSelector, [target] id aTarget, [argument] id anArgument, [order] int anOrder, [modes] CPArray modes)
Definition: CPRunLoop.j:253
CGPoint locationInWindow()
Definition: CPEvent.j:290
CGRect bounds()
Definition: CPView.j:1326
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
CPEventType type()
Definition: CPEvent.j:325
CPTrackingActiveInKeyWindow
float dividerThickness()
Definition: CPSplitView.j:167
CPView hitTest:(CGPoint aPoint)
Definition: CPView.j:1857
CGRect effectiveRectOfDividerAtIndex:(int aDivider)
Definition: CPSplitView.j:302
CPColor grayColor()
Definition: CPColor.j:317
CPNotificationCenter defaultCenter()
var CPSplitViewDelegate_splitViewDidResizeSubviews_
Definition: CPSplitView.j:54
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
CGRect bounds()
Definition: CALayer.j:203
var DidPostWillResizeNotification
Definition: CPSplitView.j:83
var CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_
Definition: CPSplitView.j:51
CGSize frameSize()
Definition: CPView.j:1056
void setResizeControlIsLeftAligned:(BOOL shouldBeLeftAligned)
Definition: CPButtonBar.j:171
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
var CPSplitViewDelegate_splitView_additionalEffectiveRectOfDividerAtIndex_
Definition: CPSplitView.j:48
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2249
var CPSplitViewIsVerticalKey
Definition: CPSplitView.j:1415
var CPSplitViewDelegate_splitView_effectiveRect_forDrawnRect_ofDividerAtIndex_
Definition: CPSplitView.j:49
var CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_
Definition: CPSplitView.j:46
var CPSplitViewAutosaveNameKey
Definition: CPSplitView.j:1418
var CPSplitViewDelegateKey
Definition: CPSplitView.j:1414
var CPSplitViewIsPaneSplitterKey
Definition: CPSplitView.j:1416
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3806
float maxPossiblePositionOfDividerAtIndex:(int dividerIndex)
Definition: CPSplitView.j:640
#define SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(shouldSuppress)
Definition: CPSplitView.j:73
CPLeftMouseUp
CGRect rectOfDividerAtIndex:(int aDivider)
Definition: CPSplitView.j:284
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPArray subviews()
Definition: CPView.j:519
CPSplitViewDidResizeSubviewsNotification
Definition: CPSplitView.j:79
void setNeedsLayout()
Definition: CPView.j:2748
CPSplitViewWillResizeSubviewsNotification
Definition: CPSplitView.j:80
var CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_
Definition: CPSplitView.j:53
void setDelegate:(id< CPSplitViewDelegate > aDelegate)
Definition: CPSplitView.j:955
void setHasResizeControl:(BOOL shouldHaveResizeControl)
Definition: CPButtonBar.j:157
void setPosition:ofDividerAtIndex:(float position, [ofDividerAtIndex] int dividerIndex)
Definition: CPSplitView.j:711
var ShouldSuppressResizeNotifications
Definition: CPSplitView.j:82
CPLeftMouseDragged
var CPSplitViewButtonBarsKey
Definition: CPSplitView.j:1417
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPNotFound
Definition: CPObjJRuntime.j:62
void updateTrackingAreas()
Definition: CPView.j:3599
Sends messages (CPNotification) between objects.
void drawDividerInRect:(CGRect aRect)
Definition: CPSplitView.j:353
id new()
Definition: CPObject.j:122
BOOL isPaneSplitter()
Definition: CPSplitView.j:237
var CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_
Definition: CPSplitView.j:52
void addTrackingArea:(CPTrackingArea trackingArea)
Definition: CPView.j:3552
void adjustSubviews()
Definition: CPSplitView.j:794
CPLeftMouseDown
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1124
float minPossiblePositionOfDividerAtIndex:(int dividerIndex)
Definition: CPSplitView.j:655
void set()
Definition: CPCursor.j:128
Definition: CPEvent.h:2
CPView superview()
Definition: CPView.j:510
CPTrackingArea initWithRect:options:owner:userInfo:(CGRect aRect, [options] CPTrackingAreaOptions options, [owner] id owner, [userInfo] CPDictionary userInfo)
CGRect frame()
Definition: CPView.j:1046
#define SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION()
Definition: CPSplitView.j:70
var CPSplitViewDelegate_splitView_shouldCollapseSubview_forDoubleClickOnDividerAtIndex_
Definition: CPSplitView.j:47
#define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE()
Definition: CPSplitView.j:57
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:137
CPTrackingCursorUpdate
var CPSplitViewDelegate_splitView_canCollapseSubview_
Definition: CPSplitView.j:45
var CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_
Definition: CPSplitView.j:50
void trackDivider:(CPEvent anEvent)
Definition: CPSplitView.j:477