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.
21 #include "history-list.h"
29 #include "secure-save.h"
30 #include "ui-fileops.h"
35 gint dirname_compare(gconstpointer data, gconstpointer user_data)
37 gchar *dirname = g_path_get_dirname(static_cast<const gchar *>(data));
38 int result = g_strcmp0(dirname, static_cast<const gchar *>(user_data));
45 static void update_recent_viewed_folder_image_list(const gchar *path);
49 *-----------------------------------------------------------------------------
50 * Implements a history chain. Used by the Back and Forward toolbar buttons.
51 * Selecting any folder appends the path to the end of the chain.
52 * Pressing the Back and Forward buttons moves along the chain, but does
53 * not make additions to the chain.
54 * The chain always increases and is deleted at the end of the session
56 *-----------------------------------------------------------------------------
59 static GList *history_chain = nullptr;
60 static guint chain_index = G_MAXUINT;
61 static gboolean nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
63 const gchar *history_chain_back()
67 chain_index = chain_index > 0 ? chain_index - 1 : 0;
69 return static_cast<const gchar *>(g_list_nth_data(history_chain, chain_index));
72 const gchar *history_chain_forward()
75 guint last = g_list_length(history_chain) - 1;
77 chain_index = chain_index < last ? chain_index + 1 : last;
79 return static_cast<const gchar *>(g_list_nth_data(history_chain, chain_index));
83 * @brief Appends a path to the history chain
84 * @param path Path selected
86 * Each time the user selects a new path it is appended to the chain
87 * except when it is identical to the current last entry
88 * The pointer is always moved to the end of the chain
90 void history_chain_append_end(const gchar *path)
96 if(chain_index == G_MAXUINT)
98 history_chain = g_list_append (history_chain, g_strdup(path));
103 work = g_list_last(history_chain);
104 if (g_strcmp0(static_cast<const gchar *>(work->data), path) != 0)
106 history_chain = g_list_append (history_chain, g_strdup(path));
107 chain_index = g_list_length(history_chain) - 1;
108 DEBUG_3("%d %s", chain_index, path);
112 chain_index = g_list_length(history_chain) - 1;
124 *-----------------------------------------------------------------------------
125 * Implements an image history chain. Whenever an image is displayed it is
126 * appended to a chain.
127 * Pressing the Image Back and Image Forward buttons moves along the chain,
128 * but does not make additions to the chain.
129 * The chain always increases and is deleted at the end of the session
131 *-----------------------------------------------------------------------------
133 static GList *image_chain = nullptr;
134 static guint image_chain_index = G_MAXUINT;
135 static gboolean image_nav_button = FALSE; /** Used to prevent the nav buttons making entries to the chain **/
136 const gchar *image_chain_back()
138 image_nav_button = TRUE;
140 image_chain_index = image_chain_index > 0 ? image_chain_index - 1 : 0;
142 return static_cast<const gchar *>(g_list_nth_data(image_chain, image_chain_index));
145 const gchar *image_chain_forward()
147 image_nav_button= TRUE;
148 guint last = g_list_length(image_chain) - 1;
150 image_chain_index = image_chain_index < last ? image_chain_index + 1 : last;
152 return static_cast<const gchar *>(g_list_nth_data(image_chain, image_chain_index));
156 * @brief Appends a path to the image history chain
157 * @param path Image path selected
159 * Each time the user selects a new image it is appended to the chain
160 * except when it is identical to the current last entry
161 * The pointer is always moved to the end of the chain
163 * Updates the recent viewed image_list
165 void image_chain_append_end(const gchar *path)
169 if (!image_nav_button)
171 if(image_chain_index == G_MAXUINT)
173 image_chain = g_list_append(image_chain, g_strdup(path));
174 image_chain_index = 0;
178 work = g_list_last(image_chain);
179 if (g_strcmp0(static_cast<const gchar *>(work->data) , path) != 0)
181 image_chain = g_list_append(image_chain, g_strdup(path));
182 image_chain_index = g_list_length(image_chain) - 1;
183 DEBUG_3("%d %s", image_chain_index, path);
187 image_chain_index = g_list_length(image_chain) - 1;
191 update_recent_viewed_folder_image_list(path);
195 image_nav_button = FALSE;
200 *-----------------------------------------------------------------------------
202 *-----------------------------------------------------------------------------
211 static GList *history_list = nullptr;
214 static gchar *quoted_from_text(const gchar *text)
218 gint l = strlen(text);
220 if (l == 0) return nullptr;
222 while (c < l && text[c] !='"') c++;
229 while (e < l && text[e] !='"') e++;
234 return g_strndup(ptr, e - c);
241 gboolean history_list_load(const gchar *path)
244 gchar *key = nullptr;
248 pathl = path_from_utf8(path);
249 f = fopen(pathl, "r");
251 if (!f) return FALSE;
253 /* first line must start with History comment */
254 if (!fgets(s_buf, sizeof(s_buf), f) ||
255 strncmp(s_buf, "#History", 8) != 0)
261 while (fgets(s_buf, sizeof(s_buf), f))
263 if (s_buf[0]=='#') continue;
271 while (ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++;
274 key = g_strndup(ptr, c);
280 value = quoted_from_text(s_buf);
283 history_list_add_to_key(key, value, 0);
296 gboolean history_list_save(const gchar *path)
303 pathl = path_from_utf8(path);
304 ssi = secure_open(pathl);
308 log_printf(_("Unable to write history lists to: %s\n"), path);
312 secure_fprintf(ssi, "#History lists\n\n");
314 list = g_list_last(history_list);
315 while (list && secsave_errno == SS_ERR_NONE)
320 hd = static_cast<HistoryData *>(list->data);
323 secure_fprintf(ssi, "[%s]\n", hd->key);
325 /* save them inverted (oldest to newest)
326 * so that when reading they are added correctly
328 work = g_list_last(hd->list);
329 list_count = g_list_position(hd->list, g_list_last(hd->list)) + 1;
330 while (work && secsave_errno == SS_ERR_NONE)
332 if ((strcmp(hd->key, "path_list") != 0 || list_count <= options->open_recent_list_maxsize)
334 (strcmp(hd->key, "recent") != 0 || !(!isfile(static_cast<const gchar *>(work->data))))
336 (strcmp(hd->key, "image_list") != 0 || list_count <= options->recent_folder_image_list_maxsize))
338 secure_fprintf(ssi, "\"%s\"\n", static_cast<gchar *>(work->data));
343 secure_fputc(ssi, '\n');
346 secure_fprintf(ssi, "#end\n");
348 return (secure_close(ssi) == 0);
351 static void history_list_free(HistoryData *hd)
356 g_list_free_full(hd->list, g_free);
360 static HistoryData *history_list_find_by_key(const gchar *key)
362 GList *work = history_list;
364 if (!key) return nullptr;
366 work = g_list_find_custom(history_list, key, [](gconstpointer data, gconstpointer user_data)
368 auto *hd = static_cast<const HistoryData *>(data);
369 return strcmp(hd->key, static_cast<const gchar *>(user_data));
374 return static_cast<HistoryData *>(work->data);
380 const gchar *history_list_find_last_path_by_key(const gchar *key)
384 hd = history_list_find_by_key(key);
385 if (!hd || !hd->list) return nullptr;
387 return static_cast<const gchar *>(hd->list->data);
390 void history_list_free_key(const gchar *key)
393 hd = history_list_find_by_key(key);
396 history_list = g_list_remove(history_list, hd);
397 history_list_free(hd);
400 void history_list_add_to_key(const gchar *key, const gchar *path, gint max)
405 if (!key || !path) return;
407 hd = history_list_find_by_key(key);
410 hd = g_new(HistoryData, 1);
411 hd->key = g_strdup(key);
413 history_list = g_list_prepend(history_list, hd);
416 /* if already in the list, simply move it to the top */
417 work = g_list_find_custom(hd->list, path, reinterpret_cast<GCompareFunc>(strcmp));
420 /* if not first, move it */
421 if (work != hd->list)
423 auto buf = static_cast<gchar *>(work->data);
425 hd->list = g_list_remove(hd->list, buf);
426 hd->list = g_list_prepend(hd->list, buf);
431 hd->list = g_list_prepend(hd->list, g_strdup(path));
433 if (max == -1) max = options->open_recent_list_maxsize;
436 work = g_list_nth(hd->list, max);
439 GList *last = work->prev;
442 last->next = nullptr;
445 work->prev = nullptr;
446 g_list_free_full(work, g_free);
451 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath)
456 if (!oldpath) return;
457 hd = history_list_find_by_key(key);
463 auto buf = static_cast<gchar *>(work->data);
465 if (!g_str_has_prefix(buf, ".") || newpath)
467 if (strcmp(buf, oldpath) == 0)
471 work->data = g_strdup(newpath);
475 hd->list = g_list_remove(hd->list, buf);
483 hd->list = g_list_remove(hd->list, buf);
491 void history_list_item_move(const gchar *key, const gchar *path, gint direction)
497 hd = history_list_find_by_key(key);
500 work = g_list_find_custom(hd->list, path, reinterpret_cast<GCompareFunc>(strcmp));
503 gint p = g_list_position(hd->list, work) + direction;
506 auto buf = static_cast<gchar *>(work->data);
507 hd->list = g_list_remove(hd->list, buf);
508 hd->list = g_list_insert(hd->list, buf, p);
511 void history_list_item_remove(const gchar *key, const gchar *path)
513 history_list_item_change(key, path, nullptr);
516 GList *history_list_get_by_key(const gchar *key)
520 hd = history_list_find_by_key(key);
521 if (!hd) return nullptr;
527 * @brief Get image last viewed in a folder
528 * @param path Must be a folder
529 * @returns Last image viewed in folder or NULL
531 * Returned string should be freed
533 gchar *get_recent_viewed_folder_image(gchar *path)
538 if (options->recent_folder_image_list_maxsize == 0)
543 hd = history_list_find_by_key("image_list");
547 hd = g_new(HistoryData, 1);
548 hd->key = g_strdup("image_list");
550 history_list = g_list_prepend(history_list, hd);
553 work = g_list_find_custom(hd->list, path, dirname_compare);
554 if (!work || !isfile(static_cast<const gchar *>(work->data)))
559 return g_strdup(static_cast<const gchar *>(work->data));
562 static void update_recent_viewed_folder_image_list(const gchar *path)
566 gchar *image_dir = nullptr;
568 if (options->recent_folder_image_list_maxsize == 0)
573 hd = history_list_find_by_key("image_list");
576 hd = g_new(HistoryData, 1);
577 hd->key = g_strdup("image_list");
579 history_list = g_list_prepend(history_list, hd);
582 image_dir = g_path_get_dirname(path);
583 work = g_list_find_custom(hd->list, image_dir, dirname_compare);
589 work->data = g_strdup(path);
590 hd->list = g_list_remove_link(hd->list, work);
591 hd->list = g_list_concat(work, hd->list);
595 hd->list = g_list_prepend(hd->list, g_strdup(path));
598 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */