</warning>\r
</listitem>\r
</varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>[</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Orientation</guimenu>\r
+ <guimenuitem>Rotate counterclockwise</guimenuitem>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Rotates the current image counterclockwise 90 degrees, does not modify the file on disk.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Shift</keycap>\r
+ <keycap>R</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Orientation</guimenu>\r
+ <guimenuitem>Rotate 180</guimenuitem>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Rotates the current image 180 degrees, does not modify the file on disk.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <guimenu>Rating</guimenu>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Set a Rating value for each image.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Alt+Keypad+n</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Rating</guimenu>\r
+ <guimenuitem>n</guimenuitem>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>"n" is in the range 0 to 5. Sets the Rating value for the image.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Alt+Keypad+Minus</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Rating</guimenu>\r
+ <guimenuitem>-1</guimenuitem>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Sets the Rating value to -1 for the image.</para>\r
+ </listitem>\r
+ </varlistentry>\r
<varlistentry>\r
<term>\r
<menuchoice>\r
<para>Title</para>\r
</entry>\r
</row>\r
+ <row>\r
+ <entry>\r
+ <para>Xmp.xmp.Rating</para>\r
+ </entry>\r
+ <entry>\r
+ <para>Rating</para>\r
+ </entry>\r
+ </row>\r
</tbody>\r
</tgroup>\r
</table>\r
" </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>"
{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},
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)
{
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);
{
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);
fd->ref = 1;
fd->magick = FD_MAGICK;
fd->exifdate = 0;
+ fd->rating = 0;
if (disable_sidecars) fd->disable_grouping = TRUE;
}
}
+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;
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);
{
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);
}
}
}
+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;
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);
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;
{ "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 },
{ "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) },
" <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/>"
case SORT_NUMBER:
return _("Sort by number");
break;
+ case SORT_RATING:
+ return _("Sort by rating");
+ break;
case SORT_NAME:
default:
return _("Sort by name");
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);
#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);
gint height;
} info_title;
+ struct {
+ gint height;
+ } info_rating;
+
/* file ops */
struct {
gboolean enable_in_place_rename;
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);
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 */
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;
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;
MatchType match_dimensions;
MatchType match_keywords;
MatchType match_comment;
+ MatchType match_rating;
MatchType match_gps;
gboolean match_name_enable;
gboolean match_similarity_enable;
gboolean match_keywords_enable;
gboolean match_comment_enable;
+ gboolean match_rating_enable;
GList *search_folder_list;
GList *search_done_list;
{ 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 },
}
}
+ 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.
(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;
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;
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,
SORT_CTIME,
SORT_PATH,
SORT_NUMBER,
- SORT_EXIFTIME
+ SORT_EXIFTIME,
+ SORT_RATING
} SortType;
typedef enum {
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