Preparing stable version
[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  *-----------------------------------------------------------------------------
30  * Implements a history chain. Used by the Back and Forward toolbar buttons.
31  * Selecting any folder appends the path to the end of the chain.
32  * Pressing the Back and Forward buttons moves along the chain, but does
33  * not make additions to the chain.
34  * The chain always increases and is deleted at the end of the session
35  *-----------------------------------------------------------------------------
36  */
37
38 static GList *history_chain = NULL;
39 static guint chain_index = G_MAXUINT;
40 static gboolean nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
41
42 const gchar *history_chain_back()
43 {
44         nav_button = TRUE;
45
46         chain_index = chain_index > 0 ? chain_index - 1 : 0;
47
48         return g_list_nth_data(history_chain, chain_index);
49 }
50
51 const gchar *history_chain_forward()
52 {
53         nav_button= TRUE;
54         guint last = g_list_length(history_chain) - 1;
55
56         chain_index = chain_index < last ? chain_index + 1 : last;
57
58         return g_list_nth_data(history_chain, chain_index);
59 }
60
61 /**
62  * @brief Appends a path to the history chain
63  * @param path Path selected
64  * 
65  * Each time the user selects a new path it is appended to the chain
66  * except when it is identical to the current last entry
67  * The pointer is always moved to the end of the chain
68  */
69 void history_chain_append_end(const gchar *path)
70 {
71         GList *work;
72
73         if (!nav_button)
74                 {
75                 if(chain_index == G_MAXUINT)
76                         {
77                         history_chain = g_list_append (history_chain, g_strdup(path));
78                         chain_index = 0;
79                         }
80                 else
81                         {
82                         work = g_list_last(history_chain);
83                         if (g_strcmp0(work->data , path) != 0)
84                                 {
85                                 history_chain = g_list_append (history_chain, g_strdup(path));
86                                 chain_index = g_list_length(history_chain) - 1;
87                                 DEBUG_3("%d %s", chain_index, path);
88                                 }
89                         else
90                                 {
91                                 chain_index = g_list_length(history_chain) - 1;
92                                 }
93                         }
94                 }
95         else
96                 {
97                 nav_button = FALSE;
98                 }
99 }
100
101 /*
102  *-----------------------------------------------------------------------------
103  * history lists
104  *-----------------------------------------------------------------------------
105  */
106
107 #define HISTORY_DEFAULT_KEY_COUNT 16
108
109
110 typedef struct _HistoryData HistoryData;
111 struct _HistoryData
112 {
113         gchar *key;
114         GList *list;
115 };
116
117 static GList *history_list = NULL;
118
119
120 static gchar *quoted_from_text(const gchar *text)
121 {
122         const gchar *ptr;
123         gint c = 0;
124         gint l = strlen(text);
125
126         if (l == 0) return NULL;
127
128         while (c < l && text[c] !='"') c++;
129         if (text[c] == '"')
130                 {
131                 gint e;
132                 c++;
133                 ptr = text + c;
134                 e = c;
135                 while (e < l && text[e] !='"') e++;
136                 if (text[e] == '"')
137                         {
138                         if (e - c > 0)
139                                 {
140                                 return g_strndup(ptr, e - c);
141                                 }
142                         }
143                 }
144         return NULL;
145 }
146
147 gboolean history_list_load(const gchar *path)
148 {
149         FILE *f;
150         gchar *key = NULL;
151         gchar s_buf[1024];
152         gchar *pathl;
153
154         pathl = path_from_utf8(path);
155         f = fopen(pathl, "r");
156         g_free(pathl);
157         if (!f) return FALSE;
158
159         /* first line must start with History comment */
160         if (!fgets(s_buf, sizeof(s_buf), f) ||
161             strncmp(s_buf, "#History", 8) != 0)
162                 {
163                 fclose(f);
164                 return FALSE;
165                 }
166
167         while (fgets(s_buf, sizeof(s_buf), f))
168                 {
169                 if (s_buf[0]=='#') continue;
170                 if (s_buf[0]=='[')
171                         {
172                         gint c;
173                         gchar *ptr;
174
175                         ptr = s_buf + 1;
176                         c = 0;
177                         while (ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++;
178
179                         g_free(key);
180                         key = g_strndup(ptr, c);
181                         }
182                 else
183                         {
184                         gchar *value;
185
186                         value = quoted_from_text(s_buf);
187                         if (value && key)
188                                 {
189                                 history_list_add_to_key(key, value, 0);
190                                 }
191                         g_free(value);
192                         }
193                 }
194
195         fclose(f);
196
197         g_free(key);
198
199         return TRUE;
200 }
201
202 gboolean history_list_save(const gchar *path)
203 {
204         SecureSaveInfo *ssi;
205         GList *list;
206         gchar *pathl;
207         gint list_count;
208
209         pathl = path_from_utf8(path);
210         ssi = secure_open(pathl);
211         g_free(pathl);
212         if (!ssi)
213                 {
214                 log_printf(_("Unable to write history lists to: %s\n"), path);
215                 return FALSE;
216                 }
217
218         secure_fprintf(ssi, "#History lists\n\n");
219
220         list = g_list_last(history_list);
221         while (list && secsave_errno == SS_ERR_NONE)
222                 {
223                 HistoryData *hd;
224                 GList *work;
225
226                 hd = list->data;
227                 list = list->prev;
228
229                 secure_fprintf(ssi, "[%s]\n", hd->key);
230
231                 /* save them inverted (oldest to newest)
232                  * so that when reading they are added correctly
233                  */
234                 work = g_list_last(hd->list);
235                 list_count = g_list_position(hd->list, g_list_last(hd->list)) + 1;
236                 while (work && secsave_errno == SS_ERR_NONE)
237                         {
238                         if (!(strcmp(hd->key, "path_list") == 0 && list_count > options->open_recent_list_maxsize))
239                                 {
240                                 secure_fprintf(ssi, "\"%s\"\n", (gchar *)work->data);
241                                 }
242                         work = work->prev;
243                         list_count--;
244                         }
245                 secure_fputc(ssi, '\n');
246                 }
247
248         secure_fprintf(ssi, "#end\n");
249
250         return (secure_close(ssi) == 0);
251 }
252
253 static void history_list_free(HistoryData *hd)
254 {
255         GList *work;
256
257         if (!hd) return;
258
259         work = hd->list;
260         while (work)
261                 {
262                 g_free(work->data);
263                 work = work->next;
264                 }
265
266         g_free(hd->key);
267         g_free(hd);
268 }
269
270 static HistoryData *history_list_find_by_key(const gchar *key)
271 {
272         GList *work = history_list;
273
274         if (!key) return NULL;
275
276         while (work)
277                 {
278                 HistoryData *hd = work->data;
279                 if (strcmp(hd->key, key) == 0) return hd;
280                 work = work->next;
281                 }
282         return NULL;
283 }
284
285 const gchar *history_list_find_last_path_by_key(const gchar *key)
286 {
287         HistoryData *hd;
288
289         hd = history_list_find_by_key(key);
290         if (!hd || !hd->list) return NULL;
291
292         return hd->list->data;
293 }
294
295 void history_list_free_key(const gchar *key)
296 {
297         HistoryData *hd;
298         hd = history_list_find_by_key(key);
299         if (!hd) return;
300
301         history_list = g_list_remove(history_list, hd);
302         history_list_free(hd);
303 }
304
305 void history_list_add_to_key(const gchar *key, const gchar *path, gint max)
306 {
307         HistoryData *hd;
308         GList *work;
309
310         if (!key || !path) return;
311
312         hd = history_list_find_by_key(key);
313         if (!hd)
314                 {
315                 hd = g_new(HistoryData, 1);
316                 hd->key = g_strdup(key);
317                 hd->list = NULL;
318                 history_list = g_list_prepend(history_list, hd);
319                 }
320
321         /* if already in the list, simply move it to the top */
322         work = hd->list;
323         while (work)
324                 {
325                 gchar *buf = work->data;
326
327                 if (strcmp(buf, path) == 0)
328                         {
329                         /* if not first, move it */
330                         if (work != hd->list)
331                                 {
332                                 hd->list = g_list_remove(hd->list, buf);
333                                 hd->list = g_list_prepend(hd->list, buf);
334                                 }
335                         return;
336                         }
337                 work = work->next;
338                 }
339
340         hd->list = g_list_prepend(hd->list, g_strdup(path));
341
342         if (max == -1) max = HISTORY_DEFAULT_KEY_COUNT;
343         if (max > 0)
344                 {
345                 gint len = 0;
346                 GList *work = hd->list;
347                 GList *last = NULL;
348
349                 while (work)
350                         {
351                         len++;
352                         last = work;
353                         work = work->next;
354                         }
355
356                 work = last;
357                 while (work && len > max)
358                         {
359                         GList *node = work;
360                         work = work->prev;
361
362                         g_free(node->data);
363                         hd->list = g_list_delete_link(hd->list, node);
364                         len--;
365                         }
366                 }
367 }
368
369 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath)
370 {
371         HistoryData *hd;
372         GList *work;
373
374         if (!oldpath) return;
375         hd = history_list_find_by_key(key);
376         if (!hd) return;
377
378         work = hd->list;
379         while (work)
380                 {
381                 gchar *buf = work->data;
382                 if (strcmp(buf, oldpath) == 0)
383                         {
384                         if (newpath)
385                                 {
386                                 work->data = g_strdup(newpath);
387                                 }
388                         else
389                                 {
390                                 hd->list = g_list_remove(hd->list, buf);
391                                 }
392                         g_free(buf);
393                         return;
394                         }
395                 work = work->next;
396                 }
397 }
398
399 void history_list_item_move(const gchar *key, const gchar *path, gint direction)
400 {
401         HistoryData *hd;
402         GList *work;
403         gint p = 0;
404
405         if (!path) return;
406         hd = history_list_find_by_key(key);
407         if (!hd) return;
408
409         work = hd->list;
410         while (work)
411                 {
412                 gchar *buf = work->data;
413                 if (strcmp(buf, path) == 0)
414                         {
415                         p += direction;
416                         if (p < 0) return;
417                         hd->list = g_list_remove(hd->list, buf);
418                         hd->list = g_list_insert(hd->list, buf, p);
419                         return;
420                         }
421                 work = work->next;
422                 p++;
423                 }
424 }
425
426 void history_list_item_remove(const gchar *key, const gchar *path)
427 {
428         history_list_item_change(key, path, NULL);
429 }
430
431 GList *history_list_get_by_key(const gchar *key)
432 {
433         HistoryData *hd;
434
435         hd = history_list_find_by_key(key);
436         if (!hd) return NULL;
437
438         return hd->list;
439 }
440 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */