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

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