/* This file is part of darktable, copyright (c) 2014-2015 pascal obry. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. darktable is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with darktable. If not, see . */ #include #include "common/collection.h" #include "common/colorspaces.h" #include "common/image_cache.h" #include "common/styles.h" #include "common/variables.h" #include "common/cups_print.h" #include "common/image_cache.h" #include "common/pdf.h" #include "common/tags.h" #include "common/printprof.h" #include "dtgtk/resetlabel.h" #include "libs/lib.h" #include "gui/gtk.h" #include "bauhaus/bauhaus.h" DT_MODULE(2) static gboolean _bauhaus_combobox_set_active_text(GtkWidget *cb, const gchar *text); const char* name () { return _("print settings"); } uint32_t views() { return DT_VIEW_PRINT; } uint32_t container() { return DT_UI_CONTAINER_PANEL_RIGHT_CENTER; } typedef struct dt_lib_print_settings_t { GtkWidget *profile, *intent, *style, *style_mode, *papers; GtkWidget *printers, *orientation, *pprofile, *pintent; GtkWidget *width, *height, *black_point_compensation; GtkWidget *info; GList *profiles; GtkButton *print_button; GtkToggleButton *lock_button; GtkWidget *b_top, *b_bottom, *b_left, *b_right; GtkDarktableToggleButton *dtba[9]; // Alignment buttons GList *paper_list; gboolean lock_activated; dt_print_info_t prt; uint16_t *buf; int32_t image_id; int32_t iwidth, iheight; int unit; int v_intent, v_pintent; int v_icctype, v_picctype; char *v_iccprofile, *v_piccprofile, *v_style; gboolean v_style_append, v_black_point_compensation; } dt_lib_print_settings_t; typedef struct dt_lib_export_profile_t { dt_colorspaces_color_profile_type_t type; // filename is only used for type DT_COLORSPACE_FILE char filename[512]; // icc file name char name[512]; // product name int pos, ppos; // position in combo boxen } dt_lib_export_profile_t; typedef struct _dialog_description { const char *name; } dialog_description_t; static double units[3] = {1.0, 0.1, 1.0/25.4}; static void _update_slider (dt_lib_print_settings_t *ps); static const int min_borders = -100; // this is in mm int position () { return 990; } // callbacks for in-memory export typedef struct dt_print_format_t { int max_width, max_height; int width, height; char style[128]; gboolean style_append; int bpp; dt_lib_print_settings_t *ps; } dt_print_format_t; static int bpp(dt_imageio_module_data_t *data) { const dt_print_format_t *d = (dt_print_format_t *)data; return d->bpp; } static int levels(dt_imageio_module_data_t *data) { const dt_print_format_t *d = (dt_print_format_t *)data; return IMAGEIO_RGB | (d->bpp == 8 ? IMAGEIO_INT8 : IMAGEIO_INT16); } static const char *mime(dt_imageio_module_data_t *data) { return "memory"; } static int write_image(dt_imageio_module_data_t *data, const char *filename, const void *in, void *exif, int exif_len, int imgid, int num, int total) { dt_print_format_t *d = (dt_print_format_t *)data; d->ps->buf = (uint16_t *)malloc(d->width * d->height * 3 * (d->bpp == 8?1:2)); if (d->bpp == 8) { const uint8_t *in_ptr = (const uint8_t *)in; uint8_t *out_ptr = (uint8_t *)d->ps->buf; for(int y = 0; y < d->height; y++) { for(int x = 0; x < d->width; x++, in_ptr += 4, out_ptr += 3) memcpy(out_ptr, in_ptr, 3); } } else { const uint16_t *in_ptr = (const uint16_t *)in; uint16_t *out_ptr = (uint16_t *)d->ps->buf; for(int y = 0; y < d->height; y++) { for(int x = 0; x < d->width; x++, in_ptr += 4, out_ptr += 3) memcpy(out_ptr, in_ptr, 6); } } return 0; } static void _print_button_clicked (GtkWidget *widget, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const int imgid = dt_view_filmstrip_get_activated_imgid(darktable.view_manager); if (imgid == -1) { dt_control_log(_("cannot print until a picture is selected")); return; } if (strlen(ps->prt.printer.name) == 0 || ps->prt.printer.resolution == 0) { dt_control_log(_("cannot print until a printer is selected")); return; } if (ps->prt.paper.width == 0 || ps->prt.paper.height == 0) { dt_control_log(_("cannot print until a paper is selected")); return; } dt_control_log(_("prepare printing image %d on `%s'"), imgid, ps->prt.printer.name); // user margin are already in the proper orientation landscape/portrait double width, height; double margin_w = ps->prt.page.margin_left + ps->prt.page.margin_right; double margin_h = ps->prt.page.margin_top + ps->prt.page.margin_bottom; if (ps->prt.page.landscape) { width = ps->prt.paper.height; height = ps->prt.paper.width; margin_w += ps->prt.printer.hw_margin_top + ps->prt.printer.hw_margin_bottom; margin_h += ps->prt.printer.hw_margin_left + ps->prt.printer.hw_margin_right; } else { width = ps->prt.paper.width; height = ps->prt.paper.height; margin_w += ps->prt.printer.hw_margin_left + ps->prt.printer.hw_margin_right; margin_h += ps->prt.printer.hw_margin_top + ps->prt.printer.hw_margin_bottom; } const int32_t width_pix = (width * ps->prt.printer.resolution) / 25.4; const int32_t height_pix = (height * ps->prt.printer.resolution) / 25.4; const double pa_width = (width - margin_w) / 25.4; const double pa_height = (height - margin_h) / 25.4; dt_print(DT_DEBUG_PRINT, "[print] printable area for image %u : %3.2fin x %3.2fin\n", imgid, pa_width, pa_height); // compute the needed size for picture for the given printer resolution const int max_width = (pa_width * ps->prt.printer.resolution); const int max_height = (pa_height * ps->prt.printer.resolution); dt_print(DT_DEBUG_PRINT, "[print] max image size %d x %d (at resolution %d)\n", max_width, max_height, ps->prt.printer.resolution); dt_imageio_module_format_t buf; buf.mime = mime; buf.levels = levels; buf.bpp = bpp; buf.write_image = write_image; dt_print_format_t dat; dat.max_width = max_width; dat.max_height = max_height; dat.style[0] = '\0'; dat.style_append = ps->v_style_append; dat.bpp = *ps->v_piccprofile ? 16 : 8; // set to 16bit when a profile is to be applied dat.ps = ps; char* style = dt_conf_get_string("plugins/print/print/style"); if (style) { g_strlcpy(dat.style, style, sizeof(dat.style)); g_free(style); } // the flags are: ignore exif, display byteorder, high quality, upscale, thumbnail const int high_quality = 1; const int upscale = 1; dt_imageio_export_with_flags(imgid, "unused", &buf, (dt_imageio_module_data_t *)&dat, 1, 0, high_quality, upscale, 0, NULL, FALSE, 0, 0, 1, 1); // after exporting we know the real size of the image, compute the layout // compute print-area (in inches) int32_t px=0, py=0, pwidth=0, pheight=0; int32_t ax=0, ay=0, awidth=0, aheight=0; int32_t ix=0, iy=0, iwidth=0, iheight=0; int32_t iwpix=dat.width, ihpix=dat.height; dt_get_print_layout (imgid, &ps->prt, width_pix, height_pix, &iwpix, &ihpix, &px, &py, &pwidth, &pheight, &ax, &ay, &awidth, &aheight, &ix, &iy, &iwidth, &iheight); const int margin_top = iy; const int margin_left = ix; const int margin_right = pwidth - iwidth - ix; const int margin_bottom = pheight - iheight - iy; dt_print(DT_DEBUG_PRINT, "[print] margins top %d ; bottom %d ; left %d ; right %d\n", margin_top, margin_bottom, margin_left, margin_right); // we have the exported buffer, let's apply the printer profile if (*ps->v_piccprofile) if (dt_apply_printer_profile(imgid, (void **)&(dat.ps->buf), dat.width, dat.height, dat.bpp, dt_colorspaces_get_profile(ps->v_picctype, ps->v_piccprofile, DT_PROFILE_DIRECTION_OUT)->profile, ps->v_pintent, ps->v_black_point_compensation)) { free(dat.ps->buf); dt_control_log(_("cannot apply printer profile `%s'"), ps->v_piccprofile); fprintf(stderr, "cannot apply printer profile `%s'\n", ps->v_piccprofile); dt_control_queue_redraw(); return; } const float page_width = dt_pdf_mm_to_point(width); const float page_height = dt_pdf_mm_to_point(height); char filename[PATH_MAX] = { 0 }; dt_loc_get_tmp_dir(filename, sizeof(filename)); g_strlcat(filename, "/pf.XXXXXX.pdf", sizeof(filename)); gint fd = g_mkstemp(filename); if(fd == -1) { free(dat.ps->buf); dt_control_log("failed to create temporary pdf for printing"); fprintf(stderr, "failed to create temporary pdf for printing\n"); return; } close(fd); const int icc_id = 0; dt_pdf_t *pdf = dt_pdf_start(filename, page_width, page_height, ps->prt.printer.resolution, DT_PDF_STREAM_ENCODER_FLATE); /* // ??? should a profile be embedded here? if (*printer_profile) icc_id = dt_pdf_add_icc(pdf, printer_profile); */ dt_pdf_image_t *pdf_image = dt_pdf_add_image(pdf, (uint8_t *)dat.ps->buf, dat.width, dat.height, 8, icc_id, 0.0); // PDF bounding-box has origin on bottom-left pdf_image->bb_x = dt_pdf_pixel_to_point((float)margin_left, ps->prt.printer.resolution); pdf_image->bb_y = dt_pdf_pixel_to_point((float)margin_bottom, ps->prt.printer.resolution); pdf_image->bb_width = dt_pdf_pixel_to_point((float)iwidth, ps->prt.printer.resolution); pdf_image->bb_height = dt_pdf_pixel_to_point((float)iheight, ps->prt.printer.resolution); if (ps->prt.page.landscape && (dat.width > dat.height)) pdf_image->rotate_to_fit = TRUE; else pdf_image->rotate_to_fit = FALSE; dt_pdf_page_t *pdf_page = dt_pdf_add_page(pdf, &pdf_image, 1); dt_pdf_finish(pdf, &pdf_page, 1); // free memory free (dat.ps->buf); free (pdf_image); free (pdf_page); // send to CUPS dt_print_file (imgid, filename, &ps->prt); unlink(filename); // add tag for this image char tag[256] = { 0 }; guint tagid = 0; snprintf (tag, sizeof(tag), "darktable|printed|%s", ps->prt.printer.name); dt_tag_new(tag, &tagid); dt_tag_attach(tagid, imgid); } static void _set_printer(const dt_lib_module_t *self, const char *printer_name) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; dt_printer_info_t *printer = dt_get_printer_info (printer_name); if (!printer) return; memcpy(&ps->prt.printer, printer, sizeof(dt_printer_info_t)); free(printer); printer = NULL; // if there is 0 hardware margins, set the user marging to 15mm if (ps->prt.printer.hw_margin_top == 0) { ps->prt.page.margin_top = 15; gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_top), ps->prt.page.margin_top * units[ps->unit]); } if (ps->prt.printer.hw_margin_bottom == 0) { ps->prt.page.margin_bottom = 15; gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_bottom), ps->prt.page.margin_bottom * units[ps->unit]); } if (ps->prt.printer.hw_margin_left == 0) { ps->prt.page.margin_left = 15; gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_left), ps->prt.page.margin_left * units[ps->unit]); } if (ps->prt.printer.hw_margin_right == 0) { ps->prt.page.margin_right = 15; gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_right), ps->prt.page.margin_right * units[ps->unit]); } dt_conf_set_string("plugins/print/print/printer", printer_name); char *default_paper = dt_conf_get_string("plugins/print/print/paper"); // next add corresponding papers // first clear current list dt_bauhaus_combobox_clear(ps->papers); // then add papers for the given printer if(ps->paper_list) g_list_free_full(ps->paper_list, free); ps->paper_list = dt_get_papers (printer_name); GList *papers = ps->paper_list; int np = 0; gboolean ispaperset = FALSE; while (papers) { const dt_paper_info_t *p = (dt_paper_info_t *)papers->data; dt_bauhaus_combobox_add(ps->papers, p->common_name); if (ispaperset == FALSE && (!g_strcmp0(default_paper, p->common_name) || default_paper[0] == '\0')) { dt_bauhaus_combobox_set(ps->papers, np); ispaperset = TRUE; } np++; papers = g_list_next (papers); } const dt_paper_info_t *paper = dt_get_paper(ps->paper_list, default_paper); if (paper) memcpy(&ps->prt.paper, paper, sizeof(dt_paper_info_t)); g_free (default_paper); dt_view_print_settings(darktable.view_manager, &ps->prt); } static void _printer_changed (GtkWidget *combo, const dt_lib_module_t *self) { const gchar *printer_name = dt_bauhaus_combobox_get_text(combo); if (printer_name) _set_printer (self, printer_name); } static void _paper_changed (GtkWidget *combo, const dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const gchar *paper_name = dt_bauhaus_combobox_get_text(combo); if (!paper_name) return; const dt_paper_info_t *paper = dt_get_paper(ps->paper_list, paper_name); if (paper) memcpy(&ps->prt.paper, paper, sizeof(dt_paper_info_t)); ps->iwidth = ps->iheight = 0; dt_conf_set_string("plugins/print/print/paper", paper_name); dt_view_print_settings(darktable.view_manager, &ps->prt); _update_slider(ps); } static double to_mm(dt_lib_print_settings_t *ps, double value) { return value / units[ps->unit]; } static void _update_slider (dt_lib_print_settings_t *ps) { dt_view_print_settings(darktable.view_manager, &ps->prt); // if widget are created, let's display the current image size if (ps->image_id != -1 && ps->width && ps->height && ps->info) { int32_t px=0, py=0, pwidth=0, pheight=0; int32_t ax=0, ay=0, awidth=0, aheight=0; int32_t ix=0, iy=0, iwidth=0, iheight=0; int32_t iwpix=ps->iwidth, ihpix=ps->iheight; int32_t pa_width, pa_height; if (ps->prt.page.landscape) { pa_height = (int32_t)ps->prt.paper.width; pa_width = (int32_t)ps->prt.paper.height; } else { pa_width = (int32_t)ps->prt.paper.width; pa_height = (int32_t)ps->prt.paper.height; } dt_get_print_layout(ps->image_id, &ps->prt, pa_width, pa_height, &iwpix, &ihpix, &px, &py, &pwidth, &pheight, &ax, &ay, &awidth, &aheight, &ix, &iy, &iwidth, &iheight); if (ps->iwidth==0 || ps->iheight==0) { ps->iwidth = iwpix; ps->iheight = ihpix; } const double h = iheight * units[ps->unit]; const double w = iwidth * units[ps->unit]; char *value; value = g_strdup_printf("%3.2f", w); gtk_label_set_text(GTK_LABEL(ps->width), value); g_free(value); value = g_strdup_printf("%3.2f", h); gtk_label_set_text(GTK_LABEL(ps->height), value); g_free(value); // compute the image down/up scale and report information double scale; if (iwidth >= awidth) scale = dt_pdf_point_to_pixel(dt_pdf_mm_to_point((double)awidth), ps->prt.printer.resolution) / ps->iwidth; else scale = dt_pdf_point_to_pixel(dt_pdf_mm_to_point((double)aheight), ps->prt.printer.resolution) / ps->iheight; value = g_strdup_printf(_("%3.2f (dpi:%d)"), scale, scale<=1.0 ? (int)ps->prt.printer.resolution : (int)(ps->prt.printer.resolution / scale)); gtk_label_set_text(GTK_LABEL(ps->info), value); g_free(value); } // set the max range for the borders depending on the others border and never allow to have an image size of 0 or less const int min_size = 5; // minimum size in mm const int pa_max_height = ps->prt.paper.height - ps->prt.printer.hw_margin_top - ps->prt.printer.hw_margin_bottom - min_size; const int pa_max_width = ps->prt.paper.width - ps->prt.printer.hw_margin_left - ps->prt.printer.hw_margin_right - min_size; gtk_spin_button_set_range (GTK_SPIN_BUTTON(ps->b_top), min_borders * units[ps->unit], (pa_max_height - ps->prt.page.margin_bottom) * units[ps->unit]); gtk_spin_button_set_range (GTK_SPIN_BUTTON(ps->b_left), min_borders * units[ps->unit], (pa_max_width - ps->prt.page.margin_right) * units[ps->unit]); gtk_spin_button_set_range (GTK_SPIN_BUTTON(ps->b_right), min_borders * units[ps->unit], (pa_max_width - ps->prt.page.margin_left) * units[ps->unit]); gtk_spin_button_set_range (GTK_SPIN_BUTTON(ps->b_bottom), min_borders * units[ps->unit], (pa_max_height - ps->prt.page.margin_top) * units[ps->unit]); } static void _top_border_callback (GtkWidget *spin, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const double value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); ps->prt.page.margin_top = to_mm(ps, value); if (ps->lock_activated == TRUE) { ps->prt.page.margin_bottom = to_mm(ps, value); ps->prt.page.margin_left = to_mm(ps, value); ps->prt.page.margin_right = to_mm(ps, value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_bottom), value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_left), value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_right), value); } _update_slider (ps); } static void _bottom_border_callback (GtkWidget *spin, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const double value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); ps->prt.page.margin_bottom = to_mm(ps, value); _update_slider (ps); } static void _left_border_callback (GtkWidget *spin, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const double value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); ps->prt.page.margin_left = to_mm(ps, value); _update_slider (ps); } static void _right_border_callback (GtkWidget *spin, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const double value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); ps->prt.page.margin_right = to_mm(ps, value); _update_slider (ps); } static void _lock_callback (GtkWidget *button, gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; ps->lock_activated = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); dt_conf_set_bool("plugins/print/print/lock_borders", ps->lock_activated); gtk_widget_set_sensitive(GTK_WIDGET(ps->b_bottom), !ps->lock_activated); gtk_widget_set_sensitive(GTK_WIDGET(ps->b_left), !ps->lock_activated); gtk_widget_set_sensitive(GTK_WIDGET(ps->b_right), !ps->lock_activated); // get value of top and set it to all other borders const double value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ps->b_top)); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_bottom), value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_left), value); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_right), value); _update_slider (ps); } static void _alignment_callback(GtkWidget *tb, gpointer user_data) { int index=-1; const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; for(int i=0; i<9; i++) { /* block signal handler */ g_signal_handlers_block_by_func (ps->dtba[i],_alignment_callback,user_data); if( GTK_WIDGET(ps->dtba[i]) == tb ) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->dtba[i]),TRUE); index=i; } else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->dtba[i]),FALSE); /* unblock signal handler */ g_signal_handlers_unblock_by_func (ps->dtba[i],_alignment_callback,user_data); } ps->prt.page.alignment = index; _update_slider (ps); } static void _orientation_changed (GtkWidget *combo, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; ps->prt.page.landscape = dt_bauhaus_combobox_get (combo); _update_slider (ps); } static void _unit_changed (GtkWidget *combo, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; int unit = dt_bauhaus_combobox_get(combo); if(unit < 0) return; // shouldn't happen, but it could be -1 if nothing is selected ps->unit = unit; dt_conf_set_int("plugins/print/print/unit", ps->unit); const double margin_top = ps->prt.page.margin_top; const double margin_left = ps->prt.page.margin_left; const double margin_right = ps->prt.page.margin_right; const double margin_bottom = ps->prt.page.margin_bottom; const int n_digits = (int)(1.0 / (units[ps->unit] * 10.0)); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(ps->b_top), n_digits); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(ps->b_bottom), n_digits); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(ps->b_left), n_digits); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(ps->b_right), n_digits); const float incr = units[ps->unit]; gtk_spin_button_set_increments(GTK_SPIN_BUTTON(ps->b_top), incr, incr); gtk_spin_button_set_increments(GTK_SPIN_BUTTON(ps->b_bottom), incr, incr); gtk_spin_button_set_increments(GTK_SPIN_BUTTON(ps->b_left), incr, incr); gtk_spin_button_set_increments(GTK_SPIN_BUTTON(ps->b_right), incr, incr); _update_slider (ps); // convert margins to new unit gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_top), margin_top * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_bottom), margin_bottom * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_left), margin_left * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_right), margin_right * units[ps->unit]); } static void _style_callback(GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; if(dt_bauhaus_combobox_get(ps->style) == 0) { dt_conf_set_string("plugins/print/print/style", ""); gtk_widget_set_sensitive(GTK_WIDGET(ps->style_mode), FALSE); } else { const gchar *style = dt_bauhaus_combobox_get_text(ps->style); dt_conf_set_string("plugins/print/print/style", style); gtk_widget_set_sensitive(GTK_WIDGET(ps->style_mode), TRUE); } if (ps->v_style) g_free(ps->v_style); ps->v_style = dt_conf_get_string("plugins/print/print/style"); } static void _style_mode_changed(GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; if(dt_bauhaus_combobox_get(ps->style_mode) == 0) ps->v_style_append = FALSE; else ps->v_style_append = TRUE; dt_conf_set_bool("plugins/print/print/style_append", ps->v_style_append); } static void _profile_changed(GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const int pos = dt_bauhaus_combobox_get(widget); GList *prof = ps->profiles; while(prof) { dt_lib_export_profile_t *pp = (dt_lib_export_profile_t *)prof->data; if(pp->pos == pos) { dt_conf_set_int("plugins/lighttable/export/icctype", pp->type); dt_conf_set_int("plugins/print/print/icctype", pp->type); dt_conf_set_string("plugins/lighttable/export/iccprofile", pp->filename); dt_conf_set_string("plugins/print/print/iccprofile", pp->filename); g_free(ps->v_iccprofile); ps->v_icctype = pp->type; ps->v_iccprofile = g_strdup(pp->filename); return; } prof = g_list_next(prof); } dt_conf_set_int("plugins/lighttable/export/icctype", DT_COLORSPACE_NONE); dt_conf_set_int("plugins/print/print/icctype", DT_COLORSPACE_NONE); dt_conf_set_string("plugins/lighttable/export/iccprofile", ""); dt_conf_set_string("plugins/print/print/iccprofile", ""); g_free(ps->v_iccprofile); ps->v_icctype = DT_COLORSPACE_NONE; ps->v_iccprofile = g_strdup(""); } static void _printer_profile_changed(GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const int pos = dt_bauhaus_combobox_get(widget); GList *prof = ps->profiles; while(prof) { dt_lib_export_profile_t *pp = (dt_lib_export_profile_t *)prof->data; if(pp->ppos == pos) { dt_conf_set_int("plugins/print/printer/icctype", pp->type); dt_conf_set_string("plugins/print/printer/iccprofile", pp->filename); g_free(ps->v_piccprofile); ps->v_picctype = pp->type; ps->v_piccprofile = g_strdup(pp->filename); // activate the black compensation and printer intent gtk_widget_set_sensitive(GTK_WIDGET(ps->pintent), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(ps->black_point_compensation), TRUE); return; } prof = g_list_next(prof); } dt_conf_set_int("plugins/print/printer/iccprofile", DT_COLORSPACE_NONE); dt_conf_set_string("plugins/print/printer/iccprofile", ""); g_free(ps->v_piccprofile); ps->v_picctype = DT_COLORSPACE_NONE; ps->v_piccprofile = g_strdup(""); gtk_widget_set_sensitive(GTK_WIDGET(ps->pintent), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(ps->black_point_compensation), FALSE); } static void _printer_intent_callback (GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const int pos = dt_bauhaus_combobox_get(widget); dt_conf_set_int("plugins/print/printer/iccintent", pos); ps->v_pintent = pos; } static void _printer_bpc_callback (GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; ps->v_black_point_compensation = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ps->black_point_compensation)); dt_conf_set_bool("plugins/print/print/black_point_compensation", ps->v_black_point_compensation); } static void _intent_callback (GtkWidget *widget, dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; const int pos = dt_bauhaus_combobox_get(widget); // record the intent that will override the out rendering module on export dt_conf_set_int("plugins/lighttable/export/iccintent", pos - 1); dt_conf_set_int("plugins/print/print/iccintent", pos - 1); ps->v_intent = pos - 1; } static void _set_orientation(dt_lib_print_settings_t *ps) { if (ps->image_id <= 0) return; dt_mipmap_buffer_t buf; dt_mipmap_cache_get(darktable.mipmap_cache, &buf, ps->image_id, DT_MIPMAP_3, DT_MIPMAP_BEST_EFFORT, 'r'); if (buf.width > buf.height) ps->prt.page.landscape = TRUE; else ps->prt.page.landscape = FALSE; dt_mipmap_cache_release(darktable.mipmap_cache, &buf); } static void _print_settings_filmstrip_activate_callback(gpointer instance,gpointer user_data) { const dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; ps->image_id = dt_view_filmstrip_get_activated_imgid(darktable.view_manager); ps->iwidth = ps->iheight = 0; _set_orientation (ps); dt_bauhaus_combobox_set (ps->orientation, ps->prt.page.landscape==TRUE?1:0); } static GList* _get_profiles () { // Create list of profiles GList *list = NULL; dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); prof->type = DT_COLORSPACE_SRGB; dt_utf8_strlcpy(prof->name, _("sRGB (web-safe)"), sizeof(prof->name)); prof->pos = -2; prof->ppos = -2; list = g_list_append(list, prof); prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); prof->type = DT_COLORSPACE_ADOBERGB; dt_utf8_strlcpy(prof->name, _("Adobe RGB (compatible)"), sizeof(prof->name)); prof->pos = -2; prof->ppos = -2; list = g_list_append(list, prof); // add the profiles from datadir/color/out/*.icc for(GList *iter = darktable.color_profiles->profiles; iter; iter = g_list_next(iter)) { dt_colorspaces_color_profile_t *p = (dt_colorspaces_color_profile_t *)iter->data; if(p->type == DT_COLORSPACE_FILE) { dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)g_malloc0(sizeof(dt_lib_export_profile_t)); g_strlcpy(prof->name, p->name, sizeof(prof->name)); g_strlcpy(prof->filename, p->filename, sizeof(prof->filename)); prof->type = DT_COLORSPACE_FILE; prof->pos = -2; prof->ppos = -2; list = g_list_append(list, prof); } } return list; } static void _new_printer_callback(dt_printer_info_t *printer, void *user_data) { static int count = 0; const dt_lib_module_t *self = (dt_lib_module_t *)user_data; const dt_lib_print_settings_t *d = (dt_lib_print_settings_t*)self->data; char *default_printer = dt_conf_get_string("plugins/print/print/printer"); g_signal_handlers_block_by_func(G_OBJECT(d->printers), G_CALLBACK(_printer_changed), NULL); dt_bauhaus_combobox_add(d->printers, printer->name); if (!g_strcmp0(default_printer, printer->name) || default_printer[0]=='\0') { dt_bauhaus_combobox_set(d->printers, count); _set_printer(self, printer->name); } count++; g_free(default_printer); g_signal_handlers_unblock_by_func(G_OBJECT(d->printers), G_CALLBACK(_printer_changed), NULL); } void gui_init (dt_lib_module_t *self) { dt_lib_print_settings_t *d = (dt_lib_print_settings_t*)malloc(sizeof(dt_lib_print_settings_t)); self->data = d; self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); char datadir[PATH_MAX] = { 0 }; char confdir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(confdir, sizeof(confdir)); dt_loc_get_datadir(datadir, sizeof(datadir)); GtkWidget *label; char tooltip[1024]; d->paper_list = NULL; d->iwidth = d->iheight = 0; d->unit = 0; d->width = d->height = NULL; d->v_piccprofile = NULL; d->v_iccprofile = NULL; d->v_style = NULL; dt_init_print_info(&d->prt); dt_view_print_settings(darktable.view_manager, &d->prt); dt_control_signal_connect(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE, G_CALLBACK(_print_settings_filmstrip_activate_callback), self); d->profiles = _get_profiles(); // get orientation of the selectd image if possible d->image_id = -1; GList *selected_images = dt_collection_get_selected(darktable.collection, 1); if(selected_images) { int imgid = GPOINTER_TO_INT(selected_images->data); d->image_id = imgid; } g_list_free(selected_images); _set_orientation(d); // create the spin-button now as values could be set when the printer has no hardware margin d->b_top = gtk_spin_button_new_with_range(0, 1000, 1); d->b_left = gtk_spin_button_new_with_range(0, 1000, 1); d->b_right = gtk_spin_button_new_with_range(0, 1000, 1); d->b_bottom = gtk_spin_button_new_with_range(0, 1000, 1); gtk_entry_set_alignment (GTK_ENTRY(d->b_top), 1); gtk_entry_set_alignment (GTK_ENTRY(d->b_left), 1); gtk_entry_set_alignment (GTK_ENTRY(d->b_right), 1); gtk_entry_set_alignment (GTK_ENTRY(d->b_bottom), 1); dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(d->b_top)); dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(d->b_left)); dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(d->b_right)); dt_gui_key_accel_block_on_focus_connect(GTK_WIDGET(d->b_bottom)); ////////////////////////// PRINTER SETTINGS // create papers combo as filled when adding printers d->papers = dt_bauhaus_combobox_new(NULL); label = dt_ui_section_label_new(_("printer")); gtk_box_pack_start(GTK_BOX(self->widget), label, TRUE, TRUE, 0); d->printers = dt_bauhaus_combobox_new(NULL); gtk_box_pack_start(GTK_BOX(self->widget), d->printers, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(d->printers), "value-changed", G_CALLBACK(_printer_changed), self); // Add printer profile combo d->pprofile = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->pprofile, NULL, _("profile")); int combo_idx, n; GList *l = d->profiles; gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->pprofile), TRUE, TRUE, 0); int printer_profile_type = dt_conf_get_int("plugins/print/printer/icctype"); gchar *printer_profile = dt_conf_get_string("plugins/print/printer/iccprofile"); combo_idx = -1; n = 0; dt_bauhaus_combobox_add(d->pprofile, _("none")); while(l) { dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)l->data; // do not add built-in profiles, these are in no way for printing if(prof->type == DT_COLORSPACE_FILE) { dt_bauhaus_combobox_add(d->pprofile, prof->name); prof->ppos = ++n; if(prof->type == printer_profile_type && (prof->type != DT_COLORSPACE_FILE || !g_strcmp0(prof->filename, printer_profile))) { g_free(d->v_piccprofile); d->v_picctype = printer_profile_type; d->v_piccprofile = g_strdup(printer_profile); combo_idx = n; } } l = g_list_next(l); } g_free (printer_profile); // profile not found, maybe a profile has been removed? revert to none if (combo_idx == -1) { dt_conf_set_int("plugins/print/printer/icctype", DT_COLORSPACE_NONE); dt_conf_set_string("plugins/print/printer/iccprofile", ""); g_free(d->v_piccprofile); d->v_picctype = DT_COLORSPACE_NONE; d->v_piccprofile = g_strdup(""); combo_idx = 0; } dt_bauhaus_combobox_set(d->pprofile, combo_idx); snprintf(tooltip, sizeof(tooltip), _("printer ICC profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(d->pprofile), "tooltip-text", tooltip, (char *)NULL); g_signal_connect(G_OBJECT(d->pprofile), "value-changed", G_CALLBACK(_printer_profile_changed), (gpointer)self); // Add printer intent combo d->pintent = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->pintent, NULL, _("intent")); dt_bauhaus_combobox_add(d->pintent, _("perceptual")); dt_bauhaus_combobox_add(d->pintent, _("relative colorimetric")); dt_bauhaus_combobox_add(d->pintent, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(d->pintent, _("absolute colorimetric")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->pintent), TRUE, TRUE, 0); d->v_pintent = dt_conf_get_int("plugins/print/printer/iccintent"); dt_bauhaus_combobox_set(d->pintent, d->v_pintent); g_signal_connect (G_OBJECT (d->pintent), "value-changed", G_CALLBACK (_printer_intent_callback), (gpointer)self); d->black_point_compensation = gtk_check_button_new_with_label(_("black point compensation")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->black_point_compensation), TRUE, FALSE, 0); g_signal_connect(d->black_point_compensation, "toggled", G_CALLBACK(_printer_bpc_callback), (gpointer)self); d->v_black_point_compensation = dt_conf_get_bool("plugins/print/print/black_point_compensation"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->black_point_compensation), d->v_black_point_compensation); g_object_set(d->black_point_compensation, "tooltip-text", _("activate black point compensation when applying the printer profile"), (char *)NULL); gtk_widget_set_sensitive(GTK_WIDGET(d->pintent), combo_idx==0?FALSE:TRUE); gtk_widget_set_sensitive(GTK_WIDGET(d->black_point_compensation), combo_idx==0?FALSE:TRUE); ////////////////////////// PAGE SETTINGS label = dt_ui_section_label_new(_("page")); gtk_box_pack_start(GTK_BOX(self->widget), label, TRUE, TRUE, 0); //// papers dt_bauhaus_widget_set_label(d->papers, NULL, _("paper size")); g_signal_connect(G_OBJECT(d->papers), "value-changed", G_CALLBACK(_paper_changed), self); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->papers), TRUE, TRUE, 0); //// portrait / landscape d->orientation = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->orientation, NULL, _("orientation")); dt_bauhaus_combobox_add(d->orientation, _("portrait")); dt_bauhaus_combobox_add(d->orientation, _("landscape")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->orientation), TRUE, TRUE, 0); GtkWidget *ucomb = dt_bauhaus_combobox_new(NULL); dt_bauhaus_combobox_add(ucomb, _("mm")); dt_bauhaus_combobox_add(ucomb, _("cm")); dt_bauhaus_combobox_add(ucomb, _("inch")); gtk_box_pack_start(GTK_BOX(self->widget), ucomb, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(d->orientation), "value-changed", G_CALLBACK(_orientation_changed), self); g_signal_connect(G_OBJECT(ucomb), "value-changed", G_CALLBACK(_unit_changed), self); d->unit = dt_conf_get_int("plugins/print/print/unit"); dt_bauhaus_combobox_set(ucomb, d->unit); dt_bauhaus_combobox_set (d->orientation, d->prt.page.landscape?1:0); //// image dimensions, create them now as we need them GtkWidget *hboxdim = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); label = gtk_label_new(_("image width/height")); gtk_box_pack_start(GTK_BOX(hboxdim),GTK_WIDGET(label),TRUE,TRUE,0); d->width = gtk_label_new(_("width")); gtk_box_pack_start(GTK_BOX(hboxdim),GTK_WIDGET(d->width),TRUE,TRUE,0); label = gtk_label_new(_(" x ")); gtk_box_pack_start(GTK_BOX(hboxdim),GTK_WIDGET(label),TRUE,TRUE,0); d->height = gtk_label_new(_("height")); gtk_box_pack_start(GTK_BOX(hboxdim),GTK_WIDGET(d->height),TRUE,TRUE,0); //// image information (downscale/upscale) GtkWidget *hboxinfo = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); label = gtk_label_new(_("scale factor")); gtk_box_pack_start(GTK_BOX(hboxinfo),GTK_WIDGET(label),TRUE,TRUE,0); d->info = gtk_label_new("1.0"); gtk_box_pack_start(GTK_BOX(hboxinfo),GTK_WIDGET(d->info),TRUE,TRUE,0); g_object_set(G_OBJECT(hboxinfo), "tooltip-text", _("image scale factor from native printer DPI:\n" " < 1 means that it is downscaled (best quality)\n" " > 1 means that the image is upscaled\n" " a too large value may result in poor print quality"), (char *)NULL); //// borders GtkGrid *bds = GTK_GRID(gtk_grid_new()); gtk_grid_set_row_spacing(bds, DT_PIXEL_APPLY_DPI(3)); gtk_grid_set_column_spacing(bds, DT_PIXEL_APPLY_DPI(3)); d->lock_activated = FALSE; //d->b_top = gtk_spin_button_new_with_range(0, 10000, 1); g_object_set(G_OBJECT(d->b_top), "tooltip-text", _("top margin"), (char *)NULL); gtk_grid_attach(bds, GTK_WIDGET(d->b_top), 1, 0, 1, 1); //d->b_left = gtk_spin_button_new_with_range(0, 10000, 1); g_object_set(G_OBJECT(d->b_left), "tooltip-text", _("left margin"), (char *)NULL); gtk_grid_attach(bds, GTK_WIDGET(d->b_left), 0, 1, 1, 1); d->lock_button = GTK_TOGGLE_BUTTON(gtk_toggle_button_new_with_label(_("lock"))); g_object_set(G_OBJECT(d->lock_button), "tooltip-text", _("change all margins uniformly"), (char *)NULL); gtk_grid_attach(bds, GTK_WIDGET(d->lock_button), 1, 1, 1, 1); //d->b_right = gtk_spin_button_new_with_range(0, 10000, 1); g_object_set(G_OBJECT(d->b_right), "tooltip-text", _("right margin"), (char *)NULL); gtk_grid_attach(bds, GTK_WIDGET(d->b_right), 2, 1, 1, 1); //d->b_bottom = gtk_spin_button_new_with_range(0, 10000, 1); g_object_set(G_OBJECT(d->b_bottom), "tooltip-text", _("bottom margin"), (char *)NULL); gtk_grid_attach(bds, GTK_WIDGET(d->b_bottom), 1, 2, 1, 1); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(bds), TRUE, TRUE, 0); g_signal_connect (G_OBJECT (d->b_top), "value-changed", G_CALLBACK (_top_border_callback), self); g_signal_connect (G_OBJECT (d->b_bottom), "value-changed", G_CALLBACK (_bottom_border_callback), self); g_signal_connect (G_OBJECT (d->b_left), "value-changed", G_CALLBACK (_left_border_callback), self); g_signal_connect (G_OBJECT (d->b_right), "value-changed", G_CALLBACK (_right_border_callback), self); g_signal_connect (G_OBJECT(d->lock_button), "toggled", G_CALLBACK(_lock_callback), self); const gboolean lock_active = dt_conf_get_bool("plugins/print/print/lock_borders"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->lock_button), lock_active); // pack image dimention hbox here gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hboxdim), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hboxinfo), TRUE, TRUE, 0); //// alignments // Create the 3x3 gtk table toggle button table... GtkGrid *bat = GTK_GRID(gtk_grid_new()); gtk_grid_set_row_spacing(bat, DT_PIXEL_APPLY_DPI(3)); gtk_grid_set_column_spacing(bat, DT_PIXEL_APPLY_DPI(3)); for(int i=0; i<9; i++) { d->dtba[i] = DTGTK_TOGGLEBUTTON (dtgtk_togglebutton_new (dtgtk_cairo_paint_alignment,CPF_STYLE_FLAT|(CPF_SPECIAL_FLAG<dtba[i]), (i%3), i/3, 1, 1); g_signal_connect (G_OBJECT (d->dtba[i]), "toggled",G_CALLBACK (_alignment_callback), self); } d->prt.page.alignment = center; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->dtba[d->prt.page.alignment]),TRUE); GtkWidget *hbox22 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); GtkWidget *label4 = gtk_label_new(_("alignment")); gtk_box_pack_start(GTK_BOX(hbox22),GTK_WIDGET(label4),TRUE,TRUE,0); gtk_box_pack_start(GTK_BOX(hbox22), GTK_WIDGET(bat), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox22), TRUE, TRUE, 0); ////////////////////////// PRINT SETTINGS label = dt_ui_section_label_new(_("print settings")); gtk_box_pack_start(GTK_BOX(self->widget), label, TRUE, TRUE, 0); // Add export profile combo d->profile = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->profile, NULL, _("profile")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->profile), TRUE, TRUE, 0); dt_bauhaus_combobox_add(d->profile, _("image settings")); int icctype = dt_conf_get_int("plugins/print/print/icctype"); gchar *iccprofile = dt_conf_get_string("plugins/print/print/iccprofile"); combo_idx = -1; n = 0; l = d->profiles; while(l) { dt_lib_export_profile_t *prof = (dt_lib_export_profile_t *)l->data; dt_bauhaus_combobox_add(d->profile, prof->name); prof->pos = ++n; if(prof->type == icctype && (prof->type != DT_COLORSPACE_FILE || !g_strcmp0(prof->filename, iccprofile))) { g_free(d->v_iccprofile); d->v_icctype = icctype; d->v_iccprofile = g_strdup(iccprofile); combo_idx = n; } l = g_list_next(l); } if (combo_idx == -1) { dt_conf_set_int("plugins/print/print/icctype", DT_COLORSPACE_NONE); dt_conf_set_string("plugins/print/print/iccprofile", ""); g_free(d->v_iccprofile); d->v_icctype = DT_COLORSPACE_NONE; d->v_iccprofile = g_strdup(""); combo_idx = 0; } g_free (iccprofile); dt_bauhaus_combobox_set(d->profile, combo_idx); snprintf(tooltip, sizeof(tooltip), _("output ICC profiles in %s/color/out or %s/color/out"), confdir, datadir); g_object_set(G_OBJECT(d->profile), "tooltip-text", tooltip, (char *)NULL); g_signal_connect(G_OBJECT(d->profile), "value-changed", G_CALLBACK(_profile_changed), (gpointer)self); // Add export intent combo d->intent = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->intent, NULL, _("intent")); dt_bauhaus_combobox_add(d->intent, _("image settings")); dt_bauhaus_combobox_add(d->intent, _("perceptual")); dt_bauhaus_combobox_add(d->intent, _("relative colorimetric")); dt_bauhaus_combobox_add(d->intent, C_("rendering intent", "saturation")); dt_bauhaus_combobox_add(d->intent, _("absolute colorimetric")); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->intent), TRUE, TRUE, 0); dt_bauhaus_combobox_set(d->intent, 0); g_signal_connect (G_OBJECT (d->intent), "value-changed", G_CALLBACK (_intent_callback), (gpointer)self); // Add export style combo d->style = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->style, NULL, _("style")); dt_bauhaus_combobox_add(d->style, _("none")); GList *styles = dt_styles_get_list(""); gchar *current_style = dt_conf_get_string("plugins/print/print/style"); combo_idx = -1; n=0; while (styles) { dt_style_t *style=(dt_style_t *)styles->data; dt_bauhaus_combobox_add(d->style, style->name); n++; if (g_strcmp0(style->name,current_style)==0) { if(d->v_style) g_free(d->v_style); d->v_style = g_strdup(current_style); combo_idx=n; } styles=g_list_next(styles); } g_free(current_style); g_list_free_full(styles, dt_style_free); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->style), TRUE, TRUE, 0); g_object_set(G_OBJECT(d->style), "tooltip-text", _("temporary style to use while printing"), (char *)NULL); // style not found, maybe a style has been removed? revert to none if (combo_idx == -1) { dt_conf_set_string("plugins/print/print/style", ""); if(d->v_style) g_free(d->v_style); d->v_style = g_strdup(""); combo_idx=0; } dt_bauhaus_combobox_set(d->style, combo_idx); g_signal_connect (G_OBJECT (d->style), "value-changed", G_CALLBACK (_style_callback), (gpointer)self); // Whether to add/replace style items d->style_mode = dt_bauhaus_combobox_new(NULL); dt_bauhaus_widget_set_label(d->style_mode, NULL, _("mode")); dt_bauhaus_combobox_add(d->style_mode, _("replace history")); dt_bauhaus_combobox_add(d->style_mode, _("append history")); d->v_style_append = dt_conf_get_bool("plugins/print/print/style_append"); dt_bauhaus_combobox_set(d->style_mode, d->v_style_append?1:0); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(d->style_mode), TRUE, TRUE, 0); g_object_set(G_OBJECT(d->style_mode), "tooltip-text", _("whether the style items are appended to the history or replacing the history"), (char *)NULL); gtk_widget_set_sensitive(GTK_WIDGET(d->style_mode), combo_idx==0?FALSE:TRUE); g_signal_connect(G_OBJECT(d->style_mode), "value-changed", G_CALLBACK(_style_mode_changed), (gpointer)self); // Print button GtkButton *button = GTK_BUTTON(gtk_button_new_with_label(_("print"))); d->print_button = button; g_object_set(G_OBJECT(button), "tooltip-text", _("print with current settings (ctrl-p)"), (char *)NULL); gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(button), TRUE, TRUE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (_print_button_clicked), (gpointer)self); // Let's start the printer discovery now dt_printers_discovery(_new_printer_callback, self); } static gboolean _bauhaus_combobox_set_active_text(GtkWidget *cb, const gchar *text) { g_assert(text != NULL); g_assert(cb != NULL); const GList *labels = dt_bauhaus_combobox_get_labels(cb); const GList *iter = labels; int i = 0; while(iter) { if(!g_strcmp0((gchar*)iter->data, text)) { dt_bauhaus_combobox_set(cb, i); return TRUE; } i++; iter = g_list_next(iter); } return FALSE; } void init_presets(dt_lib_module_t *self) { } void *legacy_params(dt_lib_module_t *self, const void *const old_params, const size_t old_params_size, const int old_version, int *new_version, size_t *new_size) { if(old_version == 1) { // we added the profile type // // old format: // char *printer // char *paper // int32_t landscape // char *f_profile // int32_t intent // char *f_pprofile // // // new format: // char *printer // char *paper // int32_t landscape // int32_t f_profile_type // char *f_profile // int32_t intent // int32_t f_pprofile_type // char *f_pprofile // const char *buf = (const char *)old_params; // printer const char *printer = buf; const int32_t printer_len = strlen(printer) + 1; buf += printer_len; // paper const char *paper = buf; const int32_t paper_len = strlen(paper) + 1; buf += paper_len; // landscape const int32_t landscape = *(int32_t *)buf; buf += sizeof(int32_t); // profile const char *profile = buf; const int32_t profile_len = strlen(profile) + 1; buf += profile_len; // intent const int32_t intent = *(int32_t *)buf; buf += sizeof(int32_t); // pprofile const char *pprofile = buf; const int32_t pprofile_len = strlen(pprofile) + 1; buf += pprofile_len; // now we got all fields from the start of the buffer and buf points to the beginning or // find the new values for the two profiles dt_colorspaces_color_profile_type_t profile_type, pprofile_type; const char *profile_filename = "", *pprofile_filename = ""; if(*profile == '\0' || !g_strcmp0(profile, "none")) { profile_type = DT_COLORSPACE_NONE; } else if(!g_strcmp0(profile, "sRGB")) { profile_type = DT_COLORSPACE_SRGB; } else if(!g_strcmp0(profile, "adobergb")) { profile_type = DT_COLORSPACE_ADOBERGB; } else { profile_type = DT_COLORSPACE_FILE; profile_filename = &profile[1]; // the old code had a '/' in the beginning } // in theory pprofile can't be srgb or adobergb, but checking for them won't hurt if(*pprofile == '\0' || !g_strcmp0(pprofile, "none")) { pprofile_type = DT_COLORSPACE_NONE; } else if(!g_strcmp0(pprofile, "sRGB")) { pprofile_type = DT_COLORSPACE_SRGB; } else if(!g_strcmp0(pprofile, "adobergb")) { pprofile_type = DT_COLORSPACE_ADOBERGB; } else { pprofile_type = DT_COLORSPACE_FILE; pprofile_filename = &pprofile[1]; // the old code had a '/' in the beginning } int32_t new_profile_len = strlen(profile_filename) + 1; int32_t new_pprofile_len = strlen(pprofile_filename) + 1; // now we got everything to reassemble the new params size_t new_params_size = old_params_size - profile_len - pprofile_len; new_params_size += 2 * sizeof(dt_colorspaces_color_profile_type_t); new_params_size += new_profile_len + new_pprofile_len; void *new_params = malloc(new_params_size); size_t pos = 0; // char *printer memcpy(new_params + pos, printer, printer_len); pos += printer_len; // char *paper memcpy(new_params + pos, paper, paper_len); pos += paper_len; // int32_t landscape memcpy(new_params + pos, &landscape, sizeof(int32_t)); pos += sizeof(int32_t); // int32_t f_profile_type memcpy(new_params + pos, &profile_type, sizeof(int32_t)); pos += sizeof(int32_t); // char *f_profile memcpy(new_params + pos, profile_filename, new_profile_len); pos += new_profile_len; // int32_t intent memcpy(new_params + pos, &intent, sizeof(int32_t)); pos += sizeof(int32_t); // int32_t f_pprofile_type memcpy(new_params + pos, &pprofile_type, sizeof(int32_t)); pos += sizeof(int32_t); // char *f_pprofile memcpy(new_params + pos, pprofile_filename, new_pprofile_len); pos += new_pprofile_len; // memcpy(new_params + pos, buf, old_params_size - ((char *)buf - (char *)old_params)); *new_size = new_params_size; *new_version = 2; return new_params; } return NULL; } int set_params(dt_lib_module_t *self, const void *params, int size) { const dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; if(!params) return 1; // get the parameters buffer const char *buf = (char *)params; // get individual items const char *printer = buf; if (!printer) return 1; const int32_t printer_len = strlen(printer) + 1; buf += printer_len; const char *paper = buf; if (!paper) return 1; const int32_t paper_len = strlen(paper) + 1; buf += paper_len; const int32_t landscape = *(int32_t *)buf; buf += sizeof(int32_t); const int32_t f_profile_type = *(int32_t *)buf; buf += sizeof(int32_t); const char *f_profile = buf; if (!f_profile) return 1; const int32_t profile_len = strlen(f_profile) + 1; buf += profile_len; const int32_t intent = *(int32_t *)buf; buf += sizeof(int32_t); const int32_t f_pprofile_type = *(int32_t *)buf; buf += sizeof(int32_t); const char *f_pprofile = buf; if (!f_pprofile) return 1; const int32_t pprofile_len = strlen(f_pprofile) + 1; buf += pprofile_len; const int32_t pintent = *(int32_t *)buf; buf += sizeof(int32_t); const int32_t bpc = *(int32_t *)buf; buf += sizeof(int32_t); const char *style = buf; if (!style) return 1; const int32_t style_len = strlen(style) + 1; buf += style_len; const int32_t style_mode = *(int32_t *)buf; buf += sizeof(int32_t); const double b_top = *(double *)buf; buf += sizeof(double); const double b_bottom = *(double *)buf; buf += sizeof(double); const double b_left = *(double *)buf; buf += sizeof(double); const double b_right = *(double *)buf; buf += sizeof(double); const int32_t alignment = *(int32_t *)buf; // buf += sizeof(int32_t); // ensure that the size is correct if(size != printer_len + paper_len + profile_len + pprofile_len + style_len + 8 * sizeof(int32_t) + 4 * sizeof(double)) return 1; // set the GUI with corresponding values if (printer[0] != '\0') _bauhaus_combobox_set_active_text(ps->printers, printer); if (paper[0] != '\0') _bauhaus_combobox_set_active_text(ps->papers, paper); dt_bauhaus_combobox_set (ps->orientation, landscape); dt_bauhaus_combobox_set(ps->profile, 0); for(GList *iter = ps->profiles; iter; iter = g_list_next(iter)) { dt_lib_export_profile_t *p = (dt_lib_export_profile_t *)iter->data; if(f_profile_type == p->type && (f_profile_type != DT_COLORSPACE_FILE || !g_strcmp0(f_profile, p->filename))) { dt_bauhaus_combobox_set(ps->profile, p->pos); break; } } dt_bauhaus_combobox_set (ps->intent, intent); dt_bauhaus_combobox_set(ps->pprofile, 0); for(GList *iter = ps->profiles; iter; iter = g_list_next(iter)) { dt_lib_export_profile_t *p = (dt_lib_export_profile_t *)iter->data; if(f_pprofile_type == p->type && (f_pprofile_type != DT_COLORSPACE_FILE || !g_strcmp0(f_pprofile, p->filename))) { dt_bauhaus_combobox_set(ps->pprofile, p->ppos); break; } } dt_bauhaus_combobox_set (ps->pintent, pintent); if (style[0] != '\0') _bauhaus_combobox_set_active_text(ps->style, style); dt_bauhaus_combobox_set (ps->style_mode, style_mode); gtk_spin_button_set_value (GTK_SPIN_BUTTON(ps->b_top), b_top * units[ps->unit]); gtk_spin_button_set_value (GTK_SPIN_BUTTON(ps->b_bottom), b_bottom * units[ps->unit]); gtk_spin_button_set_value (GTK_SPIN_BUTTON(ps->b_left), b_left * units[ps->unit]); gtk_spin_button_set_value (GTK_SPIN_BUTTON(ps->b_right), b_right * units[ps->unit]); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->dtba[alignment]),TRUE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->black_point_compensation), bpc); return 0; } void *get_params(dt_lib_module_t *self, int *size) { const dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; // get the data const char *printer = dt_bauhaus_combobox_get_text(ps->printers); const char *paper = dt_bauhaus_combobox_get_text(ps->papers); const int32_t profile_pos = dt_bauhaus_combobox_get(ps->profile); const int32_t intent = dt_bauhaus_combobox_get(ps->intent); const char *style = dt_bauhaus_combobox_get_text(ps->style); const int32_t style_mode = dt_bauhaus_combobox_get(ps->style_mode); const int32_t pprofile_pos = dt_bauhaus_combobox_get(ps->pprofile); const int32_t pintent = dt_bauhaus_combobox_get(ps->pintent); const int32_t landscape = dt_bauhaus_combobox_get(ps->orientation); const int32_t bpc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ps->black_point_compensation)); const double b_top = ps->prt.page.margin_top; const double b_bottom = ps->prt.page.margin_bottom; const double b_left = ps->prt.page.margin_left; const double b_right = ps->prt.page.margin_right; const int32_t alignment = ps->prt.page.alignment; dt_colorspaces_color_profile_type_t profile_type = DT_COLORSPACE_NONE, pprofile_type = DT_COLORSPACE_NONE; const char *profile = "", *pprofile = ""; for(GList *iter = ps->profiles; iter; iter = g_list_next(iter)) { dt_lib_export_profile_t *p = (dt_lib_export_profile_t *)iter->data; if(p->pos == profile_pos) { profile_type = p->type; profile = p->filename; } if(p->ppos == pprofile_pos) { pprofile_type = p->type; pprofile = p->filename; } } // these will be NULL when no printer is connected/found if(!printer) printer = ""; if(!paper) paper = ""; // compute the size of individual items, always get the \0 for strings const int32_t printer_len = strlen (printer) + 1; const int32_t paper_len = strlen (paper) + 1; const int32_t profile_len = strlen (profile) + 1; const int32_t pprofile_len = strlen (pprofile) + 1; const int32_t style_len = strlen (style) + 1; // compute the size of all parameters *size = printer_len + paper_len + profile_len + pprofile_len + style_len + 8 * sizeof(int32_t) + 4 * sizeof(double); // allocate the parameter buffer char *params = (char *)malloc(*size); int pos = 0; memcpy(params+pos, printer, printer_len); pos += printer_len; memcpy(params+pos, paper, paper_len); pos += paper_len; memcpy(params+pos, &landscape, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &profile_type, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, profile, profile_len); pos += profile_len; memcpy(params+pos, &intent, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &pprofile_type, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, pprofile, pprofile_len); pos += pprofile_len; memcpy(params+pos, &pintent, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &bpc, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, style, style_len); pos += style_len; memcpy(params+pos, &style_mode, sizeof(int32_t)); pos += sizeof(int32_t); memcpy(params+pos, &b_top, sizeof(double)); pos += sizeof(double); memcpy(params+pos, &b_bottom, sizeof(double)); pos += sizeof(double); memcpy(params+pos, &b_left, sizeof(double)); pos += sizeof(double); memcpy(params+pos, &b_right, sizeof(double)); pos += sizeof(double); memcpy(params+pos, &alignment, sizeof(int32_t)); pos += sizeof(int32_t); g_assert(pos == *size); return params; } void gui_cleanup (dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(ps->b_top)); dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(ps->b_left)); dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(ps->b_right)); dt_gui_key_accel_block_on_focus_disconnect(GTK_WIDGET(ps->b_bottom)); dt_control_signal_disconnect(darktable.signals, G_CALLBACK(_print_settings_filmstrip_activate_callback), self); g_list_free_full(ps->profiles, g_free); g_list_free_full(ps->paper_list, free); g_free(ps->v_iccprofile); g_free(ps->v_piccprofile); g_free(ps->v_style); free(self->data); self->data = NULL; } void gui_reset (dt_lib_module_t *self) { dt_lib_print_settings_t *ps = (dt_lib_print_settings_t *)self->data; gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_top), 15 * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_bottom), 15 * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_left), 15 * units[ps->unit]); gtk_spin_button_set_value(GTK_SPIN_BUTTON(ps->b_right), 15 * units[ps->unit]); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->dtba[center]),TRUE); ps->prt.page.alignment = center; dt_bauhaus_combobox_set(ps->profile, 0); dt_bauhaus_combobox_set(ps->pprofile, 0); dt_bauhaus_combobox_set(ps->pintent, dt_conf_get_int("plugins/print/print/iccintent") + 1); dt_bauhaus_combobox_set(ps->style, 0); dt_bauhaus_combobox_set(ps->intent, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps->black_point_compensation), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(ps->pintent), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(ps->black_point_compensation), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(ps->style_mode), FALSE); // reset page orientation to fit the picture _set_orientation (ps); dt_bauhaus_combobox_set (ps->orientation, ps->prt.page.landscape?1:0); } // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh // vim: shiftwidth=2 expandtab tabstop=2 cindent // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;