Remove min() and max() macros. Use MIN() and MAX() macros from GLib.
[midnight-commander.git] / lib / widget / history.c
blobc64ea15b01932c712d8c5211a52af19085e2fbf6
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
7 Authors:
8 Radek Doulik, 1994, 1995
9 Miguel de Icaza, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
13 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
15 This file is part of the Midnight Commander.
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 /** \file history.c
32 * \brief Source: save, load and show history
35 #include <config.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
44 #include "lib/global.h"
46 #include "lib/tty/tty.h" /* LINES, COLS */
47 #include "lib/mcconfig.h" /* for history loading and saving */
48 #include "lib/fileloc.h"
49 #include "lib/strutil.h"
50 #include "lib/util.h" /* list_append_unique */
51 #include "lib/widget.h"
53 /*** global variables ****************************************************************************/
55 /* how much history items are used */
56 int num_history_items_recorded = 60;
58 /*** file scope macro definitions ****************************************************************/
60 /*** file scope type declarations ****************************************************************/
62 typedef struct
64 Widget *widget;
65 size_t count;
66 size_t maxlen;
67 } history_dlg_data;
69 /*** file scope variables ************************************************************************/
71 /*** file scope functions ************************************************************************/
73 static cb_ret_t
74 history_dlg_reposition (WDialog * dlg_head)
76 history_dlg_data *data;
77 int x = 0, y, he, wi;
79 /* guard checks */
80 if ((dlg_head == NULL) || (dlg_head->data == NULL))
81 return MSG_NOT_HANDLED;
83 data = (history_dlg_data *) dlg_head->data;
85 y = data->widget->y;
86 he = data->count + 2;
88 if (he <= y || y > (LINES - 6))
90 he = MIN (he, y - 1);
91 y -= he;
93 else
95 y++;
96 he = MIN (he, LINES - y);
99 if (data->widget->x > 2)
100 x = data->widget->x - 2;
102 wi = data->maxlen + 4;
104 if ((wi + x) > COLS)
106 wi = MIN (wi, COLS);
107 x = COLS - wi;
110 dlg_set_position (dlg_head, y, x, y + he, x + wi);
112 return MSG_HANDLED;
115 /* --------------------------------------------------------------------------------------------- */
117 static cb_ret_t
118 history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
120 switch (msg)
122 case MSG_RESIZE:
123 return history_dlg_reposition (DIALOG (w));
125 default:
126 return dlg_default_callback (w, sender, msg, parm, data);
130 /* --------------------------------------------------------------------------------------------- */
131 /*** public functions ****************************************************************************/
132 /* --------------------------------------------------------------------------------------------- */
135 * Load the history from the ${XDG_CACHE_HOME}/mc/history file.
136 * It is called with the widgets history name and returns the GList list.
139 GList *
140 history_get (const char *input_name)
142 GList *hist = NULL;
143 char *profile;
144 mc_config_t *cfg;
146 if (num_history_items_recorded == 0) /* this is how to disable */
147 return NULL;
148 if ((input_name == NULL) || (*input_name == '\0'))
149 return NULL;
151 profile = mc_config_get_full_path (MC_HISTORY_FILE);
152 cfg = mc_config_init (profile, TRUE);
154 hist = history_load (cfg, input_name);
156 mc_config_deinit (cfg);
157 g_free (profile);
159 return hist;
162 /* --------------------------------------------------------------------------------------------- */
165 * Load history from the mc_config
167 GList *
168 history_load (mc_config_t * cfg, const char *name)
170 size_t i;
171 GList *hist = NULL;
172 char **keys;
173 size_t keys_num = 0;
174 GIConv conv = INVALID_CONV;
175 GString *buffer;
177 if (name == NULL || *name == '\0')
178 return NULL;
180 /* get number of keys */
181 keys = mc_config_get_keys (cfg, name, &keys_num);
182 g_strfreev (keys);
184 /* create charset conversion handler to convert strings
185 from utf-8 to system codepage */
186 if (!mc_global.utf8_display)
187 conv = str_crt_conv_from ("UTF-8");
189 buffer = g_string_sized_new (64);
191 for (i = 0; i < keys_num; i++)
193 char key[BUF_TINY];
194 char *this_entry;
196 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
197 this_entry = mc_config_get_string_raw (cfg, name, key, "");
199 if (this_entry == NULL)
200 continue;
202 if (conv == INVALID_CONV)
203 hist = list_append_unique (hist, this_entry);
204 else
206 g_string_set_size (buffer, 0);
207 if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
208 hist = list_append_unique (hist, this_entry);
209 else
211 hist = list_append_unique (hist, g_strndup (buffer->str, buffer->len));
212 g_free (this_entry);
217 g_string_free (buffer, TRUE);
218 if (conv != INVALID_CONV)
219 str_close_conv (conv);
221 /* return pointer to the last entry in the list */
222 return g_list_last (hist);
225 /* --------------------------------------------------------------------------------------------- */
228 * Save history to the mc_config, but don't save config to file
230 void
231 history_save (mc_config_t * cfg, const char *name, GList * h)
233 GIConv conv = INVALID_CONV;
234 GString *buffer;
235 int i;
237 if (name == NULL || *name == '\0' || h == NULL)
238 return;
240 /* go to end of list */
241 h = g_list_last (h);
243 /* go back 60 places */
244 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
245 h = g_list_previous (h);
247 if (name != NULL)
248 mc_config_del_group (cfg, name);
250 /* create charset conversion handler to convert strings
251 from system codepage to UTF-8 */
252 if (!mc_global.utf8_display)
253 conv = str_crt_conv_to ("UTF-8");
255 buffer = g_string_sized_new (64);
256 /* dump history into profile */
257 for (i = 0; h != NULL; h = g_list_next (h))
259 char key[BUF_TINY];
260 char *text = (char *) h->data;
262 /* We shouldn't have null entries, but let's be sure */
263 if (text == NULL)
264 continue;
266 g_snprintf (key, sizeof (key), "%d", i++);
268 if (conv == INVALID_CONV)
269 mc_config_set_string_raw (cfg, name, key, text);
270 else
272 g_string_set_size (buffer, 0);
273 if (str_convert (conv, text, buffer) == ESTR_FAILURE)
274 mc_config_set_string_raw (cfg, name, key, text);
275 else
276 mc_config_set_string_raw (cfg, name, key, buffer->str);
280 g_string_free (buffer, TRUE);
281 if (conv != INVALID_CONV)
282 str_close_conv (conv);
285 /* --------------------------------------------------------------------------------------------- */
287 char *
288 history_show (GList ** history, Widget * widget, int current)
290 GList *z, *hlist = NULL, *hi;
291 size_t maxlen, count = 0;
292 char *r = NULL;
293 WDialog *query_dlg;
294 WListbox *query_list;
295 history_dlg_data hist_data;
297 if (*history == NULL)
298 return NULL;
300 maxlen = str_term_width1 (_("History")) + 2;
302 for (z = *history; z != NULL; z = g_list_previous (z))
304 WLEntry *entry;
305 size_t i;
307 i = str_term_width1 ((char *) z->data);
308 maxlen = MAX (maxlen, i);
309 count++;
311 entry = g_new0 (WLEntry, 1);
312 /* history is being reverted here */
313 entry->text = g_strdup ((char *) z->data);
314 hlist = g_list_prepend (hlist, entry);
317 hist_data.widget = widget;
318 hist_data.count = count;
319 hist_data.maxlen = maxlen;
321 query_dlg =
322 dlg_create (TRUE, 0, 0, 4, 4, dialog_colors, history_dlg_callback, NULL,
323 "[History-query]", _("History"), DLG_COMPACT);
324 query_dlg->data = &hist_data;
326 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
328 /* this call makes list stick to all sides of dialog, effectively make
329 it be resized with dialog */
330 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL, NULL);
332 /* to avoid diplicating of (calculating sizes in two places)
333 code, call dlg_hist_callback function here, to set dialog and
334 controls positions.
335 The main idea - create 4x4 dialog and add 2x2 list in
336 center of it, and let dialog function resize it to needed
337 size. */
338 send_message (query_dlg, NULL, MSG_RESIZE, 0, NULL);
340 if (WIDGET (query_dlg)->y < widget->y)
342 /* draw list entries from bottom upto top */
343 listbox_set_list (query_list, hlist);
344 if (current < 0 || (size_t) current >= count)
345 listbox_select_last (query_list);
346 else
347 listbox_select_entry (query_list, count - 1 - (size_t) current);
349 else
351 /* draw list entries from top downto bottom */
352 /* revert history direction */
353 hlist = g_list_reverse (hlist);
354 listbox_set_list (query_list, hlist);
355 if (current > 0)
356 listbox_select_entry (query_list, current);
359 if (dlg_run (query_dlg) != B_CANCEL)
361 char *q;
363 listbox_get_current (query_list, &q, NULL);
364 r = g_strdup (q);
367 /* get modified history from dialog */
368 z = NULL;
369 for (hi = listbox_get_first_link (query_list); hi != NULL; hi = g_list_next (hi))
371 WLEntry *entry = LENTRY (hi->data);
373 /* history is being reverted here again */
374 z = g_list_prepend (z, entry->text);
375 entry->text = NULL;
378 /* restore history direction */
379 if (WIDGET (query_dlg)->y < widget->y)
380 z = g_list_reverse (z);
382 dlg_destroy (query_dlg);
384 g_list_free_full (*history, g_free);
385 *history = g_list_last (z);
387 return r;
390 /* --------------------------------------------------------------------------------------------- */