libs-gui/Source/NSTableHeaderView.m
Adam Fedor 6be9174933 Include <> -> ""
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@16917 72102866-910b-0410-8b05-ffd578937521
2003-06-13 15:01:12 +00:00

883 lines
21 KiB
Objective-C

/** <title>NSTableHeaderView</title>
Copyright (C) 1999 Free Software Foundation, Inc.
Author: Nicola Pero <n.pero@mi.flashnet.it>
Date: December 1999
First actual coding.
Author: Nicola Pero <nicola@brainstorm.co.uk>
Date: August 2000, Semptember 2000
Selection and resizing of Columns.
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Foundation/NSArray.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSRunLoop.h>
#include "AppKit/NSTableHeaderCell.h"
#include "AppKit/NSTableHeaderView.h"
#include "AppKit/NSTableColumn.h"
#include "AppKit/NSTableView.h"
#include "AppKit/NSWindow.h"
#include "AppKit/NSEvent.h"
#include "AppKit/NSApplication.h"
#include "AppKit/NSColor.h"
#include "AppKit/NSScrollView.h"
#include "AppKit/DPSOperators.h"
#include "AppKit/NSGraphics.h"
@interface NSTableView (GNUstepPrivate)
- (void) _userResizedTableColumn: (int)index
width: (float)width;
- (float *) _columnOrigins;
- (void) _mouseDownInHeaderOfTableColumn: (NSTableColumn *)tc;
- (void) _didClickTableColumn: (NSTableColumn *)tc;
@end
@implementation NSTableHeaderView
/*
*
* Class methods
*
*/
+ (void) initialize
{
if (self == [NSTableColumn class])
[self setVersion: 1];
}
/*
*
* Instance methods
*
*/
/*
* Initializes an instance
*/
// TODO: Remove this method, if not really needed
- (NSTableHeaderView*)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame: frameRect];
_tableView = nil;
_resizedColumn = -1;
return self;
}
/*
* Setting the table view
*/
- (void)setTableView: (NSTableView*)aTableView
{
// We do not RETAIN aTableView but aTableView is supposed
// to RETAIN us.
_tableView = aTableView;
}
- (NSTableView*)tableView
{
return _tableView;
}
/*
* Checking altered columns
*/
- (int) draggedColumn
{
// TODO
return -1;
}
- (float) draggedDistance
{
// TODO
return -1;
}
- (int) resizedColumn
{
return _resizedColumn;
}
/*
* Utility methods
*/
- (int) columnAtPoint: (NSPoint)aPoint
{
if (_tableView == nil)
return -1;
/* Ask to the tableview, which is caching geometry info */
aPoint = [self convertPoint: aPoint toView: _tableView];
aPoint.y = [_tableView bounds].origin.y;
return [_tableView columnAtPoint: aPoint];
}
- (NSRect)headerRectOfColumn: (int)columnIndex
{
NSRect rect;
if (_tableView == nil)
return NSZeroRect;
/* Ask to the tableview, which is caching geometry info */
rect = [_tableView rectOfColumn: columnIndex];
rect = [self convertRect: rect fromView: _tableView];
rect.origin.y = _bounds.origin.y;
rect.size.height = _bounds.size.height;
return rect;
}
/*
* Overidden Methods
*/
- (void)drawRect: (NSRect)aRect
{
NSArray *columns;
int firstColumnToDraw;
int lastColumnToDraw;
NSRect drawingRect;
NSTableColumn *column;
NSTableColumn *highlightedTableColumn;
float width;
int i;
NSCell *cell;
if (_tableView == nil)
return;
firstColumnToDraw = [self columnAtPoint: NSMakePoint (aRect.origin.x,
aRect.origin.y)];
if (firstColumnToDraw == -1)
firstColumnToDraw = 0;
lastColumnToDraw = [self columnAtPoint: NSMakePoint (NSMaxX (aRect),
aRect.origin.y)];
if (lastColumnToDraw == -1)
lastColumnToDraw = [_tableView numberOfColumns] - 1;
drawingRect = [self headerRectOfColumn: firstColumnToDraw];
drawingRect.origin.y++;
drawingRect.size.height--;
columns = [_tableView tableColumns];
highlightedTableColumn = [_tableView highlightedTableColumn];
for (i = firstColumnToDraw; i < lastColumnToDraw; i++)
{
column = [columns objectAtIndex: i];
width = [column width];
drawingRect.size.width = width;
cell = [column headerCell];
if ((column == highlightedTableColumn)
|| [_tableView isColumnSelected: i])
{
[cell setHighlighted: YES];
}
else
{
[cell setHighlighted: NO];
}
[cell drawWithFrame: drawingRect
inView: self];
drawingRect.origin.x += width;
}
if (lastColumnToDraw == [_tableView numberOfColumns] - 1)
{
column = [columns objectAtIndex: lastColumnToDraw];
width = [column width] - 1;
drawingRect.size.width = width;
cell = [column headerCell];
if ((column == highlightedTableColumn)
|| [_tableView isColumnSelected: lastColumnToDraw])
{
[cell setHighlighted: YES];
}
else
{
[cell setHighlighted: NO];
}
[cell drawWithFrame: drawingRect
inView: self];
drawingRect.origin.x += width;
}
else
{
column = [columns objectAtIndex: lastColumnToDraw];
width = [column width];
drawingRect.size.width = width;
cell = [column headerCell];
if ((column == highlightedTableColumn)
|| [_tableView isColumnSelected: lastColumnToDraw])
{
[cell setHighlighted: YES];
}
else
{
[cell setHighlighted: NO];
}
[cell drawWithFrame: drawingRect
inView: self];
drawingRect.origin.x += width;
}
{
NSGraphicsContext *ctxt = GSCurrentContext();
DPSsetgray(ctxt, NSBlack);
DPSrectfill(ctxt,_bounds.origin.x, _bounds.origin.y,
_bounds.size.width, 1.);
DPSrectfill(ctxt, NSMaxX(_bounds)-1., NSMinY(_bounds),
1., _bounds.size.height);
}
}
- (void) mouseDown: (NSEvent*)event
{
NSPoint location = [event locationInWindow];
int clickCount;
int columnIndex;
NSTableColumn *currentColumn;
clickCount = [event clickCount];
/*
if (clickCount > 2)
{
return;
}
*/
location = [self convertPoint: location fromView: nil];
columnIndex = [self columnAtPoint: location];
if (columnIndex == -1)
{
return;
}
currentColumn = [[_tableView tableColumns]
objectAtIndex: columnIndex];
if (clickCount == 2)
{
[_tableView _sendDoubleActionForColumn: columnIndex];
// return;
}
// if (clickCount == 1)
{
NSRect rect = [self headerRectOfColumn: columnIndex];
/* Safety check */
if (_resizedColumn != -1)
{
NSLog (@"Bug: starting resizing of column while already resizing!");
_resizedColumn = -1;
}
if ([_tableView allowsColumnResizing])
{
/* Start resizing if the mouse is down on the bounds of a column. */
if (location.x >= NSMaxX (rect) - 1)
{
if (columnIndex != ([_tableView numberOfColumns]))
{
_resizedColumn = columnIndex;
}
else
{
NSLog(@"ohoh");
}
}
else if (location.x <= NSMinX (rect) + 2)
{
if (columnIndex > 0)
{
_resizedColumn = columnIndex - 1;
}
}
}
/* Resizing */
if (_resizedColumn != -1)
{
/* Width of the highlighted area. */
const float divWidth = 4;
/* Dragging limits */
float minCoord;
float maxCoord;
float minAbsCoord;
float maxAbsCoord;
float minVisCoord;
float maxVisCoord;
NSRect tvRect;
NSPoint unconverted;
NSArray *columns;
/* Column on the left of resizing bound */
NSTableColumn *column;
NSRect rectLow = [self headerRectOfColumn: _resizedColumn];
/* Old highlighted rect, used to avoid useless redrawing */
NSRect oldRect = NSZeroRect;
/* Current highlighted rect */
NSRect r;
/* Mouse position */
float p;
float q;
BOOL outside = NO;
/* YES if some highlighting was done and needs to be undone */
BOOL lit = NO;
/* YES if some dragging was actually done - to avoid
retiling/redrawing the table if no dragging is done */
BOOL dragged = NO;
NSEvent *e;
NSDate *farAway = [NSDate distantFuture];
unsigned int eventMask = NSLeftMouseUpMask | NSLeftMouseDraggedMask
| NSPeriodicMask;
/* Determine dragging limits, constrained to visible rect */
rect = [self visibleRect];
minVisCoord = MAX (NSMinX (rectLow), NSMinX (rect)) + divWidth;
maxVisCoord = NSMaxX (rect) - divWidth;
/* Then constrain to minimum and maximum column width if any */
columns = [_tableView tableColumns];
/* Column at the left */
column = [columns objectAtIndex: _resizedColumn];
if ([column isResizable] == NO)
{
_resizedColumn = -1;
return;
}
/* We use p as a temporary variable for a while */
minAbsCoord = NSMinX (rectLow) + [column minWidth];
maxAbsCoord = NSMinX (rectLow) + [column maxWidth];
minCoord = MAX (minAbsCoord, minVisCoord);
maxCoord = MIN (maxAbsCoord, maxVisCoord);
/* Do we need to check that we already fit into this area ?
We should */
[self lockFocus];
[[NSRunLoop currentRunLoop] limitDateForMode: NSEventTrackingRunLoopMode];
[[NSColor lightGrayColor] set];
r.size.width = divWidth;
r.size.height = NSHeight (rect);
r.origin.y = NSMinY (rect);
[NSEvent startPeriodicEventsAfterDelay: 0.05 withPeriod: 0.05];
e = [NSApp nextEventMatchingMask: eventMask
untilDate: farAway
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
/* Safety assignment to make sure p is never left
unitialized - should make no difference with current code
but anyway */
p = NSMaxX (rectLow);
while ([e type] != NSLeftMouseUp)
{
if ([e type] != NSPeriodic)
{
dragged = YES;
unconverted = [e locationInWindow];
p = [self convertPoint: unconverted fromView: nil].x;
q = p;
if (p > maxVisCoord || p < minVisCoord)
{
outside = YES;
}
else
{
outside = NO;
}
if (p < minCoord)
{
p = minCoord;
}
else if (p > maxCoord)
{
p = maxCoord;
}
r.origin.x = p - (divWidth / 2.);
if (!outside && NSEqualRects(r, oldRect) == NO)
{
if (lit == YES)
{
NSHighlightRect (oldRect);
}
NSHighlightRect (r);
[_window flushWindow];
lit = YES;
oldRect = r;
}
}
else
{
if (outside)
{
q = [self convertPoint: unconverted
fromView: nil].x;
if (lit)
{
NSHighlightRect (oldRect);
[_window flushWindow];
lit = NO;
}
tvRect = [_tableView visibleRect];
if (q > maxVisCoord)
{
if (q > maxAbsCoord + 5)
q = maxAbsCoord + 5;
tvRect.origin.x += (q - maxVisCoord)/2;
}
else if (q < minVisCoord)
{
if (q < minAbsCoord - 5)
q = minAbsCoord - 5;
tvRect.origin.x += (q - minVisCoord)/2;
}
else // TODO remove this condition
{
NSLog(@"not outside !");
}
[_tableView scrollPoint: tvRect.origin];
rect = [self visibleRect];
minVisCoord = NSMinX (rect) + divWidth;
maxVisCoord = NSMaxX (rect) - divWidth;
minCoord = MAX (minAbsCoord, minVisCoord);
maxCoord = MIN (maxAbsCoord, maxVisCoord);
}
}
e = [NSApp nextEventMatchingMask: eventMask
untilDate: farAway
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
}
[NSEvent stopPeriodicEvents];
if (outside)
{
p = [self convertPoint: [e locationInWindow] fromView: nil].x;
if (p > maxAbsCoord)
p = maxAbsCoord;
else if (p < minAbsCoord)
p = minAbsCoord;
}
if (lit == YES)
{
NSHighlightRect(oldRect);
[_window flushWindow];
}
[self unlockFocus];
/* The following tiles the table. We use a private method
which avoids tiling the table twice. */
if (dragged == YES)
{
[_tableView _userResizedTableColumn: _resizedColumn
width: (p - NSMinX (rectLow))];
}
/* Clean up */
_resizedColumn = -1;
return;
}
/* We are not resizing
Let's launch a mouseDownInHeaderOfTableColumn message
*/
{
NSRect rect = [self headerRectOfColumn: columnIndex];
[_tableView _mouseDownInHeaderOfTableColumn:
[[_tableView tableColumns]
objectAtIndex: columnIndex]];
rect.origin.y++;
rect.size.height--;
[[currentColumn headerCell] setHighlighted: YES];
[self lockFocus];
[[currentColumn headerCell]
highlight: YES
withFrame: rect
inView: self];
[self unlockFocus];
[_window flushWindow];
}
/* Dragging */
/* Wait for mouse dragged events.
If mouse is dragged, move the column.
If mouse is not dragged but released, select/deselect the column. */
if ([_tableView allowsColumnReordering])
{
int i = columnIndex;
int j = columnIndex;
float minCoord;
float maxCoord;
float minVisCoord;
float maxVisCoord;
float *_cO;
float *_cO_minus1;
int numberOfColumns = [_tableView numberOfColumns];
unsigned int eventMask = (NSLeftMouseUpMask
| NSLeftMouseDraggedMask
| NSPeriodicMask);
unsigned int modifiers = [event modifierFlags];
NSEvent *e;
NSDate *distantFuture = [NSDate distantFuture];
NSRect visibleRect = [self visibleRect];
NSRect tvRect;
NSRect highlightRect, oldRect;
BOOL outside = NO;
BOOL lit = NO;
BOOL mouseDragged = NO;
float p;
NSPoint unconverted;
minVisCoord = NSMinX (visibleRect);
maxVisCoord = NSMaxX (visibleRect);
{
NSRect bounds = [self bounds];
minCoord = NSMinX(bounds);
maxCoord = NSMaxX(bounds);
}
{
float *_c = [_tableView _columnOrigins];
_cO_minus1 = malloc((numberOfColumns + 3) * sizeof(float));
_cO = _cO_minus1 + 1;
memcpy(_cO, _c, numberOfColumns * sizeof(float));
_cO[numberOfColumns] = maxCoord;
_cO[numberOfColumns + 1] = maxCoord;
_cO[-1] = minCoord;
}
highlightRect.size.height = NSHeight (visibleRect);
highlightRect.origin.y = NSMinY (visibleRect);
[self lockFocus];
[[NSColor lightGrayColor] set];
[NSEvent startPeriodicEventsAfterDelay: 0.05
withPeriod: 0.05];
e = [NSApp nextEventMatchingMask: eventMask
untilDate: distantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
while ([e type] != NSLeftMouseUp)
{
switch ([e type])
{
case NSLeftMouseDragged:
unconverted = [e locationInWindow];
p = [self convertPoint: unconverted fromView: nil].x;
if (mouseDragged == NO)
{
NSLog(@"TODO: Deselect the column");
}
mouseDragged = YES;
if (p < minVisCoord || p > maxVisCoord)
{
outside = YES;
}
else
{
outside = NO;
i = j;
if (p > (_cO[i] + _cO[i+1]) / 2)
{
while (p > (_cO[i] + _cO[i+1]) / 2)
i++;
}
else if (p < (_cO[i] + _cO[i-1]) / 2)
{
while (p < (_cO[i] + _cO[i-1]) / 2)
i--;
}
if (i != columnIndex
&& i != columnIndex + 1)
{
j = i;
highlightRect.size.height = NSHeight (visibleRect);
highlightRect.origin.y = NSMinY (visibleRect);
highlightRect.size.width = 7;
if (i == numberOfColumns)
{
highlightRect.origin.x = _cO[i] - 3;
}
else if (i == 0)
{
highlightRect.origin.x = _cO[i] - 3;
}
else
{
highlightRect.origin.x = _cO[i] - 3;
}
if (!NSEqualRects(highlightRect, oldRect))
{
if (lit)
NSHighlightRect(oldRect);
NSHighlightRect(highlightRect);
[_window flushWindow];
}
else if (!lit)
{
NSHighlightRect(highlightRect);
[_window flushWindow];
}
oldRect = highlightRect;
lit = YES;
}
else
{
i = columnIndex;
highlightRect.size.height = NSHeight (visibleRect);
highlightRect.origin.y = NSMinY (visibleRect);
highlightRect.origin.x = _cO[columnIndex];
highlightRect.size.width =
_cO[columnIndex + 1] - _cO[columnIndex];
if (!NSEqualRects(highlightRect, oldRect))
{
if (lit)
NSHighlightRect(oldRect);
// NSHighlightRect(highlightRect);
[_window flushWindow];
}
else if (!lit)
{
// NSHighlightRect(highlightRect);
// [_window flushWindow];
}
// oldRect = highlightRect;
oldRect = NSZeroRect;
lit = NO; //lit = YES;
}
}
break;
case NSPeriodic:
if (outside == YES)
{
if (lit)
{
NSHighlightRect(oldRect);
[_window flushWindow];
lit = NO;
oldRect = NSZeroRect;
}
p = [self convertPoint: unconverted
fromView: nil].x;
tvRect = [_tableView visibleRect];
if (p > maxVisCoord)
{
if (p > maxCoord)
tvRect.origin.x += (p - maxVisCoord)/8;
else
tvRect.origin.x += (p - maxVisCoord)/2;
}
else if (p < minVisCoord)
{
if (p < minCoord)
tvRect.origin.x += (p - minVisCoord)/8;
else
tvRect.origin.x += (p - minVisCoord)/2;
}
else // TODO remove this condition
{
NSLog(@"not outside !");
}
[_tableView scrollPoint: tvRect.origin];
visibleRect = [self visibleRect];
minVisCoord = NSMinX (visibleRect);
maxVisCoord = NSMaxX (visibleRect);
}
break;
default:
break;
}
e = [NSApp nextEventMatchingMask: eventMask
untilDate: distantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
}
if (lit)
{
NSHighlightRect(highlightRect);
[_window flushWindow];
lit = NO;
}
[NSEvent stopPeriodicEvents];
[self unlockFocus];
if (mouseDragged == NO)
{
[_tableView _selectColumn: columnIndex
modifiers: modifiers];
[_tableView _didClickTableColumn:
currentColumn];
[self setNeedsDisplay: YES];;
}
else // mouseDragged == YES
{
{
NSRect rect = [self headerRectOfColumn: columnIndex];
[_tableView _mouseDownInHeaderOfTableColumn:
[[_tableView tableColumns]
objectAtIndex: columnIndex]];
rect.origin.y++;
rect.size.height--;
[[currentColumn headerCell]
setHighlighted: NO];
[self lockFocus];
[[currentColumn headerCell]
highlight: NO
withFrame: rect
inView: self];
[self unlockFocus];
[_window flushWindow];
}
if (i > columnIndex)
i--;
if (i != columnIndex)
{
[_tableView moveColumn: columnIndex
toColumn: i];
}
}
free(_cO_minus1);
return;
}
else
{
NSRect cellFrame = [self headerRectOfColumn: columnIndex];
NSApplication *theApp = [NSApplication sharedApplication];
unsigned int modifiers = [event modifierFlags];
NSPoint location = [event locationInWindow];
NSPoint point = [self convertPoint: location fromView: nil];
if (![self mouse: point inRect: cellFrame])
{
NSLog(@"not in frame, what's happening ?");
return;
}
event = [theApp nextEventMatchingMask: NSLeftMouseUpMask
untilDate: nil
inMode: NSEventTrackingRunLoopMode
dequeue: NO];
location = [event locationInWindow];
point = [self convertPoint: location fromView: nil];
if (![self mouse: point inRect: cellFrame])
{
NSDebugLLog(@"NSCell",
@"tableheaderview point not in cell frame\n");
{
NSRect rect = [self headerRectOfColumn: columnIndex];
[_tableView _mouseDownInHeaderOfTableColumn:
[[_tableView tableColumns]
objectAtIndex: columnIndex]];
rect.origin.y++;
rect.size.height--;
[[currentColumn headerCell]
setHighlighted: NO];
[self lockFocus];
[[currentColumn headerCell]
highlight: NO
withFrame: rect
inView: self];
[self unlockFocus];
[_window flushWindow];
}
}
else
{
[_tableView _selectColumn: columnIndex
modifiers: modifiers];
[_tableView _didClickTableColumn:
currentColumn];
[self setNeedsDisplay: YES];
/*
if ([_tableView highlightedTableColumn] != currentColumn)
{
NSRect rect = [self headerRectOfColumn: columnIndex];
// [_tableView _mouseDownInHeaderOfTableColumn:
// [[_tableView tableColumns]
// objectAtIndex: columnIndex]];
rect.origin.y++;
rect.size.height--;
NSLog(@"highlight");
[[currentColumn headerCell] setHighlighted: NO];
[[currentColumn headerCell]
highlight: NO
withFrame: rect
inView: self];
[_window flushWindow];
}
*/
}
}
}
}
/*
* Encoding/Decoding
*/
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[super encodeWithCoder: aCoder];
/* Nothing else to encode in NSTableHeaderView:
- _tableView is set by the parent NSTableView
- _resizedColumn is reset on decoding anyway
*/
}
- (id) initWithCoder: (NSCoder*)aDecoder
{
self = [super initWithCoder: aDecoder];
_tableView = nil;
_resizedColumn = -1;
return self;
}
@end