diff --git a/src/common/collection.c b/src/common/collection.c index c3d66064c29a..db24f16b47cb 100644 --- a/src/common/collection.c +++ b/src/common/collection.c @@ -659,6 +659,8 @@ const char *dt_collection_name_untranslated(const dt_collection_properties_t pro return N_("exposure program"); case DT_COLLECTION_PROP_METERING_MODE: return N_("metering mode"); + case DT_COLLECTION_PROP_FILM_MODE: + return N_("film mode"); case DT_COLLECTION_PROP_LAST: return NULL; default: @@ -1828,6 +1830,30 @@ static gchar *get_query_string(const dt_collection_properties_t property, const dt_util_str_cat(&query, ")"); break; + case DT_COLLECTION_PROP_FILM_MODE: // film mode + query = g_strdup("("); + // handle the possibility of multiple values + elems = _strsplit_quotes(escaped_text, ",", -1); + for(int i = 0; i < g_strv_length(elems); i++) + { + if(!g_strcmp0(elems[i], _("unnamed"))) + { + dt_util_str_cat(&query, + "%sfilm_mode_id IN (SELECT id FROM main.film_mode WHERE name IS NULL OR TRIM(name)='')", + i > 0 ? " OR " : ""); + } + else + { + gchar *film_mode = _add_wildcards(elems[i]); + dt_util_str_cat(&query, "%sfilm_mode_id IN (SELECT id FROM main.film_mode WHERE name LIKE '%s')", + i > 0 ? " OR " : "", film_mode); + g_free(film_mode); + } + } + g_strfreev(elems); + dt_util_str_cat(&query, ")"); + break; + case DT_COLLECTION_PROP_GROUP_ID: // group id query = g_strdup("("); // handle the possibility of multiple values @@ -2605,15 +2631,11 @@ void dt_collection_update_query(const dt_collection_t *collection, snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", i); const int mode = dt_conf_get_int(confname); - if(*text - && g_strcmp0(text, _("unnamed")) != 0 - && (property == DT_COLLECTION_PROP_CAMERA - || property == DT_COLLECTION_PROP_LENS - || property == DT_COLLECTION_PROP_WHITEBALANCE - || property == DT_COLLECTION_PROP_FLASH - || property == DT_COLLECTION_PROP_EXPOSURE_PROGRAM - || property == DT_COLLECTION_PROP_METERING_MODE - || property == DT_COLLECTION_PROP_GROUP_ID)) + if(*text && g_strcmp0(text, _("unnamed")) != 0 + && (property == DT_COLLECTION_PROP_CAMERA || property == DT_COLLECTION_PROP_LENS + || property == DT_COLLECTION_PROP_WHITEBALANCE || property == DT_COLLECTION_PROP_FLASH + || property == DT_COLLECTION_PROP_EXPOSURE_PROGRAM || property == DT_COLLECTION_PROP_METERING_MODE + || property == DT_COLLECTION_PROP_FILM_MODE || property == DT_COLLECTION_PROP_GROUP_ID)) { gchar *text_quoted = g_strdup_printf("\"%s\"", text); g_free(text); diff --git a/src/common/collection.h b/src/common/collection.h index 9e8a533e078d..0f08f020c5dd 100644 --- a/src/common/collection.h +++ b/src/common/collection.h @@ -121,6 +121,7 @@ typedef enum dt_collection_properties_t // all new collection types need to be added before DT_COLLECTION_PROP_LAST, // which separates actual collection types from special flag values + DT_COLLECTION_PROP_FILM_MODE, DT_COLLECTION_PROP_LAST, DT_COLLECTION_PROP_UNDEF, diff --git a/src/common/database.c b/src/common/database.c index 92ef8c5a271e..193d48438b51 100644 --- a/src/common/database.c +++ b/src/common/database.c @@ -51,7 +51,7 @@ #define LAST_FULL_DATABASE_VERSION_DATA 10 // You HAVE TO bump THESE versions whenever you add an update branches to _upgrade_*_schema_step()! -#define CURRENT_DATABASE_VERSION_LIBRARY 57 +#define CURRENT_DATABASE_VERSION_LIBRARY 58 #define CURRENT_DATABASE_VERSION_DATA 13 #define USE_NESTED_TRANSACTIONS @@ -2967,6 +2967,21 @@ static int _upgrade_library_schema_step(dt_database_t *db, "[init] can't add `flash_tagvalue' column to images table in database\n"); new_version = 57; } + else if(version == 57) + { + + TRY_EXEC("CREATE TABLE film_mode" + " (id INTEGER PRIMARY KEY AUTOINCREMENT," + " name VARCHAR)", + "can't create table film_mode"); + TRY_EXEC("CREATE UNIQUE INDEX film_mode_name ON film_mode (name)", + "can't create index `film_mode_name' on table `film_mode'"); + + TRY_EXEC("ALTER TABLE main.images ADD COLUMN film_mode_id INTEGER REFERENCES film_mode (id) ON DELETE " + "RESTRICT ON UPDATE CASCADE", + "[init] can't add `film_mode' column to images table in database\n"); + new_version = 58; + } else new_version = version; // should be the fallback so that calling code sees that we are in an infinite loop diff --git a/src/common/exif.cc b/src/common/exif.cc index 0d51a142f80a..39dd078fc030 100644 --- a/src/common/exif.cc +++ b/src/common/exif.cc @@ -2335,6 +2335,21 @@ static bool _exif_decode_exif_data(dt_image_t *img, Exiv2::ExifData &exifData) } }; + if(FIND_EXIF_TAG("Exif.Fujifilm.FilmMode")) + { + const int value = pos->toLong(); + _exif_value_str(value, img->exif_film_mode, sizeof(img->exif_film_mode), pos, exifData); + } + else if(FIND_EXIF_TAG("Exif.Fujifilm.Color")) + { + // Exif.Fujifilm.Color contains the monochrome mode (sepia, normal b&w, + // color filters, acros) if FilmMode is not set. + // In the camera UI, those are all in the same menu, so it makes sense to + // merge them into the same tag in darktable. + const int value = pos->toLong(); + _exif_value_str(value, img->exif_film_mode, sizeof(img->exif_film_mode), pos, exifData); + } + img->exif_inited = TRUE; return true; } diff --git a/src/common/image.c b/src/common/image.c index 01a676e67788..206057b2b2ec 100644 --- a/src/common/image.c +++ b/src/common/image.c @@ -1318,7 +1318,7 @@ static dt_imgid_t _image_duplicate_with_version_ext(const dt_imgid_t imgid, " orientation, longitude, latitude, altitude, color_matrix," " colorspace, version, max_version," " history_end, position, aspect_ratio, exposure_bias, import_timestamp," - " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue)" + " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue, film_mode_id)" " SELECT NULL, group_id, film_id, width, height, filename," " maker_id, model_id, camera_id, lens_id," " exposure, aperture, iso, focal_length, focus_distance, datetime_taken," @@ -1326,7 +1326,7 @@ static dt_imgid_t _image_duplicate_with_version_ext(const dt_imgid_t imgid, " raw_black, raw_maximum, orientation," " longitude, latitude, altitude, color_matrix, colorspace, NULL, NULL, 0, ?1," " aspect_ratio, exposure_bias, import_timestamp," - " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue" + " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue, film_mode_id" " FROM main.images WHERE id = ?2", -1, &stmt, NULL); // clang-format on @@ -2141,6 +2141,7 @@ void dt_image_init(dt_image_t *img) memset(img->exif_flash, 0, sizeof(img->exif_flash)); memset(img->exif_exposure_program, 0, sizeof(img->exif_exposure_program)); memset(img->exif_metering_mode, 0, sizeof(img->exif_metering_mode)); + memset(img->exif_film_mode, 0, sizeof(img->exif_metering_mode)); memset(&img->exif_datetime_taken, 0, sizeof(img->exif_datetime_taken)); memset(img->camera_maker, 0, sizeof(img->camera_maker)); memset(img->camera_model, 0, sizeof(img->camera_model)); @@ -2548,7 +2549,7 @@ dt_imgid_t dt_image_copy_rename(const dt_imgid_t imgid, " raw_black, raw_maximum, orientation," " longitude, latitude, altitude, color_matrix, colorspace, version, max_version," " position, aspect_ratio, exposure_bias," - " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue)" + " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue, film_mode_id)" " SELECT NULL, group_id, ?1 as film_id, width, height, ?2 as filename," " maker_id, model_id, lens_id," " exposure, aperture, iso, focal_length, focus_distance, datetime_taken," @@ -2556,7 +2557,7 @@ dt_imgid_t dt_image_copy_rename(const dt_imgid_t imgid, " orientation, longitude, latitude, altitude," " color_matrix, colorspace, -1, -1," " ?3, aspect_ratio, exposure_bias," - " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue" + " whitebalance_id, flash_id, exposure_program_id, metering_mode_id, flash_tagvalue, film_mode_id" " FROM main.images" " WHERE id = ?4", -1, &stmt, NULL); @@ -3349,6 +3350,11 @@ int32_t dt_image_get_metering_mode_id(const char *name) return _image_get_set_name_id("metering_mode", name); } +int32_t dt_image_get_film_mode_id(const char *name) +{ + return _image_get_set_name_id("film_mode", name); +} + int32_t dt_image_get_camera_id(const char *maker, const char *model) { sqlite3_stmt *stmt; diff --git a/src/common/image.h b/src/common/image.h index 9430757ec956..5f87082327a9 100644 --- a/src/common/image.h +++ b/src/common/image.h @@ -271,6 +271,7 @@ typedef struct dt_image_t char exif_flash[64]; char exif_exposure_program[64]; char exif_metering_mode[64]; + char exif_film_mode[64]; GTimeSpan exif_datetime_taken; dt_image_correction_type_t exif_correction_type; @@ -636,6 +637,7 @@ int32_t dt_image_get_whitebalance_id(const char *name); int32_t dt_image_get_flash_id(const char *name); int32_t dt_image_get_exposure_program_id(const char *name); int32_t dt_image_get_metering_mode_id(const char *name); +int32_t dt_image_get_film_mode_id(const char *name); int32_t dt_image_get_camera_id(const char *maker, const char *model); G_END_DECLS diff --git a/src/common/image_cache.c b/src/common/image_cache.c index d489743ab4a0..f1dd4081467a 100644 --- a/src/common/image_cache.c +++ b/src/common/image_cache.c @@ -49,7 +49,7 @@ static void _image_cache_allocate(void *data, " raw_black, raw_maximum, aspect_ratio, exposure_bias," " import_timestamp, change_timestamp, export_timestamp, print_timestamp," " output_width, output_height, cm.maker, cm.model, cm.alias," - " wb.name, fl.name, ep.name, mm.name, flash_tagvalue" + " wb.name, fl.name, ep.name, mm.name, flash_tagvalue, fm.name" " FROM main.images AS mi" " LEFT JOIN main.cameras AS cm ON cm.id = mi.camera_id" " LEFT JOIN main.makers AS mk ON mk.id = mi.maker_id" @@ -59,6 +59,7 @@ static void _image_cache_allocate(void *data, " LEFT JOIN main.flash AS fl ON fl.id = mi.flash_id" " LEFT JOIN main.exposure_program AS ep ON ep.id = mi.exposure_program_id" " LEFT JOIN main.metering_mode AS mm ON mm.id = mi.metering_mode_id" + " LEFT JOIN main.film_mode AS fm ON fm.id = mi.film_mode_id" " WHERE mi.id = ?1", -1, &stmt, NULL); // clang-format on @@ -155,6 +156,8 @@ static void _image_cache_allocate(void *data, if(str) g_strlcpy(img->exif_metering_mode, str, sizeof(img->exif_metering_mode)); img->exif_flash_tagvalue = sqlite3_column_int(stmt, 42); + str = (char *)sqlite3_column_text(stmt, 43); + if(str) g_strlcpy(img->exif_film_mode, str, sizeof(img->exif_film_mode)); dt_color_harmony_get(entry->key, &img->color_harmony_guide); @@ -345,7 +348,8 @@ void dt_image_cache_write_release_info(dt_image_t *img, " import_timestamp = ?28, change_timestamp = ?29, export_timestamp = ?30," " print_timestamp = ?31, output_width = ?32, output_height = ?33," " whitebalance_id = ?36, flash_id = ?37," - " exposure_program_id = ?38, metering_mode_id = ?39, flash_tagvalue = ?41" + " exposure_program_id = ?38, metering_mode_id = ?39, flash_tagvalue = ?41," + " film_mode_id = ?42" " WHERE id = ?40", -1, &stmt, NULL); @@ -356,6 +360,7 @@ void dt_image_cache_write_release_info(dt_image_t *img, const int32_t flash_id = dt_image_get_flash_id(img->exif_flash); const int32_t exposure_program_id = dt_image_get_exposure_program_id(img->exif_exposure_program); const int32_t metering_mode_id = dt_image_get_metering_mode_id(img->exif_metering_mode); + const int32_t film_mode_id = dt_image_get_film_mode_id(img->exif_film_mode); // also make sure we update the camera_id and possibly the associated data // in cameras table. @@ -412,6 +417,7 @@ void dt_image_cache_write_release_info(dt_image_t *img, DT_DEBUG_SQLITE3_BIND_INT(stmt, 39, metering_mode_id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 40, img->id); DT_DEBUG_SQLITE3_BIND_INT(stmt, 41, img->exif_flash_tagvalue); + DT_DEBUG_SQLITE3_BIND_INT(stmt, 42, film_mode_id); const int rc = sqlite3_step(stmt); if(rc != SQLITE_DONE) diff --git a/src/libs/collect.c b/src/libs/collect.c index 30bab8d33dc7..4acefd07d948 100644 --- a/src/libs/collect.c +++ b/src/libs/collect.c @@ -2104,6 +2104,19 @@ static void _list_view(dt_lib_collect_rule_t *dr) // clang-format on break; + case DT_COLLECTION_PROP_FILM_MODE: // lens + // clang-format off + g_snprintf(query, sizeof(query), + "SELECT fm.name AS film_mode, 1, COUNT(*) AS count" + " FROM main.images AS mi, main.film_mode AS fm" + " WHERE mi.film_mode_id = fm.id" + " AND %s" + " GROUP BY LOWER(film_mode)" + " ORDER BY LOWER(film_mode) %s", where_ext, + sort_descending ? "DESC" : "ASC"); + // clang-format on + break; + case DT_COLLECTION_PROP_FILENAME: // filename // clang-format off g_snprintf(query, sizeof(query), @@ -2375,17 +2388,12 @@ static void _list_view(dt_lib_collect_rule_t *dr) // if needed, we restrict the tree to matching entries if(dr->typing - && (property == DT_COLLECTION_PROP_CAMERA - || property == DT_COLLECTION_PROP_FILENAME - || property == DT_COLLECTION_PROP_FILMROLL - || property == DT_COLLECTION_PROP_LENS - || property == DT_COLLECTION_PROP_APERTURE - || property == DT_COLLECTION_PROP_FOCAL_LENGTH - || property == DT_COLLECTION_PROP_ISO - || property == DT_COLLECTION_PROP_MODULE - || property == DT_COLLECTION_PROP_ORDER - || property == DT_COLLECTION_PROP_RATING - || property >= DT_COLLECTION_PROP_METADATA_OFFSET)) + && (property == DT_COLLECTION_PROP_CAMERA || property == DT_COLLECTION_PROP_FILENAME + || property == DT_COLLECTION_PROP_FILMROLL || property == DT_COLLECTION_PROP_LENS + || property == DT_COLLECTION_PROP_APERTURE || property == DT_COLLECTION_PROP_FOCAL_LENGTH + || property == DT_COLLECTION_PROP_ISO || property == DT_COLLECTION_PROP_MODULE + || property == DT_COLLECTION_PROP_ORDER || property == DT_COLLECTION_PROP_RATING + || property >= DT_COLLECTION_PROP_METADATA_OFFSET || property == DT_COLLECTION_PROP_FILM_MODE)) { gchar *needle = g_utf8_strdown(gtk_entry_get_text(GTK_ENTRY(dr->text)), -1); if(g_str_has_suffix(needle, "%")) @@ -3360,6 +3368,7 @@ static void _populate_collect_combo(GtkWidget *w) ADD_COLLECT_ENTRY(DT_COLLECTION_PROP_FLASH); ADD_COLLECT_ENTRY(DT_COLLECTION_PROP_EXPOSURE_PROGRAM); ADD_COLLECT_ENTRY(DT_COLLECTION_PROP_METERING_MODE); + ADD_COLLECT_ENTRY(DT_COLLECTION_PROP_FILM_MODE); dt_bauhaus_combobox_add_section(w, _("darktable")); ADD_COLLECT_ENTRY(DT_COLLECTION_PROP_GROUP_ID); @@ -4004,6 +4013,7 @@ void init(struct dt_lib_module_t *self) luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_ASPECT_RATIO); luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_EXPOSURE); luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_EXPOSURE_BIAS); + luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_FILM_MODE); luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_FILENAME); luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_GEOTAGGING); luaA_enum_value(L, dt_collection_properties_t, DT_COLLECTION_PROP_LOCAL_COPY); diff --git a/src/libs/filtering.c b/src/libs/filtering.c index e6a9bfb5dafc..e30d75d1e3e1 100644 --- a/src/libs/filtering.c +++ b/src/libs/filtering.c @@ -248,7 +248,8 @@ static _filter_t filters[] { DT_COLLECTION_PROP_WHITEBALANCE, _misc_widget_init, _misc_update }, { DT_COLLECTION_PROP_FLASH, _misc_widget_init, _misc_update }, { DT_COLLECTION_PROP_EXPOSURE_PROGRAM, _misc_widget_init, _misc_update }, - { DT_COLLECTION_PROP_METERING_MODE, _misc_widget_init, _misc_update } }; + { DT_COLLECTION_PROP_METERING_MODE, _misc_widget_init, _misc_update }, + { DT_COLLECTION_PROP_FILM_MODE, _misc_widget_init, _misc_update } }; static _filter_t *_filters_get(const dt_collection_properties_t prop) { diff --git a/src/libs/filters/misc.c b/src/libs/filters/misc.c index 2b6172bc932d..0e396339f409 100644 --- a/src/libs/filters/misc.c +++ b/src/libs/filters/misc.c @@ -113,6 +113,11 @@ void _misc_tree_update(_widgets_misc_t *misc) table = g_strdup("metering_mode"); tooltip = g_strdup(_("no metering mode defined")); } + else if(misc->prop == DT_COLLECTION_PROP_FILM_MODE) + { + table = g_strdup("film_mode"); + tooltip = g_strdup(_("no film mode defined")); + } // SQL if(misc->prop == DT_COLLECTION_PROP_CAMERA) diff --git a/src/libs/metadata_view.c b/src/libs/metadata_view.c index f4e1abd11b09..d12d7d10690c 100644 --- a/src/libs/metadata_view.c +++ b/src/libs/metadata_view.c @@ -106,6 +106,7 @@ enum md_exif_datetime, md_exif_width, md_exif_height, + md_exif_film_mode, /* size of final image */ md_width, @@ -121,7 +122,7 @@ enum md_categories, /* xmp */ - md_xmp_metadata // keep this at last position! + md_xmp_metadata // keep this at last position! }; // We have to maintain the correspondence between the displayed metadata list @@ -162,6 +163,7 @@ static const char *_labels[] = { N_("datetime"), N_("width"), N_("height"), + N_("film mode"), N_("export width"), N_("export height"), @@ -594,6 +596,7 @@ void gui_update(dt_lib_module_t *self) "COUNT(DISTINCT datetime_taken), " "COUNT(DISTINCT width), " "COUNT(DISTINCT height), " + "COUNT(DISTINCT IFNULL(film_mode_id, '')), " "COUNT(DISTINCT IFNULL(output_width, '')), " //exported width "COUNT(DISTINCT IFNULL(output_height, '')), " //exported height "COUNT(DISTINCT IFNULL(latitude, '')), " @@ -901,6 +904,11 @@ void gui_update(dt_lib_module_t *self) } break; + case md_exif_film_mode: + (void)g_snprintf(text, sizeof(text), "%s", img->exif_film_mode); + _metadata_update_value(md_exif_film_mode, text, self); + break; + case md_width: (void)g_strlcpy(text, NODATA_STRING, sizeof(text)); if(img->final_width > 0) diff --git a/src/lua/image.c b/src/lua/image.c index 63399a40a7c3..42aa8ec42f5c 100644 --- a/src/lua/image.c +++ b/src/lua/image.c @@ -540,6 +540,7 @@ int dt_lua_init_image(lua_State *L) luaA_struct_member(L, dt_image_t, exif_flash, char_64); luaA_struct_member(L, dt_image_t, exif_exposure_program, char_64); luaA_struct_member(L, dt_image_t, exif_metering_mode, char_64); + luaA_struct_member(L, dt_image_t, exif_film_mode, char_64); luaA_struct_member(L, dt_image_t, filename, const char_filename_length); luaA_struct_member(L, dt_image_t, width, const int32_t); luaA_struct_member(L, dt_image_t, height, const int32_t);