This is MiscDraggableCellMatrix.m in view mode; [Download] [Up]
// Suppress compiler warning about rcsid being unused, yet prevent assembler // code for this function from being produced. inline extern const char *suppressCompilerWarning(void) { static const char *rcsid = "$Id$ cc: "__FILE__" "__DATE__" "__TIME__; return rcsid; } // // ------------- MiscDraggableCellMatrix Class Implementation ----------------- // // NSMatrix subclass that supports reordering cells be Control-dragging. // // Written by Art Isbell (adapted from NiftyMatrix by Jayson Adams, NeXT // Developer Support Team) // Copyright 1996 by Art Isbell. // Version 1.0. All rights reserved. // // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the author // 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. // // ---------------------------------------------------------------------------- // // ----------------------------- Header Files --------------------------------- #import <AppKit/AppKit.h> #ifndef NOMISC #import <misckit/MiscDraggableCellMatrix.h> #else NOMISC #import "MiscDraggableCellMatrix.h" #endif NOMISC // ---------------- Typedef, Struct, and Union Declarations ------------------- // --------------------- Constant and Enum Definitions ------------------------ NSString *MiscMatrixRowDidMove = @"MatrixRowMoved"; // ------------------------- Function Declarations ---------------------------- @interface MiscDraggableCellMatrix(Private) // ---------------------- Private Method Declarations ------------------------- - (void)_setupCacheWindows; - (NSWindow *)_sizeCacheWindow:(NSWindow *)aCacheWindow to:(NSSize)aWindowSize; - (void)_cacheCellWithFrame:(NSRect)aCellFrame; - (void)_cacheMatrixWithVisibleRect:(NSRect)aVisibleRect; - (NSEvent *)_nextEventWithLocation:(NSPoint)aLocation; - (void)_moveActiveCell:(NSCell *)aCell fromRow:(int)aRow toRow:(int)aNewRow; - (void)_postNotificationWithOldRow:(int)aRow newRow:(int)aNewRow; - (void)_highlightNewRow:(int)newRow; @end @implementation MiscDraggableCellMatrix // ---------------------- Factory Method Definitions -------------------------- // ---------------- Overridden Instance Method Definitions -------------------- - (void)dealloc { [_matrixCache release]; [_cellCache release]; [super dealloc]; } // This is a terribly complex method that needs to be simplified, but it was // converted from working NEXTSTEP code written by another, so simplifying it // would run the high risk of breaking it. - (void)mouseDown:(NSEvent *)theEvent { // If the Control key is down, show new behavior. if (([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask) { int rowL; int columnL; int newRowL; int cellCacheGStateL; int matrixCacheGStateL; float dyL; float boundsMinYL; float cellMinXL; float cellMinYL; float cellHeightL; float cellWidthL; BOOL acceptsMouseMovedEventsL; BOOL arePeriodicEventsOccurringL; NSPoint mouseDownLocationL; NSRect boundsL; NSRect cellFrameL; NSWindow *windowL; NSView *superviewL; NSEvent *eventL = theEvent; // Post bogus mouse up event so that message to super's mouseDown: will // return. [NSApp postEvent:[NSEvent mouseEventWithType:NSLeftMouseUp location:[eventL locationInWindow] modifierFlags:[eventL modifierFlags] timestamp:[eventL timestamp] windowNumber:[eventL windowNumber] context:[eventL context] eventNumber:[eventL eventNumber] clickCount:1 pressure:0.0] atStart:YES]; // Select and highlight cell. [super mouseDown:eventL]; // Prepare the cell and matrix cache windows. [self _setupCacheWindows]; matrixCacheGStateL = [_matrixCache gState]; cellCacheGStateL = [_cellCache gState]; windowL = [self window]; acceptsMouseMovedEventsL = [windowL acceptsMouseMovedEvents]; // We're now interested in mouse dragged events. if (!acceptsMouseMovedEventsL) { [windowL setAcceptsMouseMovedEvents:YES]; } mouseDownLocationL = [self convertPoint:[eventL locationInWindow] fromView:nil]; // Find the cell that got clicked on and select it. [self getRow:&rowL column:&columnL forPoint:mouseDownLocationL]; _activeCell = [self cellAtRow:rowL column:columnL]; cellFrameL = [self cellFrameAtRow:rowL column:columnL]; // Save the mouse's location relative to the cell's origin. dyL = mouseDownLocationL.y - cellFrameL.origin.y; // Image the cell into its cache. [self _cacheCellWithFrame:[self convertRect:cellFrameL toView:nil]]; // Draw a "well" in place of the selected cell (see drawRect:). [self displayRect:cellFrameL]; // Copy what's currently visible into the matrix cache. [self _cacheMatrixWithVisibleRect:[self convertRect:[self visibleRect] toView:nil]]; // Initialize some variables used in "do" loop. arePeriodicEventsOccurringL = NO; boundsL = [self bounds]; superviewL = [self superview]; boundsMinYL = NSMinY(boundsL); cellMinXL = NSMinX(cellFrameL); cellMinYL = NSMinY(cellFrameL); cellHeightL = NSHeight(cellFrameL); cellWidthL = NSWidth(cellFrameL); // From now on we'll be drawing into ourself. [self lockFocus]; do { BOOL didScrollL; NSPoint eventLocationL; NSPoint mouseLocationL; NSRect visibleRectL; // If this isn't the mouse down event, erase the active cell using // the image in the matrix cache. if (eventL != theEvent) { visibleRectL = [self visibleRect]; PScomposite(cellMinXL, NSHeight(visibleRectL) - cellMinYL + NSMinY(visibleRectL) - cellHeightL, cellWidthL, cellHeightL, matrixCacheGStateL, cellMinXL, cellMinYL + cellHeightL, NSCompositeCopy); } eventLocationL = [eventL locationInWindow]; mouseLocationL = [self convertPoint:eventLocationL fromView:nil]; // Move the active cell. cellMinYL = cellFrameL.origin.y = mouseLocationL.y - dyL; // Constrain the cell's location to our bounds. if (cellMinYL < boundsMinYL) { cellMinYL = cellFrameL.origin.y = boundsMinYL; } else if (NSMaxY(boundsL) < NSMaxY(cellFrameL)) { cellMinYL = cellFrameL.origin.y = NSHeight(boundsL) - cellHeightL; } // Ensure the cell will be entirely visible in its new location // (if we're in a scrollView, it may not be). didScrollL = (BOOL)([self isAutoscroll] && !NSContainsRect(visibleRectL, cellFrameL)); if (didScrollL) { // The cell won't be entirely visible, so scroll, dood, scroll. [self scrollRectToVisible:cellFrameL]; // Copy the new image to the matrix cache. visibleRectL = [self convertRect:[self visibleRect] fromView:superviewL]; [self _cacheMatrixWithVisibleRect:[self convertRect:visibleRectL toView:nil]]; // Start generating timer events for autoscrolling if this // isn't the mouse down event to prevent continuous scrolling // when a partially-visible cell is clicked. if ((eventL != theEvent) && !arePeriodicEventsOccurringL) { [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.03]; arePeriodicEventsOccurringL = YES; } } else { if (arePeriodicEventsOccurringL) { // No scrolling, so stop any timer. [NSEvent stopPeriodicEvents]; arePeriodicEventsOccurringL = NO; } } // Composite the active cell's image on top of ourself. PScomposite(0.0, 0.0, cellWidthL, cellHeightL, cellCacheGStateL, cellMinXL, cellMinYL + cellHeightL, NSCompositeCopy); // Now show what we've done. [windowL flushWindow]; // If we autodidScroll, flush any lingering window server events to // make the scrolling smooth. if (didScrollL) { [[NSDPSContext currentContext] wait]; didScrollL = NO; } // If no mouse up or dragged event is available now, wait for and // get next mouse up, mouse dragged, or periodic event. eventL = [self _nextEventWithLocation:eventLocationL]; } while ([eventL type] != NSLeftMouseUp); // MouseUp, so unlock focus and stop any timer. [self unlockFocus]; if (arePeriodicEventsOccurringL) { [NSEvent stopPeriodicEvents]; arePeriodicEventsOccurringL = NO; } [windowL discardEventsMatchingMask:NSAnyEventMask beforeEvent:eventL]; // Do whatever's required for a single-click. [self sendAction]; // If mouse is out of bounds, find the cell the active cell covers. if (![self getRow:&newRowL column:&columnL forPoint:[self convertPoint:[eventL locationInWindow] fromView:nil]]) { [self getRow:&newRowL column:&columnL forPoint:cellFrameL.origin]; } // We need to shuffle cells if the active cell's going to a new // location. if (newRowL != rowL) { [self _moveActiveCell:_activeCell fromRow:rowL toRow:newRowL]; [self _postNotificationWithOldRow:rowL newRow:newRowL]; [self _highlightNewRow:newRowL]; } // No longer dragging the cell. _activeCell = nil; // Now redraw ourself. [self setNeedsDisplay:YES]; // Set the event mask to normal. if (acceptsMouseMovedEventsL != [windowL acceptsMouseMovedEvents]) { [windowL setAcceptsMouseMovedEvents:acceptsMouseMovedEventsL]; } } else { [super mouseDown:theEvent]; } } - (void)drawRect:(NSRect)aRect { // Do the regular drawing. [super drawRect:aRect]; // Draw a "well" if the user's dragging a cell. if (_activeCell != nil) { int rowL; int columnL; NSRect cellBorderL; // Get the cell's frame. [self getRow:&rowL column:&columnL ofCell:_activeCell]; cellBorderL = [self cellFrameAtRow:rowL column:columnL]; // Draw the well. if (!NSIsEmptyRect(NSIntersectionRect(cellBorderL, aRect))) { NSRectEdge sides[] = {NSMinXEdge, NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge}; float grays[] = {NSDarkGray, NSDarkGray, NSWhite, NSWhite, NSBlack, NSBlack}; cellBorderL = NSDrawTiledRects(cellBorderL, NSZeroRect, sides, grays, 6); [[NSColor colorWithCalibratedWhite:0.17 alpha:1.0] set]; NSRectFill(cellBorderL); } } } // -------------------- New Instance Method Definitions ----------------------- // ----------------- Delegate Instance Method Definitions --------------------- @end @implementation MiscDraggableCellMatrix(Private) // ---------------------- Private Method Definitions -------------------------- - (void)_setupCacheWindows { NSRect visibleRectL = [self visibleRect]; // Create the matrix cache window. _matrixCache = [self _sizeCacheWindow:_matrixCache to:visibleRectL.size]; // Create the cell cache window. _cellCache = [self _sizeCacheWindow:_cellCache to:[self cellSize]]; } - (NSWindow *)_sizeCacheWindow:(NSWindow *)aCacheWindow to:(NSSize)aWindowSize { NSRect cacheFrameL; if (aCacheWindow == nil) { // Create the cache window if it doesn't exist. cacheFrameL = NSMakeRect(0.0, 0.0, aWindowSize.width, aWindowSize.height); aCacheWindow = [[NSWindow allocWithZone:[self zone]] initWithContentRect:cacheFrameL styleMask:NSBorderlessWindowMask backing:NSBackingStoreRetained defer:NO]; // Display window so that it's available for drawing into. [aCacheWindow display]; } else { // Ensure the cache window's the right size. cacheFrameL = [aCacheWindow frame]; if (NSWidth(cacheFrameL) != aWindowSize.width || NSHeight(cacheFrameL) != aWindowSize.height) { [aCacheWindow setContentSize:aWindowSize]; } } return aCacheWindow; } - (void)_cacheCellWithFrame:(NSRect)aCellFrame { NSView *cellCacheContentViewL = [_cellCache contentView]; [cellCacheContentViewL lockFocus]; PScomposite(NSMinX(aCellFrame), NSMinY(aCellFrame), NSWidth(aCellFrame), NSHeight(aCellFrame), [[self window] gState], 0.0, 0.0, NSCompositeCopy); [cellCacheContentViewL unlockFocus]; } - (void)_cacheMatrixWithVisibleRect:(NSRect)aVisibleRect { NSView *matrixCacheContentViewL = [_matrixCache contentView]; [matrixCacheContentViewL lockFocus]; PScomposite(NSMinX(aVisibleRect), NSMinY(aVisibleRect), NSWidth(aVisibleRect), NSHeight(aVisibleRect), [[self window] gState], 0.0, 0.0, NSCompositeCopy); [matrixCacheContentViewL unlockFocus]; } - (NSEvent *)_nextEventWithLocation:(NSPoint)aLocation { NSEvent *eventL = [NSApp nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask untilDate:[NSDate date] inMode:NSEventTrackingRunLoopMode dequeue:YES]; if (eventL == nil) { eventL = [NSApp nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES]; } if ([eventL type] == NSPeriodic) { eventL = [NSEvent otherEventWithType:NSPeriodic location:aLocation modifierFlags:[eventL modifierFlags] timestamp:[eventL timestamp] windowNumber:[eventL windowNumber] context:[eventL context] subtype:[eventL subtype] data1:[eventL data1] data2:[eventL data2]]; } return eventL; } - (void)_moveActiveCell:(NSCell *)aCell fromRow:(int)aRow toRow:(int)aNewRow { // Prevent accidental release of aCell. [aCell retain]; if (aRow < aNewRow) { // Push all cells above the active cell's new location up one row so // that we fill the vacant spot. for (; aRow < aNewRow; aRow++) { [self putCell:[self cellAtRow:aRow + 1 column:0] atRow:aRow column:0]; } } else if (aNewRow < aRow) { // Push all cells below the active cell's new location down one row so // that we fill the vacant spot. for (; aNewRow < aRow; aRow--) { [self putCell:[self cellAtRow:aRow - 1 column:0] atRow:aRow column:0]; } } // Now place the active cell in its new home. [self putCell:aCell atRow:aNewRow column:0]; [aCell release]; } - (void)_postNotificationWithOldRow:(int)aRow newRow:(int)aNewRow { NSDictionary *userInfoL = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:aRow], @"OldRow", [NSNumber numberWithInt:aNewRow], @"NewRow", nil]; [[NSNotificationCenter defaultCenter] postNotificationName:MiscMatrixRowDidMove object:self userInfo:userInfoL]; } - (void)_highlightNewRow:(int)newRow { NSEvent *eventL = [NSApp currentEvent]; unsigned modifierFlagsL = [eventL modifierFlags]; NSTimeInterval timestampL = [eventL timestamp]; int windowNumberL = [eventL windowNumber]; NSDPSContext *contextL = [eventL context]; int eventNumberL = [eventL eventNumber]; NSRect cellFrameL = [self cellFrameAtRow:newRow column:0]; NSPoint locationL = [self convertPoint:cellFrameL.origin toView:nil]; // Can't figure out how to highlight cell in newRow, so do it the hard way. eventL = [NSEvent mouseEventWithType:NSLeftMouseUp location:locationL modifierFlags:modifierFlagsL timestamp:timestampL windowNumber:windowNumberL context:contextL eventNumber:eventNumberL clickCount:1 pressure:0.0]; [NSApp postEvent:eventL atStart:YES]; // Select and highlight cell. [super mouseDown:[NSEvent mouseEventWithType:NSLeftMouseDown location:locationL modifierFlags:modifierFlagsL timestamp:timestampL windowNumber:windowNumberL context:contextL eventNumber:eventNumberL clickCount:1 pressure:0.0]]; } @end // ------------------------- Function Definitions -----------------------------
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.