1 /*
2 Directory hotlist -- for the Midnight Commander
3
4 Copyright (C) 1994-2021
5 Free Software Foundation, Inc.
6
7 Written by:
8 Radek Doulik, 1994
9 Janne Kukonlehto, 1995
10 Andrej Borsenkow, 1996
11 Norbert Warmuth, 1997
12 Andrew Borodin <aborodin@vmail.ru>, 2012, 2013
13
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.
17
18 This file is part of the Midnight Commander.
19
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.
24
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.
29
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/>.
32 */
33
34 /** \file hotlist.c
35 * \brief Source: directory hotlist
36 */
37
38 #include <config.h>
39
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>
46
47 #include "lib/global.h"
48
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"
58
59 #include "src/setup.h" /* For profile_bname */
60 #include "src/history.h"
61
62 #include "command.h" /* cmdline */
63
64 #include "hotlist.h"
65
66 /*** global variables ****************************************************************************/
67
68 /*** file scope macro definitions ****************************************************************/
69
70 #define UX 3
71 #define UY 2
72
73 #define B_ADD_CURRENT B_USER
74 #define B_REMOVE (B_USER + 1)
75 #define B_NEW_GROUP (B_USER + 2)
76 #define B_NEW_ENTRY (B_USER + 3)
77 #define B_ENTER_GROUP (B_USER + 4)
78 #define B_UP_GROUP (B_USER + 5)
79 #define B_INSERT (B_USER + 6)
80 #define B_APPEND (B_USER + 7)
81 #define B_MOVE (B_USER + 8)
82 #ifdef ENABLE_VFS
83 #define B_FREE_ALL_VFS (B_USER + 9)
84 #define B_REFRESH_VFS (B_USER + 10)
85 #endif
86
87 #define TKN_GROUP 0
88 #define TKN_ENTRY 1
89 #define TKN_STRING 2
90 #define TKN_URL 3
91 #define TKN_ENDGROUP 4
92 #define TKN_COMMENT 5
93 #define TKN_EOL 125
94 #define TKN_EOF 126
95 #define TKN_UNKNOWN 127
96
97 #define SKIP_TO_EOL \
98 { \
99 int _tkn; \
100 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
101 }
102
103 #define CHECK_TOKEN(_TKN_) \
104 tkn = hot_next_token (); \
105 if (tkn != _TKN_) \
106 { \
107 hotlist_state.readonly = TRUE; \
108 hotlist_state.file_error = TRUE; \
109 while (tkn != TKN_EOL && tkn != TKN_EOF) \
110 tkn = hot_next_token (); \
111 break; \
112 }
113
114 /*** file scope type declarations ****************************************************************/
115
116 enum HotListType
117 {
118 HL_TYPE_GROUP,
119 HL_TYPE_ENTRY,
120 HL_TYPE_COMMENT,
121 HL_TYPE_DOTDOT
122 };
123
124 static struct
125 {
126 /*
127 * these reflect run time state
128 */
129
130 gboolean loaded; /* hotlist is loaded */
131 gboolean readonly; /* hotlist readonly */
132 gboolean file_error; /* parse error while reading file */
133 gboolean running; /* we are running dlg (and have to
134 update listbox */
135 gboolean moving; /* we are in moving hotlist currently */
136 gboolean modified; /* hotlist was modified */
137 hotlist_t type; /* LIST_HOTLIST || LIST_VFSLIST */
138 } hotlist_state;
139
140 /* Directory hotlist */
141 struct hotlist
142 {
143 enum HotListType type;
144 char *directory;
145 char *label;
146 struct hotlist *head;
147 struct hotlist *up;
148 struct hotlist *next;
149 };
150
151 /*** file scope variables ************************************************************************/
152
153 static WPanel *our_panel;
154
155 static gboolean hotlist_has_dot_dot = TRUE;
156
157 static WDialog *hotlist_dlg, *movelist_dlg;
158 static WGroupbox *hotlist_group, *movelist_group;
159 static WListbox *l_hotlist, *l_movelist;
160 static WLabel *pname;
161
162 static struct
163 {
164 int ret_cmd, flags, y, x, len;
165 const char *text;
166 int type;
167 widget_pos_flags_t pos_flags;
168 } hotlist_but[] =
169 {
170 /* *INDENT-OFF* */
171 { B_ENTER, DEFPUSH_BUTTON, 0, 0, 0, N_("Change &to"),
172 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
173 #ifdef ENABLE_VFS
174 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, 0, N_("&Free VFSs now"),
175 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
176 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, 0, N_("&Refresh"),
177 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
178 #endif
179 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, 0, N_("&Add current"),
180 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
181 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, 0, N_("&Up"),
182 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
183 { B_CANCEL, NORMAL_BUTTON, 0, 53, 0, N_("&Cancel"),
184 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_RIGHT | WPOS_KEEP_BOTTOM },
185 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, 0, N_("New &group"),
186 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
187 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, 0, N_("New &entry"),
188 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
189 { B_INSERT, NORMAL_BUTTON, 1, 0, 0, N_("&Insert"),
190 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
191 { B_APPEND, NORMAL_BUTTON, 1, 15, 0, N_("A&ppend"),
192 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
193 { B_REMOVE, NORMAL_BUTTON, 1, 30, 0, N_("&Remove"),
194 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
195 { B_MOVE, NORMAL_BUTTON, 1, 42, 0, N_("&Move"),
196 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
197 /* *INDENT-ON* */
198 };
199
200 static const size_t hotlist_but_num = G_N_ELEMENTS (hotlist_but);
201
202 static struct hotlist *hotlist = NULL;
203
204 static struct hotlist *current_group;
205
206 static GString *tkn_buf = NULL;
207
208 static char *hotlist_file_name;
209 static FILE *hotlist_file;
210 static time_t hotlist_file_mtime;
211
212 static int list_level = 0;
213
214 /* --------------------------------------------------------------------------------------------- */
215 /*** file scope functions ************************************************************************/
216 /* --------------------------------------------------------------------------------------------- */
217
218 static void init_movelist (struct hotlist *item);
219 static void add_new_group_cmd (void);
220 static void add_new_entry_cmd (WPanel * panel);
221 static void remove_from_hotlist (struct hotlist *entry);
222 static void load_hotlist (void);
223 static void add_dotdot_to_list (void);
224
225 /* --------------------------------------------------------------------------------------------- */
226 /** If current->data is 0, then we are dealing with a VFS pathname */
227
228 static void
update_path_name(void)229 update_path_name (void)
230 {
231 const char *text = "";
232 char *p;
233 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
234 Widget *w = WIDGET (list);
235
236 if (!listbox_is_empty (list))
237 {
238 char *ctext = NULL;
239 void *cdata = NULL;
240
241 listbox_get_current (list, &ctext, &cdata);
242 if (cdata == NULL)
243 text = ctext;
244 else
245 {
246 struct hotlist *hlp = (struct hotlist *) cdata;
247
248 if (hlp->type == HL_TYPE_ENTRY || hlp->type == HL_TYPE_DOTDOT)
249 text = hlp->directory;
250 else if (hlp->type == HL_TYPE_GROUP)
251 text = _("Subgroup - press ENTER to see list");
252 }
253 }
254
255 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
256 if (hotlist_state.moving)
257 groupbox_set_title (movelist_group, str_trunc (p, w->cols - 2));
258 else
259 {
260 groupbox_set_title (hotlist_group, str_trunc (p, w->cols - 2));
261 label_set_text (pname, str_trunc (text, w->cols));
262 }
263 g_free (p);
264 }
265
266 /* --------------------------------------------------------------------------------------------- */
267
268 static void
fill_listbox(WListbox * list)269 fill_listbox (WListbox * list)
270 {
271 struct hotlist *current;
272 GString *buff;
273
274 buff = g_string_new ("");
275
276 for (current = current_group->head; current != NULL; current = current->next)
277 switch (current->type)
278 {
279 case HL_TYPE_GROUP:
280 {
281 /* buff clean up */
282 g_string_truncate (buff, 0);
283 g_string_append (buff, "->");
284 g_string_append (buff, current->label);
285 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, buff->str, current, FALSE);
286 }
287 break;
288 case HL_TYPE_DOTDOT:
289 case HL_TYPE_ENTRY:
290 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, current->label, current, FALSE);
291 break;
292 default:
293 break;
294 }
295
296 g_string_free (buff, TRUE);
297 }
298
299 /* --------------------------------------------------------------------------------------------- */
300
301 static void
unlink_entry(struct hotlist * entry)302 unlink_entry (struct hotlist *entry)
303 {
304 struct hotlist *current = current_group->head;
305
306 if (current == entry)
307 current_group->head = entry->next;
308 else
309 {
310 while (current != NULL && current->next != entry)
311 current = current->next;
312 if (current != NULL)
313 current->next = entry->next;
314 }
315 entry->next = entry->up = NULL;
316 }
317
318 /* --------------------------------------------------------------------------------------------- */
319
320 #ifdef ENABLE_VFS
321 static void
add_name_to_list(const char * path)322 add_name_to_list (const char *path)
323 {
324 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, path, NULL, FALSE);
325 }
326 #endif /* !ENABLE_VFS */
327
328 /* --------------------------------------------------------------------------------------------- */
329
330 static int
hotlist_run_cmd(int action)331 hotlist_run_cmd (int action)
332 {
333 switch (action)
334 {
335 case B_MOVE:
336 {
337 struct hotlist *saved = current_group;
338 struct hotlist *item = NULL;
339 struct hotlist *moveto_item = NULL;
340 struct hotlist *moveto_group = NULL;
341 int ret;
342
343 if (listbox_is_empty (l_hotlist))
344 return 0; /* empty group - nothing to do */
345
346 listbox_get_current (l_hotlist, NULL, (void **) &item);
347 init_movelist (item);
348 hotlist_state.moving = TRUE;
349 ret = dlg_run (movelist_dlg);
350 hotlist_state.moving = FALSE;
351 listbox_get_current (l_movelist, NULL, (void **) &moveto_item);
352 moveto_group = current_group;
353 widget_destroy (WIDGET (movelist_dlg));
354 current_group = saved;
355 if (ret == B_CANCEL)
356 return 0;
357 if (moveto_item == item)
358 return 0; /* If we insert/append a before/after a
359 it hardly changes anything ;) */
360 unlink_entry (item);
361 listbox_remove_current (l_hotlist);
362 item->up = moveto_group;
363 if (moveto_group->head == NULL)
364 moveto_group->head = item;
365 else if (moveto_item == NULL)
366 { /* we have group with just comments */
367 struct hotlist *p = moveto_group->head;
368
369 /* skip comments */
370 while (p->next != NULL)
371 p = p->next;
372 p->next = item;
373 }
374 else if (ret == B_ENTER || ret == B_APPEND)
375 {
376 if (moveto_item->next == NULL)
377 moveto_item->next = item;
378 else
379 {
380 item->next = moveto_item->next;
381 moveto_item->next = item;
382 }
383 }
384 else if (moveto_group->head == moveto_item)
385 {
386 moveto_group->head = item;
387 item->next = moveto_item;
388 }
389 else
390 {
391 struct hotlist *p = moveto_group->head;
392
393 while (p->next != moveto_item)
394 p = p->next;
395 item->next = p->next;
396 p->next = item;
397 }
398 listbox_remove_list (l_hotlist);
399 fill_listbox (l_hotlist);
400 repaint_screen ();
401 hotlist_state.modified = TRUE;
402 return 0;
403 }
404 case B_REMOVE:
405 {
406 struct hotlist *entry = NULL;
407
408 listbox_get_current (l_hotlist, NULL, (void **) &entry);
409 remove_from_hotlist (entry);
410 }
411 return 0;
412
413 case B_NEW_GROUP:
414 add_new_group_cmd ();
415 return 0;
416
417 case B_ADD_CURRENT:
418 add2hotlist_cmd (our_panel);
419 return 0;
420
421 case B_NEW_ENTRY:
422 add_new_entry_cmd (our_panel);
423 return 0;
424
425 case B_ENTER:
426 case B_ENTER_GROUP:
427 {
428 WListbox *list;
429 void *data;
430 struct hotlist *hlp;
431
432 list = hotlist_state.moving ? l_movelist : l_hotlist;
433 listbox_get_current (list, NULL, &data);
434
435 if (data == NULL)
436 return 1;
437
438 hlp = (struct hotlist *) data;
439
440 if (hlp->type == HL_TYPE_ENTRY)
441 return (action == B_ENTER ? 1 : 0);
442 if (hlp->type != HL_TYPE_DOTDOT)
443 {
444 listbox_remove_list (list);
445 current_group = hlp;
446 fill_listbox (list);
447 return 0;
448 }
449 }
450 MC_FALLTHROUGH; /* if list empty - just go up */
451
452 case B_UP_GROUP:
453 {
454 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
455
456 listbox_remove_list (list);
457 current_group = current_group->up;
458 fill_listbox (list);
459 return 0;
460 }
461
462 #ifdef ENABLE_VFS
463 case B_FREE_ALL_VFS:
464 vfs_expire (TRUE);
465 MC_FALLTHROUGH;
466
467 case B_REFRESH_VFS:
468 listbox_remove_list (l_hotlist);
469 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
470 FALSE);
471 vfs_fill_names (add_name_to_list);
472 return 0;
473 #endif /* ENABLE_VFS */
474
475 default:
476 return 1;
477 }
478 }
479
480 /* --------------------------------------------------------------------------------------------- */
481
482 static int
hotlist_button_callback(WButton * button,int action)483 hotlist_button_callback (WButton * button, int action)
484 {
485 int ret;
486
487 (void) button;
488 ret = hotlist_run_cmd (action);
489 update_path_name ();
490 return ret;
491 }
492
493 /* --------------------------------------------------------------------------------------------- */
494
495 static inline cb_ret_t
hotlist_handle_key(WDialog * h,int key)496 hotlist_handle_key (WDialog * h, int key)
497 {
498 switch (key)
499 {
500 case KEY_M_CTRL | '\n':
501 goto l1;
502
503 case '\n':
504 case KEY_ENTER:
505 if (hotlist_button_callback (NULL, B_ENTER) != 0)
506 {
507 h->ret_value = B_ENTER;
508 dlg_stop (h);
509 }
510 return MSG_HANDLED;
511
512 case KEY_RIGHT:
513 /* enter to the group */
514 if (hotlist_state.type == LIST_VFSLIST)
515 return MSG_NOT_HANDLED;
516 return hotlist_button_callback (NULL, B_ENTER_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
517
518 case KEY_LEFT:
519 /* leave the group */
520 if (hotlist_state.type == LIST_VFSLIST)
521 return MSG_NOT_HANDLED;
522 return hotlist_button_callback (NULL, B_UP_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
523
524 case KEY_DC:
525 if (hotlist_state.moving)
526 return MSG_NOT_HANDLED;
527 hotlist_button_callback (NULL, B_REMOVE);
528 return MSG_HANDLED;
529
530 l1:
531 case ALT ('\n'):
532 case ALT ('\r'):
533 if (!hotlist_state.moving)
534 {
535 void *ldata = NULL;
536
537 listbox_get_current (l_hotlist, NULL, &ldata);
538
539 if (ldata != NULL)
540 {
541 struct hotlist *hlp = (struct hotlist *) ldata;
542
543 if (hlp->type == HL_TYPE_ENTRY)
544 {
545 char *tmp;
546
547 tmp = g_strconcat ("cd ", hlp->directory, (char *) NULL);
548 input_insert (cmdline, tmp, FALSE);
549 g_free (tmp);
550 h->ret_value = B_CANCEL;
551 dlg_stop (h);
552 }
553 }
554 }
555 return MSG_HANDLED; /* ignore key */
556
557 default:
558 return MSG_NOT_HANDLED;
559 }
560 }
561
562 /* --------------------------------------------------------------------------------------------- */
563
564 static cb_ret_t
hotlist_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)565 hotlist_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
566 {
567 WDialog *h = DIALOG (w);
568
569 switch (msg)
570 {
571 case MSG_INIT:
572 case MSG_NOTIFY: /* MSG_NOTIFY is fired by the listbox to tell us the item has changed. */
573 update_path_name ();
574 return MSG_HANDLED;
575
576 case MSG_UNHANDLED_KEY:
577 return hotlist_handle_key (h, parm);
578
579 case MSG_POST_KEY:
580 /*
581 * The code here has two purposes:
582 *
583 * (1) Always stay on the hotlist.
584 *
585 * Activating a button using its hotkey (and even pressing ENTER, as
586 * there's a "default button") moves the focus to the button. But we
587 * want to stay on the hotlist, to be able to use the usual keys (up,
588 * down, etc.). So we do `widget_select (lst)`.
589 *
590 * (2) Refresh the hotlist.
591 *
592 * We may have run a command that changed the contents of the list.
593 * We therefore need to refresh it. So we do `widget_draw (lst)`.
594 */
595 {
596 Widget *lst;
597
598 lst = WIDGET (h == hotlist_dlg ? l_hotlist : l_movelist);
599
600 /* widget_select() already redraws the widget, but since it's a
601 * no-op if the widget is already selected ("focused"), we have
602 * to call widget_draw() separately. */
603 if (!widget_get_state (lst, WST_FOCUSED))
604 widget_select (lst);
605 else
606 widget_draw (lst);
607 }
608 return MSG_HANDLED;
609
610 case MSG_RESIZE:
611 {
612 WRect r;
613
614 rect_init (&r, w->y, w->x, LINES - (h == hotlist_dlg ? 2 : 6), COLS - 6);
615
616 return dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
617 }
618
619 default:
620 return dlg_default_callback (w, sender, msg, parm, data);
621 }
622 }
623
624 /* --------------------------------------------------------------------------------------------- */
625
626 static lcback_ret_t
hotlist_listbox_callback(WListbox * list)627 hotlist_listbox_callback (WListbox * list)
628 {
629 WDialog *dlg = DIALOG (WIDGET (list)->owner);
630
631 if (!listbox_is_empty (list))
632 {
633 void *data = NULL;
634
635 listbox_get_current (list, NULL, &data);
636
637 if (data != NULL)
638 {
639 struct hotlist *hlp = (struct hotlist *) data;
640
641 if (hlp->type == HL_TYPE_ENTRY)
642 {
643 dlg->ret_value = B_ENTER;
644 dlg_stop (dlg);
645 return LISTBOX_DONE;
646 }
647 else
648 {
649 hotlist_button_callback (NULL, B_ENTER);
650 send_message (dlg, NULL, MSG_POST_KEY, '\n', NULL);
651 return LISTBOX_CONT;
652 }
653 }
654 else
655 {
656 dlg->ret_value = B_ENTER;
657 dlg_stop (dlg);
658 return LISTBOX_DONE;
659 }
660 }
661
662 hotlist_button_callback (NULL, B_UP_GROUP);
663 send_message (dlg, NULL, MSG_POST_KEY, 'u', NULL);
664 return LISTBOX_CONT;
665 }
666
667 /* --------------------------------------------------------------------------------------------- */
668 /**
669 * Expands all button names (once) and recalculates button positions.
670 * returns number of columns in the dialog box, which is 10 chars longer
671 * then buttonbar.
672 *
673 * If common width of the window (i.e. in xterm) is less than returned
674 * width - sorry :) (anyway this did not handled in previous version too)
675 */
676
677 static int
init_i18n_stuff(int list_type,int cols)678 init_i18n_stuff (int list_type, int cols)
679 {
680 size_t i;
681
682 static gboolean i18n_flag = FALSE;
683
684 if (!i18n_flag)
685 {
686 for (i = 0; i < hotlist_but_num; i++)
687 {
688 #ifdef ENABLE_NLS
689 hotlist_but[i].text = _(hotlist_but[i].text);
690 #endif /* ENABLE_NLS */
691 hotlist_but[i].len = str_term_width1 (hotlist_but[i].text) + 3;
692 if (hotlist_but[i].flags == DEFPUSH_BUTTON)
693 hotlist_but[i].len += 2;
694 }
695
696 i18n_flag = TRUE;
697 }
698
699 /* Dynamic resizing of buttonbars */
700 {
701 int len[2], count[2]; /* at most two lines of buttons */
702 int cur_x[2];
703
704 len[0] = len[1] = 0;
705 count[0] = count[1] = 0;
706 cur_x[0] = cur_x[1] = 0;
707
708 /* Count len of buttonbars, assuming 1 extra space between buttons */
709 for (i = 0; i < hotlist_but_num; i++)
710 if ((hotlist_but[i].type & list_type) != 0)
711 {
712 int row;
713
714 row = hotlist_but[i].y;
715 ++count[row];
716 len[row] += hotlist_but[i].len + 1;
717 }
718
719 (len[0])--;
720 (len[1])--;
721
722 cols = MAX (cols, MAX (len[0], len[1]));
723
724 /* arrange buttons */
725 for (i = 0; i < hotlist_but_num; i++)
726 if ((hotlist_but[i].type & list_type) != 0)
727 {
728 int row;
729
730 row = hotlist_but[i].y;
731
732 if (hotlist_but[i].x != 0)
733 {
734 /* not first int the row */
735 if (hotlist_but[i].ret_cmd == B_CANCEL)
736 hotlist_but[i].x = cols - hotlist_but[i].len - 6;
737 else
738 hotlist_but[i].x = cur_x[row];
739 }
740
741 cur_x[row] += hotlist_but[i].len + 1;
742 }
743 }
744
745 return cols;
746 }
747
748 /* --------------------------------------------------------------------------------------------- */
749
750 static void
init_hotlist(hotlist_t list_type)751 init_hotlist (hotlist_t list_type)
752 {
753 size_t i;
754 const char *title, *help_node;
755 int lines, cols;
756 int y;
757 int dh = 0;
758 WGroup *g;
759 WGroupbox *path_box;
760 Widget *hotlist_widget;
761
762 do_refresh ();
763
764 lines = LINES - 2;
765 cols = init_i18n_stuff (list_type, COLS - 6);
766
767 #ifdef ENABLE_VFS
768 if (list_type == LIST_VFSLIST)
769 {
770 title = _("Active VFS directories");
771 help_node = "[vfshot]"; /* FIXME - no such node */
772 dh = 1;
773 }
774 else
775 #endif /* !ENABLE_VFS */
776 {
777 title = _("Directory hotlist");
778 help_node = "[Hotlist]";
779 }
780
781 hotlist_dlg =
782 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
783 NULL, help_node, title);
784 g = GROUP (hotlist_dlg);
785
786 y = UY;
787 hotlist_group = groupbox_new (y, UX, lines - 10 + dh, cols - 2 * UX, _("Top level group"));
788 hotlist_widget = WIDGET (hotlist_group);
789 group_add_widget_autopos (g, hotlist_widget, WPOS_KEEP_ALL, NULL);
790
791 l_hotlist =
792 listbox_new (y + 1, UX + 1, hotlist_widget->lines - 2, hotlist_widget->cols - 2, FALSE,
793 hotlist_listbox_callback);
794
795 /* Fill the hotlist with the active VFS or the hotlist */
796 #ifdef ENABLE_VFS
797 if (list_type == LIST_VFSLIST)
798 {
799 listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
800 FALSE);
801 vfs_fill_names (add_name_to_list);
802 }
803 else
804 #endif /* !ENABLE_VFS */
805 fill_listbox (l_hotlist);
806
807 /* insert before groupbox to view scrollbar */
808 group_add_widget_autopos (g, l_hotlist, WPOS_KEEP_ALL, NULL);
809
810 y += hotlist_widget->lines;
811
812 path_box = groupbox_new (y, UX, 3, hotlist_widget->cols, _("Directory path"));
813 group_add_widget_autopos (g, path_box, WPOS_KEEP_BOTTOM | WPOS_KEEP_HORZ, NULL);
814
815 pname = label_new (y + 1, UX + 2, "");
816 group_add_widget_autopos (g, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
817 y += WIDGET (path_box)->lines;
818
819 group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
820
821 for (i = 0; i < hotlist_but_num; i++)
822 if ((hotlist_but[i].type & list_type) != 0)
823 group_add_widget_autopos (g,
824 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
825 hotlist_but[i].ret_cmd, hotlist_but[i].flags,
826 hotlist_but[i].text, hotlist_button_callback),
827 hotlist_but[i].pos_flags, NULL);
828
829 widget_select (WIDGET (l_hotlist));
830 }
831
832 /* --------------------------------------------------------------------------------------------- */
833
834 static void
init_movelist(struct hotlist * item)835 init_movelist (struct hotlist *item)
836 {
837 size_t i;
838 char *hdr;
839 int lines, cols;
840 int y;
841 WGroup *g;
842 Widget *movelist_widget;
843
844 do_refresh ();
845
846 lines = LINES - 6;
847 cols = init_i18n_stuff (LIST_MOVELIST, COLS - 6);
848
849 hdr = g_strdup_printf (_("Moving %s"), item->label);
850
851 movelist_dlg =
852 dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
853 NULL, "[Hotlist]", hdr);
854 g = GROUP (movelist_dlg);
855
856 g_free (hdr);
857
858 y = UY;
859 movelist_group = groupbox_new (y, UX, lines - 7, cols - 2 * UX, _("Directory label"));
860 movelist_widget = WIDGET (movelist_group);
861 group_add_widget_autopos (g, movelist_widget, WPOS_KEEP_ALL, NULL);
862
863 l_movelist =
864 listbox_new (y + 1, UX + 1, movelist_widget->lines - 2, movelist_widget->cols - 2, FALSE,
865 hotlist_listbox_callback);
866 fill_listbox (l_movelist);
867 /* insert before groupbox to view scrollbar */
868 group_add_widget_autopos (g, l_movelist, WPOS_KEEP_ALL, NULL);
869
870 y += movelist_widget->lines;
871
872 group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
873
874 for (i = 0; i < hotlist_but_num; i++)
875 if ((hotlist_but[i].type & LIST_MOVELIST) != 0)
876 group_add_widget_autopos (g,
877 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
878 hotlist_but[i].ret_cmd, hotlist_but[i].flags,
879 hotlist_but[i].text, hotlist_button_callback),
880 hotlist_but[i].pos_flags, NULL);
881
882 widget_select (WIDGET (l_movelist));
883 }
884
885 /* --------------------------------------------------------------------------------------------- */
886 /**
887 * Destroy the list dialog.
888 * Don't confuse with done_hotlist() for the list in memory.
889 */
890
891 static void
hotlist_done(void)892 hotlist_done (void)
893 {
894 widget_destroy (WIDGET (hotlist_dlg));
895 l_hotlist = NULL;
896 #if 0
897 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
898 #endif
899 repaint_screen ();
900 }
901
902 /* --------------------------------------------------------------------------------------------- */
903
904 static inline char *
find_group_section(struct hotlist * grp)905 find_group_section (struct hotlist *grp)
906 {
907 return g_strconcat (grp->directory, ".Group", (char *) NULL);
908 }
909
910 /* --------------------------------------------------------------------------------------------- */
911
912 static struct hotlist *
add2hotlist(char * label,char * directory,enum HotListType type,listbox_append_t pos)913 add2hotlist (char *label, char *directory, enum HotListType type, listbox_append_t pos)
914 {
915 struct hotlist *new;
916 struct hotlist *current = NULL;
917
918 /*
919 * Hotlist is neither loaded nor loading.
920 * Must be called by "Ctrl-x a" before using hotlist.
921 */
922 if (current_group == NULL)
923 load_hotlist ();
924
925 listbox_get_current (l_hotlist, NULL, (void **) ¤t);
926
927 /* Make sure '..' stays at the top of the list. */
928 if ((current != NULL) && (current->type == HL_TYPE_DOTDOT))
929 pos = LISTBOX_APPEND_AFTER;
930
931 new = g_new0 (struct hotlist, 1);
932
933 new->type = type;
934 new->label = label;
935 new->directory = directory;
936 new->up = current_group;
937
938 if (type == HL_TYPE_GROUP)
939 {
940 current_group = new;
941 add_dotdot_to_list ();
942 current_group = new->up;
943 }
944
945 if (current_group->head == NULL)
946 {
947 /* first element in group */
948 current_group->head = new;
949 }
950 else if (pos == LISTBOX_APPEND_AFTER)
951 {
952 new->next = current->next;
953 current->next = new;
954 }
955 else if (pos == LISTBOX_APPEND_BEFORE && current == current_group->head)
956 {
957 /* should be inserted before first item */
958 new->next = current;
959 current_group->head = new;
960 }
961 else if (pos == LISTBOX_APPEND_BEFORE)
962 {
963 struct hotlist *p = current_group->head;
964
965 while (p->next != current)
966 p = p->next;
967
968 new->next = current;
969 p->next = new;
970 }
971 else
972 { /* append at the end */
973 struct hotlist *p = current_group->head;
974
975 while (p->next != NULL)
976 p = p->next;
977
978 p->next = new;
979 }
980
981 if (hotlist_state.running && type != HL_TYPE_COMMENT && type != HL_TYPE_DOTDOT)
982 {
983 if (type == HL_TYPE_GROUP)
984 {
985 char *lbl;
986
987 lbl = g_strconcat ("->", new->label, (char *) NULL);
988 listbox_add_item (l_hotlist, pos, 0, lbl, new, FALSE);
989 g_free (lbl);
990 }
991 else
992 listbox_add_item (l_hotlist, pos, 0, new->label, new, FALSE);
993 listbox_select_entry (l_hotlist, l_hotlist->pos);
994 }
995
996 return new;
997 }
998
999 /* --------------------------------------------------------------------------------------------- */
1000
1001 static int
add_new_entry_input(const char * header,const char * text1,const char * text2,const char * help,char ** r1,char ** r2)1002 add_new_entry_input (const char *header, const char *text1, const char *text2,
1003 const char *help, char **r1, char **r2)
1004 {
1005 quick_widget_t quick_widgets[] = {
1006 /* *INDENT-OFF* */
1007 QUICK_LABELED_INPUT (text1, input_label_above, *r1, "input-lbl", r1, NULL,
1008 FALSE, FALSE, INPUT_COMPLETE_NONE),
1009 QUICK_SEPARATOR (FALSE),
1010 QUICK_LABELED_INPUT (text2, input_label_above, *r2, "input-lbl", r2, NULL,
1011 FALSE, FALSE, INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD),
1012 QUICK_START_BUTTONS (TRUE, TRUE),
1013 QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1014 QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1015 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1016 QUICK_END
1017 /* *INDENT-ON* */
1018 };
1019
1020 quick_dialog_t qdlg = {
1021 -1, -1, 64,
1022 header, help,
1023 quick_widgets, NULL, NULL
1024 };
1025
1026 int ret;
1027
1028 ret = quick_dialog (&qdlg);
1029
1030 return (ret != B_CANCEL) ? ret : 0;
1031 }
1032
1033 /* --------------------------------------------------------------------------------------------- */
1034
1035 static void
add_new_entry_cmd(WPanel * panel)1036 add_new_entry_cmd (WPanel * panel)
1037 {
1038 char *title, *url, *to_free;
1039 int ret;
1040
1041 /* Take current directory as default value for input fields */
1042 to_free = title = url = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1043
1044 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label:"),
1045 _("Directory path:"), "[Hotlist]", &title, &url);
1046 g_free (to_free);
1047
1048 if (ret == 0)
1049 return;
1050 if (title == NULL || *title == '\0' || url == NULL || *url == '\0')
1051 {
1052 g_free (title);
1053 g_free (url);
1054 return;
1055 }
1056
1057 if (ret == B_ENTER || ret == B_APPEND)
1058 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AFTER);
1059 else
1060 add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_BEFORE);
1061
1062 hotlist_state.modified = TRUE;
1063 }
1064
1065 /* --------------------------------------------------------------------------------------------- */
1066
1067 static int
add_new_group_input(const char * header,const char * label,char ** result)1068 add_new_group_input (const char *header, const char *label, char **result)
1069 {
1070 quick_widget_t quick_widgets[] = {
1071 /* *INDENT-OFF* */
1072 QUICK_LABELED_INPUT (label, input_label_above, "", "input", result, NULL,
1073 FALSE, FALSE, INPUT_COMPLETE_NONE),
1074 QUICK_START_BUTTONS (TRUE, TRUE),
1075 QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1076 QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1077 QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1078 QUICK_END
1079 /* *INDENT-ON* */
1080 };
1081
1082 quick_dialog_t qdlg = {
1083 -1, -1, 64,
1084 header, "[Hotlist]",
1085 quick_widgets, NULL, NULL
1086 };
1087
1088 int ret;
1089
1090 ret = quick_dialog (&qdlg);
1091
1092 return (ret != B_CANCEL) ? ret : 0;
1093 }
1094
1095 /* --------------------------------------------------------------------------------------------- */
1096
1097 static void
add_new_group_cmd(void)1098 add_new_group_cmd (void)
1099 {
1100 char *label;
1101 int ret;
1102
1103 ret = add_new_group_input (_("New hotlist group"), _("Name of new group:"), &label);
1104 if (ret == 0 || label == NULL || *label == '\0')
1105 return;
1106
1107 if (ret == B_ENTER || ret == B_APPEND)
1108 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_AFTER);
1109 else
1110 add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_BEFORE);
1111
1112 hotlist_state.modified = TRUE;
1113 }
1114
1115 /* --------------------------------------------------------------------------------------------- */
1116
1117 static void
remove_group(struct hotlist * grp)1118 remove_group (struct hotlist *grp)
1119 {
1120 struct hotlist *current = grp->head;
1121
1122 while (current != NULL)
1123 {
1124 struct hotlist *next = current->next;
1125
1126 if (current->type == HL_TYPE_GROUP)
1127 remove_group (current);
1128
1129 g_free (current->label);
1130 g_free (current->directory);
1131 g_free (current);
1132
1133 current = next;
1134 }
1135 }
1136
1137 /* --------------------------------------------------------------------------------------------- */
1138
1139 static void
remove_from_hotlist(struct hotlist * entry)1140 remove_from_hotlist (struct hotlist *entry)
1141 {
1142 if (entry == NULL)
1143 return;
1144
1145 if (entry->type == HL_TYPE_DOTDOT)
1146 return;
1147
1148 if (confirm_directory_hotlist_delete)
1149 {
1150 char text[BUF_MEDIUM];
1151 int result;
1152
1153 if (safe_delete)
1154 query_set_sel (1);
1155
1156 g_snprintf (text, sizeof (text), _("Are you sure you want to remove entry \"%s\"?"),
1157 str_trunc (entry->label, 30));
1158 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1159 _("&Yes"), _("&No"));
1160 if (result != 0)
1161 return;
1162 }
1163
1164 if (entry->type == HL_TYPE_GROUP)
1165 {
1166 struct hotlist *head = entry->head;
1167
1168 if (head != NULL && (head->type != HL_TYPE_DOTDOT || head->next != NULL))
1169 {
1170 char text[BUF_MEDIUM];
1171 int result;
1172
1173 g_snprintf (text, sizeof (text), _("Group \"%s\" is not empty.\nRemove it?"),
1174 str_trunc (entry->label, 30));
1175 result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1176 _("&Yes"), _("&No"));
1177 if (result != 0)
1178 return;
1179 }
1180
1181 remove_group (entry);
1182 }
1183
1184 unlink_entry (entry);
1185
1186 g_free (entry->label);
1187 g_free (entry->directory);
1188 g_free (entry);
1189 /* now remove list entry from screen */
1190 listbox_remove_current (l_hotlist);
1191 hotlist_state.modified = TRUE;
1192 }
1193
1194 /* --------------------------------------------------------------------------------------------- */
1195
1196 static void
load_group(struct hotlist * grp)1197 load_group (struct hotlist *grp)
1198 {
1199 gchar **profile_keys, **keys;
1200 char *group_section;
1201 struct hotlist *current = 0;
1202
1203 group_section = find_group_section (grp);
1204
1205 keys = mc_config_get_keys (mc_global.main_config, group_section, NULL);
1206
1207 current_group = grp;
1208
1209 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1210 add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1211 g_strdup (*profile_keys), HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1212
1213 g_strfreev (keys);
1214
1215 keys = mc_config_get_keys (mc_global.main_config, grp->directory, NULL);
1216
1217 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1218 add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1219 g_strdup (*profile_keys), HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1220
1221 g_free (group_section);
1222 g_strfreev (keys);
1223
1224 for (current = grp->head; current; current = current->next)
1225 load_group (current);
1226 }
1227
1228 /* --------------------------------------------------------------------------------------------- */
1229
1230 static int
hot_skip_blanks(void)1231 hot_skip_blanks (void)
1232 {
1233 int c;
1234
1235 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1236 ;
1237 return c;
1238 }
1239
1240 /* --------------------------------------------------------------------------------------------- */
1241
1242 static int
hot_next_token(void)1243 hot_next_token (void)
1244 {
1245 int c, ret = 0;
1246 size_t l;
1247
1248 if (tkn_buf == NULL)
1249 tkn_buf = g_string_new ("");
1250 g_string_set_size (tkn_buf, 0);
1251
1252 again:
1253 c = hot_skip_blanks ();
1254 switch (c)
1255 {
1256 case EOF:
1257 ret = TKN_EOF;
1258 break;
1259 case '\n':
1260 ret = TKN_EOL;
1261 break;
1262 case '#':
1263 while ((c = getc (hotlist_file)) != EOF && c != '\n')
1264 g_string_append_c (tkn_buf, c);
1265 ret = TKN_COMMENT;
1266 break;
1267 case '"':
1268 while ((c = getc (hotlist_file)) != EOF && c != '"')
1269 {
1270 if (c == '\\')
1271 {
1272 c = getc (hotlist_file);
1273 if (c == EOF)
1274 {
1275 g_string_free (tkn_buf, TRUE);
1276 return TKN_EOF;
1277 }
1278 }
1279 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1280 }
1281 ret = (c == EOF) ? TKN_EOF : TKN_STRING;
1282 break;
1283 case '\\':
1284 c = getc (hotlist_file);
1285 if (c == EOF)
1286 {
1287 g_string_free (tkn_buf, TRUE);
1288 return TKN_EOF;
1289 }
1290 if (c == '\n')
1291 goto again;
1292
1293 MC_FALLTHROUGH; /* it is taken as normal character */
1294
1295 default:
1296 do
1297 {
1298 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1299 }
1300 while ((c = fgetc (hotlist_file)) != EOF && (g_ascii_isalnum (c) || !isascii (c)));
1301 if (c != EOF)
1302 ungetc (c, hotlist_file);
1303 l = tkn_buf->len;
1304 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1305 ret = TKN_GROUP;
1306 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1307 ret = TKN_ENTRY;
1308 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1309 ret = TKN_ENDGROUP;
1310 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1311 ret = TKN_URL;
1312 else
1313 ret = TKN_UNKNOWN;
1314 break;
1315 }
1316 return ret;
1317 }
1318
1319 /* --------------------------------------------------------------------------------------------- */
1320
1321 static void
hot_load_group(struct hotlist * grp)1322 hot_load_group (struct hotlist *grp)
1323 {
1324 int tkn;
1325 struct hotlist *new_grp;
1326 char *label, *url;
1327
1328 current_group = grp;
1329
1330 while ((tkn = hot_next_token ()) != TKN_ENDGROUP)
1331 switch (tkn)
1332 {
1333 case TKN_GROUP:
1334 CHECK_TOKEN (TKN_STRING);
1335 new_grp =
1336 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1337 LISTBOX_APPEND_AT_END);
1338 SKIP_TO_EOL;
1339 hot_load_group (new_grp);
1340 current_group = grp;
1341 break;
1342 case TKN_ENTRY:
1343 {
1344 CHECK_TOKEN (TKN_STRING);
1345 label = g_strndup (tkn_buf->str, tkn_buf->len);
1346 CHECK_TOKEN (TKN_URL);
1347 CHECK_TOKEN (TKN_STRING);
1348 url = tilde_expand (tkn_buf->str);
1349 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1350 SKIP_TO_EOL;
1351 }
1352 break;
1353 case TKN_COMMENT:
1354 label = g_strndup (tkn_buf->str, tkn_buf->len);
1355 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1356 break;
1357 case TKN_EOF:
1358 hotlist_state.readonly = TRUE;
1359 hotlist_state.file_error = TRUE;
1360 return;
1361 case TKN_EOL:
1362 /* skip empty lines */
1363 break;
1364 default:
1365 hotlist_state.readonly = TRUE;
1366 hotlist_state.file_error = TRUE;
1367 SKIP_TO_EOL;
1368 break;
1369 }
1370 SKIP_TO_EOL;
1371 }
1372
1373 /* --------------------------------------------------------------------------------------------- */
1374
1375 static void
hot_load_file(struct hotlist * grp)1376 hot_load_file (struct hotlist *grp)
1377 {
1378 int tkn;
1379 struct hotlist *new_grp;
1380 char *label, *url;
1381
1382 current_group = grp;
1383
1384 while ((tkn = hot_next_token ()) != TKN_EOF)
1385 switch (tkn)
1386 {
1387 case TKN_GROUP:
1388 CHECK_TOKEN (TKN_STRING);
1389 new_grp =
1390 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1391 LISTBOX_APPEND_AT_END);
1392 SKIP_TO_EOL;
1393 hot_load_group (new_grp);
1394 current_group = grp;
1395 break;
1396 case TKN_ENTRY:
1397 {
1398 CHECK_TOKEN (TKN_STRING);
1399 label = g_strndup (tkn_buf->str, tkn_buf->len);
1400 CHECK_TOKEN (TKN_URL);
1401 CHECK_TOKEN (TKN_STRING);
1402 url = tilde_expand (tkn_buf->str);
1403 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1404 SKIP_TO_EOL;
1405 }
1406 break;
1407 case TKN_COMMENT:
1408 label = g_strndup (tkn_buf->str, tkn_buf->len);
1409 add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1410 break;
1411 case TKN_EOL:
1412 /* skip empty lines */
1413 break;
1414 default:
1415 hotlist_state.readonly = TRUE;
1416 hotlist_state.file_error = TRUE;
1417 SKIP_TO_EOL;
1418 break;
1419 }
1420 }
1421
1422 /* --------------------------------------------------------------------------------------------- */
1423
1424 static void
clean_up_hotlist_groups(const char * section)1425 clean_up_hotlist_groups (const char *section)
1426 {
1427 char *grp_section;
1428
1429 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1430 if (mc_config_has_group (mc_global.main_config, section))
1431 mc_config_del_group (mc_global.main_config, section);
1432
1433 if (mc_config_has_group (mc_global.main_config, grp_section))
1434 {
1435 char **profile_keys, **keys;
1436
1437 keys = mc_config_get_keys (mc_global.main_config, grp_section, NULL);
1438
1439 for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1440 clean_up_hotlist_groups (*profile_keys);
1441
1442 g_strfreev (keys);
1443 mc_config_del_group (mc_global.main_config, grp_section);
1444 }
1445 g_free (grp_section);
1446 }
1447
1448 /* --------------------------------------------------------------------------------------------- */
1449
1450 static void
load_hotlist(void)1451 load_hotlist (void)
1452 {
1453 gboolean remove_old_list = FALSE;
1454 struct stat stat_buf;
1455
1456 if (hotlist_state.loaded)
1457 {
1458 stat (hotlist_file_name, &stat_buf);
1459 if (hotlist_file_mtime < stat_buf.st_mtime)
1460 done_hotlist ();
1461 else
1462 return;
1463 }
1464
1465 if (hotlist_file_name == NULL)
1466 hotlist_file_name = mc_config_get_full_path (MC_HOTLIST_FILE);
1467
1468 hotlist = g_new0 (struct hotlist, 1);
1469 hotlist->type = HL_TYPE_GROUP;
1470 hotlist->label = g_strdup (_("Top level group"));
1471 hotlist->up = hotlist;
1472 /*
1473 * compatibility :-(
1474 */
1475 hotlist->directory = g_strdup ("Hotlist");
1476
1477 hotlist_file = fopen (hotlist_file_name, "r");
1478 if (hotlist_file == NULL)
1479 {
1480 int result;
1481
1482 load_group (hotlist);
1483 hotlist_state.loaded = TRUE;
1484 /*
1485 * just to be sure we got copy
1486 */
1487 hotlist_state.modified = TRUE;
1488 result = save_hotlist ();
1489 hotlist_state.modified = FALSE;
1490 if (result != 0)
1491 remove_old_list = TRUE;
1492 else
1493 message (D_ERROR, _("Hotlist Load"),
1494 _
1495 ("MC was unable to write %s file,\nyour old hotlist entries were not deleted"),
1496 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1497 }
1498 else
1499 {
1500 hot_load_file (hotlist);
1501 fclose (hotlist_file);
1502 hotlist_state.loaded = TRUE;
1503 }
1504
1505 if (remove_old_list)
1506 {
1507 GError *mcerror = NULL;
1508
1509 clean_up_hotlist_groups ("Hotlist");
1510 if (!mc_config_save_file (mc_global.main_config, &mcerror))
1511 setup_save_config_show_error (mc_global.main_config->ini_path, &mcerror);
1512
1513 mc_error_message (&mcerror, NULL);
1514 }
1515
1516 stat (hotlist_file_name, &stat_buf);
1517 hotlist_file_mtime = stat_buf.st_mtime;
1518 current_group = hotlist;
1519 }
1520
1521 /* --------------------------------------------------------------------------------------------- */
1522
1523 static void
hot_save_group(struct hotlist * grp)1524 hot_save_group (struct hotlist *grp)
1525 {
1526 struct hotlist *current;
1527 int i;
1528 char *s;
1529
1530 #define INDENT(n) \
1531 do { \
1532 for (i = 0; i < n; i++) \
1533 putc (' ', hotlist_file); \
1534 } while (0)
1535
1536 for (current = grp->head; current != NULL; current = current->next)
1537 switch (current->type)
1538 {
1539 case HL_TYPE_GROUP:
1540 INDENT (list_level);
1541 fputs ("GROUP \"", hotlist_file);
1542 for (s = current->label; *s != '\0'; s++)
1543 {
1544 if (*s == '"' || *s == '\\')
1545 putc ('\\', hotlist_file);
1546 putc (*s, hotlist_file);
1547 }
1548 fputs ("\"\n", hotlist_file);
1549 list_level += 2;
1550 hot_save_group (current);
1551 list_level -= 2;
1552 INDENT (list_level);
1553 fputs ("ENDGROUP\n", hotlist_file);
1554 break;
1555 case HL_TYPE_ENTRY:
1556 INDENT (list_level);
1557 fputs ("ENTRY \"", hotlist_file);
1558 for (s = current->label; *s != '\0'; s++)
1559 {
1560 if (*s == '"' || *s == '\\')
1561 putc ('\\', hotlist_file);
1562 putc (*s, hotlist_file);
1563 }
1564 fputs ("\" URL \"", hotlist_file);
1565 for (s = current->directory; *s != '\0'; s++)
1566 {
1567 if (*s == '"' || *s == '\\')
1568 putc ('\\', hotlist_file);
1569 putc (*s, hotlist_file);
1570 }
1571 fputs ("\"\n", hotlist_file);
1572 break;
1573 case HL_TYPE_COMMENT:
1574 fprintf (hotlist_file, "#%s\n", current->label);
1575 break;
1576 case HL_TYPE_DOTDOT:
1577 /* do nothing */
1578 break;
1579 default:
1580 break;
1581 }
1582 }
1583
1584 /* --------------------------------------------------------------------------------------------- */
1585
1586 static void
add_dotdot_to_list(void)1587 add_dotdot_to_list (void)
1588 {
1589 if (current_group != hotlist && hotlist_has_dot_dot)
1590 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, LISTBOX_APPEND_AT_END);
1591 }
1592
1593 /* --------------------------------------------------------------------------------------------- */
1594 /*** public functions ****************************************************************************/
1595 /* --------------------------------------------------------------------------------------------- */
1596
1597 void
add2hotlist_cmd(WPanel * panel)1598 add2hotlist_cmd (WPanel * panel)
1599 {
1600 char *lc_prompt;
1601 const char *cp = N_("Label for \"%s\":");
1602 int l;
1603 char *label_string, *label;
1604
1605 #ifdef ENABLE_NLS
1606 cp = _(cp);
1607 #endif
1608
1609 /* extra variable to use it in the button callback */
1610 our_panel = panel;
1611
1612 l = str_term_width1 (cp);
1613 label_string = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1614 lc_prompt = g_strdup_printf (cp, str_trunc (label_string, COLS - 2 * UX - (l + 8)));
1615 label =
1616 input_dialog (_("Add to hotlist"), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string,
1617 INPUT_COMPLETE_NONE);
1618 g_free (lc_prompt);
1619
1620 if (label == NULL || *label == '\0')
1621 {
1622 g_free (label_string);
1623 g_free (label);
1624 }
1625 else
1626 {
1627 add2hotlist (label, label_string, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1628 hotlist_state.modified = TRUE;
1629 }
1630 }
1631
1632 /* --------------------------------------------------------------------------------------------- */
1633
1634 char *
hotlist_show(hotlist_t list_type,WPanel * panel)1635 hotlist_show (hotlist_t list_type, WPanel * panel)
1636 {
1637 char *target = NULL;
1638 int res;
1639
1640 /* extra variable to use it in the button callback */
1641 our_panel = panel;
1642
1643 hotlist_state.type = list_type;
1644 load_hotlist ();
1645
1646 init_hotlist (list_type);
1647
1648 /* display file info */
1649 tty_setcolor (SELECTED_COLOR);
1650
1651 hotlist_state.running = TRUE;
1652 res = dlg_run (hotlist_dlg);
1653 hotlist_state.running = FALSE;
1654 save_hotlist ();
1655
1656 if (res == B_ENTER)
1657 {
1658 char *text = NULL;
1659 struct hotlist *hlp = NULL;
1660
1661 listbox_get_current (l_hotlist, &text, (void **) &hlp);
1662 target = g_strdup (hlp != NULL ? hlp->directory : text);
1663 }
1664
1665 hotlist_done ();
1666 return target;
1667 }
1668
1669 /* --------------------------------------------------------------------------------------------- */
1670
1671 gboolean
save_hotlist(void)1672 save_hotlist (void)
1673 {
1674 gboolean saved = FALSE;
1675 struct stat stat_buf;
1676
1677 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name != NULL)
1678 {
1679 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1680
1681 hotlist_file = fopen (hotlist_file_name, "w");
1682 if (hotlist_file == NULL)
1683 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1684 else
1685 {
1686 hot_save_group (hotlist);
1687 fclose (hotlist_file);
1688 stat (hotlist_file_name, &stat_buf);
1689 hotlist_file_mtime = stat_buf.st_mtime;
1690 hotlist_state.modified = FALSE;
1691 saved = TRUE;
1692 }
1693 }
1694
1695 return saved;
1696 }
1697
1698 /* --------------------------------------------------------------------------------------------- */
1699 /**
1700 * Unload list from memory.
1701 * Don't confuse with hotlist_done() for GUI.
1702 */
1703
1704 void
done_hotlist(void)1705 done_hotlist (void)
1706 {
1707 if (hotlist != NULL)
1708 {
1709 remove_group (hotlist);
1710 g_free (hotlist->label);
1711 g_free (hotlist->directory);
1712 MC_PTR_FREE (hotlist);
1713 }
1714
1715 hotlist_state.loaded = FALSE;
1716
1717 MC_PTR_FREE (hotlist_file_name);
1718 l_hotlist = NULL;
1719 current_group = NULL;
1720
1721 if (tkn_buf != NULL)
1722 {
1723 g_string_free (tkn_buf, TRUE);
1724 tkn_buf = NULL;
1725 }
1726 }
1727
1728 /* --------------------------------------------------------------------------------------------- */
1729