Manual pages: mcmcdiffmceditmcview

root/lib/widget/dialog.c

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

DEFINITIONS

This source file includes following definitions.
  1. dlg_default_get_colors
  2. refresh_cmd
  3. dlg_help
  4. dlg_execute_cmd
  5. dlg_handle_key
  6. dlg_key_event
  7. dlg_handle_mouse_event
  8. frontend_dlg_run
  9. dlg_default_destroy
  10. dlg_default_callback
  11. dlg_default_mouse_callback
  12. dlg_create
  13. dlg_set_default_colors
  14. dlg_close
  15. dlg_init
  16. dlg_process_event
  17. dlg_run_done
  18. dlg_run
  19. dlg_get_title

   1 /*
   2    Dialog box features module for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   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 <https://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/event.h"  // mc_event_raise()
  44 #include "lib/util.h"   // MC_PTR_FREE
  45 
  46 #include "lib/widget.h"
  47 #include "lib/widget/mouse.h"
  48 
  49 /*** global variables ****************************************************************************/
  50 
  51 /* Color styles for normal and error dialogs */
  52 dlg_colors_t dialog_colors;
  53 dlg_colors_t alarm_colors;
  54 dlg_colors_t listbox_colors;
  55 dlg_colors_t help_colors;
  56 
  57 /* A hook list for idle events */
  58 hook_t *idle_hook = NULL;
  59 
  60 /* left click outside of dialog closes it */
  61 gboolean mouse_close_dialog = FALSE;
  62 
  63 const global_keymap_t *dialog_map = NULL;
  64 
  65 /*** file scope macro definitions ****************************************************************/
  66 
  67 /*** file scope type declarations ****************************************************************/
  68 
  69 /*** forward declarations (file scope functions) *************************************************/
  70 
  71 /*** file scope variables ************************************************************************/
  72 
  73 /* --------------------------------------------------------------------------------------------- */
  74 /*** file scope functions ************************************************************************/
  75 /* --------------------------------------------------------------------------------------------- */
  76 
  77 static const int *
  78 dlg_default_get_colors (const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
  79 {
  80     return CONST_DIALOG (w)->colors;
  81 }
  82 
  83 /* --------------------------------------------------------------------------------------------- */
  84 
  85 static void
  86 refresh_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  87 {
  88 #ifdef HAVE_SLANG
  89     tty_touch_screen ();
  90     mc_refresh ();
  91 #else
  92     // Use this if the refreshes fail
  93     tty_clear_screen ();
  94     repaint_screen ();
  95 #endif
  96 }
  97 
  98 /* --------------------------------------------------------------------------------------------- */
  99 
 100 static void
 101 dlg_help (const WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 102 {
 103     ev_help_t event_data = { NULL, h->help_ctx };
 104 
 105     mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
 106 }
 107 
 108 /* --------------------------------------------------------------------------------------------- */
 109 
 110 static cb_ret_t
 111 dlg_execute_cmd (WDialog *h, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 112 {
 113     WGroup *g = GROUP (h);
 114     cb_ret_t ret = MSG_HANDLED;
 115 
 116     if (send_message (h, NULL, MSG_ACTION, command, NULL) == MSG_HANDLED)
 117         return MSG_HANDLED;
 118 
 119     switch (command)
 120     {
 121     case CK_Ok:
 122         h->ret_value = B_ENTER;
 123         dlg_close (h);
 124         break;
 125     case CK_Cancel:
 126         h->ret_value = B_CANCEL;
 127         dlg_close (h);
 128         break;
 129 
 130     case CK_Up:
 131     case CK_Left:
 132         group_select_prev_widget (g);
 133         break;
 134     case CK_Down:
 135     case CK_Right:
 136         group_select_next_widget (g);
 137         break;
 138 
 139     case CK_Help:
 140         dlg_help (h);
 141         break;
 142 
 143     case CK_Suspend:
 144         mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
 145         refresh_cmd ();
 146         break;
 147     case CK_Refresh:
 148         refresh_cmd ();
 149         break;
 150 
 151     case CK_ScreenList:
 152         if (!widget_get_state (WIDGET (h), WST_MODAL))
 153             dialog_switch_list ();
 154         else
 155             ret = MSG_NOT_HANDLED;
 156         break;
 157     case CK_ScreenNext:
 158         if (!widget_get_state (WIDGET (h), WST_MODAL))
 159             dialog_switch_next ();
 160         else
 161             ret = MSG_NOT_HANDLED;
 162         break;
 163     case CK_ScreenPrev:
 164         if (!widget_get_state (WIDGET (h), WST_MODAL))
 165             dialog_switch_prev ();
 166         else
 167             ret = MSG_NOT_HANDLED;
 168         break;
 169 
 170     default:
 171         ret = MSG_NOT_HANDLED;
 172     }
 173 
 174     return ret;
 175 }
 176 
 177 /* --------------------------------------------------------------------------------------------- */
 178 
 179 static cb_ret_t
 180 dlg_handle_key (WDialog *h, int d_key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 181 {
 182     long command;
 183 
 184     command = widget_lookup_key (WIDGET (h), d_key);
 185     if (command == CK_IgnoreKey)
 186         command = keybind_lookup_keymap_command (dialog_map, d_key);
 187     if (command != CK_IgnoreKey)
 188         return dlg_execute_cmd (h, command);
 189 
 190     return MSG_NOT_HANDLED;
 191 }
 192 
 193 /* --------------------------------------------------------------------------------------------- */
 194 
 195 static void
 196 dlg_key_event (WDialog *h, int d_key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 {
 198     Widget *w = WIDGET (h);
 199     WGroup *g = GROUP (h);
 200     cb_ret_t handled;
 201 
 202     if (g->widgets == NULL)
 203         return;
 204 
 205     if (g->current == NULL)
 206         g->current = g->widgets;
 207 
 208     // TAB used to cycle
 209     if (!widget_get_options (w, WOP_WANT_TAB))
 210     {
 211         if (d_key == '\t')
 212         {
 213             group_select_next_widget (g);
 214             return;
 215         }
 216         else if ((d_key & ~(KEY_M_SHIFT | KEY_M_CTRL)) == '\t')
 217         {
 218             group_select_prev_widget (g);
 219             return;
 220         }
 221     }
 222 
 223     // first can dlalog handle the key itself
 224     handled = send_message (h, NULL, MSG_KEY, d_key, NULL);
 225 
 226     if (handled == MSG_NOT_HANDLED)
 227         handled = group_default_callback (w, NULL, MSG_KEY, d_key, NULL);
 228 
 229     if (handled == MSG_NOT_HANDLED)
 230         handled = dlg_handle_key (h, d_key);
 231 
 232     (void) handled;
 233     send_message (h, NULL, MSG_POST_KEY, d_key, NULL);
 234 }
 235 
 236 /* --------------------------------------------------------------------------------------------- */
 237 
 238 static int
 239 dlg_handle_mouse_event (Widget *w, Gpm_Event *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 240 {
 241     if (w->mouse_callback != NULL)
 242     {
 243         int mou;
 244 
 245         mou = mouse_handle_event (w, event);
 246         if (mou != MOU_UNHANDLED)
 247             return mou;
 248     }
 249 
 250     return group_handle_mouse_event (w, event);
 251 }
 252 
 253 /* --------------------------------------------------------------------------------------------- */
 254 
 255 static void
 256 frontend_dlg_run (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 257 {
 258     Widget *wh = WIDGET (h);
 259     Gpm_Event event;
 260 
 261     event.x = -1;
 262 
 263     // close opened editors, viewers, etc
 264     if (!widget_get_state (wh, WST_MODAL) && mc_global.midnight_shutdown)
 265     {
 266         send_message (h, NULL, MSG_VALIDATE, 0, NULL);
 267         return;
 268     }
 269 
 270     while (widget_get_state (wh, WST_ACTIVE))
 271     {
 272         int d_key;
 273 
 274         if (tty_got_winch ())
 275             dialog_change_screen_size ();
 276 
 277         if (is_idle ())
 278         {
 279             if (idle_hook)
 280                 execute_hooks (idle_hook);
 281 
 282             while (widget_get_state (wh, WST_IDLE) && is_idle ())
 283                 send_message (wh, NULL, MSG_IDLE, 0, NULL);
 284 
 285             // Allow terminating the dialog from the idle handler
 286             if (!widget_get_state (wh, WST_ACTIVE))
 287                 break;
 288         }
 289 
 290         widget_update_cursor (wh);
 291 
 292         // Clear interrupt flag
 293         tty_got_interrupt ();
 294         d_key = tty_get_event (&event, GROUP (h)->mouse_status == MOU_REPEAT, TRUE);
 295 
 296         dlg_process_event (h, d_key, &event);
 297 
 298         if (widget_get_state (wh, WST_CLOSED))
 299             send_message (h, NULL, MSG_VALIDATE, 0, NULL);
 300     }
 301 }
 302 
 303 /* --------------------------------------------------------------------------------------------- */
 304 
 305 static void
 306 dlg_default_destroy (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 307 {
 308     WDialog *h = DIALOG (w);
 309 
 310     // if some widgets have history, save all histories at one moment here
 311     history_save (h, NULL);
 312     group_default_callback (w, NULL, MSG_DESTROY, 0, NULL);
 313     send_message (w, NULL, MSG_DESTROY, 0, NULL);
 314     mc_event_group_del (h->event_group);
 315     g_free (h->event_group);
 316     g_free (h);
 317 
 318     do_refresh ();
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 /*** public functions ****************************************************************************/
 323 /* --------------------------------------------------------------------------------------------- */
 324 /** Default dialog callback */
 325 
 326 cb_ret_t
 327 dlg_default_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     switch (msg)
 330     {
 331     case MSG_INIT:
 332         // nothing to init in dialog itself
 333         return MSG_HANDLED;
 334 
 335     case MSG_IDLE:
 336         // we don't want endless loop
 337         widget_idle (w, FALSE);
 338         return MSG_HANDLED;
 339 
 340     case MSG_DESTROY:
 341         // nothing to deinit in dialog itself
 342         return MSG_HANDLED;
 343 
 344     default:
 345         return group_default_callback (w, sender, msg, parm, data);
 346     }
 347 }
 348 
 349 /* --------------------------------------------------------------------------------------------- */
 350 
 351 void
 352 dlg_default_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 353 {
 354     switch (msg)
 355     {
 356     case MSG_MOUSE_CLICK:
 357         if (event->y < 0 || event->y >= w->rect.lines || event->x < 0 || event->x >= w->rect.cols)
 358         {
 359             DIALOG (w)->ret_value = B_CANCEL;
 360             dlg_close (DIALOG (w));
 361         }
 362         break;
 363 
 364     default:
 365         // return MOU_UNHANDLED
 366         event->result.abort = TRUE;
 367         break;
 368     }
 369 }
 370 
 371 /* --------------------------------------------------------------------------------------------- */
 372 
 373 WDialog *
 374 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]  */
 375             gboolean compact, const int *colors, widget_cb_fn callback,
 376             widget_mouse_cb_fn mouse_callback, const char *help_ctx, const char *title)
 377 {
 378     WRect r = { y1, x1, lines, cols };
 379     WDialog *new_d;
 380     Widget *w;
 381     WGroup *g;
 382 
 383     new_d = g_new0 (WDialog, 1);
 384     w = WIDGET (new_d);
 385     g = GROUP (new_d);
 386     widget_adjust_position (pos_flags, &r);
 387     group_init (g, &r, callback != NULL ? callback : dlg_default_callback,
 388                 mouse_callback != NULL ? mouse_callback : dlg_default_mouse_callback);
 389 
 390     w->pos_flags = pos_flags;
 391     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
 392     w->state |= WST_FOCUSED;
 393     // Temporary hack: dialog doesn't have an owner, own itself.
 394     w->owner = g;
 395 
 396     w->keymap = dialog_map;
 397 
 398     w->mouse_handler = dlg_handle_mouse_event;
 399     w->mouse.forced_capture = mouse_close_dialog && (w->pos_flags & WPOS_FULLSCREEN) == 0;
 400 
 401     w->destroy = dlg_default_destroy;
 402     w->get_colors = dlg_default_get_colors;
 403 
 404     new_d->colors = colors;
 405     new_d->help_ctx = help_ctx;
 406     new_d->compact = compact;
 407     new_d->data.p = NULL;
 408 
 409     if (modal)
 410     {
 411         w->state |= WST_MODAL;
 412 
 413         new_d->bg =
 414             WIDGET (frame_new (0, 0, w->rect.lines, w->rect.cols, title, FALSE, new_d->compact));
 415         group_add_widget (g, new_d->bg);
 416         frame_set_title (FRAME (new_d->bg), title);
 417     }
 418 
 419     // unique name of event group for this dialog
 420     new_d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_d);
 421 
 422     return new_d;
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */
 426 
 427 void
 428 dlg_set_default_colors (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 429 {
 430     dialog_colors[DLG_COLOR_NORMAL] = DIALOG_NORMAL_COLOR;
 431     dialog_colors[DLG_COLOR_FOCUS] = DIALOG_FOCUS_COLOR;
 432     dialog_colors[DLG_COLOR_HOT_NORMAL] = DIALOG_HOT_NORMAL_COLOR;
 433     dialog_colors[DLG_COLOR_HOT_FOCUS] = DIALOG_HOT_FOCUS_COLOR;
 434     dialog_colors[DLG_COLOR_SELECTED_NORMAL] = DIALOG_SELECTED_NORMAL_COLOR;
 435     dialog_colors[DLG_COLOR_SELECTED_FOCUS] = DIALOG_SELECTED_FOCUS_COLOR;
 436     dialog_colors[DLG_COLOR_TITLE] = DIALOG_TITLE_COLOR;
 437     dialog_colors[DLG_COLOR_FRAME] = DIALOG_FRAME_COLOR;
 438 
 439     alarm_colors[DLG_COLOR_NORMAL] = ERROR_NORMAL_COLOR;
 440     alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS_COLOR;
 441     alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL_COLOR;
 442     alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS_COLOR;
 443     alarm_colors[DLG_COLOR_SELECTED_NORMAL] = ERROR_HOT_FOCUS_COLOR;  // unused
 444     alarm_colors[DLG_COLOR_SELECTED_FOCUS] = ERROR_FOCUS_COLOR;       // unused
 445     alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE_COLOR;
 446     alarm_colors[DLG_COLOR_FRAME] = ERROR_FRAME_COLOR;
 447 
 448     listbox_colors[DLG_COLOR_NORMAL] = PMENU_ENTRY_COLOR;
 449     listbox_colors[DLG_COLOR_FOCUS] = PMENU_SELECTED_COLOR;
 450     listbox_colors[DLG_COLOR_HOT_NORMAL] = PMENU_ENTRY_COLOR;
 451     listbox_colors[DLG_COLOR_HOT_FOCUS] = PMENU_SELECTED_COLOR;
 452     listbox_colors[DLG_COLOR_SELECTED_NORMAL] = PMENU_SELECTED_COLOR;  // unused
 453     listbox_colors[DLG_COLOR_SELECTED_FOCUS] = PMENU_SELECTED_COLOR;   // unused
 454     listbox_colors[DLG_COLOR_TITLE] = PMENU_TITLE_COLOR;
 455     listbox_colors[DLG_COLOR_FRAME] = PMENU_FRAME_COLOR;
 456 
 457     help_colors[DLG_COLOR_NORMAL] = HELP_NORMAL_COLOR;
 458     help_colors[DLG_COLOR_FOCUS] = 0;  // unused
 459     help_colors[DLG_COLOR_HOT_NORMAL] = HELP_BOLD_COLOR;
 460     help_colors[DLG_COLOR_HOT_FOCUS] = 0;        // unused
 461     help_colors[DLG_COLOR_SELECTED_NORMAL] = 0;  // unused
 462     help_colors[DLG_COLOR_SELECTED_FOCUS] = 0;   // unused
 463     help_colors[DLG_COLOR_TITLE] = HELP_TITLE_COLOR;
 464     help_colors[DLG_COLOR_FRAME] = HELP_FRAME_COLOR;
 465 }
 466 
 467 /* --------------------------------------------------------------------------------------------- */
 468 
 469 void
 470 dlg_close (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 471 {
 472     widget_set_state (WIDGET (h), WST_CLOSED, TRUE);
 473 }
 474 
 475 /* --------------------------------------------------------------------------------------------- */
 476 /** Init the process */
 477 
 478 void
 479 dlg_init (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 480 {
 481     WGroup *g = GROUP (h);
 482     Widget *wh = WIDGET (h);
 483 
 484     if (top_dlg != NULL && widget_get_state (WIDGET (top_dlg->data), WST_MODAL))
 485         widget_set_state (wh, WST_MODAL, TRUE);
 486 
 487     // add dialog to the stack
 488     top_dlg = g_list_prepend (top_dlg, h);
 489 
 490     // Initialize dialog manager and widgets
 491     if (widget_get_state (wh, WST_CONSTRUCT))
 492     {
 493         if (!widget_get_state (wh, WST_MODAL))
 494             dialog_switch_add (h);
 495 
 496         send_message (h, NULL, MSG_INIT, 0, NULL);
 497         group_default_callback (wh, NULL, MSG_INIT, 0, NULL);
 498         history_load (h, NULL);
 499     }
 500 
 501     // Select the first widget that takes focus
 502     while (g->current != NULL && !widget_is_focusable (g->current->data))
 503         group_set_current_widget_next (g);
 504 
 505     widget_set_state (wh, WST_ACTIVE, TRUE);
 506     widget_draw (wh);
 507 
 508     h->ret_value = 0;
 509 }
 510 
 511 /* --------------------------------------------------------------------------------------------- */
 512 
 513 void
 514 dlg_process_event (WDialog *h, int key, Gpm_Event *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 515 {
 516     switch (key)
 517     {
 518     case EV_NONE:
 519         if (tty_got_interrupt ())
 520             dlg_execute_cmd (h, CK_Cancel);
 521         break;
 522 
 523     case EV_MOUSE:
 524     {
 525         Widget *w = WIDGET (h);
 526 
 527         GROUP (h)->mouse_status = w->mouse_handler (w, event);
 528         break;
 529     }
 530 
 531     default:
 532         dlg_key_event (h, key);
 533         break;
 534     }
 535 }
 536 
 537 /* --------------------------------------------------------------------------------------------- */
 538 /** Shutdown the dlg_run */
 539 
 540 void
 541 dlg_run_done (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 542 {
 543     top_dlg = g_list_remove (top_dlg, h);
 544 
 545     if (widget_get_state (WIDGET (h), WST_CLOSED))
 546     {
 547         send_message (h, GROUP (h)->current == NULL ? NULL : WIDGET (GROUP (h)->current->data),
 548                       MSG_END, 0, NULL);
 549         if (!widget_get_state (WIDGET (h), WST_MODAL))
 550             dialog_switch_remove (h);
 551     }
 552 }
 553 
 554 /* --------------------------------------------------------------------------------------------- */
 555 /**
 556  * Standard run dialog routine
 557  * We have to keep this routine small so that we can duplicate it's
 558  * behavior on complex routines like the file routines, this way,
 559  * they can call the dlg_process_event without rewriting all the code
 560  */
 561 
 562 int
 563 dlg_run (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 564 {
 565     dlg_init (h);
 566     frontend_dlg_run (h);
 567     dlg_run_done (h);
 568     return h->ret_value;
 569 }
 570 
 571 /* --------------------------------------------------------------------------------------------- */
 572 
 573 char *
 574 dlg_get_title (const WDialog *h, const ssize_t width)
     /* [previous][next][first][last][top][bottom][index][help]  */
 575 {
 576     char *t;
 577 
 578     if (h == NULL)
 579         abort ();
 580 
 581     if (h->get_title != NULL)
 582         t = h->get_title (h, width);
 583     else
 584         t = g_strdup ("");
 585 
 586     return t;
 587 }
 588 
 589 /* --------------------------------------------------------------------------------------------- */

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