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