I needed a matrix of image views that would let the user change the order of the image in it using drag and drop. Thus I wrote the following class. When the user drags an image a little insertion bar is draw to give them an indication of where the insertion will occur.
– SaileshAgrawal
DragMatrix.h
/**********************
#import <AppKit/AppKit.h>
@interface DragMatrix : NSMatrix { NSEvent * downEvent; NSRect oldDrawRect, newDrawRect; BOOL shouldDraw;
int srcRow, srcCol, dstRow, dstCol; }
// Private
DragMatrix.m
#import “DragMatrix.h”
float INS_WIDTH = 2; float CIRCLE_SIZE = 6; NSString *pasteBoardTypeCover = @”Cover”;
/**********************
@implementation DragMatrix
/**********************
/**********************
/**********************
/**********************
/**********************
(BOOL)performDragOperation:(id
pboard = [sender draggingPasteboard]; types = [pboard types]; if ([types indexOfObject:pasteBoardTypeCover] != NSNotFound) { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSString *keys[4] = {@”srcRow”, @”srcCol”, @”dstRow”, @”dstCol”}; NSNumber *objects[4]; NSDictionary *dict;
objects[0] = [NSNumber numberWithInt:srcRow]; objects[1] = [NSNumber numberWithInt:srcCol]; objects[2] = [NSNumber numberWithInt:dstRow]; objects[3] = [NSNumber numberWithInt:dstCol]; dict = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:4]; [nc postNotificationName:@”DragMatrixImageMoved” object:self userInfo:dict]; }
[self clearDragDestinationMembers]; [self setNeedsDisplay:TRUE];
return YES; }
/**********************
/**********************
(NSDragOperation)draggingUpdated:(id
if ([sender draggingSource] != self) return NSDragOperationNone;
// Note that the matrix coordiante system is flipped such that the // origin is located on the top left (as opposed to bottom left). point = [self convertPoint:[sender draggingLocation] fromView:nil];
cellSize = [self cellSize]; cellSpacing = [self intercellSpacing];
if (point.y < cellSize.height) { row = 0; } else { row = (point.y-cellSize.height)/(cellSize.height+cellSpacing.height) + 1; } if (point.x < cellSize.width) { column = 0; } else { column = (point.x-cellSize.width)/(cellSize.width+cellSpacing.width) + 1; }
cellRect = [self cellFrameAtRow:row column:column]; offsetx = cellSpacing.width/2 - INS_WIDTH/2; drawRect.size.height = cellRect.size.height; drawRect.size.width = INS_WIDTH; drawRect.origin.y = cellRect.origin.y; if (point.x < cellRect.origin.x + cellSize.width/2) { // insert to the left if (column == 0) { drawRect.origin.x = cellRect.origin.x; } else { // HACK - I just nudge it 2 pixels to the left to make it // centered. I have no idea what the correct way to do this is drawRect.origin.x = cellRect.origin.x - offsetx - 2; } dstCol = column; } else { // insert to the right if (column == [self numberOfColumns] - 1) { drawRect.origin.x = cellRect.origin.x + cellRect.size.width - 2; } else { drawRect.origin.x = cellRect.origin.x + cellRect.size.width + offsetx; } dstCol = column+1; }
shouldDraw = TRUE; dstRow = row;
// Don’t ask the view to draw it self unless necessary if (NSEqualRects(drawRect, oldDrawRect) == FALSE) { newDrawRect = drawRect; [self setNeedsDisplay:TRUE]; }
return NSDragOperationAll; }
/**********************
(void) drawRect:(NSRect)rect { [super drawRect:rect]; if (shouldDraw) { NSRect rect;
shouldDraw = TRUE; NSColor blackColor] set]; [NSBezierPath fillRect:newDrawRect];
rect.size.width = CIRCLE_SIZE; rect.size.height = CIRCLE_SIZE; rect.origin.x = newDrawRect.origin.x + INS_WIDTH/2 - CIRCLE_SIZE/2; rect.origin.y = newDrawRect.origin.y - CIRCLE_SIZE; [[NSBezierPath bezierPathWithOvalInRect:rect] stroke];
oldDrawRect = newDrawRect; } }
/**********************
(void)startDrag:(NSEvent *)event { NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSDragPboard]; NSImage *scaledImage, *dragImage; NSSize size; NSPoint dragPoint, pt; NSRect theDraggedCellFrame;
pt = [self convertPoint:[event locationInWindow] fromView:nil]; [self getRow:&srcRow column:&srcCol forPoint:pt]; // Note: _scaledImage is function we add to NSImageCell in our category. scaledImage = [[self cellAtRow:srcRow column:srcCol] _scaledImage];
theDraggedCellFrame = [self cellFrameAtRow:srcRow column:srcCol]; size = [scaledImage size]; dragPoint.x = theDraggedCellFrame.origin.x
[pb declareTypes: [NSArray arrayWithObjects: pasteBoardTypeCover, nil] owner: self]; [pb setData:nil forType:pasteBoardTypeCover];
// we want to make the image a little bit transparent so the user can see where // they’re dragging to dragImage = [[[NSImage alloc] initWithSize: [scaledImage size autorelease]; [dragImage lockFocus]; [scaledImage dissolveToPoint: NSMakePoint(0,0) fraction: .5]; [dragImage unlockFocus];
[self dragImage: dragImage at: dragPoint offset: NSMakeSize(0,0) event: event pasteboard: pb source: self slideBack: YES]; }
/**********************
/**********************
/**********************
/**********************
(void)mouseUp:(NSEvent *)event { BOOL cellWasHit; int row, column; NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil]; cellWasHit = [self getRow:&row column:&column forPoint:point]; if (!cellWasHit) { [super mouseUp:event]; return; }
if ([event modifierFlags] & NSCommandKeyMask) { int r,c, s, i, i2; r = [self selectedRow]; c = [self selectedColumn]; s = [self numberOfColumns]; i = rs + c; i2 = rows + column; [self setSelectionFrom:i2 to:i2 anchor:i2 highlight:YES]; } else if ([event modifierFlags] & NSShiftKeyMask) { int r,c, s, i, i2; r = [self selectedRow]; c = [self selectedColumn]; s = [self numberOfColumns]; i = rs + c; i2 = rows + column; [self setSelectionFrom:i to:i2 anchor:i highlight:YES]; } else { [self selectCellAtRow:row column:column]; } }
/**********************
(void)mouseDragged:(NSEvent *)event { int row, column; NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil]; if ([self getRow:&row column:&column forPoint:point]) { [self startDrag:downEvent]; } [self setDownEvent:nil]; }
/**********************
/**********************
@end
Thank you for this class. You have made my week. tips hat
Amen to that! This is almost exactly what I was looking for, and certainly more than I had hoped for in an example. Thanks! – Seth. 2005-03-28
2005-02-18 Changed dstCol = column++; to dstCol = column + 1; in the above. Someone forgot how post-increment works :)