Manual pages: mcmcdiffmceditmcview

root/lib/widget/dialog-switch.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_hotkey
  2. dialog_switch_suspend
  3. dialog_switch_goto
  4. dialog_switch_resize
  5. dialog_switch_add
  6. dialog_switch_remove
  7. dialog_switch_num
  8. dialog_switch_next
  9. dialog_switch_prev
  10. dialog_switch_list
  11. dialog_switch_process_pending
  12. dialog_switch_got_winch
  13. dialog_switch_shutdown
  14. do_refresh
  15. repaint_screen
  16. mc_refresh
  17. dialog_change_screen_size

   1 /*
   2    Support of multiply editors and viewers.
   3 
   4    Original idea and code: Oleg "Olegarch" Konovalov <olegarch@linuxinside.com>
   5 
   6    Copyright (C) 2009-2025
   7    Free Software Foundation, Inc.
   8 
   9    Written by:
  10    Daniel Borca <dborca@yahoo.com>, 2007
  11    Andrew Borodin <aborodin@vmail.ru>, 2010-2022
  12 
  13    This file is part of the Midnight Commander.
  14 
  15    The Midnight Commander is free software: you can redistribute it
  16    and/or modify it under the terms of the GNU General Public License as
  17    published by the Free Software Foundation, either version 3 of the License,
  18    or (at your option) any later version.
  19 
  20    The Midnight Commander is distributed in the hope that it will be useful,
  21    but WITHOUT ANY WARRANTY; without even the implied warranty of
  22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23    GNU General Public License for more details.
  24 
  25    You should have received a copy of the GNU General Public License
  26    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  27  */
  28 
  29 /** \file dialog-switch.c
  30  *  \brief Source: support of multiply editors and viewers.
  31  */
  32 
  33 #include <config.h>
  34 
  35 #include "lib/global.h"
  36 #include "lib/tty/tty.h"    // LINES, COLS
  37 #include "lib/tty/color.h"  // tty_set_normal_attrs()
  38 #include "lib/widget.h"
  39 #include "lib/event.h"
  40 
  41 /*** global variables ****************************************************************************/
  42 
  43 /* Primitive way to check if the the current dialog is our dialog */
  44 /* This is needed by async routines like load_prompt */
  45 GList *top_dlg = NULL;
  46 
  47 /* If set then dialogs just clean the screen when refreshing, else */
  48 /* they do a complete refresh, refreshing all the parts of the program */
  49 gboolean fast_refresh = FALSE;
  50 
  51 WDialog *filemanager = NULL;
  52 
  53 /*** file scope macro definitions ****************************************************************/
  54 
  55 /*** file scope type declarations ****************************************************************/
  56 
  57 /*** forward declarations (file scope functions) *************************************************/
  58 
  59 /*** file scope variables ************************************************************************/
  60 
  61 /* List of dialogs: filemanagers, editors, viewers */
  62 static GList *mc_dialogs = NULL;
  63 /* Currently active dialog */
  64 static GList *mc_current = NULL;
  65 /* Is there any dialogs that we have to run after returning to the manager from another dialog */
  66 static gboolean dialog_switch_pending = FALSE;
  67 
  68 /* --------------------------------------------------------------------------------------------- */
  69 /*** file scope functions ************************************************************************/
  70 /* --------------------------------------------------------------------------------------------- */
  71 
  72 static unsigned char
  73 get_hotkey (int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
  74 {
  75     return (n <= 9) ? '0' + n : 'a' + n - 10;
  76 }
  77 
  78 /* --------------------------------------------------------------------------------------------- */
  79 
  80 static void
  81 dialog_switch_suspend (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  82 {
  83     (void) user_data;
  84 
  85     if (data != mc_current->data)
  86         widget_set_state (WIDGET (data), WST_SUSPENDED, TRUE);
  87 }
  88 
  89 /* --------------------------------------------------------------------------------------------- */
  90 
  91 static void
  92 dialog_switch_goto (GList *dlg)
     /* [previous][next][first][last][top][bottom][index][help]  */
  93 {
  94     if (mc_current != dlg)
  95     {
  96         WDialog *old = DIALOG (mc_current->data);
  97 
  98         mc_current = dlg;
  99 
 100         if (old == filemanager)
 101         {
 102             // switch from panels to another dialog (editor, viewer, etc)
 103             dialog_switch_pending = TRUE;
 104             dialog_switch_process_pending ();
 105         }
 106         else
 107         {
 108             // switch from editor, viewer, etc to another dialog
 109             widget_set_state (WIDGET (old), WST_SUSPENDED, TRUE);
 110 
 111             if (DIALOG (dlg->data) != filemanager)
 112                 // switch to another editor, viewer, etc
 113                 // return to panels before run the required dialog
 114                 dialog_switch_pending = TRUE;
 115             else
 116             {
 117                 // switch to panels
 118                 widget_set_state (WIDGET (filemanager), WST_ACTIVE, TRUE);
 119                 do_refresh ();
 120             }
 121         }
 122     }
 123 }
 124 
 125 /* --------------------------------------------------------------------------------------------- */
 126 
 127 static void
 128 dialog_switch_resize (WDialog *d)
     /* [previous][next][first][last][top][bottom][index][help]  */
 129 {
 130     if (widget_get_state (WIDGET (d), WST_ACTIVE))
 131         send_message (d, NULL, MSG_RESIZE, 0, NULL);
 132     else
 133         GROUP (d)->winch_pending = TRUE;
 134 }
 135 
 136 /* --------------------------------------------------------------------------------------------- */
 137 /*** public functions ****************************************************************************/
 138 /* --------------------------------------------------------------------------------------------- */
 139 
 140 void
 141 dialog_switch_add (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 142 {
 143     GList *dlg;
 144 
 145     dlg = g_list_find (mc_dialogs, h);
 146 
 147     if (dlg != NULL)
 148         mc_current = dlg;
 149     else
 150     {
 151         mc_dialogs = g_list_prepend (mc_dialogs, h);
 152         mc_current = mc_dialogs;
 153     }
 154 
 155     // suspend forced all other screens
 156     g_list_foreach (mc_dialogs, dialog_switch_suspend, NULL);
 157 }
 158 
 159 /* --------------------------------------------------------------------------------------------- */
 160 
 161 void
 162 dialog_switch_remove (WDialog *h)
     /* [previous][next][first][last][top][bottom][index][help]  */
 163 {
 164     GList *this;
 165 
 166     if (DIALOG (mc_current->data) == h)
 167         this = mc_current;
 168     else
 169         this = g_list_find (mc_dialogs, h);
 170 
 171     mc_dialogs = g_list_delete_link (mc_dialogs, this);
 172 
 173     // adjust current dialog
 174     if (top_dlg != NULL)
 175         mc_current = g_list_find (mc_dialogs, DIALOG (top_dlg->data));
 176     else
 177         mc_current = mc_dialogs;
 178 
 179     // resume forced the current screen
 180     if (mc_current != NULL)
 181         widget_set_state (WIDGET (mc_current->data), WST_ACTIVE, TRUE);
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 
 186 size_t
 187 dialog_switch_num (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     return g_list_length (mc_dialogs);
 190 }
 191 
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 void
 195 dialog_switch_next (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     GList *next;
 198 
 199     if (mc_global.midnight_shutdown || mc_current == NULL)
 200         return;
 201 
 202     next = g_list_next (mc_current);
 203     if (next == NULL)
 204         next = mc_dialogs;
 205 
 206     dialog_switch_goto (next);
 207 }
 208 
 209 /* --------------------------------------------------------------------------------------------- */
 210 
 211 void
 212 dialog_switch_prev (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 213 {
 214     GList *prev;
 215 
 216     if (mc_global.midnight_shutdown || mc_current == NULL)
 217         return;
 218 
 219     prev = g_list_previous (mc_current);
 220     if (prev == NULL)
 221         prev = g_list_last (mc_dialogs);
 222 
 223     dialog_switch_goto (prev);
 224 }
 225 
 226 /* --------------------------------------------------------------------------------------------- */
 227 
 228 void
 229 dialog_switch_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 230 {
 231     const size_t dlg_num = g_list_length (mc_dialogs);
 232     int lines, cols;
 233     Listbox *listbox;
 234     GList *h, *selected;
 235     int i = 0;
 236 
 237     if (mc_global.midnight_shutdown || mc_current == NULL)
 238         return;
 239 
 240     lines = MIN ((size_t) (LINES * 2 / 3), dlg_num);
 241     cols = COLS * 2 / 3;
 242 
 243     listbox = listbox_window_new (lines, cols, _ ("Screens"), "[Screen selector]");
 244 
 245     for (h = mc_dialogs; h != NULL; h = g_list_next (h))
 246     {
 247         WDialog *dlg = DIALOG (h->data);
 248         char *title;
 249 
 250         if (dlg->get_title != NULL)
 251             title = dlg->get_title (dlg, WIDGET (listbox->list)->rect.cols - 2);
 252         else
 253             title = g_strdup ("");
 254 
 255         listbox_add_item_take (listbox->list, LISTBOX_APPEND_BEFORE, get_hotkey (i++), title, h,
 256                                FALSE);
 257     }
 258 
 259     selected = listbox_run_with_data (listbox, mc_current);
 260     if (selected != NULL)
 261         dialog_switch_goto (selected);
 262 }
 263 
 264 /* --------------------------------------------------------------------------------------------- */
 265 
 266 int
 267 dialog_switch_process_pending (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 268 {
 269     WDialog *h;
 270     int ret = 0;
 271 
 272     if (mc_current == NULL)
 273         return ret;
 274 
 275     h = DIALOG (mc_current->data);
 276 
 277     if (!dialog_switch_pending)
 278     {
 279         // return to panels and reload them forced
 280         if (mc_global.mc_run_mode == MC_RUN_FULL && h == filemanager)
 281             mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "update_panels", NULL);
 282     }
 283     else
 284         while (dialog_switch_pending)
 285         {
 286             Widget *wh = WIDGET (h);
 287 
 288             dialog_switch_pending = FALSE;
 289             widget_set_state (wh, WST_SUSPENDED, TRUE);
 290             ret = dlg_run (h);
 291             if (widget_get_state (wh, WST_CLOSED))
 292             {
 293                 widget_destroy (wh);
 294 
 295                 // return to panels
 296                 if (mc_global.mc_run_mode == MC_RUN_FULL)
 297                 {
 298                     mc_current = g_list_find (mc_dialogs, filemanager);
 299                     mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "update_panels", NULL);
 300                 }
 301             }
 302         }
 303 
 304     repaint_screen ();
 305 
 306     return ret;
 307 }
 308 
 309 /* --------------------------------------------------------------------------------------------- */
 310 
 311 void
 312 dialog_switch_got_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 313 {
 314     GList *dlg;
 315 
 316     for (dlg = mc_dialogs; dlg != NULL; dlg = g_list_next (dlg))
 317         if (dlg != mc_current)
 318             GROUP (dlg->data)->winch_pending = TRUE;
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 void
 324 dialog_switch_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 325 {
 326     while (mc_dialogs != NULL)
 327     {
 328         WDialog *dlg = DIALOG (mc_dialogs->data);
 329 
 330         dlg_run (dlg);
 331         widget_destroy (WIDGET (dlg));
 332     }
 333 }
 334 
 335 /* --------------------------------------------------------------------------------------------- */
 336 
 337 void
 338 do_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 339 {
 340     GList *d = top_dlg;
 341 
 342     if (fast_refresh)
 343     {
 344         if (d != NULL)
 345             widget_draw (WIDGET (d->data));
 346     }
 347     else
 348     {
 349         // Search first fullscreen dialog
 350         for (; d != NULL; d = g_list_next (d))
 351             if ((WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0)
 352                 break;
 353 
 354         /* when small dialog (i.e. error message) is created first,
 355            there is no fullscreen dialog in the stack */
 356         if (d == NULL)
 357             d = g_list_last (top_dlg);
 358 
 359         // back to top dialog
 360         for (; d != NULL; d = g_list_previous (d))
 361             widget_draw (WIDGET (d->data));
 362     }
 363 }
 364 
 365 /* --------------------------------------------------------------------------------------------- */
 366 
 367 void
 368 repaint_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 369 {
 370     do_refresh ();
 371     tty_refresh ();
 372 }
 373 
 374 /* --------------------------------------------------------------------------------------------- */
 375 
 376 void
 377 mc_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 378 {
 379 #ifdef ENABLE_BACKGROUND
 380     if (mc_global.we_are_background)
 381         return;
 382 #endif
 383 
 384     if (!tty_got_winch ())
 385         tty_refresh ();
 386     else
 387     {
 388         /* if winch was caugth, we should do not only redraw screen, but
 389            reposition/resize all */
 390         dialog_change_screen_size ();
 391     }
 392 }
 393 
 394 /* --------------------------------------------------------------------------------------------- */
 395 
 396 void
 397 dialog_change_screen_size (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 398 {
 399     GList *d;
 400 
 401     /* On startup, when mc reads directories top_dlg isn't created yet. If window is resized
 402      * at this time, SIGWINCH can be missed because tty_flush_winch() is called here before
 403      * a first tty_got_winch() call in frontend_dlg_run().
 404      *
 405      * Keep SIGWINCH events in pipe if top_dlg isn't created yet. */
 406     if (top_dlg == NULL)
 407         return;
 408 
 409     tty_flush_winch ();
 410     tty_change_screen_size ();
 411 
 412 #ifdef HAVE_SLANG
 413     tty_keypad (TRUE);
 414     tty_nodelay (FALSE);
 415 #endif
 416 
 417     // Inform all suspending dialogs
 418     dialog_switch_got_winch ();
 419 
 420     // Inform all running dialogs from first to last
 421     for (d = g_list_last (top_dlg); d != NULL; d = g_list_previous (d))
 422         dialog_switch_resize (DIALOG (d->data));
 423 
 424     // Now, force the redraw
 425     repaint_screen ();
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */

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