/*****************************************************************************/
/**               Copyright 1990 by Solbourne Computer Inc.                 **/
/**                          Longmont, Colorado                             **/
/**                                                                         **/
/**                           All Rights Reserved                           **/
/**                                                                         **/
/**    Permission to use, copy, modify, and distribute this software and    **/
/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
/**    granted, provided that the above copyright notice appear  in  all    **/
/**    copies and that both  that  copyright  notice  and  this  permis-    **/
/**    sion  notice appear in supporting  documentation,  and  that  the    **/
/**    name of Solbourne not be used in advertising                         **/
/**    in publicity pertaining to distribution of the  software  without    **/
/**    specific, written prior permission.                                  **/
/**                                                                         **/
/**    SOLBOURNE COMPUTER INC. DISCLAIMS ALL WARRANTIES WITH REGARD         **/
/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL SOLBOURNE                **/
/**    BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-           **/
/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
/*****************************************************************************/

/**********************************************************************
 *
 * $XConsortium: move.c,v 1.140 90/03/23 11:42:33 jim Exp $
 *
 * New window move code to allow interaction with the virtual desktop
 * All of this code came from the Solbourne Window Manager (swm)
 *
 * 23-Aug-90 Tom LaStrange        Initial Version.
 *
 **********************************************************************/

#if !defined(lint) && !defined(SABER)
static char RCSinfo[]=
"$XConsortium: move.c,v 1.140 90/03/23 11:42:33 jim Exp $";
#endif

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include "twm.h"
#include "screen.h"
#include "vdt.h"
#include "move.h"
#include "events.h"
#include "util.h"
#include "resize.h"
#include "parse.h"

static int dragX;
static int dragY;
static int origX;
static int origY;
static unsigned int dragWidth;
static unsigned int dragHeight;
static unsigned int dragBW;
static int dragBW2;
static int diffX;
static int diffY;
static int offsetX;
static int offsetY;
static int rootX;
static int rootY;
static Window outlineWindow;
static int scale;
static int titleHeight;
static int doingMove = False;

static void reallyStartMove(TwmWindow *tmp_win, Window window, int *x_root, int *y_root, int *cancel, Window outlineWindow, int *first, int adding, int pulldown, int panner);
static void doMove(TwmWindow *tmp_win, Window window, int x_root, int y_root, int *x_dest, int *y_dest, int panner, int paint);
static void getPointer(Window window, int *x_root, int *y_root, int *cancel, int *done, int *first, int adding, int pulldown);

/***********************************************************************
 *
 *  Procedure:
 *	DragFrame - move the window frame
 *	
 *  Returned Value:
 *	None
 *
 ***********************************************************************
 */

