30 @implementation _CPTableColumnHeaderView :
CPView
32 _CPImageAndTextView _textField;
37 return @"columnHeader";
46 @"text-inset": CGInsetMakeZero(),
50 @"text-shadow-offset": CGSizeMakeZero()
54 - (id)initWithFrame:(CGRect)frame
56 self = [
super initWithFrame:frame];
66 _textField = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
68 [_textField setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
70 [_textField setLineBreakMode:CPLineBreakByTruncatingTail];
71 [_textField setAlignment:CPLeftTextAlignment];
72 [_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
74 [
self addSubview:_textField];
77 - (void)layoutSubviews
79 [
self setBackgroundColor:[
self currentValueForThemeAttribute:@"background-color"]];
81 var inset = [
self currentValueForThemeAttribute:@"text-inset"],
82 bounds = [
self bounds];
84 [_textField setFrame:CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
85 [_textField setTextColor:[
self currentValueForThemeAttribute:@"text-color"]];
86 [_textField setFont:[
self currentValueForThemeAttribute:@"font"]];
87 [_textField setTextShadowColor:[
self currentValueForThemeAttribute:@"text-shadow-color"]];
88 [_textField setTextShadowOffset:[
self currentValueForThemeAttribute:@"text-shadow-offset"]];
89 [_textField setAlignment:[
self currentValueForThemeAttribute:@"text-alignment"]];
90 [_textField setLineBreakMode:[
self currentValueForThemeAttribute:@"line-break-mode"]];
93 - (void)setStringValue:(
CPString)string
95 [_textField setText:string];
100 return [_textField text];
110 [_textField sizeToFit];
113 - (void)setFont:(
CPFont)aFont
115 [
self setValue:aFont forThemeAttribute:@"font"];
120 return [
self currentValueForThemeAttribute:@"font"]
123 - (void)setAlignment:(CPTextAlignment)alignment
125 [
self setValue:alignment forThemeAttribute:@"text-alignment"];
128 - (CPTextAlignment)alignment
130 return [
self currentValueForThemeAttribute:@"text-alignment"]
133 - (void)setLineBreakMode:(CPLineBreakMode)mode
135 [
self setValue:mode forThemeAttribute:@"line-break-mode"];
138 - (CPLineBreakMode)lineBreakMode
140 return [
self currentValueForThemeAttribute:@"line-break-mode"]
143 - (void)setTextColor:(
CPColor)aColor
145 [
self setValue:aColor forThemeAttribute:@"text-color"];
150 return [
self currentValueForThemeAttribute:@"text-color"]
153 - (void)setTextShadowColor:(
CPColor)aColor
155 [
self setValue:aColor forThemeAttribute:@"text-shadow-color"];
160 return [
self currentValueForThemeAttribute:@"text-shadow-color"]
163 - (void)_setIndicatorImage:(
CPImage)anImage
167 [_textField setImage:anImage];
168 [_textField setImagePosition:CPImageRight];
172 [_textField setImagePosition:CPNoImage];
178 var _CPTableColumnHeaderViewStringValueKey =
@"_CPTableColumnHeaderViewStringValueKey",
179 _CPTableColumnHeaderViewFontKey =
@"_CPTableColumnHeaderViewFontKey",
180 _CPTableColumnHeaderViewTextColorKey =
@"_CPTableColumnHeaderViewTextColorKey",
181 _CPTableColumnHeaderViewTextShadowColorKey =
@"_CPTableColumnHeaderViewTextShadowColorKey",
182 _CPTableColumnHeaderViewAlignmentKey =
@"_CPTableColumnHeaderViewAlignmentKey",
183 _CPTableColumnHeaderViewLineBreakModeKey =
@"_CPTableColumnHeaderViewLineBreakModeKey",
184 _CPTableColumnHeaderViewImageKey =
@"_CPTableColumnHeaderViewImageKey";
186 @implementation _CPTableColumnHeaderView (CPCoding)
188 - (id)initWithCoder:(
CPCoder)aCoder
190 if (
self = [super initWithCoder:aCoder])
193 [
self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
194 [
self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
195 [
self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
196 [
self setTextColor:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewTextColorKey]];
197 [
self setTextShadowColor:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewTextShadowColorKey]];
198 [
self setAlignment:[aCoder decodeIntForKey:_CPTableColumnHeaderViewAlignmentKey]];
199 [
self setLineBreakMode:[aCoder decodeIntForKey:_CPTableColumnHeaderViewLineBreakModeKey]];
205 - (void)encodeWithCoder:(
CPCoder)aCoder
207 [
super encodeWithCoder:aCoder];
209 [aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
210 [aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
211 [aCoder encodeObject:[
self font] forKey:_CPTableColumnHeaderViewFontKey];
212 [aCoder encodeObject:[
self textColor] forKey:_CPTableColumnHeaderViewTextColorKey];
213 [aCoder encodeObject:[
self textShadowColor] forKey:_CPTableColumnHeaderViewTextShadowColorKey];
214 [aCoder encodeInt:[
self alignment] forKey:_CPTableColumnHeaderViewAlignmentKey];
215 [aCoder encodeInt:[
self lineBreakMode] forKey:_CPTableColumnHeaderViewLineBreakModeKey];
222 CGPoint _mouseDownLocation;
223 CGPoint _previousTrackingLocation;
226 int _lastDragDestinationColumnIndex;
230 BOOL _isTrackingColumn;
231 BOOL _drawsColumnLines;
233 float _columnOldWidth;
240 return @"tableHeaderRow";
248 @"divider-thickness": 1.0
254 _mouseDownLocation = CGPointMakeZero();
255 _previousTrackingLocation = CGPointMakeZero();
261 _isTrackingColumn = NO;
262 _drawsColumnLines = YES;
264 _columnOldWidth = 0.0;
266 [
self setBackgroundColor:[
self currentValueForThemeAttribute:@"background-color"]];
269 - (id)initWithFrame:(CGRect)aFrame
271 self = [
super initWithFrame:aFrame];
279 - (int)columnAtPoint:(CGPoint)aPoint
281 return [_tableView columnAtPoint:CGPointMake(aPoint.x, aPoint.y)];
284 - (CGRect)headerRectOfColumn:(CPInteger)aColumnIndex
286 var headerRect = CGRectMakeCopy([
self bounds]),
287 columnRect = [_tableView rectOfColumn:aColumnIndex];
289 headerRect.origin.x = CGRectGetMinX(columnRect);
290 headerRect.size.width = CGRectGetWidth(columnRect);
295 - (void)setDrawsColumnLines:(BOOL)aFlag
297 _drawsColumnLines = aFlag;
300 - (BOOL)drawsColumnLines
302 return _drawsColumnLines;
305 - (CGRect)_cursorRectForColumn:(CPInteger)column
307 if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
308 return CGRectMakeZero();
310 var rect = [
self headerRectOfColumn:column];
312 rect.origin.x = CGRectGetMaxX(rect) - 5;
313 rect.size.width = 20;
318 - (void)_setPressedColumn:(CPInteger)column
320 if (_pressedColumn != -1)
322 var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
323 [headerView unsetThemeState:CPThemeStateHighlighted];
328 var headerView = [_tableView._tableColumns[column] headerView];
329 [headerView setThemeState:CPThemeStateHighlighted];
331 if (_tableView._editingCellIndex || _tableView._editingColumn == column)
332 [[
self window] makeFirstResponder:_tableView];
335 _pressedColumn = column;
338 - (void)mouseDown:(
CPEvent)theEvent
340 [
self trackMouse:theEvent];
343 - (void)trackMouse:(
CPEvent)theEvent
345 var type = [theEvent type],
346 currentLocation = [
self convertPoint:[theEvent locationInWindow] fromView:nil];
349 currentLocation.x -= 5.0;
351 var columnIndex = [
self columnAtPoint:currentLocation],
352 shouldResize = [
self shouldResizeTableColumn:columnIndex at:CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
357 [
self stopResizingTableColumn:_activeColumn at:currentLocation];
358 else if ([
self _shouldStopTrackingTableColumn:columnIndex at:currentLocation])
360 [_tableView _didClickTableColumn:columnIndex modifierFlags:[theEvent modifierFlags]];
361 [
self stopTrackingTableColumn:columnIndex at:currentLocation];
363 _isTrackingColumn = NO;
366 [
self _updateResizeCursor:[CPApp currentEvent]];
374 if (columnIndex === -1)
377 _mouseDownLocation = currentLocation;
378 _activeColumn = columnIndex;
380 [_tableView _sendDelegateMouseDownInHeaderOfTableColumn:columnIndex];
383 [
self startResizingTableColumn:columnIndex at:currentLocation];
386 [
self startTrackingTableColumn:columnIndex at:currentLocation];
387 _isTrackingColumn = YES;
393 [
self continueResizingTableColumn:_activeColumn at:currentLocation];
396 if (_activeColumn === columnIndex && CGRectContainsPoint([
self headerRectOfColumn:columnIndex], currentLocation))
398 if (_isTrackingColumn && _pressedColumn !== -1)
400 if (![
self continueTrackingTableColumn:columnIndex at:currentLocation])
403 [
self startTrackingTableColumn:columnIndex at:currentLocation];
405 }
else if (_isTrackingColumn && _pressedColumn !== -1)
406 [
self stopTrackingTableColumn:_activeColumn at:currentLocation];
410 _previousTrackingLocation = currentLocation;
411 [CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
414 - (void)startTrackingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
416 _lastDragDestinationColumnIndex = -1;
417 [
self _setPressedColumn:aColumnIndex];
420 - (BOOL)continueTrackingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
422 if ([
self _shouldDragTableColumn:aColumnIndex at:aPoint])
424 var columnRect = [
self headerRectOfColumn:aColumnIndex],
425 offset = CGPointMakeZero(),
426 view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
427 viewLocation = CGPointMakeZero();
429 viewLocation.x = ( CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
430 viewLocation.y = CGRectGetMinY(columnRect) + offset.y;
432 [
self dragView:view at:viewLocation offset:CGSizeMakeZero() event:[CPApp currentEvent]
441 - (BOOL)_shouldStopTrackingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
443 return _isTrackingColumn && _activeColumn === aColumnIndex &&
444 CGRectContainsPoint([
self headerRectOfColumn:aColumnIndex], aPoint);
447 - (void)stopTrackingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
449 [
self _setPressedColumn:CPNotFound];
450 [
self _updateResizeCursor:[CPApp currentEvent]];
453 - (BOOL)_shouldDragTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
455 return ABS(aPoint.x - _mouseDownLocation.x) >= 10.0 && [_tableView _sendDelegateShouldReorderColumn:aColumnIndex toColumn:-1];
458 - (CGRect)_headerRectOfLastVisibleColumn
460 var tableColumns = [_tableView tableColumns],
461 columnIndex = [tableColumns count];
463 while (columnIndex--)
465 var tableColumn = [tableColumns objectAtIndex:columnIndex];
467 if (![tableColumn isHidden])
468 return [
self headerRectOfColumn:columnIndex];
474 - (void)_constrainDragView:(
CPView)theDragView at:(CGPoint)aPoint
476 var tableColumns = [_tableView tableColumns],
477 lastColumnRect = [
self _headerRectOfLastVisibleColumn],
478 activeColumnRect = [
self headerRectOfColumn:_activeColumn],
479 dragWindow = [theDragView window],
480 frame = [dragWindow frame];
483 frame.origin = [[
self window] convertGlobalToBase:frame.origin];
485 frame.origin = [
self convertPoint:frame.origin fromView:nil];
488 frame.origin.x = MAX(0.0, MIN(CGRectGetMinX(
frame), CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect)));
491 frame.origin.y = CGRectGetMinY(lastColumnRect);
494 frame.origin = [
self convertPoint:frame.origin toView:nil];
496 frame.origin = [[
self window] convertBaseToGlobal:frame.origin];
498 [dragWindow setFrame:frame];
501 - (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
503 if ([_tableView _sendDelegateShouldReorderColumn:aFromIndex toColumn:aToIndex])
505 [_tableView moveColumn:aFromIndex toColumn:aToIndex];
506 _activeColumn = aToIndex;
507 _pressedColumn = _activeColumn;
511 - (void)draggedView:(
CPView)aView beganAt:(CGPoint)aPoint
515 var column = [[_tableView tableColumns] objectAtIndex:_activeColumn];
517 [[column headerView] setHidden:YES];
518 [_tableView _setDraggedColumn:column];
520 [
self setNeedsDisplay:YES];
523 - (void)draggedView:(
CPView)aView movedTo:(CGPoint)aPoint
525 [
self _constrainDragView:aView at:aPoint];
527 var dragWindow = [aView window],
528 dragWindowFrame = [dragWindow frame];
530 var hoverPoint = CGPointCreateCopy(aPoint);
532 if (aPoint.x < _previousTrackingLocation.x)
533 hoverPoint = CGPointMake(CGRectGetMinX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
534 else if (aPoint.x > _previousTrackingLocation.x)
535 hoverPoint = CGPointMake(CGRectGetMaxX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
538 hoverPoint = [[
self window] convertGlobalToBase:hoverPoint];
540 hoverPoint = [
self convertPoint:hoverPoint fromView:nil];
542 var hoveredColumn = [
self columnAtPoint:hoverPoint];
544 if (hoveredColumn !== _lastDragDestinationColumnIndex && hoveredColumn !== -1)
546 var columnRect = [
self headerRectOfColumn:hoveredColumn],
547 columnCenterPoint = [
self convertPoint:CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect)) fromView:self];
549 if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
551 [
self _moveColumn:_activeColumn toColumn:hoveredColumn];
552 _lastDragDestinationColumnIndex = hoveredColumn;
554 else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
556 [
self _moveColumn:_activeColumn toColumn:hoveredColumn];
557 _lastDragDestinationColumnIndex = hoveredColumn;
561 _previousTrackingLocation = aPoint;
564 - (void)draggedView:(
CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
567 _isTrackingColumn = NO;
569 [_tableView _setDraggedColumn:nil];
570 [[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:NO];
571 [
self stopTrackingTableColumn:_activeColumn at:aLocation];
573 [
self setNeedsDisplay:YES];
575 [_tableView _enqueueDraggingViews];
578 - (BOOL)shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
583 if (_isTrackingColumn)
586 return [_tableView allowsColumnResizing] && CGRectContainsPoint([
self _cursorRectForColumn:aColumnIndex], aPoint);
589 - (void)startResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
593 var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
595 [tableColumn setDisableResizingPosting:YES];
596 [_tableView setDisableAutomaticResizing:YES];
599 - (void)continueResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
601 var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
602 newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
604 if (newWidth < [tableColumn minWidth])
606 else if (newWidth > [tableColumn maxWidth])
610 _tableView._lastColumnShouldSnap = NO;
611 [tableColumn setWidth:newWidth];
614 [
self setNeedsLayout];
615 [
self setNeedsDisplay:YES];
619 - (void)stopResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
621 var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
622 [tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
623 [tableColumn setDisableResizingPosting:NO];
624 [_tableView setDisableAutomaticResizing:NO];
629 - (void)_updateResizeCursor:(
CPEvent)theEvent
632 if (![_tableView allowsColumnResizing] || ([theEvent type] ===
CPLeftMouseUp && ![[
self window] acceptsMouseMovedEvents]))
638 var mouseLocation = [
self convertPoint:[theEvent locationInWindow] fromView:nil],
639 mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
640 overColumn = [
self columnAtPoint:mouseOverLocation];
642 if (overColumn >= 0 && CGRectContainsPoint([
self _cursorRectForColumn:overColumn], mouseLocation))
644 var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
645 width = [tableColumn width];
647 if (
width == [tableColumn minWidth])
649 else if (
width == [tableColumn maxWidth])
658 - (void)mouseEntered:(
CPEvent)theEvent
660 [
self _updateResizeCursor:theEvent];
663 - (void)mouseMoved:(
CPEvent)theEvent
665 [
self _updateResizeCursor:theEvent];
668 - (void)mouseExited:(
CPEvent)theEvent
674 - (void)layoutSubviews
676 var tableColumns = [_tableView tableColumns],
677 count = [tableColumns count],
678 lineThickness = [
self currentValueForThemeAttribute:@"divider-thickness"];
680 for (var i = 0; i < count; i++)
682 var column = [tableColumns objectAtIndex:i],
683 headerView = [column headerView],
684 frame = [
self headerRectOfColumn:i];
687 frame.origin.x -= 0.5;
688 frame.size.width -= lineThickness;
689 frame.size.height -= 0.5;
693 [headerView setFrame:frame];
695 if ([headerView superview] !=
self)
696 [
self addSubview:headerView];
699 [
self setBackgroundColor:[
self currentValueForThemeAttribute:@"background-color"]];
702 - (void)drawRect:(CGRect)aRect
704 if (!_tableView || ![
self drawsColumnLines])
708 exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
710 tableColumns = [_tableView tableColumns],
711 exposedTableColumns = _tableView._exposedColumns,
712 firstIndex = [exposedTableColumns firstIndex],
713 exposedRange =
CPMakeRange(firstIndex, [exposedTableColumns lastIndex] - firstIndex + 1),
714 lineThickness = [
self currentValueForThemeAttribute:@"divider-thickness"];
719 [exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:exposedRange];
721 var columnArrayIndex = 0,
722 columnArrayCount = columnsArray.length,
727 for (; columnArrayIndex < columnArrayCount; columnArrayIndex++)
730 var columnIndex = columnsArray[columnArrayIndex],
731 columnToStroke = [
self headerRectOfColumn:columnIndex];
733 columnMaxX = CGRectGetMaxX(columnToStroke);
735 CGContextMoveToPoint(context, FLOOR(columnMaxX) - 0.5 * lineThickness, ROUND(CGRectGetMinY(columnToStroke)));
736 CGContextAddLineToPoint(context, FLOOR(columnMaxX) - 0.5 * lineThickness, ROUND(CGRectGetMaxY(columnToStroke)) - 1.0);
751 var CPTableHeaderViewTableViewKey =
@"CPTableHeaderViewTableViewKey",
752 CPTableHeaderViewDrawsColumnLines =
@"CPTableHeaderViewDrawsColumnLines";
754 @implementation CPTableHeaderView (CPCoding)
756 - (id)initWithCoder:(
CPCoder)aCoder
758 if (
self = [super initWithCoder:aCoder])
761 _tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
764 if ([aCoder containsValueForKey:CPTableHeaderViewDrawsColumnLines])
765 _drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
768 _drawsColumnLines = YES;
769 CPLog.warn(
"The tableview header being decoded is using an old cib. Please run Nib2Cib.");
776 - (void)encodeWithCoder:(
CPCoder)aCoder
778 [
super encodeWithCoder:aCoder];
779 [aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
780 [aCoder encodeBool:_drawsColumnLines forKey:CPTableHeaderViewDrawsColumnLines];
785 @implementation CPTableHeaderView (CPSynthesizedAccessors)