Move widget add/del API from WDialog to WGroup.
[midnight-commander.git] / src / filemanager / layout.c
blobe2f5e83d29e76c62769f1efef8c0a5d5c0b39200
1 /*
2 Panel layout module for the Midnight Commander
4 Copyright (C) 1995-2020
5 Free Software Foundation, Inc.
7 Written by:
8 Janne Kukonlehto, 1995
9 Miguel de Icaza, 1995
10 Andrew Borodin <aborodin@vmail.ru>, 2011, 2012, 2013
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" /* For _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 "midnight.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 /*** file scope variables ************************************************************************/
141 static struct
143 panel_view_mode_t type;
144 Widget *widget;
145 char *last_saved_dir; /* last view_list working directory */
146 } panels[MAX_VIEWS] =
148 /* *INDENT-OFF* */
149 /* init MAX_VIEWS items */
150 { view_listing, NULL, NULL},
151 { view_listing, NULL, NULL}
152 /* *INDENT-ON* */
155 static layout_t old_layout;
156 static panels_layout_t old_panels_layout;
158 static gboolean equal_split;
159 static int _output_lines;
161 static int height;
163 static WRadio *radio_widget;
165 static struct
167 const char *text;
168 gboolean *variable;
169 WCheck *widget;
170 } 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 /*** file scope functions ************************************************************************/
189 /* --------------------------------------------------------------------------------------------- */
191 /* don't use max() macro to avoid double call of str_term_width1() in widget width calculation */
192 #undef max
194 static int
195 max (int a, int b)
197 return a > b ? a : b;
200 /* --------------------------------------------------------------------------------------------- */
202 static void
203 check_split (panels_layout_t * layout)
205 if (layout->horizontal_split)
207 if (layout->horizontal_equal)
208 layout->top_panel_size = height / 2;
209 else if (layout->top_panel_size < MINHEIGHT)
210 layout->top_panel_size = MINHEIGHT;
211 else if (layout->top_panel_size > height - MINHEIGHT)
212 layout->top_panel_size = height - MINHEIGHT;
214 else
216 int md_cols = CONST_WIDGET (midnight_dlg)->cols;
218 if (layout->vertical_equal)
219 layout->left_panel_size = md_cols / 2;
220 else if (layout->left_panel_size < MINWIDTH)
221 layout->left_panel_size = MINWIDTH;
222 else if (layout->left_panel_size > md_cols - MINWIDTH)
223 layout->left_panel_size = md_cols - MINWIDTH;
227 /* --------------------------------------------------------------------------------------------- */
229 static void
230 update_split (const WDialog * h)
232 /* Check split has to be done before testing if it changed, since
233 it can change due to calling check_split() as well */
234 check_split (&panels_layout);
236 if (panels_layout.horizontal_split)
237 check_options[0].widget->state = panels_layout.horizontal_equal;
238 else
239 check_options[0].widget->state = panels_layout.vertical_equal;
240 widget_draw (WIDGET (check_options[0].widget));
242 tty_setcolor (check_options[0].widget->state ? DISABLED_COLOR : COLOR_NORMAL);
244 widget_gotoyx (h, 6, 5);
245 if (panels_layout.horizontal_split)
246 tty_printf ("%03d", panels_layout.top_panel_size);
247 else
248 tty_printf ("%03d", panels_layout.left_panel_size);
250 widget_gotoyx (h, 6, 17);
251 if (panels_layout.horizontal_split)
252 tty_printf ("%03d", height - panels_layout.top_panel_size);
253 else
254 tty_printf ("%03d", CONST_WIDGET (midnight_dlg)->cols - panels_layout.left_panel_size);
256 widget_gotoyx (h, 6, 12);
257 tty_print_char ('=');
260 /* --------------------------------------------------------------------------------------------- */
262 static int
263 b_left_right_cback (WButton * button, int action)
265 (void) action;
267 if (button == bright_widget)
269 if (panels_layout.horizontal_split)
270 panels_layout.top_panel_size++;
271 else
272 panels_layout.left_panel_size++;
274 else
276 if (panels_layout.horizontal_split)
277 panels_layout.top_panel_size--;
278 else
279 panels_layout.left_panel_size--;
282 update_split (DIALOG (WIDGET (button)->owner));
283 layout_change ();
284 do_refresh ();
285 return 0;
288 /* --------------------------------------------------------------------------------------------- */
290 static int
291 bplus_cback (WButton * button, int action)
293 (void) button;
294 (void) action;
296 if (_output_lines < 99)
297 _output_lines++;
298 return 0;
301 /* --------------------------------------------------------------------------------------------- */
303 static int
304 bminus_cback (WButton * button, int action)
306 (void) button;
307 (void) action;
309 if (_output_lines > 0)
310 _output_lines--;
311 return 0;
314 /* --------------------------------------------------------------------------------------------- */
316 static cb_ret_t
317 layout_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
319 WDialog *h = DIALOG (w);
321 switch (msg)
323 case MSG_DRAW:
324 /* When repainting the whole dialog (e.g. with C-l) we have to
325 update everything */
326 dlg_default_repaint (h);
328 old_layout.output_lines = -1;
330 update_split (h);
332 if (old_layout.output_lines != _output_lines)
334 old_layout.output_lines = _output_lines;
335 tty_setcolor (mc_global.tty.console_flag != '\0' ? COLOR_NORMAL : DISABLED_COLOR);
336 widget_gotoyx (h, 9, 5);
337 tty_print_string (output_lines_label);
338 widget_gotoyx (h, 9, 5 + 3 + output_lines_label_len);
339 tty_printf ("%02d", _output_lines);
341 return MSG_HANDLED;
343 case MSG_POST_KEY:
345 const Widget *mw = CONST_WIDGET (midnight_dlg);
346 gboolean _menubar_visible, _command_prompt, _keybar_visible, _message_visible;
348 _menubar_visible = check_options[1].widget->state;
349 _command_prompt = check_options[2].widget->state;
350 _keybar_visible = check_options[3].widget->state;
351 _message_visible = check_options[4].widget->state;
353 if (mc_global.tty.console_flag == '\0')
354 height =
355 mw->lines - (_keybar_visible ? 1 : 0) - (_command_prompt ? 1 : 0) -
356 (_menubar_visible ? 1 : 0) - _output_lines - (_message_visible ? 1 : 0);
357 else
359 int minimum;
361 if (_output_lines < 0)
362 _output_lines = 0;
363 height =
364 mw->lines - (_keybar_visible ? 1 : 0) - (_command_prompt ? 1 : 0) -
365 (_menubar_visible ? 1 : 0) - _output_lines - (_message_visible ? 1 : 0);
366 minimum = MINHEIGHT * (1 + (panels_layout.horizontal_split ? 1 : 0));
367 if (height < minimum)
369 _output_lines -= minimum - height;
370 height = minimum;
374 if (old_layout.output_lines != _output_lines)
376 old_layout.output_lines = _output_lines;
377 tty_setcolor (mc_global.tty.console_flag != '\0' ? COLOR_NORMAL : DISABLED_COLOR);
378 widget_gotoyx (h, 9, 5 + 3 + output_lines_label_len);
379 tty_printf ("%02d", _output_lines);
382 return MSG_HANDLED;
384 case MSG_NOTIFY:
385 if (sender == WIDGET (radio_widget))
387 if ((panels_layout.horizontal_split ? 1 : 0) == radio_widget->sel)
388 update_split (h);
389 else
391 int eq;
393 panels_layout.horizontal_split = radio_widget->sel != 0;
395 if (panels_layout.horizontal_split)
397 eq = panels_layout.horizontal_equal;
398 if (eq)
399 panels_layout.top_panel_size = height / 2;
401 else
403 eq = panels_layout.vertical_equal;
404 if (eq)
405 panels_layout.left_panel_size = CONST_WIDGET (midnight_dlg)->cols / 2;
408 widget_disable (WIDGET (bleft_widget), eq);
409 widget_disable (WIDGET (bright_widget), eq);
411 update_split (h);
412 layout_change ();
413 do_refresh ();
416 return MSG_HANDLED;
419 if (sender == WIDGET (check_options[0].widget))
421 gboolean eq;
423 if (panels_layout.horizontal_split)
425 panels_layout.horizontal_equal = check_options[0].widget->state;
426 eq = panels_layout.horizontal_equal;
428 else
430 panels_layout.vertical_equal = check_options[0].widget->state;
431 eq = panels_layout.vertical_equal;
434 widget_disable (WIDGET (bleft_widget), eq);
435 widget_disable (WIDGET (bright_widget), eq);
437 update_split (h);
438 layout_change ();
439 do_refresh ();
441 return MSG_HANDLED;
445 gboolean ok = TRUE;
447 if (sender == WIDGET (check_options[1].widget))
448 menubar_visible = check_options[1].widget->state;
449 else if (sender == WIDGET (check_options[2].widget))
450 command_prompt = check_options[2].widget->state;
451 else if (sender == WIDGET (check_options[3].widget))
452 mc_global.keybar_visible = check_options[3].widget->state;
453 else if (sender == WIDGET (check_options[4].widget))
454 mc_global.message_visible = check_options[4].widget->state;
455 else if (sender == WIDGET (check_options[5].widget))
456 xterm_title = check_options[5].widget->state;
457 else if (sender == WIDGET (check_options[6].widget))
458 free_space = check_options[6].widget->state;
459 else
460 ok = FALSE;
462 if (ok)
464 update_split (h);
465 layout_change ();
466 do_refresh ();
467 return MSG_HANDLED;
471 return MSG_NOT_HANDLED;
473 default:
474 return dlg_default_callback (w, sender, msg, parm, data);
478 /* --------------------------------------------------------------------------------------------- */
480 static WDialog *
481 layout_dlg_create (void)
483 WDialog *layout_dlg;
484 WGroup *g;
485 int l1 = 0, width;
486 int b1, b2, b;
487 size_t i;
489 const char *title1 = N_("Panel split");
490 const char *title2 = N_("Console output");
491 const char *title3 = N_("Other options");
493 const char *s_split_direction[2] = {
494 N_("&Vertical"),
495 N_("&Horizontal")
498 const char *ok_button = N_("&OK");
499 const char *cancel_button = N_("&Cancel");
501 output_lines_label = _("Output lines:");
503 #ifdef ENABLE_NLS
505 static gboolean i18n = FALSE;
507 title1 = _(title1);
508 title2 = _(title2);
509 title3 = _(title3);
511 i = G_N_ELEMENTS (s_split_direction);
512 while (i-- != 0)
513 s_split_direction[i] = _(s_split_direction[i]);
515 if (!i18n)
517 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
518 check_options[i].text = _(check_options[i].text);
519 i18n = TRUE;
522 ok_button = _(ok_button);
523 cancel_button = _(cancel_button);
525 #endif
527 /* radiobuttons */
528 i = G_N_ELEMENTS (s_split_direction);
529 while (i-- != 0)
530 l1 = max (l1, str_term_width1 (s_split_direction[i]) + 7);
531 /* checkboxes */
532 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
533 l1 = max (l1, str_term_width1 (check_options[i].text) + 7);
534 /* groupboxes */
535 l1 = max (l1, str_term_width1 (title1) + 4);
536 l1 = max (l1, str_term_width1 (title2) + 4);
537 l1 = max (l1, str_term_width1 (title3) + 4);
538 /* label + "+"/"-" buttons */
539 output_lines_label_len = str_term_width1 (output_lines_label);
540 l1 = max (l1, output_lines_label_len + 12);
541 /* buttons */
542 b1 = str_term_width1 (ok_button) + 5; /* default button */
543 b2 = str_term_width1 (cancel_button) + 3;
544 b = b1 + b2 + 1;
545 /* dialog width */
546 width = max (l1 * 2 + 7, b);
548 layout_dlg =
549 dlg_create (TRUE, 0, 0, 15, width, WPOS_CENTER, FALSE, dialog_colors, layout_callback, NULL,
550 "[Layout]", _("Layout"));
551 g = GROUP (layout_dlg);
553 #define XTRACT(i) (*check_options[i].variable != 0), check_options[i].text
555 /* "Panel split" groupbox */
556 group_add_widget (g, groupbox_new (2, 3, 6, l1, title1));
558 radio_widget = radio_new (3, 5, 2, s_split_direction);
559 radio_widget->sel = panels_layout.horizontal_split ? 1 : 0;
560 group_add_widget (g, radio_widget);
562 check_options[0].widget = check_new (5, 5, XTRACT (0));
563 group_add_widget (g, check_options[0].widget);
565 equal_split = panels_layout.horizontal_split ?
566 panels_layout.horizontal_equal : panels_layout.vertical_equal;
568 bleft_widget = button_new (6, 8, B_2LEFT, NARROW_BUTTON, "&<", b_left_right_cback);
569 widget_disable (WIDGET (bleft_widget), equal_split);
570 group_add_widget (g, bleft_widget);
572 bright_widget = button_new (6, 14, B_2RIGHT, NARROW_BUTTON, "&>", b_left_right_cback);
573 widget_disable (WIDGET (bright_widget), equal_split);
574 group_add_widget (g, bright_widget);
576 /* "Console output" groupbox */
578 widget_state_t disabled;
579 Widget *w;
581 disabled = mc_global.tty.console_flag != '\0' ? 0 : WST_DISABLED;
583 w = WIDGET (groupbox_new (8, 3, 3, l1, title2));
584 w->state |= disabled;
585 group_add_widget (g, w);
587 w = WIDGET (button_new (9, output_lines_label_len + 5, B_PLUS,
588 NARROW_BUTTON, "&+", bplus_cback));
589 w->state |= disabled;
590 group_add_widget (g, w);
592 w = WIDGET (button_new (9, output_lines_label_len + 5 + 5, B_MINUS,
593 NARROW_BUTTON, "&-", bminus_cback));
594 w->state |= disabled;
595 group_add_widget (g, w);
598 /* "Other options" groupbox */
599 group_add_widget (g, groupbox_new (2, 4 + l1, 9, l1, title3));
601 for (i = 1; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
603 check_options[i].widget = check_new (i + 2, 6 + l1, XTRACT (i));
604 group_add_widget (g, check_options[i].widget);
607 #undef XTRACT
609 group_add_widget (g, hline_new (11, -1, -1));
610 /* buttons */
611 group_add_widget (g, button_new (12, (width - b) / 2, B_ENTER, DEFPUSH_BUTTON, ok_button, 0));
612 group_add_widget (g,
613 button_new (12, (width - b) / 2 + b1 + 1, B_CANCEL, NORMAL_BUTTON,
614 cancel_button, 0));
616 widget_select (WIDGET (radio_widget));
618 return layout_dlg;
621 /* --------------------------------------------------------------------------------------------- */
623 static void
624 panel_do_cols (int idx)
626 if (get_panel_type (idx) == view_listing)
627 set_panel_formats (PANEL (panels[idx].widget));
628 else
629 panel_update_cols (panels[idx].widget, frame_half);
632 /* --------------------------------------------------------------------------------------------- */
633 /** Save current list_view widget directory into panel */
635 static Widget *
636 restore_into_right_dir_panel (int idx, gboolean last_was_panel, int y, int x, int lines, int cols)
638 WPanel *new_widget;
639 const char *p_name;
641 p_name = get_nth_panel_name (idx);
643 if (last_was_panel)
645 vfs_path_t *saved_dir_vpath;
647 saved_dir_vpath = vfs_path_from_str (panels[idx].last_saved_dir);
648 new_widget = panel_sized_with_dir_new (p_name, y, x, lines, cols, saved_dir_vpath);
649 vfs_path_free (saved_dir_vpath);
651 else
652 new_widget = panel_sized_new (p_name, y, x, lines, cols);
654 return WIDGET (new_widget);
657 /* --------------------------------------------------------------------------------------------- */
659 static void
660 layout_save (void)
662 old_layout.menubar_visible = menubar_visible;
663 old_layout.command_prompt = command_prompt;
664 old_layout.keybar_visible = mc_global.keybar_visible;
665 old_layout.message_visible = mc_global.message_visible;
666 old_layout.xterm_title = xterm_title;
667 old_layout.free_space = free_space;
668 old_layout.output_lines = -1;
670 _output_lines = output_lines;
672 old_panels_layout = panels_layout;
675 /* --------------------------------------------------------------------------------------------- */
677 static void
678 layout_restore (void)
680 menubar_visible = old_layout.menubar_visible;
681 command_prompt = old_layout.command_prompt;
682 mc_global.keybar_visible = old_layout.keybar_visible;
683 mc_global.message_visible = old_layout.message_visible;
684 xterm_title = old_layout.xterm_title;
685 free_space = old_layout.free_space;
686 output_lines = old_layout.output_lines;
688 panels_layout = old_panels_layout;
691 /* --------------------------------------------------------------------------------------------- */
692 /*** public functions ****************************************************************************/
693 /* --------------------------------------------------------------------------------------------- */
695 void
696 layout_change (void)
698 setup_panels ();
699 /* update the main menu, because perhaps there was a change in the way
700 how the panel are split (horizontal/vertical),
701 and a change of menu visibility. */
702 update_menu ();
703 load_hint (TRUE);
706 /* --------------------------------------------------------------------------------------------- */
708 void
709 layout_box (void)
711 WDialog *layout_dlg;
713 layout_save ();
715 layout_dlg = layout_dlg_create ();
717 if (dlg_run (layout_dlg) == B_ENTER)
719 size_t i;
721 for (i = 0; i < (size_t) LAYOUT_OPTIONS_COUNT; i++)
722 if (check_options[i].widget != NULL)
723 *check_options[i].variable = check_options[i].widget->state;
725 output_lines = _output_lines;
727 else
728 layout_restore ();
730 dlg_destroy (layout_dlg);
731 layout_change ();
732 do_refresh ();
735 /* --------------------------------------------------------------------------------------------- */
737 void
738 panel_update_cols (Widget * widget, panel_display_t frame_size)
740 const Widget *mw = CONST_WIDGET (midnight_dlg);
741 int cols, x;
743 /* don't touch panel if it is not in dialog yet */
744 /* if panel is not in dialog it is not in widgets list
745 and cannot be compared with get_panel_widget() result */
746 if (widget->owner == NULL)
747 return;
749 if (panels_layout.horizontal_split)
751 widget->cols = mw->cols;
752 return;
755 if (frame_size == frame_full)
757 cols = mw->cols;
758 x = mw->x;
760 else if (widget == get_panel_widget (0))
762 cols = panels_layout.left_panel_size;
763 x = mw->x;
765 else
767 cols = mw->cols - panels_layout.left_panel_size;
768 x = mw->x + panels_layout.left_panel_size;
771 widget->cols = cols;
772 widget->x = x;
775 /* --------------------------------------------------------------------------------------------- */
777 void
778 setup_panels (void)
780 /* File manager screen layout:
782 * +---------------------------------------------------------------+
783 * | Menu bar |
784 * +-------------------------------+-------------------------------+
785 * | | |
786 * | | |
787 * | | |
788 * | | |
789 * | Left panel | Right panel |
790 * | | |
791 * | | |
792 * | | |
793 * | | |
794 * +-------------------------------+-------------------------------+
795 * | Hint (message) bar |
796 * +---------------------------------------------------------------+
797 * | |
798 * | Console content |
799 * | |
800 * +--------+------------------------------------------------------+
801 * | Prompt | Command line |
802 * | Key (button) bar |
803 * +--------+------------------------------------------------------+
806 const Widget *mw = CONST_WIDGET (midnight_dlg);
807 int start_y;
809 /* iniitial height of panels */
810 height =
811 mw->lines - (menubar_visible ? 1 : 0) - (mc_global.message_visible ? 1 : 0) -
812 (command_prompt ? 1 : 0) - (mc_global.keybar_visible ? 1 : 0);
814 if (mc_global.tty.console_flag != '\0')
816 int minimum;
818 if (output_lines < 0)
819 output_lines = 0;
820 else
821 height -= output_lines;
822 minimum = MINHEIGHT * (1 + (panels_layout.horizontal_split ? 1 : 0));
823 if (height < minimum)
825 output_lines -= minimum - height;
826 height = minimum;
830 widget_set_size (WIDGET (the_menubar), mw->y, mw->x, 1, mw->cols);
831 menubar_set_visible (the_menubar, menubar_visible);
833 check_split (&panels_layout);
834 start_y = mw->y + (menubar_visible ? 1 : 0);
836 /* update columns first... */
837 panel_do_cols (0);
838 panel_do_cols (1);
840 /* ...then rows and origin */
841 if (panels_layout.horizontal_split)
843 widget_set_size (panels[0].widget, start_y, mw->x, panels_layout.top_panel_size,
844 panels[0].widget->cols);
845 widget_set_size (panels[1].widget, start_y + panels_layout.top_panel_size, mw->x,
846 height - panels_layout.top_panel_size, panels[1].widget->cols);
848 else
850 widget_set_size (panels[0].widget, start_y, mw->x, height, panels[0].widget->cols);
851 widget_set_size (panels[1].widget, start_y, panels[1].widget->x, height,
852 panels[1].widget->cols);
855 if (mc_global.message_visible)
856 widget_set_size (WIDGET (the_hint), height + start_y, mw->x, 1, mw->cols);
857 else
858 /* make invisible */
859 widget_set_size (WIDGET (the_hint), 0, 0, 0, 0);
861 /* Output window */
862 if (mc_global.tty.console_flag != '\0' && output_lines != 0)
864 unsigned char end_line;
866 end_line = mw->lines - (mc_global.keybar_visible ? 1 : 0) - 1;
867 output_start_y = end_line - (command_prompt ? 1 : 0) - output_lines + 1;
868 show_console_contents (output_start_y, end_line - output_lines, end_line);
871 if (command_prompt)
873 #ifdef ENABLE_SUBSHELL
874 if (!mc_global.tty.use_subshell || !do_load_prompt ())
875 #endif
876 setup_cmdline ();
878 else
880 /* make invisible */
881 widget_set_size (WIDGET (cmdline), 0, 0, 0, 0);
882 widget_set_size (WIDGET (the_prompt), mw->lines, mw->cols, 0, 0);
885 widget_set_size (WIDGET (the_bar), mw->lines - 1, mw->x, mc_global.keybar_visible ? 1 : 0,
886 mw->cols);
887 buttonbar_set_visible (the_bar, mc_global.keybar_visible);
889 update_xterm_title_path ();
892 /* --------------------------------------------------------------------------------------------- */
894 void
895 panels_split_equal (void)
897 if (panels_layout.horizontal_split)
898 panels_layout.horizontal_equal = TRUE;
899 else
900 panels_layout.vertical_equal = TRUE;
902 layout_change ();
903 do_refresh ();
906 /* --------------------------------------------------------------------------------------------- */
908 void
909 panels_split_more (void)
911 if (panels_layout.horizontal_split)
913 panels_layout.horizontal_equal = FALSE;
914 panels_layout.top_panel_size++;
916 else
918 panels_layout.vertical_equal = FALSE;
919 panels_layout.left_panel_size++;
922 layout_change ();
925 /* --------------------------------------------------------------------------------------------- */
927 void
928 panels_split_less (void)
930 if (panels_layout.horizontal_split)
932 panels_layout.horizontal_equal = FALSE;
933 panels_layout.top_panel_size--;
935 else
937 panels_layout.vertical_equal = FALSE;
938 panels_layout.left_panel_size--;
941 layout_change ();
944 /* --------------------------------------------------------------------------------------------- */
947 void
948 setup_cmdline (void)
950 const Widget *mw = CONST_WIDGET (midnight_dlg);
951 int prompt_width;
952 int y;
953 char *tmp_prompt = (char *) mc_prompt;
955 #ifdef ENABLE_SUBSHELL
956 if (mc_global.tty.use_subshell)
958 tmp_prompt = g_string_free (subshell_prompt, FALSE);
959 (void) strip_ctrl_codes (tmp_prompt);
961 #endif
963 prompt_width = str_term_width1 (tmp_prompt);
965 /* Check for prompts too big */
966 if (mw->cols > 8 && prompt_width > mw->cols - 8)
968 int prompt_len;
970 prompt_width = mw->cols - 8;
971 prompt_len = str_offset_to_pos (tmp_prompt, prompt_width);
972 tmp_prompt[prompt_len] = '\0';
975 #ifdef ENABLE_SUBSHELL
976 if (mc_global.tty.use_subshell)
978 subshell_prompt = g_string_new (tmp_prompt);
979 g_free (tmp_prompt);
980 mc_prompt = subshell_prompt->str;
982 #endif
984 y = mw->lines - 1 - (mc_global.keybar_visible ? 1 : 0);
986 widget_set_size (WIDGET (the_prompt), y, mw->x, 1, prompt_width);
987 label_set_text (the_prompt, mc_prompt);
988 widget_set_size (WIDGET (cmdline), y, mw->x + prompt_width, 1, mw->cols - prompt_width);
991 /* --------------------------------------------------------------------------------------------- */
993 void
994 use_dash (gboolean flag)
996 if (flag)
997 ok_to_refresh++;
998 else
999 ok_to_refresh--;
1002 /* --------------------------------------------------------------------------------------------- */
1004 void
1005 set_hintbar (const char *str)
1007 label_set_text (the_hint, str);
1008 if (ok_to_refresh > 0)
1009 mc_refresh ();
1012 /* --------------------------------------------------------------------------------------------- */
1014 void
1015 rotate_dash (gboolean show)
1017 static guint64 timestamp = 0;
1018 /* update with 10 FPS rate */
1019 static const guint64 delay = G_USEC_PER_SEC / 10;
1021 const Widget *w = CONST_WIDGET (midnight_dlg);
1023 if (!nice_rotating_dash || (ok_to_refresh <= 0))
1024 return;
1026 if (show && !mc_time_elapsed (&timestamp, delay))
1027 return;
1029 widget_gotoyx (w, menubar_visible != 0 ? 1 : 0, w->cols - 1);
1030 tty_setcolor (NORMAL_COLOR);
1032 if (!show)
1033 tty_print_alt_char (ACS_URCORNER, FALSE);
1034 else
1036 static const char rotating_dash[4] = "|/-\\";
1037 static size_t pos = 0;
1039 tty_print_char (rotating_dash[pos]);
1040 pos = (pos + 1) % sizeof (rotating_dash);
1043 mc_refresh ();
1046 /* --------------------------------------------------------------------------------------------- */
1048 const char *
1049 get_nth_panel_name (int num)
1051 if (num == 0)
1052 return "New Left Panel";
1054 if (num == 1)
1055 return "New Right Panel";
1058 static char buffer[BUF_SMALL];
1060 g_snprintf (buffer, sizeof (buffer), "%ith Panel", num);
1061 return buffer;
1065 /* --------------------------------------------------------------------------------------------- */
1066 /* I wonder if I should start to use the folding mode than Dugan uses */
1067 /* */
1068 /* This is the centralized managing of the panel display types */
1069 /* This routine takes care of destroying and creating new widgets */
1070 /* Please note that it could manage MAX_VIEWS, not just left and right */
1071 /* Currently nothing in the code takes advantage of this and has hard- */
1072 /* coded values for two panels only */
1074 /* Set the num-th panel to the view type: type */
1075 /* This routine also keeps at least one WPanel object in the screen */
1076 /* since a lot of routines depend on the current_panel variable */
1078 void
1079 create_panel (int num, panel_view_mode_t type)
1081 int x = 0, y = 0, cols = 0, lines = 0;
1082 unsigned int the_other = 0; /* Index to the other panel */
1083 const char *file_name = NULL; /* For Quick view */
1084 Widget *new_widget = NULL, *old_widget = NULL;
1085 panel_view_mode_t old_type = view_listing;
1086 WPanel *the_other_panel = NULL;
1088 if (num >= MAX_VIEWS)
1090 fprintf (stderr, "Cannot allocate more that %d views\n", MAX_VIEWS);
1091 abort ();
1093 /* Check that we will have a WPanel * at least */
1094 if (type != view_listing)
1096 the_other = num == 0 ? 1 : 0;
1098 if (panels[the_other].type != view_listing)
1099 return;
1102 /* Get rid of it */
1103 if (panels[num].widget != NULL)
1105 Widget *w = panels[num].widget;
1106 WPanel *panel = PANEL (w);
1108 x = w->x;
1109 y = w->y;
1110 cols = w->cols;
1111 lines = w->lines;
1112 old_widget = w;
1113 old_type = panels[num].type;
1115 if (old_type == view_listing && panel->frame_size == frame_full && type != view_listing)
1117 int md_cols = CONST_WIDGET (midnight_dlg)->cols;
1119 if (panels_layout.horizontal_split)
1121 cols = md_cols;
1122 x = 0;
1124 else
1126 cols = md_cols - panels_layout.left_panel_size;
1127 if (num == 1)
1128 x = panels_layout.left_panel_size;
1133 /* Restoring saved path from panels.ini for nonlist panel */
1134 /* when it's first creation (for example view_info) */
1135 if (old_widget == NULL && type != view_listing)
1136 panels[num].last_saved_dir = _vfs_get_cwd ();
1138 switch (type)
1140 case view_nothing:
1141 case view_listing:
1143 gboolean last_was_panel;
1145 last_was_panel = old_widget != NULL && get_panel_type (num) != view_listing;
1146 new_widget = restore_into_right_dir_panel (num, last_was_panel, y, x, lines, cols);
1147 break;
1150 case view_info:
1151 new_widget = WIDGET (info_new (y, x, lines, cols));
1152 break;
1154 case view_tree:
1155 new_widget = WIDGET (tree_new (y, x, lines, cols, TRUE));
1156 break;
1158 case view_quick:
1159 new_widget = WIDGET (mcview_new (y, x, lines, cols, TRUE));
1160 the_other_panel = PANEL (panels[the_other].widget);
1161 if (the_other_panel != NULL)
1162 file_name = the_other_panel->dir.list[the_other_panel->selected].fname;
1163 else
1164 file_name = "";
1166 mcview_load ((WView *) new_widget, 0, file_name, 0, 0, 0);
1167 break;
1169 default:
1170 break;
1173 if (type != view_listing)
1174 /* Must save dir, for restoring after change type to */
1175 /* view_listing */
1176 save_panel_dir (num);
1178 panels[num].type = type;
1179 panels[num].widget = new_widget;
1181 /* We use replace to keep the circular list of the dialog in the */
1182 /* same state. Maybe we could just kill it and then replace it */
1183 if ((midnight_dlg != NULL) && (old_widget != NULL))
1185 if (old_type == view_listing)
1187 /* save and write directory history of panel
1188 * ... and other histories of midnight_dlg */
1189 dlg_save_history (midnight_dlg);
1192 widget_replace (old_widget, new_widget);
1195 if (type == view_listing)
1197 WPanel *panel = PANEL (new_widget);
1199 /* if existing panel changed type to view_listing, then load history */
1200 if (old_widget != NULL)
1202 ev_history_load_save_t event_data = { NULL, new_widget };
1204 mc_event_raise (midnight_dlg->event_group, MCEVENT_HISTORY_LOAD, &event_data);
1207 if (num == 0)
1208 left_panel = panel;
1209 else
1210 right_panel = panel;
1212 /* forced update format after set new sizes */
1213 set_panel_formats (panel);
1216 if (type == view_tree)
1217 the_tree = (WTree *) new_widget;
1219 /* Prevent current_panel's value from becoming invalid.
1220 * It's just a quick hack to prevent segfaults. Comment out and
1221 * try following:
1222 * - select left panel
1223 * - invoke menue left/tree
1224 * - as long as you stay in the left panel almost everything that uses
1225 * current_panel causes segfault, e.g. C-Enter, C-x c, ...
1227 if ((type != view_listing) && (current_panel == PANEL (old_widget)))
1228 current_panel = num == 0 ? right_panel : left_panel;
1230 g_free (old_widget);
1233 /* --------------------------------------------------------------------------------------------- */
1234 /** This routine is deeply sticked to the two panels idea.
1235 What should it do in more panels. ANSWER - don't use it
1236 in any multiple panels environment. */
1238 void
1239 swap_panels (void)
1241 WPanel *panel1, *panel2;
1242 Widget *tmp_widget;
1244 panel1 = PANEL (panels[0].widget);
1245 panel2 = PANEL (panels[1].widget);
1247 if (panels[0].type == view_listing && panels[1].type == view_listing &&
1248 !mc_config_get_bool (mc_global.main_config, CONFIG_PANELS_SECTION, "simple_swap", FALSE))
1250 WPanel panel;
1252 #define panelswap(x) panel.x = panel1->x; panel1->x = panel2->x; panel2->x = panel.x;
1253 /* Change content and related stuff */
1254 panelswap (dir);
1255 panelswap (active);
1256 panelswap (cwd_vpath);
1257 panelswap (lwd_vpath);
1258 panelswap (marked);
1259 panelswap (dirs_marked);
1260 panelswap (total);
1261 panelswap (top_file);
1262 panelswap (selected);
1263 panelswap (is_panelized);
1264 panelswap (dir_stat);
1265 #undef panelswap
1267 panel1->searching = FALSE;
1268 panel2->searching = FALSE;
1270 if (current_panel == panel1)
1271 current_panel = panel2;
1272 else
1273 current_panel = panel1;
1275 /* if sort options are different -> resort panels */
1276 if (memcmp (&panel1->sort_info, &panel2->sort_info, sizeof (dir_sort_options_t)) != 0)
1278 panel_re_sort (other_panel);
1279 panel_re_sort (current_panel);
1282 if (widget_is_active (panels[0].widget))
1283 widget_select (panels[1].widget);
1284 else if (widget_is_active (panels[1].widget))
1285 widget_select (panels[0].widget);
1287 else
1289 WPanel *tmp_panel;
1290 int x, y, cols, lines;
1291 int tmp_type;
1293 tmp_panel = right_panel;
1294 right_panel = left_panel;
1295 left_panel = tmp_panel;
1297 if (panels[0].type == view_listing)
1299 if (strcmp (panel1->panel_name, get_nth_panel_name (0)) == 0)
1301 g_free (panel1->panel_name);
1302 panel1->panel_name = g_strdup (get_nth_panel_name (1));
1305 if (panels[1].type == view_listing)
1307 if (strcmp (panel2->panel_name, get_nth_panel_name (1)) == 0)
1309 g_free (panel2->panel_name);
1310 panel2->panel_name = g_strdup (get_nth_panel_name (0));
1314 x = panels[0].widget->x;
1315 y = panels[0].widget->y;
1316 cols = panels[0].widget->cols;
1317 lines = panels[0].widget->lines;
1319 panels[0].widget->x = panels[1].widget->x;
1320 panels[0].widget->y = panels[1].widget->y;
1321 panels[0].widget->cols = panels[1].widget->cols;
1322 panels[0].widget->lines = panels[1].widget->lines;
1324 panels[1].widget->x = x;
1325 panels[1].widget->y = y;
1326 panels[1].widget->cols = cols;
1327 panels[1].widget->lines = lines;
1329 tmp_widget = panels[0].widget;
1330 panels[0].widget = panels[1].widget;
1331 panels[1].widget = tmp_widget;
1332 tmp_type = panels[0].type;
1333 panels[0].type = panels[1].type;
1334 panels[1].type = tmp_type;
1336 /* force update formats because of possible changed sizes */
1337 if (panels[0].type == view_listing)
1338 set_panel_formats (PANEL (panels[0].widget));
1339 if (panels[1].type == view_listing)
1340 set_panel_formats (PANEL (panels[1].widget));
1344 /* --------------------------------------------------------------------------------------------- */
1346 panel_view_mode_t
1347 get_panel_type (int idx)
1349 return panels[idx].type;
1352 /* --------------------------------------------------------------------------------------------- */
1354 Widget *
1355 get_panel_widget (int idx)
1357 return panels[idx].widget;
1360 /* --------------------------------------------------------------------------------------------- */
1363 get_current_index (void)
1365 return (panels[0].widget == WIDGET (current_panel) ? 0 : 1);
1368 /* --------------------------------------------------------------------------------------------- */
1371 get_other_index (void)
1373 return (get_current_index () == 0 ? 1 : 0);
1376 /* --------------------------------------------------------------------------------------------- */
1378 WPanel *
1379 get_other_panel (void)
1381 return PANEL (get_panel_widget (get_other_index ()));
1384 /* --------------------------------------------------------------------------------------------- */
1385 /** Returns the view type for the current panel/view */
1387 panel_view_mode_t
1388 get_current_type (void)
1390 return (panels[0].widget == WIDGET (current_panel) ? panels[0].type : panels[1].type);
1393 /* --------------------------------------------------------------------------------------------- */
1394 /** Returns the view type of the unselected panel */
1396 panel_view_mode_t
1397 get_other_type (void)
1399 return (panels[0].widget == WIDGET (current_panel) ? panels[1].type : panels[0].type);
1402 /* --------------------------------------------------------------------------------------------- */
1403 /** Save current list_view widget directory into panel */
1405 void
1406 save_panel_dir (int idx)
1408 panel_view_mode_t type;
1410 type = get_panel_type (idx);
1411 if (type == view_listing)
1413 WPanel *p;
1415 p = PANEL (get_panel_widget (idx));
1416 if (p != NULL)
1418 g_free (panels[idx].last_saved_dir); /* last path no needed */
1419 /* Because path can be nonlocal */
1420 panels[idx].last_saved_dir = g_strdup (vfs_path_as_str (p->cwd_vpath));
1425 /* --------------------------------------------------------------------------------------------- */
1426 /** Return working dir, if it's view_listing - cwd,
1427 but for other types - last_saved_dir */
1429 char *
1430 get_panel_dir_for (const WPanel * widget)
1432 int i;
1434 for (i = 0; i < MAX_VIEWS; i++)
1435 if (PANEL (get_panel_widget (i)) == widget)
1436 break;
1438 if (i >= MAX_VIEWS)
1439 return g_strdup (".");
1441 if (get_panel_type (i) == view_listing)
1443 vfs_path_t *cwd_vpath;
1445 cwd_vpath = PANEL (get_panel_widget (i))->cwd_vpath;
1446 return g_strdup (vfs_path_as_str (cwd_vpath));
1449 return g_strdup (panels[i].last_saved_dir);
1452 /* --------------------------------------------------------------------------------------------- */
1454 #ifdef ENABLE_SUBSHELL
1455 gboolean
1456 do_load_prompt (void)
1458 gboolean ret = FALSE;
1460 if (!read_subshell_prompt ())
1461 return ret;
1463 /* Don't actually change the prompt if it's invisible */
1464 if (top_dlg != NULL && DIALOG (top_dlg->data) == midnight_dlg && command_prompt)
1466 setup_cmdline ();
1468 /* since the prompt has changed, and we are called from one of the
1469 * tty_get_event channels, the prompt updating does not take place
1470 * automatically: force a cursor update and a screen refresh
1472 update_cursor (midnight_dlg);
1473 mc_refresh ();
1474 ret = TRUE;
1476 update_subshell_prompt = TRUE;
1477 return ret;
1480 /* --------------------------------------------------------------------------------------------- */
1483 load_prompt (int fd, void *unused)
1485 (void) fd;
1486 (void) unused;
1488 do_load_prompt ();
1489 return 0;
1491 #endif /* ENABLE_SUBSHELL */
1493 /* --------------------------------------------------------------------------------------------- */
1495 void
1496 title_path_prepare (char **path, char **login)
1498 char host[BUF_TINY];
1499 struct passwd *pw = NULL;
1500 int res = 0;
1502 *path =
1503 vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD);
1505 res = gethostname (host, sizeof (host));
1506 if (res != 0)
1507 host[0] = '\0';
1508 else
1509 host[sizeof (host) - 1] = '\0';
1511 pw = getpwuid (getuid ());
1512 if (pw != NULL)
1513 *login = g_strdup_printf ("%s@%s", pw->pw_name, host);
1514 else
1515 *login = g_strdup (host);
1518 /* --------------------------------------------------------------------------------------------- */
1520 /** Show current directory in the xterm title */
1521 void
1522 update_xterm_title_path (void)
1524 if (mc_global.tty.xterm_flag && xterm_title)
1526 char *p;
1527 char *path;
1528 char *login;
1530 title_path_prepare (&path, &login);
1532 p = g_strdup_printf ("mc [%s]:%s", login, path);
1533 g_free (login);
1534 g_free (path);
1536 fprintf (stdout, "\33]0;%s\7", str_term_form (p));
1537 g_free (p);
1539 if (!mc_global.tty.alternate_plus_minus)
1540 numeric_keypad_mode ();
1541 (void) fflush (stdout);
1545 /* --------------------------------------------------------------------------------------------- */