/*
 * xv_scroll_view.c Dan Heller <argv@sun.com> 1989
 * 
 * Display a canvas in a frame.  The canvas displays a window that has lines
 * drawn from the opposite corners and draws a black box in the top left
 * corner.  This canvas may be split in many ways (vertically and/or
 * horizontally), but the repaint routine makes sure that each paint window
 * displays the same thing.
 * 
 * This program also demonstrates how to handle splitting views
 * programmatically.  Using the left mouse button splits a view horizontally
 * at the Y location of the mouse.  Using the right mouse button splits the
 * view vertically at the X location of the mouse.
 */
#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>		/* Using Xlib graphicsas well */
#include <xview/xview.h>
#include <xview/canvas.h>
#include <xview/scrollbar.h>
#include <xview/rectlist.h>

Canvas canvas;
void  events(), repaint_proc(), init_split(), join_split();

main(argc, argv)
    int   argc;
    char *argv[];
{
    Frame frame;
    Xv_Window view;
    Rect *rect;

    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

    /*
     * Create a frame that's 300 wide by 150 high -- give it a titlebar
     */
    frame = xv_create(XV_NULL, FRAME,
		      XV_WIDTH, 300,
		      XV_HEIGHT, 150,
		      FRAME_LABEL, argv[0],
		      NULL);

    /*
     * Create a canvas that's 500 by 500.  This canvas should not adjust its
     * size if resized.  Install the repaint callback: repaint_proc()
     */
    canvas = xv_create(frame, CANVAS,
		       CANVAS_WIDTH, 500,
		       CANVAS_HEIGHT, 500,
		       CANVAS_AUTO_SHRINK, FALSE,
		       CANVAS_AUTO_EXPAND, FALSE,
		       CANVAS_REPAINT_PROC, repaint_proc,
		       NULL);

    /*
     * Install the callback for events on the first (and only, so far) paint
     * window.  We'll use the default events provided by the canvas.
     */
    xv_set(canvas_paint_window(canvas),
	   WIN_EVENT_PROC, events,
	   WIN_CONSUME_EVENTS, MS_LEFT, MS_MIDDLE, NULL,
	   NULL);

    /*
     * There's only one viewport since multi-views cannot be created when
     * creating a canvas.  Install "init" and "destroy" callbacks in the
     * canvas object.  See the corresponding routines for specifics.
     */
    xv_set(canvas,
	   OPENWIN_SPLIT,
	   OPENWIN_SPLIT_INIT_PROC, init_split,
	   OPENWIN_SPLIT_DESTROY_PROC, join_split,
	   NULL,
	   NULL);

    /*
     * Attach scrollbars to the canvas.
     */
    xv_create(canvas, SCROLLBAR,
	      SCROLLBAR_SPLITTABLE, TRUE,
	      SCROLLBAR_DIRECTION, SCROLLBAR_VERTICAL,
	      NULL);
    xv_create(canvas, SCROLLBAR,
	      SCROLLBAR_SPLITTABLE, TRUE,
	      SCROLLBAR_DIRECTION, SCROLLBAR_HORIZONTAL,
	      NULL);

    xv_main_loop(frame);
    exit(0);
}

/*
 * The repaint procedure is called whenever repainting is needed in a paint
 * window.  If the canvas has been split into several views and repainting is
 * necessary, then this repaint procedure is called for each paint window in
 * the canvas.
 */
void
repaint_proc(canvas, paint_window, repaint_area)
    Canvas canvas;
    Xv_Window paint_window;
    Rectlist repaint_area;
{
    Display *dpy;
    Window win;
    Xv_Window pw;
    Rect *rect;
    int   i;

    /* Get the size of the entire paint window */
    rect = (Rect *) xv_get(paint_window, XV_RECT);

    for (i = 0; pw = xv_get(canvas, CANVAS_NTH_PAINT_WINDOW, i); i++)
	if (pw == paint_window)
	    break;
    printf("Paint window #%d of %d\n", i + 1, xv_get(canvas, OPENWIN_NVIEWS));

    /* Use Xview graphics to draw lines from opposite corners. */
    xv_vector(paint_windo  w, 0, 0, rect->r_width, rect->r_height, PIX_SET, 1);
    xv_vector(paint_window, rect->r_width, 0, 0, rect->r_height, PIX_SET, 1);

    /* Use Xlib calls to draw a black square in the top corner of the pw */
    dpy = (Display *) XV_DISPLAY_FROM_WINDOW(paint_window);
    win = (Window) xv_get(paint_window, XV_XID);
    XFillRectangle(dpy, win, DefaultGC(dpy, DefaultScreen(dpy)), 10, 10, 50, 50);
}

/*
 * This routine is installed as the callback for events for the paint window.
 * If more paint windows are created as a result of a view split, then this
 * routine must be reinstalled in a new view.
 */
void
events(pw, event)
    Xv_Window pw;
    Event *event;
{
    int   code = event->ie_code;
    Xv_Window view;
    int   i = (int) xv_get(canvas, OPENWIN_NVIEWS);

    /* Not interested in button up events */
    if (win_inputnegevent(event))
	return;

    /* Determine which paint window this event happened in. */
    while (pw != xv_get(canvas, CANVAS_NTH_PAINT_WINDOW, --i) && i > 0);
    /* The paint window number is "i" -- get the "i"th view window */
    view = xv_get(canvas, OPENWIN_NTH_VIEW, i);

    /* determine which event was passed and deal with it. */
    switch (code) {
      case MS_LEFT:
      case MS_MIDDLE:
	/*
	 * split the view at the appropriate position -- this call will
	 * generate a call to init_split below since a new view will have
	 * been created.
	 */
	printf("split at %d,%d\n", event_x(event), event_y(event));
	xv_set(canvas,
	       OPENWIN_SPLIT,	/* takes a null-terminated attr-value list */
	       OPENWIN_SPLIT_VIEW, view,
	       OPENWIN_SPLIT_DIRECTION, code == MS_MIDDLE ?
	       OPENWIN_SPLIT_VERTICAL :
	       OPENWIN_SPLIT_HORIZONTAL,
	       OPENWIN_SPLIT_POSITION, code == MS_MIDDLE ?
	       event_x(event) :
	       event_y(event),
	       NULL,
	       NULL);
	break;
      default:
	return;
    }
    /* indicate which paint window and view window ID's */
    printf("win %x, view: %x\n", pw, view);
}

/*
 * notify this routine whenever two views are joined.
 */
void
join_split(view)
    Xv_Window view;
{
    puts("joined view");
}

/*
 * Notify this routine whenever a view is split.  The new view is created and
 * its position is indicated.  This is the first time the new view can be
 * accessed by the program.  Immediately install the callback for events for
 * the new paint window.
 */
void
init_split(oldview, newview, pos)
    Xv_Window oldview, newview;
    int   pos;
{
    xv_set(xv_get(newview, CANVAS_VIEW_PAINT_WINDOW),
	   WIN_EVENT_PROC, events,
	   WIN_CONSUME_EVENT, MS_LEFT, MS_MIDDLE, NULL,
	   NULL);
    printf("Installed event proc for new view %d\n", i + 1);
}
