Merge branch '4549_subshell_execl_argv0'
[midnight-commander.git] / lib / widget / group.c
blob3f37c1023b7d5b3a2992783c370934543113062e
1 /*
2 Widget group features module for the Midnight Commander
4 Copyright (C) 2020-2024
5 The Free Software Foundation, Inc.
7 Written by:
8 Andrew Borodin <aborodin@vmail.ru>, 2020-2022
10 This file is part of the Midnight Commander.
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.
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.
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/>.
26 /** \file group.c
27 * \brief Source: widget group features module
30 #include <config.h>
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <string.h>
36 #include "lib/global.h"
38 #include "lib/tty/key.h" /* ALT() */
40 #include "lib/widget.h"
42 /*** global variables ****************************************************************************/
44 /*** file scope macro definitions ****************************************************************/
46 /*** file scope type declarations ****************************************************************/
48 /* Control widget positions in a group */
49 typedef struct
51 int shift_x;
52 int scale_x;
53 int shift_y;
54 int scale_y;
55 } widget_shift_scale_t;
57 typedef struct
59 widget_state_t state;
60 gboolean enable;
61 } widget_state_info_t;
63 /*** forward declarations (file scope functions) *************************************************/
65 /*** file scope variables ************************************************************************/
67 /* --------------------------------------------------------------------------------------------- */
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
71 static void
72 group_widget_init (void *data, void *user_data)
74 (void) user_data;
76 send_message (WIDGET (data), NULL, MSG_INIT, 0, NULL);
79 /* --------------------------------------------------------------------------------------------- */
81 static GList *
82 group_get_next_or_prev_of (GList *list, gboolean next)
84 GList *l = NULL;
86 if (list != NULL)
88 WGroup *owner = WIDGET (list->data)->owner;
90 if (owner != NULL)
92 if (next)
94 l = g_list_next (list);
95 if (l == NULL)
96 l = owner->widgets;
98 else
100 l = g_list_previous (list);
101 if (l == NULL)
102 l = g_list_last (owner->widgets);
107 return l;
110 /* --------------------------------------------------------------------------------------------- */
112 static void
113 group_select_next_or_prev (WGroup *g, gboolean next)
115 if (g->widgets != NULL && g->current != NULL)
117 GList *l = g->current;
121 l = group_get_next_or_prev_of (l, next);
123 while (!widget_is_focusable (l->data) && l != g->current);
125 widget_select (l->data);
129 /* --------------------------------------------------------------------------------------------- */
131 static void
132 group_widget_set_state (gpointer data, gpointer user_data)
134 widget_state_info_t *state = (widget_state_info_t *) user_data;
136 widget_set_state (WIDGET (data), state->state, state->enable);
139 /* --------------------------------------------------------------------------------------------- */
141 * Send broadcast message to all widgets in the group that have specified options.
143 * @param g WGroup object
144 * @param msg message sent to widgets
145 * @param reverse if TRUE, send message in reverse order, FALSE -- in direct one.
146 * @param options if WOP_DEFAULT, the message is sent to all widgets. Else message is sent to widgets
147 * that have specified options.
150 static void
151 group_send_broadcast_msg_custom (WGroup *g, widget_msg_t msg, gboolean reverse,
152 widget_options_t options)
154 GList *p, *first;
156 if (g->widgets == NULL)
157 return;
159 if (g->current == NULL)
160 g->current = g->widgets;
162 p = group_get_next_or_prev_of (g->current, !reverse);
163 first = p;
167 Widget *w = WIDGET (p->data);
169 p = group_get_next_or_prev_of (p, !reverse);
171 if (options == WOP_DEFAULT || (options & w->options) != 0)
172 /* special case: don't draw invisible widgets */
173 if (msg != MSG_DRAW || widget_get_state (w, WST_VISIBLE))
174 send_message (w, NULL, msg, 0, NULL);
176 while (first != p);
179 /* --------------------------------------------------------------------------------------------- */
182 * Default group callback to convert group coordinates from local (relative to owner) to global
183 * (relative to screen).
185 * @param w widget
188 static void
189 group_default_make_global (Widget *w, const WRect *delta)
191 GList *iter;
193 if (delta != NULL)
195 /* change own coordinates */
196 widget_default_make_global (w, delta);
197 /* change child widget coordinates */
198 for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
199 WIDGET (iter->data)->make_global (WIDGET (iter->data), delta);
201 else if (w->owner != NULL)
203 WRect r = WIDGET (w->owner)->rect;
205 r.lines = 0;
206 r.cols = 0;
207 /* change own coordinates */
208 widget_default_make_global (w, &r);
209 /* change child widget coordinates */
210 for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
211 WIDGET (iter->data)->make_global (WIDGET (iter->data), &r);
215 /* --------------------------------------------------------------------------------------------- */
218 * Default group callback to convert group coordinates from global (relative to screen) to local
219 * (relative to owner).
221 * @param w widget
224 static void
225 group_default_make_local (Widget *w, const WRect *delta)
227 GList *iter;
229 if (delta != NULL)
231 /* change own coordinates */
232 widget_default_make_local (w, delta);
233 /* change child widget coordinates */
234 for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
235 WIDGET (iter->data)->make_local (WIDGET (iter->data), delta);
237 else if (w->owner != NULL)
239 WRect r = WIDGET (w->owner)->rect;
241 r.lines = 0;
242 r.cols = 0;
243 /* change own coordinates */
244 widget_default_make_local (w, &r);
245 /* change child widget coordinates */
246 for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
247 WIDGET (iter->data)->make_local (WIDGET (iter->data), &r);
251 /* --------------------------------------------------------------------------------------------- */
254 * Default group callback function to find widget in the group.
256 * @param w WGroup object
257 * @param what widget to find
259 * @return holder of @what if found, NULL otherwise
262 static GList *
263 group_default_find (const Widget *w, const Widget *what)
265 GList *w0;
267 w0 = widget_default_find (w, what);
268 if (w0 == NULL)
270 GList *iter;
272 for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
274 w0 = widget_find (WIDGET (iter->data), what);
275 if (w0 != NULL)
276 break;
280 return w0;
283 /* --------------------------------------------------------------------------------------------- */
286 * Default group callback function to find widget in the group using widget callback.
288 * @param w WGroup object
289 * @param cb widget callback
291 * @return widget object if found, NULL otherwise
294 static Widget *
295 group_default_find_by_type (const Widget *w, widget_cb_fn cb)
297 Widget *w0;
299 w0 = widget_default_find_by_type (w, cb);
300 if (w0 == NULL)
302 GList *iter;
304 for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
306 w0 = widget_find_by_type (WIDGET (iter->data), cb);
307 if (w0 != NULL)
308 break;
312 return w0;
315 /* --------------------------------------------------------------------------------------------- */
318 * Default group callback function to find widget by widget ID in the group.
320 * @param w WGroup object
321 * @param id widget ID
323 * @return widget object if widget with specified id is found in group, NULL otherwise
326 static Widget *
327 group_default_find_by_id (const Widget *w, unsigned long id)
329 Widget *w0;
331 w0 = widget_default_find_by_id (w, id);
332 if (w0 == NULL)
334 GList *iter;
336 for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
338 w0 = widget_find_by_id (WIDGET (iter->data), id);
339 if (w0 != NULL)
340 break;
344 return w0;
347 /* --------------------------------------------------------------------------------------------- */
349 * Update cursor position in the active widget of the group.
351 * @param g WGroup object
353 * @return MSG_HANDLED if cursor was updated in the specified group, MSG_NOT_HANDLED otherwise
356 static cb_ret_t
357 group_update_cursor (WGroup *g)
359 GList *p = g->current;
361 if (p != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
364 Widget *w = WIDGET (p->data);
366 /* Don't use widget_is_selectable() here.
367 If WOP_SELECTABLE option is not set, widget can handle mouse events.
368 For example, commandl line in file manager */
369 if (widget_get_options (w, WOP_WANT_CURSOR) && widget_get_state (w, WST_VISIBLE)
370 && !widget_get_state (w, WST_DISABLED) && widget_update_cursor (WIDGET (p->data)))
371 return MSG_HANDLED;
373 p = group_get_widget_next_of (p);
375 while (p != g->current);
377 return MSG_NOT_HANDLED;
380 /* --------------------------------------------------------------------------------------------- */
382 static void
383 group_widget_set_position (gpointer data, gpointer user_data)
385 /* there are, mainly, 2 generally possible situations:
386 * 1. control sticks to one side - it should be moved
387 * 2. control sticks to two sides of one direction - it should be sized
390 Widget *c = WIDGET (data);
391 const WRect *g = &CONST_WIDGET (c->owner)->rect;
392 const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
393 WRect r = c->rect;
395 if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
396 r.x = g->x + (g->cols - c->rect.cols) / 2;
397 else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
399 r.x += wss->shift_x;
400 r.cols += wss->scale_x;
402 else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
403 r.x += wss->shift_x;
404 else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
405 r.x += wss->shift_x + wss->scale_x;
407 if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
408 r.y = g->y + (g->lines - c->rect.lines) / 2;
409 else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
411 r.y += wss->shift_y;
412 r.lines += wss->scale_y;
414 else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
415 r.y += wss->shift_y;
416 else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
417 r.y += wss->shift_y + wss->scale_y;
419 send_message (c, NULL, MSG_RESIZE, 0, &r);
422 /* --------------------------------------------------------------------------------------------- */
424 static void
425 group_set_position (WGroup *g, const WRect *r)
427 WRect *w = &WIDGET (g)->rect;
428 widget_shift_scale_t wss;
429 /* save old positions, will be used to reposition childs */
430 WRect or = *w;
432 *w = *r;
434 /* dialog is empty */
435 if (g->widgets == NULL)
436 return;
438 if (g->current == NULL)
439 g->current = g->widgets;
441 /* values by which controls should be moved */
442 wss.shift_x = w->x - or.x;
443 wss.scale_x = w->cols - or.cols;
444 wss.shift_y = w->y - or.y;
445 wss.scale_y = w->lines - or.lines;
447 if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
448 g_list_foreach (g->widgets, group_widget_set_position, &wss);
451 /* --------------------------------------------------------------------------------------------- */
453 static void
454 group_default_resize (WGroup *g, WRect *r)
456 /* This is default resizing mechanism.
457 * The main idea of this code is to resize dialog according to flags
458 * (if any of flags require automatic resizing, like WPOS_CENTER,
459 * end after that reposition controls in dialog according to flags of widget)
462 Widget *w = WIDGET (g);
463 WRect r0;
465 r0 = r != NULL ? *r : w->rect;
466 widget_adjust_position (w->pos_flags, &r0);
467 group_set_position (g, &r0);
470 /* --------------------------------------------------------------------------------------------- */
472 static void
473 group_draw (WGroup *g)
475 Widget *wg = WIDGET (g);
477 /* draw all widgets in Z-order, from first to last */
478 if (widget_get_state (wg, WST_ACTIVE))
480 GList *p;
482 if (g->winch_pending)
484 g->winch_pending = FALSE;
485 send_message (wg, NULL, MSG_RESIZE, 0, NULL);
488 for (p = g->widgets; p != NULL; p = g_list_next (p))
489 widget_draw (WIDGET (p->data));
491 widget_update_cursor (wg);
495 /* --------------------------------------------------------------------------------------------- */
497 static cb_ret_t
498 group_handle_key (WGroup *g, int key)
500 cb_ret_t handled;
502 /* first try the hotkey */
503 handled = send_message (g, NULL, MSG_HOTKEY, key, NULL);
505 /* not used - then try widget_callback */
506 if (handled == MSG_NOT_HANDLED)
507 handled = send_message (g->current->data, NULL, MSG_KEY, key, NULL);
509 /* not used - try to use the unhandled case */
510 if (handled == MSG_NOT_HANDLED)
511 handled = send_message (g, g->current->data, MSG_UNHANDLED_KEY, key, NULL);
513 return handled;
516 /* --------------------------------------------------------------------------------------------- */
518 static cb_ret_t
519 group_handle_hotkey (WGroup *g, int key)
521 GList *current;
522 Widget *w;
523 cb_ret_t handled = MSG_NOT_HANDLED;
524 int c;
526 if (g->widgets == NULL)
527 return MSG_NOT_HANDLED;
529 if (g->current == NULL)
530 g->current = g->widgets;
532 w = WIDGET (g->current->data);
534 if (!widget_get_state (w, WST_VISIBLE) || widget_get_state (w, WST_DISABLED))
535 return MSG_NOT_HANDLED;
537 /* Explanation: we don't send letter hotkeys to other widgets
538 * if the currently selected widget is an input line */
539 if (widget_get_options (w, WOP_IS_INPUT))
541 /* skip ascii control characters, anything else can valid character in some encoding */
542 if (key >= 32 && key < 256)
543 return MSG_NOT_HANDLED;
546 /* If it's an alt key, send the message */
547 c = key & ~ALT (0);
548 if (key & ALT (0) && g_ascii_isalpha (c))
549 key = g_ascii_tolower (c);
551 if (widget_get_options (w, WOP_WANT_HOTKEY))
552 handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
554 /* If not used, send hotkey to other widgets */
555 if (handled == MSG_HANDLED)
556 return MSG_HANDLED;
558 current = group_get_widget_next_of (g->current);
560 /* send it to all widgets */
561 while (g->current != current && handled == MSG_NOT_HANDLED)
563 w = WIDGET (current->data);
565 if (widget_get_options (w, WOP_WANT_HOTKEY) && !widget_get_state (w, WST_DISABLED))
566 handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
568 if (handled == MSG_NOT_HANDLED)
569 current = group_get_widget_next_of (current);
572 if (handled == MSG_HANDLED)
574 w = WIDGET (current->data);
575 widget_select (w);
576 send_message (g, w, MSG_HOTKEY_HANDLED, 0, NULL);
579 return handled;
582 /* --------------------------------------------------------------------------------------------- */
583 /*** public functions ****************************************************************************/
584 /* --------------------------------------------------------------------------------------------- */
587 * Initialize group.
589 * @param g WGroup widget
590 * @param y1 y-coordinate of top-left corner
591 * @param x1 x-coordinate of top-left corner
592 * @param lines group height
593 * @param cols group width
594 * @param callback group callback
595 * @param mouse_callback group mouse handler
598 void
599 group_init (WGroup *g, const WRect *r, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
601 Widget *w = WIDGET (g);
603 widget_init (w, r, callback != NULL ? callback : group_default_callback, mouse_callback);
605 w->mouse_handler = group_handle_mouse_event;
607 w->make_global = group_default_make_global;
608 w->make_local = group_default_make_local;
610 w->find = group_default_find;
611 w->find_by_type = group_default_find_by_type;
612 w->find_by_id = group_default_find_by_id;
614 w->set_state = group_default_set_state;
616 g->mouse_status = MOU_UNHANDLED;
619 /* --------------------------------------------------------------------------------------------- */
621 cb_ret_t
622 group_default_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
624 WGroup *g = GROUP (w);
626 switch (msg)
628 case MSG_INIT:
629 g_list_foreach (g->widgets, group_widget_init, NULL);
630 return MSG_HANDLED;
632 case MSG_DRAW:
633 group_draw (g);
634 return MSG_HANDLED;
636 case MSG_KEY:
637 return group_handle_key (g, parm);
639 case MSG_HOTKEY:
640 return group_handle_hotkey (g, parm);
642 case MSG_CURSOR:
643 return group_update_cursor (g);
645 case MSG_RESIZE:
646 group_default_resize (g, RECT (data));
647 return MSG_HANDLED;
649 case MSG_DESTROY:
650 g_list_foreach (g->widgets, (GFunc) widget_destroy, NULL);
651 g_list_free (g->widgets);
652 g->widgets = NULL;
653 return MSG_HANDLED;
655 default:
656 return widget_default_callback (w, sender, msg, parm, data);
660 /* --------------------------------------------------------------------------------------------- */
663 * Change state of group.
665 * @param w group
666 * @param state widget state flag to modify
667 * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
668 * Only one flag per call can be modified.
669 * @return MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
671 cb_ret_t
672 group_default_set_state (Widget *w, widget_state_t state, gboolean enable)
674 gboolean ret = MSG_HANDLED;
675 WGroup *g = GROUP (w);
676 widget_state_info_t st = {
677 .state = state,
678 .enable = enable
681 ret = widget_default_set_state (w, state, enable);
683 if (state == WST_ACTIVE || state == WST_SUSPENDED || state == WST_CLOSED)
684 /* inform all child widgets */
685 g_list_foreach (g->widgets, group_widget_set_state, &st);
687 if ((w->state & WST_ACTIVE) != 0)
689 if ((w->state & WST_FOCUSED) != 0)
691 /* update current widget */
692 if (g->current != NULL)
693 widget_set_state (WIDGET (g->current->data), WST_FOCUSED, enable);
695 else
696 /* inform all child widgets */
697 g_list_foreach (g->widgets, group_widget_set_state, &st);
700 return ret;
703 /* --------------------------------------------------------------------------------------------- */
706 * Handling mouse events.
708 * @param g WGroup object
709 * @param event GPM mouse event
711 * @return result of mouse event handling
714 group_handle_mouse_event (Widget *w, Gpm_Event *event)
716 WGroup *g = GROUP (w);
718 if (g->widgets != NULL)
720 GList *p;
722 /* send the event to widgets in reverse Z-order */
723 p = g_list_last (g->widgets);
726 Widget *wp = WIDGET (p->data);
728 /* Don't use widget_is_selectable() here.
729 If WOP_SELECTABLE option is not set, widget can handle mouse events.
730 For example, commandl line in file manager */
731 if (widget_get_state (w, WST_VISIBLE) && !widget_get_state (wp, WST_DISABLED))
733 /* put global cursor position to the widget */
734 int ret;
736 ret = wp->mouse_handler (wp, event);
737 if (ret != MOU_UNHANDLED)
738 return ret;
741 p = g_list_previous (p);
743 while (p != NULL);
746 return MOU_UNHANDLED;
749 /* --------------------------------------------------------------------------------------------- */
752 * Insert widget to group before specified widget with specified positioning.
753 * Make the inserted widget current.
755 * @param g WGroup object
756 * @param w widget to be added
757 * @pos positioning flags
758 * @param before add @w before this widget
760 * @return widget ID
763 unsigned long
764 group_add_widget_autopos (WGroup *g, void *w, widget_pos_flags_t pos_flags, const void *before)
766 Widget *wg = WIDGET (g);
767 Widget *ww = WIDGET (w);
768 GList *new_current;
770 /* Don't accept NULL widget. This shouldn't happen */
771 assert (ww != NULL);
773 if ((pos_flags & WPOS_CENTER_HORZ) != 0)
774 ww->rect.x = (wg->rect.cols - ww->rect.cols) / 2;
776 if ((pos_flags & WPOS_CENTER_VERT) != 0)
777 ww->rect.y = (wg->rect.lines - ww->rect.lines) / 2;
779 ww->owner = g;
780 ww->pos_flags = pos_flags;
781 widget_make_global (ww);
783 if (g->widgets == NULL || before == NULL)
785 g->widgets = g_list_append (g->widgets, ww);
786 new_current = g_list_last (g->widgets);
788 else
790 GList *b;
792 b = g_list_find (g->widgets, before);
794 /* don't accept widget not from group. This shouldn't happen */
795 assert (b != NULL);
797 b = g_list_next (b);
798 g->widgets = g_list_insert_before (g->widgets, b, ww);
799 if (b != NULL)
800 new_current = g_list_previous (b);
801 else
802 new_current = g_list_last (g->widgets);
805 /* widget has been added at runtime */
806 if (widget_get_state (wg, WST_ACTIVE))
808 group_widget_init (ww, NULL);
809 widget_select (ww);
811 else
812 g->current = new_current;
814 return ww->id;
817 /* --------------------------------------------------------------------------------------------- */
820 * Remove widget from group.
822 * @param w Widget object
824 void
825 group_remove_widget (void *w)
827 Widget *ww = WIDGET (w);
828 WGroup *g;
829 GList *d;
831 /* Don't accept NULL widget. This shouldn't happen */
832 assert (w != NULL);
834 g = ww->owner;
836 d = g_list_find (g->widgets, ww);
837 if (d == g->current)
838 group_set_current_widget_next (g);
840 g->widgets = g_list_delete_link (g->widgets, d);
841 if (g->widgets == NULL)
842 g->current = NULL;
844 /* widget has been deleted at runtime */
845 if (widget_get_state (WIDGET (g), WST_ACTIVE))
847 group_draw (g);
848 group_select_current_widget (g);
851 widget_make_local (ww);
852 ww->owner = NULL;
855 /* --------------------------------------------------------------------------------------------- */
858 * Switch current widget to widget after current in group.
860 * @param g WGroup object
863 void
864 group_set_current_widget_next (WGroup *g)
866 g->current = group_get_next_or_prev_of (g->current, TRUE);
869 /* --------------------------------------------------------------------------------------------- */
871 * Switch current widget to widget before current in group.
873 * @param g WGroup object
876 void
877 group_set_current_widget_prev (WGroup *g)
879 g->current = group_get_next_or_prev_of (g->current, FALSE);
882 /* --------------------------------------------------------------------------------------------- */
884 * Get widget that is after specified widget in group.
886 * @param w widget holder
888 * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
891 GList *
892 group_get_widget_next_of (GList *w)
894 return group_get_next_or_prev_of (w, TRUE);
897 /* --------------------------------------------------------------------------------------------- */
899 * Get widget that is before specified widget in group.
901 * @param w widget holder
903 * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
906 GList *
907 group_get_widget_prev_of (GList *w)
909 return group_get_next_or_prev_of (w, FALSE);
912 /* --------------------------------------------------------------------------------------------- */
914 * Try to select next widget in the Z order.
916 * @param g WGroup object
919 void
920 group_select_next_widget (WGroup *g)
922 group_select_next_or_prev (g, TRUE);
925 /* --------------------------------------------------------------------------------------------- */
927 * Try to select previous widget in the Z order.
929 * @param g WGroup object
932 void
933 group_select_prev_widget (WGroup *g)
935 group_select_next_or_prev (g, FALSE);
938 /* --------------------------------------------------------------------------------------------- */
940 * Find the widget with the specified ID in the group and select it
942 * @param g WGroup object
943 * @param id widget ID
946 void
947 group_select_widget_by_id (const WGroup *g, unsigned long id)
949 Widget *w;
951 w = widget_find_by_id (CONST_WIDGET (g), id);
952 if (w != NULL)
953 widget_select (w);
956 /* --------------------------------------------------------------------------------------------- */
958 * Send broadcast message to all widgets in the group.
960 * @param g WGroup object
961 * @param msg message sent to widgets
964 void
965 group_send_broadcast_msg (WGroup *g, widget_msg_t msg)
967 group_send_broadcast_msg_custom (g, msg, FALSE, WOP_DEFAULT);
970 /* --------------------------------------------------------------------------------------------- */