home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / NewsgroupSetMatrix.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  11.3 KB  |  399 lines

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