/AppKit/CPTableHeaderView.j

http://github.com/cacaodev/cappuccino · Unknown · 826 lines · 641 code · 185 blank · 0 comment · 0 complexity · 384eace7f204085f57fa124cbcd1c15d MD5 · raw file

  1. /*
  2. * CPTableHeaderView.j
  3. * AppKit
  4. *
  5. * Created by Ross Boucher.
  6. * Copyright 2009 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 "CPTableColumn.j"
  23. @import "CPTableView.j"
  24. @import "CPView.j"
  25. @import "CPCursor.j"
  26. @import "_CPImageAndTextView.j"
  27. @global CPApp
  28. @implementation _CPTableColumnHeaderView : CPView
  29. {
  30. _CPImageAndTextView _textField;
  31. }
  32. + (CPString)defaultThemeClass
  33. {
  34. return @"columnHeader";
  35. }
  36. + (CPDictionary)themeAttributes
  37. {
  38. return @{
  39. @"background-color": [CPNull null],
  40. @"text-alignment": CPLeftTextAlignment,
  41. @"line-break-mode": CPLineBreakByTruncatingTail,
  42. @"text-inset": CGInsetMakeZero(),
  43. @"text-color": [CPNull null],
  44. @"font": [CPNull null],
  45. @"text-shadow-color": [CPNull null],
  46. @"text-shadow-offset": CGSizeMakeZero()
  47. };
  48. }
  49. - (id)initWithFrame:(CGRect)frame
  50. {
  51. self = [super initWithFrame:frame];
  52. if (self)
  53. [self _init];
  54. return self;
  55. }
  56. - (void)_init
  57. {
  58. _textField = [[_CPImageAndTextView alloc] initWithFrame:
  59. CGRectMake(5.0, 0.0, CGRectGetWidth([self bounds]) - 10.0, CGRectGetHeight([self bounds]))];
  60. [_textField setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
  61. [_textField setLineBreakMode:CPLineBreakByTruncatingTail];
  62. [_textField setTextColor:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]];
  63. [_textField setFont:[CPFont boldSystemFontOfSize:12.0]];
  64. [_textField setAlignment:CPLeftTextAlignment];
  65. [_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
  66. [_textField setTextShadowColor:[CPColor whiteColor]];
  67. [_textField setTextShadowOffset:CGSizeMake(0,1)];
  68. [self addSubview:_textField];
  69. }
  70. - (void)layoutSubviews
  71. {
  72. [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
  73. var inset = [self currentValueForThemeAttribute:@"text-inset"],
  74. bounds = [self bounds];
  75. [_textField setFrame:CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
  76. [_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
  77. [_textField setFont:[self currentValueForThemeAttribute:@"font"]];
  78. [_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
  79. [_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
  80. [_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
  81. [_textField setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
  82. }
  83. - (void)setStringValue:(CPString)string
  84. {
  85. [_textField setText:string];
  86. }
  87. - (CPString)stringValue
  88. {
  89. return [_textField text];
  90. }
  91. - (void)textField
  92. {
  93. return _textField;
  94. }
  95. - (void)sizeToFit
  96. {
  97. [_textField sizeToFit];
  98. }
  99. - (void)setFont:(CPFont)aFont
  100. {
  101. [self setValue:aFont forThemeAttribute:@"font"];
  102. }
  103. - (CPFont)font
  104. {
  105. return [self currentValueForThemeAttribute:@"font"]
  106. }
  107. - (void)setAlignment:(CPTextAlignment)alignment
  108. {
  109. [self setValue:alignment forThemeAttribute:@"text-alignment"];
  110. }
  111. - (CPTextAlignment)alignment
  112. {
  113. return [self currentValueForThemeAttribute:@"text-alignment"]
  114. }
  115. - (void)setLineBreakMode:(CPLineBreakMode)mode
  116. {
  117. [self setValue:mode forThemeAttribute:@"line-break-mode"];
  118. }
  119. - (CPLineBreakMode)lineBreakMode
  120. {
  121. return [self currentValueForThemeAttribute:@"line-break-mode"]
  122. }
  123. - (void)setTextColor:(CPColor)aColor
  124. {
  125. [self setValue:aColor forThemeAttribute:@"text-color"];
  126. }
  127. - (CPColor)textColor
  128. {
  129. return [self currentValueForThemeAttribute:@"text-color"]
  130. }
  131. - (void)setTextShadowColor:(CPColor)aColor
  132. {
  133. [self setValue:aColor forThemeAttribute:@"text-shadow-color"];
  134. }
  135. - (CPColor)textShadowColor
  136. {
  137. return [self currentValueForThemeAttribute:@"text-shadow-color"]
  138. }
  139. - (void)_setIndicatorImage:(CPImage)anImage
  140. {
  141. if (anImage)
  142. {
  143. [_textField setImage:anImage];
  144. [_textField setImagePosition:CPImageRight];
  145. }
  146. else
  147. {
  148. [_textField setImagePosition:CPNoImage];
  149. }
  150. }
  151. - (CPImage)_indicatorImage
  152. {
  153. return [_textField imagePosition] === CPNoImage ? nil : [_textField image];
  154. }
  155. - (void)drawRect:(CGRect)aRect
  156. {
  157. var bounds = [self bounds];
  158. if (!CGRectIntersectsRect(aRect, bounds))
  159. return;
  160. var context = [[CPGraphicsContext currentContext] graphicsPort],
  161. maxX = CGRectGetMaxX(bounds) - 0.5;
  162. CGContextSetLineWidth(context, 1);
  163. CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0/255.0 alpha:1.0]);
  164. CGContextBeginPath(context);
  165. CGContextMoveToPoint(context, maxX, CGRectGetMinY(bounds));
  166. CGContextAddLineToPoint(context, maxX, CGRectGetMaxY(bounds));
  167. CGContextStrokePath(context);
  168. }
  169. @end
  170. var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
  171. _CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
  172. _CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey";
  173. _CPTableColumnHeaderViewIsDraggingKey = @"_CPTableColumnHeaderViewIsDraggingKey";
  174. @implementation _CPTableColumnHeaderView (CPCoding)
  175. - (id)initWithCoder:(CPCoder)aCoder
  176. {
  177. if (self = [super initWithCoder:aCoder])
  178. {
  179. [self _init];
  180. [self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
  181. [self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
  182. [self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
  183. [self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
  184. }
  185. return self;
  186. }
  187. - (void)encodeWithCoder:(CPCoder)aCoder
  188. {
  189. [super encodeWithCoder:aCoder];
  190. [aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
  191. [aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
  192. [aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
  193. }
  194. @end
  195. CPTableHeaderViewDragColumnHeaderTag = 1;
  196. var CPTableHeaderViewResizeZone = 3.0,
  197. CPTableHeaderViewDragTolerance = 10.0;
  198. @implementation CPTableHeaderView : CPView
  199. {
  200. CGPoint _mouseDownLocation;
  201. CGPoint _columnMouseDownLocation;
  202. CGPoint _mouseEnterExitLocation;
  203. CGPoint _previousTrackingLocation;
  204. int _activeColumn;
  205. int _pressedColumn;
  206. BOOL _isResizing;
  207. BOOL _isDragging;
  208. BOOL _canDragColumn;
  209. CPView _columnDragView;
  210. CPView _columnDragHeaderView;
  211. CPView _columnDragClipView;
  212. float _columnOldWidth;
  213. CPTableView _tableView @accessors(property=tableView);
  214. }
  215. + (CPString)defaultThemeClass
  216. {
  217. return @"tableHeaderRow";
  218. }
  219. + (CPDictionary)themeAttributes
  220. {
  221. return @{
  222. @"background-color": [CPNull null],
  223. @"divider-color": [CPColor grayColor],
  224. @"divider-thickness": 1.0
  225. };
  226. }
  227. - (void)_init
  228. {
  229. _mouseDownLocation = CGPointMakeZero();
  230. _columnMouseDownLocation = CGPointMakeZero();
  231. _mouseEnterExitLocation = CGPointMakeZero();
  232. _previousTrackingLocation = CGPointMakeZero();
  233. _activeColumn = -1;
  234. _pressedColumn = -1;
  235. _isResizing = NO;
  236. _isDragging = NO;
  237. _canDragColumn = NO;
  238. _columnOldWidth = 0.0;
  239. [self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
  240. }
  241. - (id)initWithFrame:(CGRect)aFrame
  242. {
  243. self = [super initWithFrame:aFrame];
  244. if (self)
  245. [self _init];
  246. return self;
  247. }
  248. // Checking Altered Columns
  249. - (CPInteger)draggedColumn
  250. {
  251. return _isDragging ? _activeColumn : -1;
  252. }
  253. - (float)draggedDistance
  254. {
  255. if (_isDragging)
  256. return (CGRectGetMinX(_columnDragClipView) - _columnMouseDownLocation.x);
  257. else
  258. return -1;
  259. }
  260. - (CPInteger)resizedColumn
  261. {
  262. if (_isResizing)
  263. return _activeColumn;
  264. else
  265. return -1;
  266. }
  267. // Utility Methods
  268. - (CPInteger)columnAtPoint:(CGPoint)aPoint
  269. {
  270. return [_tableView columnAtPoint:aPoint];
  271. }
  272. - (CGRect)headerRectOfColumn:(CPInteger)aColumnIndex
  273. {
  274. var headerRect = [self bounds],
  275. columnRect = [_tableView rectOfColumn:aColumnIndex];
  276. headerRect.origin.x = CGRectGetMinX(columnRect);
  277. headerRect.size.width = CGRectGetWidth(columnRect);
  278. return headerRect;
  279. }
  280. // CPView Overrides
  281. - (void)viewDidMoveToWindow
  282. {
  283. [super viewDidMoveToWindow];
  284. [[self window] setAcceptsMouseMovedEvents:YES];
  285. }
  286. - (void)layoutSubviews
  287. {
  288. var tableColumns = [_tableView tableColumns],
  289. count = [tableColumns count];
  290. for (var i = 0; i < count; i++)
  291. {
  292. var column = [tableColumns objectAtIndex:i],
  293. headerView = [column headerView],
  294. frame = [self headerRectOfColumn:i];
  295. [headerView setFrame:frame];
  296. if ([headerView superview] != self)
  297. [self addSubview:headerView];
  298. }
  299. }
  300. // CPResponder Overrides
  301. - (void)mouseDown:(CPEvent)theEvent
  302. {
  303. var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
  304. adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
  305. columnIndex = [self columnAtPoint:adjustedLocation];
  306. if (columnIndex === -1)
  307. return;
  308. _mouseDownLocation = currentLocation;
  309. _activeColumn = columnIndex;
  310. _canDragColumn = YES;
  311. [_tableView _sendDelegateMouseDownInHeaderOfTableColumn:columnIndex];
  312. if ([self _shouldResizeTableColumn:columnIndex at:currentLocation])
  313. [self _startResizingTableColumn:columnIndex at:currentLocation];
  314. else
  315. [self _setPressedColumn:columnIndex];
  316. }
  317. - (void)mouseDragged:(CPEvent)theEvent
  318. {
  319. var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
  320. adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
  321. columnIndex = [self columnAtPoint:adjustedLocation];
  322. if (_isResizing)
  323. {
  324. [self _autoscroll:theEvent localLocation:currentLocation];
  325. [self _continueResizingTableColumn:_activeColumn at:currentLocation];
  326. }
  327. else if (_isDragging)
  328. {
  329. // Disable autoscrolling until it behaves correctly.
  330. //[self _autoscroll:theEvent localLocation:currentLocation];
  331. [self _dragTableColumn:_activeColumn to:currentLocation];
  332. }
  333. else // tracking a press, could become a drag
  334. {
  335. if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
  336. {
  337. if ([self _shouldDragTableColumn:columnIndex at:currentLocation])
  338. [self _startDraggingTableColumn:columnIndex at:currentLocation];
  339. else
  340. [self _setPressedColumn:_activeColumn];
  341. }
  342. else
  343. [self _setPressedColumn:-1];
  344. }
  345. }
  346. - (void)mouseUp:(CPEvent)theEvent
  347. {
  348. if (_isResizing)
  349. {
  350. [self _stopResizingTableColumn:_activeColumn];
  351. }
  352. else if (_isDragging)
  353. {
  354. [self _stopDraggingTableColumn:_activeColumn];
  355. }
  356. else if (_activeColumn != -1)
  357. {
  358. var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  359. if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
  360. [_tableView _didClickTableColumn:_activeColumn modifierFlags:[theEvent modifierFlags]];
  361. }
  362. [self _setPressedColumn:-1];
  363. [self _updateResizeCursor:[CPApp currentEvent]];
  364. _activeColumn = -1;
  365. }
  366. - (void)mouseEntered:(CPEvent)theEvent
  367. {
  368. var location = [theEvent globalLocation];
  369. if (CGPointEqualToPoint(location, _mouseEnterExitLocation))
  370. return;
  371. _mouseEnterExitLocation = location;
  372. [self _updateResizeCursor:theEvent];
  373. }
  374. - (void)mouseMoved:(CPEvent)theEvent
  375. {
  376. [self _updateResizeCursor:theEvent];
  377. }
  378. - (void)mouseExited:(CPEvent)theEvent
  379. {
  380. var location = [theEvent globalLocation];
  381. if (CGPointEqualToPoint(location, _mouseEnterExitLocation))
  382. return;
  383. _mouseEnterExitLocation = location;
  384. // FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
  385. [[CPCursor arrowCursor] set];
  386. }
  387. @end
  388. @implementation CPTableHeaderView (CPTableHeaderViewPrivate)
  389. - (CGRect)_cursorRectForColumn:(CPInteger)column
  390. {
  391. if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
  392. return CGRectMakeZero();
  393. var rect = [self headerRectOfColumn:column];
  394. rect.origin.x = (CGRectGetMaxX(rect) - CPTableHeaderViewResizeZone) - 1.0;
  395. rect.size.width = (CPTableHeaderViewResizeZone * 2.0) + 1.0; // + 1 for resize line
  396. return rect;
  397. }
  398. - (void)_setPressedColumn:(CPInteger)column
  399. {
  400. if (_pressedColumn === column)
  401. return;
  402. if (_pressedColumn != -1)
  403. {
  404. var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
  405. [headerView unsetThemeState:CPThemeStateHighlighted];
  406. }
  407. if (column != -1)
  408. {
  409. var headerView = [_tableView._tableColumns[column] headerView];
  410. [headerView setThemeState:CPThemeStateHighlighted];
  411. }
  412. else
  413. {
  414. // Once the mouse leaves the pressed column, it can no longer drag
  415. _canDragColumn = NO;
  416. }
  417. _pressedColumn = column;
  418. }
  419. - (BOOL)_shouldDragTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
  420. {
  421. return _canDragColumn && [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= CPTableHeaderViewDragTolerance;
  422. }
  423. - (void)_autoscroll:(CPEvent)theEvent localLocation:(CGPoint)theLocation
  424. {
  425. // Constrain the y coordinate so we don't autoscroll vertically
  426. var constrainedLocation = CGPointMake(theLocation.x, CGRectGetMinY([_tableView visibleRect])),
  427. constrainedEvent = [CPEvent mouseEventWithType:CPLeftMouseDragged
  428. location:[self convertPoint:constrainedLocation toView:nil]
  429. modifierFlags:[theEvent modifierFlags]
  430. timestamp:[theEvent timestamp]
  431. windowNumber:[theEvent windowNumber]
  432. context:nil
  433. eventNumber:0
  434. clickCount:[theEvent clickCount]
  435. pressure:[theEvent pressure]];
  436. [self autoscroll:constrainedEvent];
  437. [_tableView autoscroll:constrainedEvent];
  438. }
  439. - (CGRect)_headerRectOfLastVisibleColumn
  440. {
  441. var tableColumns = [_tableView tableColumns],
  442. columnIndex = [tableColumns count];
  443. while (columnIndex--)
  444. {
  445. var tableColumn = [tableColumns objectAtIndex:columnIndex];
  446. if (![tableColumn isHidden])
  447. return [self headerRectOfColumn:columnIndex];
  448. }
  449. return nil;
  450. }
  451. - (CGPoint)_constrainDragPoint:(CGPoint)aPoint
  452. {
  453. // This effectively clamps the value between the minimum and maximum
  454. var visibleRect = [_tableView visibleRect],
  455. lastColumnRect = [self _headerRectOfLastVisibleColumn],
  456. activeColumnRect = [self headerRectOfColumn:_activeColumn],
  457. maxX = CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect) - CGRectGetMinX(visibleRect),
  458. point = CGPointMake(MAX(MIN(aPoint.x, maxX), -CGRectGetMinX(visibleRect)), aPoint.y);
  459. return point;
  460. }
  461. - (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
  462. {
  463. [_tableView moveColumn:aFromIndex toColumn:aToIndex];
  464. _activeColumn = aToIndex;
  465. _pressedColumn = _activeColumn;
  466. [_tableView _setDraggedColumn:_activeColumn];
  467. [self setNeedsDisplay:YES];
  468. }
  469. - (BOOL)isDragging
  470. {
  471. return _isDragging;
  472. }
  473. - (void)_startDraggingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
  474. {
  475. _isDragging = YES;
  476. _columnDragView = [_tableView _dragViewForColumn:aColumnIndex];
  477. _previousTrackingLocation = aPoint;
  478. // Create a new clip view for the drag view that clips to the header + visible content
  479. var headerHeight = CGRectGetHeight([self frame]),
  480. scrollView = [self enclosingScrollView],
  481. contentFrame = [[scrollView contentView] frame];
  482. contentFrame.origin.y -= headerHeight;
  483. contentFrame.size.height += headerHeight;
  484. _columnDragClipView = [[CPView alloc] initWithFrame:contentFrame];
  485. [_columnDragClipView addSubview:_columnDragView];
  486. // Insert the clip view above the table header (and content)
  487. [scrollView addSubview:_columnDragClipView positioned:CPWindowAbove relativeTo:self];
  488. // Hide the underlying column header subviews, we just want to draw the chrome
  489. var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
  490. [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
  491. // The underlying column header shows normal state
  492. [headerView unsetThemeState:CPThemeStateHighlighted | CPThemeStateSelected];
  493. // Keep track of the location within the column header where the original mousedown occurred
  494. _columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
  495. _columnMouseDownLocation = [self convertPoint:_mouseDownLocation toView:_columnDragHeaderView];
  496. [_tableView _setDraggedColumn:aColumnIndex];
  497. [[CPCursor closedHandCursor] set];
  498. [self setNeedsDisplay:YES];
  499. }
  500. - (void)_dragTableColumn:(CPInteger)aColumnIndex to:(CGPoint)aPoint
  501. {
  502. var delta = aPoint.x - _previousTrackingLocation.x,
  503. columnPoint = [_columnDragHeaderView convertPoint:aPoint fromView:self];
  504. // Only move if the mouse is past the original click point in the direction of movement
  505. if ((delta > 0 && columnPoint.x > _columnMouseDownLocation.x) || (delta < 0 && columnPoint.x < _columnMouseDownLocation.x))
  506. {
  507. var dragFrame = [_columnDragView frame],
  508. newOrigin = [self _constrainDragPoint:CGPointMake(CGRectGetMinX(dragFrame) + delta, CGRectGetMinY(dragFrame))];
  509. [_columnDragView setFrameOrigin:newOrigin];
  510. // When the edge of the dragged column passes the midpoint of an adjacent column, they swap
  511. var hoverPoint = CGPointMakeCopy(aPoint);
  512. // The drag frame is in content view coordinates, we need it to be in our coordinates
  513. dragFrame = [self convertRect:dragFrame fromView:[_columnDragView superview]];
  514. if (delta > 0)
  515. hoverPoint.x = CGRectGetMaxX(dragFrame);
  516. else
  517. hoverPoint.x = CGRectGetMinX(dragFrame);
  518. var hoveredColumn = [self columnAtPoint:hoverPoint];
  519. if (hoveredColumn !== -1)
  520. {
  521. var columnRect = [self headerRectOfColumn:hoveredColumn],
  522. columnCenterPoint = CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect));
  523. if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
  524. [self _moveColumn:_activeColumn toColumn:hoveredColumn];
  525. else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
  526. [self _moveColumn:_activeColumn toColumn:hoveredColumn];
  527. }
  528. }
  529. _previousTrackingLocation = aPoint;
  530. }
  531. - (void)_stopDraggingTableColumn:(CPInteger)aColumnIndex
  532. {
  533. _isDragging = NO;
  534. [_columnDragClipView removeFromSuperview];
  535. [_tableView _setDraggedColumn:-1];
  536. var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
  537. [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
  538. if (_tableView._draggedColumnIsSelected)
  539. [headerView setThemeState:CPThemeStateSelected];
  540. var columnRect = [_tableView rectOfColumn:aColumnIndex];
  541. [_tableView _reloadDataViews];
  542. [[_tableView headerView] setNeedsLayout];
  543. [[CPCursor arrowCursor] set];
  544. }
  545. - (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
  546. {
  547. if (_isResizing)
  548. return YES;
  549. return [_tableView allowsColumnResizing] && CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
  550. }
  551. - (void)_startResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
  552. {
  553. _isResizing = YES;
  554. _previousTrackingLocation = aPoint;
  555. _activeColumn = aColumnIndex;
  556. var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
  557. _columnOldWidth = [tableColumn width];
  558. [tableColumn setDisableResizingPosting:YES];
  559. [_tableView setDisableAutomaticResizing:YES];
  560. }
  561. - (void)_continueResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
  562. {
  563. var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
  564. delta = aPoint.x - _previousTrackingLocation.x,
  565. spacing = [_tableView intercellSpacing].width,
  566. newWidth = [tableColumn width] + spacing + delta,
  567. minWidth = [tableColumn minWidth] + spacing,
  568. maxWidth = [tableColumn maxWidth] + spacing;
  569. if (newWidth <= minWidth)
  570. [[CPCursor resizeRightCursor] set];
  571. else if (newWidth >= maxWidth)
  572. [[CPCursor resizeLeftCursor] set];
  573. else
  574. [[CPCursor resizeLeftRightCursor] set];
  575. var columnRect = [_tableView rectOfColumn:aColumnIndex],
  576. columnWidth = CGRectGetWidth(columnRect);
  577. if ((delta > 0 && columnWidth == maxWidth) || (delta < 0 && columnWidth == minWidth))
  578. return;
  579. var columnMinX = CGRectGetMinX(columnRect),
  580. columnMaxX = CGRectGetMaxX(columnRect);
  581. if ((delta > 0 && aPoint.x > columnMaxX) || (delta < 0 && aPoint.x < columnMaxX))
  582. {
  583. [tableColumn setWidth:newWidth - spacing];
  584. [self setNeedsLayout];
  585. [self setNeedsDisplay:YES];
  586. }
  587. _previousTrackingLocation = aPoint;
  588. }
  589. - (void)_stopResizingTableColumn:(CPInteger)aColumnIndex
  590. {
  591. var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
  592. if ([tableColumn width] != _columnOldWidth)
  593. [_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth];
  594. [tableColumn setDisableResizingPosting:NO];
  595. [_tableView setDisableAutomaticResizing:NO];
  596. _isResizing = NO;
  597. }
  598. - (void)_updateResizeCursor:(CPEvent)theEvent
  599. {
  600. // never get stuck in resize cursor mode (FIXME take out when we turn on tracking rects)
  601. if (![_tableView allowsColumnResizing] || ([theEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents]))
  602. {
  603. [[CPCursor arrowCursor] set];
  604. return;
  605. }
  606. var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
  607. mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y),
  608. overColumn = [self columnAtPoint:mouseOverLocation];
  609. if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
  610. {
  611. var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
  612. spacing = [_tableView intercellSpacing].width,
  613. width = [tableColumn width];
  614. if (width <= [tableColumn minWidth])
  615. [[CPCursor resizeRightCursor] set];
  616. else if (width >= [tableColumn maxWidth])
  617. [[CPCursor resizeLeftCursor] set];
  618. else
  619. [[CPCursor resizeLeftRightCursor] set];
  620. }
  621. else
  622. [[CPCursor arrowCursor] set];
  623. }
  624. @end // CPTableView (CPTableViewPrivate)
  625. var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
  626. @implementation CPTableHeaderView (CPCoding)
  627. - (id)initWithCoder:(CPCoder)aCoder
  628. {
  629. if (self = [super initWithCoder:aCoder])
  630. {
  631. [self _init];
  632. _tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
  633. }
  634. return self;
  635. }
  636. - (void)encodeWithCoder:(CPCoder)aCoder
  637. {
  638. [super encodeWithCoder:aCoder];
  639. [aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
  640. }
  641. @end