API  0.9.10
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  {
304  [self _init];
305  }
306 
307  return self;
308 }
309 
310 // Checking Altered Columns
311 
312 - (CPInteger)draggedColumn
313 {
314  return _isDragging ? _activeColumn : -1;
315 }
316 
317 - (float)draggedDistance
318 {
319  if (_isDragging)
320  return (CGRectGetMinX(_columnDragClipView) - _columnMouseDownLocation.x);
321  else
322  return -1;
323 }
324 
325 - (CPInteger)resizedColumn
326 {
327  if (_isResizing)
328  return _activeColumn;
329  else
330  return -1;
331 }
332 
333 // Utility Methods
334 
335 - (CPInteger)columnAtPoint:(CGPoint)aPoint
336 {
337  return [_tableView columnAtPoint:aPoint];
338 }
339 
340 - (CGRect)headerRectOfColumn:(CPInteger)aColumnIndex
341 {
342  var headerRect = [self bounds],
343  columnRect = [_tableView rectOfColumn:aColumnIndex];
344 
345  headerRect.origin.x = CGRectGetMinX(columnRect);
346  headerRect.size.width = CGRectGetWidth(columnRect);
347 
348  return headerRect;
349 }
350 
351 // CPView Overrides
352 
353 - (void)viewDidMoveToWindow
354 {
355  [super viewDidMoveToWindow];
356 
357  [[self window] setAcceptsMouseMovedEvents:YES];
358 }
359 
360 - (void)layoutSubviews
361 {
362  var tableColumns = [_tableView tableColumns],
363  count = [tableColumns count];
364 
365  for (var i = 0; i < count; i++)
366  {
367  var column = [tableColumns objectAtIndex:i],
368  headerView = [column headerView],
369  frame = [self headerRectOfColumn:i];
370 
371  [headerView setFrame:frame];
372 
373  if ([headerView superview] != self)
374  [self addSubview:headerView];
375  }
376 }
377 
378 // CPResponder Overrides
379 
380 - (void)mouseDown:(CPEvent)theEvent
381 {
382  var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
383  adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
384  columnIndex = [self columnAtPoint:adjustedLocation];
385 
386  if (columnIndex === -1)
387  return;
388 
389  _mouseDownLocation = currentLocation;
390  _activeColumn = columnIndex;
391  _canDragColumn = YES;
392 
393  [_tableView _sendDelegateMouseDownInHeaderOfTableColumn:columnIndex];
394 
395  if ([self _shouldResizeTableColumn:columnIndex at:currentLocation])
396  [self _startResizingTableColumn:columnIndex at:currentLocation];
397  else
398  [self _setPressedColumn:columnIndex];
399 }
400 
401 - (void)mouseDragged:(CPEvent)theEvent
402 {
403  var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
404  adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
405  columnIndex = [self columnAtPoint:adjustedLocation];
406 
407  if (_isResizing)
408  {
409  [self _autoscroll:theEvent localLocation:currentLocation];
410  [self _continueResizingTableColumn:_activeColumn at:currentLocation];
411  }
412  else if (_isDragging)
413  {
414  [self _autoscroll:theEvent localLocation:currentLocation];
415  [self _dragTableColumn:_activeColumn to:currentLocation];
416  }
417  else // tracking a press, could become a drag
418  {
419  if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
420  {
421  if ([self _shouldDragTableColumn:columnIndex at:currentLocation])
422  [self _startDraggingTableColumn:columnIndex at:currentLocation];
423  else
424  [self _setPressedColumn:_activeColumn];
425  }
426  else
427  [self _setPressedColumn:-1];
428  }
429 }
430 
431 - (void)mouseUp:(CPEvent)theEvent
432 {
433  if (_isResizing)
434  {
435  [self _stopResizingTableColumn:_activeColumn];
436  }
437  else if (_isDragging)
438  {
439  [self _stopDraggingTableColumn:_activeColumn];
440  }
441  else if (_activeColumn != -1)
442  {
443  var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
444 
445  if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
446  [_tableView _didClickTableColumn:_activeColumn modifierFlags:[theEvent modifierFlags]];
447  }
448 
449  [self _setPressedColumn:-1];
450  [self _updateResizeCursor:[CPApp currentEvent]];
451 
452  _activeColumn = -1;
453 }
454 
455 @end
456 
458 
459 - (void)updateTrackingAreas
460 {
461  [self removeAllTrackingAreas];
462 
464 
465  if (!_tableView)
466  return;
467 
468  for (var i = 0; i < _tableView._tableColumns.length; i++)
469  [self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
470  options:options
471  owner:self
472  userInfo:nil]];
473 }
474 
475 - (void)cursorUpdate:(CPEvent)anEvent
476 {
477  [self _updateResizeCursor:anEvent];
478 }
479 
480 @end
481 
483 
484 - (CGRect)_cursorRectForColumn:(CPInteger)column
485 {
486  if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
487  return CGRectMakeZero();
488 
489  var rect = [self headerRectOfColumn:column];
490 
491  rect.origin.x = (CGRectGetMaxX(rect) - CPTableHeaderViewResizeZone) - 1.0;
492  rect.size.width = (CPTableHeaderViewResizeZone * 2.0) + 1.0; // + 1 for resize line
493 
494  return rect;
495 }
496 
497 - (void)_setPressedColumn:(CPInteger)column
498 {
499  if (_pressedColumn === column)
500  return;
501 
502  if (_pressedColumn != -1)
503  {
504  var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
505  [headerView unsetThemeState:CPThemeStateHighlighted];
506  }
507 
508  if (column != -1)
509  {
510  var headerView = [_tableView._tableColumns[column] headerView];
511  [headerView setThemeState:CPThemeStateHighlighted];
512  }
513  else
514  {
515  // Once the mouse leaves the pressed column, it can no longer drag
516  _canDragColumn = NO;
517  }
518 
519  _pressedColumn = column;
520 }
521 
522 - (BOOL)_shouldDragTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
523 {
524  return _canDragColumn && [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= CPTableHeaderViewDragTolerance;
525 }
526 
527 - (void)_autoscroll:(CPEvent)theEvent localLocation:(CGPoint)theLocation
528 {
529  // Constrain the y coordinate so we don't autoscroll vertically
530  var constrainedLocation = CGPointMake(theLocation.x, CGRectGetMinY([_tableView visibleRect])),
531  constrainedEvent = [CPEvent mouseEventWithType:CPLeftMouseDragged
532  location:[self convertPoint:constrainedLocation toView:nil]
533  modifierFlags:[theEvent modifierFlags]
534  timestamp:[theEvent timestamp]
535  windowNumber:[theEvent windowNumber]
536  context:nil
537  eventNumber:0
538  clickCount:[theEvent clickCount]
539  pressure:[theEvent pressure]];
540 
541  [self autoscroll:constrainedEvent];
542  [_tableView autoscroll:constrainedEvent];
543 }
544 
545 - (CGRect)_headerRectOfLastVisibleColumn
546 {
547  var tableColumns = [_tableView tableColumns],
548  columnIndex = [tableColumns count];
549 
550  while (columnIndex--)
551  {
552  var tableColumn = [tableColumns objectAtIndex:columnIndex];
553 
554  if (![tableColumn isHidden])
555  return [self headerRectOfColumn:columnIndex];
556  }
557 
558  return nil;
559 }
560 
561 - (CGPoint)_constrainDragPoint:(CGPoint)aPoint
562 {
563  // This effectively clamps the value between the minimum and maximum
564  var visibleRect = [_tableView visibleRect],
565  lastColumnRect = [self _headerRectOfLastVisibleColumn],
566  activeColumnRect = [self headerRectOfColumn:_activeColumn],
567  maxX = CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect) - CGRectGetMinX(visibleRect),
568  point = CGPointMake(MAX(MIN(aPoint.x, maxX), -CGRectGetMinX(visibleRect)), aPoint.y);
569 
570  return point;
571 }
572 
573 - (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
574 {
575  [_tableView moveColumn:aFromIndex toColumn:aToIndex];
576  _activeColumn = aToIndex;
577  _pressedColumn = _activeColumn;
578 
579  [_tableView _setDraggedColumn:_activeColumn];
580 
581  [self setNeedsDisplay:YES];
582 }
583 
584 - (BOOL)isDragging
585 {
586  return _isDragging;
587 }
588 
589 - (void)_startDraggingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
590 {
591  _isDragging = YES;
592  _columnDragView = [_tableView _dragViewForColumn:aColumnIndex];
593  _previousTrackingLocation = aPoint;
594 
595  // Create a new clip view for the drag view that clips to the header + visible content
596  var headerHeight = CGRectGetHeight([self frame]),
597  scrollView = [self enclosingScrollView],
598  contentFrame = [[scrollView contentView] frame];
599 
600  contentFrame.origin.y -= headerHeight;
601  contentFrame.size.height += headerHeight;
602 
603  _columnDragClipView = [[CPView alloc] initWithFrame:contentFrame];
604 
605  [_columnDragClipView addSubview:_columnDragView];
606 
607  // Insert the clip view above the table header (and content)
608  [scrollView addSubview:_columnDragClipView positioned:CPWindowAbove relativeTo:self];
609 
610  // Hide the underlying column header subviews, we just want to draw the chrome
611  var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
612 
613  [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
614 
615  // The underlying column header shows normal state
616  [headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
617 
618  // Keep track of the location within the column header where the original mousedown occurred
619  _columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
620 
621  _columnMouseDownLocation = [self convertPoint:_mouseDownLocation toView:_columnDragHeaderView];
622 
623  [_tableView _setDraggedColumn:aColumnIndex];
624 
626 
627  [self setNeedsDisplay:YES];
628 }
629 
630 - (void)_dragTableColumn:(CPInteger)aColumnIndex to:(CGPoint)aPoint
631 {
632  var delta = aPoint.x - _previousTrackingLocation.x,
633  columnPoint = [_columnDragHeaderView convertPoint:aPoint fromView:self];
634 
635  // Only move if the mouse is past the original click point in the direction of movement
636  if ((delta > 0 && columnPoint.x > _columnMouseDownLocation.x) || (delta < 0 && columnPoint.x < _columnMouseDownLocation.x))
637  {
638  var dragFrame = [_columnDragView frame],
639  newOrigin = [self _constrainDragPoint:CGPointMake(CGRectGetMinX(dragFrame) + delta, CGRectGetMinY(dragFrame))];
640 
641  [_columnDragView setFrameOrigin:newOrigin];
642 
643  // When the edge of the dragged column passes the midpoint of an adjacent column, they swap
644  var hoverPoint = CGPointMakeCopy(aPoint);
645 
646  // The drag frame is in content view coordinates, we need it to be in our coordinates
647  dragFrame = [self convertRect:dragFrame fromView:[_columnDragView superview]];
648 
649  if (delta > 0)
650  hoverPoint.x = CGRectGetMaxX(dragFrame);
651  else
652  hoverPoint.x = CGRectGetMinX(dragFrame);
653 
654  var hoveredColumn = [self columnAtPoint:hoverPoint];
655 
656  if (hoveredColumn !== -1)
657  {
658  var columnRect = [self headerRectOfColumn:hoveredColumn],
659  columnCenterPoint = CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect));
660 
661  if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
662  [self _moveColumn:_activeColumn toColumn:hoveredColumn];
663  else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
664  [self _moveColumn:_activeColumn toColumn:hoveredColumn];
665  }
666  }
667 
668  _previousTrackingLocation = aPoint;
669 }
670 
671 - (void)_stopDraggingTableColumn:(CPInteger)aColumnIndex
672 {
673  _isDragging = NO;
674 
675  [_columnDragClipView removeFromSuperview];
676  [_tableView _setDraggedColumn:-1];
677 
678  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
679  headerView = [tableColumn headerView];
680 
681  [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
682 
683  if (_tableView._draggedColumnIsSelected)
684  [headerView setThemeState:CPThemeStateSelected];
685 
686  [_tableView _reloadDataViews];
687  [[_tableView headerView] setNeedsLayout];
688 
690  [self updateTrackingAreas];
691 
692  [_tableView _sendDelegateDidDragTableColumn:tableColumn];
693 }
694 
695 - (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
696 {
697  if (_isResizing)
698  return YES;
699 
700  return [_tableView allowsColumnResizing] && CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
701 }
702 
703 - (void)_startResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
704 {
705  _isResizing = YES;
706  _previousTrackingLocation = aPoint;
707  _activeColumn = aColumnIndex;
708 
709  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
710 
711  _columnOldWidth = [tableColumn width];
712 
713  [tableColumn setDisableResizingPosting:YES];
714  [_tableView setDisableAutomaticResizing:YES];
715 }
716 
717 - (void)_continueResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
718 {
719  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
720  delta = aPoint.x - _previousTrackingLocation.x,
721  spacing = [_tableView intercellSpacing].width,
722  newWidth = [tableColumn width] + spacing + delta,
723  minWidth = [tableColumn minWidth] + spacing,
724  maxWidth = [tableColumn maxWidth] + spacing;
725 
726  if (newWidth <= minWidth)
728  else if (newWidth >= maxWidth)
730  else
732 
733  var columnRect = [_tableView rectOfColumn:aColumnIndex],
734  columnWidth = CGRectGetWidth(columnRect);
735 
736  if ((delta > 0 && columnWidth == maxWidth) || (delta < 0 && columnWidth == minWidth))
737  return;
738 
739  var columnMinX = CGRectGetMinX(columnRect),
740  columnMaxX = CGRectGetMaxX(columnRect);
741 
742  if ((delta > 0 && aPoint.x > columnMaxX) || (delta < 0 && aPoint.x < columnMaxX))
743  {
744  [tableColumn setWidth:newWidth - spacing];
745 
746  [self setNeedsLayout];
747  [self setNeedsDisplay:YES];
748  }
749 
750  _previousTrackingLocation = aPoint;
751 }
752 
753 - (void)_stopResizingTableColumn:(CPInteger)aColumnIndex
754 {
755  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
756 
757  if ([tableColumn width] != _columnOldWidth)
758  {
759  [_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth];
760  [self updateTrackingAreas];
761  }
762 
763  [tableColumn setDisableResizingPosting:NO];
764  [_tableView setDisableAutomaticResizing:NO];
765 
766  _isResizing = NO;
767 }
768 
769 - (void)_updateResizeCursor:(CPEvent)theEvent
770 {
771  var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
772  mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y),
773  overColumn = [self columnAtPoint:mouseOverLocation];
774 
775  if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
776  {
777  var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
778  spacing = [_tableView intercellSpacing].width,
779  width = [tableColumn width];
780 
781  if (width <= [tableColumn minWidth])
783  else if (width >= [tableColumn maxWidth])
785  else
787  }
788  else
790 }
791 
792 @end // CPTableView (CPTableViewPrivate)
793 
794 var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
795 
797 
798 - (id)initWithCoder:(CPCoder)aCoder
799 {
800  if (self = [super initWithCoder:aCoder])
801  {
802  [self _init];
803  _tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
804  }
805 
806  return self;
807 }
808 
809 - (void)encodeWithCoder:(CPCoder)aCoder
810 {
811  [super encodeWithCoder:aCoder];
812  [aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
813 }
814 
815 @end
816 
818 
823 {
824  return _tableView;
825 }
826 
830 - (void)setTableView:(CPTableView)aValue
831 {
832  _tableView = aValue;
833 }
834 
835 @end
id initWithFrame:(CGRect aFrame)
Definition: CPView.j:334
Definition: CPFont.h:2
CPInteger columnAtPoint:(CGPoint aPoint)
id mouseEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:clickCount:pressure:(CPEventType anEventType, [location] CGPoint aPoint, [modifierFlags] unsigned modifierFlags, [timestamp] CPTimeInterval aTimestamp, [windowNumber] int aWindowNumber, [context] CPGraphicsContext aGraphicsContext, [eventNumber] int anEventNumber, [clickCount] int aClickCount, [pressure] float aPressure)
Definition: CPEvent.j:130
BOOL isHidden()
Definition: CALayer.j:597
void addSubview:(CPView aSubview)
Definition: CPView.j:512
CPTableHeaderViewDragColumnHeaderTag
CGRect frame
CPGraphicsContext currentContext()
An object representation of nil.
Definition: CPNull.h:2
CPTableColumnUserResizingMask
Definition: CPTableColumn.j:28
CPCursor resizeRightCursor()
Definition: CPCursor.j:257
int columnIndex
Definition: CPTableView.j:6308
CPCursor arrowCursor()
Definition: CPCursor.j:192
float pressure()
Definition: CPEvent.j:429
BOOL autoscroll:(CPEvent anEvent)
Definition: CPView.j:2900
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
CGPoint locationInWindow()
Definition: CPEvent.j:290
CPColor whiteColor()
Definition: CPColor.j:360
var CPTableHeaderViewDragTolerance
int width
var CPTableHeaderViewTableViewKey
CGRect bounds()
Definition: CPView.j:1302
CPTrackingActiveInKeyWindow
unsigned modifierFlags()
Definition: CPEvent.j:309
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
void setAcceptsMouseMovedEvents:(BOOL shouldAcceptMouseMovedEvents)
Definition: CPWindow.j:1652
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
CPColor grayColor()
Definition: CPColor.j:316
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPScrollView enclosingScrollView()
Definition: CPView.j:2928
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
CGRect bounds()
Definition: CALayer.j:203
CPWindow window()
Definition: CPView.j:503
CPCursor closedHandCursor()
Definition: CPCursor.j:307
CGRect headerRectOfColumn:(CPInteger aColumnIndex)
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2208
CPCellImagePosition CPNoImage
Definition: CPControl.j:69
Definition: CPImage.h:2
CPColor colorWithRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:121
void viewDidMoveToWindow()
Definition: CPView.j:867
CPCursor resizeLeftRightCursor()
Definition: CPCursor.j:262
CPTableView tableView
Definition: CPTableView.j:6194
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2556
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3770
CGPoint convertPoint:toView:(CGPoint aPoint, [toView] CPView aView)
Definition: CPView.j:2232
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CPLineBreakByTruncatingTail
Definition: CPControl.j:49
void setNeedsLayout()
Definition: CPView.j:2707
CPTimeInterval timestamp()
Definition: CPEvent.j:317
CPFont boldSystemFontOfSize:(CGSize aSize)
Definition: CPFont.j:294
CGRect convertRect:fromView:(CGRect aRect, [fromView] CPView aView)
Definition: CPView.j:2285
int clickCount()
Definition: CPEvent.j:372
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
var CPTableHeaderViewResizeZone
int windowNumber()
Definition: CPEvent.j:352
CPTextAlignment CPLeftTextAlignment
Definition: CPText.j:73
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1923
void set()
Definition: CPCursor.j:122
Definition: CPEvent.h:2
void removeAllTrackingAreas()
Definition: CPView.j:3564
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
CPCursor resizeLeftCursor()
Definition: CPCursor.j:252
Definition: CPView.j:136
CPTrackingCursorUpdate