summaryrefslogtreecommitdiffstats
path: root/lib/widget/mouse.c
blob: 15ad5f5a5863155b0c2f1b8c2e97e0dc4847a75e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
   Widgets for the Midnight Commander

   Copyright (C) 2016-2023
   Free Software Foundation, Inc.

   Authors:
   Human beings.

   This file is part of the Midnight Commander.

   The Midnight Commander is free software: you can redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation, either version 3 of the License,
   or (at your option) any later version.

   The Midnight Commander is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/** \file mouse.c
 *  \brief Header: High-level mouse API
 */

#include <config.h>

#include "lib/global.h"
#include "lib/widget.h"

#include "lib/widget/mouse.h"

/*** global variables ****************************************************************************/

/*** file scope macro definitions ****************************************************************/

/*** file scope type declarations ****************************************************************/

/*** forward declarations (file scope functions) *************************************************/

/*** file scope variables ************************************************************************/

/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */

/**
 * Constructs a mouse event structure.
 *
 * It receives a Gpm_Event event and translates it into a higher level protocol.
 *
 * Tip: for details on the C mouse API, see MC's lib/tty/mouse.h,
 * or GPM's excellent 'info' manual:
 *
 *    http://www.fifi.org/cgi-bin/info2www?(gpm)Event+Types
 */
static void
init_mouse_event (mouse_event_t * event, mouse_msg_t msg, const Gpm_Event * global_gpm,
                  const Widget * w)
{
    event->msg = msg;
    event->x = global_gpm->x - w->rect.x - 1;   /* '-1' because Gpm_Event is 1-based. */
    event->y = global_gpm->y - w->rect.y - 1;
    event->count = global_gpm->type & (GPM_SINGLE | GPM_DOUBLE | GPM_TRIPLE);
    event->buttons = global_gpm->buttons;
    event->result.abort = FALSE;
    event->result.repeat = FALSE;
}

/* --------------------------------------------------------------------------------------------- */

/**
 * Translate GPM event to high-level event,
 *
 * @param w Widget object
 * @param event GPM event
 *
 * @return high level mouse event
 */
static mouse_event_t
mouse_translate_event (Widget * w, Gpm_Event * event)
{
    gboolean in_widget;
    mouse_msg_t msg = MSG_MOUSE_NONE;
    mouse_event_t local;

    /*
     * Very special widgets may want to control area outside their bounds.
     * For such widgets you will have to turn on the 'forced_capture' flag.
     * You'll also need, in your mouse handler, to inform the system of
     * events you want to pass on by setting 'event->result.abort' to TRUE.
     */
    in_widget = w->mouse.forced_capture || mouse_global_in_widget (event, w);

    if ((event->type & GPM_DOWN) != 0)
    {
        if (in_widget)
        {
            if ((event->buttons & GPM_B_UP) != 0)
                msg = MSG_MOUSE_SCROLL_UP;
            else if ((event->buttons & GPM_B_DOWN) != 0)
                msg = MSG_MOUSE_SCROLL_DOWN;
            else
            {
                /* Handle normal buttons: anything but the mouse wheel's.
                 *
                 * (Note that turning on capturing for the mouse wheel
                 * buttons doesn't make sense as they don't generate a
                 * mouse_up event, which means we'd never get uncaptured.)
                 */
                w->mouse.capture = TRUE;
                msg = MSG_MOUSE_DOWN;

                w->mouse.last_buttons_down = event->buttons;
            }
        }
    }
    else if ((event->type & GPM_UP) != 0)
    {
        /* We trigger the mouse_up event even when !in_widget. That's
         * because, for example, a paint application should stop drawing
         * lines when the button is released even outside the canvas. */
        if (w->mouse.capture)
        {
            w->mouse.capture = FALSE;
            msg = MSG_MOUSE_UP;

            /*
             * When using xterm, event->buttons reports the buttons' state
             * after the event occurred (meaning that event->buttons is zero,
             * because the mouse button is now released). When using GPM,
             * however, that field reports the button(s) that was released.
             *
             * The following makes xterm behave effectively like GPM:
             */
            if (event->buttons == 0)
                event->buttons = w->mouse.last_buttons_down;
        }
    }
    else if ((event->type & GPM_DRAG) != 0)
    {
        if (w->mouse.capture)
            msg = MSG_MOUSE_DRAG;
    }
    else if ((event->type & GPM_MOVE) != 0)
    {
        if (in_widget)
            msg = MSG_MOUSE_MOVE;
    }

    init_mouse_event (&local, msg, event, w);

    return local;
}

/* --------------------------------------------------------------------------------------------- */

/**
 * Call widget mouse handler to process high-level mouse event.
 *
 * Besides sending to the widget the event itself, this function may also
 * send one or more pseudo events. Currently, MSG_MOUSE_CLICK is the only
 * pseudo event in existence but in the future (e.g., with the introduction
 * of a drag-drop API) there may be more.
 *
 * @param w Widget object
 * @param event high level mouse event
 *
 * @return result of mouse event handling
 */
static int
mouse_process_event (Widget * w, mouse_event_t * event)
{
    int ret = MOU_UNHANDLED;

    if (event->msg != MSG_MOUSE_NONE)
    {
        w->mouse_callback (w, event->msg, event);

        /* If a widget aborts a MSG_MOUSE_DOWN, we uncapture it so it
         * doesn't steal events from other widgets. */
        if (event->msg == MSG_MOUSE_DOWN && event->result.abort)
            w->mouse.capture = FALSE;

        /* Upon releasing the mouse button: if the mouse hasn't been dragged
         * since the MSG_MOUSE_DOWN, we also trigger a click. */
        if (event->msg == MSG_MOUSE_UP && w->mouse.last_msg == MSG_MOUSE_DOWN)
            w->mouse_callback (w, MSG_MOUSE_CLICK, event);

        /* Record the current event type for the benefit of the next event. */
        w->mouse.last_msg = event->msg;

        if (!event->result.abort)
            ret = event->result.repeat ? MOU_REPEAT : MOU_NORMAL;
    }

    return ret;
}


/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */

/**
 * Translate GPM event to high-level event and process it
 *
 * @param w Widget object
 * @param event GPM event
 *
 * @return result of mouse event handling
 */
int
mouse_handle_event (Widget * w, Gpm_Event * event)
{
    mouse_event_t me;

    me = mouse_translate_event (w, event);

    return mouse_process_event (w, &me);
}

/* --------------------------------------------------------------------------------------------- */