Fix #314: Remote commands for thumbnail maintenance
[geeqie.git] / src / history_list.c
1 /*
2  * Copyright (C) 2008 - 2016 The Geeqie Team
3  *
4  * Authors: John Ellis, Vladimir Nadvornik, Laurent Monin
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "main.h"
22 #include "history_list.h"
23
24 #include "secure_save.h"
25 #include "ui_fileops.h"
26
27 /*
28  *-----------------------------------------------------------------------------
29  * history lists
30  *-----------------------------------------------------------------------------
31  */
32
33 #define HISTORY_DEFAULT_KEY_COUNT 16
34
35
36 typedef struct _HistoryData HistoryData;
37 struct _HistoryData
38 {
39         gchar *key;
40         GList *list;
41 };
42
43 static GList *history_list = NULL;
44
45
46 static gchar *quoted_from_text(const gchar *text)
47 {
48         const gchar *ptr;
49         gint c = 0;
50         gint l = strlen(text);
51
52         if (l == 0) return NULL;
53
54         while (c < l && text[c] !='"') c++;
55         if (text[c] == '"')
56                 {
57                 gint e;
58                 c++;
59                 ptr = text + c;
60                 e = c;
61                 while (e < l && text[e] !='"') e++;
62                 if (text[e] == '"')
63                         {
64                         if (e - c > 0)
65                                 {
66                                 return g_strndup(ptr, e - c);
67                                 }
68                         }
69                 }
70         return NULL;
71 }
72
73 gboolean history_list_load(const gchar *path)
74 {
75         FILE *f;
76         gchar *key = NULL;
77         gchar s_buf[1024];
78         gchar *pathl;
79
80         pathl = path_from_utf8(path);
81         f = fopen(pathl, "r");
82         g_free(pathl);
83         if (!f) return FALSE;
84
85         /* first line must start with History comment */
86         if (!fgets(s_buf, sizeof(s_buf), f) ||
87             strncmp(s_buf, "#History", 8) != 0)
88                 {
89                 fclose(f);
90                 return FALSE;
91                 }
92
93         while (fgets(s_buf, sizeof(s_buf), f))
94                 {
95                 if (s_buf[0]=='#') continue;
96                 if (s_buf[0]=='[')
97                         {
98                         gint c;
99                         gchar *ptr;
100
101                         ptr = s_buf + 1;
102                         c = 0;
103                         while (ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++;
104
105                         g_free(key);
106                         key = g_strndup(ptr, c);
107                         }
108                 else
109                         {
110                         gchar *value;
111
112                         value = quoted_from_text(s_buf);
113                         if (value && key)
114                                 {
115                                 history_list_add_to_key(key, value, 0);
116                                 }
117                         g_free(value);
118                         }
119                 }
120
121         fclose(f);
122
123         g_free(key);
124
125         return TRUE;
126 }
127
128 gboolean history_list_save(const gchar *path)
129 {
130         SecureSaveInfo *ssi;
131         GList *list;
132         gchar *pathl;
133         gint list_count;
134
135         pathl = path_from_utf8(path);
136         ssi = secure_open(pathl);
137         g_free(pathl);
138         if (!ssi)
139                 {
140                 log_printf(_("Unable to write history lists to: %s\n"), path);
141                 return FALSE;
142                 }
143
144         secure_fprintf(ssi, "#History lists\n\n");
145
146         list = g_list_last(history_list);
147         while (list && secsave_errno == SS_ERR_NONE)
148                 {
149                 HistoryData *hd;
150                 GList *work;
151
152                 hd = list->data;
153                 list = list->prev;
154
155                 secure_fprintf(ssi, "[%s]\n", hd->key);
156
157                 /* save them inverted (oldest to newest)
158                  * so that when reading they are added correctly
159                  */
160                 work = g_list_last(hd->list);
161                 list_count = g_list_position(hd->list, g_list_last(hd->list)) + 1;
162                 while (work && secsave_errno == SS_ERR_NONE)
163                         {
164                         if (!(strcmp(hd->key, "path_list") == 0 && list_count > options->open_recent_list_maxsize))
165                                 {
166                                 secure_fprintf(ssi, "\"%s\"\n", (gchar *)work->data);
167                                 }
168                         work = work->prev;
169                         list_count--;
170                         }
171                 secure_fputc(ssi, '\n');
172                 }
173
174         secure_fprintf(ssi, "#end\n");
175
176         return (secure_close(ssi) == 0);
177 }
178
179 static void history_list_free(HistoryData *hd)
180 {
181         GList *work;
182
183         if (!hd) return;
184
185         work = hd->list;
186         while (work)
187                 {
188                 g_free(work->data);
189                 work = work->next;
190                 }
191
192         g_free(hd->key);
193         g_free(hd);
194 }
195
196 static HistoryData *history_list_find_by_key(const gchar *key)
197 {
198         GList *work = history_list;
199
200         if (!key) return NULL;
201
202         while (work)
203                 {
204                 HistoryData *hd = work->data;
205                 if (strcmp(hd->key, key) == 0) return hd;
206                 work = work->next;
207                 }
208         return NULL;
209 }
210
211 const gchar *history_list_find_last_path_by_key(const gchar *key)
212 {
213         HistoryData *hd;
214
215         hd = history_list_find_by_key(key);
216         if (!hd || !hd->list) return NULL;
217
218         return hd->list->data;
219 }
220
221 void history_list_free_key(const gchar *key)
222 {
223         HistoryData *hd;
224         hd = history_list_find_by_key(key);
225         if (!hd) return;
226
227         history_list = g_list_remove(history_list, hd);
228         history_list_free(hd);
229 }
230
231 void history_list_add_to_key(const gchar *key, const gchar *path, gint max)
232 {
233         HistoryData *hd;
234         GList *work;
235
236         if (!key || !path) return;
237
238         hd = history_list_find_by_key(key);
239         if (!hd)
240                 {
241                 hd = g_new(HistoryData, 1);
242                 hd->key = g_strdup(key);
243                 hd->list = NULL;
244                 history_list = g_list_prepend(history_list, hd);
245                 }
246
247         /* if already in the list, simply move it to the top */
248         work = hd->list;
249         while (work)
250                 {
251                 gchar *buf = work->data;
252
253                 if (strcmp(buf, path) == 0)
254                         {
255                         /* if not first, move it */
256                         if (work != hd->list)
257                                 {
258                                 hd->list = g_list_remove(hd->list, buf);
259                                 hd->list = g_list_prepend(hd->list, buf);
260                                 }
261                         return;
262                         }
263                 work = work->next;
264                 }
265
266         hd->list = g_list_prepend(hd->list, g_strdup(path));
267
268         if (max == -1) max = HISTORY_DEFAULT_KEY_COUNT;
269         if (max > 0)
270                 {
271                 gint len = 0;
272                 GList *work = hd->list;
273                 GList *last = NULL;
274
275                 while (work)
276                         {
277                         len++;
278                         last = work;
279                         work = work->next;
280                         }
281
282                 work = last;
283                 while (work && len > max)
284                         {
285                         GList *node = work;
286                         work = work->prev;
287
288                         g_free(node->data);
289                         hd->list = g_list_delete_link(hd->list, node);
290                         len--;
291                         }
292                 }
293 }
294
295 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath)
296 {
297         HistoryData *hd;
298         GList *work;
299
300         if (!oldpath) return;
301         hd = history_list_find_by_key(key);
302         if (!hd) return;
303
304         work = hd->list;
305         while (work)
306                 {
307                 gchar *buf = work->data;
308                 if (strcmp(buf, oldpath) == 0)
309                         {
310                         if (newpath)
311                                 {
312                                 work->data = g_strdup(newpath);
313                                 }
314                         else
315                                 {
316                                 hd->list = g_list_remove(hd->list, buf);
317                                 }
318                         g_free(buf);
319                         return;
320                         }
321                 work = work->next;
322                 }
323 }
324
325 void history_list_item_move(const gchar *key, const gchar *path, gint direction)
326 {
327         HistoryData *hd;
328         GList *work;
329         gint p = 0;
330
331         if (!path) return;
332         hd = history_list_find_by_key(key);
333         if (!hd) return;
334
335         work = hd->list;
336         while (work)
337                 {
338                 gchar *buf = work->data;
339                 if (strcmp(buf, path) == 0)
340                         {
341                         p += direction;
342                         if (p < 0) return;
343                         hd->list = g_list_remove(hd->list, buf);
344                         hd->list = g_list_insert(hd->list, buf, p);
345                         return;
346                         }
347                 work = work->next;
348                 p++;
349                 }
350 }
351
352 void history_list_item_remove(const gchar *key, const gchar *path)
353 {
354         history_list_item_change(key, path, NULL);
355 }
356
357 GList *history_list_get_by_key(const gchar *key)
358 {
359         HistoryData *hd;
360
361         hd = history_list_find_by_key(key);
362         if (!hd) return NULL;
363
364         return hd->list;
365 }
366 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */