API  0.9.9
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  // Disable autoscrolling until it behaves correctly.
415  //[self _autoscroll:theEvent localLocation:currentLocation];
416  [self _dragTableColumn:_activeColumn to:currentLocation];
417  }
418  else // tracking a press, could become a drag
419  {
420  if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
421  {
422  if ([self _shouldDragTableColumn:columnIndex at:currentLocation])
423  [self _startDraggingTableColumn:columnIndex at:currentLocation];
424  else
425  [self _setPressedColumn:_activeColumn];
426  }
427  else
428  [self _setPressedColumn:-1];
429  }
430 }
431 
432 - (void)mouseUp:(CPEvent)theEvent
433 {
434  if (_isResizing)
435  {
436  [self _stopResizingTableColumn:_activeColumn];
437  }
438  else if (_isDragging)
439  {
440  [self _stopDraggingTableColumn:_activeColumn];
441  }
442  else if (_activeColumn != -1)
443  {
444  var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
445 
446  if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
447  [_tableView _didClickTableColumn:_activeColumn modifierFlags:[theEvent modifierFlags]];
448  }
449 
450  [self _setPressedColumn:-1];
451  [self _updateResizeCursor:[CPApp currentEvent]];
452 
453  _activeColumn = -1;
454 }
455 
456 @end
457 
459 
460 - (void)updateTrackingAreas
461 {
462  [self removeAllTrackingAreas];
463 
465 
466  for (var i = 0; i < _tableView._tableColumns.length; i++)
467  [self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
468  options:options
469  owner:self
470  userInfo:nil]];
471 }
472 
473 - (void)cursorUpdate:(CPEvent)anEvent
474 {
475  [self _updateResizeCursor:anEvent];
476 }
477 
478 @end
479 
481 
482 - (CGRect)_cursorRectForColumn:(CPInteger)column
483 {
484  if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
485  return CGRectMakeZero();
486 
487  var rect = [self headerRectOfColumn:column];
488 
489  rect.origin.x = (CGRectGetMaxX(rect) - CPTableHeaderViewResizeZone) - 1.0;
490  rect.size.width = (CPTableHeaderViewResizeZone * 2.0) + 1.0; // + 1 for resize line
491 
492  return rect;
493 }
494 
495 - (void)_setPressedColumn:(CPInteger)column
496 {
497  if (_pressedColumn === column)
498  return;
499 
500  if (_pressedColumn != -1)
501  {
502  var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
503  [headerView unsetThemeState:CPThemeStateHighlighted];
504  }
505 
506  if (column != -1)
507  {
508  var headerView = [_tableView._tableColumns[column] headerView];
509  [headerView setThemeState:CPThemeStateHighlighted];
510  }
511  else
512  {
513  // Once the mouse leaves the pressed column, it can no longer drag
514  _canDragColumn = NO;
515  }
516 
517  _pressedColumn = column;
518 }
519 
520 - (BOOL)_shouldDragTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
521 {
522  return _canDragColumn && [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= CPTableHeaderViewDragTolerance;
523 }
524 
525 - (void)_autoscroll:(CPEvent)theEvent localLocation:(CGPoint)theLocation
526 {
527  // Constrain the y coordinate so we don't autoscroll vertically
528  var constrainedLocation = CGPointMake(theLocation.x, CGRectGetMinY([_tableView visibleRect])),
529  constrainedEvent = [CPEvent mouseEventWithType:CPLeftMouseDragged
530  location:[self convertPoint:constrainedLocation toView:nil]
531  modifierFlags:[theEvent modifierFlags]
532  timestamp:[theEvent timestamp]
533  windowNumber:[theEvent windowNumber]
534  context:nil
535  eventNumber:0
536  clickCount:[theEvent clickCount]
537  pressure:[theEvent pressure]];
538 
539  [self autoscroll:constrainedEvent];
540  [_tableView autoscroll:constrainedEvent];
541 }
542 
543 - (CGRect)_headerRectOfLastVisibleColumn
544 {
545  var tableColumns = [_tableView tableColumns],
546  columnIndex = [tableColumns count];
547 
548  while (columnIndex--)
549  {
550  var tableColumn = [tableColumns objectAtIndex:columnIndex];
551 
552  if (![tableColumn isHidden])
553  return [self headerRectOfColumn:columnIndex];
554  }
555 
556  return nil;
557 }
558 
559 - (CGPoint)_constrainDragPoint:(CGPoint)aPoint
560 {
561  // This effectively clamps the value between the minimum and maximum
562  var visibleRect = [_tableView visibleRect],
563  lastColumnRect = [self _headerRectOfLastVisibleColumn],
564  activeColumnRect = [self headerRectOfColumn:_activeColumn],
565  maxX = CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect) - CGRectGetMinX(visibleRect),
566  point = CGPointMake(MAX(MIN(aPoint.x, maxX), -CGRectGetMinX(visibleRect)), aPoint.y);
567 
568  return point;
569 }
570 
571 - (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
572 {
573  [_tableView moveColumn:aFromIndex toColumn:aToIndex];
574  _activeColumn = aToIndex;
575  _pressedColumn = _activeColumn;
576 
577  [_tableView _setDraggedColumn:_activeColumn];
578 
579  [self setNeedsDisplay:YES];
580 }
581 
582 - (BOOL)isDragging
583 {
584  return _isDragging;
585 }
586 
587 - (void)_startDraggingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
588 {
589  _isDragging = YES;
590  _columnDragView = [_tableView _dragViewForColumn:aColumnIndex];
591  _previousTrackingLocation = aPoint;
592 
593  // Create a new clip view for the drag view that clips to the header + visible content
594  var headerHeight = CGRectGetHeight([self frame]),
595  scrollView = [self enclosingScrollView],
596  contentFrame = [[scrollView contentView] frame];
597 
598  contentFrame.origin.y -= headerHeight;
599  contentFrame.size.height += headerHeight;
600 
601  _columnDragClipView = [[CPView alloc] initWithFrame:contentFrame];
602 
603  [_columnDragClipView addSubview:_columnDragView];
604 
605  // Insert the clip view above the table header (and content)
606  [scrollView addSubview:_columnDragClipView positioned:CPWindowAbove relativeTo:self];
607 
608  // Hide the underlying column header subviews, we just want to draw the chrome
609  var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
610 
611  [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
612 
613  // The underlying column header shows normal state
614  [headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
615 
616  // Keep track of the location within the column header where the original mousedown occurred
617  _columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
618 
619  _columnMouseDownLocation = [self convertPoint:_mouseDownLocation toView:_columnDragHeaderView];
620 
621  [_tableView _setDraggedColumn:aColumnIndex];
622 
624 
625  [self setNeedsDisplay:YES];
626 }
627 
628 - (void)_dragTableColumn:(CPInteger)aColumnIndex to:(CGPoint)aPoint
629 {
630  var delta = aPoint.x - _previousTrackingLocation.x,
631  columnPoint = [_columnDragHeaderView convertPoint:aPoint fromView:self];
632 
633  // Only move if the mouse is past the original click point in the direction of movement
634  if ((delta > 0 && columnPoint.x > _columnMouseDownLocation.x) || (delta < 0 && columnPoint.x < _columnMouseDownLocation.x))
635  {
636  var dragFrame = [_columnDragView frame],
637  newOrigin = [self _constrainDragPoint:CGPointMake(CGRectGetMinX(dragFrame) + delta, CGRectGetMinY(dragFrame))];
638 
639  [_columnDragView setFrameOrigin:newOrigin];
640 
641  // When the edge of the dragged column passes the midpoint of an adjacent column, they swap
642  var hoverPoint = CGPointMakeCopy(aPoint);
643 
644  // The drag frame is in content view coordinates, we need it to be in our coordinates
645  dragFrame = [self convertRect:dragFrame fromView:[_columnDragView superview]];
646 
647  if (delta > 0)
648  hoverPoint.x = CGRectGetMaxX(dragFrame);
649  else
650  hoverPoint.x = CGRectGetMinX(dragFrame);
651 
652  var hoveredColumn = [self columnAtPoint:hoverPoint];
653 
654  if (hoveredColumn !== -1)
655  {
656  var columnRect = [self headerRectOfColumn:hoveredColumn],
657  columnCenterPoint = CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect));
658 
659  if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
660  [self _moveColumn:_activeColumn toColumn:hoveredColumn];
661  else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
662  [self _moveColumn:_activeColumn toColumn:hoveredColumn];
663  }
664  }
665 
666  _previousTrackingLocation = aPoint;
667 }
668 
669 - (void)_stopDraggingTableColumn:(CPInteger)aColumnIndex
670 {
671  _isDragging = NO;
672 
673  [_columnDragClipView removeFromSuperview];
674  [_tableView _setDraggedColumn:-1];
675 
676  var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
677 
678  [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
679 
680  if (_tableView._draggedColumnIsSelected)
681  [headerView setThemeState:CPThemeStateSelected];
682 
683  var columnRect = [_tableView rectOfColumn:aColumnIndex];
684 
685  [_tableView _reloadDataViews];
686  [[_tableView headerView] setNeedsLayout];
687 
689  [self updateTrackingAreas];
690 }
691 
692 - (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
693 {
694  if (_isResizing)
695  return YES;
696 
697  return [_tableView allowsColumnResizing] && CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
698 }
699 
700 - (void)_startResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
701 {
702  _isResizing = YES;
703  _previousTrackingLocation = aPoint;
704  _activeColumn = aColumnIndex;
705 
706  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
707 
708  _columnOldWidth = [tableColumn width];
709 
710  [tableColumn setDisableResizingPosting:YES];
711  [_tableView setDisableAutomaticResizing:YES];
712 }
713 
714 - (void)_continueResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
715 {
716  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
717  delta = aPoint.x - _previousTrackingLocation.x,
718  spacing = [_tableView intercellSpacing].width,
719  newWidth = [tableColumn width] + spacing + delta,
720  minWidth = [tableColumn minWidth] + spacing,
721  maxWidth = [tableColumn maxWidth] + spacing;
722 
723  if (newWidth <= minWidth)
725  else if (newWidth >= maxWidth)
727  else
729 
730  var columnRect = [_tableView rectOfColumn:aColumnIndex],
731  columnWidth = CGRectGetWidth(columnRect);
732 
733  if ((delta > 0 && columnWidth == maxWidth) || (delta < 0 && columnWidth == minWidth))
734  return;
735 
736  var columnMinX = CGRectGetMinX(columnRect),
737  columnMaxX = CGRectGetMaxX(columnRect);
738 
739  if ((delta > 0 && aPoint.x > columnMaxX) || (delta < 0 && aPoint.x < columnMaxX))
740  {
741  [tableColumn setWidth:newWidth - spacing];
742 
743  [self setNeedsLayout];
744  [self setNeedsDisplay:YES];
745  }
746 
747  _previousTrackingLocation = aPoint;
748 }
749 
750 - (void)_stopResizingTableColumn:(CPInteger)aColumnIndex
751 {
752  var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
753 
754  if ([tableColumn width] != _columnOldWidth)
755  {
756  [_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth];
757  [self updateTrackingAreas];
758  }
759 
760  [tableColumn setDisableResizingPosting:NO];
761  [_tableView setDisableAutomaticResizing:NO];
762 
763  _isResizing = NO;
764 }
765 
766 - (void)_updateResizeCursor:(CPEvent)theEvent
767 {
768  var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
769  mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y),
770  overColumn = [self columnAtPoint:mouseOverLocation];
771 
772  if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
773  {
774  var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
775  spacing = [_tableView intercellSpacing].width,
776  width = [tableColumn width];
777 
778  if (width <= [tableColumn minWidth])
780  else if (width >= [tableColumn maxWidth])
782  else
784  }
785  else
787 }
788 
789 @end // CPTableView (CPTableViewPrivate)
790 
791 var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
792 
794 
795 - (id)initWithCoder:(CPCoder)aCoder
796 {
797  if (self = [super initWithCoder:aCoder])
798  {
799  [self _init];
800  _tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
801  }
802 
803  return self;
804 }
805 
806 - (void)encodeWithCoder:(CPCoder)aCoder
807 {
808  [super encodeWithCoder:aCoder];
809  [aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
810 }
811 
812 @end
813 
815 
820 {
821  return _tableView;
822 }
823 
827 - (void)setTableView:(CPTableView)aValue
828 {
829  _tableView = aValue;
830 }
831 
832 @end
id initWithFrame:(CGRect aFrame)
Definition: CPView.j:323
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:127
BOOL isHidden()
Definition: CALayer.j:597
void addSubview:(CPView aSubview)
Definition: CPView.j:495
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:6312
CPCursor arrowCursor()
Definition: CPCursor.j:192
float pressure()
Definition: CPEvent.j:418
BOOL autoscroll:(CPEvent anEvent)
Definition: CPView.j:2834
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
CGPoint locationInWindow()
Definition: CPEvent.j:287
CPColor whiteColor()
Definition: CPColor.j:358
var CPTableHeaderViewDragTolerance
int width
var CPTableHeaderViewTableViewKey
CGRect bounds()
Definition: CPView.j:1287
CPTrackingActiveInKeyWindow
unsigned modifierFlags()
Definition: CPEvent.j:306
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
void setAcceptsMouseMovedEvents:(BOOL shouldAcceptMouseMovedEvents)
Definition: CPWindow.j:1653
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
CPColor grayColor()
Definition: CPColor.j:314
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPScrollView enclosingScrollView()
Definition: CPView.j:2862
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
CGRect bounds()
Definition: CALayer.j:203
CPWindow window()
Definition: CPView.j:486
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:2142
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:119
void viewDidMoveToWindow()
Definition: CPView.j:856
CPCursor resizeLeftRightCursor()
Definition: CPCursor.j:262
CPTableView tableView
Definition: CPTableView.j:6198
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2490
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3704
CGPoint convertPoint:toView:(CGPoint aPoint, [toView] CPView aView)
Definition: CPView.j:2166
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CPLineBreakByTruncatingTail
Definition: CPControl.j:49
void setNeedsLayout()
Definition: CPView.j:2641
CPTimeInterval timestamp()
Definition: CPEvent.j:314
CPFont boldSystemFontOfSize:(CGSize aSize)
Definition: CPFont.j:294
CGRect convertRect:fromView:(CGRect aRect, [fromView] CPView aView)
Definition: CPView.j:2219
int clickCount()
Definition: CPEvent.j:361
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
var CPTableHeaderViewResizeZone
int windowNumber()
Definition: CPEvent.j:341
CPTextAlignment CPLeftTextAlignment
Definition: CPText.j:49
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1859
void set()
Definition: CPCursor.j:122
Definition: CPEvent.h:2
void removeAllTrackingAreas()
Definition: CPView.j:3498
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
CPCursor resizeLeftCursor()
Definition: CPCursor.j:252
Definition: CPView.j:131
CPTrackingCursorUpdate