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