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-2022
   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 /*** file scope variables ************************************************************************/
  64 
  65 /* --------------------------------------------------------------------------------------------- */
  66 /*** file scope functions ************************************************************************/
  67 /* --------------------------------------------------------------------------------------------- */
  68 
  69 static void
  70 group_widget_init (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  71 {
  72     (void) user_data;
  73 
  74     send_message (WIDGET (data), NULL, MSG_INIT, 0, NULL);
  75 }
  76 
  77 /* --------------------------------------------------------------------------------------------- */
  78 
  79 static GList *
  80 group_get_next_or_prev_of (GList * list, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
  81 {
  82     GList *l = NULL;
  83 
  84     if (list != NULL)
  85     {
  86         WGroup *owner = WIDGET (list->data)->owner;
  87 
  88         if (owner != NULL)
  89         {
  90             if (next)
  91             {
  92                 l = g_list_next (list);
  93                 if (l == NULL)
  94                     l = owner->widgets;
  95             }
  96             else
  97             {
  98                 l = g_list_previous (list);
  99                 if (l == NULL)
 100                     l = g_list_last (owner->widgets);
 101             }
 102         }
 103     }
 104 
 105     return l;
 106 }
 107 
 108 /* --------------------------------------------------------------------------------------------- */
 109 
 110 static void
 111 group_select_next_or_prev (WGroup * g, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
 112 {
 113     if (g->widgets != NULL && g->current != NULL)
 114     {
 115         GList *l = g->current;
 116 
 117         do
 118         {
 119             l = group_get_next_or_prev_of (l, next);
 120         }
 121         while (!widget_is_focusable (l->data) && l != g->current);
 122 
 123         widget_select (l->data);
 124     }
 125 }
 126 
 127 /* --------------------------------------------------------------------------------------------- */
 128 
 129 static void
 130 group_widget_set_state (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 131 {
 132     widget_state_info_t *state = (widget_state_info_t *) user_data;
 133 
 134     widget_set_state (WIDGET (data), state->state, state->enable);
 135 }
 136 
 137 /* --------------------------------------------------------------------------------------------- */
 138 /**
 139  * Send broadcast message to all widgets in the group that have specified options.
 140  *
 141  * @param g WGroup object
 142  * @param msg message sent to widgets
 143  * @param reverse if TRUE, send message in reverse order, FALSE -- in direct one.
 144  * @param options if WOP_DEFAULT, the message is sent to all widgets. Else message is sent to widgets
 145  *                that have specified options.
 146  */
 147 
 148 static void
 149 group_send_broadcast_msg_custom (WGroup * g, widget_msg_t msg, gboolean reverse,
     /* [previous][next][first][last][top][bottom][index][help]  */
 150                                  widget_options_t options)
 151 {
 152     GList *p, *first;
 153 
 154     if (g->widgets == NULL)
 155         return;
 156 
 157     if (g->current == NULL)
 158         g->current = g->widgets;
 159 
 160     p = group_get_next_or_prev_of (g->current, !reverse);
 161     first = p;
 162 
 163     do
 164     {
 165         Widget *w = WIDGET (p->data);
 166 
 167         p = group_get_next_or_prev_of (p, !reverse);
 168 
 169         if (options == WOP_DEFAULT || (options & w->options) != 0)
 170             /* special case: don't draw invisible widgets */
 171             if (msg != MSG_DRAW || widget_get_state (w, WST_VISIBLE))
 172                 send_message (w, NULL, msg, 0, NULL);
 173     }
 174     while (first != p);
 175 }
 176 
 177 /* --------------------------------------------------------------------------------------------- */
 178 
 179 /**
 180   * Default group callback to convert group coordinates from local (relative to owner) to global
 181   * (relative to screen).
 182   *
 183   * @param w widget
 184   */
 185 
 186 static void
 187 group_default_make_global (Widget * w, const WRect * delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     GList *iter;
 190 
 191     if (delta != NULL)
 192     {
 193         /* change own coordinates */
 194         widget_default_make_global (w, delta);
 195         /* change child widget coordinates */
 196         for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 197             WIDGET (iter->data)->make_global (WIDGET (iter->data), delta);
 198     }
 199     else if (w->owner != NULL)
 200     {
 201         WRect r = WIDGET (w->owner)->rect;
 202 
 203         r.lines = 0;
 204         r.cols = 0;
 205         /* change own coordinates */
 206         widget_default_make_global (w, &r);
 207         /* change child widget coordinates */
 208         for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 209             WIDGET (iter->data)->make_global (WIDGET (iter->data), &r);
 210     }
 211 }
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 
 215 /**
 216   * Default group callback to convert group coordinates from global (relative to screen) to local
 217   * (relative to owner).
 218   *
 219   * @param w widget
 220   */
 221 
 222 static void
 223 group_default_make_local (Widget * w, const WRect * delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 224 {
 225     GList *iter;
 226 
 227     if (delta != NULL)
 228     {
 229         /* change own coordinates */
 230         widget_default_make_local (w, delta);
 231         /* change child widget coordinates */
 232         for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 233             WIDGET (iter->data)->make_local (WIDGET (iter->data), delta);
 234     }
 235     else if (w->owner != NULL)
 236     {
 237         WRect r = WIDGET (w->owner)->rect;
 238 
 239         r.lines = 0;
 240         r.cols = 0;
 241         /* change own coordinates */
 242         widget_default_make_local (w, &r);
 243         /* change child widget coordinates */
 244         for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 245             WIDGET (iter->data)->make_local (WIDGET (iter->data), &r);
 246     }
 247 }
 248 
 249 /* --------------------------------------------------------------------------------------------- */
 250 
 251 /**
 252  * Default group callback function to find widget in the group.
 253  *
 254  * @param w WGroup object
 255  * @param what widget to find
 256  *
 257  * @return holder of @what if found, NULL otherwise
 258  */
 259 
 260 static GList *
 261 group_default_find (const Widget * w, const Widget * what)
     /* [previous][next][first][last][top][bottom][index][help]  */
 262 {
 263     GList *w0;
 264 
 265     w0 = widget_default_find (w, what);
 266     if (w0 == NULL)
 267     {
 268         GList *iter;
 269 
 270         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 271         {
 272             w0 = widget_find (WIDGET (iter->data), what);
 273             if (w0 != NULL)
 274                 break;
 275         }
 276     }
 277 
 278     return w0;
 279 }
 280 
 281 /* --------------------------------------------------------------------------------------------- */
 282 
 283 /**
 284  * Default group callback function to find widget in the group using widget callback.
 285  *
 286  * @param w WGroup object
 287  * @param cb widget callback
 288  *
 289  * @return widget object if found, NULL otherwise
 290  */
 291 
 292 static Widget *
 293 group_default_find_by_type (const Widget * w, widget_cb_fn cb)
     /* [previous][next][first][last][top][bottom][index][help]  */
 294 {
 295     Widget *w0;
 296 
 297     w0 = widget_default_find_by_type (w, cb);
 298     if (w0 == NULL)
 299     {
 300         GList *iter;
 301 
 302         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 303         {
 304             w0 = widget_find_by_type (WIDGET (iter->data), cb);
 305             if (w0 != NULL)
 306                 break;
 307         }
 308     }
 309 
 310     return w0;
 311 }
 312 
 313 /* --------------------------------------------------------------------------------------------- */
 314 
 315 /**
 316  * Default group callback function to find widget by widget ID in the group.
 317  *
 318  * @param w WGroup object
 319  * @param id widget ID
 320  *
 321  * @return widget object if widget with specified id is found in group, NULL otherwise
 322  */
 323 
 324 static Widget *
 325 group_default_find_by_id (const Widget * w, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 326 {
 327     Widget *w0;
 328 
 329     w0 = widget_default_find_by_id (w, id);
 330     if (w0 == NULL)
 331     {
 332         GList *iter;
 333 
 334         for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
 335         {
 336             w0 = widget_find_by_id (WIDGET (iter->data), id);
 337             if (w0 != NULL)
 338                 break;
 339         }
 340     }
 341 
 342     return w0;
 343 }
 344 
 345 /* --------------------------------------------------------------------------------------------- */
 346 /**
 347  * Update cursor position in the active widget of the group.
 348  *
 349  * @param g WGroup object
 350  *
 351  * @return MSG_HANDLED if cursor was updated in the specified group, MSG_NOT_HANDLED otherwise
 352  */
 353 
 354 static cb_ret_t
 355 group_update_cursor (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 356 {
 357     GList *p = g->current;
 358 
 359     if (p != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
 360         do
 361         {
 362             Widget *w = WIDGET (p->data);
 363 
 364             /* Don't use widget_is_selectable() here.
 365                If WOP_SELECTABLE option is not set, widget can handle mouse events.
 366                For example, commandl line in file manager */
 367             if (widget_get_options (w, WOP_WANT_CURSOR) && widget_get_state (w, WST_VISIBLE)
 368                 && !widget_get_state (w, WST_DISABLED) && widget_update_cursor (WIDGET (p->data)))
 369                 return MSG_HANDLED;
 370 
 371             p = group_get_widget_next_of (p);
 372         }
 373         while (p != g->current);
 374 
 375     return MSG_NOT_HANDLED;
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static void
 381 group_widget_set_position (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     /* there are, mainly, 2 generally possible situations:
 384      * 1. control sticks to one side - it should be moved
 385      * 2. control sticks to two sides of one direction - it should be sized
 386      */
 387 
 388     Widget *c = WIDGET (data);
 389     const WRect *g = &CONST_WIDGET (c->owner)->rect;
 390     const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
 391     WRect r = c->rect;
 392 
 393     if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
 394         r.x = g->x + (g->cols - c->rect.cols) / 2;
 395     else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
 396     {
 397         r.x += wss->shift_x;
 398         r.cols += wss->scale_x;
 399     }
 400     else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
 401         r.x += wss->shift_x;
 402     else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
 403         r.x += wss->shift_x + wss->scale_x;
 404 
 405     if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
 406         r.y = g->y + (g->lines - c->rect.lines) / 2;
 407     else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
 408     {
 409         r.y += wss->shift_y;
 410         r.lines += wss->scale_y;
 411     }
 412     else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
 413         r.y += wss->shift_y;
 414     else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
 415         r.y += wss->shift_y + wss->scale_y;
 416 
 417     send_message (c, NULL, MSG_RESIZE, 0, &r);
 418 }
 419 
 420 /* --------------------------------------------------------------------------------------------- */
 421 
 422 static void
 423 group_set_position (WGroup * g, const WRect * r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 424 {
 425     WRect *w = &WIDGET (g)->rect;
 426     widget_shift_scale_t wss;
 427     /* save old positions, will be used to reposition childs */
 428     WRect or = *w;
 429 
 430     *w = *r;
 431 
 432     /* dialog is empty */
 433     if (g->widgets == NULL)
 434         return;
 435 
 436     if (g->current == NULL)
 437         g->current = g->widgets;
 438 
 439     /* values by which controls should be moved */
 440     wss.shift_x = w->x - or.x;
 441     wss.scale_x = w->cols - or.cols;
 442     wss.shift_y = w->y - or.y;
 443     wss.scale_y = w->lines - or.lines;
 444 
 445     if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
 446         g_list_foreach (g->widgets, group_widget_set_position, &wss);
 447 }
 448 
 449 /* --------------------------------------------------------------------------------------------- */
 450 
 451 static void
 452 group_default_resize (WGroup * g, WRect * r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 453 {
 454     /* This is default resizing mechanism.
 455      * The main idea of this code is to resize dialog according to flags
 456      * (if any of flags require automatic resizing, like WPOS_CENTER,
 457      * end after that reposition controls in dialog according to flags of widget)
 458      */
 459 
 460     Widget *w = WIDGET (g);
 461     WRect r0;
 462 
 463     r0 = r != NULL ? *r : w->rect;
 464     widget_adjust_position (w->pos_flags, &r0);
 465     group_set_position (g, &r0);
 466 }
 467 
 468 /* --------------------------------------------------------------------------------------------- */
 469 
 470 static void
 471 group_draw (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 472 {
 473     Widget *wg = WIDGET (g);
 474 
 475     /* draw all widgets in Z-order, from first to last */
 476     if (widget_get_state (wg, WST_ACTIVE))
 477     {
 478         GList *p;
 479 
 480         if (g->winch_pending)
 481         {
 482             g->winch_pending = FALSE;
 483             send_message (wg, NULL, MSG_RESIZE, 0, NULL);
 484         }
 485 
 486         for (p = g->widgets; p != NULL; p = g_list_next (p))
 487             widget_draw (WIDGET (p->data));
 488 
 489         widget_update_cursor (wg);
 490     }
 491 }
 492 
 493 /* --------------------------------------------------------------------------------------------- */
 494 
 495 static cb_ret_t
 496 group_handle_key (WGroup * g, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 497 {
 498     cb_ret_t handled;
 499 
 500     /* first try the hotkey */
 501     handled = send_message (g, NULL, MSG_HOTKEY, key, NULL);
 502 
 503     /* not used - then try widget_callback */
 504     if (handled == MSG_NOT_HANDLED)
 505         handled = send_message (g->current->data, NULL, MSG_KEY, key, NULL);
 506 
 507     /* not used - try to use the unhandled case */
 508     if (handled == MSG_NOT_HANDLED)
 509         handled = send_message (g, g->current->data, MSG_UNHANDLED_KEY, key, NULL);
 510 
 511     return handled;
 512 }
 513 
 514 /* --------------------------------------------------------------------------------------------- */
 515 
 516 static cb_ret_t
 517 group_handle_hotkey (WGroup * g, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 518 {
 519     GList *current;
 520     Widget *w;
 521     cb_ret_t handled = MSG_NOT_HANDLED;
 522     int c;
 523 
 524     if (g->widgets == NULL)
 525         return MSG_NOT_HANDLED;
 526 
 527     if (g->current == NULL)
 528         g->current = g->widgets;
 529 
 530     w = WIDGET (g->current->data);
 531 
 532     if (!widget_get_state (w, WST_VISIBLE) || widget_get_state (w, WST_DISABLED))
 533         return MSG_NOT_HANDLED;
 534 
 535     /* Explanation: we don't send letter hotkeys to other widgets
 536      * if the currently selected widget is an input line */
 537     if (widget_get_options (w, WOP_IS_INPUT))
 538     {
 539         /* skip ascii control characters, anything else can valid character in some encoding */
 540         if (key >= 32 && key < 256)
 541             return MSG_NOT_HANDLED;
 542     }
 543 
 544     /* If it's an alt key, send the message */
 545     c = key & ~ALT (0);
 546     if (key & ALT (0) && g_ascii_isalpha (c))
 547         key = g_ascii_tolower (c);
 548 
 549     if (widget_get_options (w, WOP_WANT_HOTKEY))
 550         handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
 551 
 552     /* If not used, send hotkey to other widgets */
 553     if (handled == MSG_HANDLED)
 554         return MSG_HANDLED;
 555 
 556     current = group_get_widget_next_of (g->current);
 557 
 558     /* send it to all widgets */
 559     while (g->current != current && handled == MSG_NOT_HANDLED)
 560     {
 561         w = WIDGET (current->data);
 562 
 563         if (widget_get_options (w, WOP_WANT_HOTKEY) && !widget_get_state (w, WST_DISABLED))
 564             handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
 565 
 566         if (handled == MSG_NOT_HANDLED)
 567             current = group_get_widget_next_of (current);
 568     }
 569 
 570     if (handled == MSG_HANDLED)
 571     {
 572         w = WIDGET (current->data);
 573         widget_select (w);
 574         send_message (g, w, MSG_HOTKEY_HANDLED, 0, NULL);
 575     }
 576 
 577     return handled;
 578 }
 579 
 580 /* --------------------------------------------------------------------------------------------- */
 581 /*** public functions ****************************************************************************/
 582 /* --------------------------------------------------------------------------------------------- */
 583 
 584 /**
 585  * Initialize group.
 586  *
 587  * @param g WGroup widget
 588  * @param y1 y-coordinate of top-left corner
 589  * @param x1 x-coordinate of top-left corner
 590  * @param lines group height
 591  * @param cols group width
 592  * @param callback group callback
 593  * @param mouse_callback group mouse handler
 594  */
 595 
 596 void
 597 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]  */
 598 {
 599     Widget *w = WIDGET (g);
 600 
 601     widget_init (w, r, callback != NULL ? callback : group_default_callback, mouse_callback);
 602 
 603     w->mouse_handler = group_handle_mouse_event;
 604 
 605     w->make_global = group_default_make_global;
 606     w->make_local = group_default_make_local;
 607 
 608     w->find = group_default_find;
 609     w->find_by_type = group_default_find_by_type;
 610     w->find_by_id = group_default_find_by_id;
 611 
 612     w->set_state = group_default_set_state;
 613 
 614     g->mouse_status = MOU_UNHANDLED;
 615 }
 616 
 617 /* --------------------------------------------------------------------------------------------- */
 618 
 619 cb_ret_t
 620 group_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 621 {
 622     WGroup *g = GROUP (w);
 623 
 624     switch (msg)
 625     {
 626     case MSG_INIT:
 627         g_list_foreach (g->widgets, group_widget_init, NULL);
 628         return MSG_HANDLED;
 629 
 630     case MSG_DRAW:
 631         group_draw (g);
 632         return MSG_HANDLED;
 633 
 634     case MSG_KEY:
 635         return group_handle_key (g, parm);
 636 
 637     case MSG_HOTKEY:
 638         return group_handle_hotkey (g, parm);
 639 
 640     case MSG_CURSOR:
 641         return group_update_cursor (g);
 642 
 643     case MSG_RESIZE:
 644         group_default_resize (g, RECT (data));
 645         return MSG_HANDLED;
 646 
 647     case MSG_DESTROY:
 648         g_list_foreach (g->widgets, (GFunc) widget_destroy, NULL);
 649         g_list_free (g->widgets);
 650         g->widgets = NULL;
 651         return MSG_HANDLED;
 652 
 653     default:
 654         return widget_default_callback (w, sender, msg, parm, data);
 655     }
 656 }
 657 
 658 /* --------------------------------------------------------------------------------------------- */
 659 
 660 /**
 661  * Change state of group.
 662  *
 663  * @param w      group
 664  * @param state  widget state flag to modify
 665  * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
 666  *               Only one flag per call can be modified.
 667  * @return       MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
 668  */
 669 cb_ret_t
 670 group_default_set_state (Widget * w, widget_state_t state, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 671 {
 672     gboolean ret = MSG_HANDLED;
 673     WGroup *g = GROUP (w);
 674     widget_state_info_t st = {
 675         .state = state,
 676         .enable = enable
 677     };
 678 
 679     ret = widget_default_set_state (w, state, enable);
 680 
 681     if (state == WST_ACTIVE || state == WST_SUSPENDED || state == WST_CLOSED)
 682         /* inform all child widgets */
 683         g_list_foreach (g->widgets, group_widget_set_state, &st);
 684 
 685     if ((w->state & WST_ACTIVE) != 0)
 686     {
 687         if ((w->state & WST_FOCUSED) != 0)
 688         {
 689             /* update current widget */
 690             if (g->current != NULL)
 691                 widget_set_state (WIDGET (g->current->data), WST_FOCUSED, enable);
 692         }
 693         else
 694             /* inform all child widgets */
 695             g_list_foreach (g->widgets, group_widget_set_state, &st);
 696     }
 697 
 698     return ret;
 699 }
 700 
 701 /* --------------------------------------------------------------------------------------------- */
 702 
 703 /**
 704  * Handling mouse events.
 705  *
 706  * @param g WGroup object
 707  * @param event GPM mouse event
 708  *
 709  * @return result of mouse event handling
 710  */
 711 int
 712 group_handle_mouse_event (Widget * w, Gpm_Event * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 713 {
 714     WGroup *g = GROUP (w);
 715 
 716     if (g->widgets != NULL)
 717     {
 718         GList *p;
 719 
 720         /* send the event to widgets in reverse Z-order */
 721         p = g_list_last (g->widgets);
 722         do
 723         {
 724             Widget *wp = WIDGET (p->data);
 725 
 726             /* Don't use widget_is_selectable() here.
 727                If WOP_SELECTABLE option is not set, widget can handle mouse events.
 728                For example, commandl line in file manager */
 729             if (widget_get_state (w, WST_VISIBLE) && !widget_get_state (wp, WST_DISABLED))
 730             {
 731                 /* put global cursor position to the widget */
 732                 int ret;
 733 
 734                 ret = wp->mouse_handler (wp, event);
 735                 if (ret != MOU_UNHANDLED)
 736                     return ret;
 737             }
 738 
 739             p = g_list_previous (p);
 740         }
 741         while (p != NULL);
 742     }
 743 
 744     return MOU_UNHANDLED;
 745 }
 746 
 747 /* --------------------------------------------------------------------------------------------- */
 748 
 749 /**
 750  * Insert widget to group before specified widget with specified positioning.
 751  * Make the inserted widget current.
 752  *
 753  * @param g WGroup object
 754  * @param w widget to be added
 755  * @pos positioning flags
 756  * @param before add @w before this widget
 757  *
 758  * @return widget ID
 759  */
 760 
 761 unsigned long
 762 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]  */
 763 {
 764     Widget *wg = WIDGET (g);
 765     Widget *ww = WIDGET (w);
 766     GList *new_current;
 767 
 768     /* Don't accept NULL widget. This shouldn't happen */
 769     assert (ww != NULL);
 770 
 771     if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 772         ww->rect.x = (wg->rect.cols - ww->rect.cols) / 2;
 773 
 774     if ((pos_flags & WPOS_CENTER_VERT) != 0)
 775         ww->rect.y = (wg->rect.lines - ww->rect.lines) / 2;
 776 
 777     ww->owner = g;
 778     ww->pos_flags = pos_flags;
 779     widget_make_global (ww);
 780 
 781     if (g->widgets == NULL || before == NULL)
 782     {
 783         g->widgets = g_list_append (g->widgets, ww);
 784         new_current = g_list_last (g->widgets);
 785     }
 786     else
 787     {
 788         GList *b;
 789 
 790         b = g_list_find (g->widgets, before);
 791 
 792         /* don't accept widget not from group. This shouldn't happen */
 793         assert (b != NULL);
 794 
 795         b = g_list_next (b);
 796         g->widgets = g_list_insert_before (g->widgets, b, ww);
 797         if (b != NULL)
 798             new_current = g_list_previous (b);
 799         else
 800             new_current = g_list_last (g->widgets);
 801     }
 802 
 803     /* widget has been added at runtime */
 804     if (widget_get_state (wg, WST_ACTIVE))
 805     {
 806         group_widget_init (ww, NULL);
 807         widget_select (ww);
 808     }
 809     else
 810         g->current = new_current;
 811 
 812     return ww->id;
 813 }
 814 
 815 /* --------------------------------------------------------------------------------------------- */
 816 
 817 /**
 818  * Remove widget from group.
 819  *
 820  * @param w Widget object
 821  */
 822 void
 823 group_remove_widget (void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 824 {
 825     Widget *ww = WIDGET (w);
 826     WGroup *g;
 827     GList *d;
 828 
 829     /* Don't accept NULL widget. This shouldn't happen */
 830     assert (w != NULL);
 831 
 832     g = ww->owner;
 833 
 834     d = g_list_find (g->widgets, ww);
 835     if (d == g->current)
 836         group_set_current_widget_next (g);
 837 
 838     g->widgets = g_list_delete_link (g->widgets, d);
 839     if (g->widgets == NULL)
 840         g->current = NULL;
 841 
 842     /* widget has been deleted at runtime */
 843     if (widget_get_state (WIDGET (g), WST_ACTIVE))
 844     {
 845         group_draw (g);
 846         group_select_current_widget (g);
 847     }
 848 
 849     widget_make_local (ww);
 850     ww->owner = NULL;
 851 }
 852 
 853 /* --------------------------------------------------------------------------------------------- */
 854 
 855 /**
 856  * Switch current widget to widget after current in group.
 857  *
 858  * @param g WGroup object
 859  */
 860 
 861 void
 862 group_set_current_widget_next (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 863 {
 864     g->current = group_get_next_or_prev_of (g->current, TRUE);
 865 }
 866 
 867 /* --------------------------------------------------------------------------------------------- */
 868 /**
 869  * Switch current widget to widget before current in group.
 870  *
 871  * @param g WGroup object
 872  */
 873 
 874 void
 875 group_set_current_widget_prev (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 876 {
 877     g->current = group_get_next_or_prev_of (g->current, FALSE);
 878 }
 879 
 880 /* --------------------------------------------------------------------------------------------- */
 881 /**
 882  * Get widget that is after specified widget in group.
 883  *
 884  * @param w widget holder
 885  *
 886  * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
 887  */
 888 
 889 GList *
 890 group_get_widget_next_of (GList * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 891 {
 892     return group_get_next_or_prev_of (w, TRUE);
 893 }
 894 
 895 /* --------------------------------------------------------------------------------------------- */
 896 /**
 897  * Get widget that is before specified widget in group.
 898  *
 899  * @param w widget holder
 900  *
 901  * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
 902  */
 903 
 904 GList *
 905 group_get_widget_prev_of (GList * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 906 {
 907     return group_get_next_or_prev_of (w, FALSE);
 908 }
 909 
 910 /* --------------------------------------------------------------------------------------------- */
 911 /**
 912  * Try to select next widget in the Z order.
 913  *
 914  * @param g WGroup object
 915  */
 916 
 917 void
 918 group_select_next_widget (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 919 {
 920     group_select_next_or_prev (g, TRUE);
 921 }
 922 
 923 /* --------------------------------------------------------------------------------------------- */
 924 /**
 925  * Try to select previous widget in the Z order.
 926  *
 927  * @param g WGroup object
 928  */
 929 
 930 void
 931 group_select_prev_widget (WGroup * g)
     /* [previous][next][first][last][top][bottom][index][help]  */
 932 {
 933     group_select_next_or_prev (g, FALSE);
 934 }
 935 
 936 /* --------------------------------------------------------------------------------------------- */
 937 /**
 938  * Find the widget with the specified ID in the group and select it
 939  *
 940  * @param g WGroup object
 941  * @param id widget ID
 942  */
 943 
 944 void
 945 group_select_widget_by_id (const WGroup * g, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 946 {
 947     Widget *w;
 948 
 949     w = widget_find_by_id (CONST_WIDGET (g), id);
 950     if (w != NULL)
 951         widget_select (w);
 952 }
 953 
 954 /* --------------------------------------------------------------------------------------------- */
 955 /**
 956  * Send broadcast message to all widgets in the group.
 957  *
 958  * @param g WGroup object
 959  * @param msg message sent to widgets
 960  */
 961 
 962 void
 963 group_send_broadcast_msg (WGroup * g, widget_msg_t msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 964 {
 965     group_send_broadcast_msg_custom (g, msg, FALSE, WOP_DEFAULT);
 966 }
 967 
 968 /* --------------------------------------------------------------------------------------------- */

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