home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Utilities / FileSpy-1.1-MIHS / MyMatrix.m < prev    next >
Encoding:
Text File  |  1996-04-12  |  11.4 KB  |  384 lines

  1. /*
  2.  *   This sourcecode is part of FileSpy, a logfile observing utility.
  3.  *   Copyright (C) 1996  Felix Rauch
  4.  *
  5.  *   This program is free software; you can redistribute it and/or modify
  6.  *   it under the terms of the GNU General Public License as published by
  7.  *   the Free Software Foundation; either version 2 of the License, or
  8.  *   (at your option) any later version.
  9.  *   
  10.  *   Notice that this program may not be possessed, used, copied,
  11.  *   distributed or modified by people having to do with nuclear
  12.  *   weapons. See the file CONDITIONS for details.
  13.  *
  14.  *   This program is distributed in the hope that it will be useful,
  15.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  *   GNU General Public License for more details.
  18.  *
  19.  *   You should have received a copy of the GNU General Public License
  20.  *   along with this program; if not, write to the Free Software
  21.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  *
  23.  *   To contact the original author, try:
  24.  *   e-mail: Felix.Rauch@nice.ch
  25.  *   Traditional mail:    Felix Rauch
  26.  *            Sempacherstrasse 33
  27.  *            8032 Zurich
  28.  *            Switzerland
  29.  */
  30.  
  31. // Most of this code has been taken from:
  32. // NiftyMatrix.m
  33. // By Jayson Adams, NeXT Developer Support Team
  34. // You may freely copy, distribute and reuse the code in this example.
  35. // NeXT disclaims any warranty of any kind, expressed or implied, as to its
  36. // fitness for any particular use.
  37.  
  38. #import <dpsclient/psops.h>
  39. #import <dpsclient/wraps.h>
  40. #import <appkit/timer.h>
  41. #import <appkit/Cell.h>
  42. #import <appkit/Window.h>
  43. #import <appkit/Application.h>
  44.  
  45. #import "MyMatrix.h"
  46.  
  47.  
  48. @implementation MyMatrix
  49.  
  50.  
  51. /* #defines stolen from Draw */
  52.  
  53. #define startTimer(timer) if (!timer) timer = NXBeginTimer(NULL, 0.1, 0.01);
  54.  
  55. #define stopTimer(timer) if (timer) { \
  56.     NXEndTimer(timer); \
  57.     timer = NULL; \
  58. }
  59.  
  60. #define MOVE_MASK NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK
  61.  
  62.  
  63. /* instance methods */
  64.  
  65. - free
  66. {
  67.     [matrixCache free];
  68.     [cellCache free];
  69.     
  70.     return [super free];
  71. }
  72.  
  73. - mouseDown:(NXEvent *)theEvent
  74. {
  75.     NXPoint        mouseDownLocation, mouseUpLocation, mouseLocation;
  76.     int            eventMask, row, column, newRow;
  77.     NXRect        visibleRect, cellCacheBounds, cellFrame;
  78.     id            matrixCacheContentView, cellCacheContentView;
  79.     float        dy;
  80.     NXEvent        *event, peek;
  81.     NXTrackingTimer    *timer = NULL;
  82.     BOOL        scrolled = NO;
  83.     
  84.   /* if the Control key isn't down, show normal behavior */
  85.     if (!(theEvent->flags & NX_CONTROLMASK)) {
  86.     return [super mouseDown:theEvent];
  87.     }
  88.     
  89.   /* prepare the cell and matrix cache windows */
  90.     [self setupCacheWindows];
  91.     
  92.   /* we're now interested in mouse dragged events */
  93.     eventMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  94.  
  95.   /* find the cell that got clicked on and select it */
  96.     mouseDownLocation = theEvent->location;
  97.     [self convertPoint:&mouseDownLocation fromView:nil];
  98.     [self getRow:&row andCol:&column forPoint:&mouseDownLocation];
  99.     activeCell = [self cellAt:row :column];
  100.     [self selectCell:activeCell];
  101.     [self getCellFrame:&cellFrame at:row :column];
  102.     
  103.   /* do whatever's required for a single-click */
  104.     [self sendAction];
  105.     
  106.   /* draw a "well" in place of the selected cell (see drawSelf::) */
  107.     [self display:&cellFrame :1];
  108.     
  109.   /* copy what's currently visible into the matrix cache */
  110.     matrixCacheContentView = [matrixCache contentView];
  111.     [matrixCacheContentView lockFocus];
  112.     [self getVisibleRect:&visibleRect];
  113.     [self convertRect:&visibleRect toView:nil];
  114.     PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
  115.             NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
  116.         [window gState], 0.0, NX_HEIGHT(&visibleRect), NX_COPY);
  117.     [matrixCacheContentView unlockFocus];
  118.  
  119.   /* image the cell into its cache */
  120.     cellCacheContentView = [cellCache contentView];
  121.     [cellCacheContentView lockFocus];
  122.     [cellCacheContentView getBounds:&cellCacheBounds];
  123.     [activeCell drawSelf:&cellCacheBounds inView:cellCacheContentView];
  124.     [cellCacheContentView unlockFocus];
  125.  
  126.   /* save the mouse's location relative to the cell's origin */
  127.     dy = mouseDownLocation.y - cellFrame.origin.y;
  128.     
  129.   /* from now on we'll be drawing into ourself */
  130.     [self lockFocus];
  131.     
  132.     event = theEvent;
  133.     while (event->type != NX_MOUSEUP) {
  134.       
  135.       /* erase the active cell using the image in the matrix cache */
  136.     [self getVisibleRect:&visibleRect];
  137.     PScomposite(NX_X(&cellFrame), NX_HEIGHT(&visibleRect) -
  138.             NX_Y(&cellFrame) + NX_Y(&visibleRect) -
  139.             NX_HEIGHT(&cellFrame), NX_WIDTH(&cellFrame),
  140.             NX_HEIGHT(&cellFrame), [matrixCache gState],
  141.             NX_X(&cellFrame), NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame),
  142.             NX_COPY);
  143.     
  144.       /* move the active cell */
  145.     mouseLocation = event->location;
  146.     [self convertPoint:&mouseLocation fromView:nil];
  147.     cellFrame.origin.y = mouseLocation.y - dy;
  148.     
  149.       /* constrain the cell's location to our bounds */
  150.     if (NX_Y(&cellFrame) < NX_X(&bounds) ) {
  151.         cellFrame.origin.y = NX_X(&bounds);
  152.     } else if (NX_MAXY(&cellFrame) > NX_MAXY(&bounds)) {
  153.         cellFrame.origin.y = NX_HEIGHT(&bounds) - NX_HEIGHT(&cellFrame);
  154.     }
  155.  
  156.       /*
  157.        * make sure the cell will be entirely visible in its new location (if
  158.        * we're in a scrollView, it may not be)
  159.        */
  160.     if (!NXContainsRect(&visibleRect, &cellFrame) && mFlags.autoscroll) {    
  161.       /*
  162.        * the cell won't be entirely visible, so scroll, dood, scroll, but
  163.        * don't display on-screen yet
  164.        */
  165.         [window disableFlushWindow];
  166.         [self scrollRectToVisible:&cellFrame];
  167.         [window reenableFlushWindow];
  168.         
  169.       /* copy the new image to the matrix cache */
  170.         [matrixCacheContentView lockFocus];
  171.         [self getVisibleRect:&visibleRect];
  172.         [self convertRectFromSuperview:&visibleRect];
  173.         [self convertRect:&visibleRect toView:nil];
  174.         PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
  175.             NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
  176.             [window gState], 0.0, NX_HEIGHT(&visibleRect),
  177.             NX_COPY);
  178.         [matrixCacheContentView unlockFocus];
  179.         
  180.       /*
  181.        * note that we scrolled and start generating timer events for
  182.        * autoscrolling
  183.        */
  184.         scrolled = YES;
  185.         startTimer(timer);
  186.     } else {
  187.       /* no scrolling, so stop any timer */
  188.         stopTimer(timer);
  189.     }
  190.       
  191.       /* composite the active cell's image on top of ourself */
  192.     PScomposite(0.0, 0.0, NX_WIDTH(&cellFrame), NX_HEIGHT(&cellFrame),
  193.             [cellCache gState], NX_X(&cellFrame),
  194.             NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame), NX_COPY);
  195.     
  196.       /* now show what we've done */
  197.     [window flushWindow];
  198.     
  199.       /*
  200.        * if we autoscrolled, flush any lingering window server events to make
  201.        * the scrolling smooth
  202.        */
  203.     if (scrolled) {
  204.         NXPing();
  205.         scrolled = NO;
  206.     }
  207.     
  208.       /* save the current mouse location, just in case we need it again */
  209.     mouseLocation = event->location;
  210.     
  211.     if (![NXApp peekNextEvent:MOVE_MASK into:&peek]) {
  212.       /*
  213.        * no mouseMoved or mouseUp event immediately avaiable, so take
  214.        * mouseMoved, mouseUp, or timer
  215.        */
  216.         event = [NXApp getNextEvent:MOVE_MASK|NX_TIMERMASK];
  217.     } else {
  218.       /* get the mouseMoved or mouseUp event in the queue */
  219.         event = [NXApp getNextEvent:MOVE_MASK];
  220.     }
  221.     
  222.       /* if a timer event, mouse location isn't valid, so we'll set it */
  223.     if (event->type == NX_TIMER) {
  224.         event->location = mouseLocation;
  225.     }
  226.     }
  227.     
  228.   /* mouseUp, so stop any timer and unlock focus */
  229.     stopTimer(timer);
  230.     [self unlockFocus];
  231.     
  232.   /* find the cell under the mouse's location */
  233.     mouseUpLocation = event->location;
  234.     [self convertPoint:&mouseUpLocation fromView:nil];
  235.     if (![self getRow:&newRow andCol:&column forPoint:&mouseUpLocation]) {
  236.       /* mouse is out of bounds, so find the cell the active cell covers */
  237.     [self getRow:&newRow andCol:&column forPoint:&(cellFrame.origin)];
  238.     }
  239.     
  240.   /* we need to shuffle cells if the active cell's going to a new location */
  241.     if (newRow != row) {
  242.       /* no autodisplay while we move cells around */
  243.     [self setAutodisplay:NO];
  244.     if (newRow > row) {
  245.       /* adjust selected row if before new active cell location */
  246.         if (selectedRow <= newRow) {
  247.         selectedRow--;
  248.         }
  249.     
  250.       /*
  251.        * push all cells above the active cell's new location up one row so
  252.        * that we fill the vacant spot
  253.        */
  254.         while (row++ < newRow) {
  255.         cell = [self cellAt:row :0];
  256.         [self putCell:cell at:(row - 1) :0];
  257.         }
  258.       /* now place the active cell in its new home */
  259.         [self putCell:activeCell at:newRow :0];
  260.     } else if (newRow < row) {
  261.           /* adjust selected row if after new active cell location */
  262.         if (selectedRow >= newRow) {
  263.         selectedRow++;
  264.         }
  265.     
  266.       /*
  267.        * push all cells below the active cell's new location down one row
  268.        * so that we fill the vacant spot
  269.        */
  270.         while (row-- > newRow) {
  271.         cell = [self cellAt:row :0];
  272.         [self putCell:cell at:(row + 1) :0];
  273.         }
  274.       /* now place the active cell in its new home */
  275.         [self putCell:activeCell at:newRow :0];
  276.     }
  277.       
  278.       /* if the active cell is selected, note its new row */
  279.     if ([activeCell state]) {
  280.         selectedRow = newRow;
  281.     }
  282.       
  283.       /* make sure the active cell's visible if we're autoscrolling */
  284.     if (mFlags.autoscroll) {
  285.         [self scrollCellToVisible:newRow :0];
  286.     }
  287.       
  288.       /* no longer dragging the cell */
  289.     activeCell = 0;
  290.     
  291.       /* size to cells after all this shuffling and turn autodisplay back on */
  292.     [[self sizeToCells] setAutodisplay:YES];
  293.     } else {
  294.       /* no longer dragging the cell */
  295.     activeCell = 0;
  296.     }
  297.     
  298.   /* now redraw ourself */
  299.     [self display];
  300.     
  301.   /* set the event mask to normal */
  302.     [window setEventMask:eventMask];
  303.  
  304.     return self;
  305. }
  306.  
  307. - drawSelf:(NXRect *)rects :(int)count
  308. {
  309.     int        row, col;
  310.     NXRect    cellBorder;
  311.     int        sides[] = {NX_XMIN, NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN,
  312.                    NX_YMIN};
  313.     float    grays[] = {NX_DKGRAY, NX_DKGRAY, NX_WHITE, NX_WHITE, NX_BLACK,
  314.                NX_BLACK};
  315.                
  316.   /* do the regular drawing */
  317.     [super drawSelf:rects :count];
  318.     
  319.   /* draw a "well" if the user's dragging a cell */
  320.     if (activeCell) {
  321.       /* get the cell's frame */
  322.     [self getRow:&row andCol:&col ofCell:activeCell];
  323.     [self getCellFrame:&cellBorder at:row :col];
  324.       
  325.       /* draw the well */
  326.     if (NXIntersectsRect(&cellBorder, &(rects[0]))) {
  327.         NXDrawTiledRects(&cellBorder, (NXRect *)0, sides, grays, 6);
  328.         PSsetgray(0.17);
  329.         NXRectFill(&cellBorder);
  330.     }
  331.     }
  332.     
  333.     return self;
  334. }
  335.  
  336. - setupCacheWindows
  337. {
  338.     NXRect    visibleRect;
  339.  
  340.   /* create the matrix cache window */
  341.     [self getVisibleRect:&visibleRect];
  342.     matrixCache = [self sizeCacheWindow:matrixCache to:&(visibleRect.size)];
  343.     
  344.   /* create the cell cache window */
  345.     cellCache = [self sizeCacheWindow:cellCache to:&cellSize];
  346.  
  347.     return self;
  348. }
  349.  
  350. - sizeCacheWindow:cacheWindow to:(NXSize *)windowSize
  351. {
  352.     NXRect    cacheFrame;
  353.     
  354.     if (!cacheWindow) {
  355.       /* create the cache window if it doesn't exist */
  356.     cacheFrame.origin.x = cacheFrame.origin.y = 0.0;
  357.     cacheFrame.size = *windowSize;
  358.     cacheWindow = [[[Window alloc] initContent:&cacheFrame
  359.                        style:NX_PLAINSTYLE
  360.                        backing:NX_RETAINED
  361.                        buttonMask:0
  362.                        defer:NO] reenableDisplay];
  363.       /* flip the contentView since we are flipped */
  364.     [[cacheWindow contentView] setFlipped:YES];
  365.     } else {
  366.       /* make sure the cache window's the right size */
  367.     [cacheWindow getFrame:&cacheFrame];
  368.     if (cacheFrame.size.width != windowSize->width ||
  369.               cacheFrame.size.height != windowSize->height) {
  370.         [cacheWindow sizeWindow:windowSize->width
  371.                        :windowSize->height];
  372.     }
  373.     }
  374.     
  375.     return cacheWindow;
  376. }
  377.  
  378. - (BOOL)acceptsFirstMouse;
  379. {
  380.     return YES;
  381. }
  382.  
  383. @end
  384.