| 1 | /* |
| 2 | Widgets for the Midnight Commander |
| 3 | |
| 4 | Copyright (C) 1994-2015 |
| 5 | Free Software Foundation, Inc. |
| 6 | |
| 7 | Authors: |
| 8 | Human beings. |
| 9 | |
| 10 | This file is part of the Midnight Commander. |
| 11 | |
| 12 | The Midnight Commander is free software: you can redistribute it |
| 13 | and/or modify it under the terms of the GNU General Public License as |
| 14 | published by the Free Software Foundation, either version 3 of the License, |
| 15 | or (at your option) any later version. |
| 16 | |
| 17 | The Midnight Commander is distributed in the hope that it will be useful, |
| 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | GNU General Public License for more details. |
| 21 | |
| 22 | You should have received a copy of the GNU General Public License |
| 23 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 24 | */ |
| 25 | |
| 26 | /** \file mouse.c |
| 27 | * \brief Header: High-level mouse API |
| 28 | */ |
| 29 | |
| 30 | #include <config.h> |
| 31 | |
| 32 | #include "lib/global.h" |
| 33 | #include "lib/widget.h" |
| 34 | |
| 35 | /*** global variables ****************************************************************************/ |
| 36 | |
| 37 | /*** file scope macro definitions ****************************************************************/ |
| 38 | |
| 39 | /*** file scope type declarations ****************************************************************/ |
| 40 | |
| 41 | /*** file scope variables ************************************************************************/ |
| 42 | |
| 43 | static int last_buttons_down; |
| 44 | |
| 45 | /* --------------------------------------------------------------------------------------------- */ |
| 46 | /*** file scope functions ************************************************************************/ |
| 47 | /* --------------------------------------------------------------------------------------------- */ |
| 48 | |
| 49 | /* |
| 50 | * Constructs a mouse event structure. The is the high-level type used |
| 51 | * with "easy callbacks". |
| 52 | */ |
| 53 | static void |
| 54 | init_mouse_event (mouse_event_t * event, mouse_msg_t msg, const Gpm_Event * global_gpm, |
| 55 | const Widget * w) |
| 56 | { |
| 57 | event->msg = msg; |
| 58 | event->x = global_gpm->x - w->x - 1; /* '-1' because Gpm_Event is 1-based. */ |
| 59 | event->y = global_gpm->y - w->y - 1; |
| 60 | event->count = global_gpm->type & (GPM_SINGLE | GPM_DOUBLE | GPM_TRIPLE); |
| 61 | event->buttons = global_gpm->buttons; |
| 62 | event->result.abort = FALSE; |
| 63 | event->result.repeat = FALSE; |
| 64 | } |
| 65 | |
| 66 | /* --------------------------------------------------------------------------------------------- */ |
| 67 | |
| 68 | /* |
| 69 | * This is the low-level mouse handler that's in use when you install |
| 70 | * an "easy callback". |
| 71 | * |
| 72 | * It receives a Gpm_Event event and translates it into a higher level |
| 73 | * protocol with which it feeds your "easy callback". |
| 74 | * |
| 75 | * Tip: for details on the C mouse API, see MC's lib/tty/mouse.h, |
| 76 | * or GPM's excellent 'info' manual: |
| 77 | * |
| 78 | * http://www.fifi.org/cgi-bin/info2www?(gpm)Event+Types |
| 79 | */ |
| 80 | static int |
| 81 | easy_mouse_translator (Gpm_Event * event, void *data) |
| 82 | { |
| 83 | Widget *w = WIDGET (data); |
| 84 | |
| 85 | gboolean in_widget; |
| 86 | gboolean run_click = FALSE; |
| 87 | mouse_msg_t msg = 0; |
| 88 | |
| 89 | in_widget = mouse_global_in_widget (event, w); |
| 90 | |
| 91 | /* |
| 92 | * Very special widgets may want to control area outside their bounds. |
| 93 | * For such widgets you will have to turn on the 'forced_capture' flag. |
| 94 | * You'll also need, in your mouse handler, to inform the system of |
| 95 | * events you want to pass on by setting 'event->result.abort' to TRUE. |
| 96 | */ |
| 97 | if (w->Mouse.forced_capture) |
| 98 | in_widget = TRUE; |
| 99 | |
| 100 | if ((event->type & GPM_DOWN) != 0) |
| 101 | { |
| 102 | if (in_widget) |
| 103 | { |
| 104 | if ((event->buttons & GPM_B_UP) != 0) |
| 105 | msg = MSG_MOUSE_SCROLL_UP; |
| 106 | else if ((event->buttons & GPM_B_DOWN) != 0) |
| 107 | msg = MSG_MOUSE_SCROLL_DOWN; |
| 108 | else |
| 109 | { |
| 110 | /* Handle normal buttons: anything but the mouse wheel's. |
| 111 | * |
| 112 | * (Note that turning on capturing for the mouse wheel |
| 113 | * buttons doesn't make sense as they don't generate a |
| 114 | * mouse_up event, which means we'd never get uncaptured.) |
| 115 | */ |
| 116 | w->Mouse.capture = TRUE; |
| 117 | msg = MSG_MOUSE_DOWN; |
| 118 | |
| 119 | last_buttons_down = event->buttons; |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | else if ((event->type & GPM_UP) != 0) |
| 124 | { |
| 125 | /* We trigger the mouse_up event even when !in_widget. That's |
| 126 | * because, for example, a paint application should stop drawing |
| 127 | * lines when the button is released even outside the canvas. */ |
| 128 | if (w->Mouse.capture) |
| 129 | { |
| 130 | w->Mouse.capture = FALSE; |
| 131 | msg = MSG_MOUSE_UP; |
| 132 | |
| 133 | if (in_widget) |
| 134 | run_click = TRUE; |
| 135 | |
| 136 | /* |
| 137 | * When using xterm, event->buttons reports the buttons' state |
| 138 | * after the event occurred (meaning that event->buttons is zero, |
| 139 | * because the mouse button is now released). When using GPM, |
| 140 | * however, that field reports the button(s) that was released. |
| 141 | * |
| 142 | * The following makes xterm behave effectively like GPM: |
| 143 | */ |
| 144 | if (event->buttons == 0) |
| 145 | event->buttons = last_buttons_down; |
| 146 | } |
| 147 | } |
| 148 | else if ((event->type & GPM_DRAG) != 0) |
| 149 | { |
| 150 | if (w->Mouse.capture) |
| 151 | msg = MSG_MOUSE_DRAG; |
| 152 | } |
| 153 | else if ((event->type & GPM_MOVE) != 0) |
| 154 | { |
| 155 | if (in_widget) |
| 156 | msg = MSG_MOUSE_MOVE; |
| 157 | } |
| 158 | |
| 159 | if (msg != 0) |
| 160 | { |
| 161 | mouse_event_t local; |
| 162 | |
| 163 | init_mouse_event (&local, msg, event, w); |
| 164 | |
| 165 | w->Mouse.callback (w, msg, &local); |
| 166 | if (run_click) |
| 167 | w->Mouse.callback (w, MSG_MOUSE_CLICK, &local); |
| 168 | |
| 169 | if (!local.result.abort) |
| 170 | return local.result.repeat ? MOU_REPEAT : MOU_NORMAL; |
| 171 | } |
| 172 | |
| 173 | return MOU_UNHANDLED; |
| 174 | } |
| 175 | |
| 176 | /* --------------------------------------------------------------------------------------------- */ |
| 177 | /*** public functions ****************************************************************************/ |
| 178 | /* --------------------------------------------------------------------------------------------- */ |
| 179 | |
| 180 | /** |
| 181 | * Use this to install an "easy mouse callback". |
| 182 | * |
| 183 | * (The mouse callback widget_init() accepts is a low-level one; you can |
| 184 | * pass NULL to it. In the future we'll probably do the opposite: have |
| 185 | * widget_init() accept the "easy" callback.) |
| 186 | */ |
| 187 | void |
| 188 | set_easy_mouse_callback (Widget * w, easy_mouse_callback cb) |
| 189 | { |
| 190 | w->mouse = easy_mouse_translator; |
| 191 | w->Mouse.callback = cb; |
| 192 | } |
| 193 | |
| 194 | /* --------------------------------------------------------------------------------------------- */ |