By Andrew Stone of Stone Design
February 1990
Abstract
The code to copy an arbitrary rectangle specified by the user is presented. This allows users to simply click and drag out a rectangle to copy the enclosed PostScript to the PasteBoard.

Introduction
Without too much work, you can add methods to copy just part of your custom view to the pasteBoard. This code comes from a very user friendly version of popi, the digital darkroom, that Joe Freeman and I are preparing for release into the public domain. We have added some nice features: the entire language is specified by buttons [no typing necessary], resizable windows, preferences to save set up, you can save images that you generate to an "image manager" for reloading and mixing later, and of course, the ability to copy any part of the image, with the following code. To use the following source code, be sure to add "cross.tiff" to your IB project. It's the cross cursor which is available in /NextDevelopers/Examples/Draw.

In this example, we also have a method for copying everything in the view: - copyPS:sender, so you can have a menu item to get the entire view. We include an - acceptsFirstMouse method so that the first mousedown begins the process of repeatedly drawing the enclosing rectangle.

Note we keep switching the gray of the instance drawing [ the temporary drawing of the rubberbanding rectangle to specify the source rectangle while in the mouse down loop] from black to white, since we don't know what color the drawing in the view is. This could be set to a more appropriate gray level, depending on the contents of the view. Since with popi, there is no telling what color the image is, we are content to keep flipping from white to black, which guarantees that we will see it. Enjoy!
HEADER DECLARATIONS:
- (BOOL) acceptsFirstMouse;
- copyPS:sender;
- gutsCopyPS:(const NXRect *)r;
- mouseDown:(NXEvent *)event;


IMPORT FILES:
#import <appkit/Cursor.h>
#import <appkit/Pasteboard.h>
#import <appkit/Window.h>
#import <dpsclient/wraps.h>
#import <math.h>


CLASS METHODS & 1 STATIC FUNCTION:

- (BOOL) acceptsFirstMouse
{
    return (YES); /// instant reaction
}
- copyPS:sender
{
    return [self gutsCopyPS:&bounds];
- gutsCopyPS:(const NXRect *)r
{
    NXStream *s;
    NXStream *stream;
    NXTypedStream *ts;
    int length,maxlen;
    int i = 0;
    char *data;
    const char *types[2];
    id pb = [NXApp pasteboard];

    [NXWait push];
    types[i++] = NXPostScriptPboard; /// add other types here
    [pb declareTypes:types num:i owner:self];
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);

    [self copyPSCodeInside:r to:stream];
    NXFlush(stream); // Bruce Wed Nov 1 09:00:36 MST 1989

    NXGetMemoryBuffer(stream, &data, &length, &maxlen);
    [pb writeType:NXPostScriptPboard data:data
        length:length];
    NXCloseMemory(stream,NX_FREEBUFFER);

    [NXWait pop];
    return self;
}

// Returns the rectangle which has p1 and p2 as its corners.

static void getRegion(NXRect *region, const NXPoint *p1, const
NXPoint *p2)
{
    region->size.width = p1->x - p2->x;
    region->size.height = p1->y - p2->y;
    if (region->size.width < 0.0) {
        region->origin.x = p2->x + region->size.width;
        region->size.width = abs(region->size.width);
    } else region->origin.x = p2->x;
    if (region->size.height < 0.0) {
        region->origin.y = p2->y + region->size.height;
        region->size.height = abs(region->size.height);
    } else region->origin.y = p2->y;
}

#define DRAG_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK)
- mouseDown:(NXEvent *)event
{
    NXPoint p, last, start;
    NXRect region, oldRegion,r;
    static const NXPoint hotChamama = {7.,7.};
    int oldMask;
    static id crossCursor = nil;
    float rectGray = NX_WHITE;
    BOOL doubleclick = (event->data.mouse.click == 2) ? YES : NO;

    // quick copy
    if (doubleclick) return [self copyPS:self];

    oldMask = [window
        addToEventMask:NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK];

    if (!crossCursor) {
        crossCursor = [Cursor newFromMachO:"cross.tiff"];
        [crossCursor setHotSpot:&hotChamama];
    }

    [crossCursor push];
    p = start = event->location;
    [self convertPoint:&start fromView:nil];
    last = start;
    [self lockFocus];
    PSsetlinewidth(0.0);
    PSsetinstance(YES);
    event = [NXApp getNextEvent:DRAG_MASK];
    while (event->type != NX_MOUSEUP) {
        p = event->location;
        [self convertPoint:&p fromView:nil];
        PSnewinstance();
        if (p.x != last.x || p.y != last.y) {
            getRegion(&region, &p, &start);
            NXInsetRect(&oldRegion, -1.0, -1.0);
            PSsetinstance(YES);
            PSsetgray(rectGray=1.-rectGray); /// alternate b&w
            PSrectstroke(region.origin.x, region.origin.y,
                        region.size.width, region.size.height);
            PSsetinstance(NO);
            oldRegion = region;
            last = p;
            NXPing();
        }
        p = event->location;
        event = [NXApp getNextEvent:DRAG_MASK];
    }
        getRegion(&r,&start,&last); /// final rect
        PSnewinstance(); ///flushes offscreen buffer
        [self gutsCopyPS:&r]; /// send the rect to be copied
        [self unlockFocus]; /// the rest is clean up
        [window flushWindow];
        [window setEventMask:oldMask];
        [crossCursor pop];
        return self;
    }





Stone Design's Create(tm)
2005-06-26 18:29:12 -0500