Use g_strndup() instead of g_strdup()
[midnight-commander.git] / src / filemanager / hotlist.c
blob23722acda428fc6f6ee673bf397be3620ce4be21
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 (TRUE);
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, NULL, 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,
792 NULL);
795 /* We add the labels.
796 * pname will hold entry's pathname;
797 * pname_group will hold name of current group
799 pname = label_new (UY - 11 + LINES, UX + 2, "");
800 add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
801 if (!hotlist_state.moving)
803 char label_text[BUF_TINY];
805 g_snprintf (label_text, sizeof (label_text), " %s ", _("Directory path"));
806 add_widget_autopos (hotlist_dlg,
807 label_new (UY - 12 + LINES, UX + 2,
808 label_text), WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
810 /* This one holds the displayed pathname */
811 pname_group = label_new (UY, UX + 2, _("Directory label"));
812 add_widget (hotlist_dlg, pname_group);
814 /* get new listbox */
815 l_hotlist = listbox_new (UY + 1, UX + 1, LINES - 14, COLS - 2 * UX - 8, FALSE, l_call);
817 /* Fill the hotlist with the active VFS or the hotlist */
818 #ifdef ENABLE_VFS
819 if (list_type == LIST_VFSLIST)
821 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), 0);
822 vfs_fill_names (add_name_to_list);
824 else
825 #endif /* !ENABLE_VFS */
826 fill_listbox ();
828 add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL, NULL);
829 /* add listbox to the dialogs */
832 /* --------------------------------------------------------------------------------------------- */
834 static void
835 init_movelist (int list_type, struct hotlist *item)
837 size_t i;
838 char *hdr = g_strdup_printf (_("Moving %s"), item->label);
839 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
841 do_refresh ();
843 movelist_dlg =
844 create_dlg (TRUE, 0, 0, LINES - 6, movelist_cols, dialog_colors,
845 hotlist_callback, NULL, "[Hotlist]", hdr, DLG_CENTER | DLG_REVERSE);
846 g_free (hdr);
848 for (i = 0; i < BUTTONS; i++)
850 if (hotlist_but[i].type & list_type)
851 add_widget (movelist_dlg,
852 button_new (BY - 4 + hotlist_but[i].y,
853 BX + hotlist_but[i].x,
854 hotlist_but[i].ret_cmd,
855 hotlist_but[i].flags,
856 hotlist_but[i].text, hotlist_button_callback));
859 /* We add the labels. We are interested in the last one,
860 * that one will hold the path name label
862 movelist_group = label_new (UY, UX + 2, _("Directory label"));
863 add_widget (movelist_dlg, movelist_group);
864 /* get new listbox */
865 l_movelist =
866 listbox_new (UY + 1, UX + 1, movelist_dlg->lines - 8,
867 movelist_dlg->cols - 2 * UX - 2, FALSE, l_call);
869 fill_listbox ();
871 add_widget (movelist_dlg, l_movelist);
872 /* add listbox to the dialogs */
875 /* --------------------------------------------------------------------------------------------- */
877 * Destroy the list dialog.
878 * Don't confuse with done_hotlist() for the list in memory.
881 static void
882 hotlist_done (void)
884 destroy_dlg (hotlist_dlg);
885 l_hotlist = NULL;
886 if (0)
887 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
888 repaint_screen ();
891 /* --------------------------------------------------------------------------------------------- */
893 static inline char *
894 find_group_section (struct hotlist *grp)
896 return g_strconcat (grp->directory, ".Group", (char *) NULL);
899 /* --------------------------------------------------------------------------------------------- */
901 static struct hotlist *
902 add2hotlist (char *label, char *directory, enum HotListType type, listbox_append_t pos)
904 struct hotlist *new;
905 struct hotlist *current = NULL;
908 * Hotlist is neither loaded nor loading.
909 * Must be called by "Ctrl-x a" before using hotlist.
911 if (!current_group)
912 load_hotlist ();
914 listbox_get_current (l_hotlist, NULL, (void **) &current);
916 /* Make sure `..' stays at the top of the list. */
917 if ((current != NULL) && (current->type == HL_TYPE_DOTDOT))
918 pos = LISTBOX_APPEND_AFTER;
920 new = new_hotlist ();
922 new->type = type;
923 new->label = label;
924 new->directory = directory;
925 new->up = current_group;
927 if (type == HL_TYPE_GROUP)
929 current_group = new;
930 add_dotdot_to_list ();
931 current_group = new->up;
934 if (!current_group->head)
936 /* first element in group */
937 current_group->head = new;
939 else if (pos == LISTBOX_APPEND_AFTER)
941 new->next = current->next;
942 current->next = new;
944 else if (pos == LISTBOX_APPEND_BEFORE && current == current_group->head)
946 /* should be inserted before first item */
947 new->next = current;
948 current_group->head = new;
950 else if (pos == LISTBOX_APPEND_BEFORE)
952 struct hotlist *p = current_group->head;
954 while (p->next != current)
955 p = p->next;
957 new->next = current;
958 p->next = new;
960 else
961 { /* append at the end */
962 struct hotlist *p = current_group->head;
964 while (p->next)
965 p = p->next;
967 p->next = new;
970 if (hotlist_state.running && type != HL_TYPE_COMMENT && type != HL_TYPE_DOTDOT)
972 if (type == HL_TYPE_GROUP)
974 char *lbl = g_strconcat ("->", new->label, (char *) NULL);
976 listbox_add_item (l_hotlist, pos, 0, lbl, new);
977 g_free (lbl);
979 else
980 listbox_add_item (l_hotlist, pos, 0, new->label, new);
981 listbox_select_entry (l_hotlist, l_hotlist->pos);
983 return new;
987 /* --------------------------------------------------------------------------------------------- */
989 * Support routine for add_new_entry_input()/add_new_group_input()
990 * Change positions of buttons (first three widgets).
992 * This is just a quick hack. Accurate procedure must take care of
993 * internationalized label lengths and total buttonbar length...assume
994 * 64 is longer anyway.
997 #ifdef ENABLE_NLS
998 static void
999 add_widgets_i18n (QuickWidget * qw, int len)
1001 int i, l[3], space, cur_x;
1003 for (i = 0; i < 3; i++)
1005 qw[i].u.button.text = _(qw[i].u.button.text);
1006 l[i] = str_term_width1 (qw[i].u.button.text) + 3;
1008 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
1010 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space)
1012 qw[i].relative_x = cur_x;
1013 qw[i].x_divisions = len;
1016 #endif /* ENABLE_NLS */
1018 /* --------------------------------------------------------------------------------------------- */
1020 static int
1021 add_new_entry_input (const char *header, const char *text1, const char *text2,
1022 const char *help, char **r1, char **r2)
1024 #define RELATIVE_Y_BUTTONS 4
1025 #define RELATIVE_Y_LABEL_PTH 3
1026 #define RELATIVE_Y_INPUT_PTH 4
1028 QuickWidget quick_widgets[] = {
1029 /* 0 */ QUICK_BUTTON (55, 80, RELATIVE_Y_BUTTONS, 0, N_("&Cancel"), B_CANCEL, NULL),
1030 /* 1 */ QUICK_BUTTON (30, 80, RELATIVE_Y_BUTTONS, 0, N_("&Insert"), B_INSERT, NULL),
1031 /* 2 */ QUICK_BUTTON (10, 80, RELATIVE_Y_BUTTONS, 0, N_("&Append"), B_APPEND, NULL),
1032 /* 3 */ QUICK_INPUT (4, 80, RELATIVE_Y_INPUT_PTH, 0, *r2, 58, 2, "input-pth", r2),
1033 /* 4 */ QUICK_LABEL (4, 80, 3, 0, text2),
1034 /* 5 */ QUICK_INPUT (4, 80, 3, 0, *r1, 58, 0, "input-lbl", r1),
1035 /* 6 */ QUICK_LABEL (4, 80, 2, 0, text1),
1036 QUICK_END
1039 int len;
1040 int i;
1041 int lines1, lines2;
1042 int cols1, cols2;
1044 #ifdef ENABLE_NLS
1045 static gboolean i18n_flag = FALSE;
1046 #endif /* ENABLE_NLS */
1048 len = str_term_width1 (header);
1049 str_msg_term_size (text1, &lines1, &cols1);
1050 str_msg_term_size (text2, &lines2, &cols2);
1051 len = max (len, cols1);
1052 len = max (max (len, cols2) + 4, 64);
1054 #ifdef ENABLE_NLS
1055 if (!i18n_flag)
1057 add_widgets_i18n (quick_widgets, len);
1058 i18n_flag = TRUE;
1060 #endif /* ENABLE_NLS */
1063 QuickDialog Quick_input = {
1064 len, lines1 + lines2 + 7, -1, -1, header,
1065 help, quick_widgets, NULL, NULL, FALSE
1068 for (i = 0; i < 7; i++)
1069 quick_widgets[i].y_divisions = Quick_input.ylen;
1071 quick_widgets[0].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1072 quick_widgets[1].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1073 quick_widgets[2].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
1074 quick_widgets[3].relative_y = RELATIVE_Y_INPUT_PTH + (lines1);
1075 quick_widgets[4].relative_y = RELATIVE_Y_LABEL_PTH + (lines1);
1077 i = quick_dialog (&Quick_input);
1080 return (i != B_CANCEL) ? i : 0;
1082 #undef RELATIVE_Y_BUTTONS
1083 #undef RELATIVE_Y_LABEL_PTH
1084 #undef RELATIVE_Y_INPUT_PTH
1087 /* --------------------------------------------------------------------------------------------- */
1089 static void
1090 add_new_entry_cmd (void)
1092 char *title, *url, *to_free;
1093 int ret;
1095 /* Take current directory as default value for input fields */
1096 to_free = title = url = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1098 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label:"),
1099 _("Directory path:"), "[Hotlist]", &title, &url);
1100 g_free (to_free);
1102 if (!ret)
1103 return;
1104 if (!title || !*title || !url || !*url)
1106 g_free (title);
1107 g_free (url);
1108 return;
1111 if (ret == B_ENTER || ret == B_APPEND)
1112 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AFTER);
1113 else
1114 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_BEFORE);
1116 hotlist_state.modified = 1;
1119 /* --------------------------------------------------------------------------------------------- */
1121 static int
1122 add_new_group_input (const char *header, const char *label, char **result)
1124 QuickWidget quick_widgets[] = {
1125 /* 0 */ QUICK_BUTTON (55, 80, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
1126 /* 1 */ QUICK_BUTTON (30, 80, 1, 0, N_("&Insert"), B_INSERT, NULL),
1127 /* 2 */ QUICK_BUTTON (10, 80, 1, 0, N_("&Append"), B_APPEND, NULL),
1128 /* 3 */ QUICK_INPUT (4, 80, 0, 0, "", 58, 0, "input", result),
1129 /* 4 */ QUICK_LABEL (4, 80, 2, 0, label),
1130 QUICK_END
1133 int len;
1134 int i;
1135 int lines, cols;
1136 int ret;
1138 #ifdef ENABLE_NLS
1139 static gboolean i18n_flag = FALSE;
1140 #endif /* ENABLE_NLS */
1142 len = str_term_width1 (header);
1143 str_msg_term_size (label, &lines, &cols);
1144 len = max (max (len, cols) + 4, 64);
1146 #ifdef ENABLE_NLS
1147 if (!i18n_flag)
1149 add_widgets_i18n (quick_widgets, len);
1150 i18n_flag = TRUE;
1152 #endif /* ENABLE_NLS */
1155 QuickDialog Quick_input = {
1156 len, lines + 6, -1, -1, header,
1157 "[Hotlist]", quick_widgets, NULL, NULL, FALSE
1160 int relative_y[] = { 1, 1, 1, 0, 2 }; /* the relative_x component from the
1161 quick_widgets variable above */
1163 for (i = 0; i < 5; i++)
1164 quick_widgets[i].y_divisions = Quick_input.ylen;
1166 for (i = 0; i < 4; i++)
1167 quick_widgets[i].relative_y = relative_y[i] + 2 + lines;
1169 ret = quick_dialog (&Quick_input);
1172 return (ret != B_CANCEL) ? ret : 0;
1175 /* --------------------------------------------------------------------------------------------- */
1177 static void
1178 add_new_group_cmd (void)
1180 char *label;
1181 int ret;
1183 ret = add_new_group_input (_("New hotlist group"), _("Name of new group:"), &label);
1184 if (!ret || !label || !*label)
1185 return;
1187 if (ret == B_ENTER || ret == B_APPEND)
1188 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_AFTER);
1189 else
1190 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_BEFORE);
1192 hotlist_state.modified = 1;
1195 /* --------------------------------------------------------------------------------------------- */
1197 static void
1198 remove_group (struct hotlist *grp)
1200 struct hotlist *current = grp->head;
1202 while (current)
1204 struct hotlist *next = current->next;
1206 if (current->type == HL_TYPE_GROUP)
1207 remove_group (current);
1209 g_free (current->label);
1210 g_free (current->directory);
1211 g_free (current);
1213 current = next;
1218 /* --------------------------------------------------------------------------------------------- */
1220 static void
1221 remove_from_hotlist (struct hotlist *entry)
1223 if (entry == NULL)
1224 return;
1226 if (entry->type == HL_TYPE_DOTDOT)
1227 return;
1229 if (confirm_directory_hotlist_delete)
1231 char text[BUF_MEDIUM];
1232 int result;
1234 if (safe_delete)
1235 query_set_sel (1);
1237 g_snprintf (text, sizeof (text), _("Are you sure you want to remove entry \"%s\"?"),
1238 str_trunc (entry->label, 30));
1239 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1240 _("&Yes"), _("&No"));
1241 if (result != 0)
1242 return;
1245 if (entry->type == HL_TYPE_GROUP)
1247 struct hotlist *head = entry->head;
1249 if (head != NULL && (head->type != HL_TYPE_DOTDOT || head->next != NULL))
1251 char text[BUF_MEDIUM];
1252 int result;
1254 g_snprintf (text, sizeof (text), _("Group \"%s\" is not empty.\nRemove it?"),
1255 str_trunc (entry->label, 30));
1256 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1257 _("&Yes"), _("&No"));
1258 if (result != 0)
1259 return;
1262 remove_group (entry);
1265 unlink_entry (entry);
1267 g_free (entry->label);
1268 g_free (entry->directory);
1269 g_free (entry);
1270 /* now remove list entry from screen */
1271 listbox_remove_current (l_hotlist);
1272 hotlist_state.modified = 1;
1275 /* --------------------------------------------------------------------------------------------- */
1277 static void
1278 load_group (struct hotlist *grp)
1280 gchar **profile_keys, **keys;
1281 gsize len;
1282 char *group_section;
1283 struct hotlist *current = 0;
1285 group_section = find_group_section (grp);
1287 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1289 current_group = grp;
1291 while (*profile_keys)
1293 add2hotlist (mc_config_get_string (mc_main_config, group_section, *profile_keys, ""),
1294 g_strdup (*profile_keys), HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1295 profile_keys++;
1297 g_free (group_section);
1298 g_strfreev (keys);
1300 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory, &len);
1302 while (*profile_keys)
1304 add2hotlist (mc_config_get_string (mc_main_config, group_section, *profile_keys, ""),
1305 g_strdup (*profile_keys), HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1306 profile_keys++;
1308 g_strfreev (keys);
1310 for (current = grp->head; current; current = current->next)
1311 load_group (current);
1314 /* --------------------------------------------------------------------------------------------- */
1316 static int
1317 hot_skip_blanks (void)
1319 int c;
1321 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1323 return c;
1327 /* --------------------------------------------------------------------------------------------- */
1329 static int
1330 hot_next_token (void)
1332 int c, ret = 0;
1333 size_t l;
1336 if (tkn_buf == NULL)
1337 tkn_buf = g_string_new ("");
1338 g_string_set_size (tkn_buf, 0);
1340 again:
1341 c = hot_skip_blanks ();
1342 switch (c)
1344 case EOF:
1345 ret = TKN_EOF;
1346 break;
1347 case '\n':
1348 ret = TKN_EOL;
1349 break;
1350 case '#':
1351 while ((c = getc (hotlist_file)) != EOF && c != '\n')
1353 g_string_append_c (tkn_buf, c);
1355 ret = TKN_COMMENT;
1356 break;
1357 case '"':
1358 while ((c = getc (hotlist_file)) != EOF && c != '"')
1360 if (c == '\\')
1362 c = getc (hotlist_file);
1363 if (c == EOF)
1365 g_string_free (tkn_buf, TRUE);
1366 return TKN_EOF;
1369 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1371 if (c == EOF)
1372 ret = TKN_EOF;
1373 else
1374 ret = TKN_STRING;
1375 break;
1376 case '\\':
1377 c = getc (hotlist_file);
1378 if (c == EOF)
1380 g_string_free (tkn_buf, TRUE);
1381 return TKN_EOF;
1383 if (c == '\n')
1384 goto again;
1386 /* fall through; it is taken as normal character */
1388 default:
1391 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1393 while ((c = fgetc (hotlist_file)) != EOF && (g_ascii_isalnum (c) || !isascii (c)));
1394 if (c != EOF)
1395 ungetc (c, hotlist_file);
1396 l = tkn_buf->len;
1397 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1398 ret = TKN_GROUP;
1399 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1400 ret = TKN_ENTRY;
1401 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1402 ret = TKN_ENDGROUP;
1403 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1404 ret = TKN_URL;
1405 else
1406 ret = TKN_UNKNOWN;
1407 break;
1409 return ret;
1412 /* --------------------------------------------------------------------------------------------- */
1414 static void
1415 hot_load_group (struct hotlist *grp)
1417 int tkn;
1418 struct hotlist *new_grp;
1419 char *label, *url;
1421 current_group = grp;
1423 while ((tkn = hot_next_token ()) != TKN_ENDGROUP)
1424 switch (tkn)
1426 case TKN_GROUP:
1427 CHECK_TOKEN (TKN_STRING);
1428 new_grp =
1429 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1430 LISTBOX_APPEND_AT_END);
1431 SKIP_TO_EOL;
1432 hot_load_group (new_grp);
1433 current_group = grp;
1434 break;
1435 case TKN_ENTRY:
1437 CHECK_TOKEN (TKN_STRING);
1438 label = g_strndup (tkn_buf->str, tkn_buf->len);
1439 CHECK_TOKEN (TKN_URL);
1440 CHECK_TOKEN (TKN_STRING);
1441 url = tilde_expand (tkn_buf->str);
1442 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1443 SKIP_TO_EOL;
1445 break;
1446 case TKN_COMMENT:
1447 label = g_strndup (tkn_buf->str, tkn_buf->len);
1448 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1449 break;
1450 case TKN_EOF:
1451 hotlist_state.readonly = 1;
1452 hotlist_state.file_error = 1;
1453 return;
1454 break;
1455 case TKN_EOL:
1456 /* skip empty lines */
1457 break;
1458 default:
1459 hotlist_state.readonly = 1;
1460 hotlist_state.file_error = 1;
1461 SKIP_TO_EOL;
1462 break;
1464 SKIP_TO_EOL;
1467 /* --------------------------------------------------------------------------------------------- */
1469 static void
1470 hot_load_file (struct hotlist *grp)
1472 int tkn;
1473 struct hotlist *new_grp;
1474 char *label, *url;
1476 current_group = grp;
1478 while ((tkn = hot_next_token ()) != TKN_EOF)
1479 switch (tkn)
1481 case TKN_GROUP:
1482 CHECK_TOKEN (TKN_STRING);
1483 new_grp =
1484 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1485 LISTBOX_APPEND_AT_END);
1486 SKIP_TO_EOL;
1487 hot_load_group (new_grp);
1488 current_group = grp;
1489 break;
1490 case TKN_ENTRY:
1492 CHECK_TOKEN (TKN_STRING);
1493 label = g_strndup (tkn_buf->str, tkn_buf->len);
1494 CHECK_TOKEN (TKN_URL);
1495 CHECK_TOKEN (TKN_STRING);
1496 url = tilde_expand (tkn_buf->str);
1497 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1498 SKIP_TO_EOL;
1500 break;
1501 case TKN_COMMENT:
1502 label = g_strndup (tkn_buf->str, tkn_buf->len);
1503 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1504 break;
1505 case TKN_EOL:
1506 /* skip empty lines */
1507 break;
1508 default:
1509 hotlist_state.readonly = 1;
1510 hotlist_state.file_error = 1;
1511 SKIP_TO_EOL;
1512 break;
1516 /* --------------------------------------------------------------------------------------------- */
1518 static void
1519 clean_up_hotlist_groups (const char *section)
1521 char *grp_section;
1522 gchar **profile_keys, **keys;
1523 gsize len;
1525 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1526 if (mc_config_has_group (mc_main_config, section))
1527 mc_config_del_group (mc_main_config, section);
1529 if (mc_config_has_group (mc_main_config, grp_section))
1531 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section, &len);
1533 while (*profile_keys)
1535 clean_up_hotlist_groups (*profile_keys);
1536 profile_keys++;
1538 g_strfreev (keys);
1539 mc_config_del_group (mc_main_config, grp_section);
1541 g_free (grp_section);
1544 /* --------------------------------------------------------------------------------------------- */
1546 static void
1547 load_hotlist (void)
1549 int remove_old_list = 0;
1550 struct stat stat_buf;
1552 if (hotlist_state.loaded)
1554 stat (hotlist_file_name, &stat_buf);
1555 if (hotlist_file_mtime < stat_buf.st_mtime)
1556 done_hotlist ();
1557 else
1558 return;
1561 if (!hotlist_file_name)
1562 hotlist_file_name = mc_config_get_full_path (MC_HOTLIST_FILE);
1564 hotlist = new_hotlist ();
1565 hotlist->type = HL_TYPE_GROUP;
1566 hotlist->label = g_strdup (_("Top level group"));
1567 hotlist->up = hotlist;
1569 * compatibility :-(
1571 hotlist->directory = g_strdup ("Hotlist");
1573 hotlist_file = fopen (hotlist_file_name, "r");
1574 if (hotlist_file == NULL)
1576 int result;
1578 load_group (hotlist);
1579 hotlist_state.loaded = 1;
1581 * just to be sure we got copy
1583 hotlist_state.modified = 1;
1584 result = save_hotlist ();
1585 hotlist_state.modified = 0;
1586 if (result)
1587 remove_old_list = 1;
1588 else
1589 message (D_ERROR, _("Hotlist Load"),
1591 ("MC was unable to write %s file,\nyour old hotlist entries were not deleted"),
1592 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1594 else
1596 hot_load_file (hotlist);
1597 fclose (hotlist_file);
1598 hotlist_state.loaded = 1;
1601 if (remove_old_list)
1603 GError *error = NULL;
1604 clean_up_hotlist_groups ("Hotlist");
1605 if (!mc_config_save_file (mc_main_config, &error))
1606 setup_save_config_show_error (mc_main_config->ini_path, &error);
1609 stat (hotlist_file_name, &stat_buf);
1610 hotlist_file_mtime = stat_buf.st_mtime;
1611 current_group = hotlist;
1614 /* --------------------------------------------------------------------------------------------- */
1616 static void
1617 hot_save_group (struct hotlist *grp)
1619 struct hotlist *current = grp->head;
1620 int i;
1621 char *s;
1623 #define INDENT(n) \
1624 do { \
1625 for (i = 0; i < n; i++) \
1626 putc (' ', hotlist_file); \
1627 } while (0)
1629 for (; current; current = current->next)
1630 switch (current->type)
1632 case HL_TYPE_GROUP:
1633 INDENT (list_level);
1634 fputs ("GROUP \"", hotlist_file);
1635 for (s = current->label; *s; s++)
1637 if (*s == '"' || *s == '\\')
1638 putc ('\\', hotlist_file);
1639 putc (*s, hotlist_file);
1641 fputs ("\"\n", hotlist_file);
1642 list_level += 2;
1643 hot_save_group (current);
1644 list_level -= 2;
1645 INDENT (list_level);
1646 fputs ("ENDGROUP\n", hotlist_file);
1647 break;
1648 case HL_TYPE_ENTRY:
1649 INDENT (list_level);
1650 fputs ("ENTRY \"", hotlist_file);
1651 for (s = current->label; *s; s++)
1653 if (*s == '"' || *s == '\\')
1654 putc ('\\', hotlist_file);
1655 putc (*s, hotlist_file);
1657 fputs ("\" URL \"", hotlist_file);
1658 for (s = current->directory; *s; s++)
1660 if (*s == '"' || *s == '\\')
1661 putc ('\\', hotlist_file);
1662 putc (*s, hotlist_file);
1664 fputs ("\"\n", hotlist_file);
1665 break;
1666 case HL_TYPE_COMMENT:
1667 fprintf (hotlist_file, "#%s\n", current->label);
1668 break;
1669 case HL_TYPE_DOTDOT:
1670 /* do nothing */
1671 break;
1675 /* --------------------------------------------------------------------------------------------- */
1677 static void
1678 add_dotdot_to_list (void)
1680 if (current_group != hotlist)
1682 if (hotlist_has_dot_dot != 0)
1683 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, LISTBOX_APPEND_AT_END);
1687 /* --------------------------------------------------------------------------------------------- */
1688 /*** public functions ****************************************************************************/
1689 /* --------------------------------------------------------------------------------------------- */
1691 void
1692 add2hotlist_cmd (void)
1694 char *lc_prompt;
1695 const char *cp = N_("Label for \"%s\":");
1696 int l;
1697 char *label_string, *label;
1699 #ifdef ENABLE_NLS
1700 cp = _(cp);
1701 #endif
1703 l = str_term_width1 (cp);
1704 label_string = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1705 lc_prompt = g_strdup_printf (cp, str_trunc (label_string, COLS - 2 * UX - (l + 8)));
1706 label = input_dialog (_("Add to hotlist"), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string);
1707 g_free (lc_prompt);
1709 if (!label || !*label)
1711 g_free (label_string);
1712 g_free (label);
1713 return;
1715 add2hotlist (label, label_string, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1716 hotlist_state.modified = 1;
1719 /* --------------------------------------------------------------------------------------------- */
1721 char *
1722 hotlist_show (int vfs_or_hotlist)
1724 char *target = NULL;
1726 hotlist_state.type = vfs_or_hotlist;
1727 load_hotlist ();
1729 init_hotlist (vfs_or_hotlist);
1731 /* display file info */
1732 tty_setcolor (SELECTED_COLOR);
1734 hotlist_state.running = 1;
1735 run_dlg (hotlist_dlg);
1736 hotlist_state.running = 0;
1737 save_hotlist ();
1739 switch (hotlist_dlg->ret_value)
1741 default:
1742 case B_CANCEL:
1743 break;
1745 case B_ENTER:
1747 char *text = NULL;
1748 struct hotlist *hlp = NULL;
1750 listbox_get_current (l_hotlist, &text, (void **) &hlp);
1751 target = g_strdup (hlp != NULL ? hlp->directory : text);
1752 break;
1754 } /* switch */
1756 hotlist_done ();
1757 return target;
1760 /* --------------------------------------------------------------------------------------------- */
1763 save_hotlist (void)
1765 int saved = 0;
1766 struct stat stat_buf;
1768 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name)
1770 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1772 hotlist_file = fopen (hotlist_file_name, "w");
1773 if (hotlist_file != NULL)
1775 hot_save_group (hotlist);
1776 fclose (hotlist_file);
1777 stat (hotlist_file_name, &stat_buf);
1778 hotlist_file_mtime = stat_buf.st_mtime;
1779 saved = 1;
1780 hotlist_state.modified = 0;
1782 else
1783 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1786 return saved;
1789 /* --------------------------------------------------------------------------------------------- */
1791 * Unload list from memory.
1792 * Don't confuse with hotlist_done() for GUI.
1795 void
1796 done_hotlist (void)
1798 if (hotlist)
1800 remove_group (hotlist);
1801 g_free (hotlist->label);
1802 g_free (hotlist->directory);
1803 g_free (hotlist);
1804 hotlist = 0;
1807 hotlist_state.loaded = 0;
1809 g_free (hotlist_file_name);
1810 hotlist_file_name = 0;
1811 l_hotlist = 0;
1812 current_group = 0;
1814 if (tkn_buf)
1816 g_string_free (tkn_buf, TRUE);
1817 tkn_buf = NULL;
1821 /* --------------------------------------------------------------------------------------------- */