static void entry_activated (GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; const gchar *tag = gtk_entry_get_text(d->entry); if(!tag || tag[0] == '\0') return; /** attach tag to selected images */ dt_tag_attach_string_list(tag, -1); dt_image_synch_xmp(-1); update(self, 1); update(self, 0); gtk_entry_set_text(d->entry, ""); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
static void new_button_clicked(GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; const gchar *tag = gtk_entry_get_text(d->entry); /** attach tag to selected images */ dt_tag_attach_string_list(tag, -1); dt_image_synch_xmp(-1); update(self, 1); update(self, 0); /** clear input box */ gtk_entry_set_text(d->entry, ""); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
// WARNING: also change src/libs/import.c when changing this! int set_params(dt_lib_module_t *self, const void *params, int size) { if(!params) return 1; char *buf = (char *)params; const char *title = buf; if(!title) return 1; const int title_len = strlen(title) + 1; buf += title_len; const char *description = buf; if(!description) return 1; const int description_len = strlen(description) + 1; buf += description_len; const char *rights = buf; if(!rights) return 1; const int rights_len = strlen(rights) + 1; buf += rights_len; const char *creator = buf; if(!creator) return 1; const int creator_len = strlen(creator) + 1; buf += creator_len; const char *publisher = buf; if(!publisher) return 1; const int publisher_len = strlen(publisher) + 1; if(size != title_len + description_len + rights_len + creator_len + publisher_len) return 1; if(title != NULL && title[0] != '\0') dt_metadata_set(-1, "Xmp.dc.title", title); if(description != NULL && description[0] != '\0') dt_metadata_set(-1, "Xmp.dc.description", description); if(rights != NULL && rights[0] != '\0') dt_metadata_set(-1, "Xmp.dc.rights", rights); if(creator != NULL && creator[0] != '\0') dt_metadata_set(-1, "Xmp.dc.creator", creator); if(publisher != NULL && publisher[0] != '\0') dt_metadata_set(-1, "Xmp.dc.publisher", publisher); dt_image_synch_xmp(-1); update(self, FALSE); return 0; }
static void entry_activated (GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; const gchar *tag = gtk_entry_get_text(d->entry); if(!tag || tag[0] == '\0') return; /* create new tag */ guint tid=0; dt_tag_new(tag, &tid); /** attach tag to selected images */ dt_tag_attach(tid,-1); dt_image_synch_xmp(-1); update(self, 1); update(self, 0); gtk_entry_set_text(d->entry, ""); }
static void detach_selected_tag(dt_lib_module_t *self, dt_lib_tagging_t *d) { GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->current; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return; guint tagid; gtk_tree_model_get(model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); int imgsel = -1; if(tagid <= 0) return; imgsel = dt_view_get_image_to_act_on(); dt_tag_detach(tagid, imgsel); dt_image_synch_xmp(imgsel); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
static void write_metadata(dt_lib_module_t *self) { dt_lib_metadata_t *d = (dt_lib_metadata_t *)self->data; int32_t mouse_over_id; d->editing = FALSE; mouse_over_id = d->imgsel; gchar *title = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(d->title)); gchar *description = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(d->description)); gchar *rights = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(d->rights)); gchar *creator = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(d->creator)); gchar *publisher = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(d->publisher)); if(title != NULL && (d->multi_title == FALSE || gtk_combo_box_get_active(GTK_COMBO_BOX(d->title)) != 0)) dt_metadata_set(mouse_over_id, "Xmp.dc.title", title); if(description != NULL && (d->multi_description == FALSE || gtk_combo_box_get_active(GTK_COMBO_BOX(d->description)) != 0)) dt_metadata_set(mouse_over_id, "Xmp.dc.description", description); if(rights != NULL && (d->multi_rights == FALSE || gtk_combo_box_get_active(GTK_COMBO_BOX(d->rights)) != 0)) dt_metadata_set(mouse_over_id, "Xmp.dc.rights", rights); if(creator != NULL && (d->multi_creator == FALSE || gtk_combo_box_get_active(GTK_COMBO_BOX(d->creator)) != 0)) dt_metadata_set(mouse_over_id, "Xmp.dc.creator", creator); if(publisher != NULL && (d->multi_publisher == FALSE || gtk_combo_box_get_active(GTK_COMBO_BOX(d->publisher)) != 0)) dt_metadata_set(mouse_over_id, "Xmp.dc.publisher", publisher); if(title != NULL) g_free(title); if(description != NULL) g_free(description); if(rights != NULL) g_free(rights); if(creator != NULL) g_free(creator); if(publisher != NULL) g_free(publisher); dt_image_synch_xmp(mouse_over_id); update(self, FALSE); }
static void detach_selected_tag(dt_lib_module_t *self, dt_lib_tagging_t *d) { GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->current; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return; guint tagid; gtk_tree_model_get (model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); int imgsel = -1; if(tagid <= 0) return; DT_CTL_GET_GLOBAL(imgsel, lib_image_mouse_over_id); dt_tag_detach(tagid,imgsel); dt_image_synch_xmp(imgsel); }
static int rights_member(lua_State *L) { if(lua_gettop(L) != 3) { const dt_image_t *my_image = checkreadimage(L, 1); GList *res = dt_metadata_get(my_image->id, "Xmp.dc.rights", NULL); if(res) lua_pushstring(L, (char *)res->data); else lua_pushstring(L, ""); releasereadimage(L, my_image); g_list_free_full(res, g_free); return 1; } else { dt_image_t *my_image = checkwriteimage(L, 1); dt_metadata_set(my_image->id, "Xmp.dc.rights", luaL_checkstring(L, 3)); dt_image_synch_xmp(my_image->id); releasewriteimage(L, my_image); return 0; } }
static void attach_selected_tag(dt_lib_module_t *self, dt_lib_tagging_t *d) { GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->related; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter) && !gtk_tree_model_get_iter_first(model, &iter)) return; guint tagid; gtk_tree_model_get (model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); int imgsel = -1; if(tagid <= 0) return; imgsel = dt_view_get_image_to_act_on(); dt_tag_attach(tagid,imgsel); dt_image_synch_xmp(imgsel); dt_collection_hint_message(darktable.collection); }
void dt_styles_apply_to_image(const char *name,gboolean duplicate, int32_t imgid) { int id=0; sqlite3_stmt *stmt; if ((id=dt_styles_get_id_by_name(name)) != 0) { /* check if we should make a duplicate before applying style */ if (duplicate) imgid = dt_image_duplicate (imgid); /* if merge onto history stack, lets find history offest in destination image */ int32_t offs = 0; #if 1 { /* apply on top of history stack */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(num) from history where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if (sqlite3_step (stmt) == SQLITE_ROW) offs = sqlite3_column_int (stmt, 0); } #else { /* replace history stack */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from history where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step (stmt); } #endif sqlite3_finalize (stmt); /* copy history items from styles onto image */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into history (imgid,num,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name) select ?1, num+?2,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name from style_items where styleid=?3", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id); sqlite3_step (stmt); sqlite3_finalize (stmt); /* add tag */ guint tagid=0; gchar ntag[512]= {0}; g_snprintf(ntag,512,"darktable|style|%s",name); if (dt_tag_new(ntag,&tagid)) dt_tag_attach(tagid,imgid); /* if current image in develop reload history */ if (dt_dev_is_current_image(darktable.develop, imgid)) { dt_dev_reload_history_items (darktable.develop); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); } /* update xmp file */ dt_image_synch_xmp(imgid); /* remove old obsolete thumbnails */ dt_mipmap_cache_remove(darktable.mipmap_cache, imgid); /* redraw center view to update visible mipmaps */ dt_control_queue_redraw_center(); } }
void dt_lightroom_import(int imgid, dt_develop_t *dev, gboolean iauto) { gboolean refresh_needed = FALSE; char imported[256] = { 0 }; // Get full pathname char *pathname = dt_get_lightroom_xmp(imgid); if(!pathname) { if(!iauto) dt_control_log(_("cannot find lightroom XMP!")); return; } // Load LR xmp xmlDocPtr doc; xmlNodePtr entryNode; // Parse xml document doc = xmlParseEntity(pathname); if(doc == NULL) { g_free(pathname); return; } // Enter first node, xmpmeta entryNode = xmlDocGetRootElement(doc); if(entryNode == NULL) { g_free(pathname); xmlFreeDoc(doc); return; } if(xmlStrcmp(entryNode->name, (const xmlChar *)"xmpmeta")) { if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } // Check that this is really a Lightroom document xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); if(xpathCtx == NULL) { g_free(pathname); xmlFreeDoc(doc); return; } xmlXPathRegisterNs(xpathCtx, BAD_CAST "stEvt", BAD_CAST "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"); xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar *)"//@stEvt:softwareAgent", xpathCtx); if(xpathObj == NULL) { if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); xmlXPathFreeContext(xpathCtx); g_free(pathname); xmlFreeDoc(doc); return; } xmlNodeSetPtr xnodes = xpathObj->nodesetval; if(xnodes != NULL && xnodes->nodeNr > 0) { xmlNodePtr xnode = xnodes->nodeTab[0]; xmlChar *value = xmlNodeListGetString(doc, xnode->xmlChildrenNode, 1); if(!strstr((char *)value, "Lightroom")) { xmlXPathFreeContext(xpathCtx); xmlXPathFreeObject(xpathObj); xmlFreeDoc(doc); xmlFree(value); if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } xmlFree(value); } else { xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); // Go safely to Description node if(entryNode) entryNode = entryNode->xmlChildrenNode; if(entryNode) entryNode = entryNode->next; if(entryNode) entryNode = entryNode->xmlChildrenNode; if(entryNode) entryNode = entryNode->next; if(!entryNode || xmlStrcmp(entryNode->name, (const xmlChar *)"Description")) { if(!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname); g_free(pathname); return; } g_free(pathname); // Look for attributes in the Description dt_iop_clipping_params_t pc; memset(&pc, 0, sizeof(pc)); gboolean has_crop = FALSE; dt_iop_flip_params_t pf; memset(&pf, 0, sizeof(pf)); gboolean has_flip = FALSE; dt_iop_exposure_params_t pe; memset(&pe, 0, sizeof(pe)); gboolean has_exposure = FALSE; dt_iop_vignette_params_t pv; memset(&pv, 0, sizeof(pv)); gboolean has_vignette = FALSE; dt_iop_grain_params_t pg; memset(&pg, 0, sizeof(pg)); gboolean has_grain = FALSE; dt_iop_spots_params_t ps; memset(&ps, 0, sizeof(ps)); gboolean has_spots = FALSE; typedef enum lr_curve_kind_t { linear = 0, medium_contrast = 1, string_contrast = 2, custom = 3 } lr_curve_kind_t; #define MAX_PTS 20 dt_iop_tonecurve_params_t ptc; memset(&ptc, 0, sizeof(ptc)); int ptc_value[4] = { 0, 0, 0, 0 }; float ptc_split[3] = { 0.0, 0.0, 0.0 }; lr_curve_kind_t curve_kind = linear; int curve_pts[MAX_PTS][2]; int n_pts = 0; dt_iop_colorzones_params_t pcz; memset(&pcz, 0, sizeof(pcz)); gboolean has_colorzones = FALSE; dt_iop_splittoning_params_t pst; memset(&pst, 0, sizeof(pst)); gboolean has_splittoning = FALSE; dt_iop_bilat_params_t pbl; memset(&pbl, 0, sizeof(pbl)); gboolean has_bilat = FALSE; gboolean has_tags = FALSE; int rating = 0; gboolean has_rating = FALSE; gdouble lat = 0, lon = 0; gboolean has_gps = FALSE; int color = 0; gboolean has_colorlabel = FALSE; float fratio = 0; // factor ratio image float crop_roundness = 0; // from lightroom int n_import = 0; // number of iop imported const float hfactor = 3.0 / 9.0; // hue factor adjustment (use 3 out of 9 boxes in colorzones) const float lfactor = 4.0 / 9.0; // lightness factor adjustment (use 4 out of 9 boxes in colorzones) int iwidth = 0, iheight = 0; // image width / height int orientation = 1; xmlAttr *attribute = entryNode->properties; while(attribute && attribute->name && attribute->children) { xmlChar *value = xmlNodeListGetString(entryNode->doc, attribute->children, 1); if(!xmlStrcmp(attribute->name, (const xmlChar *)"CropTop")) pc.cy = g_ascii_strtod((char *)value, NULL); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"CropRight")) pc.cw = g_ascii_strtod((char *)value, NULL); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"CropLeft")) pc.cx = g_ascii_strtod((char *)value, NULL); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"CropBottom")) pc.ch = g_ascii_strtod((char *)value, NULL); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"CropAngle")) pc.angle = -g_ascii_strtod((char *)value, NULL); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ImageWidth")) iwidth = atoi((char *)value); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ImageLength")) iheight = atoi((char *)value); else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Orientation")) { orientation = atoi((char *)value); if(dev != NULL && ((dev->image_storage.orientation == 6 && orientation != 6) || (dev->image_storage.orientation == 5 && orientation != 8) || (dev->image_storage.orientation == 0 && orientation != 1))) has_flip = TRUE; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HasCrop")) { if(!xmlStrcmp(value, (const xmlChar *)"True")) has_crop = TRUE; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Blacks2012")) { int v = atoi((char *)value); if(v != 0) { has_exposure = TRUE; pe.black = lr2dt_blacks((float)v); } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Exposure2012")) { float v = g_ascii_strtod((char *)value, NULL); if(v != 0.0) { has_exposure = TRUE; pe.exposure = lr2dt_exposure(v); } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"PostCropVignetteAmount")) { int v = atoi((char *)value); if(v != 0) { has_vignette = TRUE; pv.brightness = lr2dt_vignette_gain((float)v); } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"PostCropVignetteMidpoint")) { int v = atoi((char *)value); pv.scale = lr2dt_vignette_midpoint((float)v); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"PostCropVignetteStyle")) { int v = atoi((char *)value); if(v == 1) // Highlight Priority pv.saturation = -0.300; else // Color Priority & Paint Overlay pv.saturation = -0.200; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"PostCropVignetteFeather")) { int v = atoi((char *)value); if(v != 0) pv.falloff_scale = (float)v; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"PostCropVignetteRoundness")) { int v = atoi((char *)value); crop_roundness = (float)v; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"GrainAmount")) { int v = atoi((char *)value); if(v != 0) { has_grain = TRUE; pg.strength = lr2dt_grain_amount((float)v); } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"GrainFrequency")) { int v = atoi((char *)value); if(v != 0) pg.scale = lr2dt_grain_frequency((float)v); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricShadows")) { ptc_value[0] = atoi((char *)value); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricDarks")) { ptc_value[1] = atoi((char *)value); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricLights")) { ptc_value[2] = atoi((char *)value); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricHighlights")) { ptc_value[3] = atoi((char *)value); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricShadowSplit")) { ptc_split[0] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricMidtoneSplit")) { ptc_split[1] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ParametricHighlightSplit")) { ptc_split[2] = g_ascii_strtod((char *)value, NULL) / 100.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"ToneCurveName2012")) { if(!xmlStrcmp(value, (const xmlChar *)"Linear")) curve_kind = linear; else if(!xmlStrcmp(value, (const xmlChar *)"Medium Contrast")) curve_kind = medium_contrast; else if(!xmlStrcmp(value, (const xmlChar *)"Strong Contrast")) curve_kind = medium_contrast; else if(!xmlStrcmp(value, (const xmlChar *)"Custom")) curve_kind = custom; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentRed")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][0] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentOrange")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][1] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentYellow")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][2] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentGreen")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][3] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentAqua")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][4] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentBlue")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][5] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentPurple")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][6] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SaturationAdjustmentMagenta")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[1][7] = 0.5 + (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentRed")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][0] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentOrange")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][1] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentYellow")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][2] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentGreen")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][3] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentAqua")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][4] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentBlue")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][5] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentPurple")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][6] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"LuminanceAdjustmentMagenta")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[0][7] = 0.5 + lfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentRed")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][0] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentOrange")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][1] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentYellow")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][2] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentGreen")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][3] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentAqua")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][4] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentBlue")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][5] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentPurple")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][6] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"HueAdjustmentMagenta")) { int v = atoi((char *)value); if(v != 0) has_colorzones = TRUE; pcz.equalizer_y[2][7] = 0.5 + hfactor * (float)v / 200.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SplitToningShawowHue")) { int v = atoi((char *)value); if(v != 0) has_splittoning = TRUE; pst.shadow_hue = (float)v / 255.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SplitToningShawowSaturation")) { int v = atoi((char *)value); if(v != 0) has_splittoning = TRUE; pst.shadow_saturation = (float)v / 100.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SplitToningHighlightHue")) { int v = atoi((char *)value); if(v != 0) has_splittoning = TRUE; pst.highlight_hue = (float)v / 255.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SplitToningHighlightSaturation")) { int v = atoi((char *)value); if(v != 0) has_splittoning = TRUE; pst.highlight_saturation = (float)v / 100.0; } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"SplitToningBalance")) { float v = g_ascii_strtod((char *)value, NULL); pst.balance = lr2dt_splittoning_balance(v); } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Clarity2012")) { int v = atoi((char *)value); if(v != 0) { has_bilat = TRUE; pbl.detail = lr2dt_clarity((float)v); } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Rating")) { int v = atoi((char *)value); if(v != 0) { rating = v; has_rating = TRUE; } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"GPSLatitude")) { int deg; double msec; char d; if(sscanf((const char *)value, "%d,%lf%c", °, &msec, &d)) { lat = deg + msec / 60.0; if(d == 'S') lat = -lat; has_gps = TRUE; } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"GPSLongitude")) { int deg; double msec; char d; if(sscanf((const char *)value, "%d,%lf%c", °, &msec, &d)) { lon = deg + msec / 60.0; if(d == 'W') lon = -lon; has_gps = TRUE; } } else if(!xmlStrcmp(attribute->name, (const xmlChar *)"Label")) { for(int i = 0; value[i]; i++) value[i] = tolower(value[i]); if(!strcmp((char *)value, _("red"))) color = 0; else if(!strcmp((char *)value, _("yellow"))) color = 1; else if(!strcmp((char *)value, _("green"))) color = 2; else if(!strcmp((char *)value, _("blue"))) color = 3; else // just an else here to catch all other cases as on lightroom one can // change the names of labels. So purple and the user's defined labels // will be mapped to purple on darktable. color = 4; has_colorlabel = TRUE; } xmlFree(value); attribute = attribute->next; } // Look for tags (subject/Bag/* and RetouchInfo/seq/*) entryNode = entryNode->xmlChildrenNode; if(entryNode) entryNode = entryNode->next; while(entryNode) { if(dev == NULL && (!xmlStrcmp(entryNode->name, (const xmlChar *)"subject") || !xmlStrcmp(entryNode->name, (const xmlChar *)"hierarchicalSubject"))) { xmlNodePtr tagNode = entryNode; tagNode = tagNode->xmlChildrenNode; tagNode = tagNode->next; tagNode = tagNode->xmlChildrenNode; tagNode = tagNode->next; while(tagNode) { if(!xmlStrcmp(tagNode->name, (const xmlChar *)"li")) { xmlChar *value = xmlNodeListGetString(doc, tagNode->xmlChildrenNode, 1); guint tagid = 0; if(!dt_tag_exists((char *)value, &tagid)) dt_tag_new((char *)value, &tagid); dt_tag_attach(tagid, imgid); has_tags = TRUE; xmlFree(value); } tagNode = tagNode->next; } } else if(dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *)"RetouchInfo")) { xmlNodePtr riNode = entryNode; riNode = riNode->xmlChildrenNode; riNode = riNode->next; riNode = riNode->xmlChildrenNode; riNode = riNode->next; while(riNode) { if(!xmlStrcmp(riNode->name, (const xmlChar *)"li")) { xmlChar *value = xmlNodeListGetString(doc, riNode->xmlChildrenNode, 1); spot_t *p = &ps.spot[ps.num_spots]; if(sscanf((const char *)value, "centerX = %f, centerY = %f, radius = %f, sourceState = %*[a-zA-Z], " "sourceX = %f, sourceY = %f", &(p->x), &(p->y), &(p->radius), &(p->xc), &(p->yc))) { ps.num_spots++; has_spots = TRUE; } xmlFree(value); } if(ps.num_spots == MAX_SPOTS) break; riNode = riNode->next; } } else if(dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *)"ToneCurvePV2012")) { xmlNodePtr tcNode = entryNode; tcNode = tcNode->xmlChildrenNode; tcNode = tcNode->next; tcNode = tcNode->xmlChildrenNode; tcNode = tcNode->next; while(tcNode) { if(!xmlStrcmp(tcNode->name, (const xmlChar *)"li")) { xmlChar *value = xmlNodeListGetString(doc, tcNode->xmlChildrenNode, 1); if(sscanf((const char *)value, "%d, %d", &(curve_pts[n_pts][0]), &(curve_pts[n_pts][1]))) n_pts++; xmlFree(value); } if(n_pts == MAX_PTS) break; tcNode = tcNode->next; } } entryNode = entryNode->next; } xmlFreeDoc(doc); // Integrates into the history all the imported iop if(dev != NULL && dt_image_is_raw(&dev->image_storage)) { // set colorin to cmatrix which is the default from Adobe (so closer to what Lightroom does) dt_iop_colorin_params_t pci = (dt_iop_colorin_params_t){ "cmatrix", DT_INTENT_PERCEPTUAL }; dt_add_hist(imgid, "colorin", (dt_iop_params_t *)&pci, sizeof(dt_iop_colorin_params_t), imported, sizeof(imported), LRDT_COLORIN_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_crop) { pc.k_sym = 0; pc.k_apply = 0; pc.crop_auto = 0; pc.k_h = pc.k_v = 0; pc.k_type = 0; pc.kxa = pc.kxd = 0.2f; pc.kxc = pc.kxb = 0.8f; pc.kya = pc.kyb = 0.2f; pc.kyc = pc.kyd = 0.8f; float tmp; if(has_crop) { // adjust crop data according to the rotation switch(dev->image_storage.orientation) { case 5: // portrait - counter-clockwise tmp = pc.ch; pc.ch = 1.0 - pc.cx; pc.cx = pc.cy; pc.cy = 1.0 - pc.cw; pc.cw = tmp; break; case 6: // portrait - clockwise tmp = pc.ch; pc.ch = pc.cw; pc.cw = 1.0 - pc.cy; pc.cy = pc.cx; pc.cx = 1.0 - tmp; break; default: break; } if(pc.angle != 0) { const float rangle = -pc.angle * (3.141592 / 180); float x, y; // do the rotation (rangle) using center of image (0.5, 0.5) x = pc.cx - 0.5; y = 0.5 - pc.cy; pc.cx = 0.5 + x * cos(rangle) - y * sin(rangle); pc.cy = 0.5 - (x * sin(rangle) + y * cos(rangle)); x = pc.cw - 0.5; y = 0.5 - pc.ch; pc.cw = 0.5 + x * cos(rangle) - y * sin(rangle); pc.ch = 0.5 - (x * sin(rangle) + y * cos(rangle)); } } else { pc.angle = 0; pc.cx = 0; pc.cy = 0; pc.cw = 1; pc.ch = 1; } fratio = (pc.cw - pc.cx) / (pc.ch - pc.cy); dt_add_hist(imgid, "clipping", (dt_iop_params_t *)&pc, sizeof(dt_iop_clipping_params_t), imported, sizeof(imported), LRDT_CLIPPING_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_flip) { pf.orientation = 0; if(dev->image_storage.orientation == 5) // portrait switch(orientation) { case 8: pf.orientation = 0; break; case 3: pf.orientation = 5; break; case 6: pf.orientation = 3; break; case 1: pf.orientation = 6; break; // with horizontal flip case 7: pf.orientation = 1; break; case 2: pf.orientation = 4; break; case 5: pf.orientation = 2; break; case 4: pf.orientation = 7; break; } else if(dev->image_storage.orientation == 6) // portrait switch(orientation) { case 8: pf.orientation = 3; break; case 3: pf.orientation = 6; break; case 6: pf.orientation = 0; break; case 1: pf.orientation = 5; break; // with horizontal flip case 7: pf.orientation = 2; break; case 2: pf.orientation = 7; break; case 5: pf.orientation = 1; break; case 4: pf.orientation = 4; break; } else // landscape switch(orientation) { case 8: pf.orientation = 5; break; case 3: pf.orientation = 3; break; case 6: pf.orientation = 6; break; case 1: pf.orientation = 0; break; // with horizontal flip case 7: pf.orientation = 7; break; case 2: pf.orientation = 1; break; case 5: pf.orientation = 4; break; case 4: pf.orientation = 2; break; } dt_add_hist(imgid, "flip", (dt_iop_params_t *)&pf, sizeof(dt_iop_flip_params_t), imported, sizeof(imported), LRDT_FLIP_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_exposure) { dt_add_hist(imgid, "exposure", (dt_iop_params_t *)&pe, sizeof(dt_iop_exposure_params_t), imported, sizeof(imported), LRDT_EXPOSURE_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_grain) { pg.channel = 0; dt_add_hist(imgid, "grain", (dt_iop_params_t *)&pg, sizeof(dt_iop_grain_params_t), imported, sizeof(imported), LRDT_GRAIN_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_vignette) { const float base_ratio = 1.325 / 1.5; pv.autoratio = FALSE; pv.dithering = DITHER_8BIT; pv.center.x = 0.0; pv.center.y = 0.0; pv.shape = 1.0; // defensive code, should not happen, but just in case future Lr version // has not ImageWidth/ImageLength XML tag. if(iwidth == 0 || iheight == 0) pv.whratio = base_ratio; else pv.whratio = base_ratio * ((float)iwidth / (float)iheight); if(has_crop) pv.whratio = pv.whratio * fratio; // Adjust scale and ratio based on the roundness. On Lightroom changing // the roundness change the width and the height of the vignette. if(crop_roundness > 0) { float newratio = pv.whratio - (pv.whratio - 1) * (crop_roundness / 100.0); float dscale = (1 - (newratio / pv.whratio)) / 2.0; pv.scale -= dscale * 100.0; pv.whratio = newratio; } dt_add_hist(imgid, "vignette", (dt_iop_params_t *)&pv, sizeof(dt_iop_vignette_params_t), imported, sizeof(imported), LRDT_VIGNETTE_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_spots) { // Check for orientation, rotate when in portrait mode if(orientation > 4) for(int k = 0; k < ps.num_spots; k++) { float tmp = ps.spot[k].y; ps.spot[k].y = 1.0 - ps.spot[k].x; ps.spot[k].x = tmp; tmp = ps.spot[k].yc; ps.spot[k].yc = 1.0 - ps.spot[k].xc; ps.spot[k].xc = tmp; } dt_add_hist(imgid, "spots", (dt_iop_params_t *)&ps, sizeof(dt_iop_spots_params_t), imported, sizeof(imported), LRDT_SPOTS_VERSION, &n_import); refresh_needed = TRUE; } if(curve_kind != linear || ptc_value[0] != 0 || ptc_value[1] != 0 || ptc_value[2] != 0 || ptc_value[3] != 0) { ptc.tonecurve_nodes[ch_L] = 6; ptc.tonecurve_nodes[ch_a] = 7; ptc.tonecurve_nodes[ch_b] = 7; ptc.tonecurve_type[ch_L] = CUBIC_SPLINE; ptc.tonecurve_type[ch_a] = CUBIC_SPLINE; ptc.tonecurve_type[ch_b] = CUBIC_SPLINE; ptc.tonecurve_autoscale_ab = 1; ptc.tonecurve_preset = 0; float linear_ab[7] = { 0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0 }; // linear a, b curves for(int k = 0; k < 7; k++) ptc.tonecurve[ch_a][k].x = linear_ab[k]; for(int k = 0; k < 7; k++) ptc.tonecurve[ch_a][k].y = linear_ab[k]; for(int k = 0; k < 7; k++) ptc.tonecurve[ch_b][k].x = linear_ab[k]; for(int k = 0; k < 7; k++) ptc.tonecurve[ch_b][k].y = linear_ab[k]; // Set the base tonecurve if(curve_kind == linear) { ptc.tonecurve[ch_L][0].x = 0.0; ptc.tonecurve[ch_L][0].y = 0.0; ptc.tonecurve[ch_L][1].x = ptc_split[0] / 2.0; ptc.tonecurve[ch_L][1].y = ptc_split[0] / 2.0; ptc.tonecurve[ch_L][2].x = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0; ptc.tonecurve[ch_L][2].y = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0; ptc.tonecurve[ch_L][3].x = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0; ptc.tonecurve[ch_L][3].y = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0; ptc.tonecurve[ch_L][4].x = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0; ptc.tonecurve[ch_L][4].y = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0; ptc.tonecurve[ch_L][5].x = 1.0; ptc.tonecurve[ch_L][5].y = 1.0; } else { for(int k = 0; k < 6; k++) { ptc.tonecurve[ch_L][k].x = curve_pts[k][0] / 255.0; ptc.tonecurve[ch_L][k].y = curve_pts[k][1] / 255.0; } } if(curve_kind != custom) { // set shadows/darks/lights/highlight adjustments ptc.tonecurve[ch_L][1].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[0] / 100.0); ptc.tonecurve[ch_L][2].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[1] / 100.0); ptc.tonecurve[ch_L][3].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[2] / 100.0); ptc.tonecurve[ch_L][4].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[3] / 100.0); if(ptc.tonecurve[ch_L][1].y > ptc.tonecurve[ch_L][2].y) ptc.tonecurve[ch_L][1].y = ptc.tonecurve[ch_L][2].y; if(ptc.tonecurve[ch_L][3].y > ptc.tonecurve[ch_L][4].y) ptc.tonecurve[ch_L][4].y = ptc.tonecurve[ch_L][3].y; } dt_add_hist(imgid, "tonecurve", (dt_iop_params_t *)&ptc, sizeof(dt_iop_tonecurve_params_t), imported, sizeof(imported), LRDT_TONECURVE_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_colorzones) { pcz.channel = DT_IOP_COLORZONES_h; for(int i = 0; i < 3; i++) for(int k = 0; k < 8; k++) pcz.equalizer_x[i][k] = k / (DT_IOP_COLORZONES_BANDS - 1.0); dt_add_hist(imgid, "colorzones", (dt_iop_params_t *)&pcz, sizeof(dt_iop_colorzones_params_t), imported, sizeof(imported), LRDT_COLORZONES_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_splittoning) { pst.compress = 50.0; dt_add_hist(imgid, "splittoning", (dt_iop_params_t *)&pst, sizeof(dt_iop_splittoning_params_t), imported, sizeof(imported), LRDT_SPLITTONING_VERSION, &n_import); refresh_needed = TRUE; } if(dev != NULL && has_bilat) { pbl.sigma_r = 100.0; pbl.sigma_s = 100.0; dt_add_hist(imgid, "bilat", (dt_iop_params_t *)&pbl, sizeof(dt_iop_bilat_params_t), imported, sizeof(imported), LRDT_BILAT_VERSION, &n_import); refresh_needed = TRUE; } if(has_tags) { if(imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("tags"), sizeof(imported)); n_import++; } if(dev == NULL && has_rating) { dt_ratings_apply_to_image(imgid, rating); if(imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("rating"), sizeof(imported)); n_import++; } if(dev == NULL && has_gps) { dt_image_set_location(imgid, lon, lat); if(imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("geotagging"), sizeof(imported)); n_import++; } if(dev == NULL && has_colorlabel) { dt_colorlabels_set_label(imgid, color); if(imported[0]) g_strlcat(imported, ", ", sizeof(imported)); g_strlcat(imported, _("color label"), sizeof(imported)); n_import++; } if(dev != NULL && refresh_needed && dev->gui_attached) { char message[512]; g_snprintf(message, sizeof(message), ngettext("%s has been imported", "%s have been imported", n_import), imported); dt_control_log(message); if(!iauto) { /* signal history changed */ dt_dev_reload_history_items(dev); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); /* update xmp file */ dt_image_synch_xmp(imgid); dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_HISTORY_CHANGE); } } }
static void delete_button_clicked (GtkButton *button, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_tagging_t *d = (dt_lib_tagging_t *)self->data; int res = GTK_RESPONSE_YES; guint tagid; GtkTreeIter iter; GtkTreeModel *model = NULL; GtkTreeView *view = d->related; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1); // First check how many images are affected by the remove int count = dt_tag_remove(tagid,FALSE); if( count > 0 && dt_conf_get_bool("plugins/lighttable/tagging/ask_before_delete_tag") ) { GtkWidget *dialog; GtkWidget *win = dt_ui_main_window(darktable.gui->ui); gchar *tagname=dt_tag_get_name(tagid); dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, ngettext("do you really want to delete the tag `%s'?\n%d image is assigned this tag!", "do you really want to delete the tag `%s'?\n%d images are assigned this tag!", count), tagname,count); gtk_window_set_title(GTK_WINDOW(dialog), _("delete tag?")); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); free(tagname); } if(res != GTK_RESPONSE_YES) return; GList *tagged_images = NULL; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from tagged_images where tagid=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, tagid); while(sqlite3_step(stmt) == SQLITE_ROW) { tagged_images = g_list_append(tagged_images, GINT_TO_POINTER(sqlite3_column_int(stmt, 0))); } sqlite3_finalize(stmt); dt_tag_remove(tagid,TRUE); GList *list_iter; if((list_iter = g_list_first(tagged_images)) != NULL) { do { dt_image_synch_xmp(GPOINTER_TO_INT(list_iter->data)); } while((list_iter=g_list_next(list_iter)) != NULL); } g_list_free(g_list_first(tagged_images)); update(self, 0); update(self, 1); dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED); }
int dt_history_copy_and_paste_on_image (int32_t imgid, int32_t dest_imgid, gboolean merge, GList *ops) { sqlite3_stmt *stmt; if(imgid==dest_imgid) return 1; if(imgid==-1) { dt_control_log(_("you need to copy history from an image before you paste it onto another")); return 1; } /* if merge onto history stack, lets find history offest in destination image */ int32_t offs = 0; if (merge) { /* apply on top of history stack */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT MAX(num)+1 FROM history WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); if (sqlite3_step (stmt) == SQLITE_ROW) offs = sqlite3_column_int (stmt, 0); } else { /* replace history stack */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from history where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step (stmt); } sqlite3_finalize (stmt); // prepare SQL request char req[2048]; strcpy (req, "insert into history (imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version, multi_name, multi_priority) select ?1, num+?2, module, operation, op_params, enabled, blendop_params, blendop_version, multi_name, multi_priority from history where imgid = ?3"); // Add ops selection if any format: ... and num in (val1, val2) if (ops) { GList *l = ops; int first = 1; strcat (req, " and num in ("); while (l) { unsigned int value = GPOINTER_TO_UINT(l->data); char v[30]; if (!first) strcat (req, ","); snprintf (v, 30, "%u", value); strcat (req, v); first=0; l = g_list_next(l); } strcat (req, ")"); } /* add the history items to stack offest */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs); DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, imgid); sqlite3_step (stmt); sqlite3_finalize (stmt); if (merge && ops) _dt_history_cleanup_multi_instance(dest_imgid, offs); //we have to copy masks too //what to do with existing masks ? if (merge) { //there's very little chance that we will have same shapes id. //but we may want to handle this case anyway //and it's not trivial at all ! } else { //let's remove all existing shapes DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from mask where imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step (stmt); sqlite3_finalize (stmt); } //let's copy now strcpy (req, "insert into mask (imgid, formid, form, name, version, points, points_count, source) select ?1, formid, form, name, version, points, points_count, source from mask where imgid = ?2"); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step (stmt); sqlite3_finalize (stmt); /* if current image in develop reload history */ if (dt_dev_is_current_image(darktable.develop, dest_imgid)) { dt_dev_reload_history_items (darktable.develop); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); } /* update xmp file */ dt_image_synch_xmp(dest_imgid); dt_mipmap_cache_remove(darktable.mipmap_cache, dest_imgid); return 0; }
int dt_history_copy_and_paste_on_image(int32_t imgid, int32_t dest_imgid, gboolean merge, GList *ops) { sqlite3_stmt *stmt; if(imgid == dest_imgid) return 1; if(imgid == -1) { dt_control_log(_("you need to copy history from an image before you paste it onto another")); return 1; } // be sure the current history is written before pasting some other history data const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager); if(cv->view((dt_view_t *)cv) == DT_VIEW_DARKROOM) dt_dev_write_history(darktable.develop); /* if merge onto history stack, lets find history offest in destination image */ int32_t offs = 0; if(merge) { /* apply on top of history stack */ // first trim the stack to get rid of whatever is above the selected entry DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid = ?1 AND num >= (SELECT history_end " "FROM main.images WHERE id = imgid)", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT IFNULL(MAX(num), -1)+1 FROM main.history WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); if(sqlite3_step(stmt) == SQLITE_ROW) offs = sqlite3_column_int(stmt, 0); } else { /* replace history stack */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step(stmt); } sqlite3_finalize(stmt); /* delete all items from the temp styles_items, this table is used only to get a ROWNUM of the results */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.style_items", NULL, NULL, NULL); /* copy history items from styles onto temp table */ // prepare SQL request char req[2048]; g_strlcpy(req, "INSERT INTO memory.style_items (num, module, operation, op_params, enabled, blendop_params, " "blendop_version, multi_name, multi_priority) SELECT num, module, operation, " "op_params, enabled, blendop_params, blendop_version, multi_name, multi_priority FROM " "main.history WHERE imgid = ?1", sizeof(req)); // Add ops selection if any format: ... and num in (val1, val2) if(ops) { GList *l = ops; int first = 1; g_strlcat(req, " AND num IN (", sizeof(req)); while(l) { unsigned int value = GPOINTER_TO_UINT(l->data); char v[30]; if(!first) g_strlcat(req, ",", sizeof(req)); snprintf(v, sizeof(v), "%u", value); g_strlcat(req, v, sizeof(req)); first = 0; l = g_list_next(l); } g_strlcat(req, ")", sizeof(req)); } DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); /* copy the history items into the history of the dest image */ /* note: rowid starts at 1 while num has to start at 0! */ DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO main.history " "(imgid,num,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name) SELECT " "?1,?2+rowid-1,module,operation,op_params,enabled,blendop_params,blendop_" "version,multi_priority,multi_name FROM memory.style_items", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs); sqlite3_step(stmt); sqlite3_finalize(stmt); if(merge && ops) _dt_history_cleanup_multi_instance(dest_imgid, offs); // we have to copy masks too // what to do with existing masks ? if(merge) { // there's very little chance that we will have same shapes id. // but we may want to handle this case anyway // and it's not trivial at all ! } else { // let's remove all existing shapes DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.mask WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); } // let's copy now g_strlcpy(req, "INSERT INTO main.mask (imgid, formid, form, name, version, points, points_count, source) SELECT " "?1, formid, form, name, version, points, points_count, source FROM main.mask WHERE imgid = ?2", sizeof(req)); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); // always make the whole stack active DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "UPDATE main.images SET history_end = (SELECT MAX(num) + 1 FROM main.history " "WHERE imgid = ?1) WHERE id = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid); sqlite3_step(stmt); sqlite3_finalize(stmt); /* if current image in develop reload history */ if(dt_dev_is_current_image(darktable.develop, dest_imgid)) { dt_dev_reload_history_items(darktable.develop); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); } /* update xmp file */ dt_image_synch_xmp(dest_imgid); dt_mipmap_cache_remove(darktable.mipmap_cache, dest_imgid); return 0; }
static void clear_button_clicked(GtkButton *button, gpointer user_data) { dt_metadata_clear(-1); dt_image_synch_xmp(-1); update(user_data, FALSE); }
static void dt_dev_change_image(dt_develop_t *dev, const uint32_t imgid) { // stop crazy users from sleeping on key-repeat spacebar: if(dev->image_loading) return; // get last active plugin, make sure focus out is called: gchar *active_plugin = dt_conf_get_string("plugins/darkroom/active"); dt_iop_request_focus(NULL); // store last active group dt_conf_set_int("plugins/darkroom/groups", dt_dev_modulegroups_get(dev)); // store last active plugin: if(darktable.develop->gui_module) dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op); else dt_conf_set_string("plugins/darkroom/active", ""); g_assert(dev->gui_attached); // commit image ops to db dt_dev_write_history(dev); // be sure light table will update the thumbnail // TODO: only if image changed! // if() { dt_mipmap_cache_remove(darktable.mipmap_cache, dev->image_storage.id); dt_image_synch_xmp(dev->image_storage.id); } select_this_image(imgid); while(dev->history) { // clear history of old image free(((dt_dev_history_item_t *)dev->history->data)->params); free( (dt_dev_history_item_t *)dev->history->data); dev->history = g_list_delete_link(dev->history, dev->history); } // get new image: dt_dev_reload_image(dev, imgid); // make sure no signals propagate here: darktable.gui->reset = 1; GList *modules = g_list_last(dev->iop); int nb_iop = g_list_length(dev->iop); dt_dev_pixelpipe_cleanup_nodes(dev->pipe); dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe); for (int i=nb_iop-1; i>0; i--) { dt_iop_module_t *module = (dt_iop_module_t *)(g_list_nth_data(dev->iop,i)); if (module->multi_priority == 0) //if the module is the "base" instance, we keep it { dt_iop_reload_defaults(module); dt_iop_gui_update(module); } else //else we delete it and remove it from the panel { if (!dt_iop_is_hidden(module)) { gtk_container_remove (GTK_CONTAINER(dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER)),module->expander); dt_iop_gui_cleanup_module(module); } //we remove the module from the list dev->iop = g_list_remove_link(dev->iop,g_list_nth(dev->iop,i)); //we cleanup the module dt_accel_disconnect_list(module->accel_closures); dt_accel_cleanup_locals_iop(module); module->accel_closures = NULL; dt_iop_cleanup_module(module); free(module); } } dt_dev_pixelpipe_create_nodes(dev->pipe, dev); dt_dev_pixelpipe_create_nodes(dev->preview_pipe, dev); dt_dev_read_history(dev); //we have to init all module instances other than "base" instance modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); if(module->multi_priority > 0) { if (!dt_iop_is_hidden(module)) { module->gui_init(module); dt_iop_reload_defaults(module); //we search the base iop corresponding GList *mods = g_list_first(dev->iop); dt_iop_module_t *base = NULL; int pos_module = 0; int pos_base = 0; int pos = 0; while (mods) { dt_iop_module_t *mod = (dt_iop_module_t *)(mods->data); if (mod->multi_priority == 0 && mod->instance == module->instance) { base = mod; pos_base = pos; } else if (mod == module) pos_module = pos; mods = g_list_next(mods); pos++; } if (!base) continue; /* add module to right panel */ GtkWidget *expander = dt_iop_gui_get_expander(module); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, expander); GValue gv = { 0, { { 0 } } }; g_value_init(&gv,G_TYPE_INT); gtk_container_child_get_property(GTK_CONTAINER(dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER)),base->expander,"position",&gv); gtk_box_reorder_child (dt_ui_get_container(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER),expander,g_value_get_int(&gv)+pos_base-pos_module); dt_iop_gui_set_expanded(module, TRUE); dt_iop_gui_update_blending(module); } /* setup key accelerators */ module->accel_closures = NULL; if(module->connect_key_accels) module->connect_key_accels(module); dt_iop_connect_common_accels(module); //we update show params for multi-instances for each other instances dt_dev_modules_update_multishow(module->dev); } modules = g_list_next(modules); } dt_dev_pop_history_items(dev, dev->history_end); if(active_plugin) { modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); if(!strcmp(module->op, active_plugin)) dt_iop_request_focus(module); modules = g_list_next(modules); } g_free(active_plugin); } /* last set the group to update visibility of iop modules for new pipe */ dt_dev_modulegroups_set(dev,dt_conf_get_int("plugins/darkroom/groups")); // make signals work again, but only after focus event, // to avoid crop/rotate for example to add another history item. darktable.gui->reset = 0; // Signal develop initialize dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_IMAGE_CHANGED); // prefetch next few from first selected image on. dt_view_filmstrip_prefetch(); }
void dt_styles_apply_to_image(const char *name,gboolean duplicate, int32_t imgid) { int id=0; sqlite3_stmt *stmt; int32_t newimgid; if ((id=dt_styles_get_id_by_name(name)) != 0) { /* check if we should make a duplicate before applying style */ if (duplicate) { newimgid = dt_image_duplicate (imgid); if(newimgid != -1) dt_history_copy_and_paste_on_image(imgid, newimgid, FALSE, NULL); } else newimgid = imgid; /* merge onto history stack, let's find history offest in destination image */ int32_t offs = 0; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT MAX(num)+1 FROM history WHERE imgid = ?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); if (sqlite3_step (stmt) == SQLITE_ROW) offs = sqlite3_column_int (stmt, 0); sqlite3_finalize (stmt); /* delete all items from the temp styles_items, this table is used only to get a ROWNUM of the results */ DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.style_items",NULL,NULL,NULL); /* copy history items from styles onto temp table */ DT_DEBUG_SQLITE3_PREPARE_V2 (dt_database_get(darktable.db), "INSERT INTO MEMORY.style_items SELECT * FROM style_items WHERE styleid=?1 ORDER BY multi_priority DESC;", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id); sqlite3_step (stmt); sqlite3_finalize (stmt); /* copy the style items into the history */ DT_DEBUG_SQLITE3_PREPARE_V2 (dt_database_get(darktable.db), "INSERT INTO history (imgid,num,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name) SELECT ?1,?2+rowid,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name FROM MEMORY.style_items", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs); sqlite3_step (stmt); sqlite3_finalize (stmt); /* add tag */ guint tagid=0; gchar ntag[512]= {0}; g_snprintf(ntag,512,"darktable|style|%s",name); if (dt_tag_new(ntag,&tagid)) dt_tag_attach(tagid,newimgid); /* if current image in develop reload history */ if (dt_dev_is_current_image(darktable.develop, newimgid)) { dt_dev_reload_history_items (darktable.develop); dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop)); } /* update xmp file */ dt_image_synch_xmp(newimgid); /* remove old obsolete thumbnails */ dt_mipmap_cache_remove(darktable.mipmap_cache, newimgid); /* if we have created a duplicate, reset collected images */ if (duplicate) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED); /* redraw center view to update visible mipmaps */ dt_control_queue_redraw_center(); } }
static int image_newindex(lua_State *L) { const char* membername = lua_tostring(L, -2); dt_image_t * my_image=checkwriteimage(L,-3); if(luaA_struct_has_member_name(L,dt_image_t,membername)) { if(luaA_type_has_to_func(luaA_struct_typeof_member_name(L,dt_image_t,membername))) { luaA_struct_to_member_name(L, dt_image_t, my_image, membername,-1); } else { releasewriteimage(L,my_image); luaL_error(L,"%s is read only",membername); } releasewriteimage(L,my_image); return 0; } switch(luaL_checkoption(L,-2,NULL,image_fields_name)) { case RATING: { int my_score = luaL_checkinteger(L,-1); if(my_score > 5) { releasewriteimage(L,my_image); return luaL_error(L,"rating too high : %d",my_score); } if(my_score == -1) my_score = 6; if(my_score < -1) { releasewriteimage(L,my_image); return luaL_error(L,"rating too low : %d",my_score); } my_image->flags &= ~0x7; my_image->flags |= my_score; break; } case CREATOR: dt_metadata_set(my_image->id,"Xmp.dc.creator",luaL_checkstring(L,-1)); dt_image_synch_xmp(my_image->id); break; case PUBLISHER: dt_metadata_set(my_image->id,"Xmp.dc.publisher",luaL_checkstring(L,-1)); dt_image_synch_xmp(my_image->id); break; case TITLE: dt_metadata_set(my_image->id,"Xmp.dc.title",luaL_checkstring(L,-1)); dt_image_synch_xmp(my_image->id); break; case DESCRIPTION: dt_metadata_set(my_image->id,"Xmp.dc.description",luaL_checkstring(L,-1)); dt_image_synch_xmp(my_image->id); break; case RIGHTS: dt_metadata_set(my_image->id,"Xmp.dc.title",luaL_checkstring(L,-1)); dt_image_synch_xmp(my_image->id); break; case LOCAL_COPY: { int imgid = my_image->id; luaL_checktype(L,-1,LUA_TBOOLEAN); // we need to release write image for the other functions to use it releasewriteimage(L,my_image); if(lua_toboolean(L,-1)) { dt_image_local_copy_set(imgid); } else { dt_image_local_copy_reset(imgid); } return 0; } default: releasewriteimage(L,my_image); return luaL_error(L,"unknown index for image : ",lua_tostring(L,-2)); } releasewriteimage(L,my_image); return 0; }
void leave(dt_view_t *self) { /* disconnect from filmstrip image activate */ dt_control_signal_disconnect(darktable.signals, G_CALLBACK(_view_darkroom_filmstrip_activate_callback), (gpointer)self); /* disconnect from pipe finish signal */ dt_control_signal_disconnect(darktable.signals, G_CALLBACK(_darkroom_ui_pipe_finish_signal_callback), (gpointer)self); // store groups for next time: dt_conf_set_int("plugins/darkroom/groups", dt_dev_modulegroups_get(darktable.develop)); // store last active plugin: if(darktable.develop->gui_module) dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op); else dt_conf_set_string("plugins/darkroom/active", ""); dt_develop_t *dev = (dt_develop_t *)self->data; // tag image as changed // TODO: only tag the image when there was a real change. guint tagid = 0; dt_tag_new("darktable|changed",&tagid); dt_tag_attach(tagid, dev->image_storage.id); // commit image ops to db dt_dev_write_history(dev); // be sure light table will regenerate the thumbnail: // TODO: only if changed! // if() { dt_mipmap_cache_remove(darktable.mipmap_cache, dev->image_storage.id); // dump new xmp data dt_image_synch_xmp(dev->image_storage.id); } // clear gui. dev->gui_leaving = 1; dt_pthread_mutex_lock(&dev->history_mutex); dt_dev_pixelpipe_cleanup_nodes(dev->pipe); dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe); while(dev->history) { dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(dev->history->data); // printf("removing history item %d - %s, data %f %f\n", hist->module->instance, hist->module->op, *(float *)hist->params, *((float *)hist->params+1)); free(hist->params); hist->params = NULL; free(hist); dev->history = g_list_delete_link(dev->history, dev->history); } while(dev->iop) { dt_iop_module_t *module = (dt_iop_module_t *)(dev->iop->data); if (!dt_iop_is_hidden(module)) dt_iop_gui_cleanup_module(module); dt_dev_cleanup_module_accels(module); module->accel_closures = NULL; dt_iop_cleanup_module(module) ; free(module); dev->iop = g_list_delete_link(dev->iop, dev->iop); } dt_pthread_mutex_unlock(&dev->history_mutex); dt_print(DT_DEBUG_CONTROL, "[run_job-] 11 %f in darkroom mode\n", dt_get_wtime()); }