← Back to team overview

kicad-developers team mailing list archive

Re: Moving field-texts with connecting line to component; potential patch

 

Hi Henner,

The way I did it in the Piglet hierarchical drawing editor[1] was to put
a function in each primitive that is responsible for drawing rubberband
representations of the primitive.  The drawing function gets fed a
sequence of transformed mouse coordinates by a callback in the
eventloop. 


So for my line drawing primitive, when my parser decides something needs
to be moved, I set a callback with the event loop with:

    `rubber_set_callback(draw_line);

draw_line(double x, double y, int count) is my callback function
and is different for each primitive.  It is always called with
the current x,y mouse coordinates and a count.  The count starts
at zero and increments for every new coordinate.

After the parser is done with the rubber band drawing and has accepted
the pick, it calls

    rubber_clear_callback();

to clear the callback.  After receiving rubber_clear_callback(),
the event loop sends one final call to draw_line, this time with
a count of -1.

The draw_line function in geom_line.c looks like this:

    void draw_line(double x2, double y2, int count) {
        ...
        if (count == 0) {               /* first call */
            jump(&bb, D_RUBBER); /* draw new shape */
            do_line(&dbdeflist, &bb, D_RUBBER);
        } else if (count > 0) {         /* intermediate calls */
            jump(&bb, D_RUBBER); /* erase old shape */
            do_line(&dbdeflist, &bb, D_RUBBER);
            jump(&bb, D_RUBBER); /* draw new shape */
            coord_swap_last(CP, x2, y2); /* update new coords */
            do_line(&dbdeflist, &bb, D_RUBBER);
        } else {                        /* last call, cleanup */
            jump(&bb, D_RUBBER); /* erase old shape */
            do_line(&dbdeflist, &bb, D_RUBBER);
        }
        ....
        /* save old values */
        x1old=x1;
        y1old=yy1;
        x2old=x2;
        y2old=y2;
        jump(&bb, D_RUBBER);
    }

The jump() starts a new disconnected segment and turns the drawing
engine to XOR mode (D_RUBBER).  Then I just call the main line drawing
routine with the current list of coords. 

The key thing is that the XWIN event loop calls the draw_line routine
through the callback with an integer count argument.  On the first time
we just do an XOR.  The second and subsequent times, we remember our old
position and unwrite it with an XOR before drawing the new coordinate. 
Finally, after the event loop gets a rubber_clear_callback(), the last
call has count=-1.  This causees the last drawing to be erased and no
new one to be made. 

Every primitive has its own specific callback for rubber band drawing. 
For instance, I represent hierarchical sub-cells by just a bounding box
so that I don't have to render all the details.   

I found that having a centralized callback from the event loop that
simply sends back the mouse coordinates to the rubber band drawing
routine simplified my code and gave me a standard way to handle the XOR
cleanup problem. 

Here's the callback hook from the xwin.c event loop.

    while (XCheckMaskEvent(dpy, all, &xe)) { /* pending X Event */
        switch (xe.type) {
        case MotionNotify:
           ...
        if (xold != x || yold != y) {
             if (xwin_display_state() == D_ON) {
                 rubber_draw(x, y);
             }
             xold=x;
             yold=y;
        }
        ...


Here's the code for rubber.c

/* *************************************************************
this set of routines manages the rubber band drawing function for
interactive point selection.  The rubber band shapes are drawn with an
xor function such that drawing a line twice will erase it.  We need to
ensure that the first shape drawn after rubber_set_callback() gets
erased before the second shape is drawn, and so on, until the last call
is made by rubber_clear_callback(). 

    rubber_set_callback();
    First call (x1,y1,0):
            draw x1, y1

    Second call (x2,y2,1)
            draw x1, y1                 # erase last shape
            draw x2, y2                 # drawn new shape

    (N-1)th call (x(n-1),y(n-1), n-1)
            draw x(n-2), y(n-2)         # erase last shape
            draw x(n-1), y(n-1)         # draw new shape

    rubber_clear_callback();
    Last call (xn, yn, -1)
            draw x(n-1), y(n-1)         # erase last shape
                                        # and DONT draw new shape


The callback function manages this behavior by the count variable passed
in the third argument.  When count==0, it only draws the specified
shape.  When count>=0 it always undraws the old shape prior to drawing
the new shape.  When count<=0 it will undraw the old shape, and NOT draw
a new shape.  rubber_clear_callback() is responsible for making the last
call with count=-1.

************************************************************* */

#include <stdio.h>
#include "rubber.h"

typedef int Function ();

static int count=0;
Function * rubber_callback = NULL;

void rubber_set_callback(func)
Function * func;
{
    count=0;
    rubber_callback = func;
}

void rubber_clear_callback()
{
    /* let callback do final erase */
    if (rubber_callback != NULL) {
        (*(rubber_callback)) (0.0, 0.0, -1);
    }

    /* clear the callback function */
    rubber_callback = (Function *) NULL;

    count=0;
}

/* called with count == 0 on first call, count++ subsequent calls, and
 *  -1 on final call.  The drawing routine must erase previous draws on
 *  all calls with count >0.
 */

void rubber_draw(x,y)
double x, y;
{
    int debug=0;
    if (rubber_callback != NULL) {
        if (debug) printf("rubber_draw: drawing: %g %g %d\n", x,y, count);
        (*(rubber_callback)) (x, y, count);
        count++;
    }
}

Hope that gives you some ideas...

kind regards,
--
Rick Walker

[1] www.github.com/omnister/piglet




References