root/lib/widget/dialog.c

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

DEFINITIONS

This source file includes following definitions.
  1. dlg_get_next_or_prev_of
  2. dlg_select_next_or_prev
  3. dlg_broadcast_msg_to
  4. dlg_read_history
  5. dlg_find_widget_callback
  6. refresh_cmd
  7. dlg_execute_cmd
  8. dlg_handle_key
  9. dlg_mouse_translator
  10. dlg_mouse_event
  11. dlg_try_hotkey
  12. dlg_key_event
  13. frontend_dlg_run
  14. dlg_find_widget_by_id
  15. dlg_widget_set_position
  16. dlg_adjust_position
  17. dlg_default_repaint
  18. dlg_set_position
  19. dlg_set_size
  20. dlg_default_callback
  21. dlg_create
  22. dlg_set_default_colors
  23. dlg_erase
  24. add_widget_autopos
  25. add_widget
  26. add_widget_before
  27. del_widget
  28. do_refresh
  29. dlg_broadcast_msg
  30. find_widget_type
  31. dlg_find
  32. dlg_find_by_id
  33. dlg_select_by_id
  34. dlg_select_prev_widget
  35. dlg_select_next_widget
  36. update_cursor
  37. dlg_redraw
  38. dlg_stop
  39. dlg_init
  40. dlg_process_event
  41. dlg_run_done
  42. dlg_run
  43. dlg_destroy
  44. dlg_save_history
  45. dlg_set_title
  46. dlg_get_title
  47. dlg_set_current_widget_next
  48. dlg_set_current_widget_prev
  49. dlg_get_widget_next_of
  50. dlg_get_widget_prev_of

   1 /*
   2    Dialog box features module for the Midnight Commander
   3 
   4    Copyright (C) 1994-2019
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   9    The Midnight Commander is free software: you can redistribute it
  10    and/or modify it under the terms of the GNU General Public License as
  11    published by the Free Software Foundation, either version 3 of the License,
  12    or (at your option) any later version.
  13 
  14    The Midnight Commander is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21  */
  22 
  23 /** \file dialog.c
  24  *  \brief Source: dialog box features module
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include <ctype.h>
  30 #include <errno.h>
  31 #include <stdlib.h>
  32 #include <stdio.h>
  33 #include <string.h>
  34 #include <sys/types.h>
  35 #include <sys/stat.h>
  36 
  37 #include "lib/global.h"
  38 
  39 #include "lib/tty/tty.h"
  40 #include "lib/skin.h"
  41 #include "lib/tty/key.h"
  42 #include "lib/strutil.h"
  43 #include "lib/fileloc.h"        /* MC_HISTORY_FILE */
  44 #include "lib/event.h"          /* mc_event_raise() */
  45 #include "lib/util.h"           /* MC_PTR_FREE */
  46 
  47 #include "lib/widget.h"
  48 #include "lib/widget/mouse.h"
  49 
  50 /*** global variables ****************************************************************************/
  51 
  52 /* Color styles for normal and error dialogs */
  53 dlg_colors_t dialog_colors;
  54 dlg_colors_t alarm_colors;
  55 dlg_colors_t listbox_colors;
  56 
  57 /* Primitive way to check if the the current dialog is our dialog */
  58 /* This is needed by async routines like load_prompt */
  59 GList *top_dlg = NULL;
  60 
  61 /* A hook list for idle events */
  62 hook_t *idle_hook = NULL;
  63 
  64 /* If set then dialogs just clean the screen when refreshing, else */
  65 /* they do a complete refresh, refreshing all the parts of the program */
  66 gboolean fast_refresh = FALSE;
  67 
  68 /* left click outside of dialog closes it */
  69 gboolean mouse_close_dialog = FALSE;
  70 
  71 const global_keymap_t *dialog_map = NULL;
  72 
  73 /*** file scope macro definitions ****************************************************************/
  74 
  75 /*** file scope type declarations ****************************************************************/
  76 
  77 /* Control widget positions in dialog */
  78 typedef struct
  79 {
  80     int shift_x;
  81     int scale_x;
  82     int shift_y;
  83     int scale_y;
  84 } widget_shift_scale_t;
  85 
  86 /*** file scope variables ************************************************************************/
  87 
  88 /* --------------------------------------------------------------------------------------------- */
  89 /*** file scope functions ************************************************************************/
  90 /* --------------------------------------------------------------------------------------------- */
  91 
  92 static GList *
  93 dlg_get_next_or_prev_of (const GList * list, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95     GList *l = NULL;
  96 
  97     if (list != NULL)
  98     {
  99         const WDialog *owner = CONST_WIDGET (list->data)->owner;
 100 
 101         if (owner != NULL)
 102         {
 103             if (next)
 104             {
 105                 l = g_list_next (list);
 106                 if (l == NULL)
 107                     l = owner->widgets;
 108             }
 109             else
 110             {
 111                 l = g_list_previous (list);
 112                 if (l == NULL)
 113                     l = g_list_last (owner->widgets);
 114             }
 115         }
 116     }
 117 
 118     return l;
 119 }
 120 
 121 /* --------------------------------------------------------------------------------------------- */
 122 
 123 static void
 124 dlg_select_next_or_prev (WDialog * h, gboolean next)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     if (h->widgets != NULL && h->current != NULL)
 127     {
 128         GList *l = h->current;
 129         Widget *w;
 130 
 131         do
 132         {
 133             l = dlg_get_next_or_prev_of (l, next);
 134             w = WIDGET (l->data);
 135         }
 136         while ((widget_get_state (w, WST_DISABLED) || !widget_get_options (w, WOP_SELECTABLE))
 137                && l != h->current);
 138 
 139         widget_select (l->data);
 140     }
 141 }
 142 
 143 /* --------------------------------------------------------------------------------------------- */
 144 /**
 145  * broadcast a message to all the widgets in a dialog that have
 146  * the options set to flags. If flags is zero, the message is sent
 147  * to all widgets.
 148  */
 149 
 150 static void
 151 dlg_broadcast_msg_to (WDialog * h, widget_msg_t msg, gboolean reverse, widget_options_t flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 152 {
 153     GList *p, *first;
 154 
 155     if (h->widgets == NULL)
 156         return;
 157 
 158     if (h->current == NULL)
 159         h->current = h->widgets;
 160 
 161     p = dlg_get_next_or_prev_of (h->current, !reverse);
 162     first = p;
 163 
 164     do
 165     {
 166         Widget *w = WIDGET (p->data);
 167 
 168         p = dlg_get_next_or_prev_of (p, !reverse);
 169 
 170         if ((flags == 0) || ((flags & w->options) != 0))
 171             send_message (w, NULL, msg, 0, NULL);
 172     }
 173     while (first != p);
 174 }
 175 
 176 /* --------------------------------------------------------------------------------------------- */
 177 
 178 /**
 179   * Read histories from the ${XDG_CACHE_HOME}/mc/history file
 180   */
 181 static void
 182 dlg_read_history (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 183 {
 184     char *profile;
 185     ev_history_load_save_t event_data;
 186 
 187     if (num_history_items_recorded == 0)        /* this is how to disable */
 188         return;
 189 
 190     profile = mc_config_get_full_path (MC_HISTORY_FILE);
 191     event_data.cfg = mc_config_init (profile, TRUE);
 192     event_data.receiver = NULL;
 193 
 194     /* create all histories in dialog */
 195     mc_event_raise (h->event_group, MCEVENT_HISTORY_LOAD, &event_data);
 196 
 197     mc_config_deinit (event_data.cfg);
 198     g_free (profile);
 199 }
 200 
 201 /* --------------------------------------------------------------------------------------------- */
 202 
 203 static int
 204 dlg_find_widget_callback (const void *a, const void *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 205 {
 206     const Widget *w = CONST_WIDGET (a);
 207     const widget_cb_fn f = b;
 208 
 209     return (w->callback == f) ? 0 : 1;
 210 }
 211 
 212 /* --------------------------------------------------------------------------------------------- */
 213 
 214 static void
 215 refresh_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 216 {
 217 #ifdef HAVE_SLANG
 218     tty_touch_screen ();
 219     mc_refresh ();
 220 #else
 221     /* Use this if the refreshes fail */
 222     clr_scr ();
 223     repaint_screen ();
 224 #endif /* HAVE_SLANG */
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 
 229 static cb_ret_t
 230 dlg_execute_cmd (WDialog * h, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     cb_ret_t ret = MSG_HANDLED;
 233 
 234     if (send_message (h, NULL, MSG_ACTION, command, NULL) == MSG_HANDLED)
 235         return MSG_HANDLED;
 236 
 237     switch (command)
 238     {
 239     case CK_Ok:
 240         h->ret_value = B_ENTER;
 241         dlg_stop (h);
 242         break;
 243     case CK_Cancel:
 244         h->ret_value = B_CANCEL;
 245         dlg_stop (h);
 246         break;
 247 
 248     case CK_Up:
 249     case CK_Left:
 250         dlg_select_prev_widget (h);
 251         break;
 252     case CK_Down:
 253     case CK_Right:
 254         dlg_select_next_widget (h);
 255         break;
 256 
 257     case CK_Help:
 258         {
 259             ev_help_t event_data = { NULL, h->help_ctx };
 260             mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
 261         }
 262         break;
 263 
 264     case CK_Suspend:
 265         mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
 266         refresh_cmd ();
 267         break;
 268     case CK_Refresh:
 269         refresh_cmd ();
 270         break;
 271 
 272     case CK_ScreenList:
 273         if (!widget_get_state (WIDGET (h), WST_MODAL))
 274             dialog_switch_list ();
 275         else
 276             ret = MSG_NOT_HANDLED;
 277         break;
 278     case CK_ScreenNext:
 279         if (!widget_get_state (WIDGET (h), WST_MODAL))
 280             dialog_switch_next ();
 281         else
 282             ret = MSG_NOT_HANDLED;
 283         break;
 284     case CK_ScreenPrev:
 285         if (!widget_get_state (WIDGET (h), WST_MODAL))
 286             dialog_switch_prev ();
 287         else
 288             ret = MSG_NOT_HANDLED;
 289         break;
 290 
 291     default:
 292         ret = MSG_NOT_HANDLED;
 293     }
 294 
 295     return ret;
 296 }
 297 
 298 /* --------------------------------------------------------------------------------------------- */
 299 
 300 static cb_ret_t
 301 dlg_handle_key (WDialog * h, int d_key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 302 {
 303     long command;
 304 
 305     command = keybind_lookup_keymap_command (dialog_map, d_key);
 306     if (command != CK_IgnoreKey)
 307         return dlg_execute_cmd (h, command);
 308 
 309     return MSG_NOT_HANDLED;
 310 }
 311 
 312 /* --------------------------------------------------------------------------------------------- */
 313 /**
 314  * This is the low-level mouse handler.
 315  * It receives a Gpm_Event event and translates it into a higher level protocol.
 316  */
 317 static int
 318 dlg_mouse_translator (Gpm_Event * event, Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 319 {
 320     mouse_event_t me;
 321 
 322     me = mouse_translate_event (w, event);
 323 
 324     return mouse_process_event (w, &me);
 325 }
 326 
 327 /* --------------------------------------------------------------------------------------------- */
 328 
 329 static int
 330 dlg_mouse_event (WDialog * h, Gpm_Event * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 331 {
 332     Widget *wh = WIDGET (h);
 333 
 334     GList *p;
 335 
 336     /* close the dialog by mouse left click out of dialog area */
 337     if (mouse_close_dialog && (wh->pos_flags & WPOS_FULLSCREEN) == 0
 338         && ((event->buttons & GPM_B_LEFT) != 0) && ((event->type & GPM_DOWN) != 0)
 339         && !mouse_global_in_widget (event, wh))
 340     {
 341         h->ret_value = B_CANCEL;
 342         dlg_stop (h);
 343         return MOU_NORMAL;
 344     }
 345 
 346     if (wh->mouse_callback != NULL)
 347     {
 348         int mou;
 349 
 350         mou = dlg_mouse_translator (event, wh);
 351         if (mou != MOU_UNHANDLED)
 352             return mou;
 353     }
 354 
 355     if (h->widgets == NULL)
 356         return MOU_UNHANDLED;
 357 
 358     /* send the event to widgets in reverse Z-order */
 359     p = g_list_last (h->widgets);
 360     do
 361     {
 362         Widget *w = WIDGET (p->data);
 363 
 364         if (!widget_get_state (w, WST_DISABLED) && w->mouse_callback != NULL)
 365         {
 366             /* put global cursor position to the widget */
 367             int ret;
 368 
 369             ret = dlg_mouse_translator (event, w);
 370             if (ret != MOU_UNHANDLED)
 371                 return ret;
 372         }
 373 
 374         p = g_list_previous (p);
 375     }
 376     while (p != NULL);
 377 
 378     return MOU_UNHANDLED;
 379 }
 380 
 381 /* --------------------------------------------------------------------------------------------- */
 382 
 383 static cb_ret_t
 384 dlg_try_hotkey (WDialog * h, int d_key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     GList *hot_cur;
 387     Widget *current;
 388     cb_ret_t handled;
 389     int c;
 390 
 391     if (h->widgets == NULL)
 392         return MSG_NOT_HANDLED;
 393 
 394     if (h->current == NULL)
 395         h->current = h->widgets;
 396 
 397     /*
 398      * Explanation: we don't send letter hotkeys to other widgets if
 399      * the currently selected widget is an input line
 400      */
 401 
 402     current = WIDGET (h->current->data);
 403 
 404     if (widget_get_state (current, WST_DISABLED))
 405         return MSG_NOT_HANDLED;
 406 
 407     if (widget_get_options (current, WOP_IS_INPUT))
 408     {
 409         /* skip ascii control characters, anything else can valid character in
 410          * some encoding */
 411         if (d_key >= 32 && d_key < 256)
 412             return MSG_NOT_HANDLED;
 413     }
 414 
 415     /* If it's an alt key, send the message */
 416     c = d_key & ~ALT (0);
 417     if (d_key & ALT (0) && g_ascii_isalpha (c))
 418         d_key = g_ascii_tolower (c);
 419 
 420     handled = MSG_NOT_HANDLED;
 421     if (widget_get_options (current, WOP_WANT_HOTKEY))
 422         handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
 423 
 424     /* If not used, send hotkey to other widgets */
 425     if (handled == MSG_HANDLED)
 426         return MSG_HANDLED;
 427 
 428     hot_cur = dlg_get_widget_next_of (h->current);
 429 
 430     /* send it to all widgets */
 431     while (h->current != hot_cur && handled == MSG_NOT_HANDLED)
 432     {
 433         current = WIDGET (hot_cur->data);
 434 
 435         if (widget_get_options (current, WOP_WANT_HOTKEY)
 436             && !widget_get_state (current, WST_DISABLED))
 437             handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
 438 
 439         if (handled == MSG_NOT_HANDLED)
 440             hot_cur = dlg_get_widget_next_of (hot_cur);
 441     }
 442 
 443     if (handled == MSG_HANDLED)
 444         widget_select (WIDGET (hot_cur->data));
 445 
 446     return handled;
 447 }
 448 
 449 /* --------------------------------------------------------------------------------------------- */
 450 
 451 static void
 452 dlg_key_event (WDialog * h, int d_key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 453 {
 454     cb_ret_t handled;
 455 
 456     if (h->widgets == NULL)
 457         return;
 458 
 459     if (h->current == NULL)
 460         h->current = h->widgets;
 461 
 462     /* TAB used to cycle */
 463     if (!widget_get_options (WIDGET (h), WOP_WANT_TAB))
 464     {
 465         if (d_key == '\t')
 466         {
 467             dlg_select_next_widget (h);
 468             return;
 469         }
 470         else if ((d_key & ~(KEY_M_SHIFT | KEY_M_CTRL)) == '\t')
 471         {
 472             dlg_select_prev_widget (h);
 473             return;
 474         }
 475     }
 476 
 477     /* first can dlg_callback handle the key */
 478     handled = send_message (h, NULL, MSG_KEY, d_key, NULL);
 479 
 480     /* next try the hotkey */
 481     if (handled == MSG_NOT_HANDLED)
 482         handled = dlg_try_hotkey (h, d_key);
 483 
 484     if (handled == MSG_HANDLED)
 485         send_message (h, NULL, MSG_HOTKEY_HANDLED, 0, NULL);
 486     else
 487         /* not used - then try widget_callback */
 488         handled = send_message (h->current->data, NULL, MSG_KEY, d_key, NULL);
 489 
 490     /* not used- try to use the unhandled case */
 491     if (handled == MSG_NOT_HANDLED)
 492         handled = send_message (h, NULL, MSG_UNHANDLED_KEY, d_key, NULL);
 493 
 494     if (handled == MSG_NOT_HANDLED)
 495         handled = dlg_handle_key (h, d_key);
 496 
 497     (void) handled;
 498     send_message (h, NULL, MSG_POST_KEY, d_key, NULL);
 499 }
 500 
 501 /* --------------------------------------------------------------------------------------------- */
 502 
 503 static void
 504 frontend_dlg_run (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 505 {
 506     Widget *wh = WIDGET (h);
 507     Gpm_Event event;
 508 
 509     event.x = -1;
 510 
 511     /* close opened editors, viewers, etc */
 512     if (!widget_get_state (wh, WST_MODAL) && mc_global.midnight_shutdown)
 513     {
 514         send_message (h, NULL, MSG_VALIDATE, 0, NULL);
 515         return;
 516     }
 517 
 518     while (widget_get_state (wh, WST_ACTIVE))
 519     {
 520         int d_key;
 521 
 522         if (mc_global.tty.winch_flag != 0)
 523             dialog_change_screen_size ();
 524 
 525         if (is_idle ())
 526         {
 527             if (idle_hook)
 528                 execute_hooks (idle_hook);
 529 
 530             while (widget_get_state (wh, WST_IDLE) && is_idle ())
 531                 send_message (wh, NULL, MSG_IDLE, 0, NULL);
 532 
 533             /* Allow terminating the dialog from the idle handler */
 534             if (!widget_get_state (wh, WST_ACTIVE))
 535                 break;
 536         }
 537 
 538         update_cursor (h);
 539 
 540         /* Clear interrupt flag */
 541         tty_got_interrupt ();
 542         d_key = tty_get_event (&event, h->mouse_status == MOU_REPEAT, TRUE);
 543 
 544         dlg_process_event (h, d_key, &event);
 545 
 546         if (widget_get_state (wh, WST_CLOSED))
 547             send_message (h, NULL, MSG_VALIDATE, 0, NULL);
 548     }
 549 }
 550 
 551 /* --------------------------------------------------------------------------------------------- */
 552 
 553 static int
 554 dlg_find_widget_by_id (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 555 {
 556     const Widget *w = CONST_WIDGET (a);
 557     unsigned long id = GPOINTER_TO_UINT (b);
 558 
 559     return w->id == id ? 0 : 1;
 560 }
 561 
 562 /* --------------------------------------------------------------------------------------------- */
 563 static void
 564 dlg_widget_set_position (gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 565 {
 566     /* there are, mainly, 2 generally possible situations:
 567      * 1. control sticks to one side - it should be moved
 568      * 2. control sticks to two sides of one direction - it should be sized
 569      */
 570 
 571     Widget *c = WIDGET (data);
 572     Widget *wh = WIDGET (c->owner);
 573     const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
 574     int x = c->x;
 575     int y = c->y;
 576     int cols = c->cols;
 577     int lines = c->lines;
 578 
 579     if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
 580         x = wh->x + (wh->cols - c->cols) / 2;
 581     else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
 582     {
 583         x += wss->shift_x;
 584         cols += wss->scale_x;
 585     }
 586     else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
 587         x += wss->shift_x;
 588     else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
 589         x += wss->shift_x + wss->scale_x;
 590 
 591     if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
 592         y = wh->y + (wh->lines - c->lines) / 2;
 593     else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
 594     {
 595         y += wss->shift_y;
 596         lines += wss->scale_y;
 597     }
 598     else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
 599         y += wss->shift_y;
 600     else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
 601         y += wss->shift_y + wss->scale_y;
 602 
 603     widget_set_size (c, y, x, lines, cols);
 604 }
 605 
 606 /* --------------------------------------------------------------------------------------------- */
 607 
 608 static void
 609 dlg_adjust_position (widget_pos_flags_t pos_flags, int *y, int *x, int *lines, int *cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 610 {
 611     if ((pos_flags & WPOS_FULLSCREEN) != 0)
 612     {
 613         *y = 0;
 614         *x = 0;
 615         *lines = LINES;
 616         *cols = COLS;
 617     }
 618     else
 619     {
 620         if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 621             *x = (COLS - *cols) / 2;
 622 
 623         if ((pos_flags & WPOS_CENTER_VERT) != 0)
 624             *y = (LINES - *lines) / 2;
 625 
 626         if ((pos_flags & WPOS_TRYUP) != 0)
 627         {
 628             if (*y > 3)
 629                 *y -= 2;
 630             else if (*y == 3)
 631                 *y = 2;
 632         }
 633     }
 634 }
 635 
 636 /* --------------------------------------------------------------------------------------------- */
 637 /*** public functions ****************************************************************************/
 638 /* --------------------------------------------------------------------------------------------- */
 639 
 640 /** Clean the dialog area, draw the frame and the title */
 641 void
 642 dlg_default_repaint (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 643 {
 644     Widget *wh = WIDGET (h);
 645 
 646     int space;
 647 
 648     if (!widget_get_state (wh, WST_ACTIVE))
 649         return;
 650 
 651     space = h->compact ? 0 : 1;
 652 
 653     tty_setcolor (h->color[DLG_COLOR_NORMAL]);
 654     dlg_erase (h);
 655     tty_draw_box (wh->y + space, wh->x + space, wh->lines - 2 * space, wh->cols - 2 * space, FALSE);
 656 
 657     if (h->title != NULL)
 658     {
 659         /* TODO: truncate long title */
 660         tty_setcolor (h->color[DLG_COLOR_TITLE]);
 661         widget_move (h, space, (wh->cols - str_term_width1 (h->title)) / 2);
 662         tty_print_string (h->title);
 663     }
 664 }
 665 
 666 /* --------------------------------------------------------------------------------------------- */
 667 /** this function allows to set dialog position */
 668 
 669 void
 670 dlg_set_position (WDialog * h, int y, int x, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 671 {
 672     Widget *wh = WIDGET (h);
 673     widget_shift_scale_t wss;
 674 
 675     /* save old positions, will be used to reposition childs */
 676     int ox, oy, oc, ol;
 677 
 678     /* save old positions, will be used to reposition childs */
 679     ox = wh->x;
 680     oy = wh->y;
 681     oc = wh->cols;
 682     ol = wh->lines;
 683 
 684     wh->x = x;
 685     wh->y = y;
 686     wh->lines = lines;
 687     wh->cols = cols;
 688 
 689     /* dialog is empty */
 690     if (h->widgets == NULL)
 691         return;
 692 
 693     if (h->current == NULL)
 694         h->current = h->widgets;
 695 
 696     /* values by which controls should be moved */
 697     wss.shift_x = wh->x - ox;
 698     wss.scale_x = wh->cols - oc;
 699     wss.shift_y = wh->y - oy;
 700     wss.scale_y = wh->lines - ol;
 701 
 702     if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
 703         g_list_foreach (h->widgets, dlg_widget_set_position, &wss);
 704 }
 705 
 706 /* --------------------------------------------------------------------------------------------- */
 707 /** Set dialog size and position */
 708 
 709 void
 710 dlg_set_size (WDialog * h, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 711 {
 712     int x = 0, y = 0;
 713 
 714     dlg_adjust_position (WIDGET (h)->pos_flags, &y, &x, &lines, &cols);
 715     dlg_set_position (h, y, x, lines, cols);
 716 }
 717 
 718 /* --------------------------------------------------------------------------------------------- */
 719 /** Default dialog callback */
 720 
 721 cb_ret_t
 722 dlg_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 723 {
 724     WDialog *h = DIALOG (w);
 725 
 726     (void) sender;
 727     (void) parm;
 728     (void) data;
 729 
 730     switch (msg)
 731     {
 732     case MSG_DRAW:
 733         if (h->color != NULL)
 734         {
 735             dlg_default_repaint (h);
 736             return MSG_HANDLED;
 737         }
 738         return MSG_NOT_HANDLED;
 739 
 740     case MSG_IDLE:
 741         /* we don't want endless loop */
 742         widget_idle (w, FALSE);
 743         return MSG_HANDLED;
 744 
 745     case MSG_RESIZE:
 746         /* this is default resizing mechanism */
 747         /* the main idea of this code is to resize dialog
 748            according to flags (if any of flags require automatic
 749            resizing, like WPOS_CENTER, end after that reposition
 750            controls in dialog according to flags of widget) */
 751         dlg_set_size (h, w->lines, w->cols);
 752         return MSG_HANDLED;
 753 
 754     default:
 755         break;
 756     }
 757 
 758     return MSG_NOT_HANDLED;
 759 }
 760 
 761 /* --------------------------------------------------------------------------------------------- */
 762 
 763 WDialog *
 764 dlg_create (gboolean modal, int y1, int x1, int lines, int cols, widget_pos_flags_t pos_flags,
     /* [previous][next][first][last][top][bottom][index][help]  */
 765             gboolean compact, const int *colors, widget_cb_fn callback,
 766             widget_mouse_cb_fn mouse_callback, const char *help_ctx, const char *title)
 767 {
 768     WDialog *new_d;
 769     Widget *w;
 770 
 771     new_d = g_new0 (WDialog, 1);
 772     w = WIDGET (new_d);
 773     dlg_adjust_position (pos_flags, &y1, &x1, &lines, &cols);
 774     widget_init (w, y1, x1, lines, cols, (callback != NULL) ? callback : dlg_default_callback,
 775                  mouse_callback);
 776     w->pos_flags = pos_flags;
 777     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
 778 
 779     w->state |= WST_CONSTRUCT | WST_FOCUSED;
 780     if (modal)
 781         w->state |= WST_MODAL;
 782 
 783     new_d->color = colors;
 784     new_d->help_ctx = help_ctx;
 785     new_d->compact = compact;
 786     new_d->data = NULL;
 787 
 788     new_d->mouse_status = MOU_UNHANDLED;
 789 
 790     dlg_set_title (new_d, title);
 791 
 792     /* unique name of event group for this dialog */
 793     new_d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_d);
 794 
 795     return new_d;
 796 }
 797 
 798 /* --------------------------------------------------------------------------------------------- */
 799 
 800 void
 801 dlg_set_default_colors (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 802 {
 803     dialog_colors[DLG_COLOR_NORMAL] = COLOR_NORMAL;
 804     dialog_colors[DLG_COLOR_FOCUS] = COLOR_FOCUS;
 805     dialog_colors[DLG_COLOR_HOT_NORMAL] = COLOR_HOT_NORMAL;
 806     dialog_colors[DLG_COLOR_HOT_FOCUS] = COLOR_HOT_FOCUS;
 807     dialog_colors[DLG_COLOR_TITLE] = COLOR_TITLE;
 808 
 809     alarm_colors[DLG_COLOR_NORMAL] = ERROR_COLOR;
 810     alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS;
 811     alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL;
 812     alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS;
 813     alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE;
 814 
 815     listbox_colors[DLG_COLOR_NORMAL] = PMENU_ENTRY_COLOR;
 816     listbox_colors[DLG_COLOR_FOCUS] = PMENU_SELECTED_COLOR;
 817     listbox_colors[DLG_COLOR_HOT_NORMAL] = PMENU_ENTRY_COLOR;
 818     listbox_colors[DLG_COLOR_HOT_FOCUS] = PMENU_SELECTED_COLOR;
 819     listbox_colors[DLG_COLOR_TITLE] = PMENU_TITLE_COLOR;
 820 }
 821 
 822 /* --------------------------------------------------------------------------------------------- */
 823 
 824 void
 825 dlg_erase (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 826 {
 827     Widget *wh = WIDGET (h);
 828 
 829     if (wh != NULL && widget_get_state (wh, WST_ACTIVE))
 830         tty_fill_region (wh->y, wh->x, wh->lines, wh->cols, ' ');
 831 }
 832 
 833 /* --------------------------------------------------------------------------------------------- */
 834 /**
 835  * Insert widget to dialog before requested widget. Make the widget current. Return widget ID.
 836  */
 837 
 838 unsigned long
 839 add_widget_autopos (WDialog * h, void *w, widget_pos_flags_t pos_flags, const void *before)
     /* [previous][next][first][last][top][bottom][index][help]  */
 840 {
 841     Widget *wh = WIDGET (h);
 842     Widget *widget;
 843     GList *new_current;
 844 
 845     /* Don't accept 0 widgets */
 846     if (w == NULL)
 847         abort ();
 848 
 849     widget = WIDGET (w);
 850 
 851     if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 852         widget->x = (wh->cols - widget->cols) / 2;
 853     widget->x += wh->x;
 854 
 855     if ((pos_flags & WPOS_CENTER_VERT) != 0)
 856         widget->y = (wh->lines - widget->lines) / 2;
 857     widget->y += wh->y;
 858 
 859     widget->owner = h;
 860     widget->pos_flags = pos_flags;
 861     widget->id = h->widget_id++;
 862 
 863     if (h->widgets == NULL || before == NULL)
 864     {
 865         h->widgets = g_list_append (h->widgets, widget);
 866         new_current = g_list_last (h->widgets);
 867     }
 868     else
 869     {
 870         GList *b;
 871 
 872         b = g_list_find (h->widgets, before);
 873 
 874         /* don't accept widget not from dialog. This shouldn't happen */
 875         if (b == NULL)
 876             abort ();
 877 
 878         b = g_list_next (b);
 879         h->widgets = g_list_insert_before (h->widgets, b, widget);
 880         if (b != NULL)
 881             new_current = g_list_previous (b);
 882         else
 883             new_current = g_list_last (h->widgets);
 884     }
 885 
 886     /* widget has been added at runtime */
 887     if (widget_get_state (wh, WST_ACTIVE))
 888     {
 889         send_message (widget, NULL, MSG_INIT, 0, NULL);
 890         widget_select (widget);
 891     }
 892     else
 893         h->current = new_current;
 894 
 895     return widget->id;
 896 }
 897 
 898 /* --------------------------------------------------------------------------------------------- */
 899 /** wrapper to simply add lefttop positioned controls */
 900 
 901 unsigned long
 902 add_widget (WDialog * h, void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 903 {
 904     return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT,
 905                                h->current != NULL ? h->current->data : NULL);
 906 }
 907 
 908 /* --------------------------------------------------------------------------------------------- */
 909 
 910 unsigned long
 911 add_widget_before (WDialog * h, void *w, void *before)
     /* [previous][next][first][last][top][bottom][index][help]  */
 912 {
 913     return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT, before);
 914 }
 915 
 916 /* --------------------------------------------------------------------------------------------- */
 917 
 918 /** delete widget from dialog */
 919 void
 920 del_widget (void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 921 {
 922     WDialog *h;
 923     GList *d;
 924 
 925     /* Don't accept NULL widget. This shouldn't happen */
 926     if (w == NULL)
 927         abort ();
 928 
 929     h = WIDGET (w)->owner;
 930 
 931     d = g_list_find (h->widgets, w);
 932     if (d == h->current)
 933         dlg_set_current_widget_next (h);
 934 
 935     h->widgets = g_list_remove_link (h->widgets, d);
 936     if (h->widgets == NULL)
 937         h->current = NULL;
 938     send_message (d->data, NULL, MSG_DESTROY, 0, NULL);
 939     g_free (d->data);
 940     g_list_free_1 (d);
 941 
 942     /* widget has been deleted in runtime */
 943     if (widget_get_state (WIDGET (h), WST_ACTIVE))
 944     {
 945         dlg_redraw (h);
 946         dlg_select_current_widget (h);
 947     }
 948 }
 949 
 950 /* --------------------------------------------------------------------------------------------- */
 951 
 952 void
 953 do_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 954 {
 955     GList *d = top_dlg;
 956 
 957     if (fast_refresh)
 958     {
 959         if (d != NULL)
 960             dlg_redraw (DIALOG (d->data));
 961     }
 962     else
 963     {
 964         /* Search first fullscreen dialog */
 965         for (; d != NULL; d = g_list_next (d))
 966             if ((WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0)
 967                 break;
 968         /* back to top dialog */
 969         for (; d != NULL; d = g_list_previous (d))
 970             dlg_redraw (DIALOG (d->data));
 971     }
 972 }
 973 
 974 /* --------------------------------------------------------------------------------------------- */
 975 /** broadcast a message to all the widgets in a dialog */
 976 
 977 void
 978 dlg_broadcast_msg (WDialog * h, widget_msg_t msg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 979 {
 980     dlg_broadcast_msg_to (h, msg, FALSE, 0);
 981 }
 982 
 983 /* --------------------------------------------------------------------------------------------- */
 984 /** Find the widget with the given callback in the dialog h */
 985 
 986 Widget *
 987 find_widget_type (const WDialog * h, widget_cb_fn callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 988 {
 989     GList *w;
 990 
 991     w = g_list_find_custom (h->widgets, (gconstpointer) callback, dlg_find_widget_callback);
 992 
 993     return (w == NULL) ? NULL : WIDGET (w->data);
 994 }
 995 
 996 /* --------------------------------------------------------------------------------------------- */
 997 
 998 GList *
 999 dlg_find (const WDialog * h, const Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
1000 {
1001     return (w->owner == NULL || w->owner != h) ? NULL : g_list_find (h->widgets, w);
1002 }
1003 
1004 /* --------------------------------------------------------------------------------------------- */
1005 /** Find the widget with the given id */
1006 
1007 Widget *
1008 dlg_find_by_id (const WDialog * h, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
1009 {
1010     GList *w;
1011 
1012     w = g_list_find_custom (h->widgets, GUINT_TO_POINTER (id), dlg_find_widget_by_id);
1013     return w != NULL ? WIDGET (w->data) : NULL;
1014 }
1015 
1016 /* --------------------------------------------------------------------------------------------- */
1017 /** Find the widget with the given id in the dialog h and select it */
1018 
1019 void
1020 dlg_select_by_id (const WDialog * h, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
1021 {
1022     Widget *w;
1023 
1024     w = dlg_find_by_id (h, id);
1025     if (w != NULL)
1026         widget_select (w);
1027 }
1028 
1029 /* --------------------------------------------------------------------------------------------- */
1030 /** Try to select previous widget in the tab order */
1031 
1032 void
1033 dlg_select_prev_widget (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1034 {
1035     dlg_select_next_or_prev (h, FALSE);
1036 }
1037 
1038 /* --------------------------------------------------------------------------------------------- */
1039 /** Try to select next widget in the tab order */
1040 
1041 void
1042 dlg_select_next_widget (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1043 {
1044     dlg_select_next_or_prev (h, TRUE);
1045 }
1046 
1047 /* --------------------------------------------------------------------------------------------- */
1048 
1049 void
1050 update_cursor (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1051 {
1052     GList *p = h->current;
1053 
1054     if (p != NULL && widget_get_state (WIDGET (h), WST_ACTIVE))
1055     {
1056         Widget *w = WIDGET (p->data);
1057 
1058         if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR))
1059             send_message (w, NULL, MSG_CURSOR, 0, NULL);
1060         else
1061             do
1062             {
1063                 p = dlg_get_widget_next_of (p);
1064                 if (p == h->current)
1065                     break;
1066 
1067                 w = WIDGET (p->data);
1068 
1069                 if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR)
1070                     && send_message (w, NULL, MSG_CURSOR, 0, NULL) == MSG_HANDLED)
1071                     break;
1072             }
1073             while (TRUE);
1074     }
1075 }
1076 
1077 /* --------------------------------------------------------------------------------------------- */
1078 /**
1079  * Redraw the widgets in reverse order, leaving the current widget
1080  * as the last one
1081  */
1082 
1083 void
1084 dlg_redraw (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1085 {
1086     if (!widget_get_state (WIDGET (h), WST_ACTIVE))
1087         return;
1088 
1089     if (h->winch_pending)
1090     {
1091         h->winch_pending = FALSE;
1092         send_message (h, NULL, MSG_RESIZE, 0, NULL);
1093     }
1094 
1095     send_message (h, NULL, MSG_DRAW, 0, NULL);
1096     dlg_broadcast_msg (h, MSG_DRAW);
1097     update_cursor (h);
1098 }
1099 
1100 /* --------------------------------------------------------------------------------------------- */
1101 
1102 void
1103 dlg_stop (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1104 {
1105     widget_set_state (WIDGET (h), WST_CLOSED, TRUE);
1106 }
1107 
1108 /* --------------------------------------------------------------------------------------------- */
1109 /** Init the process */
1110 
1111 void
1112 dlg_init (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1113 {
1114     Widget *wh = WIDGET (h);
1115 
1116     if (top_dlg != NULL && widget_get_state (WIDGET (top_dlg->data), WST_MODAL))
1117         widget_set_state (wh, WST_MODAL, TRUE);
1118 
1119     /* add dialog to the stack */
1120     top_dlg = g_list_prepend (top_dlg, h);
1121 
1122     /* Initialize dialog manager and widgets */
1123     if (widget_get_state (wh, WST_CONSTRUCT))
1124     {
1125         if (!widget_get_state (wh, WST_MODAL))
1126             dialog_switch_add (h);
1127 
1128         send_message (h, NULL, MSG_INIT, 0, NULL);
1129         dlg_broadcast_msg (h, MSG_INIT);
1130         dlg_read_history (h);
1131     }
1132 
1133     /* Select the first widget that takes focus */
1134     while (h->current != NULL && !widget_get_options (WIDGET (h->current->data), WOP_SELECTABLE)
1135            && !widget_get_state (WIDGET (h->current->data), WST_DISABLED))
1136         dlg_set_current_widget_next (h);
1137 
1138     widget_set_state (wh, WST_ACTIVE, TRUE);
1139     dlg_redraw (h);
1140     /* focus found widget */
1141     if (h->current != NULL)
1142         widget_set_state (WIDGET (h->current->data), WST_FOCUSED, TRUE);
1143 
1144     h->ret_value = 0;
1145 }
1146 
1147 /* --------------------------------------------------------------------------------------------- */
1148 
1149 void
1150 dlg_process_event (WDialog * h, int key, Gpm_Event * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1151 {
1152     switch (key)
1153     {
1154     case EV_NONE:
1155         if (tty_got_interrupt ())
1156             dlg_execute_cmd (h, CK_Cancel);
1157         break;
1158 
1159     case EV_MOUSE:
1160         h->mouse_status = dlg_mouse_event (h, event);
1161         break;
1162 
1163     default:
1164         dlg_key_event (h, key);
1165         break;
1166     }
1167 }
1168 
1169 /* --------------------------------------------------------------------------------------------- */
1170 /** Shutdown the dlg_run */
1171 
1172 void
1173 dlg_run_done (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1174 {
1175     top_dlg = g_list_remove (top_dlg, h);
1176 
1177     if (widget_get_state (WIDGET (h), WST_CLOSED))
1178     {
1179         send_message (h, h->current == NULL ? NULL : WIDGET (h->current->data), MSG_END, 0, NULL);
1180         if (!widget_get_state (WIDGET (h), WST_MODAL))
1181             dialog_switch_remove (h);
1182     }
1183 }
1184 
1185 /* --------------------------------------------------------------------------------------------- */
1186 /**
1187  * Standard run dialog routine
1188  * We have to keep this routine small so that we can duplicate it's
1189  * behavior on complex routines like the file routines, this way,
1190  * they can call the dlg_process_event without rewriting all the code
1191  */
1192 
1193 int
1194 dlg_run (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1195 {
1196     dlg_init (h);
1197     frontend_dlg_run (h);
1198     dlg_run_done (h);
1199     return h->ret_value;
1200 }
1201 
1202 /* --------------------------------------------------------------------------------------------- */
1203 
1204 void
1205 dlg_destroy (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1206 {
1207     /* if some widgets have history, save all history at one moment here */
1208     dlg_save_history (h);
1209     dlg_broadcast_msg (h, MSG_DESTROY);
1210     g_list_free_full (h->widgets, g_free);
1211     mc_event_group_del (h->event_group);
1212     g_free (h->event_group);
1213     g_free (h->title);
1214     g_free (h);
1215 
1216     do_refresh ();
1217 }
1218 
1219 /* --------------------------------------------------------------------------------------------- */
1220 
1221 /**
1222   * Write history to the ${XDG_CACHE_HOME}/mc/history file
1223   */
1224 void
1225 dlg_save_history (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1226 {
1227     char *profile;
1228     int i;
1229 
1230     if (num_history_items_recorded == 0)        /* this is how to disable */
1231         return;
1232 
1233     profile = mc_config_get_full_path (MC_HISTORY_FILE);
1234     i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1235     if (i != -1)
1236         close (i);
1237 
1238     /* Make sure the history is only readable by the user */
1239     if (chmod (profile, S_IRUSR | S_IWUSR) != -1 || errno == ENOENT)
1240     {
1241         ev_history_load_save_t event_data;
1242 
1243         event_data.cfg = mc_config_init (profile, FALSE);
1244         event_data.receiver = NULL;
1245 
1246         /* get all histories in dialog */
1247         mc_event_raise (h->event_group, MCEVENT_HISTORY_SAVE, &event_data);
1248 
1249         mc_config_save_file (event_data.cfg, NULL);
1250         mc_config_deinit (event_data.cfg);
1251     }
1252 
1253     g_free (profile);
1254 }
1255 
1256 /* --------------------------------------------------------------------------------------------- */
1257 
1258 void
1259 dlg_set_title (WDialog * h, const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
1260 {
1261     MC_PTR_FREE (h->title);
1262 
1263     /* Strip existing spaces, add one space before and after the title */
1264     if (title != NULL && title[0] != '\0')
1265     {
1266         char *t;
1267 
1268         t = g_strstrip (g_strdup (title));
1269         if (t[0] != '\0')
1270             h->title = g_strdup_printf (" %s ", t);
1271         g_free (t);
1272     }
1273 }
1274 
1275 /* --------------------------------------------------------------------------------------------- */
1276 
1277 char *
1278 dlg_get_title (const WDialog * h, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1279 {
1280     char *t;
1281 
1282     if (h == NULL)
1283         abort ();
1284 
1285     if (h->get_title != NULL)
1286         t = h->get_title (h, len);
1287     else
1288         t = g_strdup ("");
1289 
1290     return t;
1291 }
1292 
1293 /* --------------------------------------------------------------------------------------------- */
1294 
1295 /**
1296  * Switch current widget to widget after current in dialog.
1297  *
1298  * @param h WDialog widget
1299  */
1300 
1301 void
1302 dlg_set_current_widget_next (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1303 {
1304     h->current = dlg_get_next_or_prev_of (h->current, TRUE);
1305 }
1306 
1307 /* --------------------------------------------------------------------------------------------- */
1308 
1309 /**
1310  * Switch current widget to widget before current in dialog.
1311  *
1312  * @param h WDialog widget
1313  */
1314 
1315 void
1316 dlg_set_current_widget_prev (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1317 {
1318     h->current = dlg_get_next_or_prev_of (h->current, FALSE);
1319 }
1320 
1321 /* --------------------------------------------------------------------------------------------- */
1322 
1323 /**
1324  * Get widget that is after specified widget in dialog.
1325  *
1326  * @param w widget holder
1327  *
1328  * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
1329  */
1330 
1331 GList *
1332 dlg_get_widget_next_of (GList * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
1333 {
1334     return dlg_get_next_or_prev_of (w, TRUE);
1335 }
1336 
1337 /* --------------------------------------------------------------------------------------------- */
1338 
1339 /**
1340  * Get widget that is before specified widget in dialog.
1341  *
1342  * @param w widget holder
1343  *
1344  * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
1345  */
1346 
1347 GList *
1348 dlg_get_widget_prev_of (GList * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
1349 {
1350     return dlg_get_next_or_prev_of (w, FALSE);
1351 }
1352 
1353 /* --------------------------------------------------------------------------------------------- */

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