
\subsubsection{Idea}

This example is taken from a fractal viewer, where the fractal is
stored in an image and the user wishes to select a circular region of
the image for the purpose of zooming. It is not difficult to alter
this code to select, say, a rectangular region.

The structure of the routine is simple. We override {\tt NSView}'s
{\tt mouseDown:} method and process mouse-dragged events until we get
a mouse-up event. We update the selection every time there is a
mouse-dragged event.

\subsubsection{Implementation}

We must store two points, namely the start point, and the current
point. The start point tells us where the center of the selection
lies, and the larger of the two distances (horizontal, vertical) from
the current point to the start point defines the radius of the circle
being selected.

\begin{lstlisting}{}
#define PAD 5

- (void)mouseDown:(NSEvent *)theEvent
{
    NSPoint startp, curp;
    NSEvent *curEvent = theEvent;
    
    float dx, dy, prevr=0, r;
    NSRect update;

    double deltare, deltaim, 
        newminre, newmaxre, newminim, newmaxim;

    startp = [curEvent locationInWindow];
    startp = [self convertPoint:startp fromView: nil];
\end{lstlisting}

Both the start and the current point are supposed to be in view
coordinates. Hence we must convert them from screen coordinates. We
set the previous radius to zero, prepare the start point and are now
ready to enter the loop.

\begin{lstlisting}{}
    do {
        curp = [curEvent locationInWindow];
        curp = [self convertPoint:curp fromView: nil];
        
        dx = curp.x-startp.x;
        dy = curp.y-startp.y;

        if(dx<0){
            dx = -dx;
        }
        if(dy<0){
            dy = -dy;
        }

        r = (dx<dy ? dx : dy);

\end{lstlisting}

The first step is to convert the location of the drag event into the
view's coordinates. We compute the radius next. We must restore the
pixels that we altered when we drew the circle in the previous
iteration of the loop, and draw the circle for the current radius. So
we compute a square whose center lies at the start point and which
includes the circle that was drawn. We copy the data from the image
over this rectangle, thereby restoring the pixels that we altered.

\begin{lstlisting}{}
        [self lockFocus];

        if(prevr){
            update=NSMakeRect(startp.x-prevr-PAD, startp.y-prevr-PAD,
                              2*(prevr+PAD), 2*(prevr+PAD));
            update=NSIntersectionRect(update, [self frame]);

            [image compositeToPoint:update.origin 
                   fromRect:update
                   operation:NSCompositeCopy];
        }

\end{lstlisting}

The next step is to draw a circle centered at the start point and
having the new radius. This code actually draws two circles, one
black, one white and having a slightly larger radius. This assures
that the user sees the circle even if it overlaps with a monochrome
(white, black) portion of the image.

\begin{lstlisting}{}
        PSsetgray(0.0);
        PSarc(startp.x, startp.y, r, 0, 360);
        PSstroke();
        PSsetgray(1.0);
        PSarc(startp.x, startp.y, r+1, 0, 360);
        PSstroke();

        [self unlockFocus];
        [[self window] flushWindow];

        prevr = r;
                
\end{lstlisting}

The last step is to flush the data and remember the new radius so that
the underlying area can be restored. We process events until we get a
mouse-up. 

\begin{lstlisting}{}
        curEvent = 
            [[self window] 
                nextEventMatchingMask: 
                    NSLeftMouseUpMask | NSLeftMouseDraggedMask];
    } while([curEvent type] != NSLeftMouseUp);


    deltare = maxre-minre;
    deltaim = maxim-minim;
    newminre = deltare*(double)(startp.x-r)/(double)res+minre;
    newmaxre = deltare*(double)(startp.x+r)/(double)res+minre;
    newminim = deltaim*(double)(startp.y-r)/(double)res+minim;
    newmaxim = deltaim*(double)(startp.y+r)/(double)res+minim;
    
    minre=newminre;
    maxre=newmaxre;
    minim=newminim;
    maxim=newmaxim;

    [self update];
    [self setNeedsDisplay:YES];
}

\end{lstlisting}

We invoke {\tt setNeedsDisplay:} so that the selected portion of the
image will be displayed after it has been computed by {\tt update.}
We have to compute the coordinates for this to work. Assume that we
are working with four values that describe a portion of the complex
plane, i.e. real min and max and complex min and max; the variable
{\tt res} contains the width and height of the view. We convert the
coordinates of the square centered at the start point into the
coordinate system of the unit square, then convert into the
coordinates of the square being displayed, which gives us the new
square.

