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