history.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. Widgets for the Midnight Commander
  3. Copyright (C) 1994-2015
  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 <fcntl.h>
  35. #include "lib/global.h"
  36. #include "lib/tty/tty.h" /* LINES, COLS */
  37. #include "lib/mcconfig.h" /* for history loading and saving */
  38. #include "lib/fileloc.h"
  39. #include "lib/strutil.h"
  40. #include "lib/util.h" /* list_append_unique */
  41. #include "lib/widget.h"
  42. /*** global variables ****************************************************************************/
  43. /* how much history items are used */
  44. int num_history_items_recorded = 60;
  45. /*** file scope macro definitions ****************************************************************/
  46. /*** file scope type declarations ****************************************************************/
  47. typedef struct
  48. {
  49. Widget *widget;
  50. size_t count;
  51. size_t maxlen;
  52. } history_dlg_data;
  53. /*** file scope variables ************************************************************************/
  54. /*** file scope functions ************************************************************************/
  55. static cb_ret_t
  56. history_dlg_reposition (WDialog * dlg_head)
  57. {
  58. history_dlg_data *data;
  59. int x = 0, y, he, wi;
  60. /* guard checks */
  61. if ((dlg_head == NULL) || (dlg_head->data == NULL))
  62. return MSG_NOT_HANDLED;
  63. data = (history_dlg_data *) dlg_head->data;
  64. y = data->widget->y;
  65. he = data->count + 2;
  66. if (he <= y || y > (LINES - 6))
  67. {
  68. he = min (he, y - 1);
  69. y -= he;
  70. }
  71. else
  72. {
  73. y++;
  74. he = min (he, LINES - y);
  75. }
  76. if (data->widget->x > 2)
  77. x = data->widget->x - 2;
  78. wi = data->maxlen + 4;
  79. if ((wi + x) > COLS)
  80. {
  81. wi = min (wi, COLS);
  82. x = COLS - wi;
  83. }
  84. dlg_set_position (dlg_head, y, x, y + he, x + wi);
  85. return MSG_HANDLED;
  86. }
  87. /* --------------------------------------------------------------------------------------------- */
  88. static cb_ret_t
  89. history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
  90. {
  91. switch (msg)
  92. {
  93. case MSG_RESIZE:
  94. return history_dlg_reposition (DIALOG (w));
  95. default:
  96. return dlg_default_callback (w, sender, msg, parm, data);
  97. }
  98. }
  99. /* --------------------------------------------------------------------------------------------- */
  100. /*** public functions ****************************************************************************/
  101. /* --------------------------------------------------------------------------------------------- */
  102. /**
  103. * Load the history from the ${XDG_CACHE_HOME}/mc/history file.
  104. * It is called with the widgets history name and returns the GList list.
  105. */
  106. GList *
  107. history_get (const char *input_name)
  108. {
  109. GList *hist = NULL;
  110. char *profile;
  111. mc_config_t *cfg;
  112. if (num_history_items_recorded == 0) /* this is how to disable */
  113. return NULL;
  114. if ((input_name == NULL) || (*input_name == '\0'))
  115. return NULL;
  116. profile = mc_config_get_full_path (MC_HISTORY_FILE);
  117. cfg = mc_config_init (profile, TRUE);
  118. hist = history_load (cfg, input_name);
  119. mc_config_deinit (cfg);
  120. g_free (profile);
  121. return hist;
  122. }
  123. /* --------------------------------------------------------------------------------------------- */
  124. /**
  125. * Load history form the mc_config
  126. */
  127. GList *
  128. history_load (struct mc_config_t * cfg, const char *name)
  129. {
  130. size_t i;
  131. GList *hist = NULL;
  132. char **keys;
  133. size_t keys_num = 0;
  134. GIConv conv = INVALID_CONV;
  135. GString *buffer;
  136. if (name == NULL || *name == '\0')
  137. return NULL;
  138. /* get number of keys */
  139. keys = mc_config_get_keys (cfg, name, &keys_num);
  140. g_strfreev (keys);
  141. /* create charset conversion handler to convert strings
  142. from utf-8 to system codepage */
  143. if (!mc_global.utf8_display)
  144. conv = str_crt_conv_from ("UTF-8");
  145. buffer = g_string_sized_new (64);
  146. for (i = 0; i < keys_num; i++)
  147. {
  148. char key[BUF_TINY];
  149. char *this_entry;
  150. g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
  151. this_entry = mc_config_get_string_raw (cfg, name, key, "");
  152. if (this_entry == NULL)
  153. continue;
  154. if (conv == INVALID_CONV)
  155. hist = list_append_unique (hist, this_entry);
  156. else
  157. {
  158. g_string_set_size (buffer, 0);
  159. if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
  160. hist = list_append_unique (hist, this_entry);
  161. else
  162. {
  163. hist = list_append_unique (hist, g_strndup (buffer->str, buffer->len));
  164. g_free (this_entry);
  165. }
  166. }
  167. }
  168. g_string_free (buffer, TRUE);
  169. if (conv != INVALID_CONV)
  170. str_close_conv (conv);
  171. /* return pointer to the last entry in the list */
  172. return g_list_last (hist);
  173. }
  174. /* --------------------------------------------------------------------------------------------- */
  175. /**
  176. * Save history to the mc_config, but don't save config to file
  177. */
  178. void
  179. history_save (struct mc_config_t *cfg, const char *name, GList * h)
  180. {
  181. GIConv conv = INVALID_CONV;
  182. GString *buffer;
  183. int i;
  184. if (name == NULL || *name == '\0' || h == NULL)
  185. return;
  186. /* go to end of list */
  187. h = g_list_last (h);
  188. /* go back 60 places */
  189. for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
  190. h = g_list_previous (h);
  191. if (name != NULL)
  192. mc_config_del_group (cfg, name);
  193. /* create charset conversion handler to convert strings
  194. from system codepage to UTF-8 */
  195. if (!mc_global.utf8_display)
  196. conv = str_crt_conv_to ("UTF-8");
  197. buffer = g_string_sized_new (64);
  198. /* dump history into profile */
  199. for (i = 0; h != NULL; h = g_list_next (h))
  200. {
  201. char key[BUF_TINY];
  202. char *text = (char *) h->data;
  203. /* We shouldn't have null entries, but let's be sure */
  204. if (text == NULL)
  205. continue;
  206. g_snprintf (key, sizeof (key), "%d", i++);
  207. if (conv == INVALID_CONV)
  208. mc_config_set_string_raw (cfg, name, key, text);
  209. else
  210. {
  211. g_string_set_size (buffer, 0);
  212. if (str_convert (conv, text, buffer) == ESTR_FAILURE)
  213. mc_config_set_string_raw (cfg, name, key, text);
  214. else
  215. mc_config_set_string_raw (cfg, name, key, buffer->str);
  216. }
  217. }
  218. g_string_free (buffer, TRUE);
  219. if (conv != INVALID_CONV)
  220. str_close_conv (conv);
  221. }
  222. /* --------------------------------------------------------------------------------------------- */
  223. char *
  224. history_show (GList ** history, Widget * widget, int current)
  225. {
  226. GList *z, *hlist = NULL, *hi;
  227. size_t maxlen, count = 0;
  228. char *r = NULL;
  229. WDialog *query_dlg;
  230. WListbox *query_list;
  231. history_dlg_data hist_data;
  232. if (*history == NULL)
  233. return NULL;
  234. maxlen = str_term_width1 (_("History")) + 2;
  235. for (z = *history; z != NULL; z = g_list_previous (z))
  236. {
  237. WLEntry *entry;
  238. size_t i;
  239. i = str_term_width1 ((char *) z->data);
  240. maxlen = max (maxlen, i);
  241. count++;
  242. entry = g_new0 (WLEntry, 1);
  243. /* history is being reverted here */
  244. entry->text = g_strdup ((char *) z->data);
  245. hlist = g_list_prepend (hlist, entry);
  246. }
  247. hist_data.widget = widget;
  248. hist_data.count = count;
  249. hist_data.maxlen = maxlen;
  250. query_dlg =
  251. dlg_create (TRUE, 0, 0, 4, 4, dialog_colors, history_dlg_callback, NULL,
  252. "[History-query]", _("History"), DLG_COMPACT);
  253. query_dlg->data = &hist_data;
  254. query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
  255. /* this call makes list stick to all sides of dialog, effectively make
  256. it be resized with dialog */
  257. add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL, NULL);
  258. /* to avoid diplicating of (calculating sizes in two places)
  259. code, call dlg_hist_callback function here, to set dialog and
  260. controls positions.
  261. The main idea - create 4x4 dialog and add 2x2 list in
  262. center of it, and let dialog function resize it to needed
  263. size. */
  264. send_message (query_dlg, NULL, MSG_RESIZE, 0, NULL);
  265. if (WIDGET (query_dlg)->y < widget->y)
  266. {
  267. /* draw list entries from bottom upto top */
  268. listbox_set_list (query_list, hlist);
  269. if (current < 0 || (size_t) current >= count)
  270. listbox_select_last (query_list);
  271. else
  272. listbox_select_entry (query_list, count - 1 - (size_t) current);
  273. }
  274. else
  275. {
  276. /* draw list entries from top downto bottom */
  277. /* revert history direction */
  278. hlist = g_list_reverse (hlist);
  279. listbox_set_list (query_list, hlist);
  280. if (current > 0)
  281. listbox_select_entry (query_list, current);
  282. }
  283. if (dlg_run (query_dlg) != B_CANCEL)
  284. {
  285. char *q;
  286. listbox_get_current (query_list, &q, NULL);
  287. r = g_strdup (q);
  288. }
  289. /* get modified history from dialog */
  290. z = NULL;
  291. for (hi = listbox_get_first_link (query_list); hi != NULL; hi = g_list_next (hi))
  292. {
  293. WLEntry *entry = LENTRY (hi->data);
  294. /* history is being reverted here again */
  295. z = g_list_prepend (z, entry->text);
  296. entry->text = NULL;
  297. }
  298. /* restore history direction */
  299. if (WIDGET (query_dlg)->y < widget->y)
  300. z = g_list_reverse (z);
  301. dlg_destroy (query_dlg);
  302. g_list_free_full (*history, g_free);
  303. *history = g_list_last (z);
  304. return r;
  305. }
  306. /* --------------------------------------------------------------------------------------------- */