1 /** @file
2 * @brief PNG export dialog
3 */
4 /* Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * bulia byak <buliabyak@users.sf.net>
7 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
8 *
9 * Copyright (C) 1999-2007 Authors
10 * Copyright (C) 2001-2002 Ximian, Inc.
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
20 #include <png.h>
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
32 #ifdef WITH_GNOME_VFS
33 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
34 #endif
36 #include <glibmm/i18n.h>
37 #include "helper/unit-menu.h"
38 #include "helper/units.h"
39 #include "unit-constants.h"
40 #include "helper/window.h"
41 #include "inkscape-private.h"
42 #include "document.h"
43 #include "desktop-handles.h"
44 #include "sp-item.h"
45 #include "selection.h"
46 #include "file.h"
47 #include "macros.h"
48 #include "sp-namedview.h"
49 #include "selection-chemistry.h"
51 #include "dialog-events.h"
52 #include "preferences.h"
53 #include "verbs.h"
54 #include "interface.h"
56 #include "extension/output.h"
57 #include "extension/db.h"
59 #include "io/sys.h"
61 #include "helper/png-write.h"
63 #ifdef WIN32
64 #include <windows.h>
65 #include <COMMDLG.h>
66 #include <gdk/gdkwin32.h>
67 #endif
69 #define SP_EXPORT_MIN_SIZE 1.0
71 #define DPI_BASE PX_PER_IN
73 #define EXPORT_COORD_PRECISION 3
75 #define MIN_ONSCREEN_DISTANCE 50
77 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
78 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
79 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
81 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
82 GtkObject *base);
84 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
85 GtkObject *base);
87 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
88 GtkObject *base);
90 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
91 GtkObject *base);
93 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
94 GtkObject *base);
96 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
97 GtkObject *base);
99 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
100 GtkObject *base);
102 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
103 Inkscape::Selection *selection,
104 GtkObject *base);
105 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
106 Inkscape::Selection *selection,
107 guint flags,
108 GtkObject *base );
110 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
111 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
112 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
113 static float sp_export_value_get ( GtkObject *base, const gchar *key );
114 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
116 static void sp_export_filename_modified (GtkObject * object, gpointer data);
117 static inline void sp_export_find_default_selection(GtkWidget * dlg);
118 static void sp_export_detect_size(GtkObject * base);
120 static Glib::ustring const prefs_path = "/dialogs/export/";
122 // these all need to be reinitialized to their defaults during dialog_destroy
123 static GtkWidget *dlg = NULL;
124 static win_data wd;
125 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
126 static gchar * original_name = NULL;
127 static gchar * doc_export_name = NULL;
128 static bool was_empty = TRUE;
130 /** What type of button is being pressed */
131 enum selection_type {
132 SELECTION_PAGE = 0, /**< Export the whole page */
133 SELECTION_DRAWING, /**< Export everything drawn on the page */
134 SELECTION_SELECTION, /**< Export everything that is selected */
135 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
136 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
137 };
139 /** A list of strings that is used both in the preferences, and in the
140 data fields to describe the various values of \c selection_type. */
141 static const char * selection_names[SELECTION_NUMBER_OF] = {
142 "page", "drawing", "selection", "custom"};
144 /** The names on the buttons for the various selection types. */
145 static const char * selection_labels[SELECTION_NUMBER_OF] = {
146 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
148 static void
149 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
150 {
151 sp_signal_disconnect_by_data (INKSCAPE, dlg);
153 wd.win = dlg = NULL;
154 wd.stop = 0;
155 x = -1000; y = -1000; w = 0; h = 0;
156 g_free(original_name);
157 original_name = NULL;
158 g_free(doc_export_name);
159 doc_export_name = NULL;
160 was_empty = TRUE;
162 return;
163 } // end of sp_export_dialog_destroy()
165 /// Called when dialog is closed or inkscape is shut down.
166 static bool
167 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
168 {
170 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
171 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
173 if (x<0) x=0;
174 if (y<0) y=0;
176 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
177 prefs->setInt(prefs_path + "x", x);
178 prefs->setInt(prefs_path + "y", y);
179 prefs->setInt(prefs_path + "w", w);
180 prefs->setInt(prefs_path + "h", h);
182 return FALSE; // which means, go ahead and destroy it
184 } // end of sp_export_dialog_delete()
186 /**
187 \brief Creates a new spin button for the export dialog
188 \param key The name of the spin button
189 \param val A default value for the spin button
190 \param min Minimum value for the spin button
191 \param max Maximum value for the spin button
192 \param step The step size for the spin button
193 \param page Size of the page increment
194 \param us Unit selector that effects this spin button
195 \param t Table to put the spin button in
196 \param x X location in the table \c t to start with
197 \param y Y location in the table \c t to start with
198 \param ll Text to put on the left side of the spin button (optional)
199 \param lr Text to put on the right side of the spin button (optional)
200 \param digits Number of digits to display after the decimal
201 \param sensitive Whether the spin button is sensitive or not
202 \param cb Callback for when this spin button is changed (optional)
203 \param dlg Export dialog the spin button is being placed in
205 */
206 static void
207 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
208 float step, float page, GtkWidget *us,
209 GtkWidget *t, int x, int y,
210 const gchar *ll, const gchar *lr,
211 int digits, unsigned int sensitive,
212 GCallback cb, GtkWidget *dlg )
213 {
214 GtkObject *adj = gtk_adjustment_new( val, min, max, step, page, 0 );
215 gtk_object_set_data( adj, "key", const_cast<gchar *>(key) );
216 gtk_object_set_data( GTK_OBJECT (dlg), (const gchar *)key, adj );
218 if (us) {
219 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
220 GTK_ADJUSTMENT (adj) );
221 }
223 int pos = 0;
225 GtkWidget *l = NULL;
227 if (ll) {
229 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
230 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
231 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
232 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
233 gtk_widget_set_sensitive (l, sensitive);
234 pos += 1;
236 }
238 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, digits);
239 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
240 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
241 gtk_widget_set_size_request (sb, 80, -1);
242 gtk_widget_set_sensitive (sb, sensitive);
243 pos += 1;
245 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
247 if (lr) {
249 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
250 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
251 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
252 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
253 gtk_widget_set_sensitive (l, sensitive);
254 pos += 1;
256 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
257 }
259 if (cb)
260 gtk_signal_connect (adj, "value_changed", cb, dlg);
262 return;
263 } // end of sp_export_spinbutton_new()
266 static Gtk::VBox *
267 sp_export_dialog_area_box (GtkWidget * dlg)
268 {
269 Gtk::VBox* vb = new Gtk::VBox(false, 3);
271 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
272 lbl->set_use_markup(true);
273 vb->pack_start(*lbl);
275 /* Units box */
276 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
277 /* gets added to the vbox later, but the unit selector is needed
278 earlier than that */
280 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
281 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
282 if (desktop)
283 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
284 unitbox->pack_end(*us, false, false, 0);
285 Gtk::Label* l = new Gtk::Label(_("Units:"));
286 unitbox->pack_end(*l, false, false, 3);
287 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
289 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
291 Gtk::ToggleButton* b;
292 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
293 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
294 b->set_data("key", GINT_TO_POINTER(i));
295 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
296 togglebox->pack_start(*b, false, true, 0);
297 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
298 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
299 }
301 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
302 G_CALLBACK (sp_export_selection_changed), dlg );
303 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
304 G_CALLBACK (sp_export_selection_modified), dlg );
305 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
306 G_CALLBACK (sp_export_selection_changed), dlg );
308 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
309 t->set_row_spacings (4);
310 t->set_col_spacings (4);
312 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
313 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
314 G_CALLBACK ( sp_export_area_x_value_changed),
315 dlg );
317 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
318 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
319 G_CALLBACK (sp_export_area_x_value_changed),
320 dlg );
322 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
323 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
324 G_CALLBACK
325 (sp_export_area_width_value_changed),
326 dlg );
328 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
329 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
330 G_CALLBACK (sp_export_area_y_value_changed),
331 dlg );
333 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
334 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
335 G_CALLBACK (sp_export_area_y_value_changed),
336 dlg );
338 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
339 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
340 G_CALLBACK (sp_export_area_height_value_changed),
341 dlg );
343 vb->pack_start(*togglebox, false, false, 3);
344 vb->pack_start(*t, false, false, 0);
345 vb->pack_start(*unitbox, false, false, 0);
347 return vb;
348 } // end of sp_export_dialog_area_box
351 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
353 if (id == NULL) /* This should never happen */
354 id = "bitmap";
356 gchar * directory = NULL;
358 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
359 // std::cout << "Directory from dialog" << std::endl;
360 directory = g_dirname(file_entry_text);
361 }
363 if (directory == NULL) {
364 /* Grab document directory */
365 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
366 // std::cout << "Directory from document" << std::endl;
367 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
368 }
369 }
371 if (directory == NULL) {
372 // std::cout << "Home Directory" << std::endl;
373 directory = homedir_path(NULL);
374 }
376 gchar * id_ext = g_strconcat(id, ".png", NULL);
377 gchar *filename = g_build_filename(directory, id_ext, NULL);
378 g_free(directory);
379 g_free(id_ext);
380 return filename;
381 }
383 static void
384 batch_export_clicked (GtkWidget *widget, GtkObject *base)
385 {
386 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
387 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
388 vb_singleexport->set_sensitive(false);
389 } else {
390 vb_singleexport->set_sensitive(true);
391 }
392 }
394 void
395 sp_export_dialog (void)
396 {
397 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
398 if (!dlg) {
400 gchar title[500];
401 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
403 dlg = sp_window_new (title, TRUE);
405 if (x == -1000 || y == -1000) {
406 x = prefs->getInt(prefs_path + "x", 0);
407 y = prefs->getInt(prefs_path + "y", 0);
408 }
410 if (w ==0 || h == 0) {
411 w = prefs->getInt(prefs_path + "w", 0);
412 h = prefs->getInt(prefs_path + "h", 0);
413 }
415 // if (x<0) x=0;
416 // if (y<0) y=0;
418 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
419 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
420 gtk_window_move ((GtkWindow *) dlg, x, y);
421 else
422 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
423 sp_transientize (dlg);
424 wd.win = dlg;
425 wd.stop = 0;
427 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
428 G_CALLBACK (sp_transientize_callback), &wd);
430 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
431 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
433 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
434 G_CALLBACK (sp_export_dialog_destroy), dlg);
436 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
437 G_CALLBACK (sp_export_dialog_delete), dlg);
439 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
440 G_CALLBACK (sp_export_dialog_delete), dlg);
442 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
443 G_CALLBACK (sp_dialog_hide), dlg);
445 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
446 G_CALLBACK (sp_dialog_unhide), dlg);
448 GtkTooltips *tt = gtk_tooltips_new();
450 Gtk::VBox *vb = new Gtk::VBox(false, 3);
451 vb->set_border_width(3);
452 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
454 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
455 vb_singleexport->set_border_width(0);
456 vb->pack_start(*vb_singleexport);
457 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
459 /* Export area frame */
460 {
461 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
462 area_box->set_border_width(3);
463 vb_singleexport->pack_start(*area_box, false, false, 0);
464 }
466 /* Bitmap size frame */
467 {
468 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
469 size_box->set_border_width(3);
471 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
472 lbl->set_use_markup(true);
473 size_box->pack_start(*lbl, false, false, 0);
474 const int rows = 2;
475 const int cols = 5;
476 const bool homogeneous = false;
477 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
478 t->set_row_spacings (4);
479 t->set_col_spacings (4);
480 size_box->pack_start(*t);
482 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
483 NULL, GTK_WIDGET(t->gobj()), 0, 0,
484 _("_Width:"), _("pixels at"), 0, 1,
485 G_CALLBACK
486 (sp_export_bitmap_width_value_changed),
487 dlg );
489 sp_export_spinbutton_new ( "xdpi",
490 prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
491 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
492 NULL, _("dp_i"), 2, 1,
493 G_CALLBACK (sp_export_xdpi_value_changed),
494 dlg );
496 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
497 NULL, GTK_WIDGET(t->gobj()), 0, 1,
498 _("_Height:"), _("pixels at"), 0, 1,
499 G_CALLBACK
500 (sp_export_bitmap_height_value_changed),
501 dlg );
503 /** \todo
504 * Needs fixing: there's no way to set ydpi currently, so we use
505 * the defaultxdpi value here, too...
506 */
507 sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
508 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
509 NULL, _("dpi"), 2, 0, NULL, dlg );
511 vb_singleexport->pack_start(*size_box);
512 }
514 /* File entry */
515 {
516 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
517 file_box->set_border_width(3);
519 // true = has mnemonic
520 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
521 flabel->set_use_markup(true);
522 file_box->pack_start(*flabel, false, false, 0);
524 Gtk::Entry *fe = new Gtk::Entry();
526 /*
527 * set the default filename to be that of the current path + document
528 * with .png extension
529 *
530 * One thing to notice here is that this filename may get
531 * overwritten, but it won't happen here. The filename gets
532 * written into the text field, but then the button to select
533 * the area gets set. In that code the filename can be changed
534 * if there are some with presidence in the document. So, while
535 * this code sets the name first, it may not be the one users
536 * really see.
537 */
538 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
539 {
540 gchar *name;
541 SPDocument * doc = SP_ACTIVE_DOCUMENT;
542 const gchar *uri = SP_DOCUMENT_URI (doc);
543 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
544 const gchar * text_extension = repr->attribute("inkscape:output_extension");
545 Inkscape::Extension::Output * oextension = NULL;
547 if (text_extension != NULL) {
548 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
549 }
551 if (oextension != NULL) {
552 gchar * old_extension = oextension->get_extension();
553 if (g_str_has_suffix(uri, old_extension)) {
554 gchar * uri_copy;
555 gchar * extension_point;
556 gchar * final_name;
558 uri_copy = g_strdup(uri);
559 extension_point = g_strrstr(uri_copy, old_extension);
560 extension_point[0] = '\0';
562 final_name = g_strconcat(uri_copy, ".png", NULL);
563 fe->set_text(final_name);
565 g_free(final_name);
566 g_free(uri_copy);
567 }
568 } else {
569 name = g_strconcat(uri, ".png", NULL);
570 fe->set_text(name);
571 g_free(name);
572 }
574 doc_export_name = g_strdup(fe->get_text().c_str());
575 }
576 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
577 G_CALLBACK (sp_export_filename_modified), dlg);
579 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
581 {
582 // true = has mnemonic
583 Gtk::Button *b = new Gtk::Button();
585 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
586 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
587 Gtk::ICON_SIZE_BUTTON);
588 pixlabel->pack_start(*im);
590 Gtk::Label *l = new Gtk::Label();
591 l->set_markup_with_mnemonic(_("_Browse..."));
592 pixlabel->pack_start(*l);
594 b->add(*pixlabel);
596 hb->pack_end (*b, false, false, 4);
597 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
598 G_CALLBACK (sp_export_browse_clicked), NULL );
599 }
601 hb->pack_start (*fe, true, true, 0);
602 file_box->add(*hb);
603 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
604 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
605 original_name = g_strdup(fe->get_text().c_str());
606 // pressing enter in the filename field is the same as clicking export:
607 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
608 G_CALLBACK (sp_export_export_clicked), dlg );
609 // focus is in the filename initially:
610 fe->grab_focus();
612 // mnemonic in frame label moves focus to filename:
613 flabel->set_mnemonic_widget(*fe);
615 vb_singleexport->pack_start(*file_box);
616 }
618 {
619 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
620 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
621 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
622 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
623 batch_box->pack_start(*Glib::wrap(be), false, false);
624 gtk_tooltips_set_tip(tt, be, _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"), NULL);
625 batch_box->show_all();
626 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
627 vb->pack_start(*batch_box);
628 }
630 {
631 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
632 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
633 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
634 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
635 hide_box->pack_start(*Glib::wrap(he), false, false);
636 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
637 hide_box->show_all();
638 vb->pack_start(*hide_box);
639 }
641 /* Buttons */
642 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
643 bb->set_border_width(3);
645 {
646 Gtk::Button *b = new Gtk::Button();
647 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
648 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
649 Gtk::ICON_SIZE_BUTTON);
650 image_label->pack_start(*im);
652 Gtk::Label *l = new Gtk::Label();
653 l->set_markup_with_mnemonic(_("_Export"));
654 image_label->pack_start(*l);
656 b->add(*image_label);
657 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
658 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
659 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
660 bb->pack_end(*b, false, false, 0);
661 }
663 vb->pack_end(*bb, false, false, 0);
664 vb->show_all();
666 } // end of if (!dlg)
668 sp_export_find_default_selection(dlg);
670 gtk_window_present ((GtkWindow *) dlg);
672 return;
673 } // end of sp_export_dialog()
675 static void
676 sp_export_update_checkbuttons (GtkObject *base)
677 {
678 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
679 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
680 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
681 if (num >= 2) {
682 gtk_widget_set_sensitive (be, true);
683 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
684 } else {
685 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
686 gtk_widget_set_sensitive (be, FALSE);
687 }
688 if (num > 0) {
689 gtk_widget_set_sensitive (he, true);
690 } else {
691 gtk_widget_set_sensitive (he, false);
692 }
693 }
695 static inline void
696 sp_export_find_default_selection(GtkWidget * dlg)
697 {
698 selection_type key = SELECTION_NUMBER_OF;
700 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
701 key = SELECTION_SELECTION;
702 }
704 /* Try using the preferences */
705 if (key == SELECTION_NUMBER_OF) {
706 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
707 int i = SELECTION_NUMBER_OF;
709 Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
711 if (!what.empty()) {
712 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
713 if (what == selection_names[i]) {
714 break;
715 }
716 }
717 }
719 key = (selection_type)i;
720 }
722 if (key == SELECTION_NUMBER_OF) {
723 key = SELECTION_SELECTION;
724 }
726 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
727 selection_names[key]);
728 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
730 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
731 }
734 /**
735 * \brief If selection changed or a different document activated, we must
736 * recalculate any chosen areas
737 *
738 */
739 static void
740 sp_export_selection_changed ( Inkscape::Application *inkscape,
741 Inkscape::Selection *selection,
742 GtkObject *base )
743 {
744 selection_type current_key;
745 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
747 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
748 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
749 was_empty) {
750 gtk_toggle_button_set_active
751 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
752 TRUE );
753 }
754 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
756 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
758 if (inkscape &&
759 SP_IS_INKSCAPE (inkscape) &&
760 selection &&
761 SELECTION_CUSTOM != current_key) {
762 GtkToggleButton * button;
763 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
764 sp_export_area_toggled(button, base);
765 }
767 sp_export_update_checkbuttons (base);
768 }
770 static void
771 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
772 Inkscape::Selection */*selection*/,
773 guint /*flags*/,
774 GtkObject *base )
775 {
776 selection_type current_key;
777 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
779 switch (current_key) {
780 case SELECTION_DRAWING:
781 if ( SP_ACTIVE_DESKTOP ) {
782 SPDocument *doc;
783 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
784 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
785 if (bbox) {
786 sp_export_set_area (base, bbox->min()[Geom::X],
787 bbox->min()[Geom::Y],
788 bbox->max()[Geom::X],
789 bbox->max()[Geom::Y]);
790 }
791 }
792 break;
793 case SELECTION_SELECTION:
794 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
795 NRRect bbox;
796 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
797 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
798 }
799 break;
800 default:
801 /* Do nothing for page or for custom */
802 break;
803 }
805 return;
806 }
808 /// Called when one of the selection buttons was toggled.
809 static void
810 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
811 {
812 if (gtk_object_get_data (base, "update"))
813 return;
815 selection_type key, old_key;
816 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
817 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
819 /* Ignore all "turned off" events unless we're the only active button */
820 if (!gtk_toggle_button_get_active (tb) ) {
822 /* Don't let the current selection be deactived - but rerun the
823 activate to allow the user to renew the values */
824 if (key == old_key) {
825 gtk_toggle_button_set_active ( tb, TRUE );
826 }
828 return;
829 }
831 /* Turn off the currently active button unless it's us */
832 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
834 if (old_key != key) {
835 gtk_toggle_button_set_active
836 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
837 FALSE );
838 }
840 if ( SP_ACTIVE_DESKTOP )
841 {
842 SPDocument *doc;
843 Geom::OptRect bbox;
844 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
846 /* Notice how the switch is used to 'fall through' here to get
847 various backups. If you modify this without noticing you'll
848 probabaly screw something up. */
849 switch (key) {
850 case SELECTION_SELECTION:
851 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
852 {
853 bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds();
854 /* Only if there is a selection that we can set
855 do we break, otherwise we fall through to the
856 drawing */
857 // std::cout << "Using selection: SELECTION" << std::endl;
858 key = SELECTION_SELECTION;
859 break;
860 }
861 case SELECTION_DRAWING:
862 /** \todo
863 * This returns wrong values if the document has a viewBox.
864 */
865 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
866 /* If the drawing is valid, then we'll use it and break
867 otherwise we drop through to the page settings */
868 if (bbox) {
869 // std::cout << "Using selection: DRAWING" << std::endl;
870 key = SELECTION_DRAWING;
871 break;
872 }
873 case SELECTION_PAGE:
874 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
875 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
877 // std::cout << "Using selection: PAGE" << std::endl;
878 key = SELECTION_PAGE;
879 break;
880 case SELECTION_CUSTOM:
881 default:
882 break;
883 } // switch
885 // remember area setting
886 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
887 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
889 if ( key != SELECTION_CUSTOM && bbox ) {
890 sp_export_set_area (base, bbox->min()[Geom::X],
891 bbox->min()[Geom::Y],
892 bbox->max()[Geom::X],
893 bbox->max()[Geom::Y]);
894 }
896 } // end of if ( SP_ACTIVE_DESKTOP )
899 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
900 GtkWidget * file_entry;
901 const gchar * filename = NULL;
902 float xdpi = 0.0, ydpi = 0.0;
904 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
906 switch (key) {
907 case SELECTION_PAGE:
908 case SELECTION_DRAWING: {
909 SPDocument * doc = SP_ACTIVE_DOCUMENT;
910 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
912 if (filename == NULL) {
913 if (doc_export_name != NULL) {
914 filename = g_strdup(doc_export_name);
915 } else {
916 filename = g_strdup("");
917 }
918 }
919 break;
920 }
921 case SELECTION_SELECTION:
922 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
924 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
926 /* If we still don't have a filename -- let's build
927 one that's nice */
928 if (filename == NULL) {
929 const gchar * id = NULL;
930 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
931 for(; reprlst != NULL; reprlst = reprlst->next) {
932 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
933 if (repr->attribute("id")) {
934 id = repr->attribute("id");
935 break;
936 }
937 }
939 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
940 }
941 }
942 break;
943 case SELECTION_CUSTOM:
944 default:
945 break;
946 }
948 if (filename != NULL) {
949 g_free(original_name);
950 original_name = g_strdup(filename);
951 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
952 }
954 if (xdpi != 0.0) {
955 sp_export_value_set(base, "xdpi", xdpi);
956 }
958 /* These can't be separate, and setting x sets y, so for
959 now setting this is disabled. Hopefully it won't be in
960 the future */
961 if (FALSE && ydpi != 0.0) {
962 sp_export_value_set(base, "ydpi", ydpi);
963 }
964 }
966 return;
967 } // end of sp_export_area_toggled()
969 /// Called when dialog is deleted
970 static gint
971 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
972 {
973 g_object_set_data (base, "cancel", (gpointer) 1);
974 return TRUE;
975 } // end of sp_export_progress_delete()
977 /// Called when progress is cancelled
978 static void
979 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
980 {
981 g_object_set_data (base, "cancel", (gpointer) 1);
982 } // end of sp_export_progress_cancel()
984 /// Called for every progress iteration
985 static unsigned int
986 sp_export_progress_callback (float value, void *data)
987 {
988 GtkWidget *prg;
989 int evtcount;
991 if (g_object_get_data ((GObject *) data, "cancel"))
992 return FALSE;
994 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
995 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
997 evtcount = 0;
998 while ((evtcount < 16) && gdk_events_pending ()) {
999 gtk_main_iteration_do (FALSE);
1000 evtcount += 1;
1001 }
1003 gtk_main_iteration_do (FALSE);
1005 return TRUE;
1007 } // end of sp_export_progress_callback()
1009 GtkWidget *
1010 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1011 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1013 dlg = gtk_dialog_new ();
1014 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1015 prg = gtk_progress_bar_new ();
1016 sp_transientize (dlg);
1017 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1018 g_object_set_data ((GObject *) base, "progress", prg);
1020 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1022 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1023 GTK_PROGRESS_LEFT_TO_RIGHT);
1024 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1025 prg, FALSE, FALSE, 4 );
1026 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1027 GTK_STOCK_CANCEL,
1028 GTK_RESPONSE_CANCEL );
1030 g_signal_connect ( (GObject *) dlg, "delete_event",
1031 (GCallback) sp_export_progress_delete, base);
1032 g_signal_connect ( (GObject *) btn, "clicked",
1033 (GCallback) sp_export_progress_cancel, base);
1034 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1035 gtk_widget_show_all (dlg);
1037 return dlg;
1038 }
1040 // FIXME: Some lib function should be available to do this ...
1041 static gchar *
1042 filename_add_extension (const gchar *filename, const gchar *extension)
1043 {
1044 const gchar *dot;
1046 dot = strrchr (filename, '.');
1047 if ( !dot )
1048 return g_strconcat (filename, ".", extension, NULL);
1049 {
1050 if (dot[1] == '\0')
1051 return g_strconcat (filename, extension, NULL);
1052 else
1053 {
1054 if (g_strcasecmp (dot + 1, extension) == 0)
1055 return g_strdup (filename);
1056 else
1057 {
1058 return g_strconcat (filename, ".", extension, NULL);
1059 }
1060 }
1061 }
1062 }
1064 /// Called when export button is clicked
1065 static void
1066 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1067 {
1068 if (!SP_ACTIVE_DESKTOP) return;
1070 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1072 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1073 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1074 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1075 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1076 // Batch export of selected objects
1078 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1079 gint n = 0;
1081 if (num < 1)
1082 return;
1084 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1085 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1086 g_free (progress_text);
1088 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1089 i != NULL;
1090 i = i->next) {
1091 SPItem *item = (SPItem *) i->data;
1092 // retrieve export filename hint
1093 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1094 if (!fn) {
1095 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1096 }
1098 // retrieve export dpi hints
1099 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1100 gdouble dpi = 0.0;
1101 if (dpi_hint) {
1102 dpi = atof(dpi_hint);
1103 }
1104 if (dpi == 0.0) {
1105 dpi = DPI_BASE;
1106 }
1108 Geom::OptRect area;
1109 sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1110 if (area) {
1111 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1112 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1114 if (width > 1 && height > 1) {
1115 /* Do export */
1116 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1117 *area, width, height, dpi, dpi,
1118 nv->pagecolor,
1119 NULL, NULL, TRUE, // overwrite without asking
1120 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1121 )) {
1122 gchar * error;
1123 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1124 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1125 sp_ui_error_dialog(error);
1126 g_free(safeFile);
1127 g_free(error);
1128 }
1129 }
1130 }
1131 n++;
1132 sp_export_progress_callback((float)n/num, base);
1133 }
1135 gtk_widget_destroy (prog_dlg);
1136 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1138 } else {
1140 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1141 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1143 float const x0 = sp_export_value_get_px(base, "x0");
1144 float const y0 = sp_export_value_get_px(base, "y0");
1145 float const x1 = sp_export_value_get_px(base, "x1");
1146 float const y1 = sp_export_value_get_px(base, "y1");
1147 float const xdpi = sp_export_value_get(base, "xdpi");
1148 float const ydpi = sp_export_value_get(base, "ydpi");
1149 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1150 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1152 if (filename == NULL || *filename == '\0') {
1153 sp_ui_error_dialog(_("You have to enter a filename"));
1154 return;
1155 }
1157 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1158 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1159 return;
1160 }
1162 gchar *dirname = g_path_get_dirname(filename);
1163 if ( dirname == NULL
1164 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1165 {
1166 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1167 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1168 safeDir);
1169 sp_ui_error_dialog(error);
1170 g_free(safeDir);
1171 g_free(error);
1172 g_free(dirname);
1173 return;
1174 }
1175 g_free(dirname);
1177 // make sure that .png is the extension of the file:
1178 gchar * filename_ext = filename_add_extension(filename, "png");
1179 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1181 gchar *fn = g_path_get_basename (filename_ext);
1183 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1184 g_free (fn);
1185 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1186 g_free (progress_text);
1188 /* Do export */
1189 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1190 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1191 nv->pagecolor,
1192 sp_export_progress_callback, base, FALSE,
1193 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1194 )) {
1195 gchar * error;
1196 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1197 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1198 sp_ui_error_dialog(error);
1199 g_free(safeFile);
1200 g_free(error);
1201 }
1203 /* Reset the filename so that it can be changed again by changing
1204 selections and all that */
1205 g_free(original_name);
1206 original_name = g_strdup(filename_ext);
1207 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1209 gtk_widget_destroy (prog_dlg);
1210 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1212 /* Setup the values in the document */
1213 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1214 case SELECTION_PAGE:
1215 case SELECTION_DRAWING: {
1216 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1217 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1218 bool modified = false;
1219 const gchar * temp_string;
1221 bool saved = sp_document_get_undo_sensitive(doc);
1222 sp_document_set_undo_sensitive(doc, false);
1224 temp_string = repr->attribute("inkscape:export-filename");
1225 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1226 repr->setAttribute("inkscape:export-filename", filename_ext);
1227 modified = true;
1228 }
1229 temp_string = repr->attribute("inkscape:export-xdpi");
1230 if (temp_string == NULL || xdpi != atof(temp_string)) {
1231 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1232 modified = true;
1233 }
1234 temp_string = repr->attribute("inkscape:export-ydpi");
1235 if (temp_string == NULL || xdpi != atof(temp_string)) {
1236 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1237 modified = true;
1238 }
1239 sp_document_set_undo_sensitive(doc, saved);
1241 if (modified) {
1242 doc->setModifiedSinceSave();
1243 }
1244 break;
1245 }
1246 case SELECTION_SELECTION: {
1247 const GSList * reprlst;
1248 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1249 bool modified = false;
1251 bool saved = sp_document_get_undo_sensitive(doc);
1252 sp_document_set_undo_sensitive(doc, false);
1253 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1255 for(; reprlst != NULL; reprlst = reprlst->next) {
1256 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1257 const gchar * temp_string;
1259 if (repr->attribute("id") == NULL ||
1260 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1261 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1262 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1263 temp_string = repr->attribute("inkscape:export-filename");
1264 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1265 repr->setAttribute("inkscape:export-filename", filename_ext);
1266 modified = true;
1267 }
1268 }
1269 temp_string = repr->attribute("inkscape:export-xdpi");
1270 if (temp_string == NULL || xdpi != atof(temp_string)) {
1271 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1272 modified = true;
1273 }
1274 temp_string = repr->attribute("inkscape:export-ydpi");
1275 if (temp_string == NULL || xdpi != atof(temp_string)) {
1276 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1277 modified = true;
1278 }
1279 }
1280 sp_document_set_undo_sensitive(doc, saved);
1282 if (modified) {
1283 doc->setModifiedSinceSave();
1284 }
1285 break;
1286 }
1287 default:
1288 break;
1289 }
1291 g_free (filename_ext);
1293 }
1295 } // end of sp_export_export_clicked()
1297 /// Called when Browse button is clicked
1298 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1299 static void
1300 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1301 {
1302 GtkWidget *fs, *fe;
1303 const gchar *filename;
1305 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1306 (GtkWindow*)dlg,
1307 GTK_FILE_CHOOSER_ACTION_SAVE,
1308 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1309 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1310 NULL );
1312 #ifdef WITH_GNOME_VFS
1313 if (gnome_vfs_initialized()) {
1314 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1315 }
1316 #endif
1318 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1320 sp_transientize (fs);
1322 gtk_window_set_modal(GTK_WINDOW (fs), true);
1324 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1326 if (*filename == '\0') {
1327 filename = create_filepath_from_id(NULL, NULL);
1328 }
1330 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1332 #ifdef WIN32
1333 // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1334 OPENFILENAMEW opf;
1335 WCHAR* filter_string = (WCHAR*)g_utf8_to_utf16("PNG\0*.png\0\0", 12, NULL, NULL, NULL);
1336 WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1337 WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1338 // Copy the selected file name, converting from UTF-8 to UTF-16
1339 WCHAR _filename[_MAX_PATH + 1];
1340 memset(_filename, 0, sizeof(_filename));
1341 gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1342 wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1343 g_free(utf16_path_string);
1345 opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1346 opf.lpstrFilter = filter_string;
1347 opf.lpstrCustomFilter = 0;
1348 opf.nMaxCustFilter = 0L;
1349 opf.nFilterIndex = 1L;
1350 opf.lpstrFile = _filename;
1351 opf.nMaxFile = _MAX_PATH;
1352 opf.lpstrFileTitle = NULL;
1353 opf.nMaxFileTitle=0;
1354 opf.lpstrInitialDir = 0;
1355 opf.lpstrTitle = title_string;
1356 opf.nFileOffset = 0;
1357 opf.nFileExtension = 2;
1358 opf.lpstrDefExt = extension_string;
1359 opf.lpfnHook = NULL;
1360 opf.lCustData = 0;
1361 opf.Flags = OFN_PATHMUSTEXIST;
1362 opf.lStructSize = sizeof(OPENFILENAMEW);
1363 if (GetSaveFileNameW(&opf) != 0)
1364 {
1365 // Copy the selected file name, converting from UTF-16 to UTF-8
1366 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1367 gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1368 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1369 g_free(utf8string);
1371 }
1372 g_free(extension_string);
1373 g_free(title_string);
1374 g_free(filter_string);
1375 #else
1376 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1377 {
1378 gchar *file;
1380 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1382 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1383 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1385 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1387 g_free(utf8file);
1388 g_free(file);
1389 }
1390 #endif
1392 gtk_widget_destroy (fs);
1394 return;
1395 } // end of sp_export_browse_clicked()
1397 // TODO: Move this to nr-rect-fns.h.
1398 static bool
1399 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1400 {
1401 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1402 return (
1403 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1404 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1405 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1406 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1407 );
1408 }
1410 /**
1411 \brief This function is used to detect the current selection setting
1412 based on the values in the x0, y0, x1 and y0 fields.
1413 \param base The export dialog itself
1415 One of the most confusing parts of this function is why the array
1416 is built at the beginning. What needs to happen here is that we
1417 should always check the current selection to see if it is the valid
1418 one. While this is a performance improvement it is also a usability
1419 one during the cases where things like selections and drawings match
1420 size. This way buttons change less 'randomly' (atleast in the eyes
1421 of the user). To do this an array is built where the current selection
1422 type is placed first, and then the others in an order from smallest
1423 to largest (this can be configured by reshuffling \c test_order).
1425 All of the values in this function are rounded to two decimal places
1426 because that is what is shown to the user. While everything is kept
1427 more accurate than that, the user can't control more acurrate than
1428 that, so for this to work for them - it needs to check on that level
1429 of accuracy.
1431 \todo finish writing this up
1432 */
1433 static void
1434 sp_export_detect_size(GtkObject * base) {
1435 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1436 selection_type this_test[SELECTION_NUMBER_OF + 1];
1437 selection_type key = SELECTION_NUMBER_OF;
1439 Geom::Point x(sp_export_value_get_px (base, "x0"),
1440 sp_export_value_get_px (base, "y0"));
1441 Geom::Point y(sp_export_value_get_px (base, "x1"),
1442 sp_export_value_get_px (base, "y1"));
1443 Geom::Rect current_bbox(x, y);
1444 //std::cout << "Current " << current_bbox;
1446 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1447 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1448 this_test[i + 1] = test_order[i];
1449 }
1451 for (int i = 0;
1452 i < SELECTION_NUMBER_OF + 1 &&
1453 key == SELECTION_NUMBER_OF &&
1454 SP_ACTIVE_DESKTOP != NULL;
1455 i++) {
1456 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1457 switch (this_test[i]) {
1458 case SELECTION_SELECTION:
1459 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1460 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1462 //std::cout << "Selection " << bbox;
1463 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1464 key = SELECTION_SELECTION;
1465 }
1466 }
1467 break;
1468 case SELECTION_DRAWING: {
1469 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1471 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1473 // std::cout << "Drawing " << bbox2;
1474 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1475 key = SELECTION_DRAWING;
1476 }
1477 break;
1478 }
1480 case SELECTION_PAGE: {
1481 SPDocument *doc;
1483 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1485 Geom::Point x(0.0, 0.0);
1486 Geom::Point y(sp_document_width(doc),
1487 sp_document_height(doc));
1488 Geom::Rect bbox(x, y);
1490 // std::cout << "Page " << bbox;
1491 if (sp_export_bbox_equal(bbox,current_bbox)) {
1492 key = SELECTION_PAGE;
1493 }
1495 break;
1496 }
1497 default:
1498 break;
1499 }
1500 }
1501 // std::cout << std::endl;
1503 if (key == SELECTION_NUMBER_OF) {
1504 key = SELECTION_CUSTOM;
1505 }
1507 /* We're now using a custom size, not a fixed one */
1508 /* printf("Detecting state: %s\n", selection_names[key]); */
1509 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1510 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1511 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1512 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1514 return;
1515 } /* sp_export_detect_size */
1517 /// Called when area x0 value is changed
1518 static void
1519 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1520 {
1521 float x0, x1, xdpi, width;
1523 if (gtk_object_get_data (base, "update"))
1524 return;
1526 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1527 (base, "units")))
1528 {
1529 return;
1530 }
1532 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1534 x0 = sp_export_value_get_px (base, "x0");
1535 x1 = sp_export_value_get_px (base, "x1");
1536 xdpi = sp_export_value_get (base, "xdpi");
1538 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1540 if (width < SP_EXPORT_MIN_SIZE) {
1541 const gchar *key;
1542 width = SP_EXPORT_MIN_SIZE;
1543 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1545 if (!strcmp (key, "x0")) {
1546 x1 = x0 + width * DPI_BASE / xdpi;
1547 sp_export_value_set_px (base, "x1", x1);
1548 } else {
1549 x0 = x1 - width * DPI_BASE / xdpi;
1550 sp_export_value_set_px (base, "x0", x0);
1551 }
1552 }
1554 sp_export_value_set_px (base, "width", x1 - x0);
1555 sp_export_value_set (base, "bmwidth", width);
1557 sp_export_detect_size(base);
1559 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1561 return;
1562 } // end of sp_export_area_x_value_changed()
1564 /// Called when area y0 value is changed.
1565 static void
1566 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1567 {
1568 float y0, y1, ydpi, height;
1570 if (gtk_object_get_data (base, "update"))
1571 return;
1573 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1574 (base, "units")))
1575 {
1576 return;
1577 }
1579 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1581 y0 = sp_export_value_get_px (base, "y0");
1582 y1 = sp_export_value_get_px (base, "y1");
1583 ydpi = sp_export_value_get (base, "ydpi");
1585 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1587 if (height < SP_EXPORT_MIN_SIZE) {
1588 const gchar *key;
1589 height = SP_EXPORT_MIN_SIZE;
1590 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1591 if (!strcmp (key, "y0")) {
1592 y1 = y0 + height * DPI_BASE / ydpi;
1593 sp_export_value_set_px (base, "y1", y1);
1594 } else {
1595 y0 = y1 - height * DPI_BASE / ydpi;
1596 sp_export_value_set_px (base, "y0", y0);
1597 }
1598 }
1600 sp_export_value_set_px (base, "height", y1 - y0);
1601 sp_export_value_set (base, "bmheight", height);
1603 sp_export_detect_size(base);
1605 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1607 return;
1608 } // end of sp_export_area_y_value_changed()
1610 /// Called when x1-x0 or area width is changed
1611 static void
1612 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1613 {
1614 float x0, x1, xdpi, width, bmwidth;
1616 if (gtk_object_get_data (base, "update"))
1617 return;
1619 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1620 (base, "units"))) {
1621 return;
1622 }
1624 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1626 x0 = sp_export_value_get_px (base, "x0");
1627 x1 = sp_export_value_get_px (base, "x1");
1628 xdpi = sp_export_value_get (base, "xdpi");
1629 width = sp_export_value_get_px (base, "width");
1630 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1632 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1634 bmwidth = SP_EXPORT_MIN_SIZE;
1635 width = bmwidth * DPI_BASE / xdpi;
1636 sp_export_value_set_px (base, "width", width);
1637 }
1639 sp_export_value_set_px (base, "x1", x0 + width);
1640 sp_export_value_set (base, "bmwidth", bmwidth);
1642 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1644 return;
1645 } // end of sp_export_area_width_value_changed()
1647 /// Called when y1-y0 or area height is changed.
1648 static void
1649 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1650 {
1652 float y0, y1, ydpi, height, bmheight;
1654 if (gtk_object_get_data (base, "update"))
1655 return;
1657 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1658 (base, "units"))) {
1659 return;
1660 }
1662 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1664 y0 = sp_export_value_get_px (base, "y0");
1665 y1 = sp_export_value_get_px (base, "y1");
1666 ydpi = sp_export_value_get (base, "ydpi");
1667 height = sp_export_value_get_px (base, "height");
1668 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1670 if (bmheight < SP_EXPORT_MIN_SIZE) {
1671 bmheight = SP_EXPORT_MIN_SIZE;
1672 height = bmheight * DPI_BASE / ydpi;
1673 sp_export_value_set_px (base, "height", height);
1674 }
1676 sp_export_value_set_px (base, "y1", y0 + height);
1677 sp_export_value_set (base, "bmheight", bmheight);
1679 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1681 return;
1682 } // end of sp_export_area_height_value_changed()
1684 /**
1685 \brief A function to set the ydpi
1686 \param base The export dialog
1688 This function grabs all of the y values and then figures out the
1689 new bitmap size based on the changing dpi value. The dpi value is
1690 gotten from the xdpi setting as these can not currently be independent.
1691 */
1692 static void
1693 sp_export_set_image_y (GtkObject *base)
1694 {
1695 float y0, y1, xdpi;
1697 y0 = sp_export_value_get_px (base, "y0");
1698 y1 = sp_export_value_get_px (base, "y1");
1699 xdpi = sp_export_value_get (base, "xdpi");
1701 sp_export_value_set (base, "ydpi", xdpi);
1702 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1704 return;
1705 } // end of sp_export_set_image_y()
1707 /**
1708 \brief A function to set the xdpi
1709 \param base The export dialog
1711 This function grabs all of the x values and then figures out the
1712 new bitmap size based on the changing dpi value. The dpi value is
1713 gotten from the xdpi setting as these can not currently be independent.
1714 */
1715 static void
1716 sp_export_set_image_x (GtkObject *base)
1717 {
1718 float x0, x1, xdpi;
1720 x0 = sp_export_value_get_px (base, "x0");
1721 x1 = sp_export_value_get_px (base, "x1");
1722 xdpi = sp_export_value_get (base, "xdpi");
1724 sp_export_value_set (base, "ydpi", xdpi);
1725 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1727 return;
1728 } // end of sp_export_set_image_x()
1730 /// Called when pixel width is changed
1731 static void
1732 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1733 {
1734 float x0, x1, bmwidth, xdpi;
1736 if (gtk_object_get_data (base, "update"))
1737 return;
1739 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1740 (base, "units"))) {
1741 return;
1742 }
1744 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1746 x0 = sp_export_value_get_px (base, "x0");
1747 x1 = sp_export_value_get_px (base, "x1");
1748 bmwidth = sp_export_value_get (base, "bmwidth");
1750 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1751 bmwidth = SP_EXPORT_MIN_SIZE;
1752 sp_export_value_set (base, "bmwidth", bmwidth);
1753 }
1755 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1756 sp_export_value_set (base, "xdpi", xdpi);
1758 sp_export_set_image_y (base);
1760 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1762 return;
1763 } // end of sp_export_bitmap_width_value_changed()
1765 /// Called when pixel height is changed
1766 static void
1767 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1768 {
1769 float y0, y1, bmheight, xdpi;
1771 if (gtk_object_get_data (base, "update"))
1772 return;
1774 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1775 (base, "units"))) {
1776 return;
1777 }
1779 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1781 y0 = sp_export_value_get_px (base, "y0");
1782 y1 = sp_export_value_get_px (base, "y1");
1783 bmheight = sp_export_value_get (base, "bmheight");
1785 if (bmheight < SP_EXPORT_MIN_SIZE) {
1786 bmheight = SP_EXPORT_MIN_SIZE;
1787 sp_export_value_set (base, "bmheight", bmheight);
1788 }
1790 xdpi = bmheight * DPI_BASE / (y1 - y0);
1791 sp_export_value_set (base, "xdpi", xdpi);
1793 sp_export_set_image_x (base);
1795 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1797 return;
1798 } // end of sp_export_bitmap_width_value_changed()
1800 /**
1801 \brief A function to adjust the bitmap width when the xdpi value changes
1802 \param adj The adjustment that was changed
1803 \param base The export dialog itself
1805 The first thing this function checks is to see if we are doing an
1806 update. If we are, this function just returns because there is another
1807 instance of it that will handle everything for us. If there is a
1808 units change, we also assume that everyone is being updated appropriately
1809 and there is nothing for us to do.
1811 If we're the highest level function, we set the update flag, and
1812 continue on our way.
1814 All of the values are grabbed using the \c sp_export_value_get functions
1815 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1816 xdpi value is saved in the preferences for the next time the dialog
1817 is opened. (does the selection dpi need to be set here?)
1819 A check is done to to ensure that we aren't outputing an invalid width,
1820 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1821 changed to make it valid.
1823 After all of this the bitmap width is changed.
1825 We also change the ydpi. This is a temporary hack as these can not
1826 currently be independent. This is likely to change in the future.
1827 */
1828 void
1829 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1830 {
1831 float x0, x1, xdpi, bmwidth;
1833 if (gtk_object_get_data (base, "update"))
1834 return;
1836 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1837 (base, "units"))) {
1838 return;
1839 }
1841 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1843 x0 = sp_export_value_get_px (base, "x0");
1844 x1 = sp_export_value_get_px (base, "x1");
1845 xdpi = sp_export_value_get (base, "xdpi");
1847 // remember xdpi setting
1848 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1849 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1851 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1853 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1854 bmwidth = SP_EXPORT_MIN_SIZE;
1855 if (x1 != x0)
1856 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1857 else
1858 xdpi = DPI_BASE;
1859 sp_export_value_set (base, "xdpi", xdpi);
1860 }
1862 sp_export_value_set (base, "bmwidth", bmwidth);
1864 sp_export_set_image_y (base);
1866 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1868 return;
1869 } // end of sp_export_xdpi_value_changed()
1872 /**
1873 \brief A function to change the area that is used for the exported
1874 bitmap.
1875 \param base This is the export dialog
1876 \param x0 Horizontal upper left hand corner of the picture in points
1877 \param y0 Vertical upper left hand corner of the picture in points
1878 \param x1 Horizontal lower right hand corner of the picture in points
1879 \param y1 Vertical lower right hand corner of the picture in points
1881 This function just calls \c sp_export_value_set_px for each of the
1882 parameters that is passed in. This allows for setting them all in
1883 one convient area.
1885 Update is set to suspend all of the other test running while all the
1886 values are being set up. This allows for a performance increase, but
1887 it also means that the wrong type won't be detected with only some of
1888 the values set. After all the values are set everyone is told that
1889 there has been an update.
1890 */
1891 static void
1892 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1893 {
1894 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1895 sp_export_value_set_px (base, "x1", x1);
1896 sp_export_value_set_px (base, "y1", y1);
1897 sp_export_value_set_px (base, "x0", x0);
1898 sp_export_value_set_px (base, "y0", y0);
1899 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1901 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1902 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1904 return;
1905 }
1907 /**
1908 \brief Sets the value of an adjustment
1909 \param base The export dialog
1910 \param key Which adjustment to set
1911 \param val What value to set it to
1913 This function finds the adjustment using the data stored in the
1914 export dialog. After finding the adjustment it then sets
1915 the value of it.
1916 */
1917 static void
1918 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1919 {
1920 GtkAdjustment *adj;
1922 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1924 gtk_adjustment_set_value (adj, val);
1925 }
1927 /**
1928 \brief A function to set a value using the units points
1929 \param base The export dialog
1930 \param key Which value should be set
1931 \param val What the value should be in points
1933 This function first gets the adjustment for the key that is passed
1934 in. It then figures out what units are currently being used in the
1935 dialog. After doing all of that, it then converts the incoming
1936 value and sets the adjustment.
1937 */
1938 static void
1939 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1940 {
1941 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1943 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1945 return;
1946 }
1948 /**
1949 \brief Get the value of an adjustment in the export dialog
1950 \param base The export dialog
1951 \param key Which adjustment is being looked for
1952 \return The value in the specified adjustment
1954 This function gets the adjustment from the data field in the export
1955 dialog. It then grabs the value from the adjustment.
1956 */
1957 static float
1958 sp_export_value_get ( GtkObject *base, const gchar *key )
1959 {
1960 GtkAdjustment *adj;
1962 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1964 return adj->value;
1965 }
1967 /**
1968 \brief Grabs a value in the export dialog and converts the unit
1969 to points
1970 \param base The export dialog
1971 \param key Which value should be returned
1972 \return The value in the adjustment in points
1974 This function, at its most basic, is a call to \c sp_export_value_get
1975 to get the value of the adjustment. It then finds the units that
1976 are being used by looking at the "units" attribute of the export
1977 dialog. Using that it converts the returned value into points.
1978 */
1979 static float
1980 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1981 {
1982 float value = sp_export_value_get(base, key);
1983 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1985 return sp_units_get_pixels (value, *unit);
1986 } // end of sp_export_value_get_px()
1988 /**
1989 \brief This function is called when the filename is changed by
1990 anyone. It resets the virgin bit.
1991 \param object Text entry box
1992 \param data The export dialog
1993 \return None
1995 This function gets called when the text area is modified. It is
1996 looking for the case where the text area is modified from its
1997 original value. In that case it sets the "filename-modified" bit
1998 to TRUE. If the text dialog returns back to the original text, the
1999 bit gets reset. This should stop simple mistakes.
2000 */
2001 static void
2002 sp_export_filename_modified (GtkObject * object, gpointer data)
2003 {
2004 GtkWidget * text_entry = (GtkWidget *)object;
2005 GtkWidget * export_dialog = (GtkWidget *)data;
2007 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2008 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2009 // printf("Modified: FALSE\n");
2010 } else {
2011 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2012 // printf("Modified: TRUE\n");
2013 }
2015 return;
2016 } // end sp_export_filename_modified
2018 /*
2019 Local Variables:
2020 mode:c++
2021 c-file-style:"stroustrup"
2022 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2023 indent-tabs-mode:nil
2024 fill-column:99
2025 End:
2026 */
2027 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :