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_find
  7. group_default_find_by_type
  8. group_default_find_by_id
  9. group_update_cursor
  10. group_widget_set_position
  11. group_set_position
  12. group_default_resize
  13. group_draw
  14. group_handle_key
  15. group_handle_hotkey
  16. group_init
  17. group_default_callback
  18. group_default_set_state
  19. group_handle_mouse_event
  20. group_add_widget_autopos
  21. group_remove_widget
  22. group_set_current_widget_next
  23. group_set_current_widget_prev
  24. group_get_widget_next_of
  25. group_get_widget_prev_of
  26. group_select_next_widget
  27. group_select_prev_widget
  28. group_select_widget_by_id
  29. group_send_broadcast_msg

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

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