Fix #753: Various cleanups based on gcc warnings
[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  * Implements an image history chain. Whenever an image is displayed it is
104  * appended to a chain.
105  * Pressing the Image Back and Image Forward buttons moves along the chain,
106  * but does not make additions to the chain.
107  * The chain always increases and is deleted at the end of the session
108  *-----------------------------------------------------------------------------
109  */
110 static GList *image_chain = NULL;
111 static guint image_chain_index = G_MAXUINT;
112 static gboolean image_nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
113 const gchar *image_chain_back()
114 {
115         image_nav_button = TRUE;
116
117         image_chain_index = image_chain_index > 0 ? image_chain_index - 1 : 0;
118
119         return g_list_nth_data(image_chain, image_chain_index);
120 }
121
122 const gchar *image_chain_forward()
123 {
124         image_nav_button= TRUE;
125         guint last = g_list_length(image_chain) - 1;
126
127         image_chain_index = image_chain_index < last ? image_chain_index + 1 : last;
128
129         return g_list_nth_data(image_chain, image_chain_index);
130 }
131
132 /**
133  * @brief Appends a path to the image history chain
134  * @param path Image path selected
135  * 
136  * Each time the user selects a new image it is appended to the chain
137  * except when it is identical to the current last entry
138  * The pointer is always moved to the end of the chain
139  */
140 void image_chain_append_end(const gchar *path)
141 {
142         GList *work;
143
144         if (!image_nav_button)
145                 {
146                 if(image_chain_index == G_MAXUINT)
147                         {
148                         image_chain = g_list_append(image_chain, g_strdup(path));
149                         image_chain_index = 0;
150                         }
151                 else
152                         {
153                         work = g_list_last(image_chain);
154                         if (g_strcmp0(work->data , path) != 0)
155                                 {
156                                 image_chain = g_list_append(image_chain, g_strdup(path));
157                                 image_chain_index = g_list_length(image_chain) - 1;
158                                 DEBUG_3("%d %s", image_chain_index, path);
159                                 }
160                         else
161                                 {
162                                 image_chain_index = g_list_length(image_chain) - 1;
163                                 }
164                         }
165                 }
166         else
167                 {
168                 image_nav_button = FALSE;
169                 }
170 }
171
172 /*
173  *-----------------------------------------------------------------------------
174  * history lists
175  *-----------------------------------------------------------------------------
176  */
177
178 #define HISTORY_DEFAULT_KEY_COUNT 16
179
180
181 typedef struct _HistoryData HistoryData;
182 struct _HistoryData
183 {
184         gchar *key;
185         GList *list;
186 };
187
188 static GList *history_list = NULL;
189
190
191 static gchar *quoted_from_text(const gchar *text)
192 {
193         const gchar *ptr;
194         gint c = 0;
195         gint l = strlen(text);
196
197         if (l == 0) return NULL;
198
199         while (c < l && text[c] !='"') c++;
200         if (text[c] == '"')
201                 {
202                 gint e;
203                 c++;
204                 ptr = text + c;
205                 e = c;
206                 while (e < l && text[e] !='"') e++;
207                 if (text[e] == '"')
208                         {
209                         if (e - c > 0)
210                                 {
211                                 return g_strndup(ptr, e - c);
212                                 }
213                         }
214                 }
215         return NULL;
216 }
217
218 gboolean history_list_load(const gchar *path)
219 {
220         FILE *f;
221         gchar *key = NULL;
222         gchar s_buf[1024];
223         gchar *pathl;
224
225         pathl = path_from_utf8(path);
226         f = fopen(pathl, "r");
227         g_free(pathl);
228         if (!f) return FALSE;
229
230         /* first line must start with History comment */
231         if (!fgets(s_buf, sizeof(s_buf), f) ||
232             strncmp(s_buf, "#History", 8) != 0)
233                 {
234                 fclose(f);
235                 return FALSE;
236                 }
237
238         while (fgets(s_buf, sizeof(s_buf), f))
239                 {
240                 if (s_buf[0]=='#') continue;
241                 if (s_buf[0]=='[')
242                         {
243                         gint c;
244                         gchar *ptr;
245
246                         ptr = s_buf + 1;
247                         c = 0;
248                         while (ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++;
249
250                         g_free(key);
251                         key = g_strndup(ptr, c);
252                         }
253                 else
254                         {
255                         gchar *value;
256
257                         value = quoted_from_text(s_buf);
258                         if (value && key)
259                                 {
260                                 history_list_add_to_key(key, value, 0);
261                                 }
262                         g_free(value);
263                         }
264                 }
265
266         fclose(f);
267
268         g_free(key);
269
270         return TRUE;
271 }
272
273 gboolean history_list_save(const gchar *path)
274 {
275         SecureSaveInfo *ssi;
276         GList *list;
277         gchar *pathl;
278         gint list_count;
279
280         pathl = path_from_utf8(path);
281         ssi = secure_open(pathl);
282         g_free(pathl);
283         if (!ssi)
284                 {
285                 log_printf(_("Unable to write history lists to: %s\n"), path);
286                 return FALSE;
287                 }
288
289         secure_fprintf(ssi, "#History lists\n\n");
290
291         list = g_list_last(history_list);
292         while (list && secsave_errno == SS_ERR_NONE)
293                 {
294                 HistoryData *hd;
295                 GList *work;
296
297                 hd = list->data;
298                 list = list->prev;
299
300                 secure_fprintf(ssi, "[%s]\n", hd->key);
301
302                 /* save them inverted (oldest to newest)
303                  * so that when reading they are added correctly
304                  */
305                 work = g_list_last(hd->list);
306                 list_count = g_list_position(hd->list, g_list_last(hd->list)) + 1;
307                 while (work && secsave_errno == SS_ERR_NONE)
308                         {
309                         if ((!(strcmp(hd->key, "path_list") == 0 && list_count > options->open_recent_list_maxsize)) && (!(strcmp(hd->key, "recent") == 0 && (!isfile(work->data)))))
310                                 {
311                                 secure_fprintf(ssi, "\"%s\"\n", (gchar *)work->data);
312                                 }
313                         work = work->prev;
314                         list_count--;
315                         }
316                 secure_fputc(ssi, '\n');
317                 }
318
319         secure_fprintf(ssi, "#end\n");
320
321         return (secure_close(ssi) == 0);
322 }
323
324 static void history_list_free(HistoryData *hd)
325 {
326         GList *work;
327
328         if (!hd) return;
329
330         work = hd->list;
331         while (work)
332                 {
333                 g_free(work->data);
334                 work = work->next;
335                 }
336
337         g_free(hd->key);
338         g_free(hd);
339 }
340
341 static HistoryData *history_list_find_by_key(const gchar *key)
342 {
343         GList *work = history_list;
344
345         if (!key) return NULL;
346
347         while (work)
348                 {
349                 HistoryData *hd = work->data;
350                 if (strcmp(hd->key, key) == 0) return hd;
351                 work = work->next;
352                 }
353         return NULL;
354 }
355
356 const gchar *history_list_find_last_path_by_key(const gchar *key)
357 {
358         HistoryData *hd;
359
360         hd = history_list_find_by_key(key);
361         if (!hd || !hd->list) return NULL;
362
363         return hd->list->data;
364 }
365
366 void history_list_free_key(const gchar *key)
367 {
368         HistoryData *hd;
369         hd = history_list_find_by_key(key);
370         if (!hd) return;
371
372         history_list = g_list_remove(history_list, hd);
373         history_list_free(hd);
374 }
375
376 void history_list_add_to_key(const gchar *key, const gchar *path, gint max)
377 {
378         HistoryData *hd;
379         GList *work;
380
381         if (!key || !path) return;
382
383         hd = history_list_find_by_key(key);
384         if (!hd)
385                 {
386                 hd = g_new(HistoryData, 1);
387                 hd->key = g_strdup(key);
388                 hd->list = NULL;
389                 history_list = g_list_prepend(history_list, hd);
390                 }
391
392         /* if already in the list, simply move it to the top */
393         work = hd->list;
394         while (work)
395                 {
396                 gchar *buf = work->data;
397
398                 if (strcmp(buf, path) == 0)
399                         {
400                         /* if not first, move it */
401                         if (work != hd->list)
402                                 {
403                                 hd->list = g_list_remove(hd->list, buf);
404                                 hd->list = g_list_prepend(hd->list, buf);
405                                 }
406                         return;
407                         }
408                 work = work->next;
409                 }
410
411         hd->list = g_list_prepend(hd->list, g_strdup(path));
412
413         if (max == -1) max = HISTORY_DEFAULT_KEY_COUNT;
414         if (max > 0)
415                 {
416                 gint len = 0;
417                 GList *work = hd->list;
418                 GList *last = NULL;
419
420                 while (work)
421                         {
422                         len++;
423                         last = work;
424                         work = work->next;
425                         }
426
427                 work = last;
428                 while (work && len > max)
429                         {
430                         GList *node = work;
431                         work = work->prev;
432
433                         g_free(node->data);
434                         hd->list = g_list_delete_link(hd->list, node);
435                         len--;
436                         }
437                 }
438 }
439
440 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath)
441 {
442         HistoryData *hd;
443         GList *work;
444
445         if (!oldpath) return;
446         hd = history_list_find_by_key(key);
447         if (!hd) return;
448
449         work = hd->list;
450         while (work)
451                 {
452                 gchar *buf = work->data;
453                 if (strcmp(buf, oldpath) == 0)
454                         {
455                         if (newpath)
456                                 {
457                                 work->data = g_strdup(newpath);
458                                 }
459                         else
460                                 {
461                                 hd->list = g_list_remove(hd->list, buf);
462                                 }
463                         g_free(buf);
464                         return;
465                         }
466                 work = work->next;
467                 }
468 }
469
470 void history_list_item_move(const gchar *key, const gchar *path, gint direction)
471 {
472         HistoryData *hd;
473         GList *work;
474         gint p = 0;
475
476         if (!path) return;
477         hd = history_list_find_by_key(key);
478         if (!hd) return;
479
480         work = hd->list;
481         while (work)
482                 {
483                 gchar *buf = work->data;
484                 if (strcmp(buf, path) == 0)
485                         {
486                         p += direction;
487                         if (p < 0) return;
488                         hd->list = g_list_remove(hd->list, buf);
489                         hd->list = g_list_insert(hd->list, buf, p);
490                         return;
491                         }
492                 work = work->next;
493                 p++;
494                 }
495 }
496
497 void history_list_item_remove(const gchar *key, const gchar *path)
498 {
499         history_list_item_change(key, path, NULL);
500 }
501
502 GList *history_list_get_by_key(const gchar *key)
503 {
504         HistoryData *hd;
505
506         hd = history_list_find_by_key(key);
507         if (!hd) return NULL;
508
509         return hd->list;
510 }
511 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */