1 /*
2 Widgets for the Midnight Commander
3
4 Copyright (C) 1994-2021
5 Free Software Foundation, Inc.
6
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
14
15 This file is part of the Midnight Commander.
16
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.
21
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.
26
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/>.
29 */
30
31 /** \file widget-common.c
32 * \brief Source: shared stuff of widgets
33 */
34
35 #include <config.h>
36
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "lib/global.h"
41
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"
47
48 /*** global variables ****************************************************************************/
49
50 /*** file scope macro definitions ****************************************************************/
51
52 /*** file scope type declarations ****************************************************************/
53
54 /*** file scope variables ************************************************************************/
55
56 /* maximum value of used widget ID */
57 static unsigned long widget_id = 0;
58
59 /* --------------------------------------------------------------------------------------------- */
60 /*** file scope functions ************************************************************************/
61 /* --------------------------------------------------------------------------------------------- */
62
63 /**
64 * Calc widget ID,
65 * Widget ID is uniq for each widget created during MC session (like PID in OS).
66 *
67 * @return widget ID.
68 */
69 static unsigned long
widget_set_id(void)70 widget_set_id (void)
71 {
72 unsigned long id;
73
74 id = widget_id++;
75 /* TODO IF NEEDED: if id is already used, find next free id. */
76
77 return id;
78 }
79
80 /* --------------------------------------------------------------------------------------------- */
81
82 static cb_ret_t
widget_default_resize(Widget * w,const WRect * r)83 widget_default_resize (Widget * w, const WRect * r)
84 {
85 if (r == NULL)
86 return MSG_NOT_HANDLED;
87
88 w->y = r->y;
89 w->x = r->x;
90 w->lines = r->lines;
91 w->cols = r->cols;
92
93 return MSG_HANDLED;
94 }
95
96 /* --------------------------------------------------------------------------------------------- */
97
98 static void
widget_do_focus(Widget * w,gboolean enable)99 widget_do_focus (Widget * w, gboolean enable)
100 {
101 if (w != NULL && widget_get_state (WIDGET (w->owner), WST_VISIBLE | WST_FOCUSED))
102 widget_set_state (w, WST_FOCUSED, enable);
103 }
104
105 /* --------------------------------------------------------------------------------------------- */
106 /**
107 * Focus specified widget in it's owner.
108 *
109 * @param w widget to be focused.
110 */
111
112 static void
widget_focus(Widget * w)113 widget_focus (Widget * w)
114 {
115 WGroup *g = w->owner;
116
117 if (g == NULL)
118 return;
119
120 if (WIDGET (g->current->data) != w)
121 {
122 widget_do_focus (WIDGET (g->current->data), FALSE);
123 /* Test if focus lost was allowed and focus has really been loose */
124 if (g->current == NULL || !widget_get_state (WIDGET (g->current->data), WST_FOCUSED))
125 {
126 widget_do_focus (w, TRUE);
127 g->current = widget_find (WIDGET (g), w);
128 }
129 }
130 else if (!widget_get_state (w, WST_FOCUSED))
131 widget_do_focus (w, TRUE);
132 }
133
134 /* --------------------------------------------------------------------------------------------- */
135
136 /**
137 * Put widget on top or bottom of Z-order.
138 */
139 static void
widget_reorder(GList * l,gboolean set_top)140 widget_reorder (GList * l, gboolean set_top)
141 {
142 WGroup *g = WIDGET (l->data)->owner;
143
144 g->widgets = g_list_remove_link (g->widgets, l);
145 if (set_top)
146 g->widgets = g_list_concat (g->widgets, l);
147 else
148 g->widgets = g_list_concat (l, g->widgets);
149 }
150
151 /* --------------------------------------------------------------------------------------------- */
152
153 static gboolean
hotkey_cmp(const char * s1,const char * s2)154 hotkey_cmp (const char *s1, const char *s2)
155 {
156 gboolean n1, n2;
157
158 n1 = s1 != NULL;
159 n2 = s2 != NULL;
160
161 if (n1 != n2)
162 return FALSE;
163
164 if (n1 && n2 && strcmp (s1, s2) != 0)
165 return FALSE;
166
167 return TRUE;
168 }
169
170 /* --------------------------------------------------------------------------------------------- */
171
172 static void
widget_default_mouse_callback(Widget * w,mouse_msg_t msg,mouse_event_t * event)173 widget_default_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
174 {
175 /* do nothing */
176 (void) w;
177 (void) msg;
178 (void) event;
179 }
180
181 /* --------------------------------------------------------------------------------------------- */
182
183 static const int *
widget_default_get_colors(const Widget * w)184 widget_default_get_colors (const Widget * w)
185 {
186 const Widget *owner = CONST_WIDGET (w->owner);
187
188 return (owner == NULL ? NULL : widget_get_colors (owner));
189 }
190
191 /* --------------------------------------------------------------------------------------------- */
192 /*** public functions ****************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
194
195 struct hotkey_t
hotkey_new(const char * text)196 hotkey_new (const char *text)
197 {
198 hotkey_t result;
199 const char *cp, *p;
200
201 if (text == NULL)
202 text = "";
203
204 /* search for '&', that is not on the of text */
205 cp = strchr (text, '&');
206 if (cp != NULL && cp[1] != '\0')
207 {
208 result.start = g_strndup (text, cp - text);
209
210 /* skip '&' */
211 cp++;
212 p = str_cget_next_char (cp);
213 result.hotkey = g_strndup (cp, p - cp);
214
215 cp = p;
216 result.end = g_strdup (cp);
217 }
218 else
219 {
220 result.start = g_strdup (text);
221 result.hotkey = NULL;
222 result.end = NULL;
223 }
224
225 return result;
226 }
227
228 /* --------------------------------------------------------------------------------------------- */
229
230 void
hotkey_free(const hotkey_t hotkey)231 hotkey_free (const hotkey_t hotkey)
232 {
233 g_free (hotkey.start);
234 g_free (hotkey.hotkey);
235 g_free (hotkey.end);
236 }
237
238 /* --------------------------------------------------------------------------------------------- */
239
240 int
hotkey_width(const hotkey_t hotkey)241 hotkey_width (const hotkey_t hotkey)
242 {
243 int result;
244
245 result = str_term_width1 (hotkey.start);
246 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
247 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
248 return result;
249 }
250
251 /* --------------------------------------------------------------------------------------------- */
252
253 gboolean
hotkey_equal(const hotkey_t hotkey1,const hotkey_t hotkey2)254 hotkey_equal (const hotkey_t hotkey1, const hotkey_t hotkey2)
255 {
256 /* *INDENT-OFF* */
257 return (strcmp (hotkey1.start, hotkey2.start) == 0) &&
258 hotkey_cmp (hotkey1.hotkey, hotkey2.hotkey) &&
259 hotkey_cmp (hotkey1.end, hotkey2.end);
260 /* *INDENT-ON* */
261 }
262
263 /* --------------------------------------------------------------------------------------------- */
264
265 void
hotkey_draw(Widget * w,const hotkey_t hotkey,gboolean focused)266 hotkey_draw (Widget * w, const hotkey_t hotkey, gboolean focused)
267 {
268 if (hotkey.start[0] != '\0')
269 {
270 widget_selectcolor (w, focused, FALSE);
271 tty_print_string (hotkey.start);
272 }
273
274 if (hotkey.hotkey != NULL)
275 {
276 widget_selectcolor (w, focused, TRUE);
277 tty_print_string (hotkey.hotkey);
278 }
279
280 if (hotkey.end != NULL)
281 {
282 widget_selectcolor (w, focused, FALSE);
283 tty_print_string (hotkey.end);
284 }
285 }
286
287 /* --------------------------------------------------------------------------------------------- */
288
289 char *
hotkey_get_text(const hotkey_t hotkey)290 hotkey_get_text (const hotkey_t hotkey)
291 {
292 GString *text;
293
294 text = g_string_new (hotkey.start);
295
296 if (hotkey.hotkey != NULL)
297 {
298 g_string_append_c (text, '&');
299 g_string_append (text, hotkey.hotkey);
300 }
301
302 if (hotkey.end != NULL)
303 g_string_append (text, hotkey.end);
304
305 return g_string_free (text, FALSE);
306 }
307
308 /* --------------------------------------------------------------------------------------------- */
309
310 void
widget_init(Widget * w,int y,int x,int lines,int cols,widget_cb_fn callback,widget_mouse_cb_fn mouse_callback)311 widget_init (Widget * w, int y, int x, int lines, int cols,
312 widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
313 {
314 w->id = widget_set_id ();
315 w->x = x;
316 w->y = y;
317 w->cols = cols;
318 w->lines = lines;
319 w->pos_flags = WPOS_KEEP_DEFAULT;
320 w->callback = callback;
321
322 w->keymap = NULL;
323 w->ext_keymap = NULL;
324 w->ext_mode = FALSE;
325
326 w->mouse_callback = mouse_callback != NULL ? mouse_callback : widget_default_mouse_callback;
327 w->owner = NULL;
328 w->mouse_handler = mouse_handle_event;
329 w->mouse.forced_capture = FALSE;
330 w->mouse.capture = FALSE;
331 w->mouse.last_msg = MSG_MOUSE_NONE;
332 w->mouse.last_buttons_down = 0;
333
334 w->options = WOP_DEFAULT;
335 w->state = WST_CONSTRUCT | WST_VISIBLE;
336
337 w->make_global = widget_default_make_global;
338 w->make_local = widget_default_make_local;
339
340 w->make_global = widget_default_make_global;
341 w->make_local = widget_default_make_local;
342
343 w->find = widget_default_find;
344 w->find_by_type = widget_default_find_by_type;
345 w->find_by_id = widget_default_find_by_id;
346
347 w->set_state = widget_default_set_state;
348 w->destroy = widget_default_destroy;
349 w->get_colors = widget_default_get_colors;
350 }
351
352 /* --------------------------------------------------------------------------------------------- */
353
354 /* Default callback for widgets */
355 cb_ret_t
widget_default_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)356 widget_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
357 {
358 (void) sender;
359 (void) parm;
360
361 switch (msg)
362 {
363 case MSG_INIT:
364 case MSG_FOCUS:
365 case MSG_UNFOCUS:
366 case MSG_ENABLE:
367 case MSG_DISABLE:
368 case MSG_DRAW:
369 case MSG_DESTROY:
370 case MSG_CURSOR:
371 case MSG_IDLE:
372 return MSG_HANDLED;
373
374 case MSG_RESIZE:
375 return widget_default_resize (w, CONST_RECT (data));
376
377 default:
378 return MSG_NOT_HANDLED;
379 }
380 }
381
382 /* --------------------------------------------------------------------------------------------- */
383
384 /**
385 * Apply new options to widget.
386 *
387 * @param w widget
388 * @param options widget option flags to modify. Several flags per call can be modified.
389 * @param enable TRUE if specified options should be added, FALSE if options should be removed
390 */
391 void
widget_set_options(Widget * w,widget_options_t options,gboolean enable)392 widget_set_options (Widget * w, widget_options_t options, gboolean enable)
393 {
394 if (enable)
395 w->options |= options;
396 else
397 w->options &= ~options;
398 }
399
400 /* --------------------------------------------------------------------------------------------- */
401
402 void
widget_adjust_position(widget_pos_flags_t pos_flags,int * y,int * x,int * lines,int * cols)403 widget_adjust_position (widget_pos_flags_t pos_flags, int *y, int *x, int *lines, int *cols)
404 {
405 if ((pos_flags & WPOS_FULLSCREEN) != 0)
406 {
407 *y = 0;
408 *x = 0;
409 *lines = LINES;
410 *cols = COLS;
411 }
412 else
413 {
414 if ((pos_flags & WPOS_CENTER_HORZ) != 0)
415 *x = (COLS - *cols) / 2;
416
417 if ((pos_flags & WPOS_CENTER_VERT) != 0)
418 *y = (LINES - *lines) / 2;
419
420 if ((pos_flags & WPOS_TRYUP) != 0)
421 {
422 if (*y > 3)
423 *y -= 2;
424 else if (*y == 3)
425 *y = 2;
426 }
427 }
428 }
429
430 /* --------------------------------------------------------------------------------------------- */
431 /**
432 * Change widget position and size.
433 *
434 * @param w widget
435 * @param y y coordinate of top-left corner
436 * @param x x coordinate of top-left corner
437 * @param lines width
438 * @param cols height
439 */
440
441 void
widget_set_size(Widget * w,int y,int x,int lines,int cols)442 widget_set_size (Widget * w, int y, int x, int lines, int cols)
443 {
444 WRect r = { y, x, lines, cols };
445
446 send_message (w, NULL, MSG_RESIZE, 0, &r);
447 widget_draw (w);
448 }
449
450 /* --------------------------------------------------------------------------------------------- */
451
452 void
widget_selectcolor(Widget * w,gboolean focused,gboolean hotkey)453 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
454 {
455 int color;
456 const int *colors;
457
458 colors = widget_get_colors (w);
459
460 if (widget_get_state (w, WST_DISABLED))
461 color = DISABLED_COLOR;
462 else if (hotkey)
463 color = colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_HOT_NORMAL];
464 else
465 color = colors[focused ? DLG_COLOR_FOCUS : DLG_COLOR_NORMAL];
466
467 tty_setcolor (color);
468 }
469
470 /* --------------------------------------------------------------------------------------------- */
471
472 void
widget_erase(Widget * w)473 widget_erase (Widget * w)
474 {
475 if (w != NULL)
476 tty_fill_region (w->y, w->x, w->lines, w->cols, ' ');
477 }
478
479 /* --------------------------------------------------------------------------------------------- */
480
481 void
widget_set_visibility(Widget * w,gboolean make_visible)482 widget_set_visibility (Widget * w, gboolean make_visible)
483 {
484 if (widget_get_state (w, WST_VISIBLE) != make_visible)
485 widget_set_state (w, WST_VISIBLE, make_visible);
486 }
487
488 /* --------------------------------------------------------------------------------------------- */
489 /**
490 * Check whether widget is active or not.
491 * Widget is active if it's current in the its owner and each owner in the chain is current too.
492 *
493 * @param w the widget
494 *
495 * @return TRUE if the widget is active, FALSE otherwise
496 */
497
498 gboolean
widget_is_active(const void * w)499 widget_is_active (const void *w)
500 {
501 const WGroup *owner;
502
503 /* Is group top? */
504 if (w == top_dlg->data)
505 return TRUE;
506
507 owner = CONST_WIDGET (w)->owner;
508
509 /* Is widget in any group? */
510 if (owner == NULL)
511 return FALSE;
512
513 if (w != owner->current->data)
514 return FALSE;
515
516 return widget_is_active (owner);
517 }
518
519 /* --------------------------------------------------------------------------------------------- */
520
521 cb_ret_t
widget_draw(Widget * w)522 widget_draw (Widget * w)
523 {
524 cb_ret_t ret = MSG_NOT_HANDLED;
525
526 if (w != NULL && widget_get_state (w, WST_VISIBLE))
527 {
528 WGroup *g = w->owner;
529
530 if (g != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
531 ret = w->callback (w, NULL, MSG_DRAW, 0, NULL);
532 }
533
534 return ret;
535 }
536
537 /* --------------------------------------------------------------------------------------------- */
538 /**
539 * Replace widget in the dialog.
540 *
541 * @param old_w old widget that need to be replaced
542 * @param new_w new widget that will replace @old_w
543 */
544
545 void
widget_replace(Widget * old_w,Widget * new_w)546 widget_replace (Widget * old_w, Widget * new_w)
547 {
548 WGroup *g = old_w->owner;
549 gboolean should_focus = FALSE;
550 GList *holder;
551
552 if (g->widgets == NULL)
553 return;
554
555 if (g->current == NULL)
556 g->current = g->widgets;
557
558 /* locate widget position in the list */
559 if (old_w == g->current->data)
560 holder = g->current;
561 else
562 holder = g_list_find (g->widgets, old_w);
563
564 /* if old widget is focused, we should focus the new one... */
565 if (widget_get_state (old_w, WST_FOCUSED))
566 should_focus = TRUE;
567 /* ...but if new widget isn't selectable, we cannot focus it */
568 if (!widget_get_options (new_w, WOP_SELECTABLE))
569 should_focus = FALSE;
570
571 /* if new widget isn't selectable, select other widget before replace */
572 if (!should_focus)
573 {
574 GList *l;
575
576 for (l = group_get_widget_next_of (holder); widget_is_focusable (WIDGET (l->data));
577 l = group_get_widget_next_of (l))
578 ;
579
580 widget_select (WIDGET (l->data));
581 }
582
583 /* replace widget */
584 new_w->owner = g;
585 new_w->id = old_w->id;
586 holder->data = new_w;
587
588 send_message (old_w, NULL, MSG_DESTROY, 0, NULL);
589 send_message (new_w, NULL, MSG_INIT, 0, NULL);
590
591 if (should_focus)
592 widget_select (new_w);
593 else
594 widget_draw (new_w);
595 }
596
597 /* --------------------------------------------------------------------------------------------- */
598
599 gboolean
widget_is_focusable(const Widget * w)600 widget_is_focusable (const Widget * w)
601 {
602 return (widget_get_options (w, WOP_SELECTABLE) && widget_get_state (w, WST_VISIBLE) &&
603 !widget_get_state (w, WST_DISABLED));
604 }
605
606 /* --------------------------------------------------------------------------------------------- */
607 /**
608 * Select specified widget in it's owner.
609 *
610 * Note: this function (and widget_focus(), which it calls) is a no-op
611 * if the widget is already selected.
612 *
613 * @param w widget to be selected
614 */
615
616 void
widget_select(Widget * w)617 widget_select (Widget * w)
618 {
619 WGroup *g;
620
621 if (!widget_get_options (w, WOP_SELECTABLE))
622 return;
623
624 g = GROUP (w->owner);
625 if (g != NULL)
626 {
627 if (widget_get_options (w, WOP_TOP_SELECT))
628 {
629 GList *l;
630
631 l = widget_find (WIDGET (g), w);
632 widget_reorder (l, TRUE);
633 }
634
635 widget_focus (w);
636 }
637 }
638
639 /* --------------------------------------------------------------------------------------------- */
640 /**
641 * Set widget at bottom of widget list.
642 */
643
644 void
widget_set_bottom(Widget * w)645 widget_set_bottom (Widget * w)
646 {
647 widget_reorder (widget_find (WIDGET (w->owner), w), FALSE);
648 }
649
650 /* --------------------------------------------------------------------------------------------- */
651 /**
652 * Check whether two widgets are overlapped or not.
653 * @param a 1st widget
654 * @param b 2nd widget
655 *
656 * @return TRUE if widgets are overlapped, FALSE otherwise.
657 */
658
659 gboolean
widget_overlapped(const Widget * a,const Widget * b)660 widget_overlapped (const Widget * a, const Widget * b)
661 {
662 return !((b->x >= a->x + a->cols)
663 || (a->x >= b->x + b->cols) || (b->y >= a->y + a->lines) || (a->y >= b->y + b->lines));
664 }
665
666 /* --------------------------------------------------------------------------------------------- */
667 /**
668 * Look up key event of widget and translate it to command ID.
669 * @param w widget
670 * @param key key event
671 *
672 * @return command ID binded with @key.
673 */
674
675 long
widget_lookup_key(Widget * w,int key)676 widget_lookup_key (Widget * w, int key)
677 {
678 if (w->ext_mode)
679 {
680 w->ext_mode = FALSE;
681 return keybind_lookup_keymap_command (w->ext_keymap, key);
682 }
683
684 return keybind_lookup_keymap_command (w->keymap, key);
685 }
686
687 /* --------------------------------------------------------------------------------------------- */
688
689 /**
690 * Default widget callback to convert widget coordinates from local (relative to owner) to global
691 * (relative to screen).
692 *
693 * @param w widget
694 * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
695 */
696
697 void
widget_default_make_global(Widget * w,const WRect * delta)698 widget_default_make_global (Widget * w, const WRect * delta)
699 {
700 if (delta != NULL)
701 {
702 w->y += delta->y;
703 w->x += delta->x;
704 }
705 else if (w->owner != NULL)
706 {
707 w->y += WIDGET (w->owner)->y;
708 w->x += WIDGET (w->owner)->x;
709 }
710 }
711
712 /* --------------------------------------------------------------------------------------------- */
713
714 /**
715 * Default widget callback to convert widget coordinates from global (relative to screen) to local
716 * (relative to owner).
717 *
718 * @param w widget
719 * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
720 */
721
722 void
widget_default_make_local(Widget * w,const WRect * delta)723 widget_default_make_local (Widget * w, const WRect * delta)
724 {
725 if (delta != NULL)
726 {
727 w->y -= delta->y;
728 w->x -= delta->x;
729 }
730 else if (w->owner != NULL)
731 {
732 w->y -= WIDGET (w->owner)->y;
733 w->x -= WIDGET (w->owner)->x;
734 }
735 }
736
737 /* --------------------------------------------------------------------------------------------- */
738 /**
739 * Default callback function to find widget.
740 *
741 * @param w widget
742 * @param what widget to find
743 *
744 * @return holder of @what if widget is @what, NULL otherwise
745 */
746
747 GList *
widget_default_find(const Widget * w,const Widget * what)748 widget_default_find (const Widget * w, const Widget * what)
749 {
750 return (w != what
751 || w->owner == NULL) ? NULL : g_list_find (CONST_GROUP (w->owner)->widgets, what);
752 }
753
754 /* --------------------------------------------------------------------------------------------- */
755
756 /**
757 * Default callback function to find widget by widget type using widget callback.
758 *
759 * @param w widget
760 * @param cb widget callback
761 *
762 * @return @w if widget callback is @cb, NULL otherwise
763 */
764
765 Widget *
widget_default_find_by_type(const Widget * w,widget_cb_fn cb)766 widget_default_find_by_type (const Widget * w, widget_cb_fn cb)
767 {
768 return (w->callback == cb ? WIDGET (w) : NULL);
769 }
770
771 /* --------------------------------------------------------------------------------------------- */
772 /**
773 * Default callback function to find widget by widget ID.
774 *
775 * @param w widget
776 * @param id widget ID
777 *
778 * @return @w if widget id is equal to @id, NULL otherwise
779 */
780
781 Widget *
widget_default_find_by_id(const Widget * w,unsigned long id)782 widget_default_find_by_id (const Widget * w, unsigned long id)
783 {
784 return (w->id == id ? WIDGET (w) : NULL);
785 }
786
787 /* --------------------------------------------------------------------------------------------- */
788
789 /**
790 * Default callback function to modify state of widget.
791 *
792 * @param w widget
793 * @param state widget state flag to modify
794 * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
795 * Only one flag per call can be modified.
796 * @return MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
797 */
798
799 cb_ret_t
widget_default_set_state(Widget * w,widget_state_t state,gboolean enable)800 widget_default_set_state (Widget * w, widget_state_t state, gboolean enable)
801 {
802 gboolean ret = MSG_HANDLED;
803 Widget *owner = WIDGET (GROUP (w->owner));
804
805 if (enable)
806 w->state |= state;
807 else
808 w->state &= ~state;
809
810 if (enable)
811 {
812 /* exclusive bits */
813 if ((state & WST_CONSTRUCT) != 0)
814 w->state &= ~(WST_ACTIVE | WST_SUSPENDED | WST_CLOSED);
815 else if ((state & WST_ACTIVE) != 0)
816 w->state &= ~(WST_CONSTRUCT | WST_SUSPENDED | WST_CLOSED);
817 else if ((state & WST_SUSPENDED) != 0)
818 w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_CLOSED);
819 else if ((state & WST_CLOSED) != 0)
820 w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_SUSPENDED);
821 }
822
823 if (owner == NULL)
824 return MSG_NOT_HANDLED;
825
826 switch (state)
827 {
828 case WST_VISIBLE:
829 if (widget_get_state (owner, WST_ACTIVE))
830 {
831 /* redraw owner to show/hide widget */
832 widget_draw (owner);
833
834 if (!enable)
835 {
836 /* try select another widget if current one got hidden */
837 if (w == GROUP (owner)->current->data)
838 group_select_next_widget (GROUP (owner));
839
840 widget_update_cursor (owner); /* FIXME: unneeded? */
841 }
842 }
843 break;
844
845
846 case WST_DISABLED:
847 ret = send_message (w, NULL, enable ? MSG_DISABLE : MSG_ENABLE, 0, NULL);
848 if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
849 ret = widget_draw (w);
850 break;
851
852 case WST_FOCUSED:
853 {
854 widget_msg_t msg;
855
856 msg = enable ? MSG_FOCUS : MSG_UNFOCUS;
857 ret = send_message (w, NULL, msg, 0, NULL);
858 if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
859 {
860 widget_draw (w);
861 /* Notify owner that focus was moved from one widget to another */
862 send_message (owner, w, MSG_CHANGED_FOCUS, 0, NULL);
863 }
864 }
865 break;
866
867 default:
868 break;
869 }
870
871 return ret;
872 }
873
874 /* --------------------------------------------------------------------------------------------- */
875 /**
876 * Default callback function to destroy widget.
877 *
878 * @param w widget
879 */
880
881 void
widget_default_destroy(Widget * w)882 widget_default_destroy (Widget * w)
883 {
884 send_message (w, NULL, MSG_DESTROY, 0, NULL);
885 g_free (w);
886 }
887
888 /* --------------------------------------------------------------------------------------------- */
889 /* get mouse pointer location within widget */
890
891 Gpm_Event
mouse_get_local(const Gpm_Event * global,const Widget * w)892 mouse_get_local (const Gpm_Event * global, const Widget * w)
893 {
894 Gpm_Event local;
895
896 memset (&local, 0, sizeof (local));
897
898 local.buttons = global->buttons;
899 local.x = global->x - w->x;
900 local.y = global->y - w->y;
901 local.type = global->type;
902
903 return local;
904 }
905
906 /* --------------------------------------------------------------------------------------------- */
907
908 gboolean
mouse_global_in_widget(const Gpm_Event * event,const Widget * w)909 mouse_global_in_widget (const Gpm_Event * event, const Widget * w)
910 {
911 return (event->x > w->x) && (event->y > w->y) && (event->x <= w->x + w->cols)
912 && (event->y <= w->y + w->lines);
913 }
914
915 /* --------------------------------------------------------------------------------------------- */
916