void
DragFrame(TwmWindow *tmp_win, XButtonEvent *ev, int pulldown)
{
    int cancel;
    int x_root, y_root;

    x_root = ev->x_root;
    y_root = ev->y_root;
    StartMove(tmp_win, tmp_win->frame, tmp_win->title_height,
	&x_root, &y_root, &cancel, OUT_PANNER, 1, 0, 0, False, pulldown);

    if (!cancel && WindowMoved) {
	SetupWindow (tmp_win, x_root, y_root,
	     tmp_win->frame_width, tmp_win->frame_height, -1);
	if (!Scr->NoRaiseMove)
	    RaiseFrame(tmp_win);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	DragIcon - move the window icon
 *	
 *  Returned Value:
 *	None
 *
 ***********************************************************************
 */

void
DragIcon(TwmWindow *tmp_win, XButtonEvent *ev, int pulldown)
{
    int cancel;
    int x_root, y_root;

    x_root = ev->x_root;
    y_root = ev->y_root;
    StartMove(tmp_win, tmp_win->icon_w, 0, &x_root, &y_root, &cancel,
	OUT_PANNER, 1, 0, 0, False, pulldown);

    if (!cancel && WindowMoved) {
	MoveIcon(tmp_win, x_root, y_root);
	if (!Scr->NoRaiseMove)
	    RaiseIcon(tmp_win);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	StartMove - start a move operation on an icon or a frame
 *	
 *  Returned Value:
 *	None
 *
 ***********************************************************************
 */

void
StartMove(TwmWindow *tmp_win, Window window, int title_height, int *x_root, int *y_root, int *cancel, int panner, int move_scale, unsigned objWidth, unsigned objHeight, int adding, int pulldown)
{
    Window junkRoot, junkChild;
    unsigned int junkDepth;
    int junkX, junkY;
    int first;
    XSetWindowAttributes attr;

    if (!Scr->NoGrabServer || !Scr->OpaqueMove)
	XGrabServer(dpy);

    if (Scr->OpaqueMove) {
	attr.save_under = True;
	XChangeWindowAttributes(dpy, window, CWSaveUnder, &attr);
    }
	
    if (!adding) {
	XGrabPointer(dpy, Scr->Root, True,
	    PointerMotionMask | EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask,
	    GrabModeAsync, GrabModeAsync,
	    Scr->Root, Scr->MoveCursor, CurrentTime);
    }

    rootX = *x_root;
    rootY = *y_root;

    /* how big is this thing we are moving? */
    XGetGeometry(dpy, window, &junkRoot, &junkX, &junkY, &dragWidth, &dragHeight, &dragBW, &junkDepth);
    origX = junkX;
    origY = junkY;

    /* translate its coordinates to root coordinates */
    XTranslateCoordinates(dpy, window, Scr->Root, -dragBW, -dragBW, &dragX, &dragY, &junkChild);

    dragBW2 = 2 * dragBW;
    dragWidth += dragBW2;
    dragHeight += dragBW2;
    diffX = dragX - *x_root;
    diffY = dragY - *y_root;

    /* translate the real root coordinates to our root coordinates */
    if (tmp_win->root != Scr->Root) {
	XTranslateCoordinates(dpy, Scr->Root, tmp_win->root, dragX, dragY, x_root, y_root, &junkChild);
    }

    if (!tmp_win->sticky)
	doingMove = True;
    outlineWindow = tmp_win->root;
    scale = move_scale;

    if (panner == OUT_PANNER)
    {
	titleHeight = title_height;
	objWidth = dragWidth;
	objHeight = dragHeight;
    }
    else
    {
	titleHeight = 0;
    }

    first = True;
    while (True)
    {
	*cancel = False;

	XTranslateCoordinates(dpy, Scr->Root, outlineWindow,
	    0, 0, &offsetX, &offsetY, &junkChild);

	reallyStartMove(tmp_win, window, x_root, y_root, cancel, outlineWindow, &first, adding, pulldown, panner);

	if (*cancel == IN_PANNER)
	{
	    if (window == tmp_win->frame)
		window = tmp_win->virtualWindow;
	    else
		window = tmp_win->virtualIcon;
	    panner = IN_PANNER;
	    dragWidth /= Scr->PannerScale;
	    dragHeight /= Scr->PannerScale;
	    diffX /= Scr->PannerScale;
	    diffY /= Scr->PannerScale;
	    outlineWindow = Scr->Panner;
	    scale = Scr->PannerScale;
	    titleHeight = 0;
	}
	else if (*cancel == OUT_PANNER)
	{
	    if (window == tmp_win->virtualWindow)
		window = tmp_win->frame;
	    else
		window = tmp_win->icon_w;
	    panner = OUT_PANNER;
	    dragWidth = objWidth;
	    dragHeight = objHeight;
	    diffX *= Scr->PannerScale;
	    diffY *= Scr->PannerScale;
	    outlineWindow = Scr->VirtualDesktop;
	    scale = 1;
	    titleHeight = title_height;
	}
	else
	    break;
    }

    if (panner == IN_PANNER)
    {
	*x_root *= Scr->PannerScale;
	*y_root *= Scr->PannerScale;
    }
    doingMove = False;
    if (!adding)
    {
	XUngrabPointer(dpy, CurrentTime);
	XUngrabServer(dpy);
    }
    /* can't cancel an opaque move */
    if (Scr->OpaqueMove)
	*cancel = False;

    if (*cancel)
	WindowMoved = False;

    if (Scr->OpaqueMove) {
	attr.save_under = False;
	XChangeWindowAttributes(dpy, window, CWSaveUnder, &attr);
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	reallyStartMove - do the meat of the move operation
 *	
 *  Returned Value:
 *	None
 *
 ***********************************************************************
 */

static void
reallyStartMove(TwmWindow *tmp_win, Window window, int *x_root, int *y_root, int *cancel, Window outlineWindow, int *first, int adding, int pulldown, int panner)
{
    int xdest, ydest;
    int done;

    xdest = *x_root;
    ydest = *y_root;
    done = False;

    /* put up the initial outline if adding a window */
    if (*first && adding)
	doMove(tmp_win, window, *x_root, *y_root, &xdest, &ydest, panner, True);
	
    while (True) {
	getPointer(outlineWindow, x_root, y_root, cancel, &done, first, adding, pulldown);
	if (done) {
	    doMove(tmp_win, window, *x_root, *y_root, &xdest, &ydest, panner, False);
	    break;
	}
	if (!*cancel)
	    doMove(tmp_win, window, *x_root, *y_root, &xdest, &ydest, panner, True);
    }

    *x_root = xdest;
    *y_root = ydest;
}

static void
doMove(TwmWindow *tmp_win,Window window, int x_root, int y_root, int *x_dest, int *y_dest, int panner, int paint)
{
    int xl, yt;
    int deltax, deltay;
    int vdtx, vdty;
    Window actual, virtual;

    dragX = x_root;
    dragY = y_root;

    xl = dragX + diffX;
    yt = dragY + diffY;

    deltax = xl - origX/scale;
    deltay = yt - origY/scale;

    if (MoveFunction != F_FORCEMOVE && Scr->DontMoveOff && outlineWindow != Scr->Panner) {
	if (tmp_win->root == Scr->VirtualDesktop || tmp_win->root == Scr->Panner) {
	    vdtx = Scr->vdtPositionX;
	    vdty = Scr->vdtPositionY;
	}
	else {
	    vdtx = 0;
	    vdty = 0;
	}
	if (xl < vdtx)
	    xl = vdtx;
	else if ((xl + dragWidth) > (Scr->MyDisplayWidth + vdtx))
	    xl = vdtx + Scr->MyDisplayWidth - dragWidth;
	if (yt < vdty)
	    yt = vdty;
	else if ((yt + dragHeight) > (Scr->MyDisplayHeight + vdty))
	    yt = vdty + Scr->MyDisplayHeight - dragHeight;
    }

    if (paint) {
	    if (Scr->VirtualDesktop && Scr->OpaqueMove && !tmp_win->sticky) {
		if (panner == IN_PANNER) {
		    if (window == tmp_win->virtualWindow) {
			actual = tmp_win->frame;
			virtual = tmp_win->virtualWindow;
		    }
		    else {
			actual = tmp_win->icon_w;
			virtual = tmp_win->virtualIcon;
		    }
		}
		else {
		    if (window == tmp_win->frame) {
			actual = tmp_win->frame;
			virtual = tmp_win->virtualWindow;
		    }
		    else {
			actual = tmp_win->icon_w;
			virtual = tmp_win->virtualIcon;
		    }
		}
		if (panner == IN_PANNER)
		    XMoveWindow(dpy, actual, xl*Scr->PannerScale, yt*Scr->PannerScale);
		else
		    XMoveWindow(dpy, virtual, xl/Scr->PannerScale, yt/Scr->PannerScale);
	    }

	    if (Scr->OpaqueMove)
		XMoveWindow(dpy, window, xl, yt);
	    else
		MoveOutline(outlineWindow, xl, yt, dragWidth, dragHeight, 0, titleHeight);
    }

    *x_dest = xl;
    *y_dest = yt;
}

static void
getPointer(Window window, int *x_root, int *y_root, int *cancel, int *done, int *first, int adding, int pulldown)
{
    int doingFine;
    XEvent event,bakevent;
    unsigned mask;
    static int buttons;

    if (*first) {
	*first = False;
	buttons = 0;
    }

    doingFine = True;
    while (doingFine) {
	XMaskEvent(dpy, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|
			EnterWindowMask|LeaveWindowMask|ExposureMask, &event);
	switch (event.type) {
	    case ButtonPress:
		if (pulldown) {
		    if (buttons++)
			pulldown = False;
		}
		if (!pulldown) {
		    *cancel = event.xbutton.button;
		    doingFine = False;
		    MoveOutline(outlineWindow, 0,0,0,0,0,0);
		    if (adding) {
			*done = True;
		    }
		    else {
			XGrabPointer(dpy, Scr->Root, True,
			    ButtonPressMask | ButtonReleaseMask,
			    GrabModeAsync, GrabModeAsync,
			    Scr->Root, Scr->WaitCursor, CurrentTime);
		    }
		}
		break;
	    case ButtonRelease:
		MoveOutline(outlineWindow, 0,0,0,0,0,0);

                /* clear the mask bit for the button just released */
                mask = (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
                switch (event.xbutton.button)
                {
                    case Button1: mask &= ~Button1Mask; break;
                    case Button2: mask &= ~Button2Mask; break;
                    case Button3: mask &= ~Button3Mask; break;
                    case Button4: mask &= ~Button4Mask; break;
                    case Button5: mask &= ~Button5Mask; break;
                }

                /* if all buttons have been released */
                if ((event.xbutton.state & mask) == 0)
                {
		    ButtonPressed = -1;
                    *done = True;
		    doingFine = False;

		    /* RJC: added code to do a final move here */
		    if (!WindowMoved &&
			(abs(event.xbutton.x_root - rootX) >= Scr->MoveDelta ||
			 abs(event.xbutton.y_root - rootY) >= Scr->MoveDelta))
			{
			    WindowMoved = True;
			}
		    if (WindowMoved) {
		        *x_root = event.xbutton.x_root + offsetX;
			*y_root = event.xbutton.y_root + offsetY;
		    }
		}
		break;
	    case EnterNotify:
                if (doingMove && event.xcrossing.window == Scr->Panner && event.xcrossing.detail != NotifyInferior)
                {
                    MoveOutline(outlineWindow, 0,0,0,0,0,0);
                    *cancel = IN_PANNER;
                    *done = True;
		    doingFine = False;
                }
		break;
	    case LeaveNotify:
                if (doingMove && event.xcrossing.window == Scr->Panner &&
		    event.xcrossing.detail != NotifyInferior && event.xcrossing.mode == NotifyNormal)
                {
                    MoveOutline(outlineWindow, 0,0,0,0,0,0);
                    *cancel = OUT_PANNER;
                    *done = True;
		    doingFine = False;
                }
		break;
	    case MotionNotify:
 		while(XCheckTypedEvent(dpy,MotionNotify,&event));
		if (!WindowMoved &&
		    (abs(event.xmotion.x_root - rootX) >= Scr->MoveDelta ||
		     abs(event.xmotion.y_root - rootY) >= Scr->MoveDelta))
		{
		    WindowMoved = True;
		}
		if (WindowMoved) {
		    *x_root = event.xmotion.x_root + offsetX;
		    *y_root = event.xmotion.y_root + offsetY;
		    doingFine = False;
		}
		break;

	    /* handle other generic events here */
	    case Expose:
		bakevent = Event;
		Event=event;
		DispatchEvent();
		Event=bakevent;
		break;
	}
    }
}





syntax highlighted by Code2HTML, v. 0.9.1