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 = DIALOG (mc_current->data);
 270     int ret = 0;
 271 
 272     if (!dialog_switch_pending)
 273     {
 274         // return to panels and reload them forced
 275         if (mc_global.mc_run_mode == MC_RUN_FULL && h == filemanager)
 276             mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "update_panels", NULL);
 277     }
 278     else
 279         while (dialog_switch_pending)
 280         {
 281             Widget *wh = WIDGET (h);
 282 
 283             dialog_switch_pending = FALSE;
 284             widget_set_state (wh, WST_SUSPENDED, TRUE);
 285             ret = dlg_run (h);
 286             if (widget_get_state (wh, WST_CLOSED))
 287             {
 288                 widget_destroy (wh);
 289 
 290                 // return to panels
 291                 if (mc_global.mc_run_mode == MC_RUN_FULL)
 292                 {
 293                     mc_current = g_list_find (mc_dialogs, filemanager);
 294                     mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "update_panels", NULL);
 295                 }
 296             }
 297         }
 298 
 299     repaint_screen ();
 300 
 301     return ret;
 302 }
 303 
 304 /* --------------------------------------------------------------------------------------------- */
 305 
 306 void
 307 dialog_switch_got_winch (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 308 {
 309     GList *dlg;
 310 
 311     for (dlg = mc_dialogs; dlg != NULL; dlg = g_list_next (dlg))
 312         if (dlg != mc_current)
 313             GROUP (dlg->data)->winch_pending = TRUE;
 314 }
 315 
 316 /* --------------------------------------------------------------------------------------------- */
 317 
 318 void
 319 dialog_switch_shutdown (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 320 {
 321     while (mc_dialogs != NULL)
 322     {
 323         WDialog *dlg = DIALOG (mc_dialogs->data);
 324 
 325         dlg_run (dlg);
 326         widget_destroy (WIDGET (dlg));
 327     }
 328 }
 329 
 330 /* --------------------------------------------------------------------------------------------- */
 331 
 332 void
 333 do_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 334 {
 335     GList *d = top_dlg;
 336 
 337     if (fast_refresh)
 338     {
 339         if (d != NULL)
 340             widget_draw (WIDGET (d->data));
 341     }
 342     else
 343     {
 344         // Search first fullscreen dialog
 345         for (; d != NULL; d = g_list_next (d))
 346             if ((WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0)
 347                 break;
 348 
 349         /* when small dialog (i.e. error message) is created first,
 350            there is no fullscreen dialog in the stack */
 351         if (d == NULL)
 352             d = g_list_last (top_dlg);
 353 
 354         // back to top dialog
 355         for (; d != NULL; d = g_list_previous (d))
 356             widget_draw (WIDGET (d->data));
 357     }
 358 }
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 void
 363 repaint_screen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 364 {
 365     do_refresh ();
 366     tty_refresh ();
 367 }
 368 
 369 /* --------------------------------------------------------------------------------------------- */
 370 
 371 void
 372 mc_refresh (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 373 {
 374 #ifdef ENABLE_BACKGROUND
 375     if (mc_global.we_are_background)
 376         return;
 377 #endif
 378 
 379     if (!tty_got_winch ())
 380         tty_refresh ();
 381     else
 382     {
 383         /* if winch was caugth, we should do not only redraw screen, but
 384            reposition/resize all */
 385         dialog_change_screen_size ();
 386     }
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 
 391 void
 392 dialog_change_screen_size (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 393 {
 394     GList *d;
 395 
 396     /* On startup, when mc reads directories top_dlg isn't created yet. If window is resized
 397      * at this time, SIGWINCH can be missed because tty_flush_winch() is called here before
 398      * a first tty_got_winch() call in frontend_dlg_run().
 399      *
 400      * Keep SIGWINCH events in pipe if top_dlg isn't created yet. */
 401     if (top_dlg == NULL)
 402         return;
 403 
 404     tty_flush_winch ();
 405     tty_change_screen_size ();
 406 
 407 #ifdef HAVE_SLANG
 408     tty_keypad (TRUE);
 409     tty_nodelay (FALSE);
 410 #endif
 411 
 412     // Inform all suspending dialogs
 413     dialog_switch_got_winch ();
 414 
 415     // Inform all running dialogs from first to last
 416     for (d = g_list_last (top_dlg); d != NULL; d = g_list_previous (d))
 417         dialog_switch_resize (DIALOG (d->data));
 418 
 419     // Now, force the redraw
 420     repaint_screen ();
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */

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