Merge branch '4524_cleanup'
[midnight-commander.git] / src / filemanager / hotlist.c
blob68406e4b987048c6dedbc8bb8a05cc1d02986583
1 /*
2 Directory hotlist -- for the Midnight Commander
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Radek Doulik, 1994
9 Janne Kukonlehto, 1995
10 Andrej Borsenkow, 1996
11 Norbert Warmuth, 1997
12 Andrew Borodin <aborodin@vmail.ru>, 2012-2022
14 Janne did the original Hotlist code, Andrej made the groupable
15 hotlist; the move hotlist and revamped the file format and made
16 it stronger.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /** \file hotlist.c
35 * \brief Source: directory hotlist
38 #include <config.h>
40 #include <ctype.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
47 #include "lib/global.h"
49 #include "lib/tty/tty.h" /* COLS */
50 #include "lib/tty/key.h" /* KEY_M_CTRL */
51 #include "lib/skin.h" /* colors */
52 #include "lib/mcconfig.h" /* Load/save directories hotlist */
53 #include "lib/fileloc.h"
54 #include "lib/strutil.h"
55 #include "lib/vfs/vfs.h"
56 #include "lib/util.h"
57 #include "lib/widget.h"
59 #include "src/setup.h" /* For profile_bname */
60 #include "src/history.h"
62 #include "command.h" /* cmdline */
64 #include "hotlist.h"
66 /*** global variables ****************************************************************************/
68 /*** file scope macro definitions ****************************************************************/
70 #define UX 3
71 #define UY 2
73 #define B_ADD_CURRENT B_USER
74 #define B_REMOVE (B_USER + 1)
75 #define B_NEW_GROUP (B_USER + 2)
76 #define B_NEW_ENTRY (B_USER + 3)
77 #define B_ENTER_GROUP (B_USER + 4)
78 #define B_UP_GROUP (B_USER + 5)
79 #define B_INSERT (B_USER + 6)
80 #define B_APPEND (B_USER + 7)
81 #define B_MOVE (B_USER + 8)
82 #ifdef ENABLE_VFS
83 #define B_FREE_ALL_VFS (B_USER + 9)
84 #define B_REFRESH_VFS (B_USER + 10)
85 #endif
87 #define TKN_GROUP 0
88 #define TKN_ENTRY 1
89 #define TKN_STRING 2
90 #define TKN_URL 3
91 #define TKN_ENDGROUP 4
92 #define TKN_COMMENT 5
93 #define TKN_EOL 125
94 #define TKN_EOF 126
95 #define TKN_UNKNOWN 127
97 #define SKIP_TO_EOL \
98 { \
99 int _tkn; \
100 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
103 #define CHECK_TOKEN(_TKN_) \
104 tkn = hot_next_token (); \
105 if (tkn != _TKN_) \
107 hotlist_state.readonly = TRUE; \
108 hotlist_state.file_error = TRUE; \
109 while (tkn != TKN_EOL && tkn != TKN_EOF) \
110 tkn = hot_next_token (); \
111 break; \
114 /*** file scope type declarations ****************************************************************/
116 enum HotListType
118 HL_TYPE_GROUP,
119 HL_TYPE_ENTRY,
120 HL_TYPE_COMMENT,
121 HL_TYPE_DOTDOT
124 static struct
127 * these reflect run time state
130 gboolean loaded; /* hotlist is loaded */
131 gboolean readonly; /* hotlist readonly */
132 gboolean file_error; /* parse error while reading file */
133 gboolean running; /* we are running dlg (and have to
134 update listbox */
135 gboolean moving; /* we are in moving hotlist currently */
136 gboolean modified; /* hotlist was modified */
137 hotlist_t type; /* LIST_HOTLIST || LIST_VFSLIST */
138 } hotlist_state;
140 /* Directory hotlist */
141 struct hotlist
143 enum HotListType type;
144 char *directory;
145 char *label;
146 struct hotlist *head;
147 struct hotlist *up;
148 struct hotlist *next;
151 /*** forward declarations (file scope functions) *************************************************/
153 /*** file scope variables ************************************************************************/
155 static WPanel *our_panel;
157 static gboolean hotlist_has_dot_dot = TRUE;
159 static WDialog *hotlist_dlg, *movelist_dlg;
160 static WGroupbox *hotlist_group, *movelist_group;
161 static WListbox *l_hotlist, *l_movelist;
162 static WLabel *pname;
164 static struct
166 int ret_cmd, flags, y, x, len;
167 const char *text;
168 int type;
169 widget_pos_flags_t pos_flags;
170 } hotlist_but[] =
172 /* *INDENT-OFF* */
173 { B_ENTER, DEFPUSH_BUTTON, 0, 0, 0, N_("Change &to"),
174 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
175 #ifdef ENABLE_VFS
176 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, 0, N_("&Free VFSs now"),
177 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
178 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, 0, N_("&Refresh"),
179 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
180 #endif
181 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, 0, N_("&Add current"),
182 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
183 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, 0, N_("&Up"),
184 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
185 { B_CANCEL, NORMAL_BUTTON, 0, 53, 0, N_("&Cancel"),
186 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_RIGHT | WPOS_KEEP_BOTTOM },
187 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, 0, N_("New &group"),
188 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
189 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, 0, N_("New &entry"),
190 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
191 { B_INSERT, NORMAL_BUTTON, 1, 0, 0, N_("&Insert"),
192 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
193 { B_APPEND, NORMAL_BUTTON, 1, 15, 0, N_("A&ppend"),
194 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
195 { B_REMOVE, NORMAL_BUTTON, 1, 30, 0, N_("&Remove"),
196 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
197 { B_MOVE, NORMAL_BUTTON, 1, 42, 0, N_("&Move"),
198 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
199 /* *INDENT-ON* */
202 static const size_t hotlist_but_num = G_N_ELEMENTS (hotlist_but);
204 static struct hotlist *hotlist = NULL;
206 static struct hotlist *current_group;
208 static GString *tkn_buf = NULL;
210 static char *hotlist_file_name;
211 static FILE *hotlist_file;
212 static time_t hotlist_file_mtime;
214 static int list_level = 0;
216 /* --------------------------------------------------------------------------------------------- */
217 /*** file scope functions ************************************************************************/
218 /* --------------------------------------------------------------------------------------------- */
220 static void init_movelist (struct hotlist *item);
221 static void add_new_group_cmd (void);
222 static void add_new_entry_cmd (WPanel * panel);
223 static void remove_from_hotlist (struct hotlist *entry);
224 static void load_hotlist (void);
225 static void add_dotdot_to_list (void);
227 /* --------------------------------------------------------------------------------------------- */
228 /** If current->data is 0, then we are dealing with a VFS pathname */
230 static void
231 update_path_name (void)
233 const char *text = "";
234 char *p;
235 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
236 Widget *w = WIDGET (list);
238 if (!listbox_is_empty (list))
240 char *ctext = NULL;
241 void *cdata = NULL;
243 listbox_get_current (list, &ctext, &cdata);
244 if (cdata == NULL)
245 text = ctext;
246 else
248 struct hotlist *hlp = (struct hotlist *) cdata;
250 if (hlp->type == HL_TYPE_ENTRY || hlp->type == HL_TYPE_DOTDOT)
251 text = hlp->directory;
252 else if (hlp->type == HL_TYPE_GROUP)
253 text = _("Subgroup - press ENTER to see list");
257 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
258 if (hotlist_state.moving)
259 groupbox_set_title (movelist_group, str_trunc (p, w->rect.cols - 2));
260 else
262 groupbox_set_title (hotlist_group, str_trunc (p, w->rect.cols - 2));
263 label_set_text (pname, str_trunc (text, w->rect.cols));
265 g_free (p);
268 /* --------------------------------------------------------------------------------------------- */
270 static void
271 fill_listbox (WListbox * list)
273 struct hotlist *current;
274 GString *buff;
276 buff = g_string_new ("");
278 for (current = current_group->head; current != NULL; current = current->next)
279 switch (current->type)
281 case HL_TYPE_GROUP:
283 /* buff clean up */
284 g_string_truncate (buff, 0);
285 g_string_append (buff, "->");
286 g_string_append (buff, current->label);
287 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, buff->str, current, FALSE);
289 break;
290 case HL_TYPE_DOTDOT:
291 case HL_TYPE_ENTRY:
292 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, current->label, current, FALSE);
293 break;
294 default:
295 break;
298 g_string_free (buff, TRUE);
301 /* --------------------------------------------------------------------------------------------- */
303 static void
304 unlink_entry (struct hotlist *entry)
306 struct hotlist *current = current_group->head;
308 if (current == entry)
309 current_group->head = entry->next;
310 else
312 while (current != NULL && current->next != entry)
313 current = current->next;
314 if (current != NULL)
315 current->next = entry->next;
317 entry->next = entry->up = NULL;
320 /* --------------------------------------------------------------------------------------------- */
322 #ifdef ENABLE_VFS
323 static void
324 add_name_to_list (const char *path)
326 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, path, NULL, FALSE);
328 #endif /* !ENABLE_VFS */
330 /* --------------------------------------------------------------------------------------------- */
332 static int
333 hotlist_run_cmd (int action)
335 switch (action)
337 case B_MOVE:
339 struct hotlist *saved = current_group;
340 struct hotlist *item = NULL;
341 struct hotlist *moveto_item = NULL;
342 struct hotlist *moveto_group = NULL;
343 int ret;
345 if (listbox_is_empty (l_hotlist))
346 return 0; /* empty group - nothing to do */
348 listbox_get_current (l_hotlist, NULL, (void **) &item);
349 init_movelist (item);
350 hotlist_state.moving = TRUE;
351 ret = dlg_run (movelist_dlg);
352 hotlist_state.moving = FALSE;
353 listbox_get_current (l_movelist, NULL, (void **) &moveto_item);
354 moveto_group = current_group;
355 widget_destroy (WIDGET (movelist_dlg));
356 current_group = saved;
357 if (ret == B_CANCEL)
358 return 0;
359 if (moveto_item == item)
360 return 0; /* If we insert/append a before/after a
361 it hardly changes anything ;) */
362 unlink_entry (item);
363 listbox_remove_current (l_hotlist);
364 item->up = moveto_group;
365 if (moveto_group->head == NULL)
366 moveto_group->head = item;
367 else if (moveto_item == NULL)
368 { /* we have group with just comments */
369 struct hotlist *p = moveto_group->head;
371 /* skip comments */
372 while (p->next != NULL)
373 p = p->next;
374 p->next = item;
376 else if (ret == B_ENTER || ret == B_APPEND)
378 if (moveto_item->next == NULL)
379 moveto_item->next = item;
380 else
382 item->next = moveto_item->next;
383 moveto_item->next = item;
386 else if (moveto_group->head == moveto_item)
388 moveto_group->head = item;
389 item->next = moveto_item;
391 else
393 struct hotlist *p = moveto_group->head;
395 while (p->next != moveto_item)
396 p = p->next;
397 item->next = p->next;
398 p->next = item;
400 listbox_remove_list (l_hotlist);
401 fill_listbox (l_hotlist);
402 repaint_screen ();
403 hotlist_state.modified = TRUE;
404 return 0;
406 case B_REMOVE:
408 struct hotlist *entry = NULL;
410 listbox_get_current (l_hotlist, NULL, (void **) &entry);
411 remove_from_hotlist (entry);
413 return 0;
415 case B_NEW_GROUP:
416 add_new_group_cmd ();
417 return 0;
419 case B_ADD_CURRENT:
420 add2hotlist_cmd (our_panel);
421 return 0;
423 case B_NEW_ENTRY:
424 add_new_entry_cmd (our_panel);
425 return 0;
427 case B_ENTER:
428 case B_ENTER_GROUP:
430 WListbox *list;
431 void *data;
432 struct hotlist *hlp;
434 list = hotlist_state.moving ? l_movelist : l_hotlist;
435 listbox_get_current (list, NULL, &data);
437 if (data == NULL)
438 return 1;
440 hlp = (struct hotlist *) data;
442 if (hlp->type == HL_TYPE_ENTRY)
443 return (action == B_ENTER ? 1 : 0);
444 if (hlp->type != HL_TYPE_DOTDOT)
446 listbox_remove_list (list);
447 current_group = hlp;
448 fill_listbox (list);
449 return 0;
452 MC_FALLTHROUGH; /* if list empty - just go up */
454 case B_UP_GROUP:
456 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
458 listbox_remove_list (list);
459 current_group = current_group->up;
460 fill_listbox (list);
461 return 0;
464 #ifdef ENABLE_VFS
465 case B_FREE_ALL_VFS:
466 vfs_expire (TRUE);
467 MC_FALLTHROUGH;
469 case B_REFRESH_VFS:
470 listbox_remove_list (l_hotlist);
471 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
472 FALSE);
473 vfs_fill_names (add_name_to_list);
474 return 0;
475 #endif /* ENABLE_VFS */
477 default:
478 return 1;
482 /* --------------------------------------------------------------------------------------------- */
484 static int
485 hotlist_button_callback (WButton * button, int action)
487 int ret;
489 (void) button;
490 ret = hotlist_run_cmd (action);
491 update_path_name ();
492 return ret;
495 /* --------------------------------------------------------------------------------------------- */
497 static inline cb_ret_t
498 hotlist_handle_key (WDialog * h, int key)
500 switch (key)
502 case KEY_M_CTRL | '\n':
503 goto l1;
505 case '\n':
506 case KEY_ENTER:
507 if (hotlist_button_callback (NULL, B_ENTER) != 0)
509 h->ret_value = B_ENTER;
510 dlg_close (h);
512 return MSG_HANDLED;
514 case KEY_RIGHT:
515 /* enter to the group */
516 if (hotlist_state.type == LIST_VFSLIST)
517 return MSG_NOT_HANDLED;
518 return hotlist_button_callback (NULL, B_ENTER_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
520 case KEY_LEFT:
521 /* leave the group */
522 if (hotlist_state.type == LIST_VFSLIST)
523 return MSG_NOT_HANDLED;
524 return hotlist_button_callback (NULL, B_UP_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
526 case KEY_DC:
527 if (hotlist_state.moving)
528 return MSG_NOT_HANDLED;
529 hotlist_button_callback (NULL, B_REMOVE);
530 return MSG_HANDLED;
533 case ALT ('\n'):
534 case ALT ('\r'):
535 if (!hotlist_state.moving)
537 void *ldata = NULL;
539 listbox_get_current (l_hotlist, NULL, &ldata);
541 if (ldata != NULL)
543 struct hotlist *hlp = (struct hotlist *) ldata;
545 if (hlp->type == HL_TYPE_ENTRY)
547 char *tmp;
549 tmp = g_strconcat ("cd ", hlp->directory, (char *) NULL);
550 input_insert (cmdline, tmp, FALSE);
551 g_free (tmp);
552 h->ret_value = B_CANCEL;
553 dlg_close (h);
557 return MSG_HANDLED; /* ignore key */
559 default:
560 return MSG_NOT_HANDLED;
564 /* --------------------------------------------------------------------------------------------- */
566 static cb_ret_t
567 hotlist_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
569 WDialog *h = DIALOG (w);
571 switch (msg)
573 case MSG_INIT:
574 case MSG_NOTIFY: /* MSG_NOTIFY is fired by the listbox to tell us the item has changed. */
575 update_path_name ();
576 return MSG_HANDLED;
578 case MSG_UNHANDLED_KEY:
579 return hotlist_handle_key (h, parm);
581 case MSG_POST_KEY:
583 * The code here has two purposes:
585 * (1) Always stay on the hotlist.
587 * Activating a button using its hotkey (and even pressing ENTER, as
588 * there's a "default button") moves the focus to the button. But we
589 * want to stay on the hotlist, to be able to use the usual keys (up,
590 * down, etc.). So we do `widget_select (lst)`.
592 * (2) Refresh the hotlist.
594 * We may have run a command that changed the contents of the list.
595 * We therefore need to refresh it. So we do `widget_draw (lst)`.
598 Widget *lst;
600 lst = WIDGET (h == hotlist_dlg ? l_hotlist : l_movelist);
602 /* widget_select() already redraws the widget, but since it's a
603 * no-op if the widget is already selected ("focused"), we have
604 * to call widget_draw() separately. */
605 if (!widget_get_state (lst, WST_FOCUSED))
606 widget_select (lst);
607 else
608 widget_draw (lst);
610 return MSG_HANDLED;
612 case MSG_RESIZE:
614 WRect r = w->rect;
616 r.lines = LINES - (h == hotlist_dlg ? 2 : 6);
617 r.cols = COLS - 6;
619 return dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
622 default:
623 return dlg_default_callback (w, sender, msg, parm, data);
627 /* --------------------------------------------------------------------------------------------- */
629 static lcback_ret_t
630 hotlist_listbox_callback (WListbox * list)
632 WDialog *dlg = DIALOG (WIDGET (list)->owner);
634 if (!listbox_is_empty (list))
636 void *data = NULL;
638 listbox_get_current (list, NULL, &data);
640 if (data != NULL)
642 struct hotlist *hlp = (struct hotlist *) data;
644 if (hlp->type == HL_TYPE_ENTRY)
646 dlg->ret_value = B_ENTER;
647 dlg_close (dlg);
648 return LISTBOX_DONE;
650 else
652 hotlist_button_callback (NULL, B_ENTER);
653 send_message (dlg, NULL, MSG_POST_KEY, '\n', NULL);
654 return LISTBOX_CONT;
657 else
659 dlg->ret_value = B_ENTER;
660 dlg_close (dlg);
661 return LISTBOX_DONE;
665 hotlist_button_callback (NULL, B_UP_GROUP);
666 send_message (dlg, NULL, MSG_POST_KEY, 'u', NULL);
667 return LISTBOX_CONT;
670 /* --------------------------------------------------------------------------------------------- */
672 * Expands all button names (once) and recalculates button positions.
673 * returns number of columns in the dialog box, which is 10 chars longer
674 * then buttonbar.
676 * If common width of the window (i.e. in xterm) is less than returned
677 * width - sorry :) (anyway this did not handled in previous version too)
680 static int
681 init_i18n_stuff (int list_type, int cols)
683 size_t i;
685 static gboolean i18n_flag = FALSE;
687 if (!i18n_flag)
689 for (i = 0; i < hotlist_but_num; i++)
691 #ifdef ENABLE_NLS
692 hotlist_but[i].text = _(hotlist_but[i].text);
693 #endif /* ENABLE_NLS */
694 hotlist_but[i].len = str_term_width1 (hotlist_but[i].text) + 3;
695 if (hotlist_but[i].flags == DEFPUSH_BUTTON)
696 hotlist_but[i].len += 2;
699 i18n_flag = TRUE;
702 /* Dynamic resizing of buttonbars */
704 int len[2], count[2]; /* at most two lines of buttons */
705 int cur_x[2];
707 len[0] = len[1] = 0;
708 count[0] = count[1] = 0;
709 cur_x[0] = cur_x[1] = 0;
711 /* Count len of buttonbars, assuming 1 extra space between buttons */
712 for (i = 0; i < hotlist_but_num; i++)
713 if ((hotlist_but[i].type & list_type) != 0)
715 int row;
717 row = hotlist_but[i].y;
718 ++count[row];
719 len[row] += hotlist_but[i].len + 1;
722 (len[0])--;
723 (len[1])--;
725 cols = MAX (cols, MAX (len[0], len[1]));
727 /* arrange buttons */
728 for (i = 0; i < hotlist_but_num; i++)
729 if ((hotlist_but[i].type & list_type) != 0)
731 int row;
733 row = hotlist_but[i].y;
735 if (hotlist_but[i].x != 0)
737 /* not first int the row */
738 if (hotlist_but[i].ret_cmd == B_CANCEL)
739 hotlist_but[i].x = cols - hotlist_but[i].len - 6;
740 else
741 hotlist_but[i].x = cur_x[row];
744 cur_x[row] += hotlist_but[i].len + 1;
748 return cols;
751 /* --------------------------------------------------------------------------------------------- */
753 static void
754 init_hotlist (hotlist_t list_type)
756 size_t i;
757 const char *title, *help_node;
758 int lines, cols;
759 int y;
760 int dh = 0;
761 WGroup *g;
762 WGroupbox *path_box;
763 Widget *hotlist_widget;
765 do_refresh ();
767 lines = LINES - 2;
768 cols = init_i18n_stuff (list_type, COLS - 6);
770 #ifdef ENABLE_VFS
771 if (list_type == LIST_VFSLIST)
773 title = _("Active VFS directories");
774 help_node = "[vfshot]"; /* FIXME - no such node */
775 dh = 1;
777 else
778 #endif /* !ENABLE_VFS */
780 title = _("Directory hotlist");
781 help_node = "[Hotlist]";
784 hotlist_dlg =
785 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
786 NULL, help_node, title);
787 g = GROUP (hotlist_dlg);
789 y = UY;
790 hotlist_group = groupbox_new (y, UX, lines - 10 + dh, cols - 2 * UX, _("Top level group"));
791 hotlist_widget = WIDGET (hotlist_group);
792 group_add_widget_autopos (g, hotlist_widget, WPOS_KEEP_ALL, NULL);
794 l_hotlist =
795 listbox_new (y + 1, UX + 1, hotlist_widget->rect.lines - 2, hotlist_widget->rect.cols - 2,
796 FALSE, hotlist_listbox_callback);
798 /* Fill the hotlist with the active VFS or the hotlist */
799 #ifdef ENABLE_VFS
800 if (list_type == LIST_VFSLIST)
802 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
803 FALSE);
804 vfs_fill_names (add_name_to_list);
806 else
807 #endif /* !ENABLE_VFS */
808 fill_listbox (l_hotlist);
810 /* insert before groupbox to view scrollbar */
811 group_add_widget_autopos (g, l_hotlist, WPOS_KEEP_ALL, NULL);
813 y += hotlist_widget->rect.lines;
815 path_box = groupbox_new (y, UX, 3, hotlist_widget->rect.cols, _("Directory path"));
816 group_add_widget_autopos (g, path_box, WPOS_KEEP_BOTTOM | WPOS_KEEP_HORZ, NULL);
818 pname = label_new (y + 1, UX + 2, NULL);
819 group_add_widget_autopos (g, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
820 y += WIDGET (path_box)->rect.lines;
822 group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
824 for (i = 0; i < hotlist_but_num; i++)
825 if ((hotlist_but[i].type & list_type) != 0)
826 group_add_widget_autopos (g,
827 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
828 hotlist_but[i].ret_cmd, hotlist_but[i].flags,
829 hotlist_but[i].text, hotlist_button_callback),
830 hotlist_but[i].pos_flags, NULL);
832 widget_select (WIDGET (l_hotlist));
835 /* --------------------------------------------------------------------------------------------- */
837 static void
838 init_movelist (struct hotlist *item)
840 size_t i;
841 char *hdr;
842 int lines, cols;
843 int y;
844 WGroup *g;
845 Widget *movelist_widget;
847 do_refresh ();
849 lines = LINES - 6;
850 cols = init_i18n_stuff (LIST_MOVELIST, COLS - 6);
852 hdr = g_strdup_printf (_("Moving %s"), item->label);
854 movelist_dlg =
855 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
856 NULL, "[Hotlist]", hdr);
857 g = GROUP (movelist_dlg);
859 g_free (hdr);
861 y = UY;
862 movelist_group = groupbox_new (y, UX, lines - 7, cols - 2 * UX, _("Directory label"));
863 movelist_widget = WIDGET (movelist_group);
864 group_add_widget_autopos (g, movelist_widget, WPOS_KEEP_ALL, NULL);
866 l_movelist =
867 listbox_new (y + 1, UX + 1, movelist_widget->rect.lines - 2, movelist_widget->rect.cols - 2,
868 FALSE, hotlist_listbox_callback);
869 fill_listbox (l_movelist);
870 /* insert before groupbox to view scrollbar */
871 group_add_widget_autopos (g, l_movelist, WPOS_KEEP_ALL, NULL);
873 y += movelist_widget->rect.lines;
875 group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
877 for (i = 0; i < hotlist_but_num; i++)
878 if ((hotlist_but[i].type & LIST_MOVELIST) != 0)
879 group_add_widget_autopos (g,
880 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
881 hotlist_but[i].ret_cmd, hotlist_but[i].flags,
882 hotlist_but[i].text, hotlist_button_callback),
883 hotlist_but[i].pos_flags, NULL);
885 widget_select (WIDGET (l_movelist));
888 /* --------------------------------------------------------------------------------------------- */
890 * Destroy the list dialog.
891 * Don't confuse with done_hotlist() for the list in memory.
894 static void
895 hotlist_done (void)
897 widget_destroy (WIDGET (hotlist_dlg));
898 l_hotlist = NULL;
899 #if 0
900 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
901 #endif
902 repaint_screen ();
905 /* --------------------------------------------------------------------------------------------- */
907 static inline char *
908 find_group_section (struct hotlist *grp)
910 return g_strconcat (grp->directory, ".Group", (char *) NULL);
913 /* --------------------------------------------------------------------------------------------- */
915 static struct hotlist *
916 add2hotlist (char *label, char *directory, enum HotListType type, listbox_append_t pos)
918 struct hotlist *new;
919 struct hotlist *current = NULL;
922 * Hotlist is neither loaded nor loading.
923 * Must be called by "Ctrl-x a" before using hotlist.
925 if (current_group == NULL)
926 load_hotlist ();
928 listbox_get_current (l_hotlist, NULL, (void **) &current);
930 /* Make sure '..' stays at the top of the list. */
931 if ((current != NULL) && (current->type == HL_TYPE_DOTDOT))
932 pos = LISTBOX_APPEND_AFTER;
934 new = g_new0 (struct hotlist, 1);
936 new->type = type;
937 new->label = label;
938 new->directory = directory;
939 new->up = current_group;
941 if (type == HL_TYPE_GROUP)
943 current_group = new;
944 add_dotdot_to_list ();
945 current_group = new->up;
948 if (current_group->head == NULL)
950 /* first element in group */
951 current_group->head = new;
953 else if (pos == LISTBOX_APPEND_AFTER)
955 new->next = current->next;
956 current->next = new;
958 else if (pos == LISTBOX_APPEND_BEFORE && current == current_group->head)
960 /* should be inserted before first item */
961 new->next = current;
962 current_group->head = new;
964 else if (pos == LISTBOX_APPEND_BEFORE)
966 struct hotlist *p = current_group->head;
968 while (p->next != current)
969 p = p->next;
971 new->next = current;
972 p->next = new;
974 else
975 { /* append at the end */
976 struct hotlist *p = current_group->head;
978 while (p->next != NULL)
979 p = p->next;
981 p->next = new;
984 if (hotlist_state.running && type != HL_TYPE_COMMENT && type != HL_TYPE_DOTDOT)
986 if (type == HL_TYPE_GROUP)
988 char *lbl;
990 lbl = g_strconcat ("->", new->label, (char *) NULL);
991 listbox_add_item (l_hotlist, pos, 0, lbl, new, FALSE);
992 g_free (lbl);
994 else
995 listbox_add_item (l_hotlist, pos, 0, new->label, new, FALSE);
996 listbox_set_current (l_hotlist, l_hotlist->current);
999 return new;
1002 /* --------------------------------------------------------------------------------------------- */
1004 static int
1005 add_new_entry_input (const char *header, const char *text1, const char *text2,
1006 const char *help, char **r1, char **r2)
1008 quick_widget_t quick_widgets[] = {
1009 /* *INDENT-OFF* */
1010 QUICK_LABELED_INPUT (text1, input_label_above, *r1, "input-lbl", r1, NULL,
1011 FALSE, FALSE, INPUT_COMPLETE_NONE),
1012 QUICK_SEPARATOR (FALSE),
1013 QUICK_LABELED_INPUT (text2, input_label_above, *r2, "input-lbl", r2, NULL,
1014 FALSE, FALSE, INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD),
1015 QUICK_START_BUTTONS (TRUE, TRUE),
1016 QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1017 QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1018 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1019 QUICK_END
1020 /* *INDENT-ON* */
1023 WRect r = { -1, -1, 0, 64 };
1025 quick_dialog_t qdlg = {
1026 r, header, help,
1027 quick_widgets, NULL, NULL
1030 int ret;
1032 ret = quick_dialog (&qdlg);
1034 return (ret != B_CANCEL) ? ret : 0;
1037 /* --------------------------------------------------------------------------------------------- */
1039 static void
1040 add_new_entry_cmd (WPanel * panel)
1042 char *title, *url, *to_free;
1043 int ret;
1045 /* Take current directory as default value for input fields */
1046 to_free = title = url = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1048 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label:"),
1049 _("Directory path:"), "[Hotlist]", &title, &url);
1050 g_free (to_free);
1052 if (ret == 0)
1053 return;
1054 if (title == NULL || *title == '\0' || url == NULL || *url == '\0')
1056 g_free (title);
1057 g_free (url);
1058 return;
1061 if (ret == B_ENTER || ret == B_APPEND)
1062 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AFTER);
1063 else
1064 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_BEFORE);
1066 hotlist_state.modified = TRUE;
1069 /* --------------------------------------------------------------------------------------------- */
1071 static int
1072 add_new_group_input (const char *header, const char *label, char **result)
1074 quick_widget_t quick_widgets[] = {
1075 /* *INDENT-OFF* */
1076 QUICK_LABELED_INPUT (label, input_label_above, "", "input", result, NULL,
1077 FALSE, FALSE, INPUT_COMPLETE_NONE),
1078 QUICK_START_BUTTONS (TRUE, TRUE),
1079 QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1080 QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1081 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1082 QUICK_END
1083 /* *INDENT-ON* */
1086 WRect r = { -1, -1, 0, 64 };
1088 quick_dialog_t qdlg = {
1089 r, header, "[Hotlist]",
1090 quick_widgets, NULL, NULL
1093 int ret;
1095 ret = quick_dialog (&qdlg);
1097 return (ret != B_CANCEL) ? ret : 0;
1100 /* --------------------------------------------------------------------------------------------- */
1102 static void
1103 add_new_group_cmd (void)
1105 char *label;
1106 int ret;
1108 ret = add_new_group_input (_("New hotlist group"), _("Name of new group:"), &label);
1109 if (ret == 0 || label == NULL || *label == '\0')
1110 return;
1112 if (ret == B_ENTER || ret == B_APPEND)
1113 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_AFTER);
1114 else
1115 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_BEFORE);
1117 hotlist_state.modified = TRUE;
1120 /* --------------------------------------------------------------------------------------------- */
1122 static void
1123 remove_group (struct hotlist *grp)
1125 struct hotlist *current = grp->head;
1127 while (current != NULL)
1129 struct hotlist *next = current->next;
1131 if (current->type == HL_TYPE_GROUP)
1132 remove_group (current);
1134 g_free (current->label);
1135 g_free (current->directory);
1136 g_free (current);
1138 current = next;
1142 /* --------------------------------------------------------------------------------------------- */
1144 static void
1145 remove_from_hotlist (struct hotlist *entry)
1147 if (entry == NULL)
1148 return;
1150 if (entry->type == HL_TYPE_DOTDOT)
1151 return;
1153 if (confirm_directory_hotlist_delete)
1155 char text[BUF_MEDIUM];
1156 int result;
1158 if (safe_delete)
1159 query_set_sel (1);
1161 g_snprintf (text, sizeof (text), _("Are you sure you want to remove entry \"%s\"?"),
1162 str_trunc (entry->label, 30));
1163 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1164 _("&Yes"), _("&No"));
1165 if (result != 0)
1166 return;
1169 if (entry->type == HL_TYPE_GROUP)
1171 struct hotlist *head = entry->head;
1173 if (head != NULL && (head->type != HL_TYPE_DOTDOT || head->next != NULL))
1175 char text[BUF_MEDIUM];
1176 int result;
1178 g_snprintf (text, sizeof (text), _("Group \"%s\" is not empty.\nRemove it?"),
1179 str_trunc (entry->label, 30));
1180 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1181 _("&Yes"), _("&No"));
1182 if (result != 0)
1183 return;
1186 remove_group (entry);
1189 unlink_entry (entry);
1191 g_free (entry->label);
1192 g_free (entry->directory);
1193 g_free (entry);
1194 /* now remove list entry from screen */
1195 listbox_remove_current (l_hotlist);
1196 hotlist_state.modified = TRUE;
1199 /* --------------------------------------------------------------------------------------------- */
1201 static void
1202 load_group (struct hotlist *grp)
1204 gchar **profile_keys, **keys;
1205 char *group_section;
1206 struct hotlist *current = 0;
1208 group_section = find_group_section (grp);
1210 keys = mc_config_get_keys (mc_global.main_config, group_section, NULL);
1212 current_group = grp;
1214 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1215 add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1216 g_strdup (*profile_keys), HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1218 g_strfreev (keys);
1220 keys = mc_config_get_keys (mc_global.main_config, grp->directory, NULL);
1222 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1223 add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1224 g_strdup (*profile_keys), HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1226 g_free (group_section);
1227 g_strfreev (keys);
1229 for (current = grp->head; current; current = current->next)
1230 load_group (current);
1233 /* --------------------------------------------------------------------------------------------- */
1235 static int
1236 hot_skip_blanks (void)
1238 int c;
1240 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1242 return c;
1245 /* --------------------------------------------------------------------------------------------- */
1247 static int
1248 hot_next_token (void)
1250 int c, ret = 0;
1251 size_t l;
1253 if (tkn_buf == NULL)
1254 tkn_buf = g_string_new ("");
1255 g_string_set_size (tkn_buf, 0);
1257 again:
1258 c = hot_skip_blanks ();
1259 switch (c)
1261 case EOF:
1262 ret = TKN_EOF;
1263 break;
1264 case '\n':
1265 ret = TKN_EOL;
1266 break;
1267 case '#':
1268 while ((c = getc (hotlist_file)) != EOF && c != '\n')
1269 g_string_append_c (tkn_buf, c);
1270 ret = TKN_COMMENT;
1271 break;
1272 case '"':
1273 while ((c = getc (hotlist_file)) != EOF && c != '"')
1275 if (c == '\\')
1277 c = getc (hotlist_file);
1278 if (c == EOF)
1280 g_string_free (tkn_buf, TRUE);
1281 return TKN_EOF;
1284 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1286 ret = (c == EOF) ? TKN_EOF : TKN_STRING;
1287 break;
1288 case '\\':
1289 c = getc (hotlist_file);
1290 if (c == EOF)
1292 g_string_free (tkn_buf, TRUE);
1293 return TKN_EOF;
1295 if (c == '\n')
1296 goto again;
1298 MC_FALLTHROUGH; /* it is taken as normal character */
1300 default:
1303 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1305 while ((c = fgetc (hotlist_file)) != EOF && (g_ascii_isalnum (c) || !isascii (c)));
1306 if (c != EOF)
1307 ungetc (c, hotlist_file);
1308 l = tkn_buf->len;
1309 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1310 ret = TKN_GROUP;
1311 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1312 ret = TKN_ENTRY;
1313 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1314 ret = TKN_ENDGROUP;
1315 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1316 ret = TKN_URL;
1317 else
1318 ret = TKN_UNKNOWN;
1319 break;
1321 return ret;
1324 /* --------------------------------------------------------------------------------------------- */
1326 static void
1327 hot_load_group (struct hotlist *grp)
1329 int tkn;
1330 struct hotlist *new_grp;
1331 char *label, *url;
1333 current_group = grp;
1335 while ((tkn = hot_next_token ()) != TKN_ENDGROUP)
1336 switch (tkn)
1338 case TKN_GROUP:
1339 CHECK_TOKEN (TKN_STRING);
1340 new_grp =
1341 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1342 LISTBOX_APPEND_AT_END);
1343 SKIP_TO_EOL;
1344 hot_load_group (new_grp);
1345 current_group = grp;
1346 break;
1347 case TKN_ENTRY:
1349 CHECK_TOKEN (TKN_STRING);
1350 label = g_strndup (tkn_buf->str, tkn_buf->len);
1351 CHECK_TOKEN (TKN_URL);
1352 CHECK_TOKEN (TKN_STRING);
1353 url = tilde_expand (tkn_buf->str);
1354 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1355 SKIP_TO_EOL;
1357 break;
1358 case TKN_COMMENT:
1359 label = g_strndup (tkn_buf->str, tkn_buf->len);
1360 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1361 break;
1362 case TKN_EOF:
1363 hotlist_state.readonly = TRUE;
1364 hotlist_state.file_error = TRUE;
1365 return;
1366 case TKN_EOL:
1367 /* skip empty lines */
1368 break;
1369 default:
1370 hotlist_state.readonly = TRUE;
1371 hotlist_state.file_error = TRUE;
1372 SKIP_TO_EOL;
1373 break;
1375 SKIP_TO_EOL;
1378 /* --------------------------------------------------------------------------------------------- */
1380 static void
1381 hot_load_file (struct hotlist *grp)
1383 int tkn;
1384 struct hotlist *new_grp;
1385 char *label, *url;
1387 current_group = grp;
1389 while ((tkn = hot_next_token ()) != TKN_EOF)
1390 switch (tkn)
1392 case TKN_GROUP:
1393 CHECK_TOKEN (TKN_STRING);
1394 new_grp =
1395 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1396 LISTBOX_APPEND_AT_END);
1397 SKIP_TO_EOL;
1398 hot_load_group (new_grp);
1399 current_group = grp;
1400 break;
1401 case TKN_ENTRY:
1403 CHECK_TOKEN (TKN_STRING);
1404 label = g_strndup (tkn_buf->str, tkn_buf->len);
1405 CHECK_TOKEN (TKN_URL);
1406 CHECK_TOKEN (TKN_STRING);
1407 url = tilde_expand (tkn_buf->str);
1408 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1409 SKIP_TO_EOL;
1411 break;
1412 case TKN_COMMENT:
1413 label = g_strndup (tkn_buf->str, tkn_buf->len);
1414 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1415 break;
1416 case TKN_EOL:
1417 /* skip empty lines */
1418 break;
1419 default:
1420 hotlist_state.readonly = TRUE;
1421 hotlist_state.file_error = TRUE;
1422 SKIP_TO_EOL;
1423 break;
1427 /* --------------------------------------------------------------------------------------------- */
1429 static void
1430 clean_up_hotlist_groups (const char *section)
1432 char *grp_section;
1434 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1435 if (mc_config_has_group (mc_global.main_config, section))
1436 mc_config_del_group (mc_global.main_config, section);
1438 if (mc_config_has_group (mc_global.main_config, grp_section))
1440 char **profile_keys, **keys;
1442 keys = mc_config_get_keys (mc_global.main_config, grp_section, NULL);
1444 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1445 clean_up_hotlist_groups (*profile_keys);
1447 g_strfreev (keys);
1448 mc_config_del_group (mc_global.main_config, grp_section);
1450 g_free (grp_section);
1453 /* --------------------------------------------------------------------------------------------- */
1455 static void
1456 load_hotlist (void)
1458 gboolean remove_old_list = FALSE;
1459 struct stat stat_buf;
1461 if (hotlist_state.loaded)
1463 stat (hotlist_file_name, &stat_buf);
1464 if (hotlist_file_mtime < stat_buf.st_mtime)
1465 done_hotlist ();
1466 else
1467 return;
1470 if (hotlist_file_name == NULL)
1471 hotlist_file_name = mc_config_get_full_path (MC_HOTLIST_FILE);
1473 hotlist = g_new0 (struct hotlist, 1);
1474 hotlist->type = HL_TYPE_GROUP;
1475 hotlist->label = g_strdup (_("Top level group"));
1476 hotlist->up = hotlist;
1478 * compatibility :-(
1480 hotlist->directory = g_strdup ("Hotlist");
1482 hotlist_file = fopen (hotlist_file_name, "r");
1483 if (hotlist_file == NULL)
1485 int result;
1487 load_group (hotlist);
1488 hotlist_state.loaded = TRUE;
1490 * just to be sure we got copy
1492 hotlist_state.modified = TRUE;
1493 result = save_hotlist ();
1494 hotlist_state.modified = FALSE;
1495 if (result != 0)
1496 remove_old_list = TRUE;
1497 else
1498 message (D_ERROR, _("Hotlist Load"),
1500 ("MC was unable to write %s file,\nyour old hotlist entries were not deleted"),
1501 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1503 else
1505 hot_load_file (hotlist);
1506 fclose (hotlist_file);
1507 hotlist_state.loaded = TRUE;
1510 if (remove_old_list)
1512 GError *mcerror = NULL;
1514 clean_up_hotlist_groups ("Hotlist");
1515 if (!mc_config_save_file (mc_global.main_config, &mcerror))
1516 setup_save_config_show_error (mc_global.main_config->ini_path, &mcerror);
1518 mc_error_message (&mcerror, NULL);
1521 stat (hotlist_file_name, &stat_buf);
1522 hotlist_file_mtime = stat_buf.st_mtime;
1523 current_group = hotlist;
1526 /* --------------------------------------------------------------------------------------------- */
1528 static void
1529 hot_save_group (struct hotlist *grp)
1531 struct hotlist *current;
1532 int i;
1533 char *s;
1535 #define INDENT(n) \
1536 do { \
1537 for (i = 0; i < n; i++) \
1538 putc (' ', hotlist_file); \
1539 } while (0)
1541 for (current = grp->head; current != NULL; current = current->next)
1542 switch (current->type)
1544 case HL_TYPE_GROUP:
1545 INDENT (list_level);
1546 fputs ("GROUP \"", hotlist_file);
1547 for (s = current->label; *s != '\0'; s++)
1549 if (*s == '"' || *s == '\\')
1550 putc ('\\', hotlist_file);
1551 putc (*s, hotlist_file);
1553 fputs ("\"\n", hotlist_file);
1554 list_level += 2;
1555 hot_save_group (current);
1556 list_level -= 2;
1557 INDENT (list_level);
1558 fputs ("ENDGROUP\n", hotlist_file);
1559 break;
1560 case HL_TYPE_ENTRY:
1561 INDENT (list_level);
1562 fputs ("ENTRY \"", hotlist_file);
1563 for (s = current->label; *s != '\0'; s++)
1565 if (*s == '"' || *s == '\\')
1566 putc ('\\', hotlist_file);
1567 putc (*s, hotlist_file);
1569 fputs ("\" URL \"", hotlist_file);
1570 for (s = current->directory; *s != '\0'; s++)
1572 if (*s == '"' || *s == '\\')
1573 putc ('\\', hotlist_file);
1574 putc (*s, hotlist_file);
1576 fputs ("\"\n", hotlist_file);
1577 break;
1578 case HL_TYPE_COMMENT:
1579 fprintf (hotlist_file, "#%s\n", current->label);
1580 break;
1581 case HL_TYPE_DOTDOT:
1582 /* do nothing */
1583 break;
1584 default:
1585 break;
1589 /* --------------------------------------------------------------------------------------------- */
1591 static void
1592 add_dotdot_to_list (void)
1594 if (current_group != hotlist && hotlist_has_dot_dot)
1595 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, LISTBOX_APPEND_AT_END);
1598 /* --------------------------------------------------------------------------------------------- */
1599 /*** public functions ****************************************************************************/
1600 /* --------------------------------------------------------------------------------------------- */
1602 void
1603 add2hotlist_cmd (WPanel * panel)
1605 char *lc_prompt;
1606 const char *cp = N_("Label for \"%s\":");
1607 int l;
1608 char *label_string, *label;
1610 #ifdef ENABLE_NLS
1611 cp = _(cp);
1612 #endif
1614 /* extra variable to use it in the button callback */
1615 our_panel = panel;
1617 l = str_term_width1 (cp);
1618 label_string = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1619 lc_prompt = g_strdup_printf (cp, str_trunc (label_string, COLS - 2 * UX - (l + 8)));
1620 label =
1621 input_dialog (_("Add to hotlist"), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string,
1622 INPUT_COMPLETE_NONE);
1623 g_free (lc_prompt);
1625 if (label == NULL || *label == '\0')
1627 g_free (label_string);
1628 g_free (label);
1630 else
1632 add2hotlist (label, label_string, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1633 hotlist_state.modified = TRUE;
1637 /* --------------------------------------------------------------------------------------------- */
1639 char *
1640 hotlist_show (hotlist_t list_type, WPanel * panel)
1642 char *target = NULL;
1643 int res;
1645 /* extra variable to use it in the button callback */
1646 our_panel = panel;
1648 hotlist_state.type = list_type;
1649 load_hotlist ();
1651 init_hotlist (list_type);
1653 /* display file info */
1654 tty_setcolor (SELECTED_COLOR);
1656 hotlist_state.running = TRUE;
1657 res = dlg_run (hotlist_dlg);
1658 hotlist_state.running = FALSE;
1659 save_hotlist ();
1661 if (res == B_ENTER)
1663 char *text = NULL;
1664 struct hotlist *hlp = NULL;
1666 listbox_get_current (l_hotlist, &text, (void **) &hlp);
1667 target = g_strdup (hlp != NULL ? hlp->directory : text);
1670 hotlist_done ();
1671 return target;
1674 /* --------------------------------------------------------------------------------------------- */
1676 gboolean
1677 save_hotlist (void)
1679 gboolean saved = FALSE;
1680 struct stat stat_buf;
1682 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name != NULL)
1684 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1686 hotlist_file = fopen (hotlist_file_name, "w");
1687 if (hotlist_file == NULL)
1688 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1689 else
1691 hot_save_group (hotlist);
1692 fclose (hotlist_file);
1693 stat (hotlist_file_name, &stat_buf);
1694 hotlist_file_mtime = stat_buf.st_mtime;
1695 hotlist_state.modified = FALSE;
1696 saved = TRUE;
1700 return saved;
1703 /* --------------------------------------------------------------------------------------------- */
1705 * Unload list from memory.
1706 * Don't confuse with hotlist_done() for GUI.
1709 void
1710 done_hotlist (void)
1712 if (hotlist != NULL)
1714 remove_group (hotlist);
1715 g_free (hotlist->label);
1716 g_free (hotlist->directory);
1717 MC_PTR_FREE (hotlist);
1720 hotlist_state.loaded = FALSE;
1722 MC_PTR_FREE (hotlist_file_name);
1723 l_hotlist = NULL;
1724 current_group = NULL;
1726 if (tkn_buf != NULL)
1728 g_string_free (tkn_buf, TRUE);
1729 tkn_buf = NULL;
1733 /* --------------------------------------------------------------------------------------------- */