Merge branch '4580_mcedit_segfault'
[midnight-commander.git] / src / filemanager / layout.c
blob1f96c2dd1467b17a1ce575be6b115b7801b65bb1
1 /*
2 Panel layout module for the Midnight Commander
4 Copyright (C) 1995-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Janne Kukonlehto, 1995
9 Miguel de Icaza, 1995
10 Andrew Borodin <aborodin@vmail.ru>, 2011-2022
11 Slava Zanko <slavazanko@gmail.com>, 2013
12 Avi Kelman <patcherton.fixesthings@gmail.com>, 2013
14 This file is part of the Midnight Commander.
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 /** \file layout.c
31 * \brief Source: panel layout module
34 #include <config.h>
36 #include <pwd.h> /* for username in xterm title */
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <unistd.h>
43 #include "lib/global.h"
44 #include "lib/tty/tty.h"
45 #include "lib/skin.h"
46 #include "lib/tty/key.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/mcconfig.h"
49 #include "lib/vfs/vfs.h" /* vfs_get_cwd () */
50 #include "lib/strutil.h"
51 #include "lib/widget.h"
52 #include "lib/event.h"
53 #include "lib/util.h" /* mc_time_elapsed() */
55 #include "src/consaver/cons.saver.h"
56 #include "src/viewer/mcviewer.h" /* The view widget */
57 #include "src/setup.h"
58 #ifdef ENABLE_SUBSHELL
59 #include "src/subshell/subshell.h"
60 #endif
62 #include "command.h"
63 #include "filemanager.h"
64 #include "tree.h"
65 /* Needed for the extern declarations of integer parameters */
66 #include "dir.h"
67 #include "layout.h"
68 #include "info.h" /* The Info widget */
70 /*** global variables ****************************************************************************/
72 panels_layout_t panels_layout = {
73 /* Set if the panels are split horizontally */
74 .horizontal_split = FALSE,
76 /* vertical split */
77 .vertical_equal = TRUE,
78 .left_panel_size = 0,
80 /* horizontal split */
81 .horizontal_equal = TRUE,
82 .top_panel_size = 0
85 /* Controls the display of the rotating dash on the verbose mode */
86 gboolean nice_rotating_dash = TRUE;
88 /* The number of output lines shown (if available) */
89 int output_lines = 0;
91 /* Set if the command prompt is to be displayed */
92 gboolean command_prompt = TRUE;
94 /* Set if the main menu is visible */
95 gboolean menubar_visible = TRUE;
97 /* Set to show current working dir in xterm window title */
98 gboolean xterm_title = TRUE;
100 /* Set to show free space on device assigned to current directory */
101 gboolean free_space = TRUE;
103 /* The starting line for the output of the subprogram */
104 int output_start_y = 0;
106 int ok_to_refresh = 1;
108 /*** file scope macro definitions ****************************************************************/
110 /* The maximum number of views managed by the create_panel routine */
111 /* Must be at least two (for current and other). Please note that until */
112 /* Janne gets around this, we will only manage two of them :-) */
113 #define MAX_VIEWS 2
115 /* Width 12 for a wee Quick (Hex) View */
116 #define MINWIDTH 12
117 #define MINHEIGHT 5
119 #define B_2LEFT B_USER
120 #define B_2RIGHT (B_USER + 1)
121 #define B_PLUS (B_USER + 2)
122 #define B_MINUS (B_USER + 3)
124 #define LAYOUT_OPTIONS_COUNT G_N_ELEMENTS (check_options)
126 /*** file scope type declarations ****************************************************************/
128 typedef struct
130 gboolean menubar_visible;
131 gboolean command_prompt;
132 gboolean keybar_visible;
133 gboolean message_visible;
134 gboolean xterm_title;
135 gboolean free_space;
136 int output_lines;
137 } layout_t;
139 /*** forward declarations (file scope functions) *************************************************/
141 /*** file scope variables ************************************************************************/
143 static struct
145 panel_view_mode_t type;
146 Widget *widget;
147 char *last_saved_dir; /* last view_list working directory */
148 } panels[MAX_VIEWS] = {
149 /* *INDENT-OFF* */
150 /* init MAX_VIEWS items */
151 { view_listing, NULL, NULL},
152 { view_listing, NULL, NULL}
153 /* *INDENT-ON* */
156 static layout_t old_layout;
157 static panels_layout_t old_panels_layout;
159 static gboolean equal_split;
160 static int _output_lines;
162 static int height;
164 static WRadio *radio_widget;
166 static struct
168 const char *text;
169 gboolean *variable;
170 WCheck *widget;
171 } check_options[] = {
172 /* *INDENT-OFF* */
173 { N_("&Equal split"), &equal_split, NULL },
174 { N_("&Menubar visible"), &menubar_visible, NULL },
175 { N_("Command &prompt"), &command_prompt, NULL },
176 { N_("&Keybar visible"), &mc_global.keybar_visible, NULL },
177 { N_("H&intbar visible"), &mc_global.message_visible, NULL },
178 { N_("&XTerm window title"), &xterm_title, NULL },
179 { N_("&Show free space"), &free_space, NULL }
180 /* *INDENT-ON* */
183 static const char *output_lines_label = NULL;
184 static int output_lines_label_len;
186 static WButton *bleft_widget, *bright_widget;
188 /* --------------------------------------------------------------------------------------------- */
189 /*** file scope functions ************************************************************************/
190 /* --------------------------------------------------------------------------------------------- */
192 /* don't use max() macro to avoid double call of str_term_width1() in widget width calculation */
193 #undef max
195 static int
196 max (int a, int b)
198 return a > b ? a : b;
201 /* --------------------------------------------------------------------------------------------- */
203 static void
204 check_split (panels_layout_t *layout)
206 if (layout->horizontal_split)
208 if (layout->horizontal_equal)
209 layout->top_panel_size = height / 2;
210 else if (layout->top_panel_size < MINHEIGHT)
211 layout->top_panel_size = MINHEIGHT;
212 else if (layout->top_panel_size > height - MINHEIGHT)
213 layout->top_panel_size = height - MINHEIGHT;
215 else
217 int md_cols = CONST_WIDGET (filemanager)->rect.cols;
219 if (layout->vertical_equal)
220 layout->left_panel_size = md_cols / 2;
221 else if (layout->left_panel_size < MINWIDTH)
222 layout->left_panel_size = MINWIDTH;
223 else if (layout->left_panel_size > md_cols - MINWIDTH)
224 layout->left_panel_size = md_cols - MINWIDTH;
228 /* --------------------------------------------------------------------------------------------- */
230 static void
231 update_split (const WDialog *h)
233 /* Check split has to be done before testing if it changed, since
234 it can change due to calling check_split() as well */
235 check_split (&panels_layout);
237 if (panels_layout.horizontal_split)
238 check_options[0].widget->state = panels_layout.horizontal_equal;
239 else
240 check_options[0].widget->state = panels_layout.vertical_equal;
241 widget_draw (WIDGET (check_options[0].widget));
243 tty_setcolor (check_options[0].widget->state ? DISABLED_COLOR : COLOR_NORMAL);
245 widget_gotoyx (h, 6, 5);
246 if (panels_layout.horizontal_split)
247 tty_printf ("%03d", panels_layout.top_panel_size);
248 else
249 tty_printf ("%03d", panels_layout.left_panel_size);
251 widget_gotoyx (h, 6, 17);
252 if (panels_layout.horizontal_split)
253 tty_printf ("%03d", height - panels_layout.top_panel_size);
254 else
255 tty_printf ("%03d", CONST_WIDGET (filemanager)->rect.cols - panels_layout.left_panel_size);
257 widget_gotoyx (h, 6, 12);
258 tty_print_char ('=');
261 /* --------------------------------------------------------------------------------------------- */
263 static int
264 b_left_right_cback (WButton *button, int action)
266 (void) action;
268 if (button == bright_widget)
270 if (panels_layout.horizontal_split)
271 panels_layout.top_panel_size++;
272 else
273 panels_layout.left_panel_size++;
275 else
277 if (panels_layout.horizontal_split)
278 panels_layout.top_panel_size--;
279 else
280 panels_layout.left_panel_size--;
283 update_split (DIALOG (WIDGET (button)->owner));
284 layout_change ();
285 do_refresh ();
286 return 0;
289 /* --------------------------------------------------------------------------------------------- */
291 static int
292 bplus_cback (WButton *button, int action)
294 (void) button;
295 (void) action;
297 if (_output_lines < 99)
298 _output_lines++;
299 return 0;
302 /* --------------------------------------------------------------------------------------------- */
304 static int
305 bminus_cback (WButton *button, int action)
307 (void) button;
308 (void) action;
310 if (_output_lines > 0)
311 _output_lines--;
312 return 0;
315 /* --------------------------------------------------------------------------------------------- */
317 static cb_ret_t
318 layout_bg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
320 switch (msg)
322 case MSG_DRAW:
323 frame_callback (w, NULL, MSG_DRAW, 0, NULL);
325 old_layout.output_lines = -1;
327 update_split (DIALOG (w->owner));
329 if (old_layout.output_lines != _output_lines)
331 old_layout.output_lines = _output_lines;
332 tty_setcolor (mc_global.tty.console_flag != '\0' ? COLOR_NORMAL : DISABLED_COLOR);
333 widget_gotoyx (w, 9, 5);
334 tty_print_string (output_lines_label);
335 widget_gotoyx (w, 9, 5 + 3 + output_lines_label_len);
336 tty_printf ("%02d", _output_lines);
338 return MSG_HANDLED;
340 default:
341 return frame_callback (w, sender, msg, parm, data);
345 /* --------------------------------------------------------------------------------------------- */
347 static cb_ret_t
348 layout_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
350 WDialog *h = DIALOG (w);
352 switch (msg)
354 case MSG_POST_KEY:
356 const Widget *mw = CONST_WIDGET (filemanager);
357 gboolean _menubar_visible, _command_prompt, _keybar_visible, _message_visible;
359 _menubar_visible = check_options[1].widget->state;
360 _command_prompt = check_options[2].widget->state;
361 _keybar_visible = check_options[3].widget->state;
362 _message_visible = check_options[4].widget->state;
364 if (mc_global.tty.console_flag == '\0')
365 height =
366 mw->rect.lines - (_keybar_visible ? 1 : 0) - (_command_prompt ? 1 : 0) -
367 (_menubar_visible ? 1 : 0) - _output_lines - (_message_visible ? 1 : 0);
368 else
370 int minimum;
372 if (_output_lines < 0)
373 _output_lines = 0;
374 height =
375 mw->rect.lines - (_keybar_visible ? 1 : 0) - (_command_prompt ? 1 : 0) -
376 (_menubar_visible ? 1 : 0) - _output_lines - (_message_visible ? 1 : 0);
377 minimum = MINHEIGHT * (1 + (panels_layout.horizontal_split ? 1 : 0));
378 if (height < minimum)
380 _output_lines -= minimum - height;
381 height = minimum;
385 if (old_layout.output_lines != _output_lines)
387 old_layout.output_lines = _output_lines;
388 tty_setcolor (mc_global.tty.console_flag != '\0' ? COLOR_NORMAL : DISABLED_COLOR);
389 widget_gotoyx (h, 9, 5 + 3 + output_lines_label_len);
390 tty_printf ("%02d", _output_lines);
393 return MSG_HANDLED;
395 case MSG_NOTIFY:
396 if (sender == WIDGET (radio_widget))
398 if ((panels_layout.horizontal_split ? 1 : 0) == radio_widget->sel)
399 update_split (h);
400 else
402 int eq;
404 panels_layout.horizontal_split = radio_widget->sel != 0;
406 if (panels_layout.horizontal_split)
408 eq = panels_layout.horizontal_equal;
409 if (eq)
410 panels_layout.top_panel_size = height / 2;
412 else
414 eq = panels_layout.vertical_equal;
415 if (eq)
416 panels_layout.left_panel_size = CONST_WIDGET (filemanager)->rect.cols / 2;
419 widget_disable (WIDGET (bleft_widget), eq);
420 widget_disable (WIDGET (bright_widget), eq);
422 update_split (h);
423 layout_change ();
424 do_refresh ();
427 return MSG_HANDLED;
430 if (sender == WIDGET (check_options[0].widget))
432 gboolean eq;
434 if (panels_layout.horizontal_split)
436 panels_layout.horizontal_equal = check_options[0].widget->state;
437 eq = panels_layout.horizontal_equal;
439 else
441 panels_layout.vertical_equal = check_options[0].widget->state;
442 eq = panels_layout.vertical_equal;
445 widget_disable (WIDGET (bleft_widget), eq);
446 widget_disable (WIDGET (bright_widget), eq);
448 update_split (h);
449 layout_change ();
450 do_refresh ();
452 return MSG_HANDLED;
456 gboolean ok = TRUE;
458 if (sender == WIDGET (check_options[1].widget))
459 menubar_visible = check_options[1].widget->state;
460 else if (sender == WIDGET (check_options[2].widget))
461 command_prompt = check_options[2].widget->state;
462 else if (sender == WIDGET (check_options[3].widget))
463 mc_global.keybar_visible = check_options[3].widget->state;
464 else if (sender == WIDGET (check_options[4].widget))
465 mc_global.message_visible = check_options[4].widget->state;
466 else if (sender == WIDGET (check_options[5].widget))
467 xterm_title = check_options[5].widget->state;
468 else if (sender == WIDGET (check_options[6].widget))
469 free_space = check_options[6].widget->state;
470 else
471 ok = FALSE;
473 if (ok)
475 update_split (h);
476 layout_change ();
477 do_refresh ();
478 return MSG_HANDLED;
482 return MSG_NOT_HANDLED;
484 default:
485 return dlg_default_callback (w, sender, msg, parm, data);
489 /* --------------------------------------------------------------------------------------------- */
491 static WDialog *
492 layout_dlg_create (void)
494 WDialog *layout_dlg;
495 WGroup *g;
496 int l1 = 0, width;
497 int b1, b2, b;
498 size_t i;
500 const char *title1 = N_("Panel split");
501 const char *title2 = N_("Console output");
502 const char *title3 = N_("Other options");
504 const char *s_split_direction[2] = {
505 N_("&Vertical"),
506 N_("&Horizontal")
509 const char *ok_button = N_("&OK");
510 const char *cancel_button = N_("&Cancel");
512 output_lines_label = _("Output lines:");
514 #ifdef ENABLE_NLS
516 static gboolean i18n = FALSE;
518 title1 = _(title1);
519 title2 = _(title2);
520 title3 = _(title3);
522 i = G_N_ELEMENTS (s_split_direction);
523 while (i-- != 0)
524 s_split_direction[i] = _(s_split_direction[i]);
526 if (!i18n)
528 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
529 check_options[i].text = _(check_options[i].text);
530 i18n = TRUE;
533 ok_button = _(ok_button);
534 cancel_button = _(cancel_button);
536 #endif
538 /* radiobuttons */
539 i = G_N_ELEMENTS (s_split_direction);
540 while (i-- != 0)
541 l1 = max (l1, str_term_width1 (s_split_direction[i]) + 7);
542 /* checkboxes */
543 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
544 l1 = max (l1, str_term_width1 (check_options[i].text) + 7);
545 /* groupboxes */
546 l1 = max (l1, str_term_width1 (title1) + 4);
547 l1 = max (l1, str_term_width1 (title2) + 4);
548 l1 = max (l1, str_term_width1 (title3) + 4);
549 /* label + "+"/"-" buttons */
550 output_lines_label_len = str_term_width1 (output_lines_label);
551 l1 = max (l1, output_lines_label_len + 12);
552 /* buttons */
553 b1 = str_term_width1 (ok_button) + 5; /* default button */
554 b2 = str_term_width1 (cancel_button) + 3;
555 b = b1 + b2 + 1;
556 /* dialog width */
557 width = max (l1 * 2 + 7, b);
559 layout_dlg =
560 dlg_create (TRUE, 0, 0, 15, width, WPOS_CENTER, FALSE, dialog_colors, layout_callback, NULL,
561 "[Layout]", _("Layout"));
562 g = GROUP (layout_dlg);
564 /* draw background */
565 layout_dlg->bg->callback = layout_bg_callback;
567 #define XTRACT(i) (*check_options[i].variable != 0), check_options[i].text
569 /* "Panel split" groupbox */
570 group_add_widget (g, groupbox_new (2, 3, 6, l1, title1));
572 radio_widget = radio_new (3, 5, 2, s_split_direction);
573 radio_widget->sel = panels_layout.horizontal_split ? 1 : 0;
574 group_add_widget (g, radio_widget);
576 check_options[0].widget = check_new (5, 5, XTRACT (0));
577 group_add_widget (g, check_options[0].widget);
579 equal_split = panels_layout.horizontal_split ?
580 panels_layout.horizontal_equal : panels_layout.vertical_equal;
582 bleft_widget = button_new (6, 8, B_2LEFT, NARROW_BUTTON, "&<", b_left_right_cback);
583 widget_disable (WIDGET (bleft_widget), equal_split);
584 group_add_widget (g, bleft_widget);
586 bright_widget = button_new (6, 14, B_2RIGHT, NARROW_BUTTON, "&>", b_left_right_cback);
587 widget_disable (WIDGET (bright_widget), equal_split);
588 group_add_widget (g, bright_widget);
590 /* "Console output" groupbox */
592 widget_state_t disabled;
593 Widget *w;
595 disabled = mc_global.tty.console_flag != '\0' ? 0 : WST_DISABLED;
597 w = WIDGET (groupbox_new (8, 3, 3, l1, title2));
598 w->state |= disabled;
599 group_add_widget (g, w);
601 w = WIDGET (button_new (9, output_lines_label_len + 5, B_PLUS,
602 NARROW_BUTTON, "&+", bplus_cback));
603 w->state |= disabled;
604 group_add_widget (g, w);
606 w = WIDGET (button_new (9, output_lines_label_len + 5 + 5, B_MINUS,
607 NARROW_BUTTON, "&-", bminus_cback));
608 w->state |= disabled;
609 group_add_widget (g, w);
612 /* "Other options" groupbox */
613 group_add_widget (g, groupbox_new (2, 4 + l1, 9, l1, title3));
615 for (i = 1; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
617 check_options[i].widget = check_new (i + 2, 6 + l1, XTRACT (i));
618 group_add_widget (g, check_options[i].widget);
621 #undef XTRACT
623 group_add_widget (g, hline_new (11, -1, -1));
624 /* buttons */
625 group_add_widget (g, button_new (12, (width - b) / 2, B_ENTER, DEFPUSH_BUTTON, ok_button, 0));
626 group_add_widget (g,
627 button_new (12, (width - b) / 2 + b1 + 1, B_CANCEL, NORMAL_BUTTON,
628 cancel_button, 0));
630 widget_select (WIDGET (radio_widget));
632 return layout_dlg;
635 /* --------------------------------------------------------------------------------------------- */
637 static void
638 panel_do_cols (int idx)
640 if (get_panel_type (idx) == view_listing)
641 set_panel_formats (PANEL (panels[idx].widget));
642 else
643 panel_update_cols (panels[idx].widget, frame_half);
646 /* --------------------------------------------------------------------------------------------- */
647 /** Save current list_view widget directory into panel */
649 static Widget *
650 restore_into_right_dir_panel (int idx, gboolean last_was_panel, const WRect *r)
652 WPanel *new_widget;
653 const char *p_name;
655 p_name = get_nth_panel_name (idx);
657 if (last_was_panel)
659 vfs_path_t *saved_dir_vpath;
661 saved_dir_vpath = vfs_path_from_str (panels[idx].last_saved_dir);
662 new_widget = panel_sized_with_dir_new (p_name, r, saved_dir_vpath);
663 vfs_path_free (saved_dir_vpath, TRUE);
665 else
666 new_widget = panel_sized_new (p_name, r);
668 return WIDGET (new_widget);
671 /* --------------------------------------------------------------------------------------------- */
673 static void
674 layout_save (void)
676 old_layout.menubar_visible = menubar_visible;
677 old_layout.command_prompt = command_prompt;
678 old_layout.keybar_visible = mc_global.keybar_visible;
679 old_layout.message_visible = mc_global.message_visible;
680 old_layout.xterm_title = xterm_title;
681 old_layout.free_space = free_space;
682 old_layout.output_lines = -1;
684 _output_lines = output_lines;
686 old_panels_layout = panels_layout;
689 /* --------------------------------------------------------------------------------------------- */
691 static void
692 layout_restore (void)
694 menubar_visible = old_layout.menubar_visible;
695 command_prompt = old_layout.command_prompt;
696 mc_global.keybar_visible = old_layout.keybar_visible;
697 mc_global.message_visible = old_layout.message_visible;
698 xterm_title = old_layout.xterm_title;
699 free_space = old_layout.free_space;
700 output_lines = old_layout.output_lines;
702 panels_layout = old_panels_layout;
705 /* --------------------------------------------------------------------------------------------- */
706 /*** public functions ****************************************************************************/
707 /* --------------------------------------------------------------------------------------------- */
709 void
710 layout_change (void)
712 setup_panels ();
713 /* update the main menu, because perhaps there was a change in the way
714 how the panel are split (horizontal/vertical),
715 and a change of menu visibility. */
716 update_menu ();
717 load_hint (TRUE);
720 /* --------------------------------------------------------------------------------------------- */
722 void
723 layout_box (void)
725 WDialog *layout_dlg;
727 layout_save ();
729 layout_dlg = layout_dlg_create ();
731 if (dlg_run (layout_dlg) == B_ENTER)
733 size_t i;
735 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
736 if (check_options[i].widget != NULL)
737 *check_options[i].variable = check_options[i].widget->state;
739 output_lines = _output_lines;
741 else
742 layout_restore ();
744 widget_destroy (WIDGET (layout_dlg));
745 layout_change ();
746 do_refresh ();
749 /* --------------------------------------------------------------------------------------------- */
751 void
752 panel_update_cols (Widget *widget, panel_display_t frame_size)
754 const Widget *mw = CONST_WIDGET (filemanager);
755 int cols, x;
757 /* don't touch panel if it is not in dialog yet */
758 /* if panel is not in dialog it is not in widgets list
759 and cannot be compared with get_panel_widget() result */
760 if (widget->owner == NULL)
761 return;
763 if (panels_layout.horizontal_split)
765 widget->rect.cols = mw->rect.cols;
766 return;
769 if (frame_size == frame_full)
771 cols = mw->rect.cols;
772 x = mw->rect.x;
774 else if (widget == get_panel_widget (0))
776 cols = panels_layout.left_panel_size;
777 x = mw->rect.x;
779 else
781 cols = mw->rect.cols - panels_layout.left_panel_size;
782 x = mw->rect.x + panels_layout.left_panel_size;
785 widget->rect.cols = cols;
786 widget->rect.x = x;
789 /* --------------------------------------------------------------------------------------------- */
791 void
792 setup_panels (void)
794 /* File manager screen layout:
796 * +---------------------------------------------------------------+
797 * | Menu bar |
798 * +-------------------------------+-------------------------------+
799 * | | |
800 * | | |
801 * | | |
802 * | | |
803 * | Left panel | Right panel |
804 * | | |
805 * | | |
806 * | | |
807 * | | |
808 * +-------------------------------+-------------------------------+
809 * | Hint (message) bar |
810 * +---------------------------------------------------------------+
811 * | |
812 * | Console content |
813 * | |
814 * +--------+------------------------------------------------------+
815 * | Prompt | Command line |
816 * | Key (button) bar |
817 * +--------+------------------------------------------------------+
820 Widget *mw = WIDGET (filemanager);
821 const WRect *r = &CONST_WIDGET (mw)->rect;
822 int start_y;
823 gboolean active;
824 WRect rb;
826 active = widget_get_state (mw, WST_ACTIVE);
828 /* lock the group to avoid many redraws */
829 if (active)
830 widget_set_state (mw, WST_SUSPENDED, TRUE);
832 /* initial height of panels */
833 height =
834 r->lines - (menubar_visible ? 1 : 0) - (mc_global.message_visible ? 1 : 0) -
835 (command_prompt ? 1 : 0) - (mc_global.keybar_visible ? 1 : 0);
837 if (mc_global.tty.console_flag != '\0')
839 int minimum;
841 if (output_lines < 0)
842 output_lines = 0;
843 else
844 height -= output_lines;
845 minimum = MINHEIGHT * (1 + (panels_layout.horizontal_split ? 1 : 0));
846 if (height < minimum)
848 output_lines -= minimum - height;
849 height = minimum;
853 rb = *r;
854 rb.lines = 1;
855 widget_set_size_rect (WIDGET (the_menubar), &rb);
856 widget_set_visibility (WIDGET (the_menubar), menubar_visible);
858 check_split (&panels_layout);
859 start_y = r->y + (menubar_visible ? 1 : 0);
861 /* update columns first... */
862 panel_do_cols (0);
863 panel_do_cols (1);
865 /* ...then rows and origin */
866 if (panels_layout.horizontal_split)
868 widget_set_size (panels[0].widget, start_y, r->x, panels_layout.top_panel_size,
869 panels[0].widget->rect.cols);
870 widget_set_size (panels[1].widget, start_y + panels_layout.top_panel_size, r->x,
871 height - panels_layout.top_panel_size, panels[1].widget->rect.cols);
873 else
875 widget_set_size (panels[0].widget, start_y, r->x, height, panels[0].widget->rect.cols);
876 widget_set_size (panels[1].widget, start_y, panels[1].widget->rect.x, height,
877 panels[1].widget->rect.cols);
880 widget_set_size (WIDGET (the_hint), height + start_y, r->x, 1, r->cols);
881 widget_set_visibility (WIDGET (the_hint), mc_global.message_visible);
883 /* Output window */
884 if (mc_global.tty.console_flag != '\0' && output_lines != 0)
886 unsigned char end_line;
888 end_line = r->lines - (mc_global.keybar_visible ? 1 : 0) - 1;
889 output_start_y = end_line - (command_prompt ? 1 : 0) - output_lines + 1;
890 show_console_contents (output_start_y, end_line - output_lines, end_line);
893 if (command_prompt)
895 #ifdef ENABLE_SUBSHELL
896 if (!mc_global.tty.use_subshell || !do_load_prompt ())
897 #endif
898 setup_cmdline ();
900 else
902 /* make invisible */
903 widget_hide (WIDGET (cmdline));
904 widget_hide (WIDGET (the_prompt));
907 rb = *r;
908 rb.y = r->lines - 1;
909 rb.lines = 1;
910 widget_set_size_rect (WIDGET (the_bar), &rb);
911 widget_set_visibility (WIDGET (the_bar), mc_global.keybar_visible);
913 update_xterm_title_path ();
914 update_terminal_cwd ();
916 /* unlock */
917 if (active)
919 widget_set_state (mw, WST_ACTIVE, TRUE);
920 widget_draw (mw);
924 /* --------------------------------------------------------------------------------------------- */
926 void
927 panels_split_equal (void)
929 if (panels_layout.horizontal_split)
930 panels_layout.horizontal_equal = TRUE;
931 else
932 panels_layout.vertical_equal = TRUE;
934 layout_change ();
935 do_refresh ();
938 /* --------------------------------------------------------------------------------------------- */
940 void
941 panels_split_more (void)
943 if (panels_layout.horizontal_split)
945 panels_layout.horizontal_equal = FALSE;
946 panels_layout.top_panel_size++;
948 else
950 panels_layout.vertical_equal = FALSE;
951 panels_layout.left_panel_size++;
954 layout_change ();
957 /* --------------------------------------------------------------------------------------------- */
959 void
960 panels_split_less (void)
962 if (panels_layout.horizontal_split)
964 panels_layout.horizontal_equal = FALSE;
965 panels_layout.top_panel_size--;
967 else
969 panels_layout.vertical_equal = FALSE;
970 panels_layout.left_panel_size--;
973 layout_change ();
976 /* --------------------------------------------------------------------------------------------- */
978 void
979 setup_cmdline (void)
981 const Widget *mw = CONST_WIDGET (filemanager);
982 const WRect *r = &mw->rect;
983 int prompt_width;
984 int y;
985 char *tmp_prompt = (char *) mc_prompt;
987 if (!command_prompt)
988 return;
990 #ifdef ENABLE_SUBSHELL
991 if (mc_global.tty.use_subshell)
993 /* Workaround: avoid crash on FreeBSD (see ticket #4213 for details) */
994 if (subshell_prompt != NULL)
995 tmp_prompt = g_string_free (subshell_prompt, FALSE);
996 else
997 tmp_prompt = g_strdup (mc_prompt);
998 (void) strip_ctrl_codes (tmp_prompt);
1000 #endif
1002 prompt_width = str_term_width1 (tmp_prompt);
1004 /* Check for prompts too big */
1005 if (r->cols > 8 && prompt_width > r->cols - 8)
1007 int prompt_len;
1009 prompt_width = r->cols - 8;
1010 prompt_len = str_offset_to_pos (tmp_prompt, prompt_width);
1011 tmp_prompt[prompt_len] = '\0';
1014 #ifdef ENABLE_SUBSHELL
1015 if (mc_global.tty.use_subshell)
1017 subshell_prompt = g_string_new_take (tmp_prompt);
1018 mc_prompt = subshell_prompt->str;
1020 #endif
1022 y = r->lines - 1 - (mc_global.keybar_visible ? 1 : 0);
1024 widget_set_size (WIDGET (the_prompt), y, r->x, 1, prompt_width);
1025 label_set_text (the_prompt, mc_prompt);
1026 widget_set_size (WIDGET (cmdline), y, r->x + prompt_width, 1, r->cols - prompt_width);
1028 widget_show (WIDGET (the_prompt));
1029 widget_show (WIDGET (cmdline));
1032 /* --------------------------------------------------------------------------------------------- */
1034 void
1035 use_dash (gboolean flag)
1037 if (flag)
1038 ok_to_refresh++;
1039 else
1040 ok_to_refresh--;
1043 /* --------------------------------------------------------------------------------------------- */
1045 void
1046 set_hintbar (const char *str)
1048 label_set_text (the_hint, str);
1049 if (ok_to_refresh > 0)
1050 mc_refresh ();
1053 /* --------------------------------------------------------------------------------------------- */
1055 void
1056 rotate_dash (gboolean show)
1058 static gint64 timestamp = 0;
1059 /* update with 10 FPS rate */
1060 static const gint64 delay = G_USEC_PER_SEC / 10;
1062 const Widget *w = CONST_WIDGET (filemanager);
1064 if (!nice_rotating_dash || (ok_to_refresh <= 0))
1065 return;
1067 if (show && !mc_time_elapsed (&timestamp, delay))
1068 return;
1070 widget_gotoyx (w, menubar_visible ? 1 : 0, w->rect.cols - 1);
1071 tty_setcolor (NORMAL_COLOR);
1073 if (!show)
1074 tty_print_alt_char (ACS_URCORNER, FALSE);
1075 else
1077 static const char rotating_dash[4] = "|/-\\";
1078 static size_t pos = 0;
1080 tty_print_char (rotating_dash[pos]);
1081 pos = (pos + 1) % sizeof (rotating_dash);
1084 mc_refresh ();
1087 /* --------------------------------------------------------------------------------------------- */
1089 const char *
1090 get_nth_panel_name (int num)
1092 if (num == 0)
1093 return "New Left Panel";
1095 if (num == 1)
1096 return "New Right Panel";
1099 static char buffer[BUF_SMALL];
1101 g_snprintf (buffer, sizeof (buffer), "%ith Panel", num);
1102 return buffer;
1106 /* --------------------------------------------------------------------------------------------- */
1107 /* I wonder if I should start to use the folding mode than Dugan uses */
1108 /* */
1109 /* This is the centralized managing of the panel display types */
1110 /* This routine takes care of destroying and creating new widgets */
1111 /* Please note that it could manage MAX_VIEWS, not just left and right */
1112 /* Currently nothing in the code takes advantage of this and has hard- */
1113 /* coded values for two panels only */
1115 /* Set the num-th panel to the view type: type */
1116 /* This routine also keeps at least one WPanel object in the screen */
1117 /* since a lot of routines depend on the current_panel variable */
1119 void
1120 create_panel (int num, panel_view_mode_t type)
1122 WRect r = { 0, 0, 0, 0 };
1123 unsigned int the_other = 0; /* Index to the other panel */
1124 Widget *new_widget = NULL, *old_widget = NULL;
1125 panel_view_mode_t old_type = view_listing;
1127 if (num >= MAX_VIEWS)
1129 fprintf (stderr, "Cannot allocate more that %d views\n", MAX_VIEWS);
1130 abort ();
1132 /* Check that we will have a WPanel * at least */
1133 if (type != view_listing)
1135 the_other = num == 0 ? 1 : 0;
1137 if (panels[the_other].type != view_listing)
1138 return;
1141 /* Get rid of it */
1142 if (panels[num].widget != NULL)
1144 Widget *w = panels[num].widget;
1145 WPanel *panel = PANEL (w);
1147 r = w->rect;
1148 old_widget = w;
1149 old_type = panels[num].type;
1151 if (old_type == view_listing && panel->frame_size == frame_full && type != view_listing)
1153 int md_cols = CONST_WIDGET (filemanager)->rect.cols;
1155 if (panels_layout.horizontal_split)
1157 r.cols = md_cols;
1158 r.x = 0;
1160 else
1162 r.cols = md_cols - panels_layout.left_panel_size;
1163 if (num == 1)
1164 r.x = panels_layout.left_panel_size;
1169 /* Restoring saved path from panels.ini for nonlist panel */
1170 /* when it's first creation (for example view_info) */
1171 if (old_widget == NULL && type != view_listing)
1172 panels[num].last_saved_dir = vfs_get_cwd ();
1174 switch (type)
1176 case view_nothing:
1177 case view_listing:
1179 gboolean last_was_panel;
1181 last_was_panel = old_widget != NULL && get_panel_type (num) != view_listing;
1182 new_widget = restore_into_right_dir_panel (num, last_was_panel, &r);
1183 break;
1186 case view_info:
1187 new_widget = WIDGET (info_new (&r));
1188 break;
1190 case view_tree:
1191 new_widget = WIDGET (tree_new (&r, TRUE));
1192 break;
1194 case view_quick:
1196 WPanel *the_other_panel;
1197 const char *file_name = "";
1199 new_widget = WIDGET (mcview_new (&r, TRUE));
1200 the_other_panel = PANEL (panels[the_other].widget);
1201 if (the_other_panel != NULL)
1202 file_name = panel_current_entry (the_other_panel)->fname->str;
1204 mcview_load ((WView *) new_widget, 0, file_name, 0, 0, 0);
1205 break;
1208 default:
1209 break;
1212 if (type != view_listing)
1213 /* Must save dir, for restoring after change type to */
1214 /* view_listing */
1215 save_panel_dir (num);
1217 panels[num].type = type;
1218 panels[num].widget = new_widget;
1220 /* We use replace to keep the circular list of the dialog in the */
1221 /* same state. Maybe we could just kill it and then replace it */
1222 if (old_widget != NULL)
1224 if (old_type == view_listing)
1226 /* save and write directory history of panel
1227 * ... and other histories of filemanager */
1228 dlg_save_history (filemanager);
1231 widget_replace (old_widget, new_widget);
1234 if (type == view_listing)
1236 WPanel *panel = PANEL (new_widget);
1238 /* if existing panel changed type to view_listing, then load history */
1239 if (old_widget != NULL)
1241 ev_history_load_save_t event_data = { NULL, new_widget };
1243 mc_event_raise (filemanager->event_group, MCEVENT_HISTORY_LOAD, &event_data);
1246 if (num == 0)
1247 left_panel = panel;
1248 else
1249 right_panel = panel;
1251 /* forced update format after set new sizes */
1252 set_panel_formats (panel);
1255 if (type == view_tree)
1256 the_tree = (WTree *) new_widget;
1258 /* Prevent current_panel's value from becoming invalid.
1259 * It's just a quick hack to prevent segfaults. Comment out and
1260 * try following:
1261 * - select left panel
1262 * - invoke menu left/tree
1263 * - as long as you stay in the left panel almost everything that uses
1264 * current_panel causes segfault, e.g. C-Enter, C-x c, ...
1266 if ((type != view_listing) && (current_panel == PANEL (old_widget)))
1267 current_panel = num == 0 ? right_panel : left_panel;
1269 g_free (old_widget);
1272 /* --------------------------------------------------------------------------------------------- */
1273 /** This routine is deeply sticked to the two panels idea.
1274 What should it do in more panels. ANSWER - don't use it
1275 in any multiple panels environment. */
1277 void
1278 swap_panels (void)
1280 WPanel *panel1, *panel2;
1281 Widget *tmp_widget;
1283 panel1 = PANEL (panels[0].widget);
1284 panel2 = PANEL (panels[1].widget);
1286 if (panels[0].type == view_listing && panels[1].type == view_listing &&
1287 !mc_config_get_bool (mc_global.main_config, CONFIG_PANELS_SECTION, "simple_swap", FALSE))
1289 WPanel panel;
1291 #define panelswap(x) panel.x = panel1->x; panel1->x = panel2->x; panel2->x = panel.x;
1292 /* Change content and related stuff */
1293 panelswap (dir);
1294 panelswap (active);
1295 panelswap (cwd_vpath);
1296 panelswap (lwd_vpath);
1297 panelswap (marked);
1298 panelswap (dirs_marked);
1299 panelswap (total);
1300 panelswap (top);
1301 panelswap (current);
1302 panelswap (is_panelized);
1303 panelswap (panelized_descr);
1304 panelswap (dir_stat);
1305 #undef panelswap
1307 panel1->quick_search.active = FALSE;
1308 panel2->quick_search.active = FALSE;
1310 if (current_panel == panel1)
1311 current_panel = panel2;
1312 else
1313 current_panel = panel1;
1315 /* if sort options are different -> resort panels */
1316 if (memcmp (&panel1->sort_info, &panel2->sort_info, sizeof (dir_sort_options_t)) != 0)
1318 panel_re_sort (other_panel);
1319 panel_re_sort (current_panel);
1322 if (widget_is_active (panels[0].widget))
1323 widget_select (panels[1].widget);
1324 else if (widget_is_active (panels[1].widget))
1325 widget_select (panels[0].widget);
1327 else
1329 WPanel *tmp_panel;
1330 WRect r;
1331 int tmp_type;
1333 tmp_panel = right_panel;
1334 right_panel = left_panel;
1335 left_panel = tmp_panel;
1337 if (panels[0].type == view_listing)
1339 if (strcmp (panel1->name, get_nth_panel_name (0)) == 0)
1341 g_free (panel1->name);
1342 panel1->name = g_strdup (get_nth_panel_name (1));
1345 if (panels[1].type == view_listing)
1347 if (strcmp (panel2->name, get_nth_panel_name (1)) == 0)
1349 g_free (panel2->name);
1350 panel2->name = g_strdup (get_nth_panel_name (0));
1354 r = panels[0].widget->rect;
1355 panels[0].widget->rect = panels[1].widget->rect;
1356 panels[1].widget->rect = r;
1358 tmp_widget = panels[0].widget;
1359 panels[0].widget = panels[1].widget;
1360 panels[1].widget = tmp_widget;
1361 tmp_type = panels[0].type;
1362 panels[0].type = panels[1].type;
1363 panels[1].type = tmp_type;
1365 /* force update formats because of possible changed sizes */
1366 if (panels[0].type == view_listing)
1367 set_panel_formats (PANEL (panels[0].widget));
1368 if (panels[1].type == view_listing)
1369 set_panel_formats (PANEL (panels[1].widget));
1373 /* --------------------------------------------------------------------------------------------- */
1375 panel_view_mode_t
1376 get_panel_type (int idx)
1378 return panels[idx].type;
1381 /* --------------------------------------------------------------------------------------------- */
1383 Widget *
1384 get_panel_widget (int idx)
1386 return panels[idx].widget;
1389 /* --------------------------------------------------------------------------------------------- */
1392 get_current_index (void)
1394 return (panels[0].widget == WIDGET (current_panel) ? 0 : 1);
1397 /* --------------------------------------------------------------------------------------------- */
1400 get_other_index (void)
1402 return (get_current_index () == 0 ? 1 : 0);
1405 /* --------------------------------------------------------------------------------------------- */
1407 WPanel *
1408 get_other_panel (void)
1410 return PANEL (get_panel_widget (get_other_index ()));
1413 /* --------------------------------------------------------------------------------------------- */
1414 /** Returns the view type for the current panel/view */
1416 panel_view_mode_t
1417 get_current_type (void)
1419 return (panels[0].widget == WIDGET (current_panel) ? panels[0].type : panels[1].type);
1422 /* --------------------------------------------------------------------------------------------- */
1423 /** Returns the view type of the unselected panel */
1425 panel_view_mode_t
1426 get_other_type (void)
1428 return (panels[0].widget == WIDGET (current_panel) ? panels[1].type : panels[0].type);
1431 /* --------------------------------------------------------------------------------------------- */
1432 /** Save current list_view widget directory into panel */
1434 void
1435 save_panel_dir (int idx)
1437 panel_view_mode_t type;
1439 type = get_panel_type (idx);
1440 if (type == view_listing)
1442 WPanel *p;
1444 p = PANEL (get_panel_widget (idx));
1445 if (p != NULL)
1447 g_free (panels[idx].last_saved_dir); /* last path no needed */
1448 /* Because path can be nonlocal */
1449 panels[idx].last_saved_dir = g_strdup (vfs_path_as_str (p->cwd_vpath));
1454 /* --------------------------------------------------------------------------------------------- */
1455 /** Return working dir, if it's view_listing - cwd,
1456 but for other types - last_saved_dir */
1458 char *
1459 get_panel_dir_for (const WPanel *widget)
1461 int i;
1463 for (i = 0; i < MAX_VIEWS; i++)
1464 if (PANEL (get_panel_widget (i)) == widget)
1465 break;
1467 if (i >= MAX_VIEWS)
1468 return g_strdup (".");
1470 if (get_panel_type (i) == view_listing)
1472 vfs_path_t *cwd_vpath;
1474 cwd_vpath = PANEL (get_panel_widget (i))->cwd_vpath;
1475 return g_strdup (vfs_path_as_str (cwd_vpath));
1478 return g_strdup (panels[i].last_saved_dir);
1481 /* --------------------------------------------------------------------------------------------- */
1483 #ifdef ENABLE_SUBSHELL
1484 gboolean
1485 do_load_prompt (void)
1487 gboolean ret = FALSE;
1489 if (!read_subshell_prompt ())
1490 return ret;
1492 /* Don't actually change the prompt if it's invisible */
1493 if (top_dlg != NULL && DIALOG (top_dlg->data) == filemanager && command_prompt)
1495 setup_cmdline ();
1497 /* since the prompt has changed, and we are called from one of the
1498 * tty_get_event channels, the prompt updating does not take place
1499 * automatically: force a cursor update and a screen refresh
1501 widget_update_cursor (WIDGET (filemanager));
1502 mc_refresh ();
1503 ret = TRUE;
1505 update_subshell_prompt = TRUE;
1506 return ret;
1509 /* --------------------------------------------------------------------------------------------- */
1512 load_prompt (int fd, void *unused)
1514 (void) fd;
1515 (void) unused;
1517 if (should_read_new_subshell_prompt)
1518 do_load_prompt ();
1519 else
1520 flush_subshell (0, QUIETLY);
1522 return 0;
1524 #endif /* ENABLE_SUBSHELL */
1526 /* --------------------------------------------------------------------------------------------- */
1528 void
1529 title_path_prepare (char **path, char **login)
1531 char host[BUF_TINY];
1532 struct passwd *pw = NULL;
1533 int res = 0;
1535 *path =
1536 vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
1538 res = gethostname (host, sizeof (host));
1539 if (res != 0)
1540 host[0] = '\0';
1541 else
1542 host[sizeof (host) - 1] = '\0';
1544 pw = getpwuid (getuid ());
1545 if (pw != NULL)
1546 *login = g_strdup_printf ("%s@%s", pw->pw_name, host);
1547 else
1548 *login = g_strdup (host);
1551 /* --------------------------------------------------------------------------------------------- */
1553 /** Show current directory in the xterm title */
1554 void
1555 update_xterm_title_path (void)
1557 if (mc_global.tty.xterm_flag && xterm_title)
1559 char *p;
1560 char *path;
1561 char *login;
1563 title_path_prepare (&path, &login);
1565 p = g_strdup_printf ("mc [%s]:%s", login, path);
1566 g_free (login);
1567 g_free (path);
1569 fprintf (stdout, ESC_STR "]0;%s" ESC_STR "\\", str_term_form (p));
1570 g_free (p);
1572 if (!mc_global.tty.alternate_plus_minus)
1573 numeric_keypad_mode ();
1574 (void) fflush (stdout);
1578 /* --------------------------------------------------------------------------------------------- */
1580 /** Tell the current directory to the terminal so it can open new tabs there */
1581 void
1582 update_terminal_cwd (void)
1584 if (mc_global.tty.xterm_flag && vfs_current_is_local ())
1586 const gchar *host;
1587 char *path, *path_uri;
1589 host = g_get_host_name ();
1590 path = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_NONE);
1591 path_uri = g_uri_escape_string (path, "/", FALSE);
1593 fprintf (stdout, ESC_STR "]7;file://%s%s" ESC_STR "\\", host, path_uri);
1594 (void) fflush (stdout);
1596 g_free (path_uri);
1597 g_free (path);
1601 /* --------------------------------------------------------------------------------------------- */