Annotation of Examples/AppKit/Draw/gvDrag.m, revision 1.1.1.1

1.1       root        1: #import "draw.h"
                      2: 
                      3: /* See the Dragging.rtf for overview of Dragging in Draw. */
                      4: 
                      5: #define ERROR -1
                      6: 
                      7: @implementation GraphicView(Drag)
                      8: 
                      9: /*
                     10:  * Determines whether there is a Graphic at the specified point that is
                     11:  * willing to accept a dragged color.  If color is non-NULL, then the
                     12:  * color is actually set in that Graphic (i.e. you can find out if any
                     13:  * Graphics is "willing" to accept a color by calling this with
                     14:  * color == NULL).
                     15:  *
                     16:  * We use the mechanism of sending a Graphic the message colorAcceptorAt:
                     17:  * and letting it return a Graphic (rather than just asking each Graphic
                     18:  * doYouAcceptAColorAt:) so that Group's of Graphics can return one of
                     19:  * the Graphic's inside itself as the one to handle a dropped color.
                     20:  */
                     21: 
                     22: - (BOOL)acceptsColor:(NXColor *)color atPoint:(const NXPoint *)point
                     23: {
                     24:     id change;
                     25:     NXRect gbounds;
                     26:     Graphic *graphic;
                     27:     int i, count = [glist count];
                     28: 
                     29:     if (!point) return NO;
                     30: 
                     31:     for (i = 0; i < count; i++) {
                     32:        graphic = [glist objectAt:i];
                     33:        if (graphic = [graphic colorAcceptorAt:point]) {
                     34:            if (color) {
                     35:                [graphic getExtendedBounds:&gbounds];
                     36:                change = [[FillGraphicsChange alloc] initGraphicView:self forChangeToGraphic:graphic];
                     37:                [change startChange];
                     38:                    [graphic setFillColor:color];
                     39:                    [self cache:&gbounds];              // acceptColor:atPoint:
                     40:                    [window flushWindow];
                     41:                [change endChange];
                     42:                return YES;
                     43:            } else {
                     44:                return YES;
                     45:            }
                     46:        }
                     47:     }
                     48: 
                     49:     return NO;
                     50: }
                     51: 
                     52: /* Just counts the number of Pasteboard types in types. */
                     53: 
                     54: static int countTypes(const NXAtom *types)
                     55: {
                     56:     int count = 0;
                     57:     while (types && *types) {
                     58:        count++;
                     59:        types++;
                     60:     }
                     61:     return count;
                     62: }
                     63: 
                     64: /*
                     65:  * Registers the view with the Workspace Manager so that when the
                     66:  * user picks up an icon in the Workspace and drags it over our view
                     67:  * and lets go, dragging messages will be sent to our view.
                     68:  * We register for NXFilenamePboardType because we handle data link
                     69:  * files and NXImage and Text files dragged into draw as well as any
                     70:  * random file when the Control key is depressed (indicating a link
                     71:  * operation) during the drag.  We also accept anything that NXImage
                     72:  * is able to handle (even if it's not in a file, i.e., it's directly
                     73:  * in the dragged Pasteboard--unusual, but we can handle it, so why
                     74:  * not?).
                     75:  */
                     76: 
                     77: - registerForDragging
                     78: {
                     79:     const NXAtom *pboardImageTypes;
                     80:     [self registerForDraggedTypes:&NXFilenamePboardType count:1];
                     81:     [self registerForDraggedTypes:&NXColorPboardType count:1];
                     82:     pboardImageTypes = [NXImage imagePasteboardTypes];
                     83:     [self registerForDraggedTypes:pboardImageTypes count:countTypes(pboardImageTypes)];
                     84:     return self;
                     85: }
                     86: 
                     87: /*
                     88:  * This is where we determine whether the contents of the dragging Pasteboard
                     89:  * is acceptable to Draw.  The gvFlags.drag*Ok flags say whether we can accept
                     90:  * the dragged information as a result of a copy or link (or both) operation.
                     91:  * If NXImage can handle the Pasteboard, then we know we can do copy.  We
                     92:  * always know we can do link as long as we have a linkManager.  We cache as
                     93:  * much of the answer around as we can so that draggingUpdated: will be fast.
                     94:  * Of course, we can't cache the part of the answer which is dependent upon
                     95:  * the position inside our view (important for colors).
                     96:  */
                     97: 
                     98: - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
                     99: {
                    100:     Pasteboard *pboard;
                    101:     NXDragOperation sourceMask;
                    102: 
                    103:     gvFlags.dragCopyOk = NO;
                    104:     gvFlags.dragLinkOk = NO;
                    105: 
                    106:     sourceMask = [sender draggingSourceOperationMask];
                    107:     pboard = [sender draggingPasteboard];
                    108: 
                    109:     if (IncludesType([pboard types], NXColorPboardType)) {
                    110:        NXPoint p = [sender draggingLocation];
                    111:        [self convertPoint:&p fromView:nil];
                    112:        if ([self acceptsColor:NULL atPoint:&p]) return NX_DragOperationGeneric;
                    113:     } else if (sourceMask & NX_DragOperationCopy) {
                    114:        if ([NXImage canInitFromPasteboard:pboard]) {
                    115:            gvFlags.dragCopyOk = YES;
                    116:        } else if (IncludesType([pboard types], NXFilenamePboardType)) {
                    117:            gvFlags.dragCopyOk = YES;
                    118:        }
                    119:     }
                    120: 
                    121:     if (linkManager) gvFlags.dragLinkOk = YES;
                    122: 
                    123:     if (sourceMask & NX_DragOperationCopy) {
                    124:        if (gvFlags.dragCopyOk) return NX_DragOperationCopy;
                    125:     } else if (sourceMask & NX_DragOperationLink) {
                    126:        if (gvFlags.dragLinkOk) return NX_DragOperationLink;
                    127:     }
                    128: 
                    129:     return NX_DragOperationNone;
                    130: }
                    131: 
                    132: /*
                    133:  * This is basically the same as draggingEntered: but, instead of being
                    134:  * called when the dragging enters our view, it is called every time the
                    135:  * mouse moves while dragging inside our view.
                    136:  */
                    137: 
                    138: - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
                    139: {
                    140:     NXDragOperation sourceMask = [sender draggingSourceOperationMask];
                    141:     if (IncludesType([[sender draggingPasteboard] types], NXColorPboardType)) {
                    142:        NXPoint p = [sender draggingLocation];
                    143:        [self convertPoint:&p fromView:nil];
                    144:        if ([self acceptsColor:NULL atPoint:&p]) return NX_DragOperationGeneric;
                    145:     } else if (sourceMask & NX_DragOperationCopy) {
                    146:        if (gvFlags.dragCopyOk) return NX_DragOperationCopy;
                    147:     } else if (sourceMask & NX_DragOperationLink) {
                    148:        if (gvFlags.dragLinkOk) return NX_DragOperationLink;
                    149:     }
                    150:     return NX_DragOperationNone;
                    151: }
                    152: 
                    153: #define FILE_ICON_OR_LINK_BUTTON NXLocalString("Do you want the link to this file to appear as a Link Button or as the icon of the file?", NULL, "Question asked of a user when he drags a file icon into Draw.")
                    154: #define FILE_ICON NXLocalizedString("File Icon", NULL, "Button choice if user wants a file dragged into Draw to appear as an icon.")
                    155: #define LINK_BUTTON NXLocalizedString("Link Button", NULL, "Button choice if user wants a file dragged into Draw to appear as a link button.")
                    156: #define FILE_CONTENTS_OR_ICON_OR_LINK_BUTTON NXLocalizedString("Do you want the contents of this file to appear in Draw, or would you like to create only a link to this data, in which case, do you want the link to appear as a Link Button or as the icon of the file?", NULL, "Question asked of a user when he drags a file which can be imaged directly in Draw into Draw.")
                    157: #define FILE_CONTENTS NXLocalizedString("File Contents", NULL, "Button choice if user wants a file dragged into Draw to appear as the contents of the file.")
                    158: 
                    159: #define BAD_IMAGE NXLocalString("Unable to import that image into Draw.", NULL, "Message of alert which lets the user know that an image (PostScript or TIFF or something) that he tried to import was some how defective.")
                    160: 
                    161: /*
                    162:  * Takes the name of a saved link (.objlink) file and incorporates
                    163:  * the "linked thing" into the view.  This is really just some glue between
                    164:  * the dragging mechanism and the addLink:toGraphic:at:update: method
                    165:  * which does all the work of actually incorporating the linked stuff
                    166:  * into the view.
                    167:  */
                    168: 
                    169: - (int)createGraphicForDraggedLink:(const char *)file at:(const NXPoint *)p
                    170: {
                    171:     NXDataLink *link;
                    172:     Graphic *graphic = nil;
                    173: 
                    174:     if (linkManager) {
                    175:        link = [[NXDataLink alloc] initFromFile:file];
                    176:        if ([self addLink:link toGraphic:graphic at:p update:UPDATE_IMMEDIATELY]) {
                    177:            return YES;
                    178:        } else {
                    179:            // addLink: frees the link if there's an error
                    180:            NXRunAlertPanel(NULL, BAD_IMAGE, NULL, NULL, NULL);
                    181:            return ERROR;
                    182:        }
                    183:     }
                    184: 
                    185:     return NO;
                    186: }
                    187: 
                    188: /* A couple of convenience methods to determine what kind of file we have. */
                    189: 
                    190: static BOOL isNXImageFile(const char *file)
                    191: {
                    192:     const char *extension = strrchr(file, '.');
                    193:     return (extension && [NXImage imageRepForFileType:extension+1]) ? YES : NO;
                    194: }
                    195: 
                    196: static BOOL isRTFFile(const char *file)
                    197: {
                    198:     const char *extension = strrchr(file, '.');
                    199:     return (extension && !strcmp(extension, ".rtf")) ? YES : NO;
                    200: }
                    201: 
                    202: /*
                    203:  * Creates a Graphic from a file NXImage or the Text object can handle
                    204:  * (or just allows linking it if NXImage nor Text can handle the file).
                    205:  * It links to it if the doLink is YES.
                    206:  *
                    207:  * If we are linking, then we ask the user if she wants the file's icon,
                    208:  * a link button, or (if we can do so) the actually contents of the file
                    209:  * to appear in the view.
                    210:  *
                    211:  * Note the use of the workspace protocol object to get information about
                    212:  * the file.  We know that we cannot import the contents of a WriteNow or
                    213:  * other known document format into Draw, so we don't even give the user
                    214:  * the option of trying to do so.
                    215:  *
                    216:  * Again, if it ends up that we are linking, we just call the all-powerful
                    217:  * addLink:toGraphic:at:update: method in gvLinks.m, otherwise, we just
                    218:  * call placeGraphic:at:.
                    219:  */
                    220: 
                    221: - (int)createGraphicForDraggedFile:(const char *)file withIcon:(NXImage *)icon at:(const NXPoint *)p andLink:(BOOL)doLink
                    222: {
                    223:     NXAtom fileType;
                    224:     Graphic *graphic;
                    225:     NXDataLink *link;
                    226:     int choice, updateMode = UPDATE_NORMALLY;
                    227:     BOOL isImportable;
                    228: 
                    229:     isImportable = isNXImageFile(file) || isRTFFile(file);
                    230:     if (!isImportable && [[Application workspace] getInfoForFile:file application:NULL type:&fileType]) {
                    231:        isImportable = (fileType == NXPlainFileType);
                    232:     }
                    233: 
                    234:     if (!linkManager) doLink = NO;
                    235: 
                    236:     if (doLink) {
                    237:        if (isImportable) {
                    238:            choice = NXRunAlertPanel(NULL, FILE_CONTENTS_OR_ICON_OR_LINK_BUTTON, FILE_CONTENTS, FILE_ICON, LINK_BUTTON);
                    239:        } else {
                    240:            choice = NXRunAlertPanel(NULL, FILE_ICON_OR_LINK_BUTTON, FILE_ICON, LINK_BUTTON, NULL);
                    241:            if (choice == NX_ALERTDEFAULT) {
                    242:                choice = NX_ALERTALTERNATE;
                    243:            } else if (choice == NX_ALERTALTERNATE) {
                    244:                choice = NX_ALERTOTHER;
                    245:            }
                    246:        }
                    247:     } else if (isImportable) {
                    248:        choice = NX_ALERTDEFAULT;
                    249:     } else {
                    250:        return NO;
                    251:     }
                    252: 
                    253:     if (choice == NX_ALERTDEFAULT) {           // import the contents of the file
                    254:        if (isNXImageFile(file)) {
                    255:            graphic = [[Image allocFromZone:[self zone]] initFromFile:file];
                    256:        } else {
                    257:            graphic = [[TextGraphic allocFromZone:[self zone]] initFromFile:file];
                    258:        }
                    259:        [icon free];
                    260:        updateMode = UPDATE_NORMALLY;
                    261:     } else if (choice == NX_ALERTALTERNATE) {  // show the file's icon
                    262:        graphic = [[Image allocFromZone:[self zone]] initFromIcon:icon];
                    263:        updateMode = UPDATE_NEVER;
                    264:     } else {                                   // show a link button
                    265:        graphic = [[Image allocFromZone:[self zone]] initWithLinkButton];
                    266:        [icon free];
                    267:        updateMode = UPDATE_NEVER;
                    268:     }
                    269: 
                    270:     if (graphic) {
                    271:        if (doLink) {
                    272:            link = [[NXDataLink alloc] initLinkedToFile:file];
                    273:            if ([self addLink:link toGraphic:graphic at:p update:UPDATE_NORMALLY]) return YES;
                    274:        } else {
                    275:            if ([self placeGraphic:graphic at:p]) return YES;
                    276:        }
                    277:     }
                    278: 
                    279:     NXRunAlertPanel(NULL, BAD_IMAGE, NULL, NULL, NULL);
                    280: 
                    281:     return ERROR;
                    282: }
                    283:  
                    284: /*
                    285:  * If we get this far, we are pretty sure we can succeed (though we're
                    286:  * not 100% sure because it might be, for example, a WriteNow file
                    287:  * without the link button down).
                    288:  *
                    289:  * We return YES here and do the work in conclude so that we don't
                    290:  * timeout if we are dragging in a big image or something that will
                    291:  * take a long time to import (or if we have to ask the user a question
                    292:  * to figure out how to import the dragged thing).  The bummer here
                    293:  * is that if creating the image should fail for some reason, we can't
                    294:  * give the slide-back feedback because it's too late to do so in
                    295:  * concludeDragOperation:.
                    296:  */
                    297: 
                    298: - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
                    299: {
                    300:     Pasteboard *pboard = [sender draggingPasteboard];
                    301: 
                    302:     if (IncludesType([pboard types], NXColorPboardType)) {
                    303:        BOOL retval = NO;
                    304:        NXColor color = NXReadColorFromPasteboard(pboard);
                    305:        NXPoint p = [sender draggingLocation];
                    306:        [self convertPoint:&p fromView:nil];
                    307:        retval = [self acceptsColor:&color atPoint:&p];
                    308:        [NXApp updateWindows];  // reflect color change in Inspector, et. al.
                    309:        return retval;
                    310:     }
                    311: 
                    312:     return YES;
                    313: }
                    314: 
                    315: /* Another convenience method for identifying .objlink files. */
                    316: 
                    317: static BOOL isLinkFile(const char *file)
                    318: {
                    319:     const char *extension = strrchr(file, '.');
                    320:     return (extension && !strcmp(extension+1, NXDataLinkFilenameExtension)) ? YES : NO;
                    321: }
                    322: 
                    323: /*
                    324:  * Actually do the "drop" of the drag here.
                    325:  *
                    326:  * Note that if we successfully dropped, we bring the window
                    327:  * we dropped into to the front and activate ourselves.  We also
                    328:  * update our inspectors, etc. (this is especially important for
                    329:  * the LinkInspector window!) by calling updateWindows.
                    330:  */
                    331: 
                    332: - concludeDragOperation:(id <NXDraggingInfo>)sender
                    333: {
                    334:     NXPoint p;
                    335:     int length;
                    336:     Pasteboard *pboard;
                    337:     char *data, *file, *tab;
                    338:     int foundOne = NO;
                    339:     BOOL doLink;
                    340: 
                    341:     p = [sender draggingLocation];
                    342:     [self convertPoint:&p fromView:nil];
                    343: 
                    344:     doLink = ([self draggingUpdated:sender] == NX_DragOperationLink);
                    345:     pboard = [sender draggingPasteboard];
                    346: 
                    347:     if (IncludesType([pboard types], NXFilenamePboardType)) {
                    348:        [pboard readType:NXFilenamePboardType data:&data length:&length];
                    349:        file = data;
                    350:        while (file) {
                    351:            if (tab = strchr(file, '\t')) *tab = '\0';
                    352:            if (isLinkFile(file)) {
                    353:                foundOne = [self createGraphicForDraggedLink:file at:&p] || foundOne;
                    354:            } else {
                    355:                foundOne = [self createGraphicForDraggedFile:file withIcon:[sender draggedImageCopy] at:&p andLink:doLink] || foundOne;
                    356:            }
                    357:            file = tab ? ++tab : NULL;
                    358:        }
                    359:        [pboard deallocatePasteboardData:data length:length];
                    360:     }
                    361: 
                    362:     if (!foundOne) foundOne = [self pasteForeignDataFromPasteboard:pboard andLink:doLink at:&p];
                    363:     
                    364:     if (foundOne > 0) {
                    365:        [NXApp activateSelf:YES];
                    366:        [window makeKeyAndOrderFront:self];
                    367:        [NXApp updateWindows];
                    368:     }
                    369: 
                    370:     return self;
                    371: }
                    372: 
                    373: @end

unix.superglobalmegacorp.com

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