/* 
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
*/ 
/* 
 * HISTORY
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: CButtonPress.c /main/7 1995/07/14 11:37:22 drk $"
#endif
#endif
/***********************************************************************
	Calls:

	Summary:
            Simulate a user pressing then releasing a mouse button one or
        more times.

        INPUTS:
            modifier_keys       - ShiftMask, LockMask, ControlMask, Mod1Mask,
                                  etc.
            button_num          - Mouse button number 1 through 5
            clicks              - Number of button multi clicks
            hold_time_intervals - Number of Xt timer intervals to press the
                                  button
        
        OUTPUTS:
            none

        RETURNS:
            nothing
************************************************************************/

#include <X11/IntrinsicP.h>
#include "xislib.h"
#include <AutoMessages.h>

/* 
  When an X-server gets a button press from the real mouse device, this is 
  how it determines the appropriate event window to send it to:

    If ActiveGrabInProgress
        Send to client window that grabbed it (if visible)

    Else
        Search visible ancestors of window from root down for a passive button 
            grab

        If found, 
            Activate grab (call XGrabPointer) on that window (pg 144 Xlib)
            Send event to that same window
        Else
            Starting from lowest ancestor window containing pointer, go up tree
               (up meaning to ancestors) looking for window that has this event 
               selected.
            Send event to that window (with subwindow set to child in direction
               of window containing pointer).


  Since we have no way of determining which window currently has the pointer
  grabbed nor do we have a way of determining which windows have passive button
  grabs, we will skip these two cases and implement them in special ways when
  they come up. This appears feasible since button grabs are not used anywhere
  in Xm except in menus.

  Note: Actually, I do know of a way to determe if the pointer is currently
        grabbed: open a second display connection and try to grab the pointer
        with this new display.  If you get back an AlreadyGrabbed flag, then
        you know its grabbed.  The problem is, which window grabbed the pointer?
        You could rely on Enter/Leave Notify events with Grab/Ungrab modes
        but you would have to be sure these events were selected for every
        window at all times (not very feasible).
*/

#define GOOD_GRAB_BITS  \
		    ButtonPressMask | ButtonReleaseMask | EnterWindowMask | \
                    LeaveWindowMask | PointerMotionMask | \
		    PointerMotionHintMask | Button1MotionMask | \
		    Button2MotionMask | Button3MotionMask | \
                    Button4MotionMask | Button5MotionMask | \
		    ButtonMotionMask | KeymapStateMask

