Updated Russian translation.
[midnight-commander.git] / lib / widget / history.c
blobf54a35fa2766901d795bc8bf9754001a41ea7bc5
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
11 2009, 2010 Andrew Borodin
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 /** \file history.c
30 * \brief Source: save, load and show history
33 #include <config.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h" /* LINES, COLS */
46 #include "lib/mcconfig.h" /* for history loading and saving */
47 #include "lib/fileloc.h"
48 #include "lib/strutil.h"
49 #include "lib/util.h" /* list_append_unique */
50 #include "lib/widget.h"
52 /* TODO: these includes should be removed! */
53 #include "src/setup.h" /* num_history_items_recorded */
55 /*** global variables ****************************************************************************/
57 int num_history_items_recorded = 60;
59 /*** file scope macro definitions ****************************************************************/
61 /*** file scope type declarations ****************************************************************/
63 typedef struct
65 Widget *widget;
66 size_t count;
67 size_t maxlen;
68 } history_dlg_data;
70 /*** file scope variables ************************************************************************/
72 /*** file scope functions ************************************************************************/
74 static cb_ret_t
75 history_dlg_reposition (Dlg_head * dlg_head)
77 history_dlg_data *data;
78 int x = 0, y, he, wi;
80 /* guard checks */
81 if ((dlg_head == NULL) || (dlg_head->data == NULL))
82 return MSG_NOT_HANDLED;
84 data = (history_dlg_data *) dlg_head->data;
86 y = data->widget->y;
87 he = data->count + 2;
89 if (he <= y || y > (LINES - 6))
91 he = min (he, y - 1);
92 y -= he;
94 else
96 y++;
97 he = min (he, LINES - y);
100 if (data->widget->x > 2)
101 x = data->widget->x - 2;
103 wi = data->maxlen + 4;
105 if ((wi + x) > COLS)
107 wi = min (wi, COLS);
108 x = COLS - wi;
111 dlg_set_position (dlg_head, y, x, y + he, x + wi);
113 return MSG_HANDLED;
116 /* --------------------------------------------------------------------------------------------- */
118 static cb_ret_t
119 history_dlg_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
121 switch (msg)
123 case DLG_RESIZE:
124 return history_dlg_reposition (h);
126 default:
127 return default_dlg_callback (h, sender, msg, parm, data);
131 /* --------------------------------------------------------------------------------------------- */
132 /*** public functions ****************************************************************************/
133 /* --------------------------------------------------------------------------------------------- */
136 This loads the history of an input line to the widget. It is called
137 with the widgets history name on creation of the widget, and returns
138 the GList list.
140 GList *
141 history_get (const char *input_name)
143 size_t i;
144 GList *hist = NULL;
145 char *profile;
146 mc_config_t *cfg;
147 char **keys;
148 size_t keys_num = 0;
149 GIConv conv = INVALID_CONV;
150 GString *buffer;
152 if (num_history_items_recorded == 0) /* this is how to disable */
153 return NULL;
154 if ((input_name == NULL) || (*input_name == '\0'))
155 return NULL;
157 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
158 cfg = mc_config_init (profile);
160 /* get number of keys */
161 keys = mc_config_get_keys (cfg, input_name, &keys_num);
162 g_strfreev (keys);
164 /* create charset conversion handler to convert strings
165 from utf-8 to system codepage */
166 if (!utf8_display)
167 conv = str_crt_conv_from ("UTF-8");
169 buffer = g_string_sized_new (64);
171 for (i = 0; i < keys_num; i++)
173 char key[BUF_TINY];
174 char *this_entry;
176 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
177 this_entry = mc_config_get_string_raw (cfg, input_name, key, "");
179 if (this_entry == NULL)
180 continue;
182 if (conv == INVALID_CONV)
183 hist = list_append_unique (hist, this_entry);
184 else
186 g_string_set_size (buffer, 0);
187 if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
188 hist = list_append_unique (hist, this_entry);
189 else
191 hist = list_append_unique (hist, g_strdup (buffer->str));
192 g_free (this_entry);
197 g_string_free (buffer, TRUE);
198 if (conv != INVALID_CONV)
199 str_close_conv (conv);
200 mc_config_deinit (cfg);
201 g_free (profile);
203 /* return pointer to the last entry in the list */
204 return g_list_last (hist);
207 /* --------------------------------------------------------------------------------------------- */
210 This saves the history of an input line from the widget. It is called
211 with the widgets history name. It stores histories in the file
212 ~/.mc/history in using the profile code.
214 void
215 history_put (const char *input_name, GList * h)
217 int i;
218 char *profile;
219 mc_config_t *cfg;
220 GIConv conv = INVALID_CONV;
221 GString *buffer;
223 if (num_history_items_recorded == 0) /* this is how to disable */
224 return;
225 if ((input_name == NULL) || (*input_name == '\0'))
226 return;
227 if (h == NULL)
228 return;
230 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
232 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
233 if (i != -1)
234 close (i);
236 /* Make sure the history is only readable by the user */
237 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
239 g_free (profile);
240 return;
243 /* go to end of list */
244 h = g_list_last (h);
246 /* go back 60 places */
247 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
248 h = g_list_previous (h);
250 cfg = mc_config_init (profile);
252 if (input_name != NULL)
253 mc_config_del_group (cfg, input_name);
255 /* create charset conversion handler to convert strings
256 from system codepage to UTF-8 */
257 if (!utf8_display)
258 conv = str_crt_conv_to ("UTF-8");
260 buffer = g_string_sized_new (64);
261 /* dump history into profile */
262 for (i = 0; h != NULL; h = g_list_next (h))
264 char key[BUF_TINY];
265 char *text = (char *) h->data;
267 /* We shouldn't have null entries, but let's be sure */
268 if (text == NULL)
269 continue;
271 g_snprintf (key, sizeof (key), "%d", i++);
273 if (conv == INVALID_CONV)
274 mc_config_set_string_raw (cfg, input_name, key, text);
275 else
277 g_string_set_size (buffer, 0);
278 if (str_convert (conv, text, buffer) == ESTR_FAILURE)
279 mc_config_set_string_raw (cfg, input_name, key, text);
280 else
281 mc_config_set_string_raw (cfg, input_name, key, buffer->str);
285 g_string_free (buffer, TRUE);
286 if (conv != INVALID_CONV)
287 str_close_conv (conv);
288 mc_config_save_file (cfg, NULL);
289 mc_config_deinit (cfg);
290 g_free (profile);
293 /* --------------------------------------------------------------------------------------------- */
295 char *
296 history_show (GList ** history, Widget * widget)
298 GList *z, *hlist = NULL, *hi;
299 size_t maxlen, i, count = 0;
300 char *r = NULL;
301 Dlg_head *query_dlg;
302 WListbox *query_list;
303 history_dlg_data hist_data;
305 if (*history == NULL)
306 return NULL;
308 maxlen = str_term_width1 (_("History")) + 2;
310 for (z = *history; z != NULL; z = g_list_previous (z))
312 WLEntry *entry;
314 i = str_term_width1 ((char *) z->data);
315 maxlen = max (maxlen, i);
316 count++;
318 entry = g_new0 (WLEntry, 1);
319 /* history is being reverted here */
320 entry->text = g_strdup ((char *) z->data);
321 hlist = g_list_prepend (hlist, entry);
324 hist_data.widget = widget;
325 hist_data.count = count;
326 hist_data.maxlen = maxlen;
328 query_dlg =
329 create_dlg (TRUE, 0, 0, 4, 4, dialog_colors, history_dlg_callback,
330 "[History-query]", _("History"), DLG_COMPACT);
331 query_dlg->data = &hist_data;
333 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
335 /* this call makes list stick to all sides of dialog, effectively make
336 it be resized with dialog */
337 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
339 /* to avoid diplicating of (calculating sizes in two places)
340 code, call dlg_hist_callback function here, to set dialog and
341 controls positions.
342 The main idea - create 4x4 dialog and add 2x2 list in
343 center of it, and let dialog function resize it to needed
344 size. */
345 history_dlg_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
347 if (query_dlg->y < widget->y)
349 /* draw list entries from bottom upto top */
350 listbox_set_list (query_list, hlist);
351 listbox_select_last (query_list);
353 else
355 /* draw list entries from top downto bottom */
356 /* revert history direction */
357 hlist = g_list_reverse (hlist);
358 listbox_set_list (query_list, hlist);
361 if (run_dlg (query_dlg) != B_CANCEL)
363 char *q;
365 listbox_get_current (query_list, &q, NULL);
366 if (q != NULL)
367 r = g_strdup (q);
370 /* get modified history from dialog */
371 z = NULL;
372 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
374 WLEntry *entry;
376 entry = (WLEntry *) hi->data;
377 /* history is being reverted here again */
378 z = g_list_prepend (z, entry->text);
379 entry->text = NULL;
382 /* restore history direction */
383 if (query_dlg->y < widget->y)
384 z = g_list_reverse (z);
386 destroy_dlg (query_dlg);
388 g_list_foreach (*history, (GFunc) g_free, NULL);
389 g_list_free (*history);
390 *history = g_list_last (z);
392 return r;
395 /* --------------------------------------------------------------------------------------------- */