1 /* Directory hotlist -- for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997 the Free Software Foundation.
10 Janne did the original Hotlist code, Andrej made the groupable
11 hotlist; the move hotlist and revamped the file format and made
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <sys/types.h>
40 #include "tty.h" /* COLS */
41 #include "color.h" /* dialog_colors */
44 #include "setup.h" /* For profile_bname */
45 #include "profile.h" /* Load/save directories hotlist */
46 #include "wtools.h" /* QuickDialog */
47 #include "panel.h" /* current_panel */
48 #include "main.h" /* repaint_screen */
50 #include "key.h" /* KEY_M_CTRL */
51 #include "command.h" /* cmdline */
57 #define BY (LINES - 6)
59 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
61 #define B_ADD_CURRENT B_USER
62 #define B_REMOVE (B_USER + 1)
63 #define B_NEW_GROUP (B_USER + 2)
64 #define B_NEW_ENTRY (B_USER + 3)
65 #define B_UP_GROUP (B_USER + 4)
66 #define B_INSERT (B_USER + 5)
67 #define B_APPEND (B_USER + 6)
68 #define B_MOVE (B_USER + 7)
71 #include "../vfs/gc.h"
72 #define B_FREE_ALL_VFS (B_USER + 8)
73 #define B_REFRESH_VFS (B_USER + 9)
76 static WListbox
*l_hotlist
;
77 static WListbox
*l_movelist
;
79 static Dlg_head
*hotlist_dlg
;
80 static Dlg_head
*movelist_dlg
;
82 static WLabel
*pname
, *pname_group
, *movelist_group
;
92 * these parameters are intended to be user configurable
94 int expanded
; /* expanded view of all groups at startup */
97 * these reflect run time state
100 int loaded
; /* hotlist is loaded */
101 int readonly
; /* hotlist readonly */
102 int file_error
; /* parse error while reading file */
103 int running
; /* we are running dlg (and have to
105 int moving
; /* we are in moving hotlist currently */
106 int modified
; /* hotlist was modified */
107 int type
; /* LIST_HOTLIST || LIST_VFSLIST */
110 static struct _hotlist_but
{
111 int ret_cmd
, flags
, y
, x
;
115 { B_MOVE
, NORMAL_BUTTON
, 1, 42, N_("&Move"), LIST_HOTLIST
},
116 { B_REMOVE
, NORMAL_BUTTON
, 1, 30, N_("&Remove"), LIST_HOTLIST
},
117 { B_APPEND
, NORMAL_BUTTON
, 1, 15, N_("&Append"), LIST_MOVELIST
},
118 { B_INSERT
, NORMAL_BUTTON
, 1, 0, N_("&Insert"), LIST_MOVELIST
},
119 { B_NEW_ENTRY
, NORMAL_BUTTON
, 1, 15, N_("New &Entry"), LIST_HOTLIST
},
120 { B_NEW_GROUP
, NORMAL_BUTTON
, 1, 0, N_("New &Group"), LIST_HOTLIST
},
121 { B_CANCEL
, NORMAL_BUTTON
, 0, 53, N_("&Cancel"), LIST_HOTLIST
|LIST_VFSLIST
|LIST_MOVELIST
},
122 { B_UP_GROUP
, NORMAL_BUTTON
, 0, 42, N_("&Up"), LIST_HOTLIST
|LIST_MOVELIST
},
123 { B_ADD_CURRENT
, NORMAL_BUTTON
, 0, 20, N_("&Add current"), LIST_HOTLIST
},
125 { B_REFRESH_VFS
, NORMAL_BUTTON
, 0, 43, N_("&Refresh"), LIST_VFSLIST
},
126 { B_FREE_ALL_VFS
, NORMAL_BUTTON
, 0, 20, N_("Fr&ee VFSs now"), LIST_VFSLIST
},
128 { B_ENTER
, DEFPUSH_BUTTON
, 0, 0, N_("Change &To"), LIST_HOTLIST
|LIST_VFSLIST
|LIST_MOVELIST
},
131 /* Directory hotlist */
132 static struct hotlist
{
133 enum HotListType type
;
136 struct hotlist
*head
;
138 struct hotlist
*next
;
141 static struct hotlist
*current_group
;
143 static void init_movelist (int, struct hotlist
*);
144 static void add_new_group_cmd (void);
145 static void add_new_entry_cmd (void);
146 static void remove_from_hotlist (struct hotlist
*entry
);
147 static void load_hotlist (void);
149 #define new_hotlist() g_new0(struct hotlist, 1)
152 hotlist_refresh (Dlg_head
* dlg
)
154 common_dialog_repaint (dlg
);
155 attrset (COLOR_NORMAL
);
156 draw_box (dlg
, 2, 5, dlg
->lines
- (hotlist_state
.moving
? 6 : 10),
157 dlg
->cols
- (UX
* 2));
158 if (!hotlist_state
.moving
)
159 draw_box (dlg
, dlg
->lines
- 8, 5, 3, dlg
->cols
- (UX
* 2));
162 /* If current->data is 0, then we are dealing with a VFS pathname */
164 update_path_name (void)
167 WListbox
*list
= hotlist_state
.moving
? l_movelist
: l_hotlist
;
168 Dlg_head
*dlg
= list
->widget
.parent
;
171 if (list
->current
->data
!= 0) {
172 struct hotlist
*hlp
= (struct hotlist
*) list
->current
->data
;
174 if (hlp
->type
== HL_TYPE_ENTRY
)
175 text
= hlp
->directory
;
177 text
= _("Subgroup - press ENTER to see list");
179 text
= list
->current
->text
;
184 if (!hotlist_state
.moving
)
185 label_set_text (pname
,
186 name_trunc (text
, dlg
->cols
- (UX
* 2 + 4)));
188 p
= g_strconcat (" ", current_group
->label
, " ", NULL
);
189 if (!hotlist_state
.moving
)
190 label_set_text (pname_group
,
191 name_trunc (p
, dlg
->cols
- (UX
* 2 + 4)));
193 label_set_text (movelist_group
,
194 name_trunc (p
, dlg
->cols
- (UX
* 2 + 4)));
200 #define CHECK_BUFFER \
204 if ((i = strlen (current->label) + 3) > buflen) { \
206 buf = g_malloc (buflen = 1024 * (i/1024 + 1)); \
211 static void fill_listbox (void)
213 struct hotlist
*current
= current_group
->head
;
218 buf
= g_malloc (buflen
= 1024);
222 switch (current
->type
) {
226 strcat (strcat (buf
, "->"), current
->label
);
227 if (hotlist_state
.moving
)
228 listbox_add_item (l_movelist
, 0, 0, buf
, current
);
230 listbox_add_item (l_hotlist
, 0, 0, buf
, current
);
234 if (hotlist_state
.moving
)
235 listbox_add_item (l_movelist
, 0, 0, current
->label
, current
);
237 listbox_add_item (l_hotlist
, 0, 0, current
->label
, current
);
242 current
= current
->next
;
249 unlink_entry (struct hotlist
*entry
)
251 struct hotlist
*current
= current_group
->head
;
253 if (current
== entry
)
254 current_group
->head
= entry
->next
;
256 while (current
&& current
->next
!= entry
)
257 current
= current
->next
;
259 current
->next
= entry
->next
;
265 static void add_name_to_list (const char *path
)
267 listbox_add_item (l_hotlist
, 0, 0, path
, 0);
269 #endif /* !USE_VFS */
272 hotlist_button_callback (int action
)
277 struct hotlist
*saved
= current_group
;
278 struct hotlist
*item
;
279 struct hotlist
*moveto_item
= 0;
280 struct hotlist
*moveto_group
= 0;
283 if (!l_hotlist
->current
)
284 return MSG_NOT_HANDLED
; /* empty group - nothing to do */
285 item
= l_hotlist
->current
->data
;
286 hotlist_state
.moving
= 1;
287 init_movelist (LIST_MOVELIST
, item
);
288 run_dlg (movelist_dlg
);
289 ret
= movelist_dlg
->ret_value
;
290 hotlist_state
.moving
= 0;
291 if (l_movelist
->current
)
292 moveto_item
= l_movelist
->current
->data
;
293 moveto_group
= current_group
;
294 destroy_dlg (movelist_dlg
);
295 current_group
= saved
;
297 return MSG_NOT_HANDLED
;
298 if (moveto_item
== item
)
299 return MSG_NOT_HANDLED
; /* If we insert/append a before/after a
300 it hardly changes anything ;) */
302 listbox_remove_current (l_hotlist
, 1);
303 item
->up
= moveto_group
;
304 if (!moveto_group
->head
)
305 moveto_group
->head
= item
;
306 else if (!moveto_item
) { /* we have group with just comments */
307 struct hotlist
*p
= moveto_group
->head
;
313 } else if (ret
== B_ENTER
|| ret
== B_APPEND
)
314 if (!moveto_item
->next
)
315 moveto_item
->next
= item
;
317 item
->next
= moveto_item
->next
;
318 moveto_item
->next
= item
;
319 } else if (moveto_group
->head
== moveto_item
) {
320 moveto_group
->head
= item
;
321 item
->next
= moveto_item
;
323 struct hotlist
*p
= moveto_group
->head
;
325 while (p
->next
!= moveto_item
)
327 item
->next
= p
->next
;
330 listbox_remove_list (l_hotlist
);
333 hotlist_state
.modified
= 1;
334 return MSG_NOT_HANDLED
;
338 if (l_hotlist
->current
&& l_hotlist
->current
->data
)
339 remove_from_hotlist (l_hotlist
->current
->data
);
340 return MSG_NOT_HANDLED
;
344 add_new_group_cmd ();
345 return MSG_NOT_HANDLED
;
350 return MSG_NOT_HANDLED
;
354 add_new_entry_cmd ();
355 return MSG_NOT_HANDLED
;
360 WListbox
*list
= hotlist_state
.moving
? l_movelist
: l_hotlist
;
362 if (list
->current
->data
) {
363 struct hotlist
*hlp
=
364 (struct hotlist
*) list
->current
->data
;
365 if (hlp
->type
== HL_TYPE_ENTRY
)
368 listbox_remove_list (list
);
371 return MSG_NOT_HANDLED
;
377 /* Fall through if list empty - just go up */
381 WListbox
*list
= hotlist_state
.moving
? l_movelist
: l_hotlist
;
382 listbox_remove_list (list
);
383 current_group
= current_group
->up
;
385 return MSG_NOT_HANDLED
;
395 listbox_remove_list (l_hotlist
);
396 listbox_add_item (l_hotlist
, 0, 0, home_dir
, 0);
397 vfs_fill_names (add_name_to_list
);
398 return MSG_NOT_HANDLED
;
409 hotlist_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
416 case DLG_UNHANDLED_KEY
:
418 case KEY_M_CTRL
| '\n':
423 if (hotlist_button_callback (B_ENTER
)) {
424 h
->ret_value
= B_ENTER
;
430 if (hotlist_state
.type
!= LIST_VFSLIST
)
431 return !hotlist_button_callback (B_UP_GROUP
);
433 return MSG_NOT_HANDLED
;
436 if (!hotlist_state
.moving
) {
437 hotlist_button_callback (B_REMOVE
);
444 if (!hotlist_state
.moving
) {
445 if (l_hotlist
->current
) {
446 if (l_hotlist
->current
->data
) {
447 struct hotlist
*hlp
=
448 (struct hotlist
*) l_hotlist
->current
->data
;
449 if (hlp
->type
== HL_TYPE_ENTRY
) {
451 g_strconcat ("cd ", hlp
->directory
, NULL
);
452 stuff (cmdline
, tmp
, 0);
455 h
->ret_value
= B_CANCEL
;
461 return MSG_HANDLED
; /* ignore key */
463 return MSG_NOT_HANDLED
;
466 if (hotlist_state
.moving
)
467 dlg_select_widget (movelist_dlg
, l_movelist
);
469 dlg_select_widget (hotlist_dlg
, l_hotlist
);
470 /* always stay on hotlist */
474 attrset (MENU_ENTRY_COLOR
);
479 return default_dlg_callback (h
, msg
, parm
);
483 static int l_call (WListbox
*list
)
485 Dlg_head
*dlg
= list
->widget
.parent
;
488 if (list
->current
->data
) {
489 struct hotlist
*hlp
= (struct hotlist
*) list
->current
->data
;
490 if (hlp
->type
== HL_TYPE_ENTRY
) {
491 dlg
->ret_value
= B_ENTER
;
495 hotlist_button_callback (B_ENTER
);
496 hotlist_callback (dlg
, DLG_POST_KEY
, '\n');
500 dlg
->ret_value
= B_ENTER
;
506 hotlist_button_callback (B_UP_GROUP
);
507 hotlist_callback (dlg
, DLG_POST_KEY
, 'u');
512 * Expands all button names (once) and recalculates button positions.
513 * returns number of columns in the dialog box, which is 10 chars longer
516 * If common width of the window (i.e. in xterm) is less than returned
517 * width - sorry :) (anyway this did not handled in previous version too)
520 init_i18n_stuff(int list_type
, int cols
)
523 static char* cancel_but
= N_("&Cancel");
526 static int hotlist_i18n_flag
= 0;
528 if (!hotlist_i18n_flag
)
530 i
= sizeof (hotlist_but
) / sizeof (hotlist_but
[0]);
532 hotlist_but
[i
].text
= _(hotlist_but
[i
].text
);
534 cancel_but
= _(cancel_but
);
535 hotlist_i18n_flag
= 1;
537 #endif /* ENABLE_NLS */
539 /* Dynamic resizing of buttonbars */
541 int len
[2], count
[2]; /* at most two lines of buttons */
544 i
= sizeof (hotlist_but
) / sizeof (hotlist_but
[0]);
545 len
[0] = len
[1] = count
[0] = count
[1] = 0;
547 /* Count len of buttonbars, assuming 2 extra space between buttons */
550 if (! (hotlist_but
[i
].type
& list_type
))
553 row
= hotlist_but
[i
].y
;
555 len
[row
] += strlen (hotlist_but
[i
].text
) + 5;
556 if (hotlist_but
[i
].flags
== DEFPUSH_BUTTON
)
562 cols
= max(cols
, max(len
[0], len
[1]));
564 /* arrange buttons */
566 cur_x
[0] = cur_x
[1] = 0;
567 i
= sizeof (hotlist_but
) / sizeof (hotlist_but
[0]);
570 if (! (hotlist_but
[i
].type
& list_type
))
573 row
= hotlist_but
[i
].y
;
575 if (hotlist_but
[i
].x
!= 0)
577 /* not first int the row */
578 if (!strcmp (hotlist_but
[i
].text
, cancel_but
))
580 cols
- strlen (hotlist_but
[i
].text
) - 13;
582 hotlist_but
[i
].x
= cur_x
[row
];
585 cur_x
[row
] += strlen (hotlist_but
[i
].text
) + 2
586 + (hotlist_but
[i
].flags
== DEFPUSH_BUTTON
? 5 : 3);
594 init_hotlist (int list_type
)
597 char *title
, *help_node
;
600 hotlist_cols
= init_i18n_stuff (list_type
, COLS
- 6);
604 hotlist_state
.expanded
=
605 GetPrivateProfileInt ("HotlistConfig", "expanded_view_of_groups",
608 if (list_type
== LIST_VFSLIST
) {
609 title
= _("Active VFS directories");
610 help_node
= "[vfshot]"; /* FIXME - no such node */
612 title
= _("Directory hotlist");
613 help_node
= "[Hotlist]";
617 create_dlg (0, 0, LINES
- 2, hotlist_cols
, dialog_colors
,
618 hotlist_callback
, help_node
, title
, DLG_CENTER
| DLG_REVERSE
);
620 for (i
= 0; i
< BUTTONS
; i
++) {
621 if (hotlist_but
[i
].type
& list_type
)
622 add_widget (hotlist_dlg
,
623 button_new (BY
+ hotlist_but
[i
].y
,
624 BX
+ hotlist_but
[i
].x
,
625 hotlist_but
[i
].ret_cmd
,
626 hotlist_but
[i
].flags
,
628 hotlist_button_callback
));
631 /* We add the labels.
632 * pname will hold entry's pathname;
633 * pname_group will hold name of current group
635 pname
= label_new (UY
- 11 + LINES
, UX
+ 2, "");
636 add_widget (hotlist_dlg
, pname
);
637 if (!hotlist_state
.moving
) {
638 add_widget (hotlist_dlg
,
639 label_new (UY
- 12 + LINES
, UX
+ 1,
640 _(" Directory path ")));
642 /* This one holds the displayed pathname */
643 pname_group
= label_new (UY
, UX
+ 1, _(" Directory label "));
644 add_widget (hotlist_dlg
, pname_group
);
646 /* get new listbox */
648 listbox_new (UY
+ 1, UX
+ 1, COLS
- 2 * UX
- 8, LINES
- 14,
651 /* Fill the hotlist with the active VFS or the hotlist */
653 if (list_type
== LIST_VFSLIST
) {
654 listbox_add_item (l_hotlist
, 0, 0, home_dir
, 0);
655 vfs_fill_names (add_name_to_list
);
657 #endif /* !USE_VFS */
660 add_widget (hotlist_dlg
, l_hotlist
);
661 /* add listbox to the dialogs */
665 init_movelist (int list_type
, struct hotlist
*item
)
668 char *hdr
= g_strdup_printf (_("Moving %s"), item
->label
);
669 int movelist_cols
= init_i18n_stuff (list_type
, COLS
- 6);
674 create_dlg (0, 0, LINES
- 6, movelist_cols
, dialog_colors
,
675 hotlist_callback
, "[Hotlist]", hdr
, DLG_CENTER
| DLG_REVERSE
);
678 for (i
= 0; i
< BUTTONS
; i
++) {
679 if (hotlist_but
[i
].type
& list_type
)
680 add_widget (movelist_dlg
,
681 button_new (BY
- 4 + hotlist_but
[i
].y
,
682 BX
+ hotlist_but
[i
].x
,
683 hotlist_but
[i
].ret_cmd
,
684 hotlist_but
[i
].flags
,
686 hotlist_button_callback
));
689 /* We add the labels. We are interested in the last one,
690 * that one will hold the path name label
692 movelist_group
= label_new (UY
, UX
+ 1, _(" Directory label "));
693 add_widget (movelist_dlg
, movelist_group
);
694 /* get new listbox */
696 listbox_new (UY
+ 1, UX
+ 1, movelist_dlg
->cols
- 2 * UX
- 2,
697 movelist_dlg
->lines
- 8, l_call
);
701 add_widget (movelist_dlg
, l_movelist
);
702 /* add listbox to the dialogs */
706 * Destroy the list dialog.
707 * Don't confuse with done_hotlist() for the list in memory.
709 static void hotlist_done (void)
711 destroy_dlg (hotlist_dlg
);
714 update_panels (UP_OPTIMIZE
, UP_KEEPSEL
);
719 find_group_section (struct hotlist
*grp
)
721 return g_strconcat (grp
->directory
, ".Group", NULL
);
726 /* 1.11.96 bor: added pos parameter to control placement of new item.
727 see widget.c, listbox_add_item()
728 now hotlist is in unsorted mode
730 static struct hotlist
*
731 add2hotlist (char *label
, char *directory
, enum HotListType type
, int pos
)
734 struct hotlist
*current
= NULL
;
737 * Hotlist is neither loaded nor loading.
738 * Must be called by "Ctrl-x a" before using hotlist.
743 if (l_hotlist
&& l_hotlist
->current
)
744 current
= l_hotlist
->current
->data
;
746 new = new_hotlist ();
750 new->directory
= directory
;
751 new->up
= current_group
;
753 if (!current_group
->head
) { /* first element in group */
754 current_group
->head
= new;
755 } else if (pos
== 2) { /* should be appended after current*/
756 new->next
= current
->next
;
758 } else if (pos
== 1 &&
759 current
== current_group
->head
) {
760 /* should be inserted before first item */
762 current_group
->head
= new;
763 } else if (pos
== 1) { /* befor current */
764 struct hotlist
*p
= current_group
->head
;
766 while (p
->next
!= current
)
771 } else { /* append at the end */
772 struct hotlist
*p
= current_group
->head
;
780 if (hotlist_state
.running
&& type
!= HL_TYPE_COMMENT
) {
781 if (type
== HL_TYPE_GROUP
) {
782 char *lbl
= g_strconcat ("->", new->label
, NULL
);
784 listbox_add_item (l_hotlist
, pos
, 0, lbl
, new);
787 listbox_add_item (l_hotlist
, pos
, 0, new->label
, new);
788 listbox_select_entry (l_hotlist
, l_hotlist
->current
);
796 * Support routine for add_new_entry_input()/add_new_group_input()
797 * Change positions of buttons (first three widgets).
799 * This is just a quick hack. Accurate procedure must take care of
800 * internationalized label lengths and total buttonbar length...assume
801 * 64 is longer anyway.
803 static void add_widgets_i18n(QuickWidget
* qw
, int len
)
805 int i
, l
[3], space
, cur_x
;
807 for (i
= 0; i
< 3; i
++)
809 qw
[i
].text
= _(qw
[i
].text
);
810 l
[i
] = strlen (qw
[i
].text
) + 3;
812 space
= (len
- 4 - l
[0] - l
[1] - l
[2]) / 4;
814 for (cur_x
= 2 + space
, i
= 3; i
--; cur_x
+= l
[i
] + space
)
816 qw
[i
].relative_x
= cur_x
;
817 qw
[i
].x_divisions
= len
;
820 #endif /* ENABLE_NLS */
822 static int add_new_entry_input (char *header
, char *text1
, char *text2
, char *help
, char **r1
, char **r2
)
824 #define RELATIVE_Y_BUTTONS 4
825 #define RELATIVE_Y_LABEL_PTH 3
826 #define RELATIVE_Y_INPUT_PTH 4
828 QuickDialog Quick_input
;
829 static QuickWidget quick_widgets
[] = {
830 { quick_button
, 55, 80, RELATIVE_Y_BUTTONS
, 0, N_("&Cancel"), 0, B_CANCEL
,
831 0, 0, "button-cancel" },
832 { quick_button
, 30, 80, RELATIVE_Y_BUTTONS
, 0, N_("&Insert"), 0, B_INSERT
,
833 0, 0, "button-insert" },
834 { quick_button
, 10, 80, RELATIVE_Y_BUTTONS
, 0, N_("&Append"), 0, B_APPEND
,
835 0, 0, "button-append" },
836 { quick_input
, 4, 80, RELATIVE_Y_INPUT_PTH
, 0, "",58, 0,
838 { quick_label
, RELATIVE_Y_LABEL_PTH
, 80, 3, 0, 0, 0, 0,
840 { quick_input
, 4, 80, 3, 0, "", 58, 0,
842 { quick_label
, 3, 80, 2, 0, 0, 0, 0,
849 char *my_str1
, *my_str2
;
852 static int i18n_flag
= 0;
853 #endif /* ENABLE_NLS */
855 len
= max (strlen (header
), (size_t) msglen (text1
, &lines1
));
856 len
= max (len
, (size_t) msglen (text2
, &lines2
)) + 4;
862 add_widgets_i18n(quick_widgets
, len
);
865 #endif /* ENABLE_NLS */
867 Quick_input
.xlen
= len
;
868 Quick_input
.xpos
= -1;
869 Quick_input
.title
= header
;
870 Quick_input
.help
= help
;
871 Quick_input
.i18n
= 0;
872 quick_widgets
[6].text
= text1
;
873 quick_widgets
[4].text
= text2
;
874 quick_widgets
[5].text
= *r1
;
875 quick_widgets
[3].text
= *r2
;
877 for (i
= 0; i
< 7; i
++)
878 quick_widgets
[i
].y_divisions
= lines1
+lines2
+7;
879 Quick_input
.ylen
= lines1
+ lines2
+ 7;
881 quick_widgets
[0].relative_y
= RELATIVE_Y_BUTTONS
+ (lines1
+ lines2
);
882 quick_widgets
[1].relative_y
= RELATIVE_Y_BUTTONS
+ (lines1
+ lines2
);
883 quick_widgets
[2].relative_y
= RELATIVE_Y_BUTTONS
+ (lines1
+ lines2
);
884 quick_widgets
[3].relative_y
= RELATIVE_Y_INPUT_PTH
+ (lines1
);
885 quick_widgets
[4].relative_y
= RELATIVE_Y_LABEL_PTH
+ (lines1
);
887 quick_widgets
[5].str_result
= &my_str1
;
888 quick_widgets
[3].str_result
= &my_str2
;
890 Quick_input
.widgets
= quick_widgets
;
891 if ((i
= quick_dialog (&Quick_input
)) != B_CANCEL
){
892 *r1
= *(quick_widgets
[5].str_result
);
893 *r2
= *(quick_widgets
[3].str_result
);
899 static void add_new_entry_cmd (void)
901 char *title
= 0, *url
= 0;
904 /* Take current directory as default value for input fields */
905 title
= url
= current_panel
->cwd
;
907 ret
= add_new_entry_input (_("New hotlist entry"), _("Directory label"), _("Directory path"),
908 "[Hotlist]", &title
, &url
);
910 if (!ret
|| !title
|| !*title
|| !url
|| !*url
)
913 if (ret
== B_ENTER
|| ret
== B_APPEND
)
914 add2hotlist (g_strdup (title
),g_strdup (url
), HL_TYPE_ENTRY
, 2);
916 add2hotlist (g_strdup (title
),g_strdup (url
), HL_TYPE_ENTRY
, 1);
918 hotlist_state
.modified
= 1;
921 static int add_new_group_input (char *header
, char *label
, char **result
)
924 QuickDialog Quick_input
;
925 static QuickWidget quick_widgets
[] = {
926 { quick_button
, 55, 80, 1, 0, N_("&Cancel"), 0, B_CANCEL
, 0, 0,
928 { quick_button
, 30, 80, 1, 0, N_("&Insert"), 0, B_INSERT
, 0, 0,
930 { quick_button
, 10, 80, 1, 0, N_("&Append"), 0, B_APPEND
, 0, 0,
932 { quick_input
, 4, 80, 0, 0, "", 58, 0, 0, 0, "input" },
933 { quick_label
, 3, 80, 2, 0, 0, 0, 0, 0, 0, "label" },
935 int relative_y
[] = {1, 1, 1, 0, 2}; /* the relative_x component from the
936 quick_widgets variable above */
943 static int i18n_flag
= 0;
944 #endif /* ENABLE_NLS */
946 len
= max (strlen (header
), (size_t) msglen (label
, &lines
)) + 4;
952 add_widgets_i18n(quick_widgets
, len
);
955 #endif /* ENABLE_NLS */
957 Quick_input
.xlen
= len
;
958 Quick_input
.xpos
= -1;
959 Quick_input
.title
= header
;
960 Quick_input
.help
= "[Hotlist]";
961 Quick_input
.i18n
= 0;
962 quick_widgets
[4].text
= label
;
964 for (i
= 0; i
< 5; i
++)
965 quick_widgets
[i
].y_divisions
= lines
+6;
966 Quick_input
.ylen
= lines
+ 6;
968 for (i
= 0; i
< 4; i
++)
969 quick_widgets
[i
].relative_y
= relative_y
[i
] + 2 + lines
;
971 quick_widgets
[3].str_result
= &my_str
;
972 quick_widgets
[3].text
= "";
974 Quick_input
.widgets
= quick_widgets
;
975 if ((ret
= quick_dialog (&Quick_input
)) != B_CANCEL
){
982 static void add_new_group_cmd (void)
987 ret
= add_new_group_input (_(" New hotlist group "), _("Name of new group"), &label
);
988 if (!ret
|| !label
|| !*label
)
991 if (ret
== B_ENTER
|| ret
== B_APPEND
)
992 add2hotlist (label
, 0, HL_TYPE_GROUP
, 2);
994 add2hotlist (label
, 0, HL_TYPE_GROUP
, 1);
996 hotlist_state
.modified
= 1;
999 void add2hotlist_cmd (void)
1001 char *prompt
, *label
;
1002 char *cp
= _("Label for \"%s\":");
1003 int l
= strlen (cp
);
1005 prompt
= g_strdup_printf (cp
, name_trunc (current_panel
->cwd
, COLS
-2*UX
-(l
+8)));
1006 label
= input_dialog (_(" Add to hotlist "), prompt
, current_panel
->cwd
);
1008 if (!label
|| !*label
)
1011 add2hotlist (label
,g_strdup (current_panel
->cwd
), HL_TYPE_ENTRY
, 0);
1012 hotlist_state
.modified
= 1;
1015 static void remove_group (struct hotlist
*grp
)
1017 struct hotlist
*current
= grp
->head
;
1020 struct hotlist
*next
= current
->next
;
1022 if (current
->type
== HL_TYPE_GROUP
)
1023 remove_group (current
);
1026 g_free (current
->label
);
1027 if (current
->directory
)
1028 g_free (current
->directory
);
1036 static void remove_from_hotlist (struct hotlist
*entry
)
1038 if (entry
->type
== HL_TYPE_GROUP
) {
1043 header
= g_strconcat (_(" Remove: "),
1044 name_trunc (entry
->label
, 30),
1047 result
= query_dialog (header
, _("\n Group not empty.\n Remove it?"),
1049 _("&No"), _("&Yes"));
1056 remove_group (entry
);
1059 unlink_entry (entry
);
1062 g_free (entry
->label
);
1063 if (entry
->directory
)
1064 g_free (entry
->directory
);
1066 /* now remove list entry from screen */
1067 listbox_remove_current (l_hotlist
, 1);
1068 hotlist_state
.modified
= 1;
1071 char *hotlist_cmd (int vfs_or_hotlist
)
1073 char *target
= NULL
;
1075 hotlist_state
.type
= vfs_or_hotlist
;
1078 init_hotlist (vfs_or_hotlist
);
1080 /* display file info */
1081 attrset (SELECTED_COLOR
);
1083 hotlist_state
.running
= 1;
1084 run_dlg (hotlist_dlg
);
1085 hotlist_state
.running
= 0;
1088 switch (hotlist_dlg
->ret_value
) {
1093 if (l_hotlist
->current
->data
) {
1094 struct hotlist
*hlp
= (struct hotlist
*) l_hotlist
->current
->data
;
1095 target
= g_strdup (hlp
->directory
);
1097 target
= g_strdup (l_hotlist
->current
->text
);
1106 load_group (struct hotlist
*grp
)
1110 char *group_section
;
1111 struct hotlist
*current
= 0;
1113 group_section
= find_group_section (grp
);
1115 profile_keys
= profile_init_iterator (group_section
, profile_name
);
1117 current_group
= grp
;
1119 while (profile_keys
){
1120 profile_keys
= profile_iterator_next (profile_keys
, &key
, &value
);
1121 add2hotlist (g_strdup (value
), g_strdup (key
), HL_TYPE_GROUP
, 0);
1123 g_free (group_section
);
1125 profile_keys
= profile_init_iterator (grp
->directory
, profile_name
);
1127 while (profile_keys
){
1128 profile_keys
= profile_iterator_next (profile_keys
, &key
, &value
);
1129 add2hotlist (g_strdup (value
),g_strdup (key
), HL_TYPE_ENTRY
, 0);
1132 for (current
= grp
->head
; current
; current
= current
->next
)
1133 load_group (current
);
1138 #define TKN_STRING 2
1140 #define TKN_ENDGROUP 4
1141 #define TKN_COMMENT 5
1144 #define TKN_UNKNOWN 127
1146 static char *tkn_buf
;
1147 static int tkn_buf_length
;
1148 static int tkn_length
;
1150 static char *hotlist_file_name
;
1151 static FILE *hotlist_file
;
1152 static time_t hotlist_file_mtime
;
1154 static int hot_skip_blanks (void)
1158 while ((c
= getc (hotlist_file
)) != EOF
&& c
!= '\n' && isspace (c
))
1164 static int hot_next_token (void)
1168 #define CHECK_BUF() \
1170 if (tkn_length == tkn_buf_length) \
1171 tkn_buf = tkn_buf ? ( g_realloc (tkn_buf, tkn_buf_length += 1024)) \
1172 : ( g_malloc (tkn_buf_length = 1024)); \
1178 c
= hot_skip_blanks ();
1187 while ((c
= getc (hotlist_file
)) != EOF
&& c
!= '\n') {
1192 tkn_buf
[tkn_length
++] = c
== '\n' ? ' ' : c
;
1196 tkn_buf
[tkn_length
] = '\0';
1200 while ((c
= getc (hotlist_file
)) != EOF
&& c
!= '"') {
1202 if ((c
= getc (hotlist_file
)) == EOF
)
1205 tkn_buf
[tkn_length
++] = c
== '\n' ? ' ' : c
;
1210 tkn_buf
[tkn_length
] = '\0';
1214 if ((c
= getc (hotlist_file
)) == EOF
)
1219 /* fall through; it is taken as normal character */
1224 tkn_buf
[tkn_length
++] = toupper(c
);
1225 } while ((c
= fgetc (hotlist_file
)) != EOF
&& isalnum (c
));
1227 ungetc (c
, hotlist_file
);
1229 tkn_buf
[tkn_length
] = '\0';
1230 if (strncmp (tkn_buf
, "GROUP", tkn_length
) == 0)
1232 else if (strncmp (tkn_buf
, "ENTRY", tkn_length
) == 0)
1234 else if (strncmp (tkn_buf
, "ENDGROUP", tkn_length
) == 0)
1235 return TKN_ENDGROUP
;
1236 else if (strncmp (tkn_buf
, "URL", tkn_length
) == 0)
1244 #define SKIP_TO_EOL { \
1246 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1249 #define CHECK_TOKEN(_TKN_) \
1250 if ((tkn = hot_next_token ()) != _TKN_) { \
1251 hotlist_state.readonly = 1; \
1252 hotlist_state.file_error = 1; \
1253 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1254 tkn = hot_next_token (); \
1259 hot_load_group (struct hotlist
* grp
)
1262 struct hotlist
*new_grp
;
1265 current_group
= grp
;
1267 while ((tkn
= hot_next_token()) != TKN_ENDGROUP
)
1270 CHECK_TOKEN(TKN_STRING
);
1271 new_grp
= add2hotlist (g_strdup (tkn_buf
), 0, HL_TYPE_GROUP
, 0);
1273 hot_load_group (new_grp
);
1274 current_group
= grp
;
1277 CHECK_TOKEN(TKN_STRING
);
1278 label
= g_strdup (tkn_buf
);
1279 CHECK_TOKEN(TKN_URL
);
1280 CHECK_TOKEN(TKN_STRING
);
1281 url
= g_strdup (tkn_buf
);
1282 add2hotlist (label
, url
, HL_TYPE_ENTRY
, 0);
1286 label
= g_strdup (tkn_buf
);
1287 add2hotlist (label
, 0, HL_TYPE_COMMENT
, 0);
1290 hotlist_state
.readonly
= 1;
1291 hotlist_state
.file_error
= 1;
1295 /* skip empty lines */
1298 hotlist_state
.readonly
= 1;
1299 hotlist_state
.file_error
= 1;
1307 hot_load_file (struct hotlist
* grp
)
1310 struct hotlist
*new_grp
;
1313 current_group
= grp
;
1315 while ((tkn
= hot_next_token())!= TKN_EOF
)
1318 CHECK_TOKEN(TKN_STRING
);
1319 new_grp
= add2hotlist (g_strdup (tkn_buf
), 0, HL_TYPE_GROUP
, 0);
1321 hot_load_group (new_grp
);
1322 current_group
= grp
;
1325 CHECK_TOKEN(TKN_STRING
);
1326 label
= g_strdup (tkn_buf
);
1327 CHECK_TOKEN(TKN_URL
);
1328 CHECK_TOKEN(TKN_STRING
);
1329 url
= g_strdup (tkn_buf
);
1330 add2hotlist (label
, url
, HL_TYPE_ENTRY
, 0);
1334 label
= g_strdup (tkn_buf
);
1335 add2hotlist (label
, 0, HL_TYPE_COMMENT
, 0);
1338 /* skip empty lines */
1341 hotlist_state
.readonly
= 1;
1342 hotlist_state
.file_error
= 1;
1349 clean_up_hotlist_groups (char *section
)
1355 grp_section
= g_strconcat (section
, ".Group", NULL
);
1356 if (profile_has_section (section
, profile_name
))
1357 profile_clean_section (section
, profile_name
);
1358 if (profile_has_section (grp_section
, profile_name
)) {
1359 profile_keys
= profile_init_iterator (grp_section
, profile_name
);
1361 while (profile_keys
) {
1362 profile_keys
= profile_iterator_next (profile_keys
, &key
, &value
);
1363 clean_up_hotlist_groups (key
);
1365 profile_clean_section (grp_section
, profile_name
);
1367 g_free (grp_section
);
1375 int remove_old_list
= 0;
1376 struct stat stat_buf
;
1378 if (hotlist_state
.loaded
) {
1379 stat (hotlist_file_name
, &stat_buf
);
1380 if (hotlist_file_mtime
< stat_buf
.st_mtime
)
1386 if (!hotlist_file_name
)
1387 hotlist_file_name
= concat_dir_and_file (home_dir
, HOTLIST_FILENAME
);
1389 hotlist
= new_hotlist ();
1390 hotlist
->type
= HL_TYPE_GROUP
;
1391 hotlist
->label
= g_strdup (_(" Top level group "));
1392 hotlist
->up
= hotlist
;
1396 hotlist
->directory
= g_strdup ("Hotlist");
1398 if ((hotlist_file
= fopen (hotlist_file_name
, "r")) == 0) {
1401 load_group (hotlist
);
1402 hotlist_state
.loaded
= 1;
1404 * just to be sure we got copy
1406 hotlist_state
.modified
= 1;
1407 result
= save_hotlist ();
1408 hotlist_state
.modified
= 0;
1410 remove_old_list
= 1;
1414 msg
= g_strconcat (_("MC was unable to write ~/"), HOTLIST_FILENAME
,
1415 _(" file, your old hotlist entries were not deleted"), NULL
);
1417 message (D_ERROR
, _(" Hotlist Load "), msg
);
1421 hot_load_file (hotlist
);
1422 fclose (hotlist_file
);
1423 hotlist_state
.loaded
= 1;
1426 if (remove_old_list
) {
1427 clean_up_hotlist_groups ("Hotlist");
1431 stat (hotlist_file_name
, &stat_buf
);
1432 hotlist_file_mtime
= stat_buf
.st_mtime
;
1433 current_group
= hotlist
;
1437 save_group (struct hotlist
*grp
)
1439 struct hotlist
*current
= grp
->head
;
1440 char *group_section
;
1442 group_section
= find_group_section (grp
);
1444 profile_clean_section (group_section
, profile_name
);
1445 for (;current
&& current
->type
== HL_TYPE_GROUP
; current
= current
->next
){
1446 WritePrivateProfileString (group_section
,
1451 g_free (group_section
);
1453 for (current
= grp
->head
;
1454 current
&& current
->type
== HL_TYPE_GROUP
;
1455 current
= current
->next
)
1456 save_group (current
);
1458 profile_clean_section (grp
->directory
, profile_name
);
1459 for (;current
; current
= current
->next
){
1460 WritePrivateProfileString (grp
->directory
,
1467 static int list_level
= 0;
1470 hot_save_group (struct hotlist
*grp
)
1472 struct hotlist
*current
= grp
->head
;
1478 for (i = 0; i < n; i++) \
1479 putc (' ', hotlist_file); \
1482 for (;current
; current
= current
->next
)
1483 switch (current
->type
) {
1485 INDENT (list_level
);
1486 fputs ("GROUP \"", hotlist_file
);
1487 for (s
= current
->label
; *s
; s
++) {
1489 putc ('\\', hotlist_file
);
1490 else if (*s
== '\\')
1491 putc ('\\', hotlist_file
);
1492 putc (*s
, hotlist_file
);
1494 fputs ("\"\n", hotlist_file
);
1496 hot_save_group (current
);
1498 INDENT (list_level
);
1499 fputs ("ENDGROUP\n", hotlist_file
);
1503 fputs ("ENTRY \"", hotlist_file
);
1504 for (s
= current
->label
; *s
; s
++) {
1506 putc ('\\', hotlist_file
);
1507 else if (*s
== '\\')
1508 putc ('\\', hotlist_file
);
1509 putc (*s
, hotlist_file
);
1511 fputs ("\" URL \"", hotlist_file
);
1512 for (s
= current
->directory
; *s
; s
++) {
1514 putc ('\\', hotlist_file
);
1515 else if (*s
== '\\')
1516 putc ('\\', hotlist_file
);
1517 putc (*s
, hotlist_file
);
1519 fputs ("\"\n", hotlist_file
);
1521 case HL_TYPE_COMMENT
:
1522 fprintf (hotlist_file
, "#%s\n", current
->label
);
1528 int save_hotlist (void)
1531 struct stat stat_buf
;
1533 if (!hotlist_state
.readonly
&& hotlist_state
.modified
&& hotlist_file_name
) {
1534 char *fbak
= g_strconcat (hotlist_file_name
, ".bak", NULL
);
1536 rename (hotlist_file_name
, fbak
);
1537 if ((hotlist_file
= fopen (hotlist_file_name
, "w")) != 0) {
1538 if (stat (fbak
, &stat_buf
) == 0)
1539 chmod (hotlist_file_name
, stat_buf
.st_mode
);
1541 chmod (hotlist_file_name
, S_IRUSR
| S_IWUSR
);
1542 hot_save_group (hotlist
);
1543 fclose (hotlist_file
);
1544 stat (hotlist_file_name
, &stat_buf
);
1545 hotlist_file_mtime
= stat_buf
.st_mtime
;
1547 hotlist_state
.modified
= 0;
1549 rename (fbak
, hotlist_file_name
);
1557 * Unload list from memory.
1558 * Don't confuse with hotlist_done() for GUI.
1560 void done_hotlist (void)
1563 remove_group (hotlist
);
1565 g_free (hotlist
->label
);
1566 if (hotlist
->directory
)
1567 g_free (hotlist
->directory
);
1572 hotlist_state
.loaded
= 0;
1574 if (hotlist_file_name
){
1575 g_free (hotlist_file_name
);
1576 hotlist_file_name
= 0;