/* Support of multiply editors and viewers. Original idea and code: Oleg "Olegarch" Konovalov Copyright (C) 2009-2024 Free Software Foundation, Inc. Written by: Daniel Borca , 2007 Andrew Borodin , 2010-2022 This file is part of the Midnight Commander. The Midnight Commander is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The Midnight Commander is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \file dialog-switch.c * \brief Source: support of multiply editors and viewers. */ #include #include "lib/global.h" #include "lib/tty/tty.h" /* LINES, COLS */ #include "lib/tty/color.h" /* tty_set_normal_attrs() */ #include "lib/widget.h" #include "lib/event.h" /*** global variables ****************************************************************************/ /* Primitive way to check if the the current dialog is our dialog */ /* This is needed by async routines like load_prompt */ GList *top_dlg = NULL; /* If set then dialogs just clean the screen when refreshing, else */ /* they do a complete refresh, refreshing all the parts of the program */ gboolean fast_refresh = FALSE; WDialog *filemanager = NULL; /*** file scope macro definitions ****************************************************************/ /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ /* List of dialogs: filemanagers, editors, viewers */ static GList *mc_dialogs = NULL; /* Currently active dialog */ static GList *mc_current = NULL; /* Is there any dialogs that we have to run after returning to the manager from another dialog */ static gboolean dialog_switch_pending = FALSE; /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static unsigned char get_hotkey (int n) { return (n <= 9) ? '0' + n : 'a' + n - 10; } /* --------------------------------------------------------------------------------------------- */ static void dialog_switch_suspend (void *data, void *user_data) { (void) user_data; if (data != mc_current->data) widget_set_state (WIDGET (data), WST_SUSPENDED, TRUE); } /* --------------------------------------------------------------------------------------------- */ static void dialog_switch_goto (GList *dlg) { if (mc_current != dlg) { WDialog *old = DIALOG (mc_current->data); mc_current = dlg; if (old == filemanager) { /* switch from panels to another dialog (editor, viewer, etc) */ dialog_switch_pending = TRUE; dialog_switch_process_pending (); } else { /* switch from editor, viewer, etc to another dialog */ widget_set_state (WIDGET (old), WST_SUSPENDED, TRUE); if (DIALOG (dlg->data) != filemanager) /* switch to another editor, viewer, etc */ /* return to panels before run the required dialog */ dialog_switch_pending = TRUE; else { /* switch to panels */ widget_set_state (WIDGET (filemanager), WST_ACTIVE, TRUE); do_refresh (); } } } } /* --------------------------------------------------------------------------------------------- */ static void dialog_switch_resize (WDialog *d) { if (widget_get_state (WIDGET (d), WST_ACTIVE)) send_message (d, NULL, MSG_RESIZE, 0, NULL); else GROUP (d)->winch_pending = TRUE; } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void dialog_switch_add (WDialog *h) { GList *dlg; dlg = g_list_find (mc_dialogs, h); if (dlg != NULL) mc_current = dlg; else { mc_dialogs = g_list_prepend (mc_dialogs, h); mc_current = mc_dialogs; } /* suspend forced all other screens */ g_list_foreach (mc_dialogs, dialog_switch_suspend, NULL); } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_remove (WDialog *h) { GList *this; if (DIALOG (mc_current->data) == h) this = mc_current; else this = g_list_find (mc_dialogs, h); mc_dialogs = g_list_delete_link (mc_dialogs, this); /* adjust current dialog */ if (top_dlg != NULL) mc_current = g_list_find (mc_dialogs, DIALOG (top_dlg->data)); else mc_current = mc_dialogs; /* resume forced the current screen */ if (mc_current != NULL) widget_set_state (WIDGET (mc_current->data), WST_ACTIVE, TRUE); } /* --------------------------------------------------------------------------------------------- */ size_t dialog_switch_num (void) { return g_list_length (mc_dialogs); } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_next (void) { GList *next; if (mc_global.midnight_shutdown || mc_current == NULL) return; next = g_list_next (mc_current); if (next == NULL) next = mc_dialogs; dialog_switch_goto (next); } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_prev (void) { GList *prev; if (mc_global.midnight_shutdown || mc_current == NULL) return; prev = g_list_previous (mc_current); if (prev == NULL) prev = g_list_last (mc_dialogs); dialog_switch_goto (prev); } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_list (void) { const size_t dlg_num = g_list_length (mc_dialogs); int lines, cols; Listbox *listbox; GList *h, *selected; int i = 0; if (mc_global.midnight_shutdown || mc_current == NULL) return; lines = MIN ((size_t) (LINES * 2 / 3), dlg_num); cols = COLS * 2 / 3; listbox = listbox_window_new (lines, cols, _ ("Screens"), "[Screen selector]"); for (h = mc_dialogs; h != NULL; h = g_list_next (h)) { WDialog *dlg = DIALOG (h->data); char *title; if (dlg->get_title != NULL) title = dlg->get_title (dlg, WIDGET (listbox->list)->rect.cols - 2); else title = g_strdup (""); listbox_add_item_take (listbox->list, LISTBOX_APPEND_BEFORE, get_hotkey (i++), title, h, FALSE); } selected = listbox_run_with_data (listbox, mc_current); if (selected != NULL) dialog_switch_goto (selected); } /* --------------------------------------------------------------------------------------------- */ int dialog_switch_process_pending (void) { int ret = 0; while (dialog_switch_pending) { WDialog *h = DIALOG (mc_current->data); Widget *wh = WIDGET (h); dialog_switch_pending = FALSE; widget_set_state (wh, WST_SUSPENDED, TRUE); ret = dlg_run (h); if (widget_get_state (wh, WST_CLOSED)) { widget_destroy (wh); /* return to panels */ if (mc_global.mc_run_mode == MC_RUN_FULL) { mc_current = g_list_find (mc_dialogs, filemanager); mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "update_panels", NULL); } } } repaint_screen (); return ret; } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_got_winch (void) { GList *dlg; for (dlg = mc_dialogs; dlg != NULL; dlg = g_list_next (dlg)) if (dlg != mc_current) GROUP (dlg->data)->winch_pending = TRUE; } /* --------------------------------------------------------------------------------------------- */ void dialog_switch_shutdown (void) { while (mc_dialogs != NULL) { WDialog *dlg = DIALOG (mc_dialogs->data); dlg_run (dlg); widget_destroy (WIDGET (dlg)); } } /* --------------------------------------------------------------------------------------------- */ void do_refresh (void) { GList *d = top_dlg; if (fast_refresh) { if (d != NULL) widget_draw (WIDGET (d->data)); } else { /* Search first fullscreen dialog */ for (; d != NULL; d = g_list_next (d)) if ((WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0) break; /* when small dialog (i.e. error message) is created first, there is no fullscreen dialog in the stack */ if (d == NULL) d = g_list_last (top_dlg); /* back to top dialog */ for (; d != NULL; d = g_list_previous (d)) widget_draw (WIDGET (d->data)); } } /* --------------------------------------------------------------------------------------------- */ void repaint_screen (void) { do_refresh (); tty_refresh (); } /* --------------------------------------------------------------------------------------------- */ void mc_refresh (void) { #ifdef ENABLE_BACKGROUND if (mc_global.we_are_background) return; #endif /* ENABLE_BACKGROUND */ if (!tty_got_winch ()) tty_refresh (); else { /* if winch was caugth, we should do not only redraw screen, but reposition/resize all */ dialog_change_screen_size (); } } /* --------------------------------------------------------------------------------------------- */ void dialog_change_screen_size (void) { GList *d; tty_flush_winch (); tty_change_screen_size (); #ifdef HAVE_SLANG tty_keypad (TRUE); tty_nodelay (FALSE); #endif /* Inform all suspending dialogs */ dialog_switch_got_winch (); /* Inform all running dialogs from first to last */ for (d = g_list_last (top_dlg); d != NULL; d = g_list_previous (d)) dialog_switch_resize (DIALOG (d->data)); /* Now, force the redraw */ repaint_screen (); } /* --------------------------------------------------------------------------------------------- */