Include a Other Software section in Help file
[geeqie.git] / src / collect.c
index 2287d12..2d4ed65 100644 (file)
@@ -30,6 +30,7 @@
 #include "img-view.h"
 #include "layout.h"
 #include "layout_image.h"
+#include "layout_util.h"
 #include "misc.h"
 #include "pixbuf_util.h"
 #include "print.h"
 #define COLLECT_DEF_WIDTH 440
 #define COLLECT_DEF_HEIGHT 450
 
+/**
+ *  list of paths to collections */
+
+/**
+ * @brief  List of currently open Collections.
+ * 
+ * Type ::_CollectionData 
+ */
 static GList *collection_list = NULL;
+
+/**
+ * @brief  List of currently open Collection windows.
+ * 
+ * Type ::_CollectWindow
+ */
 static GList *collection_window_list = NULL;
 
 static void collection_window_get_geometry(CollectWindow *cw);
@@ -154,9 +169,25 @@ static gint collection_list_sort_cb(gconstpointer a, gconstpointer b)
                        if (cia->fd->cdate > cib->fd->cdate) return 1;
                        return 0;
                        break;
+               case SORT_EXIFTIME:
+                       if (cia->fd->exifdate < cib->fd->exifdate) return -1;
+                       if (cia->fd->exifdate > cib->fd->exifdate) return 1;
+                       break;
+               case SORT_EXIFTIMEDIGITIZED:
+                       if (cia->fd->exifdate_digitized < cib->fd->exifdate_digitized) return -1;
+                       if (cia->fd->exifdate_digitized > cib->fd->exifdate_digitized) return 1;
+                       break;
+               case SORT_RATING:
+                       if (cia->fd->rating < cib->fd->rating) return -1;
+                       if (cia->fd->rating > cib->fd->rating) return 1;
+                       break;
                case SORT_PATH:
                        return utf8_compare(cia->fd->path, cib->fd->path, options->file_sort.case_sensitive);
                        break;
+               case SORT_CLASS:
+                       if (cia->fd->format_class < cib->fd->format_class) return -1;
+                       if (cia->fd->format_class > cib->fd->format_class) return 1;
+                       break;
 #ifdef HAVE_STRVERSCMP
                case SORT_NUMBER:
                        return strverscmp(cia->fd->name, cib->fd->name);
@@ -307,6 +338,129 @@ CollectWindow *collection_window_find_by_path(const gchar *path)
        return NULL;
 }
 
+/**
+ * @brief Checks string for existence of Collection.
+ * @param[in] param Filename, with or without extension of any collection
+ * @returns full pathname if found or NULL
+ * 
+ * Return value must be freed with g_free()
+ */
+gchar *collection_path(const gchar *param)
+{
+       gchar *path = NULL;
+       gchar *full_name = NULL;
+
+       if (file_extension_match(param, GQ_COLLECTION_EXT))
+               {
+               path = g_build_filename(get_collections_dir(), param, NULL);
+               }
+       else if (file_extension_match(param, NULL))
+               {
+               full_name = g_strconcat(param, GQ_COLLECTION_EXT, NULL);
+               path = g_build_filename(get_collections_dir(), full_name, NULL);
+               }
+
+       if (!isfile(path))
+               {
+               g_free(path);
+               path = NULL;
+               }
+
+       g_free(full_name);
+       return path;
+}
+
+/**
+ * @brief Checks input string for existence of Collection.
+ * @param[in] param Filename with or without extension of any collection
+ * @returns TRUE if found
+ * 
+ * 
+ */
+gboolean is_collection(const gchar *param)
+{
+       gchar *name = NULL;
+
+       name = collection_path(param);
+       if (name)
+               {
+               g_free(name);
+               return TRUE;
+               }
+       return FALSE;
+}
+
+/**
+ * @brief Creates a text list of the image paths of the contents of a Collection
+ * @param[in] name The name of the collection, with or without extension
+ * @param[inout] contents A GString to which the image paths are appended
+ * 
+ * 
+ */
+void collection_contents(const gchar *name, GString **contents)
+{
+       gchar *path;
+       CollectionData *cd;
+       CollectInfo *ci;
+       GList *work;
+       FileData *fd;
+
+       if (is_collection(name))
+               {
+               path = collection_path(name);
+               cd = collection_new("");
+               collection_load(cd, path, COLLECTION_LOAD_APPEND);
+               work = cd->list;
+               while (work)
+                       {
+                       ci = work->data;
+                       fd = ci->fd;
+                       *contents = g_string_append(*contents, g_strdup(fd->path));
+                       *contents = g_string_append(*contents, "\n");
+
+                       work = work->next;
+                       }
+               g_free(path);
+               collection_free(cd);
+               }
+}
+
+/**
+ * @brief Returns a list of filedatas of the contents of a Collection
+ * @param[in] name The name of the collection, with or without extension
+ * 
+ * 
+ */
+GList *collection_contents_fd(const gchar *name)
+{
+       gchar *path;
+       CollectionData *cd;
+       CollectInfo *ci;
+       GList *work;
+       FileData *fd;
+       GList *list = NULL;
+
+       if (is_collection(name))
+               {
+               path = collection_path(name);
+               cd = collection_new("");
+               collection_load(cd, path, COLLECTION_LOAD_APPEND);
+               work = cd->list;
+               while (work)
+                       {
+                       ci = work->data;
+                       fd = ci->fd;
+                       list = g_list_append(list, ci->fd);
+
+                       work = work->next;
+                       }
+               g_free(path);
+               collection_free(cd);
+               }
+
+       return list;
+}
+
 /*
  *-------------------------------------------------------------------
  * please use these to actually add/remove stuff
@@ -840,6 +994,7 @@ static gboolean collection_window_keypress(GtkWidget *widget, GdkEventKey *event
                                file_util_rename(NULL, collection_table_selection_get_list(cw->table), cw->window);
                                break;
                        case 'D': case 'd':
+                               options->file_ops.safe_delete_enable = TRUE;
                                file_util_delete(NULL, collection_table_selection_get_list(cw->table), cw->window);
                                break;
                        case 'S': case 's':
@@ -926,14 +1081,17 @@ static gboolean collection_window_keypress(GtkWidget *widget, GdkEventKey *event
                                        collection_remove_by_info(cw->cd, collection_table_get_focus_info(cw->table));
                                        }
                                break;
-                       case GDK_KEY_F1:
-                               help_window_show("GuideReferenceKeyboardShortcuts.html#CollectionsKeyboardShortcuts");
-                               break;
                        default:
                                stop_signal = FALSE;
                                break;
                        }
                }
+       if (!stop_signal && is_help_key(event))
+               {
+               help_window_show("GuideCollections.html");
+               stop_signal = TRUE;
+               }
+
        return stop_signal;
 }
 
@@ -1099,7 +1257,7 @@ static void collection_close_dlg_show(CollectWindow *cw)
                                collection_close_cancel_cb, cw);
        generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
                                   _("Close collection"),
-                                  _("Collection has been modified.\nSave first?"));
+                                  _("Collection has been modified.\nSave first?"), TRUE);
 
        generic_dialog_add_button(gd, GTK_STOCK_SAVE, NULL, collection_close_save_cb, TRUE);
        generic_dialog_add_button(gd, GTK_STOCK_DELETE, _("_Discard"), collection_close_close_cb, FALSE);
@@ -1128,19 +1286,39 @@ void collection_window_close_by_collection(CollectionData *cd)
        if (cw) collection_window_close_final(cw);
 }
 
+/**
+ * @brief Check if any Collection windows have unsaved data
+ * @returns TRUE if unsaved data exists
+ * 
+ * Also saves window geometry for Collection windows that have
+ * no unsaved data
+ */
 gboolean collection_window_modified_exists(void)
 {
        GList *work;
+       gboolean ret;
+
+       ret = FALSE;
 
        work = collection_window_list;
        while (work)
                {
                CollectWindow *cw = work->data;
-               if (cw->cd->changed) return TRUE;
+               if (cw->cd->changed)
+                       {
+                       ret = TRUE;
+                       }
+               else
+                       {
+                       if (!collection_save(cw->table->cd, cw->table->cd->path))
+                               {
+                               log_printf("failed saving to collection path: %s\n", cw->table->cd->path);
+                               }
+                       }
                work = work->next;
                }
 
-       return FALSE;
+       return ret;
 }
 
 static gboolean collection_window_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
