diff options
Diffstat (limited to 'lib/widget/mouse.c')
-rw-r--r-- | lib/widget/mouse.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/lib/widget/mouse.c b/lib/widget/mouse.c new file mode 100644 index 0000000..15ad5f5 --- /dev/null +++ b/lib/widget/mouse.c @@ -0,0 +1,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); +} + +/* --------------------------------------------------------------------------------------------- */ |