Annotation of Examples/AppKit/ScrollDoodScroll/NiftyMatrix.m, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.