home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-02-11 | 38.3 KB | 1,315 lines |
- //=============================================================================
- //
- // Copyright (C) 1995, 1996 by Paul S. McCarthy and Eric Sunshine.
- // Written by Paul S. McCarthy and Eric Sunshine.
- // All Rights Reserved.
- //
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the authors
- // and its use is governed by the MiscKit license, found in the file
- // "License.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
- //=============================================================================
- //-----------------------------------------------------------------------------
- // MiscBorderView.M
- //
- // View class for the row/column borders on a MiscTableView.
- // Supports resizing, dragging.
- //
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // $Id: MiscBorderView.M,v 1.9 96/02/12 00:04:25 sunshine Exp $
- // $Log: MiscBorderView.M,v $
- // Revision 1.9 96/02/12 00:04:25 sunshine
- // no comment.
- //
- // Revision 1.8 96/01/17 05:17:09 sunshine
- // Fixed crasher. Was allocating drag-cache window (an alloc once free never
- // object) from [self zone]. When the zone was destroyed the window pointer
- // (in a static variable) ceased to point to valid memory.
- //
- // Revision 1.7 96/01/16 19:51:25 zarnuk
- // Eliminated a lot of unnecessary drawing while dragging.
- //-----------------------------------------------------------------------------
- #import "MiscBorderView.h"
- #import "MiscBorderCell.h"
- #import "MiscHighlightTracker.h"
- #import "MiscListTracker.h"
- #import "MiscRadioTracker.h"
- #import "MiscTableBorder.h"
- #import "MiscTableView.h"
- #import "MiscTableWell.h"
- #import <misckit/MiscTableScroll.h>
- #import <misckit/MiscTableTypes.h>
-
- #import <new.h>
-
- extern "Objective-C" {
- #import <appkit/Application.h>
- #import <appkit/ClipView.h>
- #import <appkit/Font.h>
- #import <appkit/NXCursor.h>
- #import <appkit/NXImage.h>
- #import <appkit/Text.h>
- #import <appkit/Window.h>
- #import <appkit/graphics.h>
- #import <appkit/timer.h>
- #import <objc/NXBundle.h>
- #import <dpsclient/dpsNeXT.h>
- #import <dpsclient/wraps.h>
- }
-
- extern "C" {
- #import <assert.h>
- #import <float.h>
- #import <limits.h>
- #import <math.h>
- #import <string.h> // memset().
- #import <stdio.h> // FILENAME_MAX
- }
-
- #define MISC_RESIZE_EPSILON 5
- #define MISC_FRAME_HEIGHT 18
- #define MISC_FRAME_WIDTH 46
-
- static NXCursor* horzCursor = 0;
- static NXCursor* vertCursor = 0;
-
- static inline double dmin( double a, double b ) { return (a < b ? a : b); }
- static inline double dmax( double a, double b ) { return (a > b ? a : b); }
-
-
- //----------------------------------------------------------------------------
- // startTimer
- //----------------------------------------------------------------------------
- static inline void startTimer( NXTrackingTimer*& timer )
- {
- if (timer == 0)
- timer = NXBeginTimer( 0, 0.1, 0.1 );
- }
-
-
- //----------------------------------------------------------------------------
- // stopTimer
- //----------------------------------------------------------------------------
- static inline void stopTimer( NXTrackingTimer*& timer )
- {
- if (timer)
- {
- NXEndTimer( timer );
- timer = 0;
- }
- }
-
-
-
- //=============================================================================
- // IMPLEMENTATION
- //=============================================================================
- @implementation MiscBorderView
-
- //-----------------------------------------------------------------------------
- // + cursorFromImageName:
- // NOTE: Cannot use [self zone] in a class (factory) method
- // like this one, hence no allocFromZone:.
- //-----------------------------------------------------------------------------
- + (NXCursor*) cursorFromImageName:(char const*)name
- {
- NXImage* image = [NXImage findImageNamed:name];
- if (image == 0)
- {
- char path[ FILENAME_MAX + 1 ];
- [[NXBundle bundleForClass:self]
- getPath:path forResource:name ofType:"tiff"];
- image = [[NXImage alloc] initFromFile:path];
- [image setName:name];
- }
- NXPoint const hot = { 8, 8 };
- NXCursor* cursor = [[NXCursor alloc] initFromImage: image];
- [cursor setHotSpot:&hot];
- return cursor;
- }
-
-
- //-----------------------------------------------------------------------------
- // + initialize
- //-----------------------------------------------------------------------------
- + initialize
- {
- if (horzCursor == 0)
- {
- horzCursor = [self cursorFromImageName:"MiscHorzResizeCursor"];
- vertCursor = [self cursorFromImageName:"MiscVertResizeCursor"];
- }
- return self;
- }
-
-
- //-----------------------------------------------------------------------------
- // TYPE VARIATIONS
- //-----------------------------------------------------------------------------
-
- - (NXCoord) frameHeight
- { return (isHorz ? MISC_FRAME_HEIGHT : MISC_FRAME_WIDTH); }
- - (NXCursor*) cursor
- { return isHorz ? horzCursor : vertCursor; }
- - (MiscBorderType) borderType
- { return isHorz ? MISC_COL_BORDER : MISC_ROW_BORDER; }
- - (MiscBorderType) otherBorderType
- { return isHorz ? MISC_ROW_BORDER : MISC_COL_BORDER; }
- - (MiscTableBorder*) otherBorder
- { return [scroll border: [self otherBorderType]]; }
- - (void) clearOtherBorder { [self otherBorder]->selectionSet().empty(); }
- - cellAt: (MiscCoord_P) row : (MiscCoord_P) col
- { return isHorz ? [scroll cellAt:row:col] : [scroll cellAt:col:row]; }
-
- - (NXCoord) sizeWidth: (NXSize const*) size
- { return isHorz ? size->width : size->height; }
- - (NXCoord) sizeHeight: (NXSize const*) size
- { return isHorz ? size->height : size->width; }
-
- - (NXCoord) pointX: (NXPoint const*) point
- { return isHorz ? point->x : point->y; }
- - (NXCoord) pointY: (NXPoint const*) point
- { return isHorz ? point->y : point->x; }
- - (void) point: (NXPoint*) point setX: (NXCoord) x
- { if (isHorz) point->x = x; else point->y = x; };
- - (void) point: (NXPoint*) point setY: (NXCoord) y
- { if (isHorz) point->y = y; else point->x = y; };
-
- - (NXCoord)rectMinX:(NXRect const*)frameRect
- { return isHorz ? NX_X(frameRect) : NX_Y(frameRect); }
- - (NXCoord)rectMaxX:(NXRect const*)frameRect
- { return isHorz ? NX_MAXX(frameRect) : NX_MAXY(frameRect); }
- - (NXCoord)rectMinY:(NXRect const*)frameRect
- { return isHorz ? NX_Y(frameRect) : NX_X(frameRect); }
- - (NXCoord)rectMaxY:(NXRect const*)frameRect
- { return isHorz ? NX_MAXY(frameRect) : NX_MAXX(frameRect); }
- - (NXCoord)rectWidth:(NXRect const*)frameRect
- { return isHorz ? NX_WIDTH(frameRect) : NX_HEIGHT(frameRect); }
- - (NXCoord)rectHeight:(NXRect const*)frameRect
- { return isHorz ? NX_HEIGHT(frameRect) : NX_WIDTH(frameRect); }
-
- - (void) setRect:(NXRect*)rect minX:(NXCoord)x
- { if (isHorz) NX_X(rect) = x; else NX_Y(rect) = x; }
- - (void) setRect:(NXRect*)rect minY:(NXCoord)y
- { if (isHorz) NX_Y(rect) = y; else NX_X(rect) = y; }
- - (void) setRect:(NXRect*)rect width:(NXCoord)width
- { if (isHorz) NX_WIDTH(rect) = width; else NX_HEIGHT(rect) = width; }
- - (void) setRect:(NXRect*)rect height:(NXCoord)height
- { if (isHorz) NX_HEIGHT(rect) = height; else NX_WIDTH(rect) = height; }
-
- - (void) changeFrameIfNeeded
- {
- MiscPixels const my_width = (MiscPixels) floor([self rectWidth:&frame]);
- MiscPixels const i_width = info->totalSize();
- if (my_width != i_width)
- {
- NXRect rect = frame;
- [window invalidateCursorRectsForView:self];
- [self setRect:&rect width:i_width];
- [self sizeTo: rect.size.width : rect.size.height ];
- [[scroll docView] adjustSize];
- }
- }
-
- - (void) setPos:(MiscCoord_V)pos width:(MiscPixels)size
- {
- MiscCoord_P pPos = info->visualToPhysical(pos);
- if (isHorz)
- [scroll setCol:pPos size:(NXCoord)size];
- else
- [scroll setRow:pPos size:(NXCoord)size];
- [self changeFrameIfNeeded];
- [window invalidateCursorRectsForView:self];
- [scroll update];
- }
-
- - (void) moveFrom:(MiscCoord_V)from to:(MiscCoord_V)to
- {
- if (isHorz)
- [scroll moveColFrom:from to:to];
- else
- [scroll moveRowFrom:from to:to];
- [window invalidateCursorRectsForView:self];
- [scroll update];
- }
-
-
-
- //-----------------------------------------------------------------------------
- // - initFrame:scroll:info:type:
- //-----------------------------------------------------------------------------
- - initFrame: (NXRect const*) frameRect
- scroll: (MiscTableScroll*) i_scroll
- info: (MiscTableBorder*) i_info
- type: (MiscBorderViewType) type
- {
- NXZone* const z = [self zone];
-
- [super initFrame: 0];
- [self setFlipped:YES];
- [self setOpaque:YES];
- [self setClipping:NO];
-
- isHorz = (type == MISC_COL_BORDER_VIEW);
- scroll = i_scroll;
- info = i_info;
- theCell = [[MiscBorderCell allocFromZone:z] initTextCell:"Kilroy"];
-
- oldSel = new( NXZoneMalloc(z,sizeof(*oldSel)) ) MiscSparseSet;
- [self setSelectionMode: [scroll selectionMode]];
-
- NXRect myFrame = {{0,0},{0,0}};
- if (frameRect != 0)
- myFrame.origin = frameRect->origin;
- [self setRect: &myFrame width: i_info->totalSize()];
- [self setRect: &myFrame height: [self frameHeight]];
- [self setFrame: &myFrame];
- return self;
- }
-
-
- //-----------------------------------------------------------------------------
- // - free
- //-----------------------------------------------------------------------------
- - free
- {
- [theCell free];
- [tracker free];
- if (oldSel != 0)
- {
- oldSel->MiscSparseSet::~MiscSparseSet();
- NXZoneFree( [self zone], oldSel );
- }
- return [super free];
- }
-
-
-
- //=============================================================================
- // CONVERSIONS
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - range::fromRect:
- //
- // Returns by reference the range of visual slots contained in rect.
- // rMin is inclusive, rMax is exclusive.
- //-----------------------------------------------------------------------------
- - (void) range: (MiscCoord_V*)rMin : (MiscCoord_V*)rMax
- fromRect: (NXRect const*)rect
- {
- *rMin = info->visualForOffset(MiscPixels(floor([self rectMinX:rect])));
- *rMax = info->visualForOffset(MiscPixels(floor([self rectMaxX:rect]))) + 1;
- }
-
-
- //-----------------------------------------------------------------------------
- // - rect:forPos:
- //-----------------------------------------------------------------------------
- - (void) rect:(NXRect*)nxrect forPos:(MiscCoord_V)pos
- {
- [self getBounds:nxrect];
- [self setRect: nxrect minX: info->getOffset(pos)];
- [self setRect: nxrect width: info->effectiveSize(pos)];
- }
-
-
-
- //=============================================================================
- // DRAWING
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - getVisibleRange::
- //
- // Returns by reference the range of visual slots contained in the visible
- // rectangle. rMin is inclusive, rMax is exclusive.
- //-----------------------------------------------------------------------------
- - (void) getVisibleRange: (MiscCoord_V*)rMin : (MiscCoord_V*)rMax
- {
- NXRect visRect;
- [superview getDocVisibleRect:&visRect];
- [self range:rMin:rMax fromRect:&visRect];
- }
-
-
- //-----------------------------------------------------------------------------
- // drewPos:
- //
- // Keeps the old selection sets up to date whenever we draw a cell. This
- // way -reflectSelection has valid data to work from.
- //-----------------------------------------------------------------------------
- - (void) drewPos: (MiscCoord_V)slot
- {
- BOOL isSelected = info->selectionSet().contains( slot );
- if (isSelected != oldSel->contains(slot))
- {
- if (isSelected)
- oldSel->add( slot );
- else
- oldSel->remove( slot );
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // - drawPos:inRect:controlView:
- //-----------------------------------------------------------------------------
- - (void) drawPos: (MiscCoord_V) pos inRect:(NXRect const*) rect
- controlView: (View*) controlView
- {
- [theCell setStringValue: info->getTitle(pos)];
- [theCell setState: info->selectionSet().contains(pos)];
- [theCell drawSelf: rect inView: controlView];
- }
-
-
- //-----------------------------------------------------------------------------
- // - drawPos:updateSel:
- //-----------------------------------------------------------------------------
- - (void) drawPos: (MiscCoord_V) pos updateSel: (BOOL) updateSel
- {
- if (0 <= pos && pos < info->count())
- {
- NXRect rect;
- [self rect:&rect forPos:pos];
- [self drawPos:pos inRect:&rect controlView: self];
- if (updateSel)
- [self drewPos:pos];
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // - drawPos:
- //-----------------------------------------------------------------------------
- - (void) drawPos: (MiscCoord_V) pos
- {
- [self drawPos:pos updateSel:YES];
- }
-
-
- //-----------------------------------------------------------------------------
- // - drawRect:
- //-----------------------------------------------------------------------------
- - (void) drawRect: (NXRect const*) rect
- {
- MiscCoord_V pos_min;
- MiscCoord_V pos_max;
- [self range:&pos_min:&pos_max fromRect:rect];
- for (MiscCoord_V pos = pos_min; pos < pos_max; pos++)
- [self drawPos:pos];
- }
-
-
- //-----------------------------------------------------------------------------
- // - drawSelf::
- //-----------------------------------------------------------------------------
- - drawSelf: (NXRect const*) rects :(int) nrects
- {
- if (nrects == 1)
- [self drawRect: rects];
- else if (nrects == 3)
- {
- [self drawRect: ++rects];
- [self drawRect: ++rects];
- }
- return self;
- }
-
-
-
- //-----------------------------------------------------------------------------
- // - drawSlot: [public]
- //-----------------------------------------------------------------------------
- - (void) drawSlot: (MiscCoord_V) n
- {
- MiscCoord_V rMin, rMax;
- [self getVisibleRange:&rMin:&rMax];
- if (rMin <= n && n < rMax)
- {
- if ([self isAutodisplay] && [self canDraw])
- {
- [self lockFocus];
- [self drawPos:n];
- [self unlockFocus];
- }
- else
- {
- [self setNeedsDisplay:YES];
- }
- }
- }
-
-
-
- //=============================================================================
- // SELECTION
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - setSelectionMode:
- //-----------------------------------------------------------------------------
- - (void) setSelectionMode: (MiscSelectionMode) mode
- {
- NXZone* const z = [self zone];
- if (tracker)
- [tracker free];
- switch (mode)
- {
- case MISC_LIST_MODE:
- tracker = [MiscListTracker allocFromZone:z];
- break;
- case MISC_RADIO_MODE:
- tracker = [MiscRadioTracker allocFromZone:z];
- break;
- case MISC_HIGHLIGHT_MODE:
- tracker = [MiscHighlightTracker allocFromZone:z];
- break;
- }
- [tracker initBorder: info];
- }
-
-
- //-----------------------------------------------------------------------------
- // - reflectSelection
- //-----------------------------------------------------------------------------
- - (void) reflectSelection
- {
- if ([self canDraw])
- {
- MiscSparseSet const& newSel = info->selectionSet();
-
- MiscCoord_V rMin, rMax;
- [self getVisibleRange:&rMin:&rMax];
-
- [window disableFlushWindow];
- int locked = 0;
- for (MiscCoord_V i = rMin; i < rMax; i++)
- {
- if (oldSel->contains(i) != newSel.contains(i))
- {
- if (!locked)
- {
- locked = 1;
- [self lockFocus];
- }
- [self drawPos:i updateSel:NO];
- }
- }
- if (locked)
- [self unlockFocus];
-
- [[window reenableFlushWindow] flushWindow];
- *oldSel = newSel;
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // - selectPos:
- //-----------------------------------------------------------------------------
- - (void) selectPos: (MiscCoord_V) pos
- {
- MiscSparseSet& s = info->selectionSet();
- s.empty();
- s.add( pos );
- [scroll selectionChanged];
- }
-
-
-
- //=============================================================================
- // CURSOR MANAGEMENT
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - cursorRect:forPos:
- //-----------------------------------------------------------------------------
- - (BOOL) cursorRect:(NXRect*)nxrect forPos:(MiscCoord_V)pos;
- {
- MiscPixels const pos_width = info->effectiveSize(pos);
- if (pos_width > 0)
- {
- MiscPixels const pos_offset = info->getOffset(pos);
- MiscPixels const min_x = pos_offset + pos_width - MISC_RESIZE_EPSILON;
- *nxrect = bounds;
- [self setRect:nxrect minX:min_x];
- [self setRect:nxrect width:MISC_RESIZE_EPSILON];
- return YES;
- }
- return NO;
- }
-
-
- //-----------------------------------------------------------------------------
- // - resetCursorRects
- //-----------------------------------------------------------------------------
- - resetCursorRects
- {
- if (info->isSizeable())
- {
- NXCursor* const cursor = [self cursor];
- MiscCoord_V min_pos, max_pos;
- [self getVisibleRange:&min_pos:&max_pos];
- int const count = info->count();
- if (0 <= min_pos && min_pos < count)
- {
- NXRect rect;
- int const lim = (max_pos <= count) ? max_pos : count;
- for (MiscCoord_V pos = min_pos; pos < lim; pos++)
- if (info->isSizeable(pos) && [self cursorRect:&rect forPos:pos])
- [self addCursorRect:&rect cursor:cursor];
- }
- }
- return self;
- }
-
-
- //-----------------------------------------------------------------------------
- // - refresh
- //-----------------------------------------------------------------------------
- - (void) refresh
- {
- [self changeFrameIfNeeded];
- if ([self canDraw])
- {
- [window disableFlushWindow];
- NXRect rect;
- [superview getDocVisibleRect:&rect];
- [self lockFocus];
- [self drawSelf:&rect:1];
- [self unlockFocus];
- [[window reenableFlushWindow] flushWindow];
- }
- }
-
-
-
- //=============================================================================
- // MOUSE-TRACKING
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - startTracking
- //-----------------------------------------------------------------------------
- - (void) startTracking
- {
- [self clearOtherBorder];
- if ([window firstResponder] == [scroll docView])
- [window makeFirstResponder: window];
- }
-
-
- //-----------------------------------------------------------------------------
- // - endTracking:
- //-----------------------------------------------------------------------------
- - (void) endTracking: (MiscCoord_V)pos
- {
- if (pos < 0)
- pos = 0;
- else if (pos >= info->count())
- pos = info->count() - 1;
- [scroll border:[self borderType] setCursor:info->visualToPhysical(pos)];
- [scroll selectText: self];
- }
-
-
- //-----------------------------------------------------------------------------
- // - posForMousePt:
- //-----------------------------------------------------------------------------
- - (MiscCoord_V) posForMousePt: (NXPoint const*) p
- {
- MiscPixels pix = MiscPixels( floor([self pointX: p]) );
- return (pix < [self rectMaxX: &bounds] ?
- info->visualForOffset( pix ) : info->count());
- }
-
-
-
- //=============================================================================
- // RESIZING
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - resizeEvent:x:deltaX:minX:maxX:
- //-----------------------------------------------------------------------------
- - (int) resizeEvent:(NXEvent const*)p
- x:(MiscPixels)x
- deltaX:(MiscPixels)deltaX
- minX:(MiscPixels)minX
- maxX:(MiscPixels)maxX
- {
- NXRect docFrame; [scroll getDocClipFrame: &docFrame];
- NXRect clipFrame; [superview getFrame: &clipFrame];
- NXCoord const minDrawX = [self rectMinX: &clipFrame]; // scroll coords
- NXCoord const maxDrawX = [self rectMaxX: &clipFrame]; // scroll coords
-
- x += deltaX;
-
- NXCoord draw_x; // scroll coords
- NXRect line;
- [self setRect:&line height:[self rectHeight:&clipFrame] +
- [self rectHeight:&docFrame] - 1 ];
- [self setRect:&line width: 2 ];
- [self setRect:&line minX: x - 1 ];
- [self setRect:&line minY: 0 ];
- [scroll convertPoint:&(line.origin) fromView:self];
- draw_x = [self rectMinX:&line];
-
- BOOL did_scroll = NO;
- BOOL in_bounds = (minDrawX <= draw_x && draw_x <= maxDrawX);
-
- [scroll lockFocus];
- PSsetinstance(YES);
- PSsetgray(NX_BLACK);
-
- if (in_bounds)
- PSrectfill( line.origin.x, line.origin.y,
- line.size.width, line.size.height );
-
- NXTrackingTimer* timer = 0;
- startTimer( timer );
- NXEvent lastEvent = *p;
-
- while (1)
- {
- p = [NXApp getNextEvent:NX_MOUSEUPMASK |
- NX_MOUSEDRAGGEDMASK |
- NX_TIMERMASK ];
- if (p == 0 || p->type == NX_MOUSEUP)
- break;
- if (p->type == NX_TIMER)
- {
- NXPoint mousePt = lastEvent.location;
- [scroll convertPoint:&mousePt fromView:0];
- if (mousePt.x < minDrawX || maxDrawX < mousePt.x)
- {
- PSsetinstance(NO);
- [self autoscroll: &lastEvent];
- PSsetinstance(YES);
- did_scroll = YES;
- }
- }
- else
- lastEvent = *p;
-
- NXPoint new_loc = lastEvent.location;
- [self convertPoint:&new_loc fromView:0];
- MiscPixels mouseX = (MiscPixels) floor([self pointX:&new_loc]);
- MiscPixels new_x = (mouseX + deltaX);
- if (new_x < minX)
- new_x = minX;
- else if (new_x >= maxX)
- new_x = maxX - 1;
-
- BOOL const did_move = (new_x != x);
- if (did_move || did_scroll)
- {
- if (in_bounds && !did_scroll)
- PShideinstance( line.origin.x, line.origin.y,
- line.size.width, line.size.height );
- if (did_move)
- {
- x = new_x;
- [self setRect:&line minX: x - 1 ];
- [self setRect:&line minY:0];
- [scroll convertPoint:&(line.origin) fromView:self];
- draw_x = [self rectMinX:&line];
- in_bounds = (minDrawX <= draw_x && draw_x <= maxDrawX);
- }
-
- if (in_bounds)
- PSrectfill( line.origin.x, line.origin.y,
- line.size.width, line.size.height );
- did_scroll = NO;
- }
- }
-
- stopTimer( timer );
-
- if (in_bounds)
- PShideinstance( line.origin.x, line.origin.y,
- line.size.width, line.size.height );
-
- PSsetinstance(NO);
- [scroll unlockFocus];
-
- x -= deltaX;
- return x;
- }
-
-
- //-----------------------------------------------------------------------------
- // - resizeEvent:inPos:atX:deltaX:finalWidth:
- //-----------------------------------------------------------------------------
- - (BOOL) resizeEvent: (NXEvent const*) p
- inPos: (MiscCoord_V) pos
- atX: (MiscPixels) x_origin
- deltaX: (MiscPixels) delta_x
- finalWidth: (MiscPixels*) finalWidth
- {
- MiscPixels const org_x = info->getOffset(pos);
- MiscPixels const min_x = org_x + info->effectiveMinSize(pos);
- MiscPixels const max_x = org_x + info->effectiveMaxSize(pos) + 1;
- MiscPixels const curr_x = [self resizeEvent:p x:x_origin deltaX:delta_x
- minX:min_x maxX:max_x];
- MiscPixels final_delta = curr_x - x_origin;
- if (final_delta != 0)
- {
- *finalWidth = info->effectiveSize(pos) + final_delta;
- return YES;
- }
-
- return NO;
- }
-
-
- //-----------------------------------------------------------------------------
- // - inHotZone:forPos:atX:deltaX:
- //-----------------------------------------------------------------------------
- - (BOOL) inHotZone: (NXPoint const*) pt
- forPos: (MiscCoord_V*) pos
- atX: (MiscPixels*) pos_x
- deltaX: (MiscPixels*) delta_x
- {
- if (info->isSizeable())
- {
- MiscCoord_V const plim = info->count();
- MiscPixels x = (MiscPixels) floor([self pointX:pt]);
- MiscCoord_V p = info->visualForOffset(x);
- if (0 <= p && p < plim)
- {
- MiscPixels max_x = info->getOffset(p) + info->effectiveSize(p);
- MiscPixels delta = max_x - x;
-
- if (0 <= delta && delta <= MISC_RESIZE_EPSILON)
- {
- do { p++; } while (p < plim && info->effectiveSize(p) <= 0);
- p--;
- if (0 <= p && p < plim && info->isSizeable(p))
- {
- *pos = p;
- *pos_x = x;
- *delta_x = delta;
- return YES;
- }
- }
- }
- }
-
- return NO;
- }
-
-
- //-----------------------------------------------------------------------------
- // - adjustSize
- //-----------------------------------------------------------------------------
- - (void) adjustSize
- {
- [self changeFrameIfNeeded];
- }
-
-
-
- //=============================================================================
- // DRAGGING
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - drawDocCells:at:controlView
- //-----------------------------------------------------------------------------
- - (void) drawDocCells: (MiscCoord_V) pos at: (NXPoint const*) pt
- controlView: (View*) controlView
- {
- NXRect rDoc;
- [[scroll docView] getVisibleRect: &rDoc];
- MiscTableBorder* b = [self otherBorder];
- MiscCoord_V sMin = b->visualForOffset(MiscPixels([self rectMinY: &rDoc]));
- MiscCoord_V sMax = b->visualForOffset(MiscPixels([self rectMaxY: &rDoc]));
- if (sMin >= 0 && sMax >= 0)
- {
- MiscCoord_P p_pos = info->visualToPhysical( pos );
- NXCoord delta = [self rectMinY: &rDoc] - b->getOffset( sMin );
-
- NXRect r = {{0,0},{0,0}};
- [self setRect: &r minY: [self pointY: pt] - delta];
- [self setRect: &r width: info->effectiveSize( pos )];
-
- for (int i = sMin; i <= sMax; i++)
- {
- id cell = [self cellAt:b->visualToPhysical(i):p_pos];
- [self setRect: &r height: b->effectiveSize(i)];
- [cell drawSelf: &r inView: controlView];
- [self setRect:&r minY:[self rectMinY:&r] + [self rectHeight:&r]];
- }
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // - setDragCache:forPos:
- //
- // NOTE *1*: We create a special window just for the sake of getting the
- // text on the drag-cache to display properly. 1) By creating a
- // window, the cells have an object to which they can send their
- // getFieldEditor:for: message. 2) By providing a view which is
- // at least as large as the drag-cache we avoid clipping problems.
- // If it wasn't large enough the text would get clipped at the
- // wrong places.
- // NOTE *2*: The weird combination of non-deferred window plus
- // -reenableDisplay was necessary to get the text to actually
- // display. Without both of them text wouldn't draw at all.
- // Let's have a look at that AppKit source!
- //-----------------------------------------------------------------------------
- - (void) setDragCache: (NXImage*) cache forPos: (MiscCoord_V) pos
- {
- Window* w = [[Window allocFromZone: [self zone]] // NOTE *1*
- initContent: 0 style: NX_PLAINSTYLE
- backing: NX_NONRETAINED buttonMask: 0 defer: NO];
- [w reenableDisplay]; // NOTE *2*
- [[w contentView] setFlipped: YES];
-
- NXRect rDoc; [[scroll docView] getVisibleRect: &rDoc];
- NXRect rClip; [superview getFrame: &rClip];
- [scroll convertRect: &rDoc fromView: [scroll docView]];
-
- NXRect r = {{0,0},{0,0}};
- [self setRect: &r width: info->effectiveSize( pos )];
- [self setRect: &r height:
- ([self rectHeight: &rClip] + [self rectHeight: &rDoc])];
-
- NXRect rBorder = r;
- [self setRect: &rBorder height: [self rectHeight: &rClip]];
-
- NXRect rContent = r;
- [self setRect: &rContent minY: [self rectHeight: &rClip]];
-
- [w sizeWindow: NX_WIDTH(&r) : NX_HEIGHT(&r)];
- [cache setFlipped: YES];
- [cache setSize: &r.size];
- [cache lockFocus];
- [self drawDocCells: pos at: &rContent.origin controlView: [w contentView]];
- [self drawPos: pos inRect: &rBorder controlView: [w contentView]];
- [cache unlockFocus];
-
- [w free];
- }
-
-
- //-----------------------------------------------------------------------------
- // - setVisibleCache:min:max:
- //-----------------------------------------------------------------------------
- - (void) setVisibleCache:(NXImage*)cache min:(NXCoord*)pMin max:(NXCoord*)pMax
- {
- NXRect rDoc; [[scroll docView] getVisibleRect: &rDoc];
- NXRect rVis; [self getVisibleRect: &rVis];
- *pMin = [self rectMinX: &rVis];
- *pMax = [self rectMaxX: &rVis];
- [scroll convertRect: &rDoc fromView: [scroll docView]];
- [scroll convertRect: &rVis fromView: self];
-
- NXRect r;
- [self setRect: &r minX: [self rectMinX: &rVis]];
- [self setRect: &r minY: [self rectMinY: &rVis]];
- [self setRect: &r width: [self rectWidth: &rVis]];
- [self setRect: &r height:
- ([self rectHeight: &rVis] + [self rectHeight: &rDoc])];
-
- [scroll convertRect: &r toView: 0];
- [cache setSize: &r.size];
- [cache lockFocus];
- PScomposite( NX_X(&r), NX_Y(&r), NX_WIDTH(&r), NX_HEIGHT(&r),
- [window gState], 0.0, 0.0, NX_COPY );
- [cache unlockFocus];
- }
-
-
- //-----------------------------------------------------------------------------
- // - setWells::forPos:
- //-----------------------------------------------------------------------------
- - (void) setWells: (MiscTableWell**) w1 : (MiscTableWell**) w2
- forPos: (MiscCoord_V) pos
- {
- NXZone* const z = [self zone];
- MiscTableView* doc = [scroll docView];
- NXRect rDoc; [doc getVisibleRect: &rDoc];
- NXRect rClip; [superview getFrame: &rClip];
-
- NXRect r = {{0,0},{0,0}};
- [self setRect: &r minX: info->getOffset( pos )];
- [self setRect: &r width: info->effectiveSize( pos )];
- [self setRect: &r height: [self rectHeight: &rClip]];
-
- *w1 = [[MiscTableWell allocFromZone:z] initFrame: &r];
- [self addSubview: *w1];
-
- [self setRect: &r minY: [self rectMinY: &rDoc]];
- [self setRect: &r height: [self rectHeight: &rDoc]];
- *w2 = [[MiscTableWell allocFromZone:z] initFrame: &r];
- [doc addSubview: *w2];
-
- [*w1 display];
- [*w2 display];
- }
-
-
- //-----------------------------------------------------------------------------
- // - clearWells::
- //-----------------------------------------------------------------------------
- - (void) clearWells: (MiscTableWell**) w1 : (MiscTableWell**) w2
- {
- [*w1 removeFromSuperview];
- [*w2 removeFromSuperview];
- [*w1 free];
- [*w2 free];
- *w1 = 0;
- *w2 = 0;
- // NOTE: Does not need display here. Everything will get displayed later.
- }
-
-
- //-----------------------------------------------------------------------------
- // - offsetFromEvent:
- //-----------------------------------------------------------------------------
- - (NXCoord) offsetFromEvent: (NXEvent const*) ev
- {
- NXPoint mLoc = ev->location;
- [self convertPoint: &mLoc fromView: 0];
- return floor( [self pointX: &mLoc] );
- }
-
-
- //-----------------------------------------------------------------------------
- // - calcDrop::
- //-----------------------------------------------------------------------------
- - (MiscCoord_V) calcDrop: (MiscCoord_V) fromPos
- : (NXPoint const*) mouseDownPt
- : (NXPoint const*) mouseUpPt
- {
- MiscCoord_V toPos = fromPos;
-
- MiscPixels const start_pos = (MiscPixels) floor([self pointX:mouseDownPt]);
- MiscPixels const end_pos = (MiscPixels) floor([self pointX:mouseUpPt]);
- MiscPixels const delta_pos = (end_pos - start_pos);
-
- MiscPixels const SLOP = 4;
- if (delta_pos < -SLOP || SLOP < delta_pos)
- {
- MiscPixels const start_ofs = info->getOffset( fromPos );
- MiscPixels drop_pos = start_ofs + delta_pos;
- if (delta_pos < 0)
- drop_pos += SLOP;
- else
- drop_pos += info->effectiveSize(fromPos) - SLOP;
- toPos = info->visualForOffset( drop_pos );
- if (toPos < 0)
- toPos = 0;
- }
-
- return toPos;
- }
-
-
- //-----------------------------------------------------------------------------
- // - dragEvent:inPos:
- //-----------------------------------------------------------------------------
- - (MiscCoord_V) dragEvent: (NXEvent*) event inPos: (MiscCoord_V) pos
- {
- int const WANTED = (NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK | NX_TIMERMASK);
- NXPoint mouseDownPt = event->location;
-
- [self convertPoint:&mouseDownPt fromView:0];
- NXPoint mouseUpPt = mouseDownPt;
-
- [window disableFlushWindow];
-
- MiscTableWell *w1, *w2;
- [self setWells: &w1 : &w2 forPos: pos];
-
- NXZone* const z = [self zone];
- NXCoord pMin,pMax;
- NXImage* visCache = [[NXImage allocFromZone:z] init];
- NXImage* dragCache = [[NXImage allocFromZone:z] init];
- [self setVisibleCache: visCache min: &pMin max: &pMax];
- [self setDragCache: dragCache forPos: pos];
-
- NXSize size;
- [dragCache getSize: &size];
- NXCoord pLoc = info->getOffset( pos );
- NXCoord delta = [self offsetFromEvent: event] - pLoc;
-
- NXTrackingTimer* timer = 0;
- NXEvent lastEvent = *event;
- [scroll lockFocus];
- while (1)
- {
- NXPoint pt;
- NXRect rDrag = {{0,0},{0,0}};
- NXCoord const w = [self sizeWidth: &size];
- NXCoord const dw =
- dmin( dmin( dmin(w, pMax - pMin), pMax - pLoc), pLoc + w - pMin );
- BOOL const shouldDraw = (dw > 0.0);
- if (shouldDraw)
- {
- if (isHorz)
- {
- pt.x = dmax( pLoc, pMin );
- pt.y = size.height;
- if (pLoc < pMin)
- rDrag.origin.x = (pMin - pLoc);
- }
- else
- {
- pt.x = 0.0;
- pt.y = dmax( pLoc, pMin ) + dw;
- if (pLoc + w >= pMax)
- rDrag.origin.y = pLoc + w - pMax;
- }
- [scroll convertPoint: &pt fromView: self];
- [self setRect: &rDrag width: dw];
- [self setRect: &rDrag height: [self sizeHeight: &size]];
- [dragCache composite: NX_COPY fromRect: &rDrag toPoint: &pt];
- }
- [[window reenableFlushWindow] flushWindow];
-
- event = [NXApp getNextEvent: WANTED];
-
- [window disableFlushWindow];
- if (shouldDraw)
- {
- NXRect rVis = {{0,0},{0,0}};
- NXSize s; [visCache getSize: &s];
- NXCoord xTarg;
- if (isHorz)
- {
- xTarg = (pLoc < pMin ? 0.0 : pLoc - pMin);
- }
- else
- {
- if (pLoc < pMin)
- xTarg = s.height - dw;
- else if (pLoc < pMax)
- xTarg = pMax - pLoc - dw;
- else
- xTarg = 0.0;
- }
- [self setRect: &rVis minX: xTarg];
- [self setRect: &rVis width: [self rectWidth: &rDrag]];
- [self setRect: &rVis height: [self rectHeight: &rDrag]];
- [visCache composite: NX_COPY fromRect: &rVis toPoint: &pt];
- }
-
- if (event == 0)
- break;
- if (event->type == NX_MOUSEUP)
- {
- mouseUpPt = event->location;
- [self convertPoint:&mouseUpPt fromView:0];
- break;
- }
- if (event->type != NX_TIMER)
- lastEvent = *event;
-
- NXCoord mLoc = [self offsetFromEvent: &lastEvent];
- if ((mLoc < pMin && pMin > 0.0) ||
- (mLoc > pMax && pMax < [self rectMaxX: &bounds]))
- {
- [self autoscroll: &lastEvent];
- [self setVisibleCache: visCache min: &pMin max: &pMax];
- mLoc = [self offsetFromEvent: &lastEvent];
- startTimer( timer );
- }
- else
- {
- stopTimer( timer );
- }
-
- pLoc = mLoc - delta;
- if (pLoc < pMin - [self sizeWidth: &size])
- pLoc = pMin - [self sizeWidth: &size];
- else if (pLoc > pMax)
- pLoc = pMax;
- }
-
- stopTimer( timer );
- [scroll unlockFocus];
- [self clearWells: &w1 : &w2];
- [visCache free];
- [dragCache free];
-
- MiscCoord_V const toPos = [self calcDrop:pos :&mouseDownPt :&mouseUpPt];
-
- if (toPos != pos)
- {
- BOOL const was_auto = [scroll isAutodisplay];
- [scroll setAutodisplay:NO];
- [self moveFrom: pos to: toPos];
- [self selectPos: toPos];
- [scroll border:[self borderType] slotDraggedFrom:pos to:toPos];
- [scroll setAutodisplay:was_auto]; // Will display if needed.
- }
- else
- {
- // Need to redisplay the slot that the wells were covering.
- MiscBorderType const b = [self borderType];
- int const phys_pos = [scroll border:b slotAtPosition:toPos];
- [scroll border:b drawSlotTitle:phys_pos];
- [scroll border:b drawSlot:phys_pos];
- }
-
- [[window reenableFlushWindow] flushWindow];
- return toPos;
- }
-
-
- //-----------------------------------------------------------------------------
- // - awaitDragEvent:inPos:
- //-----------------------------------------------------------------------------
- - (MiscCoord_V) awaitDragEvent: (NXEvent const*) event inPos: (MiscCoord_V) pos
- {
- MiscCoord_V toPos = pos;
- int const WANTED = (NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK);
- float const FOREVER = FLT_MAX;
- float const SLOP = 2.0;
- NXEvent mouseDown = *event;
- for (;;)
- {
- NXEvent peek;
- NXEvent* p = [NXApp peekNextEvent: WANTED into: &peek waitFor: FOREVER
- threshold: NX_MODALRESPTHRESHOLD];
- if (p == 0 || p->type == NX_MOUSEUP)
- break;
- else if (p->location.x > mouseDown.location.x + SLOP ||
- p->location.y > mouseDown.location.y + SLOP ||
- p->location.x < mouseDown.location.x - SLOP ||
- p->location.y < mouseDown.location.y - SLOP)
- {
- toPos = [self dragEvent: &mouseDown inPos: pos];
- break;
- }
- else
- [NXApp getNextEvent: NX_MOUSEDRAGGEDMASK];
- }
- return toPos;
- }
-
-
-
- //=============================================================================
- // SELECTION
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - selectionEvent:fromPos:
- //-----------------------------------------------------------------------------
- - (void) selectionEvent:(NXEvent const*)p fromPos:(MiscCoord_V)pos
- {
- BOOL doubleClicked = (p->data.mouse.click > 1);
-
- [tracker mouseDown: p atPos: pos];
- [scroll reflectSelection];
-
- NXTrackingTimer* timer = 0;
- startTimer( timer );
- NXEvent lastEvent = *p;
-
- while (1)
- {
- p = [NXApp getNextEvent:NX_MOUSEUPMASK |
- NX_MOUSEDRAGGEDMASK |
- NX_TIMERMASK ];
-
- if (p == 0 || p->type == NX_MOUSEUP)
- break;
- else if (p->type == NX_TIMER)
- [self autoscroll: &lastEvent];
- else
- lastEvent = *p;
-
- NXPoint new_loc = lastEvent.location;
- [self convertPoint:&new_loc fromView:0];
- MiscCoord_V new_pos = [self posForMousePt: &new_loc];
- if (new_pos != pos)
- {
- pos = new_pos;
- [tracker mouseDragged: p atPos: pos];
- [scroll reflectSelection];
- }
- }
-
- stopTimer( timer );
-
- [tracker mouseUp: p atPos: pos];
- [scroll reflectSelection];
- [self endTracking: pos];
-
- if (doubleClicked)
- [scroll sendDoubleActionIfEnabled];
- else
- [scroll sendActionIfEnabled];
- }
-
-
-
- //=============================================================================
- // MOUSE-EVENTS
- //=============================================================================
- //-----------------------------------------------------------------------------
- // - acceptsFirstMouse
- //-----------------------------------------------------------------------------
- - (BOOL) acceptsFirstMouse
- {
- return YES;
- }
-
-
- //-----------------------------------------------------------------------------
- // - mouseDown:
- //-----------------------------------------------------------------------------
- - mouseDown:(NXEvent*)p
- {
- NXPoint evpt = p->location;
- [self convertPoint: &evpt fromView: 0];
- MiscCoord_V pos = [self posForMousePt: &evpt];
- int old_mask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
- MiscPixels x, delta_x;
-
- if ([self inHotZone:&evpt forPos:&pos atX:&x deltaX:&delta_x])
- {
- [self startTracking];
- [self selectPos:pos];
- MiscPixels finalWidth;
- BOOL doit;
- doit = [self resizeEvent:p inPos:pos atX:x deltaX:delta_x
- finalWidth:&finalWidth];
- if (doit)
- {
- [window disableFlushWindow];
- [self setPos:pos width:finalWidth];
- [scroll border:[self borderType] slotResized:pos];
- [[window reenableFlushWindow] flushWindow];
- }
- [self endTracking:pos];
- [scroll sendActionIfEnabled];
- }
- else if (info->isDraggable() &&
- (info->isModifierDrag() == ((p->flags & NX_COMMANDMASK) != 0)))
- {
- [self startTracking];
- [self selectPos:pos];
- [window flushWindow];
- DPSFlush();
- MiscCoord_V toPos = [self awaitDragEvent:p inPos:pos];
- [self endTracking:toPos];
- [scroll sendActionIfEnabled];
- }
- else
- {
- assert( tracker != 0 );
- [self startTracking];
- [self selectionEvent: p fromPos: pos];
- }
-
- [window setEventMask:old_mask];
- return self;
- }
-
- @end
-