root/lib/widget/group.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. group_widget_init
  2. group_get_next_or_prev_of
  3. group_select_next_or_prev
  4. group_widget_set_state
  5. group_send_broadcast_msg_custom
  6. group_default_make_global
  7. group_default_make_local
  8. group_default_find
  9. group_default_find_by_type
  10. group_default_find_by_id
  11. group_update_cursor
  12. group_widget_set_position
  13. group_set_position
  14. group_default_resize
  15. group_draw
  16. group_handle_key
  17. group_handle_hotkey
  18. group_init
  19. group_default_callback
  20. group_default_set_state
  21. group_handle_mouse_event
  22. group_add_widget_autopos
  23. group_remove_widget
  24. group_set_current_widget_next
  25. group_set_current_widget_prev
  26. group_get_widget_next_of
  27. group_get_widget_prev_of
  28. group_select_next_widget
  29. group_select_prev_widget
  30. group_select_widget_by_id
  31. group_send_broadcast_msg

   1 /*
   2    Widget group features module for the Midnight Commander
   3 
   4    Copyright (C) 2020-2024
   5    The Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2020-2022
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24  */
  25 
  26 /** \file group.c
  27  *  \brief Source: widget group features module
  28  */
  29 
  30 #include <config.h>
  31 
  32 #include <assert.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 
  36 #include "lib/global.h"
  37 
  38 #include "lib/tty/key.h"        /* ALT() */
  39 
  40 #include "lib/widget.h"
  41 
  42 /*** global variables ****************************************************************************/
  43 
  44 /*** file scope macro definitions ****************************************************************/
  45 
  46 /*** file scope type declarations ****************************************************************/
  47 
  48 /* Control widget positions in a group */
  49 typedef struct
  50 {
  51     int shift_x;
  52     int scale_x;
  53     int shift_y;
  54     int scale_y;
  55 } widget_shift_scale_t;
  56 
  57 typedef struct
  58 {
  59     widget_state_t state;
  60     gboolean enable;
  61 } widget_state_info_t;
  62 
  63 /*** forward declarations (file scope functions) *************************************************/
  64 
  65 /*** file scope variables ************************************************************************/
  66 
  67 /* --------------------------------------------------------------------------------------------- */
  68 /*** file scope functions ************************************************************************/
  69 /* --------------------------------------------------------------------------------------------- */
  70 
  71 static void
  72 group_widget_init (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  73 {
  74     (void) user_data;
  75 
  76     send_message (WIDGET (data), NULL, MSG_INIT, 0, NULL);
  77 }
  78 
  79 /* --------------------------------------------------------------------------------------------- */
  80 
  81 static GList *
  82 group_get_next_or_prev_of (GList *list, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
  83 {
  84     GList *l = NULL;
  85 
  86     if (list != NULL)
  87     {
  88         WGroup *owner = WIDGET (list->data)->owner;
  89 
  90         if (owner != NULL)
  91         {
  92             if (next)
  93             {
  94                 l = g_list_next (list);
  95                 if (l == NULL)
  96                     l = owner->widgets;
  97             }
  98             else
  99             {
 100                 l = g_list_previous (list);
 101                 if (l == NULL)
 102                     l = g_list_last (owner->widgets);
 103             }
 104         }
 105     }
 106 
 107     return l;
 108 }
 109 
 110 /* --------------------------------------------------------------------------------------------- */
 111 
 112 static void
 113 group_select_next_or_prev (WGroup *g, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
 114 {
 115     if (g->widgets != NULL && g->current != NULL)
 116     {
 117         GList *l = g->current;
 118 
 119         do
 120         {
 121             l = group_get_next_or_prev_of (l, next);
 122         }
 123         while (!widget_is_focusable (l->data) && l != g->current);
 124 
 125         widget_select (l->data);
 126     }
 127 }
 128 
 129 /* --------------------------------------------------------------------------------------------- */
 130 
 131 static void
 132 group_widget_set_state (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 133 {
 134     widget_state_info_t *state = (widget_state_info_t *) user_data;
 135 
 136     widget_set_state (WIDGET (data), state->state, state->enable);
 137 }
 138 
 139 /* --------------------------------------------------------------------------------------------- */
 140 /**
 141  * Send broadcast message to all widgets in the group that have specified options.
 142  *
 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.
 148  */
 149 
 150 static void
 151 group_send_broadcast_msg_custom (WGroup *g, widget_msg_t msg, gboolean reverse,
     /* [previous][next][first][last][top][bottom][index][help]  */
 152                                  widget_options_t options)
 153 {
 154     GList *p, *first;
 155 
 156     if (g->widgets == NULL)
 157         return;
 158 
 159     if (g->current == NULL)
 160         g->current = g->widgets;
 161 
 162     p = group_get_next_or_prev_of (g->current, !reverse);
 163     first = p;
 164 
 165     do
 166     {
 167         Widget *w = WIDGET (p->data);
 168 
 169         p = group_get_next_or_prev_of (p, !reverse);
 170 
 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);
 175     }
 176     while (first != p);
 177 }
 178 
 179 /* --------------------------------------------------------------------------------------------- */
 180 
 181 /**
 182   * Default group callback to convert group coordinates from local (relative to owner) to global
 183   * (relative to screen).
 184   *
 185   * @param w widget
 186   */
 187 
 188 static void
 189 group_default_make_global (Widget *w, const WRect *delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 190 {
 191     GList *iter;
 192 
 193     if (delta != NULL)
 194     {
 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);
 200     }
 201     else if (w->owner != NULL)
 202     {
 203         WRect r = WIDGET (w->owner)->rect;
 204 
 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);
 212     }
 213 }
 214 
 215 /* --------------------------------------------------------------------------------------------- */
 216 
 217 /**
 218   * Default group callback to convert group coordinates from global (relative to screen) to local
 219   * (relative to owner).
 220   *
 221   * @param w widget
 222   */
 223 
 224 static void
 225 group_default_make_local (Widget *w, const WRect *delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 226 {
 227     GList *iter;
 228 
 229     if (delta != NULL)
 230     {
 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);
 236     }
 237     else if (w->owner != NULL)
 238     {
 239         WRect r = WIDGET (w->owner)->rect;
 240 
 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);
 248     }
 249 }
 250 
 251 /* --------------------------------------------------------------------------------------------- */
 252 
 253 /**
 254  * Default group callback function to find widget in the group.
 255  *
 256  * @param w WGroup object
 257  * @param what widget to find
 258  *
 259  * @return holder of @what if found, NULL otherwise
 260  */
 261 
 262 static GList *
 263 group_default_find (const Widget *w, const Widget *what)
     /* [previous][next][first][last][top][bottom][index][help]  */
 264 {
 265     GList *w0;
 266 
 267     w0 = widget_default_find (w, what);
 268     if (w0 == NULL)
 269     {
 270         GList *iter;
 271 
 272         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 273         {
 274             w0 = widget_find (WIDGET (iter->data), what);
 275             if (w0 != NULL)
 276                 break;
 277         }
 278     }
 279 
 280     return w0;
 281 }
 282 
 283 /* --------------------------------------------------------------------------------------------- */
 284 
 285 /**
 286  * Default group callback function to find widget in the group using widget callback.
 287  *
 288  * @param w WGroup object
 289  * @param cb widget callback
 290  *
 291  * @return widget object if found, NULL otherwise
 292  */
 293 
 294 static Widget *
 295 group_default_find_by_type (const Widget *w, widget_cb_fn cb)
     /* [previous][next][first][last][top][bottom][index][help]  */
 296 {
 297     Widget *w0;
 298 
 299     w0 = widget_default_find_by_type (w, cb);
 300     if (w0 == NULL)
 301     {
 302         GList *iter;
 303 
 304         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 305         {
 306             w0 = widget_find_by_type (WIDGET (iter->data), cb);
 307             if (w0 != NULL)
 308                 break;
 309         }
 310     }
 311 
 312     return w0;
 313 }
 314 
 315 /* --------------------------------------------------------------------------------------------- */
 316 
 317 /**
 318  * Default group callback function to find widget by widget ID in the group.
 319  *
 320  * @param w WGroup object
 321  * @param id widget ID
 322  *
 323  * @return widget object if widget with specified id is found in group, NULL otherwise
 324  */
 325 
 326 static Widget *
 327 group_default_find_by_id (const Widget *w, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     Widget *w0;
 330 
 331     w0 = widget_default_find_by_id (w, id);
 332     if (w0 == NULL)
 333     {
 334         GList *iter;
 335 
 336         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 337         {
 338             w0 = widget_find_by_id (WIDGET (iter->data), id);
 339             if (w0 != NULL)
 340                 break;
 341         }
 342     }
 343 
 344     return w0;
 345 }
 346 
 347 /* --------------------------------------------------------------------------------------------- */
 348 /**
 349  * Update cursor position in the active widget of the group.
 350  *
 351  * @param g WGroup object
 352  *
 353  * @return MSG_HANDLED if cursor was updated in the specified group, MSG_NOT_HANDLED otherwise
 354  */
 355 
 356 static cb_ret_t
 357 group_update_cursor (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 358 {
 359     GList *p = g->current;
 360 
 361     if (p != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
 362         do
 363         {
 364             Widget *w = WIDGET (p->data);
 365 
 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;
 372 
 373             p = group_get_widget_next_of (p);
 374         }
 375         while (p != g->current);
 376 
 377     return MSG_NOT_HANDLED;
 378 }
 379 
 380 /* --------------------------------------------------------------------------------------------- */
 381 
 382 static void
 383 group_widget_set_position (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 384 {
 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
 388      */
 389 
 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;
 394 
 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)
 398     {
 399         r.x += wss->shift_x;
 400         r.cols += wss->scale_x;
 401     }
 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;
 406 
 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)
 410     {
 411         r.y += wss->shift_y;
 412         r.lines += wss->scale_y;
 413     }
 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;
 418 
 419     send_message (c, NULL, MSG_RESIZE, 0, &r);
 420 }
 421 
 422 /* --------------------------------------------------------------------------------------------- */
 423 
 424 static void
 425 group_set_position (WGroup *g, const WRect *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 426 {
 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;
 431 
 432     *w = *r;
 433 
 434     /* dialog is empty */
 435     if (g->widgets == NULL)
 436         return;
 437 
 438     if (g->current == NULL)
 439         g->current = g->widgets;
 440 
 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;
 446 
 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);
 449 }
 450 
 451 /* --------------------------------------------------------------------------------------------- */
 452 
 453 static void
 454 group_default_resize (WGroup *g, WRect *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 455 {
 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)
 460      */
 461 
 462     Widget *w = WIDGET (g);
 463     WRect r0;
 464 
 465     r0 = r != NULL ? *r : w->rect;
 466     widget_adjust_position (w->pos_flags, &r0);
 467     group_set_position (g, &r0);
 468 }
 469 
 470 /* --------------------------------------------------------------------------------------------- */
 471 
 472 static void
 473 group_draw (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 474 {
 475     Widget *wg = WIDGET (g);
 476 
 477     /* draw all widgets in Z-order, from first to last */
 478     if (widget_get_state (wg, WST_ACTIVE))
 479     {
 480         GList *p;
 481 
 482         if (g->winch_pending)
 483         {
 484             g->winch_pending = FALSE;
 485             send_message (wg, NULL, MSG_RESIZE, 0, NULL);
 486         }
 487 
 488         for (p = g->widgets; p != NULL; p = g_list_next (p))
 489             widget_draw (WIDGET (p->data));
 490 
 491         widget_update_cursor (wg);
 492     }
 493 }
 494 
 495 /* --------------------------------------------------------------------------------------------- */
 496 
 497 static cb_ret_t
 498 group_handle_key (WGroup *g, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 499 {
 500     cb_ret_t handled;
 501 
 502     /* first try the hotkey */
 503     handled = send_message (g, NULL, MSG_HOTKEY, key, NULL);
 504 
 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);
 508 
 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);
 512 
 513     return handled;
 514 }
 515 
 516 /* --------------------------------------------------------------------------------------------- */
 517 
 518 static cb_ret_t
 519 group_handle_hotkey (WGroup *g, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 520 {
 521     GList *current;
 522     Widget *w;
 523     cb_ret_t handled = MSG_NOT_HANDLED;
 524     int c;
 525 
 526     if (g->widgets == NULL)
 527         return MSG_NOT_HANDLED;
 528 
 529     if (g->current == NULL)
 530         g->current = g->widgets;
 531 
 532     w = WIDGET (g->current->data);
 533 
 534     if (!widget_get_state (w, WST_VISIBLE) || widget_get_state (w, WST_DISABLED))
 535         return MSG_NOT_HANDLED;
 536 
 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))
 540     {
 541         /* skip ascii control characters, anything else can valid character in some encoding */
 542         if (key >= 32 && key < 256)
 543             return MSG_NOT_HANDLED;
 544     }
 545 
 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);
 550 
 551     if (widget_get_options (w, WOP_WANT_HOTKEY))
 552         handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
 553 
 554     /* If not used, send hotkey to other widgets */
 555     if (handled == MSG_HANDLED)
 556         return MSG_HANDLED;
 557 
 558     current = group_get_widget_next_of (g->current);
 559 
 560     /* send it to all widgets */
 561     while (g->current != current && handled == MSG_NOT_HANDLED)
 562     {
 563         w = WIDGET (current->data);
 564 
 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);
 567 
 568         if (handled == MSG_NOT_HANDLED)
 569             current = group_get_widget_next_of (current);
 570     }
 571 
 572     if (handled == MSG_HANDLED)
 573     {
 574         w = WIDGET (current->data);
 575         widget_select (w);
 576         send_message (g, w, MSG_HOTKEY_HANDLED, 0, NULL);
 577     }
 578 
 579     return handled;
 580 }
 581 
 582 /* --------------------------------------------------------------------------------------------- */
 583 /*** public functions ****************************************************************************/
 584 /* --------------------------------------------------------------------------------------------- */
 585 
 586 /**
 587  * Initialize group.
 588  *
 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
 596  */
 597 
 598 void
 599 group_init (WGroup *g, const WRect *r, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 600 {
 601     Widget *w = WIDGET (g);
 602 
 603     widget_init (w, r, callback != NULL ? callback : group_default_callback, mouse_callback);
 604 
 605     w->mouse_handler = group_handle_mouse_event;
 606 
 607     w->make_global = group_default_make_global;
 608     w->make_local = group_default_make_local;
 609 
 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;
 613 
 614     w->set_state = group_default_set_state;
 615 
 616     g->mouse_status = MOU_UNHANDLED;
 617 }
 618 
 619 /* --------------------------------------------------------------------------------------------- */
 620 
 621 cb_ret_t
 622 group_default_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 623 {
 624     WGroup *g = GROUP (w);
 625 
 626     switch (msg)
 627     {
 628     case MSG_INIT:
 629         g_list_foreach (g->widgets, group_widget_init, NULL);
 630         return MSG_HANDLED;
 631 
 632     case MSG_DRAW:
 633         group_draw (g);
 634         return MSG_HANDLED;
 635 
 636     case MSG_KEY:
 637         return group_handle_key (g, parm);
 638 
 639     case MSG_HOTKEY:
 640         return group_handle_hotkey (g, parm);
 641 
 642     case MSG_CURSOR:
 643         return group_update_cursor (g);
 644 
 645     case MSG_RESIZE:
 646         group_default_resize (g, RECT (data));
 647         return MSG_HANDLED;
 648 
 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;
 654 
 655     default:
 656         return widget_default_callback (w, sender, msg, parm, data);
 657     }
 658 }
 659 
 660 /* --------------------------------------------------------------------------------------------- */
 661 
 662 /**
 663  * Change state of group.
 664  *
 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.
 670  */
 671 cb_ret_t
 672 group_default_set_state (Widget *w, widget_state_t state, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 673 {
 674     gboolean ret = MSG_HANDLED;
 675     WGroup *g = GROUP (w);
 676     widget_state_info_t st = {
 677         .state = state,
 678         .enable = enable
 679     };
 680 
 681     ret = widget_default_set_state (w, state, enable);
 682 
 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);
 686 
 687     if ((w->state & WST_ACTIVE) != 0)
 688     {
 689         if ((w->state & WST_FOCUSED) != 0)
 690         {
 691             /* update current widget */
 692             if (g->current != NULL)
 693                 widget_set_state (WIDGET (g->current->data), WST_FOCUSED, enable);
 694         }
 695         else
 696             /* inform all child widgets */
 697             g_list_foreach (g->widgets, group_widget_set_state, &st);
 698     }
 699 
 700     return ret;
 701 }
 702 
 703 /* --------------------------------------------------------------------------------------------- */
 704 
 705 /**
 706  * Handling mouse events.
 707  *
 708  * @param g WGroup object
 709  * @param event GPM mouse event
 710  *
 711  * @return result of mouse event handling
 712  */
 713 int
 714 group_handle_mouse_event (Widget *w, Gpm_Event *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 715 {
 716     WGroup *g = GROUP (w);
 717 
 718     if (g->widgets != NULL)
 719     {
 720         GList *p;
 721 
 722         /* send the event to widgets in reverse Z-order */
 723         p = g_list_last (g->widgets);
 724         do
 725         {
 726             Widget *wp = WIDGET (p->data);
 727 
 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))
 732             {
 733                 /* put global cursor position to the widget */
 734                 int ret;
 735 
 736                 ret = wp->mouse_handler (wp, event);
 737                 if (ret != MOU_UNHANDLED)
 738                     return ret;
 739             }
 740 
 741             p = g_list_previous (p);
 742         }
 743         while (p != NULL);
 744     }
 745 
 746     return MOU_UNHANDLED;
 747 }
 748 
 749 /* --------------------------------------------------------------------------------------------- */
 750 
 751 /**
 752  * Insert widget to group before specified widget with specified positioning.
 753  * Make the inserted widget current.
 754  *
 755  * @param g WGroup object
 756  * @param w widget to be added
 757  * @pos positioning flags
 758  * @param before add @w before this widget
 759  *
 760  * @return widget ID
 761  */
 762 
 763 unsigned long
 764 group_add_widget_autopos (WGroup *g, void *w, widget_pos_flags_t pos_flags, const void *before)
     /* [previous][next][first][last][top][bottom][index][help]  */
 765 {
 766     Widget *wg = WIDGET (g);
 767     Widget *ww = WIDGET (w);
 768     GList *new_current;
 769 
 770     /* Don't accept NULL widget. This shouldn't happen */
 771     assert (ww != NULL);
 772 
 773     if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 774         ww->rect.x = (wg->rect.cols - ww->rect.cols) / 2;
 775 
 776     if ((pos_flags & WPOS_CENTER_VERT) != 0)
 777         ww->rect.y = (wg->rect.lines - ww->rect.lines) / 2;
 778 
 779     ww->owner = g;
 780     ww->pos_flags = pos_flags;
 781     widget_make_global (ww);
 782 
 783     if (g->widgets == NULL || before == NULL)
 784     {
 785         g->widgets = g_list_append (g->widgets, ww);
 786         new_current = g_list_last (g->widgets);
 787     }
 788     else
 789     {
 790         GList *b;
 791 
 792         b = g_list_find (g->widgets, before);
 793 
 794         /* don't accept widget not from group. This shouldn't happen */
 795         assert (b != NULL);
 796 
 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);
 803     }
 804 
 805     /* widget has been added at runtime */
 806     if (widget_get_state (wg, WST_ACTIVE))
 807     {
 808         group_widget_init (ww, NULL);
 809         widget_select (ww);
 810     }
 811     else
 812         g->current = new_current;
 813 
 814     return ww->id;
 815 }
 816 
 817 /* --------------------------------------------------------------------------------------------- */
 818 
 819 /**
 820  * Remove widget from group.
 821  *
 822  * @param w Widget object
 823  */
 824 void
 825 group_remove_widget (void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 826 {
 827     Widget *ww = WIDGET (w);
 828     WGroup *g;
 829     GList *d;
 830 
 831     /* Don't accept NULL widget. This shouldn't happen */
 832     assert (w != NULL);
 833 
 834     g = ww->owner;
 835 
 836     d = g_list_find (g->widgets, ww);
 837     if (d == g->current)
 838         group_set_current_widget_next (g);
 839 
 840     g->widgets = g_list_delete_link (g->widgets, d);
 841     if (g->widgets == NULL)
 842         g->current = NULL;
 843 
 844     /* widget has been deleted at runtime */
 845     if (widget_get_state (WIDGET (g), WST_ACTIVE))
 846     {
 847         group_draw (g);
 848         group_select_current_widget (g);
 849     }
 850 
 851     widget_make_local (ww);
 852     ww->owner = NULL;
 853 }
 854 
 855 /* --------------------------------------------------------------------------------------------- */
 856 
 857 /**
 858  * Switch current widget to widget after current in group.
 859  *
 860  * @param g WGroup object
 861  */
 862 
 863 void
 864 group_set_current_widget_next (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 865 {
 866     g->current = group_get_next_or_prev_of (g->current, TRUE);
 867 }
 868 
 869 /* --------------------------------------------------------------------------------------------- */
 870 /**
 871  * Switch current widget to widget before current in group.
 872  *
 873  * @param g WGroup object
 874  */
 875 
 876 void
 877 group_set_current_widget_prev (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 878 {
 879     g->current = group_get_next_or_prev_of (g->current, FALSE);
 880 }
 881 
 882 /* --------------------------------------------------------------------------------------------- */
 883 /**
 884  * Get widget that is after specified widget in group.
 885  *
 886  * @param w widget holder
 887  *
 888  * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
 889  */
 890 
 891 GList *
 892 group_get_widget_next_of (GList *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 893 {
 894     return group_get_next_or_prev_of (w, TRUE);
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 /**
 899  * Get widget that is before specified widget in group.
 900  *
 901  * @param w widget holder
 902  *
 903  * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
 904  */
 905 
 906 GList *
 907 group_get_widget_prev_of (GList *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 908 {
 909     return group_get_next_or_prev_of (w, FALSE);
 910 }
 911 
 912 /* --------------------------------------------------------------------------------------------- */
 913 /**
 914  * Try to select next widget in the Z order.
 915  *
 916  * @param g WGroup object
 917  */
 918 
 919 void
 920 group_select_next_widget (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 921 {
 922     group_select_next_or_prev (g, TRUE);
 923 }
 924 
 925 /* --------------------------------------------------------------------------------------------- */
 926 /**
 927  * Try to select previous widget in the Z order.
 928  *
 929  * @param g WGroup object
 930  */
 931 
 932 void
 933 group_select_prev_widget (WGroup *g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 934 {
 935     group_select_next_or_prev (g, FALSE);
 936 }
 937 
 938 /* --------------------------------------------------------------------------------------------- */
 939 /**
 940  * Find the widget with the specified ID in the group and select it
 941  *
 942  * @param g WGroup object
 943  * @param id widget ID
 944  */
 945 
 946 void
 947 group_select_widget_by_id (const WGroup *g, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 948 {
 949     Widget *w;
 950 
 951     w = widget_find_by_id (CONST_WIDGET (g), id);
 952     if (w != NULL)
 953         widget_select (w);
 954 }
 955 
 956 /* --------------------------------------------------------------------------------------------- */
 957 /**
 958  * Send broadcast message to all widgets in the group.
 959  *
 960  * @param g WGroup object
 961  * @param msg message sent to widgets
 962  */
 963 
 964 void
 965 group_send_broadcast_msg (WGroup *g, widget_msg_t msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 966 {
 967     group_send_broadcast_msg_custom (g, msg, FALSE, WOP_DEFAULT);
 968 }
 969 
 970 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */