/*
TEACH GO_XDRAG                                                 3rd Sep 1992
                                                            B.Rabau/J.Meyer

        CONTENTS - (Use <ENTER> g to access required sections)

 -- Purpose
 -- How to start
 -- Drag example
 -- Fixed image
 -- Transparent image
 -- Remember start position
 -- Rubberband


-- Purpose ------------------------------------------------------------

In many graphical applications it is usefull to be able to grab hold of
a graphical "object" and to move it over the screen to a new location.

A "go_drag" operation like this can be seen as the following sequence:
    - Erase the graphical "object" which is picked up.
    - Draw it at new position
    - Keep erasing at new position then redrawing at the next ...
    - Drop it at the final destination.

If this is implemented with callback procedures in X-Windows then it
soon is obvious that that this can be rather slow, and result in the
"object" lagging behind having to catch up with the mouse.

A further complication is that the object which is dragged does
not always remain the same during the go_drag operation (see further).

To avoid this the LIB * GO_XDRAG was written.

See REF * GO_XDRAG and HELP * GO for more details.

-- How to start --------------------------------------------------------
*/
;;; After loading the library with:

    uses go;
    uses go_xdrag;

;;; The purpose of this teach file is to show the features of the
;;; go_xdrag library. Poplog/GO has extensive facilities which use this
;;; library on a very high level. To avoid confusion over which features
;;; specifically belong to LIB * GO_XDRAG and which are part of other GO
;;; classes we will use event handlers from LIB * RC_MOUSE (see also
;;; TEACH * RC_GRAPHIC).

    uses rc_mouse;
    true -> rc_mousing;

;;; To remove event handlers from GO we will make the created panes
;;; "live" and then add some new mouse event handlers from rc_graphic.

/*
-- Drag example --------------------------------------------------------
 */
;;; The example consists of creating a go_polygon (triangle to start with)
;;; which we will then drag around.

;;; Each time go_drag calls the user procedures (or methods) data will
;;; be passed in the form of a class: REF * GO_XDRAG/dragContext.

;;; The function which is called by the go_drag operation will use the
;;; data in dragContext to find out where to go_draw the picture. Mark
;;; and load this:

vars drag_obj;

define do_the_drag(dragContext) ;
lvars dragContext;
    explode( dragContext.lastCoords ) -> go_position_of_centre( drag_obj );
    go_redraw( drag_obj );
enddefine;

/*
-- Fixed Image ---------------------------------------------------------
 */
;;; To start of the go_drag operation we will set the convenience flag
;;; go_fixed_image. This counts on the fact that the image will not change
;;; during the go_drag operation. The wrap function do_the_drag() defined
;;; above is passed in as a parameter.

define drag_demo(arg1,arg2,arg3);

    ;;; clean up the procedure which started the go_drag
    [] -> rc_button_procedures ;

    ;;; start the go_drag operation of the image drawn by do_the_drag()
    go_fixed_drag(do_the_drag, drag_obj);
enddefine ;

;;; Create a few extra pictures besides the triangle which we will drag
;;; around. We will create a click-sensitive rc_window with:
;;;     go_init_rc();
;;; (see also TEACH * GO_RC_WINDOW). And we will use the go_polygon
;;; class defined in LIB * GO_POLYGON to define regular polygons.

define setup_picture();
    go_destroy();
    go_init_rc();
    true -> go_live_mode( go_default_pane );

    newgo_polygon() -> drag_obj;
    go_add_to( drag_obj, go_default_pane );

    vars picture1 = instance go_polygon;
                        go_npoints = 5;
                        go_xcentre = 100;
                    endinstance;
    vars picture2 = instance go_polygon;
                        go_npoints = 7;
                        go_xcentre = -100;
                    endinstance;
    go_add_to( picture1, go_default_pane );
    go_add_to( picture2, go_default_pane );

enddefine;

;;; Attach the go_drag procedure to the mouse (could also be done with
;;; XtAddCallback).
setup_picture();
[drag_demo] -> rc_button_procedures ;

;;; Now you can go_drag the small triangle around. Every time you want to
;;; repeat a go_drag operation, you need to call the last line again:
[drag_demo] -> rc_button_procedures ;
;;; Since the original picture is not cleaned up, you should also call:
go_redraw( go_default_pane );

/*
-- Transparent Image ---------------------------------------------------------
 */
;;; As you can see when you move the triangle over the other drawings,
;;; the fixed picture is based on a rectangular non transparent box
;;; which takes the same color as the background of the go_rc_window.

;;; This can be avoided by repeatedly drawing on the real go_rc_window
;;; rather than copying a saved image. This will still go faster than
;;; xor'ing the triangle and redrawing it further since go_drag will
;;; only draw temporarily (not in memory) and refresh a stored clean
;;; background without having to xor.

define transparent_drag_demo(arg1,arg2,arg3);

    ;;; clean up the procedure which started the go_drag
    [] -> rc_button_procedures ;

    ;;; start the go_drag operation of the image drawn by do_the_drag()
    go_drawn_drag(do_the_drag, drag_obj);
enddefine ;

;;; Identical to the fixed go_drag example you can now do:
setup_picture();
[transparent_drag_demo] -> rc_button_procedures ;

;;; as you can see the triangular shape is transparent and does not
;;; cover other lines.

/*
-- Remember start position -------------------------------------------
 */
;;; Sometimes the start of a go_drag needs to be different from the go_drag
;;; itself. This could be the case if the erase operation is very complex
;;; (e.g. when xor would not correctly restore the underlying graphics).
;;; This can be done with the full drag() call rather than the convenience
;;; functions which have been used up to now.

;;; Here we limit ourself to showing a smaller go_polygon inside the original

define indicate_start(dragContext) ;
lvars dragContext, small;
    explode( dragContext.lastCoords ) -> go_position_of_centre( drag_obj );
    go_clear( drag_obj );                   ;;; erase the original go_polygon

    go_copy_object( drag_obj ) -> small;
    10 -> go_radius( small );               ;;; draw a little go_polygon
enddefine;

define erase_drag_demo(arg1,arg2,arg3);
lvars fixed;
    false-> fixed;            ;;; Image could change or transparent
;;; true -> fixed;            ;;; Image doesn't change & not transparent

    ;;; clean up the procedure which started the go_drag
    [] -> rc_button_procedures ;

    ;;; start the go_drag operation of the image erasing with indicate_start()
    ;;; and drawing with do_the_drag().
    go_drag(indicate_start, do_the_drag, fixed, drag_obj);
enddefine ;

;;; Again we start up with:
setup_picture();
[erase_drag_demo] -> rc_button_procedures ;

;;; This time we note that a small triangle is left where the original
;;; triangle started. We also note some "dirty dots" which are left from
;;; xor'ing two lines crossing in the corners (double xor = drawing).

;;; You can also experiment with "true -> fixed" in which case the go_drag
;;; itself is faster (important if the object is more complex to go_draw).


/*
-- Rubberband ----------------------------------------------------------
 */

;;; It is possible to change the object continuously. A simple example
;;; would be a rubberband line which forces the user to change the go_bounding_width
;;; of the update continuously.

define rubber(dragContext) ;
lvars dragContext, startx, starty, x, y;

    explode( dragContext.startCoords ) -> starty -> startx;

    ;;; Prevent the mouse position to be relative rather than absolute
    ;;; This should probably be a routine in RC_DRAG
    unless (dragContext.startXOffset = 0) and
           (dragContext.startYOffset = 0) then
        {^(startx + dragContext.startXOffset)
         ^(starty + dragContext.startYOffset)} -> dragContext.lastCoords;
        0 -> dragContext.startXOffset;
        0 -> dragContext.startYOffset;
    endunless;

    explode( dragContext.lastCoords ) -> y -> x;

    rc_drawline(startx, starty, x, y);
    round(startx - x) -> dragContext.dragWidth;
    round(starty - y) -> dragContext.dragHeight;
enddefine;

define rubber_drag_demo(arg1,arg2,arg3);
lvars x,y,go_bounding_width,go_bounding_height,fixed;
    rc_xposition  -> x;
    rc_yposition  -> y;
    1 ->> go_bounding_width -> go_bounding_height ;
    false-> fixed;            ;;; Image could change or transparent

    ;;; clean up the procedure which started the go_drag
    [] -> rc_button_procedures ;

    ;;; start the go_drag operation of the image with rubber()
    rc_drag(rubber, rubber, x, y, go_bounding_width, go_bounding_height, fixed);
enddefine ;

;;; Again we start up with:
setup_picture();
[rubber_drag_demo] -> rc_button_procedures ;

--- C.all/lib/proto/go/teach/go_xdrag
--- Copyright University of Sussex 1993. All rights reserved.
