00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import "CPImage.j"
00024 @import "CPView.j"
00025
00026 #include "CoreGraphics/CGGeometry.h"
00027 #include "Platform/Platform.h"
00028 #include "Platform/DOM/CPDOMDisplayServer.h"
00029
00030
00031 CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
00032 CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
00033
00034 var CPSplitViewHorizontalImage = nil,
00035 CPSplitViewVerticalImage = nil;
00036
00037 @implementation CPSplitView : CPView
00038 {
00039 id _delegate;
00040 BOOL _isVertical;
00041 BOOL _isPaneSplitter;
00042
00043 int _currentDivider;
00044 float _initialOffset;
00045
00046 CPString _originComponent;
00047 CPString _sizeComponent;
00048
00049 CPArray _DOMDividerElements;
00050 CPString _dividerImagePath;
00051 int _drawingDivider;
00052
00053 BOOL _needsResizeSubviews;
00054 }
00055
00056
00057
00058
00059 + (void)initialize
00060 {
00061 if (self != [CPSplitView class])
00062 return;
00063
00064 var bundle = [CPBundle bundleForClass:self];
00065 CPSplitViewHorizontalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewHorizontal.png"] size:CPSizeMake(5.0, 10.0)];
00066 CPSplitViewVerticalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewVertical.png"] size:CPSizeMake(10.0, 5.0)];
00067 }
00068
00069 - (id)initWithFrame:(CGRect)aFrame
00070 {
00071 if (self = [super initWithFrame:aFrame])
00072 {
00073 _currentDivider = CPNotFound;
00074
00075 _DOMDividerElements = [];
00076
00077 [self _setVertical:YES];
00078 }
00079
00080 return self;
00081 }
00082
00083 - (float)dividerThickness
00084 {
00085 return _isPaneSplitter ? 1.0 : 10.0;
00086 }
00087
00088 - (BOOL)isVertical
00089 {
00090 return _isVertical;
00091 }
00092
00093 - (void)setVertical:(BOOL)shouldBeVertical
00094 {
00095 if (![self _setVertical:shouldBeVertical])
00096 return;
00097
00098
00099 var frame = [self frame],
00100 dividerThickness = [self dividerThickness];
00101
00102 [self _postNotificationWillResize];
00103
00104 var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
00105 index = 0,
00106 count = _subviews.length;
00107
00108 if ([self isVertical])
00109 for (; index < count; ++index)
00110 [_subviews[index] setFrame:CGRectMake(ROUND((eachSize + dividerThickness) * index), 0, eachSize, frame.size.height)];
00111 else
00112 for (; index < count; ++index)
00113 [_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
00114
00115 [self setNeedsDisplay:YES];
00116
00117 [self _postNotificationDidResize];
00118
00119 }
00120
00121 - (BOOL)_setVertical:(BOOL)shouldBeVertical
00122 {
00123 var changed = (_isVertical != shouldBeVertical);
00124
00125 _isVertical = shouldBeVertical;
00126
00127 _originComponent = [self isVertical] ? "x" : "y";
00128 _sizeComponent = [self isVertical] ? "width" : "height";
00129 _dividerImagePath = [self isVertical] ? [CPSplitViewVerticalImage filename] : [CPSplitViewHorizontalImage filename];
00130
00131 return changed;
00132 }
00133
00134 - (BOOL)isPaneSplitter
00135 {
00136 return _isPaneSplitter;
00137 }
00138
00139 - (void)setIsPaneSplitter:(BOOL)shouldBePaneSplitter
00140 {
00141 if (_isPaneSplitter == shouldBePaneSplitter)
00142 return;
00143
00144 _isPaneSplitter = shouldBePaneSplitter;
00145
00146 #if PLATFORM(DOM)
00147 _DOMDividerElements = [];
00148 #endif
00149
00150 [self setNeedsDisplay:YES];
00151 }
00152
00153 - (void)didAddSubview:(CPView)aSubview
00154 {
00155 _needsResizeSubviews = YES;
00156
00157 }
00158
00159 - (BOOL)isSubviewCollapsed:(CPView)subview
00160 {
00161 return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
00162 }
00163
00164 - (CGRect)rectOfDividerAtIndex:(int)aDivider
00165 {
00166 var frame = [_subviews[aDivider] frame],
00167 rect = CGRectMakeZero();
00168
00169 rect.size = [self frame].size;
00170
00171 rect.size[_sizeComponent] = [self dividerThickness];
00172 rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
00173
00174 return rect;
00175 }
00176
00177 - (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
00178 {
00179 var realRect = [self rectOfDividerAtIndex:aDivider];
00180
00181 var padding = 2;
00182
00183 realRect.size[_sizeComponent] += padding * 2;
00184 realRect.origin[_originComponent] -= padding;
00185
00186 return realRect;
00187 }
00188
00189 - (void)drawRect:(CGRect)rect
00190 {
00191 var count = [_subviews count] - 1;
00192
00193 if (count > 0)
00194 while (count--)
00195 [self drawDividerInRect:[self rectOfDividerAtIndex:count]];
00196 }
00197
00198 - (void)drawDividerInRect:(CGRect)aRect
00199 {
00200 #if PLATFORM(DOM)
00201 if (!_DOMDividerElements[_drawingDivider])
00202 {
00203 _DOMDividerElements[_drawingDivider] = document.createElement("div");
00204 _DOMDividerElements[_drawingDivider].style.cursor = "move";
00205 _DOMDividerElements[_drawingDivider].style.position = "absolute";
00206 _DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
00207
00208 CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
00209
00210 if (_isPaneSplitter)
00211 {
00212 _DOMDividerElements[_drawingDivider].style.backgroundColor = "#A5A5A5";
00213 _DOMDividerElements[_drawingDivider].style.backgroundImage = "";
00214 }
00215 else
00216 {
00217 _DOMDividerElements[_drawingDivider].style.backgroundColor = "";
00218 _DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
00219 }
00220 }
00221
00222 CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
00223 CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
00224 #endif
00225 }
00226
00227 - (void)viewWillDraw
00228 {
00229 [self _adjustSubviewsWithCalculatedSize];
00230 }
00231
00232 - (void)_adjustSubviewsWithCalculatedSize
00233 {
00234 if (!_needsResizeSubviews)
00235 return;
00236
00237 _needsResizeSubviews = NO;
00238
00239 var subviews = [self subviews],
00240 count = subviews.length,
00241 oldSize = CGSizeMakeZero();
00242
00243 if ([self isVertical])
00244 {
00245 oldSize.width += [self dividerThickness] * (count - 1);
00246 oldSize.height = CGRectGetHeight([self frame]);
00247 }
00248 else
00249 {
00250 oldSize.width = CGRectGetWidth([self frame]);
00251 oldSize.height += [self dividerThickness] * (count - 1);
00252 }
00253
00254 while (count--)
00255 oldSize[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
00256
00257 [self resizeSubviewsWithOldSize:oldSize];
00258 }
00259
00260 - (BOOL)cursorAtPoint:(CPPoint)aPoint hitDividerAtIndex:(int)anIndex
00261 {
00262 var frame = [_subviews[anIndex] frame],
00263 startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
00264 effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
00265 additionalRect = null;
00266
00267 if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
00268 effectiveRect = [_delegate splitView:self effectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
00269
00270 if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
00271 additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
00272
00273 return CGRectContainsPoint(effectiveRect, aPoint) || (additionalRect && CGRectContainsPoint(additionalRect, aPoint));
00274 }
00275
00276 - (CPView)hitTest:(CGPoint)aPoint
00277 {
00278 if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
00279 return nil;
00280
00281 var point = [self convertPoint:aPoint fromView:[self superview]];
00282
00283 var count = [_subviews count] - 1;
00284 for (var i = 0; i < count; i++)
00285 {
00286 if ([self cursorAtPoint:point hitDividerAtIndex:i])
00287 return self;
00288 }
00289
00290 return [super hitTest:aPoint];
00291 }
00292
00293
00294
00295
00296
00297 - (void)trackDivider:(CPEvent)anEvent
00298 {
00299 var type = [anEvent type];
00300
00301 if (type == CPLeftMouseUp)
00302 {
00303 if (_currentDivider != CPNotFound)
00304 {
00305 _currentDivider = CPNotFound;
00306 [self _postNotificationDidResize];
00307 }
00308
00309 return;
00310 }
00311
00312 if (type == CPLeftMouseDown)
00313 {
00314 var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00315
00316 _currentDivider = CPNotFound;
00317 var count = [_subviews count] - 1;
00318 for (var i = 0; i < count; i++)
00319 {
00320 var frame = [_subviews[i] frame],
00321 startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
00322
00323 if ([self cursorAtPoint:point hitDividerAtIndex:i])
00324 {
00325 if ([anEvent clickCount] == 2 &&
00326 [_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
00327 [_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
00328 {
00329 var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
00330 maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
00331
00332 if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
00333 {
00334 if ([self isSubviewCollapsed:_subviews[i]])
00335 [self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
00336 else
00337 [self setPosition:minPosition ofDividerAtIndex:i];
00338 }
00339 else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
00340 {
00341 if ([self isSubviewCollapsed:_subviews[i+1]])
00342 [self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
00343 else
00344 [self setPosition:maxPosition ofDividerAtIndex:i];
00345 }
00346 }
00347 else
00348 {
00349 _currentDivider = i;
00350 _initialOffset = startPosition - point[_originComponent];
00351
00352 [self _postNotificationWillResize];
00353 }
00354 }
00355 }
00356 }
00357
00358 else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
00359 {
00360 var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00361
00362 [self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
00363 }
00364
00365 [CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
00366 }
00367
00368 - (void)mouseDown:(CPEvent)anEvent
00369 {
00370 [self trackDivider:anEvent];
00371 }
00372
00373 - (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
00374 {
00375 var frame = [_subviews[dividerIndex + 1] frame];
00376
00377 if (dividerIndex + 1 < [_subviews count] - 1)
00378 return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
00379 else
00380 return [self frame].size[_sizeComponent] - [self dividerThickness];
00381 }
00382
00383 - (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
00384 {
00385 var frame = [_subviews[dividerIndex - 1] frame];
00386
00387 if (dividerIndex > 0)
00388 return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
00389 else
00390 return 0;
00391 }
00392
00393 - (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
00394 {
00395 [self _adjustSubviewsWithCalculatedSize];
00396
00397
00398 if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
00399 position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
00400
00401 var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
00402 proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
00403 actualMax = proposedMax,
00404 actualMin = proposedMin;
00405
00406 if([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
00407 actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
00408
00409 if([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
00410 actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
00411
00412 var frame = [self frame],
00413 viewA = _subviews[dividerIndex],
00414 frameA = [viewA frame],
00415 viewB = _subviews[dividerIndex+1],
00416 frameB = [viewB frame];
00417
00418 var realPosition = MAX(MIN(position, actualMax), actualMin);
00419
00420 if (position < proposedMin + (actualMin - proposedMin) / 2)
00421 if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
00422 if ([_delegate splitView:self canCollapseSubview:viewA])
00423 realPosition = proposedMin;
00424
00425 frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
00426 [_subviews[dividerIndex] setFrame:frameA];
00427
00428 frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
00429 frameB.origin[_originComponent] = realPosition + [self dividerThickness];
00430 [_subviews[dividerIndex + 1] setFrame:frameB];
00431
00432 [self setNeedsDisplay:YES];
00433 }
00434
00435 - (void)setFrameSize:(CGSize)aSize
00436 {
00437 [self _adjustSubviewsWithCalculatedSize];
00438
00439 [super setFrameSize:aSize];
00440
00441 [self setNeedsDisplay:YES];
00442 }
00443
00444 - (void)resizeSubviewsWithOldSize:(CPSize)oldSize
00445 {
00446 if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
00447 {
00448 [_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
00449 return;
00450 }
00451
00452 [self _postNotificationWillResize];
00453
00454 var index = 0,
00455 count = [_subviews count],
00456 bounds = [self bounds],
00457 dividerThickness = [self dividerThickness];
00458
00459 for (; index < count; ++index)
00460 {
00461 var view = _subviews[index],
00462 viewFrame = CGRectMakeCopy(bounds);
00463
00464 if (index + 1 == count)
00465 viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] - viewFrame.origin[_originComponent];
00466 else
00467 viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] * ([view frame].size[_sizeComponent] / oldSize[_sizeComponent]);
00468
00469 bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
00470
00471 [view setFrame:viewFrame];
00472 }
00473
00474 [self _postNotificationDidResize];
00475 }
00476
00477 - (void)setDelegate:(id)delegate
00478 {
00479 if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
00480 [[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
00481 if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
00482 [[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
00483
00484 _delegate = delegate;
00485
00486 if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
00487 [[CPNotificationCenter defaultCenter] addObserver:_delegate
00488 selector:@selector(splitViewDidResizeSubviews:)
00489 name:CPSplitViewDidResizeSubviewsNotification
00490 object:self];
00491 if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
00492 [[CPNotificationCenter defaultCenter] addObserver:_delegate
00493 selector:@selector(splitViewWillResizeSubviews:)
00494 name:CPSplitViewWillResizeSubviewsNotification
00495 object:self];
00496 }
00497
00498 - (void)_postNotificationWillResize
00499 {
00500 [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self];
00501 }
00502
00503 - (void)_postNotificationDidResize
00504 {
00505 [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self];
00506 }
00507
00508 @end
00509
00510 var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
00511 CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
00512 CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey";
00513
00514 @implementation CPSplitView (CPCoding)
00515
00516
00517
00518
00519
00520 - (id)initWithCoder:(CPCoder)aCoder
00521 {
00522 self = [super initWithCoder:aCoder];
00523
00524 if (self)
00525 {
00526 _currentDivider = CPNotFound;
00527
00528 _DOMDividerElements = [];
00529
00530 _delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];;
00531
00532 _isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
00533 [self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
00534 }
00535
00536 return self;
00537 }
00538
00539
00540
00541
00542
00543 - (void)encodeWithCoder:(CPCoder)aCoder
00544 {
00545 [super encodeWithCoder:aCoder];
00546
00547 [aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
00548
00549 [aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
00550 [aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
00551 }
00552
00553 @end