ftp.nice.ch/users/felix/FileSpy.1.0.NIHS.s.tar.gz#/FileSpy/MyMatrix.m

This is MyMatrix.m in view mode; [Download] [Up]

/*
 *   This sourcecode is part of FileSpy, a logfile observing utility.
 *   Copyright (C) 1996  Felix Rauch
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *   
 *   Notice that this program may not be possessed, used, copied,
 *   distributed or modified by people having to do with nuclear
 *   weapons. See the file CONDITIONS for details.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   To contact the original author, try:
 *   e-mail: Felix.Rauch@nice.ch
 *   Traditional mail:	Felix Rauch
 *			Sempacherstrasse 33
 *			8032 Zurich
 *			Switzerland
 */

// Most of this code has been taken from:
// NiftyMatrix.m
// By Jayson Adams, NeXT Developer Support Team
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.

#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <appkit/timer.h>
#import <appkit/Cell.h>
#import <appkit/Window.h>
#import <appkit/Application.h>

#import "MyMatrix.h"


@implementation MyMatrix


/* #defines stolen from Draw */

#define startTimer(timer) if (!timer) timer = NXBeginTimer(NULL, 0.1, 0.01);

#define stopTimer(timer) if (timer) { \
    NXEndTimer(timer); \
    timer = NULL; \
}

#define MOVE_MASK NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK


/* instance methods */

- free
{
    [matrixCache free];
    [cellCache free];
    
    return [super free];
}

