(make_symlink): add missing space in error message.
[midnight-commander.git] / lib / widget / widget-common.c
blobe6deefbf0335f2bece13eb849a275949ee5c38ec
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994-2017
5 Free Software Foundation, Inc.
7 Authors:
8 Radek Doulik, 1994, 1995
9 Miguel de Icaza, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
13 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
15 This file is part of the Midnight Commander.
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 /** \file widget-common.c
32 * \brief Source: shared stuff of widgets
35 #include <config.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/color.h"
44 #include "lib/skin.h"
45 #include "lib/strutil.h"
46 #include "lib/widget.h"
48 /*** global variables ****************************************************************************/
50 /*** file scope macro definitions ****************************************************************/
52 /*** file scope type declarations ****************************************************************/
54 /*** file scope variables ************************************************************************/
56 /* --------------------------------------------------------------------------------------------- */
57 /*** file scope functions ************************************************************************/
58 /* --------------------------------------------------------------------------------------------- */
60 static void
61 widget_do_focus (Widget * w, gboolean enable)
63 if (w != NULL && widget_get_state (WIDGET (w->owner), WST_FOCUSED))
64 widget_set_state (w, WST_FOCUSED, enable);
67 /* --------------------------------------------------------------------------------------------- */
68 /**
69 * Focus specified widget in it's owner.
71 * @param w widget to be focused.
74 static void
75 widget_focus (Widget * w)
77 WDialog *h = DIALOG (w->owner);
79 if (h == NULL)
80 return;
82 if (WIDGET (h->current->data) != w)
84 widget_do_focus (WIDGET (h->current->data), FALSE);
85 /* Test if focus lost was allowed and focus has really been loose */
86 if (h->current == NULL || !widget_get_state (WIDGET (h->current->data), WST_FOCUSED))
88 widget_do_focus (w, TRUE);
89 h->current = dlg_find (h, w);
92 else if (!widget_get_state (w, WST_FOCUSED))
93 widget_do_focus (w, TRUE);
96 /* --------------------------------------------------------------------------------------------- */
98 /**
99 * Put widget on top or bottom of Z-order.
101 static void
102 widget_reorder (GList * l, gboolean set_top)
104 WDialog *h = WIDGET (l->data)->owner;
106 h->widgets = g_list_remove_link (h->widgets, l);
107 if (set_top)
108 h->widgets = g_list_concat (h->widgets, l);
109 else
110 h->widgets = g_list_concat (l, h->widgets);
114 /* --------------------------------------------------------------------------------------------- */
115 /*** public functions ****************************************************************************/
116 /* --------------------------------------------------------------------------------------------- */
118 struct hotkey_t
119 parse_hotkey (const char *text)
121 hotkey_t result;
122 const char *cp, *p;
124 if (text == NULL)
125 text = "";
127 /* search for '&', that is not on the of text */
128 cp = strchr (text, '&');
129 if (cp != NULL && cp[1] != '\0')
131 result.start = g_strndup (text, cp - text);
133 /* skip '&' */
134 cp++;
135 p = str_cget_next_char (cp);
136 result.hotkey = g_strndup (cp, p - cp);
138 cp = p;
139 result.end = g_strdup (cp);
141 else
143 result.start = g_strdup (text);
144 result.hotkey = NULL;
145 result.end = NULL;
148 return result;
151 /* --------------------------------------------------------------------------------------------- */
153 void
154 release_hotkey (const hotkey_t hotkey)
156 g_free (hotkey.start);
157 g_free (hotkey.hotkey);
158 g_free (hotkey.end);
161 /* --------------------------------------------------------------------------------------------- */
164 hotkey_width (const hotkey_t hotkey)
166 int result;
168 result = str_term_width1 (hotkey.start);
169 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
170 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
171 return result;
174 /* --------------------------------------------------------------------------------------------- */
176 void
177 hotkey_draw (Widget * w, const hotkey_t hotkey, gboolean focused)
179 widget_selectcolor (w, focused, FALSE);
180 tty_print_string (hotkey.start);
182 if (hotkey.hotkey != NULL)
184 widget_selectcolor (w, focused, TRUE);
185 tty_print_string (hotkey.hotkey);
186 widget_selectcolor (w, focused, FALSE);
189 if (hotkey.end != NULL)
190 tty_print_string (hotkey.end);
193 /* --------------------------------------------------------------------------------------------- */
195 void
196 widget_init (Widget * w, int y, int x, int lines, int cols,
197 widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
199 w->x = x;
200 w->y = y;
201 w->cols = cols;
202 w->lines = lines;
203 w->pos_flags = WPOS_KEEP_DEFAULT;
204 w->callback = callback;
205 w->mouse_callback = mouse_callback;
206 w->owner = NULL;
207 w->mouse.forced_capture = FALSE;
208 w->mouse.capture = FALSE;
209 w->mouse.last_msg = MSG_MOUSE_NONE;
210 w->mouse.last_buttons_down = 0;
212 w->options = WOP_DEFAULT;
213 w->state = WST_DEFAULT;
216 /* --------------------------------------------------------------------------------------------- */
218 /* Default callback for widgets */
219 cb_ret_t
220 widget_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
222 (void) w;
223 (void) sender;
224 (void) parm;
225 (void) data;
227 switch (msg)
229 case MSG_INIT:
230 case MSG_FOCUS:
231 case MSG_UNFOCUS:
232 case MSG_ENABLE:
233 case MSG_DISABLE:
234 case MSG_DRAW:
235 case MSG_DESTROY:
236 case MSG_CURSOR:
237 case MSG_IDLE:
238 return MSG_HANDLED;
240 default:
241 return MSG_NOT_HANDLED;
245 /* --------------------------------------------------------------------------------------------- */
248 * Apply new options to widget.
250 * @param w widget
251 * @param options widget option flags to modify. Several flags per call can be modified.
252 * @param enable TRUE if specified options should be added, FALSE if options should be removed
254 void
255 widget_set_options (Widget * w, widget_options_t options, gboolean enable)
257 if (enable)
258 w->options |= options;
259 else
260 w->options &= ~options;
263 /* --------------------------------------------------------------------------------------------- */
266 * Modify state of widget.
268 * @param w widget
269 * @param state widget state flag to modify
270 * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
271 * Only one flag per call can be modified.
272 * @return MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
274 cb_ret_t
275 widget_set_state (Widget * w, widget_state_t state, gboolean enable)
277 gboolean ret = MSG_HANDLED;
279 if (enable)
280 w->state |= state;
281 else
282 w->state &= ~state;
284 if (enable)
286 /* exclusive bits */
287 if ((state & WST_CONSTRUCT) != 0)
288 w->state &= ~(WST_ACTIVE | WST_SUSPENDED | WST_CLOSED);
289 else if ((state & WST_ACTIVE) != 0)
290 w->state &= ~(WST_CONSTRUCT | WST_SUSPENDED | WST_CLOSED);
291 else if ((state & WST_SUSPENDED) != 0)
292 w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_CLOSED);
293 else if ((state & WST_CLOSED) != 0)
294 w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_SUSPENDED);
297 if (w->owner == NULL)
298 return MSG_NOT_HANDLED;
300 switch (state)
302 case WST_DISABLED:
303 ret = send_message (w, NULL, enable ? MSG_DISABLE : MSG_ENABLE, 0, NULL);
304 if (ret == MSG_HANDLED && widget_get_state (WIDGET (w->owner), WST_ACTIVE))
305 ret = send_message (w, NULL, MSG_DRAW, 0, NULL);
306 break;
308 case WST_FOCUSED:
310 widget_msg_t msg;
312 msg = enable ? MSG_FOCUS : MSG_UNFOCUS;
313 ret = send_message (w, NULL, msg, 0, NULL);
314 if (ret == MSG_HANDLED && widget_get_state (WIDGET (w->owner), WST_ACTIVE))
316 send_message (w, NULL, MSG_DRAW, 0, NULL);
317 /* Notify owner that focus was moved from one widget to another */
318 send_message (w->owner, w, MSG_CHANGED_FOCUS, 0, NULL);
321 break;
323 default:
324 break;
327 return ret;
330 /* --------------------------------------------------------------------------------------------- */
332 void
333 widget_set_size (Widget * widget, int y, int x, int lines, int cols)
335 widget->x = x;
336 widget->y = y;
337 widget->cols = cols;
338 widget->lines = lines;
339 send_message (widget, NULL, MSG_RESIZE, 0, NULL);
340 if (widget->owner != NULL && widget_get_state (WIDGET (widget->owner), WST_ACTIVE))
341 send_message (widget, NULL, MSG_DRAW, 0, NULL);
344 /* --------------------------------------------------------------------------------------------- */
346 void
347 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
349 WDialog *h = w->owner;
350 int color;
352 if (widget_get_state (w, WST_DISABLED))
353 color = DISABLED_COLOR;
354 else if (hotkey)
356 if (focused)
357 color = h->color[DLG_COLOR_HOT_FOCUS];
358 else
359 color = h->color[DLG_COLOR_HOT_NORMAL];
361 else
363 if (focused)
364 color = h->color[DLG_COLOR_FOCUS];
365 else
366 color = h->color[DLG_COLOR_NORMAL];
369 tty_setcolor (color);
372 /* --------------------------------------------------------------------------------------------- */
374 void
375 widget_erase (Widget * w)
377 if (w != NULL)
378 tty_fill_region (w->y, w->x, w->lines, w->cols, ' ');
381 /* --------------------------------------------------------------------------------------------- */
383 * Check whether widget is active or not.
384 * @param w the widget
386 * @return TRUE if the widget is active, FALSE otherwise
389 gboolean
390 widget_is_active (const void *w)
392 return (w == CONST_WIDGET (w)->owner->current->data);
395 /* --------------------------------------------------------------------------------------------- */
397 void
398 widget_redraw (Widget * w)
400 if (w != NULL)
402 WDialog *h = w->owner;
404 if (h != NULL && widget_get_state (WIDGET (h), WST_ACTIVE))
405 w->callback (w, NULL, MSG_DRAW, 0, NULL);
409 /* --------------------------------------------------------------------------------------------- */
411 * Replace widget in the dialog.
413 * @param old_w old widget that need to be replaced
414 * @param new_w new widget that will replace @old_w
417 void
418 widget_replace (Widget * old_w, Widget * new_w)
420 WDialog *h = old_w->owner;
421 gboolean should_focus = FALSE;
422 GList *holder;
424 if (h->widgets == NULL)
425 return;
427 if (h->current == NULL)
428 h->current = h->widgets;
430 /* locate widget position in the list */
431 if (old_w == h->current->data)
432 holder = h->current;
433 else
434 holder = g_list_find (h->widgets, old_w);
436 /* if old widget is focused, we should focus the new one... */
437 if (widget_get_state (old_w, WST_FOCUSED))
438 should_focus = TRUE;
439 /* ...but if new widget isn't selectable, we cannot focus it */
440 if (!widget_get_options (new_w, WOP_SELECTABLE))
441 should_focus = FALSE;
443 /* if new widget isn't selectable, select other widget before replace */
444 if (!should_focus)
446 GList *l;
448 for (l = dlg_get_widget_next_of (holder);
449 !widget_get_options (WIDGET (l->data), WOP_SELECTABLE)
450 && !widget_get_state (WIDGET (l->data), WST_DISABLED); l = dlg_get_widget_next_of (l))
453 widget_select (WIDGET (l->data));
456 /* replace widget */
457 new_w->owner = h;
458 new_w->id = old_w->id;
459 holder->data = new_w;
461 send_message (old_w, NULL, MSG_DESTROY, 0, NULL);
462 send_message (new_w, NULL, MSG_INIT, 0, NULL);
464 if (should_focus)
465 widget_select (new_w);
466 else
467 widget_redraw (new_w);
470 /* --------------------------------------------------------------------------------------------- */
472 * Select specified widget in it's owner.
474 * @param w widget to be selected
477 void
478 widget_select (Widget * w)
480 WDialog *h;
482 if (!widget_get_options (w, WOP_SELECTABLE))
483 return;
485 h = w->owner;
486 if (h != NULL)
488 if (widget_get_options (w, WOP_TOP_SELECT))
490 GList *l;
492 l = dlg_find (h, w);
493 widget_reorder (l, TRUE);
496 widget_focus (w);
500 /* --------------------------------------------------------------------------------------------- */
502 * Set widget at bottom of widget list.
505 void
506 widget_set_bottom (Widget * w)
508 widget_reorder (dlg_find (w->owner, w), FALSE);
511 /* --------------------------------------------------------------------------------------------- */
513 * Check whether two widgets are overlapped or not.
514 * @param a 1st widget
515 * @param b 2nd widget
517 * @return TRUE if widgets are overlapped, FALSE otherwise.
520 gboolean
521 widget_overlapped (const Widget * a, const Widget * b)
523 return !((b->x >= a->x + a->cols)
524 || (a->x >= b->x + b->cols) || (b->y >= a->y + a->lines) || (a->y >= b->y + b->lines));
527 /* --------------------------------------------------------------------------------------------- */
528 /* get mouse pointer location within widget */
530 Gpm_Event
531 mouse_get_local (const Gpm_Event * global, const Widget * w)
533 Gpm_Event local;
535 local.buttons = global->buttons;
536 #ifdef HAVE_LIBGPM
537 local.clicks = 0;
538 local.margin = 0;
539 local.modifiers = 0;
540 local.vc = 0;
541 #endif
542 local.x = global->x - w->x;
543 local.y = global->y - w->y;
544 local.type = global->type;
546 return local;
549 /* --------------------------------------------------------------------------------------------- */
551 gboolean
552 mouse_global_in_widget (const Gpm_Event * event, const Widget * w)
554 return (event->x > w->x) && (event->y > w->y) && (event->x <= w->x + w->cols)
555 && (event->y <= w->y + w->lines);
558 /* --------------------------------------------------------------------------------------------- */