history.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. Widgets for the Midnight Commander
  3. Copyright (C) 1994-2018
  4. Free Software Foundation, Inc.
  5. Authors:
  6. Radek Doulik, 1994, 1995
  7. Miguel de Icaza, 1994, 1995
  8. Jakub Jelinek, 1995
  9. Andrej Borsenkow, 1996
  10. Norbert Warmuth, 1997
  11. Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
  12. This file is part of the Midnight Commander.
  13. The Midnight Commander is free software: you can redistribute it
  14. and/or modify it under the terms of the GNU General Public License as
  15. published by the Free Software Foundation, either version 3 of the License,
  16. or (at your option) any later version.
  17. The Midnight Commander is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. GNU General Public License for more details.
  21. You should have received a copy of the GNU General Public License
  22. along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. /** \file history.c
  25. * \brief Source: save, load and show history
  26. */
  27. #include <config.h>
  28. #include <errno.h>
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #include "lib/global.h"
  35. #include "lib/tty/tty.h" /* LINES, COLS */
  36. #include "lib/mcconfig.h" /* for history loading and saving */
  37. #include "lib/fileloc.h"
  38. #include "lib/strutil.h"
  39. #include "lib/util.h" /* list_append_unique */
  40. #include "lib/widget.h"
  41. /*** global variables ****************************************************************************/
  42. /* how much history items are used */
  43. int num_history_items_recorded = 60;
  44. /*** file scope macro definitions ****************************************************************/
  45. /*** file scope type declarations ****************************************************************/
  46. typedef struct
  47. {
  48. Widget *widget;
  49. size_t count;
  50. size_t maxlen;
  51. } history_dlg_data;
  52. /*** file scope variables ************************************************************************/
  53. /*** file scope functions ************************************************************************/
  54. static cb_ret_t
  55. history_dlg_reposition (WDialog * dlg_head)
  56. {
  57. history_dlg_data *data;
  58. int x = 0, y, he, wi;
  59. /* guard checks */
  60. if ((dlg_head == NULL) || (dlg_head->data == NULL))
  61. return MSG_NOT_HANDLED;
  62. data = (history_dlg_data *) dlg_head->data;
  63. y = data->widget->y;
  64. he = data->count + 2;
  65. if (he <= y || y > (LINES - 6))
  66. {
  67. he = MIN (he, y - 1);
  68. y -= he;
  69. }
  70. else
  71. {
  72. y++;
  73. he = MIN (he, LINES - y);
  74. }
  75. if (data->widget->x > 2)
  76. x = data->widget->x - 2;
  77. wi = data->maxlen + 4;
  78. if ((wi + x) > COLS)
  79. {
  80. wi = MIN (wi, COLS);
  81. x = COLS - wi;
  82. }
  83. dlg_set_position (dlg_head, y, x, he, wi);
  84. return MSG_HANDLED;
  85. }
  86. /* --------------------------------------------------------------------------------------------- */
  87. static cb_ret_t
  88. history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
  89. {
  90. switch (msg)
  91. {
  92. case MSG_RESIZE:
  93. return history_dlg_reposition (DIALOG (w));
  94. default:
  95. return dlg_default_callback (w, sender, msg, parm, data);
  96. }
  97. }
  98. /* --------------------------------------------------------------------------------------------- */
  99. /*** public functions ****************************************************************************/
  100. /* --------------------------------------------------------------------------------------------- */
  101. /**
  102. * Load the history from the ${XDG_CACHE_HOME}/mc/history file.
  103. * It is called with the widgets history name and returns the GList list.
  104. */
  105. GList *
  106. history_get (const char *input_name)
  107. {
  108. GList *hist = NULL;
  109. char *profile;
  110. mc_config_t *cfg;
  111. if (num_history_items_recorded == 0) /* this is how to disable */
  112. return NULL;
  113. if ((input_name == NULL) || (*input_name == '\0'))
  114. return NULL;
  115. profile = mc_config_get_full_path (MC_HISTORY_FILE);
  116. cfg = mc_config_init (profile, TRUE);
  117. hist = history_load (cfg, input_name);
  118. mc_config_deinit (cfg);
  119. g_free (profile);
  120. return hist;
  121. }
  122. /* --------------------------------------------------------------------------------------------- */
  123. /**
  124. * Load history from the mc_config
  125. */
  126. GList *
  127. history_load (mc_config_t * cfg, const char *name)
  128. {
  129. size_t i;
  130. GList *hist = NULL;
  131. char **keys;
  132. size_t keys_num = 0;
  133. GIConv conv = INVALID_CONV;
  134. GString *buffer;
  135. if (name == NULL || *name == '\0')
  136. return NULL;
  137. /* get number of keys */
  138. keys = mc_config_get_keys (cfg, name, &keys_num);
  139. g_strfreev (keys);
  140. /* create charset conversion handler to convert strings
  141. from utf-8 to system codepage */
  142. if (!mc_global.utf8_display)
  143. conv = str_crt_conv_from ("UTF-8");
  144. buffer = g_string_sized_new (64);
  145. for (i = 0; i < keys_num; i++)
  146. {
  147. char key[BUF_TINY];
  148. char *this_entry;
  149. g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
  150. this_entry = mc_config_get_string_raw (cfg, name, key, "");
  151. if (this_entry == NULL)
  152. continue;
  153. if (conv == INVALID_CONV)
  154. hist = list_append_unique (hist, this_entry);
  155. else
  156. {
  157. g_string_set_size (buffer, 0);
  158. if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
  159. hist = list_append_unique (hist, this_entry);
  160. else
  161. {
  162. hist = list_append_unique (hist, g_strndup (buffer->str, buffer->len));
  163. g_free (this_entry);
  164. }
  165. }
  166. }
  167. g_string_free (buffer, TRUE);
  168. if (conv != INVALID_CONV)
  169. str_close_conv (conv);
  170. /* return pointer to the last entry in the list */
  171. return g_list_last (hist);
  172. }
  173. /* --------------------------------------------------------------------------------------------- */
  174. /**
  175. * Save history to the mc_config, but don't save config to file
  176. */
  177. void
  178. history_save (mc_config_t * cfg, const char *name, GList * h)
  179. {
  180. GIConv conv = INVALID_CONV;
  181. GString *buffer;
  182. int i;
  183. if (name == NULL || *name == '\0' || h == NULL)
  184. return;
  185. /* go to end of list */
  186. h = g_list_last (h);
  187. /* go back 60 places */
  188. for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
  189. h = g_list_previous (h);
  190. if (name != NULL)
  191. mc_config_del_group (cfg, name);
  192. /* create charset conversion handler to convert strings
  193. from system codepage to UTF-8 */
  194. if (!mc_global.utf8_display)
  195. conv = str_crt_conv_to ("UTF-8");
  196. buffer = g_string_sized_new (64);
  197. /* dump history into profile */
  198. for (i = 0; h != NULL; h = g_list_next (h))
  199. {
  200. char key[BUF_TINY];
  201. char *text = (char *) h->data;
  202. /* We shouldn't have null entries, but let's be sure */
  203. if (text == NULL)
  204. continue;
  205. g_snprintf (key, sizeof (key), "%d", i++);
  206. if (conv == INVALID_CONV)
  207. mc_config_set_string_raw (cfg, name, key, text);
  208. else
  209. {
  210. g_string_set_size (buffer, 0);
  211. if (str_convert (conv, text, buffer) == ESTR_FAILURE)
  212. mc_config_set_string_raw (cfg, name, key, text);
  213. else
  214. mc_config_set_string_raw (cfg, name, key, buffer->str);
  215. }
  216. }
  217. g_string_free (buffer, TRUE);
  218. if (conv != INVALID_CONV)
  219. str_close_conv (conv);
  220. }
  221. /* --------------------------------------------------------------------------------------------- */
  222. char *
  223. history_show (GList ** history, Widget * widget, int current)
  224. {
  225. GList *z, *hlist = NULL, *hi;
  226. size_t maxlen, count = 0;
  227. char *r = NULL;
  228. WDialog *query_dlg;
  229. WListbox *query_list;
  230. history_dlg_data hist_data;
  231. if (*history == NULL)
  232. return NULL;
  233. maxlen = str_term_width1 (_("History")) + 2;
  234. for (z = *history; z != NULL; z = g_list_previous (z))
  235. {
  236. WLEntry *entry;
  237. size_t i;
  238. i = str_term_width1 ((char *) z->data);
  239. maxlen = MAX (maxlen, i);
  240. count++;
  241. entry = g_new0 (WLEntry, 1);
  242. /* history is being reverted here */
  243. entry->text = g_strdup ((char *) z->data);
  244. hlist = g_list_prepend (hlist, entry);
  245. }
  246. hist_data.widget = widget;
  247. hist_data.count = count;
  248. hist_data.maxlen = maxlen;
  249. query_dlg =
  250. dlg_create (TRUE, 0, 0, 4, 4, WPOS_KEEP_DEFAULT, TRUE, dialog_colors, history_dlg_callback,
  251. NULL, "[History-query]", _("History"));
  252. query_dlg->data = &hist_data;
  253. query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
  254. /* this call makes list stick to all sides of dialog, effectively make
  255. it be resized with dialog */
  256. add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL, NULL);
  257. /* to avoid diplicating of (calculating sizes in two places)
  258. code, call dlg_hist_callback function here, to set dialog and
  259. controls positions.
  260. The main idea - create 4x4 dialog and add 2x2 list in
  261. center of it, and let dialog function resize it to needed
  262. size. */
  263. send_message (query_dlg, NULL, MSG_RESIZE, 0, NULL);
  264. if (WIDGET (query_dlg)->y < widget->y)
  265. {
  266. /* draw list entries from bottom upto top */
  267. listbox_set_list (query_list, hlist);
  268. if (current < 0 || (size_t) current >= count)
  269. listbox_select_last (query_list);
  270. else
  271. listbox_select_entry (query_list, count - 1 - (size_t) current);
  272. }
  273. else
  274. {
  275. /* draw list entries from top downto bottom */
  276. /* revert history direction */
  277. hlist = g_list_reverse (hlist);
  278. listbox_set_list (query_list, hlist);
  279. if (current > 0)
  280. listbox_select_entry (query_list, current);
  281. }
  282. if (dlg_run (query_dlg) != B_CANCEL)
  283. {
  284. char *q;
  285. listbox_get_current (query_list, &q, NULL);
  286. r = g_strdup (q);
  287. }
  288. /* get modified history from dialog */
  289. z = NULL;
  290. for (hi = listbox_get_first_link (query_list); hi != NULL; hi = g_list_next (hi))
  291. {
  292. WLEntry *entry = LENTRY (hi->data);
  293. /* history is being reverted here again */
  294. z = g_list_prepend (z, entry->text);
  295. entry->text = NULL;
  296. }
  297. /* restore history direction */
  298. if (WIDGET (query_dlg)->y < widget->y)
  299. z = g_list_reverse (z);
  300. dlg_destroy (query_dlg);
  301. g_list_free_full (*history, g_free);
  302. *history = g_list_last (z);
  303. return r;
  304. }
  305. /* --------------------------------------------------------------------------------------------- */