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

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