2 * Copyright (C) 2008 - 2016 The Geeqie Team
4 * Authors: John Ellis, Vladimir Nadvornik, Laurent Monin
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.
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.
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.
22 #include "history-list.h"
24 #include "secure-save.h"
25 #include "ui-fileops.h"
27 static void update_recent_viewed_folder_image_list(const gchar *path);
31 *-----------------------------------------------------------------------------
32 * Implements a history chain. Used by the Back and Forward toolbar buttons.
33 * Selecting any folder appends the path to the end of the chain.
34 * Pressing the Back and Forward buttons moves along the chain, but does
35 * not make additions to the chain.
36 * The chain always increases and is deleted at the end of the session
38 *-----------------------------------------------------------------------------
41 static GList *history_chain = nullptr;
42 static guint chain_index = G_MAXUINT;
43 static gboolean nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
45 const gchar *history_chain_back()
49 chain_index = chain_index > 0 ? chain_index - 1 : 0;
51 return static_cast<const gchar *>(g_list_nth_data(history_chain, chain_index));
54 const gchar *history_chain_forward()
57 guint last = g_list_length(history_chain) - 1;
59 chain_index = chain_index < last ? chain_index + 1 : last;
61 return static_cast<const gchar *>(g_list_nth_data(history_chain, chain_index));
65 * @brief Appends a path to the history chain
66 * @param path Path selected
68 * Each time the user selects a new path it is appended to the chain
69 * except when it is identical to the current last entry
70 * The pointer is always moved to the end of the chain
72 void history_chain_append_end(const gchar *path)
78 if(chain_index == G_MAXUINT)
80 history_chain = g_list_append (history_chain, g_strdup(path));
85 work = g_list_last(history_chain);
86 if (g_strcmp0(static_cast<const gchar *>(work->data), path) != 0)
88 history_chain = g_list_append (history_chain, g_strdup(path));
89 chain_index = g_list_length(history_chain) - 1;
90 DEBUG_3("%d %s", chain_index, path);
94 chain_index = g_list_length(history_chain) - 1;
106 *-----------------------------------------------------------------------------
107 * Implements an image history chain. Whenever an image is displayed it is
108 * appended to a chain.
109 * Pressing the Image Back and Image Forward buttons moves along the chain,
110 * but does not make additions to the chain.
111 * The chain always increases and is deleted at the end of the session
113 *-----------------------------------------------------------------------------
115 static GList *image_chain = nullptr;
116 static guint image_chain_index = G_MAXUINT;
117 static gboolean image_nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
118 const gchar *image_chain_back()
120 image_nav_button = TRUE;
122 image_chain_index = image_chain_index > 0 ? image_chain_index - 1 : 0;
124 return static_cast<const gchar *>(g_list_nth_data(image_chain, image_chain_index));
127 const gchar *image_chain_forward()
129 image_nav_button= TRUE;
130 guint last = g_list_length(image_chain) - 1;
132 image_chain_index = image_chain_index < last ? image_chain_index + 1 : last;
134 return static_cast<const gchar *>(g_list_nth_data(image_chain, image_chain_index));
138 * @brief Appends a path to the image history chain
139 * @param path Image path selected
141 * Each time the user selects a new image it is appended to the chain
142 * except when it is identical to the current last entry
143 * The pointer is always moved to the end of the chain
145 * Updates the recent viewed image_list
147 void image_chain_append_end(const gchar *path)
151 if (!image_nav_button)
153 if(image_chain_index == G_MAXUINT)
155 image_chain = g_list_append(image_chain, g_strdup(path));
156 image_chain_index = 0;
160 work = g_list_last(image_chain);
161 if (g_strcmp0(static_cast<const gchar *>(work->data) , path) != 0)
163 image_chain = g_list_append(image_chain, g_strdup(path));
164 image_chain_index = g_list_length(image_chain) - 1;
165 DEBUG_3("%d %s", image_chain_index, path);
169 image_chain_index = g_list_length(image_chain) - 1;
173 update_recent_viewed_folder_image_list(path);
177 image_nav_button = FALSE;
182 *-----------------------------------------------------------------------------
184 *-----------------------------------------------------------------------------
193 static GList *history_list = nullptr;
196 static gchar *quoted_from_text(const gchar *text)
200 gint l = strlen(text);
202 if (l == 0) return nullptr;
204 while (c < l && text[c] !='"') c++;
211 while (e < l && text[e] !='"') e++;
216 return g_strndup(ptr, e - c);
223 gboolean history_list_load(const gchar *path)
226 gchar *key = nullptr;
230 pathl = path_from_utf8(path);
231 f = fopen(pathl, "r");
233 if (!f) return FALSE;
235 /* first line must start with History comment */
236 if (!fgets(s_buf, sizeof(s_buf), f) ||
237 strncmp(s_buf, "#History", 8) != 0)
243 while (fgets(s_buf, sizeof(s_buf), f))
245 if (s_buf[0]=='#') continue;
253 while (ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++;
256 key = g_strndup(ptr, c);
262 value = quoted_from_text(s_buf);
265 history_list_add_to_key(key, value, 0);
278 gboolean history_list_save(const gchar *path)
285 pathl = path_from_utf8(path);
286 ssi = secure_open(pathl);
290 log_printf(_("Unable to write history lists to: %s\n"), path);
294 secure_fprintf(ssi, "#History lists\n\n");
296 list = g_list_last(history_list);
297 while (list && secsave_errno == SS_ERR_NONE)
302 hd = static_cast<HistoryData *>(list->data);
305 secure_fprintf(ssi, "[%s]\n", hd->key);
307 /* save them inverted (oldest to newest)
308 * so that when reading they are added correctly
310 work = g_list_last(hd->list);
311 list_count = g_list_position(hd->list, g_list_last(hd->list)) + 1;
312 while (work && secsave_errno == SS_ERR_NONE)
314 if ((strcmp(hd->key, "path_list") != 0 || list_count <= options->open_recent_list_maxsize)
316 (strcmp(hd->key, "recent") != 0 || !(!isfile(static_cast<const gchar *>(work->data))))
318 (strcmp(hd->key, "image_list") != 0 || list_count <= options->recent_folder_image_list_maxsize))
320 secure_fprintf(ssi, "\"%s\"\n", static_cast<gchar *>(work->data));
325 secure_fputc(ssi, '\n');
328 secure_fprintf(ssi, "#end\n");
330 return (secure_close(ssi) == 0);
333 static void history_list_free(HistoryData *hd)
350 static HistoryData *history_list_find_by_key(const gchar *key)
352 GList *work = history_list;
354 if (!key) return nullptr;
358 auto hd = static_cast<HistoryData *>(work->data);
359 if (strcmp(hd->key, key) == 0) return hd;
365 const gchar *history_list_find_last_path_by_key(const gchar *key)
369 hd = history_list_find_by_key(key);
370 if (!hd || !hd->list) return nullptr;
372 return static_cast<const gchar *>(hd->list->data);
375 void history_list_free_key(const gchar *key)
378 hd = history_list_find_by_key(key);
381 history_list = g_list_remove(history_list, hd);
382 history_list_free(hd);
385 void history_list_add_to_key(const gchar *key, const gchar *path, gint max)
390 if (!key || !path) return;
392 hd = history_list_find_by_key(key);
395 hd = g_new(HistoryData, 1);
396 hd->key = g_strdup(key);
398 history_list = g_list_prepend(history_list, hd);
401 /* if already in the list, simply move it to the top */
405 auto buf = static_cast<gchar *>(work->data);
407 if (strcmp(buf, path) == 0)
409 /* if not first, move it */
410 if (work != hd->list)
412 hd->list = g_list_remove(hd->list, buf);
413 hd->list = g_list_prepend(hd->list, buf);
420 hd->list = g_list_prepend(hd->list, g_strdup(path));
422 if (max == -1) max = options->open_recent_list_maxsize;
426 GList *work = hd->list;
427 GList *last = nullptr;
437 while (work && len > max)
443 hd->list = g_list_delete_link(hd->list, node);
449 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath)
454 if (!oldpath) return;
455 hd = history_list_find_by_key(key);
461 auto buf = static_cast<gchar *>(work->data);
463 if (!(g_str_has_prefix(buf, ".") && !newpath))
465 if (strcmp(buf, oldpath) == 0)
469 work->data = g_strdup(newpath);
473 hd->list = g_list_remove(hd->list, buf);
481 hd->list = g_list_remove(hd->list, buf);
489 void history_list_item_move(const gchar *key, const gchar *path, gint direction)
496 hd = history_list_find_by_key(key);
502 auto buf = static_cast<gchar *>(work->data);
503 if (strcmp(buf, path) == 0)
507 hd->list = g_list_remove(hd->list, buf);
508 hd->list = g_list_insert(hd->list, buf, p);
516 void history_list_item_remove(const gchar *key, const gchar *path)
518 history_list_item_change(key, path, nullptr);
521 GList *history_list_get_by_key(const gchar *key)
525 hd = history_list_find_by_key(key);
526 if (!hd) return nullptr;
532 * @brief Get image last viewed in a folder
533 * @param path Must be a folder
534 * @returns Last image viewed in folder or NULL
536 * Returned string should be freed
538 gchar *get_recent_viewed_folder_image(gchar *path)
543 gchar *ret = nullptr;
545 if (options->recent_folder_image_list_maxsize == 0)
550 hd = history_list_find_by_key("image_list");
554 hd = g_new(HistoryData, 1);
555 hd->key = g_strdup("image_list");
557 history_list = g_list_prepend(history_list, hd);
564 dirname = g_path_get_dirname(static_cast<const gchar *>(work->data));
566 if (g_strcmp0(dirname, path) == 0)
568 if (isfile(static_cast<const gchar *>(work->data)))
570 ret = g_strdup(static_cast<const gchar *>(work->data));
583 static void update_recent_viewed_folder_image_list(const gchar *path)
587 gchar *image_dir = nullptr;
588 gchar *list_dir = nullptr;
589 gboolean found = FALSE;
591 if (options->recent_folder_image_list_maxsize == 0)
596 image_dir = g_path_get_dirname(path);
597 hd = history_list_find_by_key("image_list");
600 hd = g_new(HistoryData, 1);
601 hd->key = g_strdup("image_list");
603 history_list = g_list_prepend(history_list, hd);
610 list_dir = g_path_get_dirname(static_cast<const gchar *>(work->data));
612 /* If folder already in list, update and move to start of list */
613 if (g_strcmp0(list_dir, image_dir) == 0)
616 work->data = g_strdup(path);
617 hd->list = g_list_remove_link(hd->list, work);
618 hd->list = g_list_concat(work, hd->list);
631 hd->list = g_list_prepend(hd->list, g_strdup(path));
634 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */