history.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* Widgets for the Midnight Commander
  2. Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
  3. 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
  4. Authors: 1994, 1995 Radek Doulik
  5. 1994, 1995 Miguel de Icaza
  6. 1995 Jakub Jelinek
  7. 1996 Andrej Borsenkow
  8. 1997 Norbert Warmuth
  9. 2009, 2010 Andrew Borodin
  10. This program is free software; you can redistribute it and/or modify
  11. it under the terms of the GNU General Public License as published by
  12. the Free Software Foundation; either version 2 of the License, or
  13. (at your option) any later version.
  14. This program is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. GNU General Public License for more details.
  18. You should have received a copy of the GNU General Public License
  19. along with this program; if not, write to the Free Software
  20. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. */
  22. /** \file history.c
  23. * \brief Source: save, load and show history
  24. */
  25. #include <config.h>
  26. #include <errno.h>
  27. #include <stdlib.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <fcntl.h>
  33. #include "lib/global.h"
  34. #include "lib/tty/tty.h" /* LINES, COLS */
  35. #include "lib/mcconfig.h" /* for history loading and saving */
  36. #include "lib/fileloc.h"
  37. #include "lib/strutil.h"
  38. #include "lib/util.h" /* list_append_unique */
  39. #include "lib/widget.h"
  40. /* TODO: these includes should be removed! */
  41. #include "src/setup.h" /* num_history_items_recorded */
  42. /*** global variables ****************************************************************************/
  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 (Dlg_head * 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, y + he, x + wi);
  84. return MSG_HANDLED;
  85. }
  86. /* --------------------------------------------------------------------------------------------- */
  87. static cb_ret_t
  88. history_dlg_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  89. {
  90. switch (msg)
  91. {
  92. case DLG_RESIZE:
  93. return history_dlg_reposition (h);
  94. default:
  95. return default_dlg_callback (h, sender, msg, parm, data);
  96. }
  97. }
  98. /* --------------------------------------------------------------------------------------------- */
  99. /*** public functions ****************************************************************************/
  100. /* --------------------------------------------------------------------------------------------- */
  101. /*
  102. This loads the history of an input line to the widget. It is called
  103. with the widgets history name on creation of the widget, and returns
  104. the GList list.
  105. */
  106. GList *
  107. history_get (const char *input_name)
  108. {
  109. size_t i;
  110. GList *hist = NULL;
  111. char *profile;
  112. mc_config_t *cfg;
  113. char **keys;
  114. size_t keys_num = 0;
  115. GIConv conv = INVALID_CONV;
  116. GString *buffer;
  117. if (num_history_items_recorded == 0) /* this is how to disable */
  118. return NULL;
  119. if ((input_name == NULL) || (*input_name == '\0'))
  120. return NULL;
  121. profile = g_build_filename (mc_config_get_cache_path (), MC_HISTORY_FILE, NULL);
  122. cfg = mc_config_init (profile);
  123. /* get number of keys */
  124. keys = mc_config_get_keys (cfg, input_name, &keys_num);
  125. g_strfreev (keys);
  126. /* create charset conversion handler to convert strings
  127. from utf-8 to system codepage */
  128. if (!utf8_display)
  129. conv = str_crt_conv_from ("UTF-8");
  130. buffer = g_string_sized_new (64);
  131. for (i = 0; i < keys_num; i++)
  132. {
  133. char key[BUF_TINY];
  134. char *this_entry;
  135. g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
  136. this_entry = mc_config_get_string_raw (cfg, input_name, key, "");
  137. if (this_entry == NULL)
  138. continue;
  139. if (conv == INVALID_CONV)
  140. hist = list_append_unique (hist, this_entry);
  141. else
  142. {
  143. g_string_set_size (buffer, 0);
  144. if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
  145. hist = list_append_unique (hist, this_entry);
  146. else
  147. {
  148. hist = list_append_unique (hist, g_strdup (buffer->str));
  149. g_free (this_entry);
  150. }
  151. }
  152. }
  153. g_string_free (buffer, TRUE);
  154. if (conv != INVALID_CONV)
  155. str_close_conv (conv);
  156. mc_config_deinit (cfg);
  157. g_free (profile);
  158. /* return pointer to the last entry in the list */
  159. return g_list_last (hist);
  160. }
  161. /* --------------------------------------------------------------------------------------------- */
  162. /*
  163. This saves the history of an input line from the widget. It is called
  164. with the widgets history name. It stores histories in the file
  165. ${XDG_CACHE_HOME}/mc/history in using the profile code.
  166. */
  167. void
  168. history_put (const char *input_name, GList * h)
  169. {
  170. int i;
  171. char *profile;
  172. mc_config_t *cfg;
  173. GIConv conv = INVALID_CONV;
  174. GString *buffer;
  175. if (num_history_items_recorded == 0) /* this is how to disable */
  176. return;
  177. if ((input_name == NULL) || (*input_name == '\0'))
  178. return;
  179. if (h == NULL)
  180. return;
  181. profile = g_build_filename (mc_config_get_cache_path (), MC_HISTORY_FILE, NULL);
  182. i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  183. if (i != -1)
  184. close (i);
  185. /* Make sure the history is only readable by the user */
  186. if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
  187. {
  188. g_free (profile);
  189. return;
  190. }
  191. /* go to end of list */
  192. h = g_list_last (h);
  193. /* go back 60 places */
  194. for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
  195. h = g_list_previous (h);
  196. cfg = mc_config_init (profile);
  197. if (input_name != NULL)
  198. mc_config_del_group (cfg, input_name);
  199. /* create charset conversion handler to convert strings
  200. from system codepage to UTF-8 */
  201. if (!utf8_display)
  202. conv = str_crt_conv_to ("UTF-8");
  203. buffer = g_string_sized_new (64);
  204. /* dump history into profile */
  205. for (i = 0; h != NULL; h = g_list_next (h))
  206. {
  207. char key[BUF_TINY];
  208. char *text = (char *) h->data;
  209. /* We shouldn't have null entries, but let's be sure */
  210. if (text == NULL)
  211. continue;
  212. g_snprintf (key, sizeof (key), "%d", i++);
  213. if (conv == INVALID_CONV)
  214. mc_config_set_string_raw (cfg, input_name, key, text);
  215. else
  216. {
  217. g_string_set_size (buffer, 0);
  218. if (str_convert (conv, text, buffer) == ESTR_FAILURE)
  219. mc_config_set_string_raw (cfg, input_name, key, text);
  220. else
  221. mc_config_set_string_raw (cfg, input_name, key, buffer->str);
  222. }
  223. }
  224. g_string_free (buffer, TRUE);
  225. if (conv != INVALID_CONV)
  226. str_close_conv (conv);
  227. mc_config_save_file (cfg, NULL);
  228. mc_config_deinit (cfg);
  229. g_free (profile);
  230. }
  231. /* --------------------------------------------------------------------------------------------- */
  232. char *
  233. history_show (GList ** history, Widget * widget)
  234. {
  235. GList *z, *hlist = NULL, *hi;
  236. size_t maxlen, i, count = 0;
  237. char *r = NULL;
  238. Dlg_head *query_dlg;
  239. WListbox *query_list;
  240. history_dlg_data hist_data;
  241. if (*history == NULL)
  242. return NULL;
  243. maxlen = str_term_width1 (_("History")) + 2;
  244. for (z = *history; z != NULL; z = g_list_previous (z))
  245. {
  246. WLEntry *entry;
  247. i = str_term_width1 ((char *) z->data);
  248. maxlen = max (maxlen, i);
  249. count++;
  250. entry = g_new0 (WLEntry, 1);
  251. /* history is being reverted here */
  252. entry->text = g_strdup ((char *) z->data);
  253. hlist = g_list_prepend (hlist, entry);
  254. }
  255. hist_data.widget = widget;
  256. hist_data.count = count;
  257. hist_data.maxlen = maxlen;
  258. query_dlg =
  259. create_dlg (TRUE, 0, 0, 4, 4, dialog_colors, history_dlg_callback,
  260. "[History-query]", _("History"), DLG_COMPACT);
  261. query_dlg->data = &hist_data;
  262. query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
  263. /* this call makes list stick to all sides of dialog, effectively make
  264. it be resized with dialog */
  265. add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
  266. /* to avoid diplicating of (calculating sizes in two places)
  267. code, call dlg_hist_callback function here, to set dialog and
  268. controls positions.
  269. The main idea - create 4x4 dialog and add 2x2 list in
  270. center of it, and let dialog function resize it to needed
  271. size. */
  272. history_dlg_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
  273. if (query_dlg->y < widget->y)
  274. {
  275. /* draw list entries from bottom upto top */
  276. listbox_set_list (query_list, hlist);
  277. listbox_select_last (query_list);
  278. }
  279. else
  280. {
  281. /* draw list entries from top downto bottom */
  282. /* revert history direction */
  283. hlist = g_list_reverse (hlist);
  284. listbox_set_list (query_list, hlist);
  285. }
  286. if (run_dlg (query_dlg) != B_CANCEL)
  287. {
  288. char *q;
  289. listbox_get_current (query_list, &q, NULL);
  290. if (q != NULL)
  291. r = g_strdup (q);
  292. }
  293. /* get modified history from dialog */
  294. z = NULL;
  295. for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
  296. {
  297. WLEntry *entry;
  298. entry = (WLEntry *) hi->data;
  299. /* history is being reverted here again */
  300. z = g_list_prepend (z, entry->text);
  301. entry->text = NULL;
  302. }
  303. /* restore history direction */
  304. if (query_dlg->y < widget->y)
  305. z = g_list_reverse (z);
  306. destroy_dlg (query_dlg);
  307. g_list_foreach (*history, (GFunc) g_free, NULL);
  308. g_list_free (*history);
  309. *history = g_list_last (z);
  310. return r;
  311. }
  312. /* --------------------------------------------------------------------------------------------- */