- mouseDown:(NXEvent *)theEvent
{
    NXPoint		mouseDownLocation, mouseUpLocation, mouseLocation;
    int			eventMask, row, column, newRow;
    NXRect		visibleRect, cellCacheBounds, cellFrame;
    id			matrixCacheContentView, cellCacheContentView;
    float		dy;
    NXEvent		*event, peek;
    NXTrackingTimer	*timer = NULL;
    BOOL		scrolled = NO;
    
  /* if the Control key isn't down, show normal behavior */
    if (!(theEvent->flags & NX_CONTROLMASK)) {
	return [super mouseDown:theEvent];
    }
    
  /* prepare the cell and matrix cache windows */
    [self setupCacheWindows];
    
  /* we're now interested in mouse dragged events */
    eventMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];

  /* find the cell that got clicked on and select it */
    mouseDownLocation = theEvent->location;
    [self convertPoint:&mouseDownLocation fromView:nil];
    [self getRow:&row andCol:&column forPoint:&mouseDownLocation];
    activeCell = [self cellAt:row :column];
    [self selectCell:activeCell];
    [self getCellFrame:&cellFrame at:row :column];
    
  /* do whatever's required for a single-click */
    [self sendAction];
    
  /* draw a "well" in place of the selected cell (see drawSelf::) */
    [self display:&cellFrame :1];
    
  /* copy what's currently visible into the matrix cache */
    matrixCacheContentView = [matrixCache contentView];
    [matrixCacheContentView lockFocus];
    [self getVisibleRect:&visibleRect];
    [self convertRect:&visibleRect toView:nil];
    PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
    		NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
		[window gState], 0.0, NX_HEIGHT(&visibleRect), NX_COPY);
    [matrixCacheContentView unlockFocus];

  /* image the cell into its cache */
    cellCacheContentView = [cellCache contentView];
    [cellCacheContentView lockFocus];
    [cellCacheContentView getBounds:&cellCacheBounds];
    [activeCell drawSelf:&cellCacheBounds inView:cellCacheContentView];
    [cellCacheContentView unlockFocus];

  /* save the mouse's location relative to the cell's origin */
    dy = mouseDownLocation.y - cellFrame.origin.y;
    
  /* from now on we'll be drawing into ourself */
    [self lockFocus];
    
    event = theEvent;
    while (event->type != NX_MOUSEUP) {
      
      /* erase the active cell using the image in the matrix cache */
	[self getVisibleRect:&visibleRect];
	PScomposite(NX_X(&cellFrame), NX_HEIGHT(&visibleRect) -
		    NX_Y(&cellFrame) + NX_Y(&visibleRect) -
		    NX_HEIGHT(&cellFrame), NX_WIDTH(&cellFrame),
		    NX_HEIGHT(&cellFrame), [matrixCache gState],
		    NX_X(&cellFrame), NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame),
		    NX_COPY);
	
      /* move the active cell */
	mouseLocation = event->location;
	[self convertPoint:&mouseLocation fromView:nil];
	cellFrame.origin.y = mouseLocation.y - dy;
	
      /* constrain the cell's location to our bounds */
	if (NX_Y(&cellFrame) < NX_X(&bounds) ) {
	    cellFrame.origin.y = NX_X(&bounds);
	} else if (NX_MAXY(&cellFrame) > NX_MAXY(&bounds)) {
	    cellFrame.origin.y = NX_HEIGHT(&bounds) - NX_HEIGHT(&cellFrame);
	}

      /*
       * make sure the cell will be entirely visible in its new location (if
       * we're in a scrollView, it may not be)
       */
	if (!NXContainsRect(&visibleRect, &cellFrame) && mFlags.autoscroll) {	
	  /*
	   * the cell won't be entirely visible, so scroll, dood, scroll, but
	   * don't display on-screen yet
	   */
	    [window disableFlushWindow];
	    [self scrollRectToVisible:&cellFrame];
	    [window reenableFlushWindow];
	    
	  /* copy the new image to the matrix cache */
	    [matrixCacheContentView lockFocus];
	    [self getVisibleRect:&visibleRect];
	    [self convertRectFromSuperview:&visibleRect];
	    [self convertRect:&visibleRect toView:nil];
	    PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
			NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
			[window gState], 0.0, NX_HEIGHT(&visibleRect),
			NX_COPY);
	    [matrixCacheContentView unlockFocus];
	    
	  /*
	   * note that we scrolled and start generating timer events for
	   * autoscrolling
	   */
	    scrolled = YES;
	    startTimer(timer);
	} else {
	  /* no scrolling, so stop any timer */
	    stopTimer(timer);
	}
      
      /* composite the active cell's image on top of ourself */
	PScomposite(0.0, 0.0, NX_WIDTH(&cellFrame), NX_HEIGHT(&cellFrame),
		    [cellCache gState], NX_X(&cellFrame),
		    NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame), NX_COPY);
	
      /* now show what we've done */
	[window flushWindow];
	
      /*
       * if we autoscrolled, flush any lingering window server events to make
       * the scrolling smooth
       */
	if (scrolled) {
	    NXPing();
	    scrolled = NO;
	}
	
      /* save the current mouse location, just in case we need it again */
	mouseLocation = event->location;
	
	if (![NXApp peekNextEvent:MOVE_MASK into:&peek]) {
	  /*
	   * no mouseMoved or mouseUp event immediately avaiable, so take
	   * mouseMoved, mouseUp, or timer
	   */
	    event = [NXApp getNextEvent:MOVE_MASK|NX_TIMERMASK];
	} else {
	  /* get the mouseMoved or mouseUp event in the queue */
	    event = [NXApp getNextEvent:MOVE_MASK];
	}
	
      /* if a timer event, mouse location isn't valid, so we'll set it */
	if (event->type == NX_TIMER) {
	    event->location = mouseLocation;
	}
    }
    
  /* mouseUp, so stop any timer and unlock focus */
    stopTimer(timer);
    [self unlockFocus];
    
  /* find the cell under the mouse's location */
    mouseUpLocation = event->location;
    [self convertPoint:&mouseUpLocation fromView:nil];
    if (![self getRow:&newRow andCol:&column forPoint:&mouseUpLocation]) {
      /* mouse is out of bounds, so find the cell the active cell covers */
	[self getRow:&newRow andCol:&column forPoint:&(cellFrame.origin)];
    }
    
  /* we need to shuffle cells if the active cell's going to a new location */
    if (newRow != row) {
      /* no autodisplay while we move cells around */
	[self setAutodisplay:NO];
	if (newRow > row) {
	  /* adjust selected row if before new active cell location */
	    if (selectedRow <= newRow) {
		selectedRow--;
	    }
	
	  /*
	   * push all cells above the active cell's new location up one row so
	   * that we fill the vacant spot
	   */
	    while (row++ < newRow) {
		cell = [self cellAt:row :0];
		[self putCell:cell at:(row - 1) :0];
	    }
	  /* now place the active cell in its new home */
	    [self putCell:activeCell at:newRow :0];
	} else if (newRow < row) {
          /* adjust selected row if after new active cell location */
	    if (selectedRow >= newRow) {
		selectedRow++;
	    }
	
	  /*
	   * push all cells below the active cell's new location down one row
	   * so that we fill the vacant spot
	   */
	    while (row-- > newRow) {
		cell = [self cellAt:row :0];
		[self putCell:cell at:(row + 1) :0];
	    }
	  /* now place the active cell in its new home */
	    [self putCell:activeCell at:newRow :0];
	}
      
      /* if the active cell is selected, note its new row */
	if ([activeCell state]) {
	    selectedRow = newRow;
	}
      
      /* make sure the active cell's visible if we're autoscrolling */
	if (mFlags.autoscroll) {
	    [self scrollCellToVisible:newRow :0];
	}
      
      /* no longer dragging the cell */
	activeCell = 0;
    
      /* size to cells after all this shuffling and turn autodisplay back on */
	[[self sizeToCells] setAutodisplay:YES];
    } else {
      /* no longer dragging the cell */
	activeCell = 0;
    }
    
  /* now redraw ourself */
    [self display];
    
  /* set the event mask to normal */
    [window setEventMask:eventMask];

    return self;
}

- drawSelf:(NXRect *)rects :(int)count
{
    int		row, col;
    NXRect	cellBorder;
    int		sides[] = {NX_XMIN, NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN,
    			   NX_YMIN};
    float	grays[] = {NX_DKGRAY, NX_DKGRAY, NX_WHITE, NX_WHITE, NX_BLACK,
			   NX_BLACK};
			   
  /* do the regular drawing */
    [super drawSelf:rects :count];
    
  /* draw a "well" if the user's dragging a cell */
    if (activeCell) {
      /* get the cell's frame */
	[self getRow:&row andCol:&col ofCell:activeCell];
	[self getCellFrame:&cellBorder at:row :col];
      
      /* draw the well */
	if (NXIntersectsRect(&cellBorder, &(rects[0]))) {
	    NXDrawTiledRects(&cellBorder, (NXRect *)0, sides, grays, 6);
	    PSsetgray(0.17);
	    NXRectFill(&cellBorder);
	}
    }
    
    return self;
}

- setupCacheWindows
{
    NXRect	visibleRect;

  /* create the matrix cache window */
    [self getVisibleRect:&visibleRect];
    matrixCache = [self sizeCacheWindow:matrixCache to:&(visibleRect.size)];
    
  /* create the cell cache window */
    cellCache = [self sizeCacheWindow:cellCache to:&cellSize];

    return self;
}

- sizeCacheWindow:cacheWindow to:(NXSize *)windowSize
{
    NXRect	cacheFrame;
    
    if (!cacheWindow) {
      /* create the cache window if it doesn't exist */
	cacheFrame.origin.x = cacheFrame.origin.y = 0.0;
	cacheFrame.size = *windowSize;
	cacheWindow = [[[Window alloc] initContent:&cacheFrame
				       style:NX_PLAINSTYLE
				       backing:NX_RETAINED
				       buttonMask:0
				       defer:NO] reenableDisplay];
      /* flip the contentView since we are flipped */
	[[cacheWindow contentView] setFlipped:YES];
    } else {
      /* make sure the cache window's the right size */
	[cacheWindow getFrame:&cacheFrame];
	if (cacheFrame.size.width != windowSize->width ||
      	    cacheFrame.size.height != windowSize->height) {
	    [cacheWindow sizeWindow:windowSize->width
			    	   :windowSize->height];
	}
    }
    
    return cacheWindow;
}

- (BOOL)acceptsFirstMouse;
{
    return YES;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.