mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
Before this commit, in the method [view convertPoint:aPoint toView:aView] : aPoint was not in the coordinate system of view.
839 lines
24 KiB
Plaintext
839 lines
24 KiB
Plaintext
/*
|
|
* CPTableHeaderView.j
|
|
* AppKit
|
|
*
|
|
* Created by Ross Boucher.
|
|
* Copyright 2009 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
@import "CPTableColumn.j"
|
|
@import "CPView.j"
|
|
@import "CPCursor.j"
|
|
@import "_CPImageAndTextView.j"
|
|
@import "CPTrackingArea.j"
|
|
|
|
@class CPTableView
|
|
|
|
@global CPApp
|
|
|
|
@implementation _CPTableColumnHeaderView : CPView
|
|
{
|
|
_CPImageAndTextView _textField;
|
|
}
|
|
|
|
+ (CPString)defaultThemeClass
|
|
{
|
|
return @"columnHeader";
|
|
}
|
|
|
|
+ (CPDictionary)themeAttributes
|
|
{
|
|
return @{
|
|
@"background-color": [CPNull null],
|
|
@"text-alignment": CPLeftTextAlignment,
|
|
@"line-break-mode": CPLineBreakByTruncatingTail,
|
|
@"text-inset": CGInsetMakeZero(),
|
|
@"text-color": [CPNull null],
|
|
@"font": [CPNull null],
|
|
@"text-shadow-color": [CPNull null],
|
|
@"text-shadow-offset": CGSizeMakeZero()
|
|
};
|
|
}
|
|
|
|
- (id)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
|
|
if (self)
|
|
[self _init];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)_init
|
|
{
|
|
_textField = [[_CPImageAndTextView alloc] initWithFrame:
|
|
CGRectMake(5.0, 0.0, CGRectGetWidth([self bounds]) - 10.0, CGRectGetHeight([self bounds]))];
|
|
|
|
[_textField setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
|
|
|
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
|
[_textField setTextColor:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]];
|
|
[_textField setFont:[CPFont boldSystemFontOfSize:12.0]];
|
|
[_textField setAlignment:CPLeftTextAlignment];
|
|
[_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
|
|
[_textField setTextShadowColor:[CPColor whiteColor]];
|
|
[_textField setTextShadowOffset:CGSizeMake(0,1)];
|
|
|
|
[self addSubview:_textField];
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
|
|
|
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
|
bounds = [self bounds];
|
|
|
|
[_textField setFrame:CGRectMake(inset.left, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
|
|
[_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
|
[_textField setFont:[self currentValueForThemeAttribute:@"font"]];
|
|
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
|
[_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
|
[_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
|
|
[_textField setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
|
|
}
|
|
|
|
- (void)setStringValue:(CPString)string
|
|
{
|
|
[_textField setText:string];
|
|
}
|
|
|
|
- (CPString)stringValue
|
|
{
|
|
return [_textField text];
|
|
}
|
|
|
|
- (void)textField
|
|
{
|
|
return _textField;
|
|
}
|
|
|
|
- (void)sizeToFit
|
|
{
|
|
[_textField sizeToFit];
|
|
}
|
|
|
|
- (void)setFont:(CPFont)aFont
|
|
{
|
|
[self setValue:aFont forThemeAttribute:@"font"];
|
|
}
|
|
|
|
- (CPFont)font
|
|
{
|
|
return [self currentValueForThemeAttribute:@"font"];
|
|
}
|
|
|
|
- (void)setAlignment:(CPTextAlignment)alignment
|
|
{
|
|
[self setValue:alignment forThemeAttribute:@"text-alignment"];
|
|
}
|
|
|
|
- (CPTextAlignment)alignment
|
|
{
|
|
return [self currentValueForThemeAttribute:@"text-alignment"];
|
|
}
|
|
|
|
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
|
{
|
|
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
|
}
|
|
|
|
- (CPLineBreakMode)lineBreakMode
|
|
{
|
|
return [self currentValueForThemeAttribute:@"line-break-mode"];
|
|
}
|
|
|
|
- (void)setTextColor:(CPColor)aColor
|
|
{
|
|
[self setValue:aColor forThemeAttribute:@"text-color"];
|
|
}
|
|
|
|
- (CPColor)textColor
|
|
{
|
|
return [self currentValueForThemeAttribute:@"text-color"];
|
|
}
|
|
|
|
- (void)setTextShadowColor:(CPColor)aColor
|
|
{
|
|
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
|
}
|
|
|
|
- (CPColor)textShadowColor
|
|
{
|
|
return [self currentValueForThemeAttribute:@"text-shadow-color"];
|
|
}
|
|
|
|
- (void)_setIndicatorImage:(CPImage)anImage
|
|
{
|
|
if (anImage)
|
|
{
|
|
[_textField setImage:anImage];
|
|
[_textField setImagePosition:CPImageRight];
|
|
}
|
|
else
|
|
{
|
|
[_textField setImagePosition:CPNoImage];
|
|
}
|
|
}
|
|
|
|
- (CPImage)_indicatorImage
|
|
{
|
|
return [_textField imagePosition] === CPNoImage ? nil : [_textField image];
|
|
}
|
|
|
|
- (void)drawRect:(CGRect)aRect
|
|
{
|
|
var bounds = [self bounds];
|
|
|
|
if (!CGRectIntersectsRect(aRect, bounds))
|
|
return;
|
|
|
|
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
|
maxX = CGRectGetMaxX(bounds) - 0.5;
|
|
|
|
CGContextSetLineWidth(context, 1);
|
|
CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
|
|
|
|
CGContextBeginPath(context);
|
|
|
|
CGContextMoveToPoint(context, maxX, CGRectGetMinY(bounds));
|
|
CGContextAddLineToPoint(context, maxX, CGRectGetMaxY(bounds));
|
|
|
|
CGContextStrokePath(context);
|
|
}
|
|
|
|
@end
|
|
|
|
var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
|
|
_CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
|
|
_CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey",
|
|
_CPTableColumnHeaderViewIsDraggingKey = @"_CPTableColumnHeaderViewIsDraggingKey";
|
|
|
|
@implementation _CPTableColumnHeaderView (CPCoding)
|
|
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
if (self = [super initWithCoder:aCoder])
|
|
{
|
|
[self _init];
|
|
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
|
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
|
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
[super encodeWithCoder:aCoder];
|
|
|
|
[aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
|
|
[aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
|
|
[aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
|
|
}
|
|
|
|
@end
|
|
|
|
CPTableHeaderViewDragColumnHeaderTag = 1;
|
|
|
|
var CPTableHeaderViewResizeZone = 3.0,
|
|
CPTableHeaderViewDragTolerance = 10.0;
|
|
|
|
@implementation CPTableHeaderView : CPView
|
|
{
|
|
CGPoint _mouseDownLocation;
|
|
CGPoint _columnMouseDownLocation;
|
|
CGPoint _mouseEnterExitLocation;
|
|
CGPoint _previousTrackingLocation;
|
|
|
|
int _activeColumn;
|
|
int _pressedColumn;
|
|
|
|
BOOL _isResizing;
|
|
BOOL _isDragging;
|
|
BOOL _canDragColumn;
|
|
|
|
CPView _columnDragView;
|
|
CPView _columnDragHeaderView;
|
|
CPView _columnDragClipView;
|
|
|
|
float _columnOldWidth;
|
|
|
|
CPTableView _tableView @accessors(property=tableView);
|
|
}
|
|
|
|
+ (CPString)defaultThemeClass
|
|
{
|
|
return @"tableHeaderRow";
|
|
}
|
|
|
|
+ (CPDictionary)themeAttributes
|
|
{
|
|
return @{
|
|
@"background-color": [CPNull null],
|
|
@"divider-color": [CPColor grayColor],
|
|
@"divider-thickness": 1.0
|
|
};
|
|
}
|
|
|
|
- (void)_init
|
|
{
|
|
_mouseDownLocation = CGPointMakeZero();
|
|
_columnMouseDownLocation = CGPointMakeZero();
|
|
_mouseEnterExitLocation = CGPointMakeZero();
|
|
_previousTrackingLocation = CGPointMakeZero();
|
|
|
|
_activeColumn = -1;
|
|
_pressedColumn = -1;
|
|
|
|
_isResizing = NO;
|
|
_isDragging = NO;
|
|
_canDragColumn = NO;
|
|
|
|
_columnOldWidth = 0.0;
|
|
|
|
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
|
}
|
|
|
|
- (id)initWithFrame:(CGRect)aFrame
|
|
{
|
|
self = [super initWithFrame:aFrame];
|
|
|
|
if (self)
|
|
{
|
|
[self _init];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
// Checking Altered Columns
|
|
|
|
- (CPInteger)draggedColumn
|
|
{
|
|
return _isDragging ? _activeColumn : -1;
|
|
}
|
|
|
|
- (float)draggedDistance
|
|
{
|
|
if (_isDragging)
|
|
return (CGRectGetMinX(_columnDragClipView) - _columnMouseDownLocation.x);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
- (CPInteger)resizedColumn
|
|
{
|
|
if (_isResizing)
|
|
return _activeColumn;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
// Utility Methods
|
|
|
|
- (CPInteger)columnAtPoint:(CGPoint)aPoint
|
|
{
|
|
return [_tableView columnAtPoint:aPoint];
|
|
}
|
|
|
|
- (CGRect)headerRectOfColumn:(CPInteger)aColumnIndex
|
|
{
|
|
var headerRect = [self bounds],
|
|
columnRect = [_tableView rectOfColumn:aColumnIndex];
|
|
|
|
headerRect.origin.x = CGRectGetMinX(columnRect);
|
|
headerRect.size.width = CGRectGetWidth(columnRect);
|
|
|
|
return headerRect;
|
|
}
|
|
|
|
// CPView Overrides
|
|
|
|
- (void)viewDidMoveToWindow
|
|
{
|
|
[super viewDidMoveToWindow];
|
|
|
|
[[self window] setAcceptsMouseMovedEvents:YES];
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
var tableColumns = [_tableView tableColumns],
|
|
count = [tableColumns count];
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var column = [tableColumns objectAtIndex:i],
|
|
headerView = [column headerView],
|
|
frame = [self headerRectOfColumn:i];
|
|
|
|
[headerView setFrame:frame];
|
|
|
|
if ([headerView superview] != self)
|
|
[self addSubview:headerView];
|
|
}
|
|
}
|
|
|
|
// CPResponder Overrides
|
|
|
|
- (void)mouseDown:(CPEvent)theEvent
|
|
{
|
|
var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
|
adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
|
|
columnIndex = [self columnAtPoint:adjustedLocation];
|
|
|
|
if (columnIndex === -1)
|
|
return;
|
|
|
|
_mouseDownLocation = currentLocation;
|
|
_activeColumn = columnIndex;
|
|
_canDragColumn = YES;
|
|
|
|
[_tableView _sendDelegateMouseDownInHeaderOfTableColumn:columnIndex];
|
|
|
|
if ([self _shouldResizeTableColumn:columnIndex at:currentLocation])
|
|
[self _startResizingTableColumn:columnIndex at:currentLocation];
|
|
else
|
|
[self _setPressedColumn:columnIndex];
|
|
}
|
|
|
|
- (void)mouseDragged:(CPEvent)theEvent
|
|
{
|
|
var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
|
adjustedLocation = CGPointMake(MAX(currentLocation.x - CPTableHeaderViewResizeZone, 0.0), currentLocation.y),
|
|
columnIndex = [self columnAtPoint:adjustedLocation];
|
|
|
|
if (_isResizing)
|
|
{
|
|
[self _autoscroll:theEvent localLocation:currentLocation];
|
|
[self _continueResizingTableColumn:_activeColumn at:currentLocation];
|
|
}
|
|
else if (_isDragging)
|
|
{
|
|
[self _autoscroll:theEvent localLocation:currentLocation];
|
|
[self _dragTableColumn:_activeColumn to:currentLocation];
|
|
}
|
|
else // tracking a press, could become a drag
|
|
{
|
|
if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
|
|
{
|
|
if ([self _shouldDragTableColumn:columnIndex at:currentLocation])
|
|
[self _startDraggingTableColumn:columnIndex at:currentLocation];
|
|
else
|
|
[self _setPressedColumn:_activeColumn];
|
|
}
|
|
else
|
|
[self _setPressedColumn:-1];
|
|
}
|
|
}
|
|
|
|
- (void)mouseUp:(CPEvent)theEvent
|
|
{
|
|
if (_isResizing)
|
|
{
|
|
[self _stopResizingTableColumn:_activeColumn];
|
|
}
|
|
else if (_isDragging)
|
|
{
|
|
[self _stopDraggingTableColumn:_activeColumn];
|
|
}
|
|
else if (_activeColumn != -1)
|
|
{
|
|
var currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
|
|
|
|
if (CGRectContainsPoint([self headerRectOfColumn:_activeColumn], currentLocation))
|
|
[_tableView _didClickTableColumn:_activeColumn modifierFlags:[theEvent modifierFlags]];
|
|
}
|
|
|
|
[self _setPressedColumn:-1];
|
|
[self _updateResizeCursor:[CPApp currentEvent]];
|
|
|
|
_activeColumn = -1;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPTableHeaderView (CPTrackingArea)
|
|
{
|
|
CPMutableArray _tableHeaderViewTrackingAreas;
|
|
}
|
|
|
|
- (void)updateTrackingAreas
|
|
{
|
|
if (_tableHeaderViewTrackingAreas)
|
|
{
|
|
for (var i = 0, count = [_tableHeaderViewTrackingAreas count]; i < count; i++)
|
|
[self removeTrackingArea:_tableHeaderViewTrackingAreas[i]];
|
|
|
|
_tableHeaderViewTrackingAreas = nil;
|
|
}
|
|
|
|
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
|
|
|
if (!_tableView)
|
|
return;
|
|
|
|
_tableHeaderViewTrackingAreas = @[];
|
|
|
|
for (var i = 0; i < _tableView._tableColumns.length; i++)
|
|
{
|
|
[_tableHeaderViewTrackingAreas addObject:[[CPTrackingArea alloc] initWithRect:[self _cursorRectForColumn:i]
|
|
options:options
|
|
owner:self
|
|
userInfo:nil]];
|
|
|
|
[self addTrackingArea:_tableHeaderViewTrackingAreas[i]];
|
|
}
|
|
|
|
[super updateTrackingAreas];
|
|
}
|
|
|
|
- (void)cursorUpdate:(CPEvent)anEvent
|
|
{
|
|
[self _updateResizeCursor:anEvent];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPTableHeaderView (CPTableHeaderViewPrivate)
|
|
|
|
- (CGRect)_cursorRectForColumn:(CPInteger)column
|
|
{
|
|
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
|
|
return CGRectMakeZero();
|
|
|
|
var rect = [self headerRectOfColumn:column];
|
|
|
|
rect.origin.x = (CGRectGetMaxX(rect) - CPTableHeaderViewResizeZone) - 1.0;
|
|
rect.size.width = (CPTableHeaderViewResizeZone * 2.0) + 1.0; // + 1 for resize line
|
|
|
|
return rect;
|
|
}
|
|
|
|
- (void)_setPressedColumn:(CPInteger)column
|
|
{
|
|
if (_pressedColumn === column)
|
|
return;
|
|
|
|
if (_pressedColumn != -1)
|
|
{
|
|
var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
|
|
[headerView unsetThemeState:CPThemeStateHighlighted];
|
|
}
|
|
|
|
if (column != -1)
|
|
{
|
|
var headerView = [_tableView._tableColumns[column] headerView];
|
|
[headerView setThemeState:CPThemeStateHighlighted];
|
|
}
|
|
else
|
|
{
|
|
// Once the mouse leaves the pressed column, it can no longer drag
|
|
_canDragColumn = NO;
|
|
}
|
|
|
|
_pressedColumn = column;
|
|
}
|
|
|
|
- (BOOL)_shouldDragTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
|
{
|
|
return _canDragColumn && [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= CPTableHeaderViewDragTolerance;
|
|
}
|
|
|
|
- (void)_autoscroll:(CPEvent)theEvent localLocation:(CGPoint)theLocation
|
|
{
|
|
// Constrain the y coordinate so we don't autoscroll vertically
|
|
var constrainedLocation = CGPointMake(theLocation.x, CGRectGetMaxY([self frame])),
|
|
constrainedEvent = [CPEvent mouseEventWithType:CPLeftMouseDragged
|
|
location:[self convertPoint:constrainedLocation toView:nil]
|
|
modifierFlags:[theEvent modifierFlags]
|
|
timestamp:[theEvent timestamp]
|
|
windowNumber:[theEvent windowNumber]
|
|
context:nil
|
|
eventNumber:0
|
|
clickCount:[theEvent clickCount]
|
|
pressure:[theEvent pressure]];
|
|
|
|
[self autoscroll:constrainedEvent];
|
|
[_tableView autoscroll:constrainedEvent];
|
|
}
|
|
|
|
- (CGRect)_headerRectOfLastVisibleColumn
|
|
{
|
|
var tableColumns = [_tableView tableColumns],
|
|
columnIndex = [tableColumns count];
|
|
|
|
while (columnIndex--)
|
|
{
|
|
var tableColumn = [tableColumns objectAtIndex:columnIndex];
|
|
|
|
if (![tableColumn isHidden])
|
|
return [self headerRectOfColumn:columnIndex];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (CGPoint)_constrainDragPoint:(CGPoint)aPoint
|
|
{
|
|
// This effectively clamps the value between the minimum and maximum
|
|
var visibleRect = [_tableView visibleRect],
|
|
lastColumnRect = [self _headerRectOfLastVisibleColumn],
|
|
activeColumnRect = [self headerRectOfColumn:_activeColumn],
|
|
maxX = CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect) - CGRectGetMinX(visibleRect),
|
|
point = CGPointMake(MAX(MIN(aPoint.x, maxX), -CGRectGetMinX(visibleRect)), aPoint.y);
|
|
|
|
return point;
|
|
}
|
|
|
|
- (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
|
|
{
|
|
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
|
_activeColumn = aToIndex;
|
|
_pressedColumn = _activeColumn;
|
|
|
|
[_tableView _setDraggedColumn:_activeColumn];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (BOOL)isDragging
|
|
{
|
|
return _isDragging;
|
|
}
|
|
|
|
- (void)_startDraggingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
|
{
|
|
_isDragging = YES;
|
|
_columnDragView = [_tableView _dragViewForColumn:aColumnIndex];
|
|
_previousTrackingLocation = aPoint;
|
|
|
|
// Create a new clip view for the drag view that clips to the header + visible content
|
|
var headerHeight = CGRectGetHeight([self frame]),
|
|
scrollView = [self enclosingScrollView],
|
|
contentFrame = [[scrollView contentView] frame];
|
|
|
|
contentFrame.origin.y -= headerHeight;
|
|
contentFrame.size.height += headerHeight;
|
|
|
|
_columnDragClipView = [[CPView alloc] initWithFrame:contentFrame];
|
|
|
|
[_columnDragClipView addSubview:_columnDragView];
|
|
|
|
// Insert the clip view above the table header (and content)
|
|
[scrollView addSubview:_columnDragClipView positioned:CPWindowAbove relativeTo:self];
|
|
|
|
// Hide the underlying column header subviews, we just want to draw the chrome
|
|
var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
|
|
|
|
[[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
|
|
|
|
// The underlying column header shows normal state
|
|
[headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
|
|
|
// Keep track of the location within the column header where the original mousedown occurred
|
|
_columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
|
|
|
|
_columnMouseDownLocation = [self convertPoint:_mouseDownLocation toView:_columnDragHeaderView];
|
|
|
|
[_tableView _setDraggedColumn:aColumnIndex];
|
|
|
|
[[CPCursor closedHandCursor] set];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)_dragTableColumn:(CPInteger)aColumnIndex to:(CGPoint)aPoint
|
|
{
|
|
var delta = aPoint.x - _previousTrackingLocation.x,
|
|
columnPoint = [_columnDragHeaderView convertPoint:aPoint fromView:self];
|
|
|
|
// Only move if the mouse is past the original click point in the direction of movement
|
|
if ((delta > 0 && columnPoint.x > _columnMouseDownLocation.x) || (delta < 0 && columnPoint.x < _columnMouseDownLocation.x))
|
|
{
|
|
var dragFrame = [_columnDragView frame],
|
|
newOrigin = [self _constrainDragPoint:CGPointMake(CGRectGetMinX(dragFrame) + delta, CGRectGetMinY(dragFrame))];
|
|
|
|
[_columnDragView setFrameOrigin:newOrigin];
|
|
|
|
// When the edge of the dragged column passes the midpoint of an adjacent column, they swap
|
|
var hoverPoint = CGPointMakeCopy(aPoint);
|
|
|
|
// The drag frame is in content view coordinates, we need it to be in our coordinates
|
|
dragFrame = [self convertRect:dragFrame fromView:[_columnDragView superview]];
|
|
|
|
if (delta > 0)
|
|
hoverPoint.x = CGRectGetMaxX(dragFrame);
|
|
else
|
|
hoverPoint.x = CGRectGetMinX(dragFrame);
|
|
|
|
var hoveredColumn = [self columnAtPoint:hoverPoint];
|
|
|
|
if (hoveredColumn !== -1)
|
|
{
|
|
var columnRect = [self headerRectOfColumn:hoveredColumn],
|
|
columnCenterPoint = CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect));
|
|
|
|
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
|
|
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
|
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
|
|
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
|
}
|
|
}
|
|
|
|
_previousTrackingLocation = aPoint;
|
|
}
|
|
|
|
- (void)_stopDraggingTableColumn:(CPInteger)aColumnIndex
|
|
{
|
|
_isDragging = NO;
|
|
|
|
[_columnDragClipView removeFromSuperview];
|
|
[_tableView _setDraggedColumn:-1];
|
|
|
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
|
headerView = [tableColumn headerView];
|
|
|
|
[[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
|
|
|
|
if (_tableView._draggedColumnIsSelected)
|
|
[headerView setThemeState:CPThemeStateSelected];
|
|
|
|
[_tableView _reloadDataViews];
|
|
[[_tableView headerView] setNeedsLayout];
|
|
|
|
[[CPCursor arrowCursor] set];
|
|
[self updateTrackingAreas];
|
|
|
|
[_tableView _sendDelegateDidDragTableColumn:tableColumn];
|
|
}
|
|
|
|
- (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
|
{
|
|
if (_isResizing)
|
|
return YES;
|
|
|
|
return [_tableView allowsColumnResizing] && CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
|
|
}
|
|
|
|
- (void)_startResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
|
{
|
|
_isResizing = YES;
|
|
_previousTrackingLocation = aPoint;
|
|
_activeColumn = aColumnIndex;
|
|
|
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
|
|
|
_columnOldWidth = [tableColumn width];
|
|
|
|
[tableColumn setDisableResizingPosting:YES];
|
|
[_tableView setDisableAutomaticResizing:YES];
|
|
}
|
|
|
|
- (void)_continueResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
|
{
|
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
|
delta = aPoint.x - _previousTrackingLocation.x,
|
|
spacing = [_tableView intercellSpacing].width,
|
|
newWidth = [tableColumn width] + spacing + delta,
|
|
minWidth = [tableColumn minWidth] + spacing,
|
|
maxWidth = [tableColumn maxWidth] + spacing;
|
|
|
|
if (newWidth <= minWidth)
|
|
[[CPCursor resizeRightCursor] set];
|
|
else if (newWidth >= maxWidth)
|
|
[[CPCursor resizeLeftCursor] set];
|
|
else
|
|
[[CPCursor resizeLeftRightCursor] set];
|
|
|
|
var columnRect = [_tableView rectOfColumn:aColumnIndex],
|
|
columnWidth = CGRectGetWidth(columnRect);
|
|
|
|
if ((delta > 0 && columnWidth == maxWidth) || (delta < 0 && columnWidth == minWidth))
|
|
return;
|
|
|
|
var columnMinX = CGRectGetMinX(columnRect),
|
|
columnMaxX = CGRectGetMaxX(columnRect);
|
|
|
|
if ((delta > 0 && aPoint.x > columnMaxX) || (delta < 0 && aPoint.x < columnMaxX))
|
|
{
|
|
[tableColumn setWidth:newWidth - spacing];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
_previousTrackingLocation = aPoint;
|
|
}
|
|
|
|
- (void)_stopResizingTableColumn:(CPInteger)aColumnIndex
|
|
{
|
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
|
|
|
if ([tableColumn width] != _columnOldWidth)
|
|
{
|
|
[_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth];
|
|
[self updateTrackingAreas];
|
|
}
|
|
|
|
[tableColumn setDisableResizingPosting:NO];
|
|
[_tableView setDisableAutomaticResizing:NO];
|
|
|
|
_isResizing = NO;
|
|
}
|
|
|
|
- (void)_updateResizeCursor:(CPEvent)theEvent
|
|
{
|
|
var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
|
mouseOverLocation = CGPointMake(MAX(mouseLocation.x - CPTableHeaderViewResizeZone, 0.0), mouseLocation.y),
|
|
overColumn = [self columnAtPoint:mouseOverLocation];
|
|
|
|
if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
|
|
{
|
|
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
|
|
spacing = [_tableView intercellSpacing].width,
|
|
width = [tableColumn width];
|
|
|
|
if (width <= [tableColumn minWidth])
|
|
[[CPCursor resizeRightCursor] set];
|
|
else if (width >= [tableColumn maxWidth])
|
|
[[CPCursor resizeLeftCursor] set];
|
|
else
|
|
[[CPCursor resizeLeftRightCursor] set];
|
|
}
|
|
else
|
|
[[CPCursor arrowCursor] set];
|
|
}
|
|
|
|
@end // CPTableView (CPTableViewPrivate)
|
|
|
|
var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
|
|
|
|
@implementation CPTableHeaderView (CPCoding)
|
|
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
if (self = [super initWithCoder:aCoder])
|
|
{
|
|
[self _init];
|
|
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
[super encodeWithCoder:aCoder];
|
|
[aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
|
|
}
|
|
|
|
@end
|