Moved dir $(srcdir)/syntax into $(srcdir)/misc/syntax
[midnight-commander.git] / src / hotlist.c
blob03eafbee54b224db609002460e4123dd8c35743a
1 /* Directory hotlist -- for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
3 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5 Written by:
6 1994 Radek Doulik
7 1995 Janne Kukonlehto
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 Janne did the original Hotlist code, Andrej made the groupable
12 hotlist; the move hotlist and revamped the file format and made
13 it stronger.
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 /** \file hotlist.c
31 * \brief Source: directory hotlist
34 #include <config.h>
36 #include <ctype.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
43 #include "global.h"
45 #include "../src/tty/tty.h" /* COLS */
46 #include "../src/skin/skin.h"
47 #include "../src/tty/key.h" /* KEY_M_CTRL */
49 #include "dialog.h"
50 #include "widget.h"
51 #include "setup.h" /* For profile_bname */
52 #include "../src/mcconfig/mcconfig.h" /* Load/save directories hotlist */
53 #include "wtools.h" /* QuickDialog */
54 #include "panel.h" /* current_panel */
55 #include "main.h" /* update_panels() */
56 #include "layout.h" /* repaint_screen() */
57 #include "hotlist.h"
58 #include "command.h" /* cmdline */
59 #include "history.h"
60 #include "strutil.h"
61 #include "util.h"
62 #include "fileloc.h"
64 #define UX 5
65 #define UY 2
67 #define BX UX
68 #define BY (LINES - 6)
70 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
71 #define LABELS 3
72 #define B_ADD_CURRENT B_USER
73 #define B_REMOVE (B_USER + 1)
74 #define B_NEW_GROUP (B_USER + 2)
75 #define B_NEW_ENTRY (B_USER + 3)
76 #define B_UP_GROUP (B_USER + 4)
77 #define B_INSERT (B_USER + 5)
78 #define B_APPEND (B_USER + 6)
79 #define B_MOVE (B_USER + 7)
81 #ifdef ENABLE_VFS
82 #include "../vfs/gc.h"
83 #define B_FREE_ALL_VFS (B_USER + 8)
84 #define B_REFRESH_VFS (B_USER + 9)
85 #endif
87 int hotlist_has_dot_dot = 1;
89 static WListbox *l_hotlist;
90 static WListbox *l_movelist;
92 static Dlg_head *hotlist_dlg;
93 static Dlg_head *movelist_dlg;
95 static WLabel *pname, *pname_group, *movelist_group;
97 enum HotListType {
98 HL_TYPE_GROUP,
99 HL_TYPE_ENTRY,
100 HL_TYPE_COMMENT,
101 HL_TYPE_DOTDOT
104 static struct {
106 * these parameters are intended to be user configurable
108 int expanded; /* expanded view of all groups at startup */
111 * these reflect run time state
114 int loaded; /* hotlist is loaded */
115 int readonly; /* hotlist readonly */
116 int file_error; /* parse error while reading file */
117 int running; /* we are running dlg (and have to
118 update listbox */
119 int moving; /* we are in moving hotlist currently */
120 int modified; /* hotlist was modified */
121 int type; /* LIST_HOTLIST || LIST_VFSLIST */
122 } hotlist_state;
124 static struct _hotlist_but {
125 int ret_cmd, flags, y, x;
126 const char *text;
127 int type;
128 widget_pos_flags_t pos_flags;
129 } hotlist_but[] = {
130 { B_MOVE, NORMAL_BUTTON, 1, 42, N_("&Move"),
131 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
132 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"),
133 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
134 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"),
135 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
136 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"),
137 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
138 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &Entry"),
139 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
140 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &Group"),
141 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
142 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"),
143 LIST_HOTLIST | LIST_VFSLIST|LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
144 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"),
145 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
146 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"),
147 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
148 #ifdef ENABLE_VFS
149 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, N_("&Refresh"),
150 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
151 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, N_("Fr&ee VFSs now"),
152 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
153 #endif
154 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &To"),
155 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
158 /* Directory hotlist */
159 static struct hotlist{
160 enum HotListType type;
161 char *directory;
162 char *label;
163 struct hotlist *head;
164 struct hotlist *up;
165 struct hotlist *next;
166 } *hotlist = NULL;
168 static struct hotlist *current_group;
170 static void init_movelist (int, struct hotlist *);
171 static void add_new_group_cmd (void);
172 static void add_new_entry_cmd (void);
173 static void remove_from_hotlist (struct hotlist *entry);
174 static void load_hotlist (void);
175 static void add_dotdot_to_list (void);
177 #define new_hotlist() g_new0(struct hotlist, 1)
179 static void
180 hotlist_refresh (Dlg_head * dlg)
182 common_dialog_repaint (dlg);
183 tty_setcolor (COLOR_NORMAL);
184 draw_box (dlg, 2, 5, dlg->lines - (hotlist_state.moving ? 6 : 10),
185 dlg->cols - (UX * 2));
186 if (!hotlist_state.moving)
187 draw_box (dlg, dlg->lines - 8, 5, 3, dlg->cols - (UX * 2));
190 /* If current->data is 0, then we are dealing with a VFS pathname */
191 static void
192 update_path_name (void)
194 const char *text = "";
195 char *p;
196 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
197 Dlg_head *dlg = list->widget.parent;
199 if (list->current) {
200 if (list->current->data != 0) {
201 struct hotlist *hlp = (struct hotlist *) list->current->data;
203 if (hlp->type == HL_TYPE_ENTRY ||
204 hlp->type == HL_TYPE_DOTDOT)
205 text = hlp->directory;
206 else if (hlp->type == HL_TYPE_GROUP)
207 text = _("Subgroup - press ENTER to see list");
208 } else {
209 text = list->current->text;
212 if (!hotlist_state.moving)
213 label_set_text (pname,
214 str_trunc (text, dlg->cols - (UX * 2 + 4)));
216 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
217 if (!hotlist_state.moving)
218 label_set_text (pname_group,
219 str_trunc (p, dlg->cols - (UX * 2 + 4)));
220 else
221 label_set_text (movelist_group,
222 str_trunc (p, dlg->cols - (UX * 2 + 4)));
223 g_free (p);
225 dlg_redraw (dlg);
228 #define CHECK_BUFFER \
229 do { \
230 int i; \
232 if ((i = strlen (current->label) + 3) > buflen) { \
233 g_free (buf); \
234 buf = g_malloc (buflen = 1024 * (i/1024 + 1)); \
236 buf[0] = '\0'; \
237 } while (0)
239 static void fill_listbox (void)
241 struct hotlist *current = current_group->head;
242 GString *buff = g_string_new ("");
244 while (current){
245 switch (current->type) {
246 case HL_TYPE_GROUP:
248 /* buff clean up */
249 g_string_truncate(buff, 0);
250 g_string_append(buff,"->");
251 g_string_append(buff,current->label);
252 if (hotlist_state.moving)
253 listbox_add_item (l_movelist, 0, 0, buff->str, current);
254 else
255 listbox_add_item (l_hotlist, 0, 0, buff->str, current);
257 break;
258 case HL_TYPE_DOTDOT:
259 case HL_TYPE_ENTRY:
260 if (hotlist_state.moving)
261 listbox_add_item (l_movelist, 0, 0, current->label, current);
262 else
263 listbox_add_item (l_hotlist, 0, 0, current->label, current);
264 break;
265 default:
266 break;
268 current = current->next;
270 g_string_free (buff, TRUE);
273 static void
274 unlink_entry (struct hotlist *entry)
276 struct hotlist *current = current_group->head;
278 if (current == entry)
279 current_group->head = entry->next;
280 else {
281 while (current && current->next != entry)
282 current = current->next;
283 if (current)
284 current->next = entry->next;
286 entry->next =
287 entry->up = 0;
290 #ifdef ENABLE_VFS
291 static void add_name_to_list (const char *path)
293 listbox_add_item (l_hotlist, 0, 0, path, 0);
295 #endif /* !ENABLE_VFS */
297 static int
298 hotlist_button_callback (int action)
300 switch (action) {
301 case B_MOVE:
303 struct hotlist *saved = current_group;
304 struct hotlist *item;
305 struct hotlist *moveto_item = 0;
306 struct hotlist *moveto_group = 0;
307 int ret;
309 if (!l_hotlist->current)
310 return MSG_NOT_HANDLED; /* empty group - nothing to do */
311 item = l_hotlist->current->data;
312 hotlist_state.moving = 1;
313 init_movelist (LIST_MOVELIST, item);
314 run_dlg (movelist_dlg);
315 ret = movelist_dlg->ret_value;
316 hotlist_state.moving = 0;
317 if (l_movelist->current)
318 moveto_item = l_movelist->current->data;
319 moveto_group = current_group;
320 destroy_dlg (movelist_dlg);
321 current_group = saved;
322 if (ret == B_CANCEL)
323 return MSG_NOT_HANDLED;
324 if (moveto_item == item)
325 return MSG_NOT_HANDLED; /* If we insert/append a before/after a
326 it hardly changes anything ;) */
327 unlink_entry (item);
328 listbox_remove_current (l_hotlist, 1);
329 item->up = moveto_group;
330 if (!moveto_group->head)
331 moveto_group->head = item;
332 else if (!moveto_item) { /* we have group with just comments */
333 struct hotlist *p = moveto_group->head;
335 /* skip comments */
336 while (p->next)
337 p = p->next;
338 p->next = item;
339 } else if (ret == B_ENTER || ret == B_APPEND)
340 if (!moveto_item->next)
341 moveto_item->next = item;
342 else {
343 item->next = moveto_item->next;
344 moveto_item->next = item;
345 } else if (moveto_group->head == moveto_item) {
346 moveto_group->head = item;
347 item->next = moveto_item;
348 } else {
349 struct hotlist *p = moveto_group->head;
351 while (p->next != moveto_item)
352 p = p->next;
353 item->next = p->next;
354 p->next = item;
356 listbox_remove_list (l_hotlist);
357 fill_listbox ();
358 repaint_screen ();
359 hotlist_state.modified = 1;
360 return MSG_NOT_HANDLED;
361 break;
363 case B_REMOVE:
364 if (l_hotlist->current && l_hotlist->current->data)
365 remove_from_hotlist (l_hotlist->current->data);
366 return MSG_NOT_HANDLED;
367 break;
369 case B_NEW_GROUP:
370 add_new_group_cmd ();
371 return MSG_NOT_HANDLED;
372 break;
374 case B_ADD_CURRENT:
375 add2hotlist_cmd ();
376 return MSG_NOT_HANDLED;
377 break;
379 case B_NEW_ENTRY:
380 add_new_entry_cmd ();
381 return MSG_NOT_HANDLED;
382 break;
384 case B_ENTER:
386 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
387 if (list->current) {
388 if (list->current->data) {
389 struct hotlist *hlp =
390 (struct hotlist *) list->current->data;
391 if (hlp->type == HL_TYPE_ENTRY)
392 return MSG_HANDLED;
393 else if (hlp->type == HL_TYPE_DOTDOT) {
394 /* Fall through - go up */
397 else {
398 listbox_remove_list (list);
399 current_group = hlp;
400 fill_listbox ();
401 return MSG_NOT_HANDLED;
403 } else
404 return MSG_HANDLED;
407 /* Fall through if list empty - just go up */
409 case B_UP_GROUP:
411 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
412 listbox_remove_list (list);
413 current_group = current_group->up;
414 fill_listbox ();
415 return MSG_NOT_HANDLED;
416 break;
419 #ifdef ENABLE_VFS
420 case B_FREE_ALL_VFS:
421 vfs_expire (1);
422 /* fall through */
424 case B_REFRESH_VFS:
425 listbox_remove_list (l_hotlist);
426 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
427 vfs_fill_names (add_name_to_list);
428 return MSG_NOT_HANDLED;
429 #endif /* ENABLE_VFS */
431 default:
432 return MSG_HANDLED;
433 break;
438 static cb_ret_t
439 hotlist_callback (Dlg_head *h, Widget *sender,
440 dlg_msg_t msg, int parm, void *data)
442 switch (msg) {
443 case DLG_DRAW:
444 hotlist_refresh (h);
445 return MSG_HANDLED;
447 case DLG_UNHANDLED_KEY:
448 switch (parm) {
449 case KEY_M_CTRL | '\n':
450 goto l1;
451 case '\n':
452 case KEY_ENTER:
453 case KEY_RIGHT:
454 if (hotlist_button_callback (B_ENTER)) {
455 h->ret_value = B_ENTER;
456 dlg_stop (h);
458 return MSG_HANDLED;
459 break;
460 case KEY_LEFT:
461 if (hotlist_state.type != LIST_VFSLIST)
462 return !hotlist_button_callback (B_UP_GROUP);
463 else
464 return MSG_NOT_HANDLED;
465 break;
466 case KEY_DC:
467 if (!hotlist_state.moving) {
468 hotlist_button_callback (B_REMOVE);
469 return MSG_HANDLED;
471 break;
473 case ALT ('\n'):
474 case ALT ('\r'):
475 if (!hotlist_state.moving) {
476 if (l_hotlist->current) {
477 if (l_hotlist->current->data) {
478 struct hotlist *hlp =
479 (struct hotlist *) l_hotlist->current->data;
480 if (hlp->type == HL_TYPE_ENTRY) {
481 char *tmp =
482 g_strconcat ("cd ", hlp->directory, (char *) NULL);
483 stuff (cmdline, tmp, 0);
484 g_free (tmp);
485 dlg_stop (h);
486 h->ret_value = B_CANCEL;
487 return MSG_HANDLED;
492 return MSG_HANDLED; /* ignore key */
494 return MSG_NOT_HANDLED;
496 case DLG_POST_KEY:
497 if (hotlist_state.moving)
498 dlg_select_widget (l_movelist);
499 else
500 dlg_select_widget (l_hotlist);
501 /* always stay on hotlist */
502 /* fall through */
504 case DLG_INIT:
505 tty_setcolor (MENU_ENTRY_COLOR);
506 update_path_name ();
507 return MSG_HANDLED;
509 case DLG_RESIZE:
510 /* simply call dlg_set_size() with new size */
511 dlg_set_size (h, LINES - 2, COLS - 6);
512 return MSG_HANDLED;
514 default:
515 return default_dlg_callback (h, sender, msg, parm, data);
519 static int l_call (WListbox *list)
521 Dlg_head *dlg = list->widget.parent;
523 if (list->current){
524 if (list->current->data) {
525 struct hotlist *hlp = (struct hotlist*) list->current->data;
526 if (hlp->type == HL_TYPE_ENTRY) {
527 dlg->ret_value = B_ENTER;
528 dlg_stop (dlg);
529 return LISTBOX_DONE;
530 } else {
531 hotlist_button_callback (B_ENTER);
532 hotlist_callback (dlg, NULL, DLG_POST_KEY, '\n', NULL);
533 return LISTBOX_CONT;
535 } else {
536 dlg->ret_value = B_ENTER;
537 dlg_stop (dlg);
538 return LISTBOX_DONE;
542 hotlist_button_callback (B_UP_GROUP);
543 hotlist_callback (dlg, NULL, DLG_POST_KEY, 'u', NULL);
544 return LISTBOX_CONT;
548 * Expands all button names (once) and recalculates button positions.
549 * returns number of columns in the dialog box, which is 10 chars longer
550 * then buttonbar.
552 * If common width of the window (i.e. in xterm) is less than returned
553 * width - sorry :) (anyway this did not handled in previous version too)
555 static int
556 init_i18n_stuff(int list_type, int cols)
558 register int i;
559 static const char* cancel_but = N_("&Cancel");
561 #ifdef ENABLE_NLS
562 static int hotlist_i18n_flag = 0;
564 if (!hotlist_i18n_flag)
566 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
567 while (i--)
568 hotlist_but [i].text = _(hotlist_but [i].text);
570 cancel_but = _(cancel_but);
571 hotlist_i18n_flag = 1;
573 #endif /* ENABLE_NLS */
575 /* Dynamic resizing of buttonbars */
577 int len[2], count[2]; /* at most two lines of buttons */
578 int cur_x[2], row;
580 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
581 len[0] = len[1] = count[0] = count[1] = 0;
583 /* Count len of buttonbars, assuming 2 extra space between buttons */
584 while (i--)
586 if (! (hotlist_but[i].type & list_type))
587 continue;
589 row = hotlist_but [i].y;
590 ++count [row];
591 len [row] += str_term_width1 (hotlist_but [i].text) + 5;
592 if (hotlist_but [i].flags == DEFPUSH_BUTTON)
593 len [row] += 2;
595 len[0] -= 2;
596 len[1] -= 2;
598 cols = max(cols, max(len[0], len[1]));
600 /* arrange buttons */
602 cur_x[0] = cur_x[1] = 0;
603 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
604 while (i--)
606 if (! (hotlist_but[i].type & list_type))
607 continue;
609 row = hotlist_but [i].y;
611 if (hotlist_but [i].x != 0)
613 /* not first int the row */
614 if (!strcmp (hotlist_but [i].text, cancel_but))
615 hotlist_but [i].x =
616 cols - str_term_width1 (hotlist_but [i].text) - 13;
617 else
618 hotlist_but [i].x = cur_x [row];
621 cur_x [row] += str_term_width1 (hotlist_but [i].text) + 2
622 + (hotlist_but [i].flags == DEFPUSH_BUTTON ? 5 : 3);
626 return cols;
629 static void
630 init_hotlist (int list_type)
632 size_t i;
633 const char *title, *help_node;
634 int hotlist_cols;
636 hotlist_cols = init_i18n_stuff (list_type, COLS - 6);
638 do_refresh ();
640 hotlist_state.expanded =
641 mc_config_get_int (mc_main_config, "HotlistConfig", "expanded_view_of_groups", 0);
643 if (list_type == LIST_VFSLIST) {
644 title = _("Active VFS directories");
645 help_node = "[vfshot]"; /* FIXME - no such node */
646 } else {
647 title = _("Directory hotlist");
648 help_node = "[Hotlist]";
651 hotlist_dlg =
652 create_dlg (0, 0, LINES - 2, hotlist_cols, dialog_colors,
653 hotlist_callback, help_node, title, DLG_CENTER | DLG_REVERSE);
655 for (i = 0; i < BUTTONS; i++) {
656 if (hotlist_but[i].type & list_type)
657 add_widget_autopos (hotlist_dlg,
658 button_new (BY + hotlist_but[i].y,
659 BX + hotlist_but[i].x,
660 hotlist_but[i].ret_cmd,
661 hotlist_but[i].flags,
662 hotlist_but[i].text,
663 hotlist_button_callback),
664 hotlist_but[i].pos_flags);
667 /* We add the labels.
668 * pname will hold entry's pathname;
669 * pname_group will hold name of current group
671 pname = label_new (UY - 11 + LINES, UX + 2, "");
672 add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
673 if (!hotlist_state.moving) {
674 add_widget_autopos (hotlist_dlg,
675 label_new (UY - 12 + LINES, UX + 1,
676 _(" Directory path ")),
677 WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
679 /* This one holds the displayed pathname */
680 pname_group = label_new (UY, UX + 1, _(" Directory label "));
681 add_widget (hotlist_dlg, pname_group);
683 /* get new listbox */
684 l_hotlist =
685 listbox_new (UY + 1, UX + 1, LINES - 14, COLS - 2 * UX - 8,
686 l_call);
688 /* Fill the hotlist with the active VFS or the hotlist */
689 #ifdef ENABLE_VFS
690 if (list_type == LIST_VFSLIST) {
691 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
692 vfs_fill_names (add_name_to_list);
693 } else
694 #endif /* !ENABLE_VFS */
695 fill_listbox ();
697 add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL);
698 /* add listbox to the dialogs */
701 static void
702 init_movelist (int list_type, struct hotlist *item)
704 size_t i;
705 char *hdr = g_strdup_printf (_("Moving %s"), item->label);
706 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
708 do_refresh ();
710 movelist_dlg =
711 create_dlg (0, 0, LINES - 6, movelist_cols, dialog_colors,
712 hotlist_callback, "[Hotlist]", hdr, DLG_CENTER | DLG_REVERSE);
713 g_free (hdr);
715 for (i = 0; i < BUTTONS; i++) {
716 if (hotlist_but[i].type & list_type)
717 add_widget (movelist_dlg,
718 button_new (BY - 4 + hotlist_but[i].y,
719 BX + hotlist_but[i].x,
720 hotlist_but[i].ret_cmd,
721 hotlist_but[i].flags,
722 hotlist_but[i].text,
723 hotlist_button_callback));
726 /* We add the labels. We are interested in the last one,
727 * that one will hold the path name label
729 movelist_group = label_new (UY, UX + 1, _(" Directory label "));
730 add_widget (movelist_dlg, movelist_group);
731 /* get new listbox */
732 l_movelist =
733 listbox_new (UY + 1, UX + 1, movelist_dlg->lines - 8,
734 movelist_dlg->cols - 2 * UX - 2, l_call);
736 fill_listbox ();
738 add_widget (movelist_dlg, l_movelist);
739 /* add listbox to the dialogs */
743 * Destroy the list dialog.
744 * Don't confuse with done_hotlist() for the list in memory.
746 static void hotlist_done (void)
748 destroy_dlg (hotlist_dlg);
749 l_hotlist = NULL;
750 if (0)
751 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
752 repaint_screen ();
755 static inline char *
756 find_group_section (struct hotlist *grp)
758 return g_strconcat (grp->directory, ".Group", (char *) NULL);
762 /* 1.11.96 bor: added pos parameter to control placement of new item.
763 see widget.c, listbox_add_item()
764 now hotlist is in unsorted mode
766 enum {
767 HL_BEFORE_CURRENT = 1
768 ,HL_AFTER_CURRENT = 2
771 static struct hotlist *
772 add2hotlist (char *label, char *directory, enum HotListType type, int pos)
774 struct hotlist *new;
775 struct hotlist *current = NULL;
778 * Hotlist is neither loaded nor loading.
779 * Must be called by "Ctrl-x a" before using hotlist.
781 if (!current_group)
782 load_hotlist ();
784 if (l_hotlist && l_hotlist->current) {
785 current = l_hotlist->current->data;
787 /* Make sure `..' stays at the top of the list. */
788 if (current->type == HL_TYPE_DOTDOT)
789 pos = HL_AFTER_CURRENT;
792 new = new_hotlist ();
794 new->type = type;
795 new->label = label;
796 new->directory = directory;
797 new->up = current_group;
799 if (type == HL_TYPE_GROUP) {
800 current_group = new;
801 add_dotdot_to_list ();
802 current_group = new->up;
805 if (!current_group->head) { /* first element in group */
806 current_group->head = new;
807 } else if (pos == HL_AFTER_CURRENT) {
808 new->next = current->next;
809 current->next = new;
810 } else if (pos == HL_BEFORE_CURRENT &&
811 current == current_group->head) {
812 /* should be inserted before first item */
813 new->next = current;
814 current_group->head = new;
815 } else if (pos == HL_BEFORE_CURRENT) {
816 struct hotlist *p = current_group->head;
818 while (p->next != current)
819 p = p->next;
821 new->next = current;
822 p->next = new;
823 } else { /* append at the end */
824 struct hotlist *p = current_group->head;
826 while (p->next)
827 p = p->next;
829 p->next = new;
832 if (hotlist_state.running && type != HL_TYPE_COMMENT &&
833 type != HL_TYPE_DOTDOT) {
834 if (type == HL_TYPE_GROUP) {
835 char *lbl = g_strconcat ("->", new->label, (char *) NULL);
837 listbox_add_item (l_hotlist, pos, 0, lbl, new);
838 g_free (lbl);
839 } else
840 listbox_add_item (l_hotlist, pos, 0, new->label, new);
841 listbox_select_entry (l_hotlist, l_hotlist->current);
843 return new;
847 #ifdef ENABLE_NLS
849 * Support routine for add_new_entry_input()/add_new_group_input()
850 * Change positions of buttons (first three widgets).
852 * This is just a quick hack. Accurate procedure must take care of
853 * internationalized label lengths and total buttonbar length...assume
854 * 64 is longer anyway.
856 static void
857 add_widgets_i18n (QuickWidget* qw, int len)
859 int i, l[3], space, cur_x;
861 for (i = 0; i < 3; i++) {
862 qw [i].u.button.text = _(qw [i].u.button.text);
863 l[i] = str_term_width1 (qw [i].u.button.text) + 3;
865 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
867 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space) {
868 qw [i].relative_x = cur_x;
869 qw [i].x_divisions = len;
872 #endif /* ENABLE_NLS */
874 static int
875 add_new_entry_input (const char *header, const char *text1, const char *text2,
876 const char *help, char **r1, char **r2)
878 #define RELATIVE_Y_BUTTONS 4
879 #define RELATIVE_Y_LABEL_PTH 3
880 #define RELATIVE_Y_INPUT_PTH 4
882 QuickWidget quick_widgets [] =
884 /* 0 */ QUICK_BUTTON (55, 80, RELATIVE_Y_BUTTONS, 0, N_("&Cancel"), B_CANCEL, NULL),
885 /* 1 */ QUICK_BUTTON (30, 80, RELATIVE_Y_BUTTONS, 0, N_("&Insert"), B_INSERT, NULL),
886 /* 2 */ QUICK_BUTTON (10, 80, RELATIVE_Y_BUTTONS, 0, N_("&Append"), B_APPEND, NULL),
887 /* 3 */ QUICK_INPUT (4, 80, RELATIVE_Y_INPUT_PTH, 0, *r2, 58, 2, "input-pth", r2),
888 /* 4 */ QUICK_LABEL (RELATIVE_Y_LABEL_PTH, 80, 3, 0, text2),
889 /* 5 */ QUICK_INPUT (4, 80, 3, 0, *r1, 58, 0, "input-lbl", r1),
890 /* 6 */ QUICK_LABEL (3, 80, 2, 0, text1),
891 QUICK_END
894 int len;
895 int i;
896 int lines1, lines2;
897 int cols1, cols2;
899 #ifdef ENABLE_NLS
900 static gboolean i18n_flag = FALSE;
901 #endif /* ENABLE_NLS */
903 msglen(text1, &lines1, &cols1);
904 msglen(text2, &lines2, &cols2);
905 len = max (str_term_width1 (header), cols1);
906 len = max (max (len, cols2) + 4, 64);
908 #ifdef ENABLE_NLS
909 if (!i18n_flag) {
910 add_widgets_i18n (quick_widgets, len);
911 i18n_flag = TRUE;
913 #endif /* ENABLE_NLS */
916 QuickDialog Quick_input =
918 len, lines1 + lines2 + 7, -1, -1, header,
919 help, quick_widgets, FALSE
922 for (i = 0; i < 7; i++)
923 quick_widgets [i].y_divisions = Quick_input.ylen;
925 quick_widgets [0].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
926 quick_widgets [1].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
927 quick_widgets [2].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
928 quick_widgets [3].relative_y = RELATIVE_Y_INPUT_PTH + (lines1);
929 quick_widgets [4].relative_y = RELATIVE_Y_LABEL_PTH + (lines1);
931 i = quick_dialog (&Quick_input);
934 return (i != B_CANCEL) ? i : 0;
936 #undef RELATIVE_Y_BUTTONS
937 #undef RELATIVE_Y_LABEL_PTH
938 #undef RELATIVE_Y_INPUT_PTH
941 static void add_new_entry_cmd (void)
943 char *title, *url, *to_free;
944 int ret;
946 /* Take current directory as default value for input fields */
947 to_free = title = url = strip_password (g_strdup (current_panel->cwd), 1);
949 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label"),
950 _("Directory path"), "[Hotlist]", &title, &url);
951 g_free (to_free);
953 if (!ret)
954 return;
955 if (!title || !*title || !url || !*url) {
956 g_free (title);
957 g_free (url);
958 return;
961 if (ret == B_ENTER || ret == B_APPEND)
962 add2hotlist (title, url, HL_TYPE_ENTRY, HL_AFTER_CURRENT);
963 else
964 add2hotlist (title, url, HL_TYPE_ENTRY, HL_BEFORE_CURRENT);
966 hotlist_state.modified = 1;
969 static int
970 add_new_group_input (const char *header, const char *label, char **result)
972 QuickWidget quick_widgets [] =
974 /* 0 */ QUICK_BUTTON (55, 80, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
975 /* 1 */ QUICK_BUTTON (30, 80, 1, 0, N_("&Insert"), B_INSERT, NULL),
976 /* 2 */ QUICK_BUTTON (10, 80, 1, 0, N_("&Append"), B_APPEND, NULL),
977 /* 3 */ QUICK_INPUT (4, 80, 0, 0, "", 58, 0, "input" , result),
978 /* 4 */ QUICK_LABEL (3, 80, 2, 0, label),
979 QUICK_END
982 int len;
983 int i;
984 int lines, cols;
985 int ret;
987 #ifdef ENABLE_NLS
988 static gboolean i18n_flag = FALSE;
989 #endif /* ENABLE_NLS */
991 msglen (label, &lines, &cols);
992 len = max (max (str_term_width1 (header), cols) + 4, 64);
994 #ifdef ENABLE_NLS
995 if (!i18n_flag) {
996 add_widgets_i18n (quick_widgets, len);
997 i18n_flag = TRUE;
999 #endif /* ENABLE_NLS */
1002 QuickDialog Quick_input =
1004 len, lines + 6, -1, -1, header,
1005 "[Hotlist]", quick_widgets, FALSE
1008 int relative_y[] = {1, 1, 1, 0, 2}; /* the relative_x component from the
1009 quick_widgets variable above */
1011 for (i = 0; i < 5; i++)
1012 quick_widgets[i].y_divisions = Quick_input.ylen;
1014 for (i = 0; i < 4; i++)
1015 quick_widgets[i].relative_y = relative_y[i] + 2 + lines;
1017 ret = quick_dialog (&Quick_input);
1020 return (ret != B_CANCEL) ? ret : 0;
1023 static void add_new_group_cmd (void)
1025 char *label;
1026 int ret;
1028 ret = add_new_group_input (_(" New hotlist group "), _("Name of new group"), &label);
1029 if (!ret || !label || !*label)
1030 return;
1032 if (ret == B_ENTER || ret == B_APPEND)
1033 add2hotlist (label, 0, HL_TYPE_GROUP, HL_AFTER_CURRENT);
1034 else
1035 add2hotlist (label, 0, HL_TYPE_GROUP, HL_BEFORE_CURRENT);
1037 hotlist_state.modified = 1;
1040 void add2hotlist_cmd (void)
1042 char *lc_prompt, *label;
1043 const char *cp = _("Label for \"%s\":");
1044 int l = str_term_width1 (cp);
1045 char *label_string = g_strdup (current_panel->cwd);
1047 strip_password (label_string, 1);
1049 lc_prompt = g_strdup_printf (cp, path_trunc (current_panel->cwd, COLS-2*UX-(l+8)));
1050 label = input_dialog (_(" Add to hotlist "), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string);
1051 g_free (lc_prompt);
1053 if (!label || !*label) {
1054 g_free (label_string);
1055 g_free (label);
1056 return;
1058 add2hotlist (label, label_string, HL_TYPE_ENTRY, 0);
1059 hotlist_state.modified = 1;
1062 static void remove_group (struct hotlist *grp)
1064 struct hotlist *current = grp->head;
1066 while (current) {
1067 struct hotlist *next = current->next;
1069 if (current->type == HL_TYPE_GROUP)
1070 remove_group (current);
1072 g_free (current->label);
1073 g_free (current->directory);
1074 g_free (current);
1076 current = next;
1081 static void remove_from_hotlist (struct hotlist *entry)
1083 if (entry->type == HL_TYPE_DOTDOT)
1084 return;
1086 if (confirm_directory_hotlist_delete) {
1087 char *title;
1088 int result;
1090 title = g_strconcat (_(" Remove: "),
1091 str_trunc (entry->label, 30),
1092 " ", (char *) NULL);
1094 if (safe_delete)
1095 query_set_sel (1);
1096 result = query_dialog (title,
1097 _("\n Are you sure you want to remove this entry?"),
1098 D_ERROR, 2, _("&Yes"), _("&No"));
1100 g_free (title);
1102 if (result != 0)
1103 return;
1106 if (entry->type == HL_TYPE_GROUP) {
1107 if (entry->head) {
1108 char *header;
1109 int result;
1111 header = g_strconcat (_(" Remove: "),
1112 str_trunc (entry->label, 30),
1113 " ", (char *) NULL);
1114 result = query_dialog (header, _("\n Group not empty.\n Remove it?"),
1115 D_ERROR, 2,
1116 _("&Yes"), _("&No"));
1117 g_free (header);
1119 if (result != 0)
1120 return;
1123 remove_group (entry);
1126 unlink_entry (entry);
1128 g_free (entry->label);
1129 g_free (entry->directory);
1130 g_free (entry);
1131 /* now remove list entry from screen */
1132 listbox_remove_current (l_hotlist, 1);
1133 hotlist_state.modified = 1;
1136 char *hotlist_cmd (int vfs_or_hotlist)
1138 char *target = NULL;
1140 hotlist_state.type = vfs_or_hotlist;
1141 load_hotlist ();
1143 init_hotlist (vfs_or_hotlist);
1145 /* display file info */
1146 tty_setcolor (SELECTED_COLOR);
1148 hotlist_state.running = 1;
1149 run_dlg (hotlist_dlg);
1150 hotlist_state.running = 0;
1151 save_hotlist ();
1153 switch (hotlist_dlg->ret_value) {
1154 case B_CANCEL:
1155 break;
1157 case B_ENTER:
1158 if (l_hotlist->current->data) {
1159 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
1160 target = g_strdup (hlp->directory);
1161 } else
1162 target = g_strdup (l_hotlist->current->text);
1163 break;
1166 hotlist_done ();
1167 return target;
1170 static void
1171 load_group (struct hotlist *grp)
1173 gchar **profile_keys, **keys;
1174 gsize len;
1175 char *group_section;
1176 struct hotlist *current = 0;
1178 group_section = find_group_section (grp);
1180 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1182 current_group = grp;
1184 while (*profile_keys){
1185 add2hotlist (
1186 mc_config_get_string(mc_main_config, group_section, *profile_keys, ""),
1187 g_strdup (*profile_keys),
1188 HL_TYPE_GROUP,
1190 profile_keys++;
1192 g_free (group_section);
1193 g_strfreev(keys);
1195 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory,&len);
1197 while (*profile_keys){
1198 add2hotlist (
1199 mc_config_get_string(mc_main_config,group_section,*profile_keys,""),
1200 g_strdup (*profile_keys),
1201 HL_TYPE_ENTRY,
1203 profile_keys++;
1205 g_strfreev(keys);
1207 for (current = grp->head; current; current = current->next)
1208 load_group (current);
1211 #define TKN_GROUP 0
1212 #define TKN_ENTRY 1
1213 #define TKN_STRING 2
1214 #define TKN_URL 3
1215 #define TKN_ENDGROUP 4
1216 #define TKN_COMMENT 5
1217 #define TKN_EOL 125
1218 #define TKN_EOF 126
1219 #define TKN_UNKNOWN 127
1221 static GString *tkn_buf = NULL;
1223 static char *hotlist_file_name;
1224 static FILE *hotlist_file;
1225 static time_t hotlist_file_mtime;
1227 static int hot_skip_blanks (void)
1229 int c;
1231 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1233 return c;
1237 static int hot_next_token (void)
1239 int c, ret=0;
1240 size_t l;
1243 if (tkn_buf == NULL) tkn_buf = g_string_new ("");
1244 g_string_set_size(tkn_buf,0);
1246 again:
1247 c = hot_skip_blanks ();
1248 switch (c) {
1249 case EOF:
1250 ret = TKN_EOF;
1251 break;
1252 case '\n':
1253 ret = TKN_EOL;
1254 break;
1255 case '#':
1256 while ((c = getc (hotlist_file)) != EOF && c != '\n') {
1257 g_string_append_c (tkn_buf, c);
1259 ret = TKN_COMMENT;
1260 break;
1261 case '"':
1262 while ((c = getc (hotlist_file)) != EOF && c != '"') {
1263 if (c == '\\')
1264 if ((c = getc (hotlist_file)) == EOF){
1265 g_string_free (tkn_buf, TRUE);
1266 return TKN_EOF;
1268 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1270 if (c == EOF)
1271 ret = TKN_EOF;
1272 else
1273 ret = TKN_STRING;
1274 break;
1275 case '\\':
1276 if ((c = getc (hotlist_file)) == EOF){
1277 g_string_free (tkn_buf, TRUE);
1278 return TKN_EOF;
1280 if (c == '\n')
1281 goto again;
1283 /* fall through; it is taken as normal character */
1285 default:
1286 do {
1287 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1288 } while ((c = fgetc (hotlist_file)) != EOF &&
1289 (g_ascii_isalnum (c) || !isascii (c)));
1290 if (c != EOF)
1291 ungetc (c, hotlist_file);
1292 l = tkn_buf->len;
1293 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1294 ret = TKN_GROUP;
1295 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1296 ret = TKN_ENTRY;
1297 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1298 ret = TKN_ENDGROUP;
1299 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1300 ret = TKN_URL;
1301 else
1302 ret = TKN_UNKNOWN;
1303 break;
1305 return ret;
1308 #define SKIP_TO_EOL { \
1309 int _tkn; \
1310 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1313 #define CHECK_TOKEN(_TKN_) \
1314 if ((tkn = hot_next_token ()) != _TKN_) { \
1315 hotlist_state.readonly = 1; \
1316 hotlist_state.file_error = 1; \
1317 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1318 tkn = hot_next_token (); \
1319 break; \
1322 static void
1323 hot_load_group (struct hotlist * grp)
1325 int tkn;
1326 struct hotlist *new_grp;
1327 char *label, *url;
1329 current_group = grp;
1331 while ((tkn = hot_next_token()) != TKN_ENDGROUP)
1332 switch (tkn) {
1333 case TKN_GROUP:
1334 CHECK_TOKEN(TKN_STRING);
1335 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1336 SKIP_TO_EOL;
1337 hot_load_group (new_grp);
1338 current_group = grp;
1339 break;
1340 case TKN_ENTRY:
1341 CHECK_TOKEN(TKN_STRING);
1342 label = g_strdup (tkn_buf->str);
1343 CHECK_TOKEN(TKN_URL);
1344 CHECK_TOKEN(TKN_STRING);
1345 url = g_strdup (tkn_buf->str);
1346 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1347 SKIP_TO_EOL;
1348 break;
1349 case TKN_COMMENT:
1350 label = g_strdup (tkn_buf->str);
1351 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1352 break;
1353 case TKN_EOF:
1354 hotlist_state.readonly = 1;
1355 hotlist_state.file_error = 1;
1356 return;
1357 break;
1358 case TKN_EOL:
1359 /* skip empty lines */
1360 break;
1361 default:
1362 hotlist_state.readonly = 1;
1363 hotlist_state.file_error = 1;
1364 SKIP_TO_EOL;
1365 break;
1367 SKIP_TO_EOL;
1370 static void
1371 hot_load_file (struct hotlist * grp)
1373 int tkn;
1374 struct hotlist *new_grp;
1375 char *label, *url;
1377 current_group = grp;
1379 while ((tkn = hot_next_token())!= TKN_EOF)
1380 switch (tkn) {
1381 case TKN_GROUP:
1382 CHECK_TOKEN(TKN_STRING);
1383 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1384 SKIP_TO_EOL;
1385 hot_load_group (new_grp);
1386 current_group = grp;
1387 break;
1388 case TKN_ENTRY:
1389 CHECK_TOKEN(TKN_STRING);
1390 label = g_strdup (tkn_buf->str);
1391 CHECK_TOKEN(TKN_URL);
1392 CHECK_TOKEN(TKN_STRING);
1393 url = g_strdup (tkn_buf->str);
1394 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1395 SKIP_TO_EOL;
1396 break;
1397 case TKN_COMMENT:
1398 label = g_strdup (tkn_buf->str);
1399 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1400 break;
1401 case TKN_EOL:
1402 /* skip empty lines */
1403 break;
1404 default:
1405 hotlist_state.readonly = 1;
1406 hotlist_state.file_error = 1;
1407 SKIP_TO_EOL;
1408 break;
1412 static void
1413 clean_up_hotlist_groups (const char *section)
1415 char *grp_section;
1416 gchar **profile_keys, **keys;
1417 gsize len;
1419 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1420 if (mc_config_has_group(mc_main_config, section))
1421 mc_config_del_group (mc_main_config, section);
1423 if (mc_config_has_group (mc_main_config, grp_section)) {
1424 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section,&len);
1426 while (*profile_keys) {
1427 clean_up_hotlist_groups (*profile_keys);
1428 profile_keys++;
1430 g_strfreev(keys);
1431 mc_config_del_group (mc_main_config, grp_section);
1433 g_free (grp_section);
1438 static void
1439 load_hotlist (void)
1441 int remove_old_list = 0;
1442 struct stat stat_buf;
1444 if (hotlist_state.loaded) {
1445 stat (hotlist_file_name, &stat_buf);
1446 if (hotlist_file_mtime < stat_buf.st_mtime)
1447 done_hotlist ();
1448 else
1449 return;
1452 if (!hotlist_file_name)
1453 hotlist_file_name = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HOTLIST_FILE, NULL);
1455 hotlist = new_hotlist ();
1456 hotlist->type = HL_TYPE_GROUP;
1457 hotlist->label = g_strdup (_(" Top level group "));
1458 hotlist->up = hotlist;
1460 * compatibility :-(
1462 hotlist->directory = g_strdup ("Hotlist");
1464 if ((hotlist_file = fopen (hotlist_file_name, "r")) == 0) {
1465 int result;
1467 load_group (hotlist);
1468 hotlist_state.loaded = 1;
1470 * just to be sure we got copy
1472 hotlist_state.modified = 1;
1473 result = save_hotlist ();
1474 hotlist_state.modified = 0;
1475 if (result) {
1476 remove_old_list = 1;
1477 } else {
1478 message (D_ERROR, _(" Hotlist Load "),
1479 _("MC was unable to write ~/%s file, your old hotlist entries were not deleted"),
1480 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1482 } else {
1483 hot_load_file (hotlist);
1484 fclose (hotlist_file);
1485 hotlist_state.loaded = 1;
1488 if (remove_old_list) {
1489 GError *error = NULL;
1490 clean_up_hotlist_groups ("Hotlist");
1491 if (! mc_config_save_file (mc_main_config, &error))
1492 setup_save_config_show_error(mc_main_config->ini_path, &error);
1495 stat (hotlist_file_name, &stat_buf);
1496 hotlist_file_mtime = stat_buf.st_mtime;
1497 current_group = hotlist;
1501 static int list_level = 0;
1503 static void
1504 hot_save_group (struct hotlist *grp)
1506 struct hotlist *current = grp->head;
1507 int i;
1508 char *s;
1510 #define INDENT(n) \
1511 do { \
1512 for (i = 0; i < n; i++) \
1513 putc (' ', hotlist_file); \
1514 } while (0)
1516 for (;current; current = current->next)
1517 switch (current->type) {
1518 case HL_TYPE_GROUP:
1519 INDENT (list_level);
1520 fputs ("GROUP \"", hotlist_file);
1521 for (s = current->label; *s; s++) {
1522 if (*s == '"' || *s == '\\')
1523 putc ('\\', hotlist_file);
1524 putc (*s, hotlist_file);
1526 fputs ("\"\n", hotlist_file);
1527 list_level += 2;
1528 hot_save_group (current);
1529 list_level -= 2;
1530 INDENT (list_level);
1531 fputs ("ENDGROUP\n", hotlist_file);
1532 break;
1533 case HL_TYPE_ENTRY:
1534 INDENT(list_level);
1535 fputs ("ENTRY \"", hotlist_file);
1536 for (s = current->label; *s; s++) {
1537 if (*s == '"' || *s == '\\')
1538 putc ('\\', hotlist_file);
1539 putc (*s, hotlist_file);
1541 fputs ("\" URL \"", hotlist_file);
1542 for (s = current->directory; *s; s++) {
1543 if (*s == '"' || *s == '\\')
1544 putc ('\\', hotlist_file);
1545 putc (*s, hotlist_file);
1547 fputs ("\"\n", hotlist_file);
1548 break;
1549 case HL_TYPE_COMMENT:
1550 fprintf (hotlist_file, "#%s\n", current->label);
1551 break;
1552 case HL_TYPE_DOTDOT:
1553 /* do nothing */
1554 break;
1558 int save_hotlist (void)
1560 int saved = 0;
1561 struct stat stat_buf;
1563 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name) {
1564 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1566 if ((hotlist_file = fopen (hotlist_file_name, "w")) != 0) {
1567 hot_save_group (hotlist);
1568 fclose (hotlist_file);
1569 stat (hotlist_file_name, &stat_buf);
1570 hotlist_file_mtime = stat_buf.st_mtime;
1571 saved = 1;
1572 hotlist_state.modified = 0;
1573 } else
1574 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1577 return saved;
1581 * Unload list from memory.
1582 * Don't confuse with hotlist_done() for GUI.
1584 void done_hotlist (void)
1586 if (hotlist){
1587 remove_group (hotlist);
1588 g_free (hotlist->label);
1589 g_free (hotlist->directory);
1590 g_free (hotlist);
1591 hotlist = 0;
1594 hotlist_state.loaded = 0;
1596 g_free (hotlist_file_name);
1597 hotlist_file_name = 0;
1598 l_hotlist = 0;
1599 current_group = 0;
1601 if (tkn_buf){
1602 g_string_free (tkn_buf, TRUE);
1603 tkn_buf = NULL;
1607 static void
1608 add_dotdot_to_list (void)
1610 if (current_group != hotlist) {
1611 if (hotlist_has_dot_dot != 0)
1612 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, 0);