void xisCommonButtonPress(modifier_keys,button_num,clicks)
unsigned int modifier_keys;
int button_num;
int clicks;
{
    static char routine_name[] = "xisCommonButtonPress():";
    Window subwindow,send_window;
    short pursue;
    XisObjectRecord *action_object, *current_object, *send_object;
    int root_x,root_y;
    Time time;
    int i, multi = 0;
    char msg_string[125];
    
    xisUseSessionInfo(routine_name);
    xisUpdateObjectAttributes();
    
    if (clicks > 1)
	multi++;

    /* First make sure this button is not already pressed */

    if ( xisState.mod_button_state & xisMouseButtonMask[button_num] ) {
            sprintf(msg_string,_AutoMessages[WARNMSG46], routine_name,button_num); 
            AutoMessage(msg_string);
        xisReleaseMouseButton(NoModifierKeys,button_num);
    }

    /* Then look up which object the pointer is over or is already grabbed */

    xisGetPointerLocation(&root_x,&root_y);
    xisInform.pointer_obj = xisFindObjectAtLocation(root_x,root_y);

    if (xisState.mod_button_state != 0 && xisPointerGrabbed) {
        send_window = xisGrabPointerWindow;
        send_object = xisFindObject(xisState.selected_widget,
                                    xisState.selected_object_type,
                                    xisState.selected_instance);
        action_object = send_object;
        pursue = TRUE;
    }
    else {
        if (xisPointerGrabbed) {
	  sprintf(msg_string, _AutoMessages[WARNMSG48], routine_name);
	  AutoMessage(msg_string);
	}
           

        action_object = xisInform.pointer_obj;

        xisState.selected_widget = action_object->id.widget;
        xisState.selected_object_type = action_object->id.object_type;
        xisState.selected_instance = action_object->id.instance;

        /* Find window (object) which has elected to receive these events */

        send_object = action_object;    
        subwindow = 0L;

        while (!send_object->id.window) 
            send_object = send_object->parent;

        while (send_object != NULL) {

            if ((send_object->your_event_mask&ButtonPressMask) && 
                (send_object->visibility_state != IsUnmapped) ) {
                pursue = 1;
                send_window = send_object->id.window;
                break;
            }
            if ((send_object->do_not_propagate_mask&ButtonPressMask) &&
                (send_object->visibility_state != IsUnmapped) ) {
                pursue = 0;
                break;
	    }

            subwindow = send_object->id.window;
            send_object = send_object->parent;
        } /* End while() */

        if (!subwindow)
            subwindow = None; /* convert to X's terminology */

        xisGrabPointerSubwindow = subwindow;
    } /* End if (xisState.mod_button_state != 0 && xisPointerGrabbed) */

    if ((send_object != NULL) && pursue) {
    
        if (clicks == 0) 
            xisInform.event_code = EventMouseButtonDown;
        else 
            xisInform.event_code = EventMouseButtonMultiClick;

        /* Call all relavent InformExpectedActions functions */    

        xisInform.is_valid = 1;
        xisInform.action_obj = action_object;
        xisInform.modifier_key_status = modifier_keys;
        xisInform.button_num = button_num;
        xisInform.key_code = 0;
        xisInform.edge_code = 0;
        xisInform.num_clicks = clicks;
    
        current_object = action_object;

        while (current_object != NULL) {
            if (current_object->proc_InformExpectedActions != NULL) {
                xisInform.current_obj = current_object;
                (*current_object->proc_InformExpectedActions)
                                                  (xisInform.event_code);
	    }
            current_object = current_object->parent;
	}

        xisInform.is_valid = 0;

        /* Send synthesized Button Press event */

        (*xisTraceMsg)("    Action_obj=%s\n",xisGetObjectName(action_object));
        (*xisTraceMsg)("    Send_obj = %s\n",xisGetObjectName(send_object));

        if (clicks == 0)
            clicks = 1;

        time = xisGetServerTime(xisMultiClickTime); 

        for (i=0; i<clicks; i++) {
            xisSendEvent(send_window, xisGrabPointerSubwindow,
                         ButtonPress, xisUseCurrentTime && !multi ?
			 CurrentTime : time + 4*i + 1,
                         root_x - send_object->x,
                         root_y - send_object->y, 
                         root_x, root_y, modifier_keys,
                         xisMouseButtonDetail[button_num]);

            xisState.mod_button_state |= xisMouseButtonMask[button_num] |
                                         modifier_keys;

            xisProcessEvents(NULL,0);

            if (!xisPointerGrabbed) {
                /**NoteGrab: after xisProcessEvents now for menus **/
                xisPointerGrabbed = 1;
                xisGrabPointerWindow = send_window;
	 
   
	        if (xisPointerGrabMode == POINTER_GRAB_CONTROLLED) {

                   while (!send_object->id.window)
                        send_object = send_object->parent;

                   XGrabPointer(xisDisplay,send_object->id.window,
                             send_object->your_event_mask&OwnerGrabButtonMask,
                             (send_object->your_event_mask & GOOD_GRAB_BITS) |
                             (ButtonPressMask|ButtonReleaseMask),
                             GrabModeAsync,GrabModeAsync,None,None,
                             xisUseCurrentTime && !multi ? CurrentTime : 
							   time + 4*i + 1 );

                /**NoteGrab***/
              }
            }

            if (clicks > 1) { /* Doing a Multi-click */
                xisSendEvent(send_window, xisGrabPointerSubwindow, 
                             ButtonRelease, xisUseCurrentTime && !multi ?
			     CurrentTime : time + 4*i +2,
                             root_x - send_object->x,
                             root_y - send_object->y, 
                             root_x, root_y, modifier_keys,
                             xisMouseButtonDetail[button_num]);

                xisState.mod_button_state &= (~xisMouseButtonMask[button_num]) &
                                             (~modifier_keys);

                if (xisState.mod_button_state == 0) {
	            if (xisPointerGrabMode == POINTER_GRAB_CONTROLLED)
                       XUngrabPointer(xisDisplay, xisUseCurrentTime && !multi ? 
						  CurrentTime : 
						  time + 4*i + 3);
                    xisPointerGrabbed = 0;
                    xisGrabPointerWindow = 0;
	        }

                xisProcessEvents(NULL,0);
	    }
            else
                clicks = 0;
	}
        if (xisUseSyntheticTime)
            xisSyntheticTime += 4*i;
    } /* End if ((send_object != NULL) && pursue) */

    if (clicks == 0)
        xisState.mod_button_state |= xisMouseButtonMask[button_num] |
                                     modifier_keys;
    else {
        xisState.mod_button_state &= (~xisMouseButtonMask[button_num]);
        xisState.selected_widget = 0;
        xisState.selected_object_type = 0;
        xisState.selected_instance = 0;
    }

} /* End xisCommonButtonPress() */
