Mercurial > hg > forks > geeqie
changeset 2500:eb2ce489ceea
Fix #323: Rating system
https://github.com/BestImageViewer/geeqie/issues/323
Initial implementation.
Set values either by Edit menu, or Alt+Keypad+n: n is 0 to 5
Alt+keypad+minus sets the value to -1.
author | Colin Clark <colin.clark@cclark.uk> |
---|---|
date | Thu, 08 Jun 2017 20:46:52 +0100 |
parents | 92345664aa61 |
children | b5cec98159e7 |
files | doc/docbook/GuideMainWindowMenus.xml doc/docbook/GuideReferenceTags.xml src/bar.c src/bar_comment.c src/filedata.c src/layout_image.c src/layout_image.h src/layout_util.c src/menu.c src/metadata.h src/options.h src/preferences.c src/search.c src/typedefs.h |
diffstat | 14 files changed, 330 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/docbook/GuideMainWindowMenus.xml Thu Jun 08 11:46:20 2017 +0100 +++ b/doc/docbook/GuideMainWindowMenus.xml Thu Jun 08 20:46:52 2017 +0100 @@ -711,6 +711,81 @@ <menuchoice> <shortcut> <keycombo> + <keycap>[</keycap> + </keycombo> + </shortcut> + <guimenu>Orientation</guimenu> + <guimenuitem>Rotate counterclockwise</guimenuitem> + </menuchoice> + </term> + <listitem> + <para>Rotates the current image counterclockwise 90 degrees, does not modify the file on disk.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <menuchoice> + <shortcut> + <keycombo> + <keycap>Shift</keycap> + <keycap>R</keycap> + </keycombo> + </shortcut> + <guimenu>Orientation</guimenu> + <guimenuitem>Rotate 180</guimenuitem> + </menuchoice> + </term> + <listitem> + <para>Rotates the current image 180 degrees, does not modify the file on disk.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <menuchoice> + <guimenu>Rating</guimenu> + </menuchoice> + </term> + <listitem> + <para>Set a Rating value for each image.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <menuchoice> + <shortcut> + <keycombo> + <keycap>Alt+Keypad+n</keycap> + </keycombo> + </shortcut> + <guimenu>Rating</guimenu> + <guimenuitem>n</guimenuitem> + </menuchoice> + </term> + <listitem> + <para>"n" is in the range 0 to 5. Sets the Rating value for the image.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <menuchoice> + <shortcut> + <keycombo> + <keycap>Alt+Keypad+Minus</keycap> + </keycombo> + </shortcut> + <guimenu>Rating</guimenu> + <guimenuitem>-1</guimenuitem> + </menuchoice> + </term> + <listitem> + <para>Sets the Rating value to -1 for the image.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <menuchoice> + <shortcut> + <keycombo> <keycap>Ctrl</keycap> <keycap>S</keycap> </keycombo>
--- a/doc/docbook/GuideReferenceTags.xml Thu Jun 08 11:46:20 2017 +0100 +++ b/doc/docbook/GuideReferenceTags.xml Thu Jun 08 20:46:52 2017 +0100 @@ -283,6 +283,14 @@ <para>Title</para> </entry> </row> + <row> + <entry> + <para>Xmp.xmp.Rating</para> + </entry> + <entry> + <para>Rating</para> + </entry> + </row> </tbody> </tgroup> </table>
--- a/src/bar.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/bar.c Thu Jun 08 20:46:52 2017 +0100 @@ -83,6 +83,14 @@ " </bar>" " </layout>" "</gq>"; +static const gchar default_config_rating[] = +"<gq>" +" <layout id = '_current_'>" +" <bar>" +" <pane_comment id = 'rating' expanded = 'true' key = '" RATING_KEY "' height = '10' />" +" </bar>" +" </layout>" +"</gq>"; static const gchar default_config_exif[] = "<gq>" @@ -176,6 +184,7 @@ {PANE_COMMENT, "title", N_("Title"), default_config_title}, {PANE_KEYWORDS, "keywords", N_("Keywords"), default_config_keywords}, {PANE_COMMENT, "comment", N_("Comment"), default_config_comment}, + {PANE_COMMENT, "rating", N_("Rating"), default_config_rating}, {PANE_EXIF, "exif", N_("Exif"), default_config_exif}, /* other pre-configured panes */ {PANE_EXIF, "file_info", N_("File info"), default_config_file_info}, @@ -567,7 +576,7 @@ void bar_populate_default(GtkWidget *bar) { - const gchar *populate_id[] = {"histogram", "title", "keywords", "comment", "exif", NULL}; + const gchar *populate_id[] = {"histogram", "title", "keywords", "comment", "rating", "exif", NULL}; const gchar **id = populate_id; while (*id)
--- a/src/bar_comment.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/bar_comment.c Thu Jun 08 20:46:52 2017 +0100 @@ -175,6 +175,10 @@ { pcd->height = options->info_comment.height; } + if (!g_strcmp0(pcd->pane.id, "rating")) + { + pcd->height = options->info_rating.height; + } WRITE_NL(); WRITE_STRING("<pane_comment "); write_char_option(outstr, indent, "id", pcd->pane.id); @@ -311,6 +315,10 @@ { options->info_comment.height = height; } + if (!g_strcmp0(id, "rating")) + { + options->info_rating.height = height; + } bar_pane_translate_title(PANE_COMMENT, id, &title); ret = bar_pane_comment_new(id, title, key, expanded, height);
--- a/src/filedata.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/filedata.c Thu Jun 08 20:46:52 2017 +0100 @@ -428,6 +428,7 @@ fd->ref = 1; fd->magick = FD_MAGICK; fd->exifdate = 0; + fd->rating = 0; if (disable_sidecars) fd->disable_grouping = TRUE; @@ -514,6 +515,24 @@ } } +void set_rating_data(GList *files) +{ + gchar *rating_str; + DEBUG_1("%s set_rating_data: ...", get_exec_time()); + + while (files) + { + FileData *file = files->data; + rating_str = metadata_read_string(file, RATING_KEY, METADATA_PLAIN); + if (rating_str ) + { + file->rating = atoi(rating_str); + g_free(rating_str); + } + files = files->next; + } +} + FileData *file_data_new_no_grouping(const gchar *path_utf8) { struct stat st; @@ -1026,6 +1045,11 @@ if (fa->exifdate > fb->exifdate) return 1; /* fall back to name */ break; + case SORT_RATING: + if (fa->rating < fb->rating) return -1; + if (fa->rating > fb->rating) return 1; + /* fall back to name */ + break; #ifdef HAVE_STRVERSCMP case SORT_NUMBER: ret = strverscmp(fa->name, fb->name); @@ -1081,6 +1105,10 @@ { set_exif_time_data(list); } + if (method == SORT_RATING) + { + set_rating_data(list); + } return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb); }
--- a/src/layout_image.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/layout_image.c Thu Jun 08 20:46:52 2017 +0100 @@ -1105,6 +1105,56 @@ } } +static void image_alter_rating(FileData *fd_n, const gchar *rating) +{ + metadata_write_string(fd_n, RATING_KEY, rating); +} + +void layout_image_rating(LayoutWindow *lw, const gchar *rating) +{ + if (!layout_valid(&lw)) return; + + GtkTreeModel *store; + GList *work; + GtkTreeSelection *selection; + GtkTreePath *tpath; + FileData *fd_n; + GtkTreeIter iter; + IconData *id; + + if (!lw || !lw->vf) return; + + if (lw->vf->type == FILEVIEW_ICON) + { + if (!VFICON(lw->vf)->selection) return; + work = VFICON(lw->vf)->selection; + } + else + { + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview)); + work = gtk_tree_selection_get_selected_rows(selection, &store); + } + + while (work) + { + if (lw->vf->type == FILEVIEW_ICON) + { + id = work->data; + fd_n = id->fd; + work = work->next; + } + else + { + tpath = work->data; + gtk_tree_model_get_iter(store, &iter, tpath); + gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1); + work = work->next; + } + + image_alter_rating(fd_n, rating); + } +} + void layout_image_reset_orientation(LayoutWindow *lw) { ImageWindow *imd= lw->image;
--- a/src/layout_image.h Thu Jun 08 11:46:20 2017 +0100 +++ b/src/layout_image.h Thu Jun 08 20:46:52 2017 +0100 @@ -64,6 +64,8 @@ void layout_image_set_desaturate(LayoutWindow *lw, gboolean desaturate); gboolean layout_image_get_desaturate(LayoutWindow *lw); +void layout_image_rating(LayoutWindow *lw, const gchar *rating); + /* gint layout_image_stereo_get(LayoutWindow *lw); void layout_image_stereo_set(LayoutWindow *lw, gint stereo_mode);
--- a/src/layout_util.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/layout_util.c Thu Jun 08 20:46:52 2017 +0100 @@ -352,6 +352,55 @@ layout_image_alter_orientation(lw, ALTER_ROTATE_90); } +static void layout_menu_rating_0_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "0"); +} + +static void layout_menu_rating_1_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "1"); +} + +static void layout_menu_rating_2_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "2"); +} + +static void layout_menu_rating_3_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "3"); +} + +static void layout_menu_rating_4_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "4"); +} + +static void layout_menu_rating_5_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "5"); +} + +static void layout_menu_rating_m1_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_image_rating(lw, "-1"); +} + static void layout_menu_alter_90cc_cb(GtkAction *action, gpointer data) { LayoutWindow *lw = data; @@ -1524,6 +1573,7 @@ { "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL }, { "SelectMenu", NULL, N_("_Select"), NULL, NULL, NULL }, { "OrientationMenu", NULL, N_("_Orientation"), NULL, NULL, NULL }, + { "RatingMenu", NULL, N_("_Rating"), NULL, NULL, NULL }, { "ExternalMenu", NULL, N_("E_xternal Editors"), NULL, NULL, NULL }, { "PreferencesMenu", NULL, N_("P_references"), NULL, NULL, NULL }, { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL }, @@ -1569,6 +1619,13 @@ { "CloseWindow", GTK_STOCK_CLOSE, N_("C_lose window"), "<control>W", N_("Close window"), CB(layout_menu_close_cb) }, { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", N_("Quit"), CB(layout_menu_exit_cb) }, { "RotateCW", NULL, N_("_Rotate clockwise"), "bracketright", N_("Rotate clockwise"), CB(layout_menu_alter_90_cb) }, + { "Rating0", NULL, N_("_Rating 0"), "<alt>KP_0", N_("Rating 0"), CB(layout_menu_rating_0_cb) }, + { "Rating1", NULL, N_("_Rating 1"), "<alt>KP_1", N_("Rating 1"), CB(layout_menu_rating_1_cb) }, + { "Rating2", NULL, N_("_Rating 2"), "<alt>KP_2", N_("Rating 2"), CB(layout_menu_rating_2_cb) }, + { "Rating3", NULL, N_("_Rating 3"), "<alt>KP_3", N_("Rating 3"), CB(layout_menu_rating_3_cb) }, + { "Rating4", NULL, N_("_Rating 4"), "<alt>KP_4", N_("Rating 4"), CB(layout_menu_rating_4_cb) }, + { "Rating5", NULL, N_("_Rating 5"), "<alt>KP_5", N_("Rating 5"), CB(layout_menu_rating_5_cb) }, + { "RatingM1", NULL, N_("_Rating -1"), "<alt>KP_Subtract", N_("Rating -1"), CB(layout_menu_rating_m1_cb) }, { "RotateCCW", NULL, N_("Rotate _counterclockwise"), "bracketleft", N_("Rotate counterclockwise"), CB(layout_menu_alter_90cc_cb) }, { "Rotate180", NULL, N_("Rotate 1_80"), "<shift>R", N_("Rotate 180"), CB(layout_menu_alter_180_cb) }, { "Mirror", NULL, N_("_Mirror"), "<shift>M", N_("Mirror"), CB(layout_menu_alter_mirror_cb) }, @@ -1777,6 +1834,16 @@ " <menuitem action='ExifRotate'/>" " <separator/>" " </menu>" +" <menu action='RatingMenu'>" +" <menuitem action='Rating0'/>" +" <menuitem action='Rating1'/>" +" <menuitem action='Rating2'/>" +" <menuitem action='Rating3'/>" +" <menuitem action='Rating4'/>" +" <menuitem action='Rating5'/>" +" <menuitem action='RatingM1'/>" +" <separator/>" +" </menu>" " <menuitem action='SaveMetadata'/>" " <placeholder name='PropertiesSection'/>" " <separator/>"
--- a/src/menu.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/menu.c Thu Jun 08 20:46:52 2017 +0100 @@ -158,6 +158,9 @@ case SORT_NUMBER: return _("Sort by number"); break; + case SORT_RATING: + return _("Sort by rating"); + break; case SORT_NAME: default: return _("Sort by name"); @@ -205,6 +208,7 @@ submenu_add_sort_item(submenu, func, SORT_CTIME, show_current, type); submenu_add_sort_item(submenu, func, SORT_EXIFTIME, show_current, type); submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type); + submenu_add_sort_item(submenu, func, SORT_RATING, show_current, type); if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type); if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
--- a/src/metadata.h Thu Jun 08 11:46:20 2017 +0100 +++ b/src/metadata.h Thu Jun 08 20:46:52 2017 +0100 @@ -25,6 +25,7 @@ #define COMMENT_KEY "Xmp.dc.description" #define KEYWORD_KEY "Xmp.dc.subject" #define ORIENTATION_KEY "Xmp.tiff.Orientation" +#define RATING_KEY "Xmp.xmp.Rating" void metadata_cache_free(FileData *fd);
--- a/src/options.h Thu Jun 08 11:46:20 2017 +0100 +++ b/src/options.h Thu Jun 08 20:46:52 2017 +0100 @@ -67,6 +67,10 @@ gint height; } info_title; + struct { + gint height; + } info_rating; + /* file ops */ struct { gboolean enable_in_place_rename;
--- a/src/preferences.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/preferences.c Thu Jun 08 20:46:52 2017 +0100 @@ -370,6 +370,7 @@ options->info_keywords.height = c_options->info_keywords.height; options->info_title.height = c_options->info_title.height; options->info_comment.height = c_options->info_comment.height; + options->info_rating.height = c_options->info_rating.height; #ifdef DEBUG set_debug_level(debug_c); @@ -1486,6 +1487,9 @@ pref_spin_new_int(hbox, _("Comment:"), NULL, 1, 9999, 1, options->info_comment.height, &c_options->info_comment.height); + pref_spin_new_int(hbox, _("Rating:"), NULL, + 1, 9999, 1, + options->info_rating.height, &c_options->info_rating.height); } /* image tab */
--- a/src/search.c Thu Jun 08 11:46:20 2017 +0100 +++ b/src/search.c Thu Jun 08 20:46:52 2017 +0100 @@ -138,6 +138,11 @@ GtkWidget *menu_comment; GtkWidget *entry_comment; + GtkWidget *check_rating; + GtkWidget *menu_rating; + GtkWidget *spin_rating; + GtkWidget *spin_rating_end; + FileData *search_dir_fd; gboolean search_path_recurse; gchar *search_name; @@ -159,6 +164,8 @@ CacheData *search_similarity_cd; GList *search_keyword_list; gchar *search_comment; + gint search_rating; + gint search_rating_end; gboolean search_comment_match_case; gboolean search_date_exif; @@ -170,6 +177,7 @@ MatchType match_dimensions; MatchType match_keywords; MatchType match_comment; + MatchType match_rating; MatchType match_gps; gboolean match_name_enable; @@ -179,6 +187,7 @@ gboolean match_similarity_enable; gboolean match_keywords_enable; gboolean match_comment_enable; + gboolean match_rating_enable; GList *search_folder_list; GList *search_done_list; @@ -266,6 +275,14 @@ { N_("miss"), SEARCH_MATCH_NONE } }; + +static const MatchList text_search_menu_rating[] = { + { N_("equal to"), SEARCH_MATCH_EQUAL }, + { N_("less than"), SEARCH_MATCH_UNDER }, + { N_("greater than"), SEARCH_MATCH_OVER }, + { N_("between"), SEARCH_MATCH_BETWEEN } +}; + static const MatchList text_search_menu_gps[] = { { N_("not geocoded"), SEARCH_MATCH_NONE }, { N_("less than"), SEARCH_MATCH_UNDER }, @@ -1962,6 +1979,31 @@ } } + if (match && sd->match_rating_enable) + { + tested = TRUE; + match = FALSE; + gint rating; + + rating = metadata_read_int(fd, RATING_KEY, 0); + if (sd->match_rating == SEARCH_MATCH_EQUAL) + { + match = (rating == sd->search_rating); + } + else if (sd->match_rating == SEARCH_MATCH_UNDER) + { + match = (rating < sd->search_rating); + } + else if (sd->match_rating == SEARCH_MATCH_OVER) + { + match = (rating > sd->search_rating); + } + else if (sd->match_rating == SEARCH_MATCH_BETWEEN) + { + match = MATCH_IS_BETWEEN(rating, sd->search_rating, sd->search_rating_end); + } + } + if (match && sd->match_gps_enable) { /* Calculate the distance the image is from the specified origin. @@ -2538,6 +2580,16 @@ (sd->match_size == SEARCH_MATCH_BETWEEN)); } +static void menu_choice_rating_cb(GtkWidget *combo, gpointer data) +{ + SearchData *sd = data; + + if (!menu_choice_get_match_type(combo, &sd->match_rating)) return; + + menu_choice_set_visible(gtk_widget_get_parent(sd->spin_rating_end), + (sd->match_rating == SEARCH_MATCH_BETWEEN)); +} + static void menu_choice_date_cb(GtkWidget *combo, gpointer data) { SearchData *sd = data; @@ -2766,6 +2818,7 @@ sd->match_dimensions = SEARCH_MATCH_EQUAL; sd->match_keywords = SEARCH_MATCH_ALL; sd->match_comment = SEARCH_MATCH_CONTAINS; + sd->match_rating = SEARCH_MATCH_EQUAL; sd->match_name_enable = TRUE; @@ -2934,6 +2987,19 @@ pref_checkbox_new_int(hbox, _("Match case"), sd->search_comment_match_case, &sd->search_comment_match_case); + /* Search for image rating */ + hbox = menu_choice(sd->box_search, &sd->check_rating, &sd->menu_rating, + _("Image rating is"), &sd->match_rating_enable, + text_search_menu_rating, sizeof(text_search_menu_rating) / sizeof(MatchList), + G_CALLBACK(menu_choice_rating_cb), sd); + sd->spin_size = menu_spin(hbox, -1, 5, sd->search_rating, + G_CALLBACK(menu_choice_spin_cb), &sd->search_rating); + hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE); + gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); + pref_label_new(hbox2, _("and")); + sd->spin_rating_end = menu_spin(hbox2, -1, 5, sd->search_rating_end, + G_CALLBACK(menu_choice_spin_cb), &sd->search_rating_end); + /* Search for images within a specified range of a lat/long coordinate */ hbox = menu_choice(sd->box_search, &sd->check_gps, &sd->menu_gps,
--- a/src/typedefs.h Thu Jun 08 11:46:20 2017 +0100 +++ b/src/typedefs.h Thu Jun 08 20:46:52 2017 +0100 @@ -66,7 +66,8 @@ SORT_CTIME, SORT_PATH, SORT_NUMBER, - SORT_EXIFTIME + SORT_EXIFTIME, + SORT_RATING } SortType; typedef enum { @@ -573,6 +574,7 @@ time_t exifdate; GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values GList *cached_metadata; + gint rating; }; struct _LayoutOptions