@@ -1160,6 +1338,13 @@ CollectWindow *collection_window_new(const gchar *path)
        GtkWidget *extra_label;
        GdkGeometry geometry;
 
+       /* If the collection is already opened in another window, return that one */
+       cw = collection_window_find_by_path(path);
+       if (cw)
+               {
+               return cw;
+               }
+
        cw = g_new0(CollectWindow, 1);
 
        collection_window_list = g_list_append(collection_window_list, cw);
@@ -1167,6 +1352,7 @@ CollectWindow *collection_window_new(const gchar *path)
        cw->cd = collection_new(path);
 
        cw->window = window_new(GTK_WINDOW_TOPLEVEL, "collection", PIXBUF_INLINE_ICON_BOOK, NULL, NULL);
+       DEBUG_NAME(cw->window);
 
        geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
        geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
@@ -1175,11 +1361,15 @@ CollectWindow *collection_window_new(const gchar *path)
        gtk_window_set_geometry_hints(GTK_WINDOW(cw->window), NULL, &geometry,
                                      GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
 
+       if (options->collections_on_top)
+               {
+               gtk_window_set_keep_above(GTK_WINDOW(cw->window), TRUE);
+               }
 
        if (options->save_window_positions && path && collection_load_only_geometry(cw->cd, path))
                {
-               /* FIXME: x, y is not implemented */
                gtk_window_set_default_size(GTK_WINDOW(cw->window), cw->cd->window_w, cw->cd->window_h);
+               gtk_window_move(GTK_WINDOW(cw->window), cw->cd->window_x, cw->cd->window_y);
                }
        else
                {
@@ -1209,6 +1399,7 @@ CollectWindow *collection_window_new(const gchar *path)
        gtk_widget_show(cw->status_box);
 
        frame = gtk_frame_new(NULL);
+       DEBUG_NAME(frame);
        gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
        gtk_box_pack_start(GTK_BOX(cw->status_box), frame, TRUE, TRUE, 0);
        gtk_widget_show(frame);
@@ -1219,6 +1410,10 @@ CollectWindow *collection_window_new(const gchar *path)
 
        extra_label = gtk_progress_bar_new();
        gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(extra_label), 0.0);
+#if GTK_CHECK_VERSION(3,0,0)
+       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(extra_label), "");
+       gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(extra_label), TRUE);
+#endif
        gtk_box_pack_start(GTK_BOX(cw->status_box), extra_label, TRUE, TRUE, 0);
        gtk_widget_show(extra_label);