Changes to handle vfs_path_t object:
[midnight-commander.git] / src / filemanager / hotlist.c
blob7c2aa2a2e9b66eb5a3f305873051dc95c0ccb6d8
1 /*
2 Directory hotlist -- for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
5 2003, 2004, 2005, 2006, 2007, 2008, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Radek Doulik, 1994
10 Janne Kukonlehto, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
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 "midnight.h" /* current_panel */
63 #include "command.h" /* cmdline */
65 #include "hotlist.h"
67 /*** global variables ****************************************************************************/
69 int hotlist_has_dot_dot = 1;
71 /*** file scope macro definitions ****************************************************************/
73 #define UX 5
74 #define UY 2
76 #define BX UX
77 #define BY (LINES - 6)
79 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
80 #define LABELS 3
81 #define B_ADD_CURRENT B_USER
82 #define B_REMOVE (B_USER + 1)
83 #define B_NEW_GROUP (B_USER + 2)
84 #define B_NEW_ENTRY (B_USER + 3)
85 #define B_UP_GROUP (B_USER + 4)
86 #define B_INSERT (B_USER + 5)
87 #define B_APPEND (B_USER + 6)
88 #define B_MOVE (B_USER + 7)
90 #ifdef ENABLE_VFS
91 #define B_FREE_ALL_VFS (B_USER + 8)
92 #define B_REFRESH_VFS (B_USER + 9)
93 #endif
95 #define new_hotlist() g_new0(struct hotlist, 1)
97 #define CHECK_BUFFER \
98 do \
99 { \
100 size_t i; \
101 i = strlen (current->label); \
102 if (i + 3 > buflen) { \
103 g_free (buf); \
104 buflen = 1024 * (i/1024 + 1); \
105 buf = g_malloc (buflen); \
107 buf[0] = '\0'; \
108 } while (0)
110 #define TKN_GROUP 0
111 #define TKN_ENTRY 1
112 #define TKN_STRING 2
113 #define TKN_URL 3
114 #define TKN_ENDGROUP 4
115 #define TKN_COMMENT 5
116 #define TKN_EOL 125
117 #define TKN_EOF 126
118 #define TKN_UNKNOWN 127
120 #define SKIP_TO_EOL \
122 int _tkn; \
123 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
126 #define CHECK_TOKEN(_TKN_) \
127 tkn = hot_next_token (); \
128 if (tkn != _TKN_) \
130 hotlist_state.readonly = 1; \
131 hotlist_state.file_error = 1; \
132 while (tkn != TKN_EOL && tkn != TKN_EOF) \
133 tkn = hot_next_token (); \
134 break; \
137 /*** file scope type declarations ****************************************************************/
139 enum HotListType
141 HL_TYPE_GROUP,
142 HL_TYPE_ENTRY,
143 HL_TYPE_COMMENT,
144 HL_TYPE_DOTDOT
147 static struct
150 * these parameters are intended to be user configurable
152 int expanded; /* expanded view of all groups at startup */
155 * these reflect run time state
158 int loaded; /* hotlist is loaded */
159 int readonly; /* hotlist readonly */
160 int file_error; /* parse error while reading file */
161 int running; /* we are running dlg (and have to
162 update listbox */
163 int moving; /* we are in moving hotlist currently */
164 int modified; /* hotlist was modified */
165 int type; /* LIST_HOTLIST || LIST_VFSLIST */
166 } hotlist_state;
168 /* Directory hotlist */
169 struct hotlist
171 enum HotListType type;
172 char *directory;
173 char *label;
174 struct hotlist *head;
175 struct hotlist *up;
176 struct hotlist *next;
179 /*** file scope variables ************************************************************************/
181 static WListbox *l_hotlist;
182 static WListbox *l_movelist;
184 static Dlg_head *hotlist_dlg;
185 static Dlg_head *movelist_dlg;
187 static WLabel *pname, *pname_group, *movelist_group;
189 static struct _hotlist_but
191 int ret_cmd, flags, y, x;
192 const char *text;
193 int type;
194 widget_pos_flags_t pos_flags;
195 } hotlist_but[] =
197 /* *INDENT-OFF* */
198 { B_MOVE, NORMAL_BUTTON, 1, 42, N_("&Move"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
199 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"),
200 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
201 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"),
202 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
203 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"),
204 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
205 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &entry"),
206 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
207 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &group"),
208 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
209 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"),
210 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
211 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"),
212 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
213 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"),
214 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
215 #ifdef ENABLE_VFS
216 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, N_("&Refresh"),
217 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
218 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, N_("Fr&ee VFSs now"),
219 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
220 #endif
221 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &to"),
222 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
223 /* *INDENT-ON* */
226 static struct hotlist *hotlist = NULL;
228 static struct hotlist *current_group;
230 static GString *tkn_buf = NULL;
232 static char *hotlist_file_name;
233 static FILE *hotlist_file;
234 static time_t hotlist_file_mtime;
236 static int list_level = 0;
238 /*** file scope functions ************************************************************************/
239 /* --------------------------------------------------------------------------------------------- */
241 static void init_movelist (int, struct hotlist *);
242 static void add_new_group_cmd (void);
243 static void add_new_entry_cmd (void);
244 static void remove_from_hotlist (struct hotlist *entry);
245 static void load_hotlist (void);
246 static void add_dotdot_to_list (void);
248 /* --------------------------------------------------------------------------------------------- */
250 static void
251 hotlist_refresh (Dlg_head * dlg)
253 /* TODO: use groupboxes here */
254 common_dialog_repaint (dlg);
255 tty_setcolor (COLOR_NORMAL);
256 draw_box (dlg, 2, 5, dlg->lines - (hotlist_state.moving ? 6 : 10), dlg->cols - (UX * 2), TRUE);
257 if (!hotlist_state.moving)
258 draw_box (dlg, dlg->lines - 8, 5, 3, dlg->cols - (UX * 2), TRUE);
261 /* --------------------------------------------------------------------------------------------- */
262 /** If current->data is 0, then we are dealing with a VFS pathname */
264 static void
265 update_path_name (void)
267 const char *text = "";
268 char *p;
269 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
270 Dlg_head *dlg = list->widget.owner;
272 if (list->count != 0)
274 char *ctext = NULL;
275 void *cdata = NULL;
277 listbox_get_current (list, &ctext, &cdata);
278 if (cdata == NULL)
279 text = ctext;
280 else
282 struct hotlist *hlp = (struct hotlist *) cdata;
284 if (hlp->type == HL_TYPE_ENTRY || hlp->type == HL_TYPE_DOTDOT)
285 text = hlp->directory;
286 else if (hlp->type == HL_TYPE_GROUP)
287 text = _("Subgroup - press ENTER to see list");
290 if (!hotlist_state.moving)
291 label_set_text (pname, str_trunc (text, dlg->cols - (UX * 2 + 4)));
293 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
294 if (!hotlist_state.moving)
295 label_set_text (pname_group, str_trunc (p, dlg->cols - (UX * 2 + 4)));
296 else
297 label_set_text (movelist_group, str_trunc (p, dlg->cols - (UX * 2 + 4)));
298 g_free (p);
300 dlg_redraw (dlg);
303 /* --------------------------------------------------------------------------------------------- */
305 static void
306 fill_listbox (void)
308 struct hotlist *current = current_group->head;
309 GString *buff = g_string_new ("");
311 while (current)
313 switch (current->type)
315 case HL_TYPE_GROUP:
317 /* buff clean up */
318 g_string_truncate (buff, 0);
319 g_string_append (buff, "->");
320 g_string_append (buff, current->label);
321 if (hotlist_state.moving)
322 listbox_add_item (l_movelist, LISTBOX_APPEND_AT_END, 0, buff->str, current);
323 else
324 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, buff->str, current);
326 break;
327 case HL_TYPE_DOTDOT:
328 case HL_TYPE_ENTRY:
329 if (hotlist_state.moving)
330 listbox_add_item (l_movelist, LISTBOX_APPEND_AT_END, 0, current->label, current);
331 else
332 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, current->label, current);
333 break;
334 default:
335 break;
337 current = current->next;
339 g_string_free (buff, TRUE);
342 /* --------------------------------------------------------------------------------------------- */
344 static void
345 unlink_entry (struct hotlist *entry)
347 struct hotlist *current = current_group->head;
349 if (current == entry)
350 current_group->head = entry->next;
351 else
353 while (current && current->next != entry)
354 current = current->next;
355 if (current)
356 current->next = entry->next;
358 entry->next = entry->up = 0;
361 /* --------------------------------------------------------------------------------------------- */
363 #ifdef ENABLE_VFS
364 static void
365 add_name_to_list (const char *path)
367 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, path, 0);
369 #endif /* !ENABLE_VFS */
371 /* --------------------------------------------------------------------------------------------- */
373 static int
374 hotlist_button_callback (WButton * button, int action)
376 (void) button;
378 switch (action)
380 case B_MOVE:
382 struct hotlist *saved = current_group;
383 struct hotlist *item = NULL;
384 struct hotlist *moveto_item = NULL;
385 struct hotlist *moveto_group = NULL;
386 int ret;
388 if (l_hotlist->count == 0)
389 return MSG_NOT_HANDLED; /* empty group - nothing to do */
391 listbox_get_current (l_hotlist, NULL, (void **) &item);
392 hotlist_state.moving = 1;
393 init_movelist (LIST_MOVELIST, item);
395 ret = run_dlg (movelist_dlg);
397 hotlist_state.moving = 0;
398 listbox_get_current (l_movelist, NULL, (void **) &moveto_item);
399 moveto_group = current_group;
400 destroy_dlg (movelist_dlg);
401 current_group = saved;
402 if (ret == B_CANCEL)
403 return MSG_NOT_HANDLED;
404 if (moveto_item == item)
405 return MSG_NOT_HANDLED; /* If we insert/append a before/after a
406 it hardly changes anything ;) */
407 unlink_entry (item);
408 listbox_remove_current (l_hotlist);
409 item->up = moveto_group;
410 if (!moveto_group->head)
411 moveto_group->head = item;
412 else if (!moveto_item)
413 { /* we have group with just comments */
414 struct hotlist *p = moveto_group->head;
416 /* skip comments */
417 while (p->next)
418 p = p->next;
419 p->next = item;
421 else if (ret == B_ENTER || ret == B_APPEND)
422 if (!moveto_item->next)
423 moveto_item->next = item;
424 else
426 item->next = moveto_item->next;
427 moveto_item->next = item;
429 else if (moveto_group->head == moveto_item)
431 moveto_group->head = item;
432 item->next = moveto_item;
434 else
436 struct hotlist *p = moveto_group->head;
438 while (p->next != moveto_item)
439 p = p->next;
440 item->next = p->next;
441 p->next = item;
443 listbox_remove_list (l_hotlist);
444 fill_listbox ();
445 repaint_screen ();
446 hotlist_state.modified = 1;
447 return MSG_NOT_HANDLED;
449 case B_REMOVE:
451 struct hotlist *entry = NULL;
452 listbox_get_current (l_hotlist, NULL, (void **) &entry);
453 remove_from_hotlist (entry);
455 return MSG_NOT_HANDLED;
457 case B_NEW_GROUP:
458 add_new_group_cmd ();
459 return MSG_NOT_HANDLED;
461 case B_ADD_CURRENT:
462 add2hotlist_cmd ();
463 return MSG_NOT_HANDLED;
465 case B_NEW_ENTRY:
466 add_new_entry_cmd ();
467 return MSG_NOT_HANDLED;
469 case B_ENTER:
471 WListbox *list;
472 void *data;
473 struct hotlist *hlp;
475 list = hotlist_state.moving ? l_movelist : l_hotlist;
476 listbox_get_current (list, NULL, &data);
478 if (data == NULL)
479 return MSG_HANDLED;
481 hlp = (struct hotlist *) data;
483 if (hlp->type == HL_TYPE_ENTRY)
484 return MSG_HANDLED;
485 if (hlp->type != HL_TYPE_DOTDOT)
487 listbox_remove_list (list);
488 current_group = hlp;
489 fill_listbox ();
490 return MSG_NOT_HANDLED;
492 /* Fall through - go up */
494 /* Fall through if list empty - just go up */
496 case B_UP_GROUP:
498 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
499 listbox_remove_list (list);
500 current_group = current_group->up;
501 fill_listbox ();
502 return MSG_NOT_HANDLED;
505 #ifdef ENABLE_VFS
506 case B_FREE_ALL_VFS:
507 vfs_expire (1);
508 /* fall through */
510 case B_REFRESH_VFS:
511 listbox_remove_list (l_hotlist);
512 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), 0);
513 vfs_fill_names (add_name_to_list);
514 return MSG_NOT_HANDLED;
515 #endif /* ENABLE_VFS */
517 default:
518 return MSG_HANDLED;
522 /* --------------------------------------------------------------------------------------------- */
524 static inline cb_ret_t
525 hotlist_handle_key (Dlg_head * h, int key)
527 switch (key)
529 case KEY_M_CTRL | '\n':
530 goto l1;
532 case '\n':
533 case KEY_ENTER:
534 case KEY_RIGHT:
535 if (hotlist_button_callback (NULL, B_ENTER))
537 h->ret_value = B_ENTER;
538 dlg_stop (h);
540 return MSG_HANDLED;
542 case KEY_LEFT:
543 if (hotlist_state.type != LIST_VFSLIST)
544 return !hotlist_button_callback (NULL, B_UP_GROUP);
545 else
546 return MSG_NOT_HANDLED;
548 case KEY_DC:
549 if (hotlist_state.moving)
550 return MSG_NOT_HANDLED;
551 else
553 hotlist_button_callback (NULL, B_REMOVE);
554 return MSG_HANDLED;
558 case ALT ('\n'):
559 case ALT ('\r'):
560 if (!hotlist_state.moving)
562 void *ldata = NULL;
564 listbox_get_current (l_hotlist, NULL, &ldata);
566 if (ldata != NULL)
568 struct hotlist *hlp = (struct hotlist *) ldata;
570 if (hlp->type == HL_TYPE_ENTRY)
572 char *tmp;
574 tmp = g_strconcat ("cd ", hlp->directory, (char *) NULL);
575 input_insert (cmdline, tmp, FALSE);
576 g_free (tmp);
577 h->ret_value = B_CANCEL;
578 dlg_stop (h);
582 return MSG_HANDLED; /* ignore key */
584 default:
585 return MSG_NOT_HANDLED;
589 /* --------------------------------------------------------------------------------------------- */
591 static cb_ret_t
592 hotlist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
594 switch (msg)
596 case DLG_DRAW:
597 hotlist_refresh (h);
598 return MSG_HANDLED;
600 case DLG_UNHANDLED_KEY:
601 return hotlist_handle_key (h, parm);
603 case DLG_POST_KEY:
604 if (hotlist_state.moving)
605 dlg_select_widget (l_movelist);
606 else
607 dlg_select_widget (l_hotlist);
608 /* always stay on hotlist */
609 /* fall through */
611 case DLG_INIT:
612 tty_setcolor (MENU_ENTRY_COLOR);
613 update_path_name ();
614 return MSG_HANDLED;
616 case DLG_RESIZE:
617 /* simply call dlg_set_size() with new size */
618 dlg_set_size (h, LINES - 2, COLS - 6);
619 return MSG_HANDLED;
621 default:
622 return default_dlg_callback (h, sender, msg, parm, data);
626 /* --------------------------------------------------------------------------------------------- */
628 static lcback_ret_t
629 l_call (WListbox * list)
631 Dlg_head *dlg = list->widget.owner;
633 if (list->count != 0)
635 void *data = NULL;
637 listbox_get_current (list, NULL, &data);
639 if (data != NULL)
641 struct hotlist *hlp = (struct hotlist *) data;
642 if (hlp->type == HL_TYPE_ENTRY)
644 dlg->ret_value = B_ENTER;
645 dlg_stop (dlg);
646 return LISTBOX_DONE;
648 else
650 hotlist_button_callback (NULL, B_ENTER);
651 hotlist_callback (dlg, NULL, DLG_POST_KEY, '\n', NULL);
652 return LISTBOX_CONT;
655 else
657 dlg->ret_value = B_ENTER;
658 dlg_stop (dlg);
659 return LISTBOX_DONE;
663 hotlist_button_callback (NULL, B_UP_GROUP);
664 hotlist_callback (dlg, NULL, DLG_POST_KEY, 'u', NULL);
665 return LISTBOX_CONT;
668 /* --------------------------------------------------------------------------------------------- */
670 * Expands all button names (once) and recalculates button positions.
671 * returns number of columns in the dialog box, which is 10 chars longer
672 * then buttonbar.
674 * If common width of the window (i.e. in xterm) is less than returned
675 * width - sorry :) (anyway this did not handled in previous version too)
678 static int
679 init_i18n_stuff (int list_type, int cols)
681 register int i;
682 static const char *cancel_but = N_("&Cancel");
684 #ifdef ENABLE_NLS
685 static int hotlist_i18n_flag = 0;
687 if (!hotlist_i18n_flag)
689 i = sizeof (hotlist_but) / sizeof (hotlist_but[0]);
690 while (i--)
691 hotlist_but[i].text = _(hotlist_but[i].text);
693 cancel_but = _(cancel_but);
694 hotlist_i18n_flag = 1;
696 #endif /* ENABLE_NLS */
698 /* Dynamic resizing of buttonbars */
700 int len[2], count[2]; /* at most two lines of buttons */
701 int cur_x[2], row;
703 i = sizeof (hotlist_but) / sizeof (hotlist_but[0]);
704 len[0] = len[1] = count[0] = count[1] = 0;
706 /* Count len of buttonbars, assuming 2 extra space between buttons */
707 while (i--)
709 if (!(hotlist_but[i].type & list_type))
710 continue;
712 row = hotlist_but[i].y;
713 ++count[row];
714 len[row] += str_term_width1 (hotlist_but[i].text) + 5;
715 if (hotlist_but[i].flags == DEFPUSH_BUTTON)
716 len[row] += 2;
718 len[0] -= 2;
719 len[1] -= 2;
721 cols = max (cols, max (len[0], len[1]));
723 /* arrange buttons */
725 cur_x[0] = cur_x[1] = 0;
726 i = sizeof (hotlist_but) / sizeof (hotlist_but[0]);
727 while (i--)
729 if (!(hotlist_but[i].type & list_type))
730 continue;
732 row = hotlist_but[i].y;
734 if (hotlist_but[i].x != 0)
736 /* not first int the row */
737 if (!strcmp (hotlist_but[i].text, cancel_but))
738 hotlist_but[i].x = cols - str_term_width1 (hotlist_but[i].text) - 13;
739 else
740 hotlist_but[i].x = cur_x[row];
743 cur_x[row] += str_term_width1 (hotlist_but[i].text) + 2
744 + (hotlist_but[i].flags == DEFPUSH_BUTTON ? 5 : 3);
748 return cols;
751 /* --------------------------------------------------------------------------------------------- */
753 static void
754 init_hotlist (int list_type)
756 size_t i;
757 const char *title, *help_node;
758 int hotlist_cols;
760 hotlist_cols = init_i18n_stuff (list_type, COLS - 6);
762 do_refresh ();
764 hotlist_state.expanded =
765 mc_config_get_int (mc_main_config, "HotlistConfig", "expanded_view_of_groups", 0);
767 if (list_type == LIST_VFSLIST)
769 title = _("Active VFS directories");
770 help_node = "[vfshot]"; /* FIXME - no such node */
772 else
774 title = _("Directory hotlist");
775 help_node = "[Hotlist]";
778 hotlist_dlg =
779 create_dlg (TRUE, 0, 0, LINES - 2, hotlist_cols, dialog_colors,
780 hotlist_callback, help_node, title, DLG_CENTER | DLG_REVERSE);
782 for (i = 0; i < BUTTONS; i++)
784 if (hotlist_but[i].type & list_type)
785 add_widget_autopos (hotlist_dlg,
786 button_new (BY + hotlist_but[i].y,
787 BX + hotlist_but[i].x,
788 hotlist_but[i].ret_cmd,
789 hotlist_but[i].flags,
790 hotlist_but[i].text,
791 hotlist_button_callback), hotlist_but[i].pos_flags);
794 /* We add the labels.
795 * pname will hold entry's pathname;
796 * pname_group will hold name of current group
798 pname = label_new (UY - 11 + LINES, UX + 2, "");
799 add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
800 if (!hotlist_state.moving)
802 char label_text[BUF_TINY];
804 g_snprintf (label_text, sizeof (label_text), " %s ", _("Directory path"));
805 add_widget_autopos (hotlist_dlg,
806 label_new (UY - 12 + LINES, UX + 2,
807 label_text), WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
809 /* This one holds the displayed pathname */
810 pname_group = label_new (UY, UX + 2, _("Directory label"));
811 add_widget (hotlist_dlg, pname_group);
813 /* get new listbox */
814 l_hotlist = listbox_new (UY + 1, UX + 1, LINES - 14, COLS - 2 * UX - 8, FALSE, l_call);
816 /* Fill the hotlist with the active VFS or the hotlist */
817 #ifdef ENABLE_VFS
818 if (list_type == LIST_VFSLIST)
820 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), 0);
821 vfs_fill_names (add_name_to_list);
823 else
824 #endif /* !ENABLE_VFS */
825 fill_listbox ();
827 add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL);
828 /* add listbox to the dialogs */
831 /* --------------------------------------------------------------------------------------------- */
833 static void
834 init_movelist (int list_type, struct hotlist *item)
836 size_t i;
837 char *hdr = g_strdup_printf (_("Moving %s"), item->label);
838 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
840 do_refresh ();
842 movelist_dlg =
843 create_dlg (TRUE, 0, 0, LINES - 6, movelist_cols, dialog_colors,
844 hotlist_callback, "[Hotlist]", hdr, DLG_CENTER | DLG_REVERSE);
845 g_free (hdr);
847 for (i = 0; i < BUTTONS; i++)
849 if (hotlist_but[i].type & list_type)
850 add_widget (movelist_dlg,
851 button_new (BY - 4 + hotlist_but[i].y,
852 BX + hotlist_but[i].x,
853 hotlist_but[i].ret_cmd,
854 hotlist_but[i].flags,
855 hotlist_but[i].text, hotlist_button_callback));
858 /* We add the labels. We are interested in the last one,
859 * that one will hold the path name label
861 movelist_group = label_new (UY, UX + 2, _("Directory label"));
862 add_widget (movelist_dlg, movelist_group);
863 /* get new listbox */
864 l_movelist =
865 listbox_new (UY + 1, UX + 1, movelist_dlg->lines - 8,
866 movelist_dlg->cols - 2 * UX - 2, FALSE, l_call);
868 fill_listbox ();
870 add_widget (movelist_dlg, l_movelist);
871 /* add listbox to the dialogs */
874 /* --------------------------------------------------------------------------------------------- */
876 * Destroy the list dialog.
877 * Don't confuse with done_hotlist() for the list in memory.
880 static void
881 hotlist_done (void)
883 destroy_dlg (hotlist_dlg);
884 l_hotlist = NULL;
885 if (0)
886 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
887 repaint_screen ();
890 /* --------------------------------------------------------------------------------------------- */
892 static inline char *
893 find_group_section (struct hotlist *grp)
895 return g_strconcat (grp->directory, ".Group", (char *) NULL);
898 /* --------------------------------------------------------------------------------------------- */
900 static struct hotlist *
901 add2hotlist (char *label, char *directory, enum HotListType type, listbox_append_t pos)
903 struct hotlist *new;
904 struct hotlist *current = NULL;
907 * Hotlist is neither loaded nor loading.
908 * Must be called by "Ctrl-x a" before using hotlist.
910 if (!current_group)
911 load_hotlist ();
913 listbox_get_current (l_hotlist, NULL, (void **) &current);
915 /* Make sure `..' stays at the top of the list. */
916 if ((current != NULL) && (current->type == HL_TYPE_DOTDOT))
917 pos = LISTBOX_APPEND_AFTER;
919 new = new_hotlist ();
921 new->type = type;
922 new->label = label;
923 new->directory = directory;
924 new->up = current_group;
926 if (type == HL_TYPE_GROUP)
928 current_group = new;
929 add_dotdot_to_list ();
930 current_group = new->up;
933 if (!current_group->head)
935 /* first element in group */
936 current_group->head = new;
938 else if (pos == LISTBOX_APPEND_AFTER)
940 new->next = current->next;
941 current->next = new;
943 else if (pos == LISTBOX_APPEND_BEFORE && current == current_group->head)
945 /* should be inserted before first item */
946 new->next = current;
947 current_group->head = new;
949 else if (pos == LISTBOX_APPEND_BEFORE)
951 struct hotlist *p = current_group->head;
953 while (p->next != current)
954 p = p->next;
956 new->next = current;
957 p->next = new;
959 else
960 { /* append at the end */
961 struct hotlist *p = current_group->head;
963 while (p->next)
964 p = p->next;
966 p->next = new;
969 if (hotlist_state.running && type != HL_TYPE_COMMENT && type != HL_TYPE_DOTDOT)
971 if (type == HL_TYPE_GROUP)
973 char *lbl = g_strconcat ("->", new->label, (char *) NULL);
975 listbox_add_item (l_hotlist, pos, 0, lbl, new);
976 g_free (lbl);
978 else
979 listbox_add_item (l_hotlist, pos, 0, new->label, new);
980 listbox_select_entry (l_hotlist, l_hotlist->pos);
982 return new;
986 /* --------------------------------------------------------------------------------------------- */
988 * Support routine for add_new_entry_input()/add_new_group_input()
989 * Change positions of buttons (first three widgets).
991 * This is just a quick hack. Accurate procedure must take care of
992 * internationalized label lengths and total buttonbar length...assume
993 * 64 is longer anyway.
996 #ifdef ENABLE_NLS
997 static void
998 add_widgets_i18n (QuickWidget * qw, int len)
1000 int i, l[3], space, cur_x;
1002 for (i = 0; i < 3; i++)
1004 qw[i].u.button.text = _(qw[i].u.button.text);
1005 l[i] = str_term_width1 (qw[i].u.button.text) + 3;
1007 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
1009 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space)
1011 qw[i].relative_x = cur_x;
1012 qw[i].x_divisions = len;
1015 #endif /* ENABLE_NLS */
1017 /* --------------------------------------------------------------------------------------------- */
1019 static int
1020 add_new_entry_input (const char *header, const char *text1, const char *text2,
1021 const char *help, char **r1, char **r2)
1023 #define RELATIVE_Y_BUTTONS 4
1024 #define RELATIVE_Y_LABEL_PTH 3
1025 #define RELATIVE_Y_INPUT_PTH 4
1027 QuickWidget quick_widgets[] = {
1028 /* 0 */ QUICK_BUTTON (55, 80, RELATIVE_Y_BUTTONS, 0, N_("&Cancel"), B_CANCEL, NULL),
1029 /* 1 */ QUICK_BUTTON (30, 80, RELATIVE_Y_BUTTONS, 0, N_("&Insert"), B_INSERT, NULL),
1030 /* 2 */ QUICK_BUTTON (10, 80, RELATIVE_Y_BUTTONS, 0, N_("&Append"), B_APPEND, NULL),
1031 /* 3 */ QUICK_INPUT (4, 80, RELATIVE_Y_INPUT_PTH, 0, *r2, 58, 2, "input-pth", r2),
1032 /* 4 */ QUICK_LABEL (4, 80, 3, 0, text2),
1033 /* 5 */ QUICK_INPUT (4, 80, 3, 0, *r1, 58, 0, "input-lbl", r1),
1034 /* 6 */ QUICK_LABEL (4, 80, 2, 0, text1),
1035 QUICK_END
1038 int len;
1039 int i;
1040 int lines1, lines2;
1041 int cols1, cols2;
1043 #ifdef ENABLE_NLS
1044 static gboolean i18n_flag = FALSE;
1045 #endif /* ENABLE_NLS */
1047 len = str_term_width1 (header);
1048 str_msg_term_size (text1, &lines1, &cols1);
1049 str_msg_term_size (text2, &lines2, &cols2);
1050 len = max (len, cols1);
1051 len = max (max (len, cols2) + 4, 64);
1053 #ifdef ENABLE_NLS
1054 if (!i18n_flag)
1056 add_widgets_i18n (quick_widgets, len);
1057 i18n_flag = TRUE;
1059 #endif /* ENABLE_NLS */
1062 QuickDialog Quick_input = {
1063 len, lines1 + lines2 + 7, -1, -1, header,
1064 help, quick_widgets, NULL, FALSE
1067 for (i = 0; i < 7; i++)
1068 quick_widgets[i].y_divisions = Quick_input.ylen;
1070 quick_widgets[0].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1071 quick_widgets[1].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1072 quick_widgets[2].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1073 quick_widgets[3].relative_y = RELATIVE_Y_INPUT_PTH + (lines1);
1074 quick_widgets[4].relative_y = RELATIVE_Y_LABEL_PTH + (lines1);
1076 i = quick_dialog (&Quick_input);
1079 return (i != B_CANCEL) ? i : 0;
1081 #undef RELATIVE_Y_BUTTONS
1082 #undef RELATIVE_Y_LABEL_PTH
1083 #undef RELATIVE_Y_INPUT_PTH
1086 /* --------------------------------------------------------------------------------------------- */
1088 static void
1089 add_new_entry_cmd (void)
1091 char *title, *url, *to_free;
1092 int ret;
1094 /* Take current directory as default value for input fields */
1095 to_free = title = url = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1097 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label:"),
1098 _("Directory path:"), "[Hotlist]", &title, &url);
1099 g_free (to_free);
1101 if (!ret)
1102 return;
1103 if (!title || !*title || !url || !*url)
1105 g_free (title);
1106 g_free (url);
1107 return;
1110 if (ret == B_ENTER || ret == B_APPEND)
1111 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AFTER);
1112 else
1113 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_BEFORE);
1115 hotlist_state.modified = 1;
1118 /* --------------------------------------------------------------------------------------------- */
1120 static int
1121 add_new_group_input (const char *header, const char *label, char **result)
1123 QuickWidget quick_widgets[] = {
1124 /* 0 */ QUICK_BUTTON (55, 80, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
1125 /* 1 */ QUICK_BUTTON (30, 80, 1, 0, N_("&Insert"), B_INSERT, NULL),
1126 /* 2 */ QUICK_BUTTON (10, 80, 1, 0, N_("&Append"), B_APPEND, NULL),
1127 /* 3 */ QUICK_INPUT (4, 80, 0, 0, "", 58, 0, "input", result),
1128 /* 4 */ QUICK_LABEL (4, 80, 2, 0, label),
1129 QUICK_END
1132 int len;
1133 int i;
1134 int lines, cols;
1135 int ret;
1137 #ifdef ENABLE_NLS
1138 static gboolean i18n_flag = FALSE;
1139 #endif /* ENABLE_NLS */
1141 len = str_term_width1 (header);
1142 str_msg_term_size (label, &lines, &cols);
1143 len = max (max (len, cols) + 4, 64);
1145 #ifdef ENABLE_NLS
1146 if (!i18n_flag)
1148 add_widgets_i18n (quick_widgets, len);
1149 i18n_flag = TRUE;
1151 #endif /* ENABLE_NLS */
1154 QuickDialog Quick_input = {
1155 len, lines + 6, -1, -1, header,
1156 "[Hotlist]", quick_widgets, NULL, FALSE
1159 int relative_y[] = { 1, 1, 1, 0, 2 }; /* the relative_x component from the
1160 quick_widgets variable above */
1162 for (i = 0; i < 5; i++)
1163 quick_widgets[i].y_divisions = Quick_input.ylen;
1165 for (i = 0; i < 4; i++)
1166 quick_widgets[i].relative_y = relative_y[i] + 2 + lines;
1168 ret = quick_dialog (&Quick_input);
1171 return (ret != B_CANCEL) ? ret : 0;
1174 /* --------------------------------------------------------------------------------------------- */
1176 static void
1177 add_new_group_cmd (void)
1179 char *label;
1180 int ret;
1182 ret = add_new_group_input (_("New hotlist group"), _("Name of new group:"), &label);
1183 if (!ret || !label || !*label)
1184 return;
1186 if (ret == B_ENTER || ret == B_APPEND)
1187 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_AFTER);
1188 else
1189 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_BEFORE);
1191 hotlist_state.modified = 1;
1194 /* --------------------------------------------------------------------------------------------- */
1196 static void
1197 remove_group (struct hotlist *grp)
1199 struct hotlist *current = grp->head;
1201 while (current)
1203 struct hotlist *next = current->next;
1205 if (current->type == HL_TYPE_GROUP)
1206 remove_group (current);
1208 g_free (current->label);
1209 g_free (current->directory);
1210 g_free (current);
1212 current = next;
1217 /* --------------------------------------------------------------------------------------------- */
1219 static void
1220 remove_from_hotlist (struct hotlist *entry)
1222 if (entry == NULL)
1223 return;
1225 if (entry->type == HL_TYPE_DOTDOT)
1226 return;
1228 if (confirm_directory_hotlist_delete)
1230 char text[BUF_MEDIUM];
1231 int result;
1233 if (safe_delete)
1234 query_set_sel (1);
1236 g_snprintf (text, sizeof (text), _("Are you sure you want to remove entry \"%s\"?"),
1237 str_trunc (entry->label, 30));
1238 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1239 _("&Yes"), _("&No"));
1240 if (result != 0)
1241 return;
1244 if (entry->type == HL_TYPE_GROUP)
1246 struct hotlist *head = entry->head;
1248 if (head != NULL && (head->type != HL_TYPE_DOTDOT || head->next != NULL))
1250 char text[BUF_MEDIUM];
1251 int result;
1253 g_snprintf (text, sizeof (text), _("Group \"%s\" is not empty.\nRemove it?"),
1254 str_trunc (entry->label, 30));
1255 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1256 _("&Yes"), _("&No"));
1257 if (result != 0)
1258 return;
1261 remove_group (entry);
1264 unlink_entry (entry);
1266 g_free (entry->label);
1267 g_free (entry->directory);
1268 g_free (entry);
1269 /* now remove list entry from screen */
1270 listbox_remove_current (l_hotlist);
1271 hotlist_state.modified = 1;
1274 /* --------------------------------------------------------------------------------------------- */
1276 static void
1277 load_group (struct hotlist *grp)
1279 gchar **profile_keys, **keys;
1280 gsize len;
1281 char *group_section;
1282 struct hotlist *current = 0;
1284 group_section = find_group_section (grp);
1286 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1288 current_group = grp;
1290 while (*profile_keys)
1292 add2hotlist (mc_config_get_string (mc_main_config, group_section, *profile_keys, ""),
1293 g_strdup (*profile_keys), HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1294 profile_keys++;
1296 g_free (group_section);
1297 g_strfreev (keys);
1299 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory, &len);
1301 while (*profile_keys)
1303 add2hotlist (mc_config_get_string (mc_main_config, group_section, *profile_keys, ""),
1304 g_strdup (*profile_keys), HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1305 profile_keys++;
1307 g_strfreev (keys);
1309 for (current = grp->head; current; current = current->next)
1310 load_group (current);
1313 /* --------------------------------------------------------------------------------------------- */
1315 static int
1316 hot_skip_blanks (void)
1318 int c;
1320 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1322 return c;
1326 /* --------------------------------------------------------------------------------------------- */
1328 static int
1329 hot_next_token (void)
1331 int c, ret = 0;
1332 size_t l;
1335 if (tkn_buf == NULL)
1336 tkn_buf = g_string_new ("");
1337 g_string_set_size (tkn_buf, 0);
1339 again:
1340 c = hot_skip_blanks ();
1341 switch (c)
1343 case EOF:
1344 ret = TKN_EOF;
1345 break;
1346 case '\n':
1347 ret = TKN_EOL;
1348 break;
1349 case '#':
1350 while ((c = getc (hotlist_file)) != EOF && c != '\n')
1352 g_string_append_c (tkn_buf, c);
1354 ret = TKN_COMMENT;
1355 break;
1356 case '"':
1357 while ((c = getc (hotlist_file)) != EOF && c != '"')
1359 if (c == '\\')
1361 c = getc (hotlist_file);
1362 if (c == EOF)
1364 g_string_free (tkn_buf, TRUE);
1365 return TKN_EOF;
1368 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1370 if (c == EOF)
1371 ret = TKN_EOF;
1372 else
1373 ret = TKN_STRING;
1374 break;
1375 case '\\':
1376 c = getc (hotlist_file);
1377 if (c == EOF)
1379 g_string_free (tkn_buf, TRUE);
1380 return TKN_EOF;
1382 if (c == '\n')
1383 goto again;
1385 /* fall through; it is taken as normal character */
1387 default:
1390 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1392 while ((c = fgetc (hotlist_file)) != EOF && (g_ascii_isalnum (c) || !isascii (c)));
1393 if (c != EOF)
1394 ungetc (c, hotlist_file);
1395 l = tkn_buf->len;
1396 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1397 ret = TKN_GROUP;
1398 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1399 ret = TKN_ENTRY;
1400 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1401 ret = TKN_ENDGROUP;
1402 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1403 ret = TKN_URL;
1404 else
1405 ret = TKN_UNKNOWN;
1406 break;
1408 return ret;
1411 /* --------------------------------------------------------------------------------------------- */
1413 static void
1414 hot_load_group (struct hotlist *grp)
1416 int tkn;
1417 struct hotlist *new_grp;
1418 char *label, *url;
1420 current_group = grp;
1422 while ((tkn = hot_next_token ()) != TKN_ENDGROUP)
1423 switch (tkn)
1425 case TKN_GROUP:
1426 CHECK_TOKEN (TKN_STRING);
1427 new_grp =
1428 add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1429 SKIP_TO_EOL;
1430 hot_load_group (new_grp);
1431 current_group = grp;
1432 break;
1433 case TKN_ENTRY:
1435 CHECK_TOKEN (TKN_STRING);
1436 label = g_strdup (tkn_buf->str);
1437 CHECK_TOKEN (TKN_URL);
1438 CHECK_TOKEN (TKN_STRING);
1439 url = g_strdup (tkn_buf->str);
1440 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1441 SKIP_TO_EOL;
1443 break;
1444 case TKN_COMMENT:
1445 label = g_strdup (tkn_buf->str);
1446 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1447 break;
1448 case TKN_EOF:
1449 hotlist_state.readonly = 1;
1450 hotlist_state.file_error = 1;
1451 return;
1452 break;
1453 case TKN_EOL:
1454 /* skip empty lines */
1455 break;
1456 default:
1457 hotlist_state.readonly = 1;
1458 hotlist_state.file_error = 1;
1459 SKIP_TO_EOL;
1460 break;
1462 SKIP_TO_EOL;
1465 /* --------------------------------------------------------------------------------------------- */
1467 static void
1468 hot_load_file (struct hotlist *grp)
1470 int tkn;
1471 struct hotlist *new_grp;
1472 char *label, *url;
1474 current_group = grp;
1476 while ((tkn = hot_next_token ()) != TKN_EOF)
1477 switch (tkn)
1479 case TKN_GROUP:
1480 CHECK_TOKEN (TKN_STRING);
1481 new_grp =
1482 add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1483 SKIP_TO_EOL;
1484 hot_load_group (new_grp);
1485 current_group = grp;
1486 break;
1487 case TKN_ENTRY:
1489 CHECK_TOKEN (TKN_STRING);
1490 label = g_strdup (tkn_buf->str);
1491 CHECK_TOKEN (TKN_URL);
1492 CHECK_TOKEN (TKN_STRING);
1493 url = g_strdup (tkn_buf->str);
1494 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1495 SKIP_TO_EOL;
1497 break;
1498 case TKN_COMMENT:
1499 label = g_strdup (tkn_buf->str);
1500 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1501 break;
1502 case TKN_EOL:
1503 /* skip empty lines */
1504 break;
1505 default:
1506 hotlist_state.readonly = 1;
1507 hotlist_state.file_error = 1;
1508 SKIP_TO_EOL;
1509 break;
1513 /* --------------------------------------------------------------------------------------------- */
1515 static void
1516 clean_up_hotlist_groups (const char *section)
1518 char *grp_section;
1519 gchar **profile_keys, **keys;
1520 gsize len;
1522 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1523 if (mc_config_has_group (mc_main_config, section))
1524 mc_config_del_group (mc_main_config, section);
1526 if (mc_config_has_group (mc_main_config, grp_section))
1528 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section, &len);
1530 while (*profile_keys)
1532 clean_up_hotlist_groups (*profile_keys);
1533 profile_keys++;
1535 g_strfreev (keys);
1536 mc_config_del_group (mc_main_config, grp_section);
1538 g_free (grp_section);
1541 /* --------------------------------------------------------------------------------------------- */
1543 static void
1544 load_hotlist (void)
1546 int remove_old_list = 0;
1547 struct stat stat_buf;
1549 if (hotlist_state.loaded)
1551 stat (hotlist_file_name, &stat_buf);
1552 if (hotlist_file_mtime < stat_buf.st_mtime)
1553 done_hotlist ();
1554 else
1555 return;
1558 if (!hotlist_file_name)
1559 hotlist_file_name = mc_config_get_full_path (MC_HOTLIST_FILE);
1561 hotlist = new_hotlist ();
1562 hotlist->type = HL_TYPE_GROUP;
1563 hotlist->label = g_strdup (_("Top level group"));
1564 hotlist->up = hotlist;
1566 * compatibility :-(
1568 hotlist->directory = g_strdup ("Hotlist");
1570 hotlist_file = fopen (hotlist_file_name, "r");
1571 if (hotlist_file == NULL)
1573 int result;
1575 load_group (hotlist);
1576 hotlist_state.loaded = 1;
1578 * just to be sure we got copy
1580 hotlist_state.modified = 1;
1581 result = save_hotlist ();
1582 hotlist_state.modified = 0;
1583 if (result)
1584 remove_old_list = 1;
1585 else
1586 message (D_ERROR, _("Hotlist Load"),
1588 ("MC was unable to write %s file,\nyour old hotlist entries were not deleted"),
1589 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1591 else
1593 hot_load_file (hotlist);
1594 fclose (hotlist_file);
1595 hotlist_state.loaded = 1;
1598 if (remove_old_list)
1600 GError *error = NULL;
1601 clean_up_hotlist_groups ("Hotlist");
1602 if (!mc_config_save_file (mc_main_config, &error))
1603 setup_save_config_show_error (mc_main_config->ini_path, &error);
1606 stat (hotlist_file_name, &stat_buf);
1607 hotlist_file_mtime = stat_buf.st_mtime;
1608 current_group = hotlist;
1611 /* --------------------------------------------------------------------------------------------- */
1613 static void
1614 hot_save_group (struct hotlist *grp)
1616 struct hotlist *current = grp->head;
1617 int i;
1618 char *s;
1620 #define INDENT(n) \
1621 do { \
1622 for (i = 0; i < n; i++) \
1623 putc (' ', hotlist_file); \
1624 } while (0)
1626 for (; current; current = current->next)
1627 switch (current->type)
1629 case HL_TYPE_GROUP:
1630 INDENT (list_level);
1631 fputs ("GROUP \"", hotlist_file);
1632 for (s = current->label; *s; s++)
1634 if (*s == '"' || *s == '\\')
1635 putc ('\\', hotlist_file);
1636 putc (*s, hotlist_file);
1638 fputs ("\"\n", hotlist_file);
1639 list_level += 2;
1640 hot_save_group (current);
1641 list_level -= 2;
1642 INDENT (list_level);
1643 fputs ("ENDGROUP\n", hotlist_file);
1644 break;
1645 case HL_TYPE_ENTRY:
1646 INDENT (list_level);
1647 fputs ("ENTRY \"", hotlist_file);
1648 for (s = current->label; *s; s++)
1650 if (*s == '"' || *s == '\\')
1651 putc ('\\', hotlist_file);
1652 putc (*s, hotlist_file);
1654 fputs ("\" URL \"", hotlist_file);
1655 for (s = current->directory; *s; s++)
1657 if (*s == '"' || *s == '\\')
1658 putc ('\\', hotlist_file);
1659 putc (*s, hotlist_file);
1661 fputs ("\"\n", hotlist_file);
1662 break;
1663 case HL_TYPE_COMMENT:
1664 fprintf (hotlist_file, "#%s\n", current->label);
1665 break;
1666 case HL_TYPE_DOTDOT:
1667 /* do nothing */
1668 break;
1672 /* --------------------------------------------------------------------------------------------- */
1674 static void
1675 add_dotdot_to_list (void)
1677 if (current_group != hotlist)
1679 if (hotlist_has_dot_dot != 0)
1680 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, LISTBOX_APPEND_AT_END);
1684 /* --------------------------------------------------------------------------------------------- */
1685 /*** public functions ****************************************************************************/
1686 /* --------------------------------------------------------------------------------------------- */
1688 void
1689 add2hotlist_cmd (void)
1691 char *lc_prompt;
1692 const char *cp = N_("Label for \"%s\":");
1693 int l;
1694 char *label_string, *label;
1696 #ifdef ENABLE_NLS
1697 cp = _(cp);
1698 #endif
1700 l = str_term_width1 (cp);
1701 label_string = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1702 lc_prompt = g_strdup_printf (cp, str_trunc (label_string, COLS - 2 * UX - (l + 8)));
1703 label = input_dialog (_("Add to hotlist"), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string);
1704 g_free (lc_prompt);
1706 if (!label || !*label)
1708 g_free (label_string);
1709 g_free (label);
1710 return;
1712 add2hotlist (label, label_string, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1713 hotlist_state.modified = 1;
1714 g_free (label_string);
1717 /* --------------------------------------------------------------------------------------------- */
1719 char *
1720 hotlist_show (int vfs_or_hotlist)
1722 char *target = NULL;
1724 hotlist_state.type = vfs_or_hotlist;
1725 load_hotlist ();
1727 init_hotlist (vfs_or_hotlist);
1729 /* display file info */
1730 tty_setcolor (SELECTED_COLOR);
1732 hotlist_state.running = 1;
1733 run_dlg (hotlist_dlg);
1734 hotlist_state.running = 0;
1735 save_hotlist ();
1737 switch (hotlist_dlg->ret_value)
1739 default:
1740 case B_CANCEL:
1741 break;
1743 case B_ENTER:
1745 char *text = NULL;
1746 struct hotlist *hlp = NULL;
1748 listbox_get_current (l_hotlist, &text, (void **) &hlp);
1749 target = g_strdup (hlp != NULL ? hlp->directory : text);
1750 break;
1752 } /* switch */
1754 hotlist_done ();
1755 return target;
1758 /* --------------------------------------------------------------------------------------------- */
1761 save_hotlist (void)
1763 int saved = 0;
1764 struct stat stat_buf;
1766 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name)
1768 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1770 hotlist_file = fopen (hotlist_file_name, "w");
1771 if (hotlist_file != NULL)
1773 hot_save_group (hotlist);
1774 fclose (hotlist_file);
1775 stat (hotlist_file_name, &stat_buf);
1776 hotlist_file_mtime = stat_buf.st_mtime;
1777 saved = 1;
1778 hotlist_state.modified = 0;
1780 else
1781 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1784 return saved;
1787 /* --------------------------------------------------------------------------------------------- */
1789 * Unload list from memory.
1790 * Don't confuse with hotlist_done() for GUI.
1793 void
1794 done_hotlist (void)
1796 if (hotlist)
1798 remove_group (hotlist);
1799 g_free (hotlist->label);
1800 g_free (hotlist->directory);
1801 g_free (hotlist);
1802 hotlist = 0;
1805 hotlist_state.loaded = 0;
1807 g_free (hotlist_file_name);
1808 hotlist_file_name = 0;
1809 l_hotlist = 0;
1810 current_group = 0;
1812 if (tkn_buf)
1814 g_string_free (tkn_buf, TRUE);
1815 tkn_buf = NULL;
1819 /* --------------------------------------------------------------------------------------------- */