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 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 1999-2007 Authors
12 * Copyright (C) 2001-2002 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
22 #include <png.h>
24 #include <gtk/gtk.h>
25 #include <gtkmm/box.h>
26 #include <gtkmm/buttonbox.h>
27 #include <gtkmm/label.h>
28 #include <gtkmm/widget.h>
29 #include <gtkmm/togglebutton.h>
30 #include <gtkmm/entry.h>
31 #include <gtkmm/image.h>
32 #include <gtkmm/stockid.h>
33 #include <gtkmm/stock.h>
34 #ifdef WITH_GNOME_VFS
35 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
36 #endif
38 #include <glibmm/i18n.h>
39 #include "helper/unit-menu.h"
40 #include "helper/units.h"
41 #include "unit-constants.h"
42 #include "helper/window.h"
43 #include "inkscape-private.h"
44 #include "document.h"
45 #include "desktop-handles.h"
46 #include "sp-item.h"
47 #include "selection.h"
48 #include "file.h"
49 #include "macros.h"
50 #include "sp-namedview.h"
51 #include "selection-chemistry.h"
53 #include "dialog-events.h"
54 #include "preferences.h"
55 #include "verbs.h"
56 #include "interface.h"
58 #include "extension/output.h"
59 #include "extension/db.h"
61 #include "io/sys.h"
63 #include "helper/png-write.h"
65 #ifdef WIN32
66 #include <windows.h>
67 #include <commdlg.h>
68 #include <gdk/gdkwin32.h>
69 #endif
71 using Inkscape::DocumentUndo;
73 #define SP_EXPORT_MIN_SIZE 1.0
75 #define DPI_BASE PX_PER_IN
77 #define EXPORT_COORD_PRECISION 3
79 #define MIN_ONSCREEN_DISTANCE 50
81 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
82 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
83 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
85 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
86 GtkObject *base);
88 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
89 GtkObject *base);
91 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
92 GtkObject *base);
94 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
95 GtkObject *base);
97 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
98 GtkObject *base);
100 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
101 GtkObject *base);
103 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
104 GtkObject *base);
106 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
107 Inkscape::Selection *selection,
108 GtkObject *base);
109 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
110 Inkscape::Selection *selection,
111 guint flags,
112 GtkObject *base );
114 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
115 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
116 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
117 static float sp_export_value_get ( GtkObject *base, const gchar *key );
118 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
120 static void sp_export_filename_modified (GtkObject * object, gpointer data);
121 static inline void sp_export_find_default_selection(GtkWidget * dlg);
122 static void sp_export_detect_size(GtkObject * base);
124 static Glib::ustring const prefs_path = "/dialogs/export/";
126 // these all need to be reinitialized to their defaults during dialog_destroy
127 static GtkWidget *dlg = NULL;
128 static win_data wd;
129 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
130 static gchar * original_name = NULL;
131 static gchar * doc_export_name = NULL;
132 static bool was_empty = TRUE;
134 /** What type of button is being pressed */
135 enum selection_type {
136 SELECTION_PAGE = 0, /**< Export the whole page */
137 SELECTION_DRAWING, /**< Export everything drawn on the page */
138 SELECTION_SELECTION, /**< Export everything that is selected */
139 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
140 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
141 };
143 /** A list of strings that is used both in the preferences, and in the
144 data fields to describe the various values of \c selection_type. */
145 static const char * selection_names[SELECTION_NUMBER_OF] = {
146 "page", "drawing", "selection", "custom"};
148 /** The names on the buttons for the various selection types. */
149 static const char * selection_labels[SELECTION_NUMBER_OF] = {
150 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
152 static void
153 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
154 {
155 sp_signal_disconnect_by_data (INKSCAPE, dlg);
157 wd.win = dlg = NULL;
158 wd.stop = 0;
159 x = -1000; y = -1000; w = 0; h = 0;
160 g_free(original_name);
161 original_name = NULL;
162 g_free(doc_export_name);
163 doc_export_name = NULL;
164 was_empty = TRUE;
166 return;
167 } // end of sp_export_dialog_destroy()
169 /// Called when dialog is closed or inkscape is shut down.
170 static bool
171 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
172 {
174 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
175 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
177 if (x<0) x=0;
178 if (y<0) y=0;
180 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
181 prefs->setInt(prefs_path + "x", x);
182 prefs->setInt(prefs_path + "y", y);
183 prefs->setInt(prefs_path + "w", w);
184 prefs->setInt(prefs_path + "h", h);
186 return FALSE; // which means, go ahead and destroy it
188 } // end of sp_export_dialog_delete()
190 /**
191 \brief Creates a new spin button for the export dialog
192 \param key The name of the spin button
193 \param val A default value for the spin button
194 \param min Minimum value for the spin button
195 \param max Maximum value for the spin button
196 \param step The step size for the spin button
197 \param page Size of the page increment
198 \param us Unit selector that effects this spin button
199 \param t Table to put the spin button in
200 \param x X location in the table \c t to start with
201 \param y Y location in the table \c t to start with
202 \param ll Text to put on the left side of the spin button (optional)
203 \param lr Text to put on the right side of the spin button (optional)
204 \param digits Number of digits to display after the decimal
205 \param sensitive Whether the spin button is sensitive or not
206 \param cb Callback for when this spin button is changed (optional)
207 \param dlg Export dialog the spin button is being placed in
209 */
210 static void
211 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
212 float step, float page, GtkWidget *us,
213 GtkWidget *t, int x, int y,
214 const gchar *ll, const gchar *lr,
215 int digits, unsigned int sensitive,
216 GCallback cb, GtkWidget *dlg )
217 {
218 GtkObject *adj = gtk_adjustment_new( val, min, max, step, page, 0 );
219 gtk_object_set_data( adj, "key", const_cast<gchar *>(key) );
220 gtk_object_set_data( GTK_OBJECT (dlg), (const gchar *)key, adj );
222 if (us) {
223 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
224 GTK_ADJUSTMENT (adj) );
225 }
227 int pos = 0;
229 GtkWidget *l = NULL;
231 if (ll) {
233 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
234 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
235 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
236 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
237 gtk_widget_set_sensitive (l, sensitive);
238 pos += 1;
240 }
242 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, digits);
243 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
244 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
245 gtk_widget_set_size_request (sb, 80, -1);
246 gtk_widget_set_sensitive (sb, sensitive);
247 pos += 1;
249 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
251 if (lr) {
253 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
254 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
255 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
256 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
257 gtk_widget_set_sensitive (l, sensitive);
258 pos += 1;
260 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
261 }
263 if (cb)
264 gtk_signal_connect (adj, "value_changed", cb, dlg);
266 return;
267 } // end of sp_export_spinbutton_new()
270 static Gtk::VBox *
271 sp_export_dialog_area_box (GtkWidget * dlg)
272 {
273 Gtk::VBox* vb = new Gtk::VBox(false, 3);
275 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
276 lbl->set_use_markup(true);
277 vb->pack_start(*lbl);
279 /* Units box */
280 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
281 /* gets added to the vbox later, but the unit selector is needed
282 earlier than that */
284 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
285 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
286 if (desktop)
287 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
288 unitbox->pack_end(*us, false, false, 0);
289 Gtk::Label* l = new Gtk::Label(_("Units:"));
290 unitbox->pack_end(*l, false, false, 3);
291 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
293 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
295 Gtk::ToggleButton* b;
296 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
297 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
298 b->set_data("key", GINT_TO_POINTER(i));
299 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
300 togglebox->pack_start(*b, false, true, 0);
301 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
302 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
303 }
305 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
306 G_CALLBACK (sp_export_selection_changed), dlg );
307 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
308 G_CALLBACK (sp_export_selection_modified), dlg );
309 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
310 G_CALLBACK (sp_export_selection_changed), dlg );
312 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
313 t->set_row_spacings (4);
314 t->set_col_spacings (4);
316 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
317 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
318 G_CALLBACK ( sp_export_area_x_value_changed),
319 dlg );
321 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
322 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
323 G_CALLBACK (sp_export_area_x_value_changed),
324 dlg );
326 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
327 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
328 G_CALLBACK
329 (sp_export_area_width_value_changed),
330 dlg );
332 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
333 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
334 G_CALLBACK (sp_export_area_y_value_changed),
335 dlg );
337 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
338 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
339 G_CALLBACK (sp_export_area_y_value_changed),
340 dlg );
342 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
343 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
344 G_CALLBACK (sp_export_area_height_value_changed),
345 dlg );
347 vb->pack_start(*togglebox, false, false, 3);
348 vb->pack_start(*t, false, false, 0);
349 vb->pack_start(*unitbox, false, false, 0);
351 return vb;
352 } // end of sp_export_dialog_area_box
355 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
357 if (id == NULL) /* This should never happen */
358 id = "bitmap";
360 gchar * directory = NULL;
362 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
363 // std::cout << "Directory from dialog" << std::endl;
364 directory = g_dirname(file_entry_text);
365 }
367 if (directory == NULL) {
368 /* Grab document directory */
369 if ( SP_ACTIVE_DOCUMENT->getURI() ) {
370 // std::cout << "Directory from document" << std::endl;
371 directory = g_dirname( SP_ACTIVE_DOCUMENT->getURI() );
372 }
373 }
375 if (directory == NULL) {
376 // std::cout << "Home Directory" << std::endl;
377 directory = homedir_path(NULL);
378 }
380 gchar * id_ext = g_strconcat(id, ".png", NULL);
381 gchar *filename = g_build_filename(directory, id_ext, NULL);
382 g_free(directory);
383 g_free(id_ext);
384 return filename;
385 }
387 static void
388 batch_export_clicked (GtkWidget *widget, GtkObject *base)
389 {
390 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
391 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
392 vb_singleexport->set_sensitive(false);
393 } else {
394 vb_singleexport->set_sensitive(true);
395 }
396 }
398 void
399 sp_export_dialog (void)
400 {
401 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
402 if (!dlg) {
404 gchar title[500];
405 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
407 dlg = sp_window_new (title, TRUE);
409 if (x == -1000 || y == -1000) {
410 x = prefs->getInt(prefs_path + "x", 0);
411 y = prefs->getInt(prefs_path + "y", 0);
412 }
414 if (w ==0 || h == 0) {
415 w = prefs->getInt(prefs_path + "w", 0);
416 h = prefs->getInt(prefs_path + "h", 0);
417 }
419 // if (x<0) x=0;
420 // if (y<0) y=0;
422 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
423 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
424 gtk_window_move ((GtkWindow *) dlg, x, y);
425 else
426 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
427 sp_transientize (dlg);
428 wd.win = dlg;
429 wd.stop = 0;
431 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
432 G_CALLBACK (sp_transientize_callback), &wd);
434 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
435 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
437 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
438 G_CALLBACK (sp_export_dialog_destroy), dlg);
440 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
441 G_CALLBACK (sp_export_dialog_delete), dlg);
443 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
444 G_CALLBACK (sp_export_dialog_delete), dlg);
446 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
447 G_CALLBACK (sp_dialog_hide), dlg);
449 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
450 G_CALLBACK (sp_dialog_unhide), dlg);
452 GtkTooltips *tt = gtk_tooltips_new();
454 Gtk::VBox *vb = new Gtk::VBox(false, 3);
455 vb->set_border_width(3);
456 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
458 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
459 vb_singleexport->set_border_width(0);
460 vb->pack_start(*vb_singleexport);
461 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
463 /* Export area frame */
464 {
465 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
466 area_box->set_border_width(3);
467 vb_singleexport->pack_start(*area_box, false, false, 0);
468 }
470 /* Bitmap size frame */
471 {
472 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
473 size_box->set_border_width(3);
475 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
476 lbl->set_use_markup(true);
477 size_box->pack_start(*lbl, false, false, 0);
478 const int rows = 2;
479 const int cols = 5;
480 const bool homogeneous = false;
481 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
482 t->set_row_spacings (4);
483 t->set_col_spacings (4);
484 size_box->pack_start(*t);
486 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
487 NULL, GTK_WIDGET(t->gobj()), 0, 0,
488 _("_Width:"), _("pixels at"), 0, 1,
489 G_CALLBACK
490 (sp_export_bitmap_width_value_changed),
491 dlg );
493 sp_export_spinbutton_new ( "xdpi",
494 prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
495 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
496 NULL, _("dp_i"), 2, 1,
497 G_CALLBACK (sp_export_xdpi_value_changed),
498 dlg );
500 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
501 NULL, GTK_WIDGET(t->gobj()), 0, 1,
502 _("_Height:"), _("pixels at"), 0, 1,
503 G_CALLBACK
504 (sp_export_bitmap_height_value_changed),
505 dlg );
507 /** \todo
508 * Needs fixing: there's no way to set ydpi currently, so we use
509 * the defaultxdpi value here, too...
510 */
511 sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
512 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
513 NULL, _("dpi"), 2, 0, NULL, dlg );
515 vb_singleexport->pack_start(*size_box);
516 }
518 /* File entry */
519 {
520 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
521 file_box->set_border_width(3);
523 // true = has mnemonic
524 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
525 flabel->set_use_markup(true);
526 file_box->pack_start(*flabel, false, false, 0);
528 Gtk::Entry *fe = new Gtk::Entry();
530 /*
531 * set the default filename to be that of the current path + document
532 * with .png extension
533 *
534 * One thing to notice here is that this filename may get
535 * overwritten, but it won't happen here. The filename gets
536 * written into the text field, but then the button to select
537 * the area gets set. In that code the filename can be changed
538 * if there are some with presidence in the document. So, while
539 * this code sets the name first, it may not be the one users
540 * really see.
541 */
542 if ( SP_ACTIVE_DOCUMENT && SP_ACTIVE_DOCUMENT->getURI() )
543 {
544 gchar *name;
545 SPDocument * doc = SP_ACTIVE_DOCUMENT;
546 const gchar *uri = doc->getURI();
547 const gchar *text_extension = get_file_save_extension (Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS).c_str();
548 Inkscape::Extension::Output * oextension = NULL;
550 if (text_extension != NULL) {
551 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
552 }
554 if (oextension != NULL) {
555 gchar * old_extension = oextension->get_extension();
556 if (g_str_has_suffix(uri, old_extension)) {
557 gchar * uri_copy;
558 gchar * extension_point;
559 gchar * final_name;
561 uri_copy = g_strdup(uri);
562 extension_point = g_strrstr(uri_copy, old_extension);
563 extension_point[0] = '\0';
565 final_name = g_strconcat(uri_copy, ".png", NULL);
566 fe->set_text(final_name);
568 g_free(final_name);
569 g_free(uri_copy);
570 }
571 } else {
572 name = g_strconcat(uri, ".png", NULL);
573 fe->set_text(name);
574 g_free(name);
575 }
577 doc_export_name = g_strdup(fe->get_text().c_str());
578 }
579 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
580 G_CALLBACK (sp_export_filename_modified), dlg);
582 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
584 {
585 // true = has mnemonic
586 Gtk::Button *b = new Gtk::Button();
588 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
589 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
590 Gtk::ICON_SIZE_BUTTON);
591 pixlabel->pack_start(*im);
593 Gtk::Label *l = new Gtk::Label();
594 l->set_markup_with_mnemonic(_("_Browse..."));
595 pixlabel->pack_start(*l);
597 b->add(*pixlabel);
599 hb->pack_end (*b, false, false, 4);
600 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
601 G_CALLBACK (sp_export_browse_clicked), NULL );
602 }
604 hb->pack_start (*fe, true, true, 0);
605 file_box->add(*hb);
606 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
607 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
608 original_name = g_strdup(fe->get_text().c_str());
609 // pressing enter in the filename field is the same as clicking export:
610 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
611 G_CALLBACK (sp_export_export_clicked), dlg );
612 // focus is in the filename initially:
613 fe->grab_focus();
615 // mnemonic in frame label moves focus to filename:
616 flabel->set_mnemonic_widget(*fe);
618 vb_singleexport->pack_start(*file_box);
619 }
621 {
622 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
623 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
624 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
625 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
626 batch_box->pack_start(*Glib::wrap(be), false, false);
627 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);
628 batch_box->show_all();
629 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
630 vb->pack_start(*batch_box);
631 }
633 {
634 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
635 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
636 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
637 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
638 hide_box->pack_start(*Glib::wrap(he), false, false);
639 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
640 hide_box->show_all();
641 vb->pack_start(*hide_box);
642 }
644 /* Buttons */
645 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
646 bb->set_border_width(3);
648 {
649 Gtk::Button *b = new Gtk::Button();
650 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
651 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
652 Gtk::ICON_SIZE_BUTTON);
653 image_label->pack_start(*im);
655 Gtk::Label *l = new Gtk::Label();
656 l->set_markup_with_mnemonic(_("_Export"));
657 image_label->pack_start(*l);
659 b->add(*image_label);
660 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
661 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
662 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
663 bb->pack_end(*b, false, false, 0);
664 }
666 vb->pack_end(*bb, false, false, 0);
667 vb->show_all();
669 } // end of if (!dlg)
671 sp_export_find_default_selection(dlg);
673 gtk_window_present ((GtkWindow *) dlg);
675 return;
676 } // end of sp_export_dialog()
678 static void
679 sp_export_update_checkbuttons (GtkObject *base)
680 {
681 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
682 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
683 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
684 if (num >= 2) {
685 gtk_widget_set_sensitive (be, true);
686 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
687 } else {
688 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
689 gtk_widget_set_sensitive (be, FALSE);
690 }
691 if (num > 0) {
692 gtk_widget_set_sensitive (he, true);
693 } else {
694 gtk_widget_set_sensitive (he, false);
695 }
696 }
698 static inline void
699 sp_export_find_default_selection(GtkWidget * dlg)
700 {
701 selection_type key = SELECTION_NUMBER_OF;
703 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
704 key = SELECTION_SELECTION;
705 }
707 /* Try using the preferences */
708 if (key == SELECTION_NUMBER_OF) {
709 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
710 int i = SELECTION_NUMBER_OF;
712 Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
714 if (!what.empty()) {
715 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
716 if (what == selection_names[i]) {
717 break;
718 }
719 }
720 }
722 key = (selection_type)i;
723 }
725 if (key == SELECTION_NUMBER_OF) {
726 key = SELECTION_SELECTION;
727 }
729 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
730 selection_names[key]);
731 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
733 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
734 }
737 /**
738 * \brief If selection changed or a different document activated, we must
739 * recalculate any chosen areas
740 *
741 */
742 static void
743 sp_export_selection_changed ( Inkscape::Application *inkscape,
744 Inkscape::Selection *selection,
745 GtkObject *base )
746 {
747 selection_type current_key;
748 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
750 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
751 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
752 was_empty) {
753 gtk_toggle_button_set_active
754 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
755 TRUE );
756 }
757 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
759 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
761 if (inkscape &&
762 SP_IS_INKSCAPE (inkscape) &&
763 selection &&
764 SELECTION_CUSTOM != current_key) {
765 GtkToggleButton * button;
766 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
767 sp_export_area_toggled(button, base);
768 }
770 sp_export_update_checkbuttons (base);
771 }
773 static void
774 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
775 Inkscape::Selection */*selection*/,
776 guint /*flags*/,
777 GtkObject *base )
778 {
779 selection_type current_key;
780 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
782 switch (current_key) {
783 case SELECTION_DRAWING:
784 if ( SP_ACTIVE_DESKTOP ) {
785 SPDocument *doc;
786 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
787 Geom::OptRect bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
788 if (bbox) {
789 sp_export_set_area (base, bbox->min()[Geom::X],
790 bbox->min()[Geom::Y],
791 bbox->max()[Geom::X],
792 bbox->max()[Geom::Y]);
793 }
794 }
795 break;
796 case SELECTION_SELECTION:
797 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
798 NRRect bbox;
799 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox, SPItem::RENDERING_BBOX);
800 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
801 }
802 break;
803 default:
804 /* Do nothing for page or for custom */
805 break;
806 }
808 return;
809 }
811 /// Called when one of the selection buttons was toggled.
812 static void
813 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
814 {
815 if (gtk_object_get_data (base, "update"))
816 return;
818 selection_type key, old_key;
819 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
820 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
822 /* Ignore all "turned off" events unless we're the only active button */
823 if (!gtk_toggle_button_get_active (tb) ) {
825 /* Don't let the current selection be deactived - but rerun the
826 activate to allow the user to renew the values */
827 if (key == old_key) {
828 gtk_toggle_button_set_active ( tb, TRUE );
829 }
831 return;
832 }
834 /* Turn off the currently active button unless it's us */
835 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
837 if (old_key != key) {
838 gtk_toggle_button_set_active
839 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
840 FALSE );
841 }
843 if ( SP_ACTIVE_DESKTOP )
844 {
845 SPDocument *doc;
846 Geom::OptRect bbox;
847 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
849 /* Notice how the switch is used to 'fall through' here to get
850 various backups. If you modify this without noticing you'll
851 probabaly screw something up. */
852 switch (key) {
853 case SELECTION_SELECTION:
854 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
855 {
856 bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds(SPItem::RENDERING_BBOX);
857 /* Only if there is a selection that we can set
858 do we break, otherwise we fall through to the
859 drawing */
860 // std::cout << "Using selection: SELECTION" << std::endl;
861 key = SELECTION_SELECTION;
862 break;
863 }
864 case SELECTION_DRAWING:
865 /** \todo
866 * This returns wrong values if the document has a viewBox.
867 */
868 bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
869 /* If the drawing is valid, then we'll use it and break
870 otherwise we drop through to the page settings */
871 if (bbox) {
872 // std::cout << "Using selection: DRAWING" << std::endl;
873 key = SELECTION_DRAWING;
874 break;
875 }
876 case SELECTION_PAGE:
877 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
878 Geom::Point(doc->getWidth(), doc->getHeight()));
880 // std::cout << "Using selection: PAGE" << std::endl;
881 key = SELECTION_PAGE;
882 break;
883 case SELECTION_CUSTOM:
884 default:
885 break;
886 } // switch
888 // remember area setting
889 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
890 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
892 if ( key != SELECTION_CUSTOM && bbox ) {
893 sp_export_set_area (base, bbox->min()[Geom::X],
894 bbox->min()[Geom::Y],
895 bbox->max()[Geom::X],
896 bbox->max()[Geom::Y]);
897 }
899 } // end of if ( SP_ACTIVE_DESKTOP )
902 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
903 GtkWidget * file_entry;
904 const gchar * filename = NULL;
905 float xdpi = 0.0, ydpi = 0.0;
907 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
909 switch (key) {
910 case SELECTION_PAGE:
911 case SELECTION_DRAWING: {
912 SPDocument * doc = SP_ACTIVE_DOCUMENT;
913 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
915 if (filename == NULL) {
916 if (doc_export_name != NULL) {
917 filename = g_strdup(doc_export_name);
918 } else {
919 filename = g_strdup("");
920 }
921 }
922 break;
923 }
924 case SELECTION_SELECTION:
925 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
927 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
929 /* If we still don't have a filename -- let's build
930 one that's nice */
931 if (filename == NULL) {
932 const gchar * id = NULL;
933 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
934 for(; reprlst != NULL; reprlst = reprlst->next) {
935 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
936 if (repr->attribute("id")) {
937 id = repr->attribute("id");
938 break;
939 }
940 }
942 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
943 }
944 }
945 break;
946 case SELECTION_CUSTOM:
947 default:
948 break;
949 }
951 if (filename != NULL) {
952 g_free(original_name);
953 original_name = g_strdup(filename);
954 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
955 }
957 if (xdpi != 0.0) {
958 sp_export_value_set(base, "xdpi", xdpi);
959 }
961 /* These can't be separate, and setting x sets y, so for
962 now setting this is disabled. Hopefully it won't be in
963 the future */
964 if (FALSE && ydpi != 0.0) {
965 sp_export_value_set(base, "ydpi", ydpi);
966 }
967 }
969 return;
970 } // end of sp_export_area_toggled()
972 /// Called when dialog is deleted
973 static gint
974 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
975 {
976 g_object_set_data (base, "cancel", (gpointer) 1);
977 return TRUE;
978 } // end of sp_export_progress_delete()
980 /// Called when progress is cancelled
981 static void
982 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
983 {
984 g_object_set_data (base, "cancel", (gpointer) 1);
985 } // end of sp_export_progress_cancel()
987 /// Called for every progress iteration
988 static unsigned int
989 sp_export_progress_callback (float value, void *data)
990 {
991 GtkWidget *prg;
992 int evtcount;
994 if (g_object_get_data ((GObject *) data, "cancel"))
995 return FALSE;
997 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
998 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
1000 evtcount = 0;
1001 while ((evtcount < 16) && gdk_events_pending ()) {
1002 gtk_main_iteration_do (FALSE);
1003 evtcount += 1;
1004 }
1006 gtk_main_iteration_do (FALSE);
1008 return TRUE;
1010 } // end of sp_export_progress_callback()
1012 GtkWidget *
1013 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1014 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1016 dlg = gtk_dialog_new ();
1017 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1018 prg = gtk_progress_bar_new ();
1019 sp_transientize (dlg);
1020 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1021 g_object_set_data ((GObject *) base, "progress", prg);
1023 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1025 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1026 GTK_PROGRESS_LEFT_TO_RIGHT);
1027 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1028 prg, FALSE, FALSE, 4 );
1029 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1030 GTK_STOCK_CANCEL,
1031 GTK_RESPONSE_CANCEL );
1033 g_signal_connect ( (GObject *) dlg, "delete_event",
1034 (GCallback) sp_export_progress_delete, base);
1035 g_signal_connect ( (GObject *) btn, "clicked",
1036 (GCallback) sp_export_progress_cancel, base);
1037 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1038 gtk_widget_show_all (dlg);
1040 return dlg;
1041 }
1043 // FIXME: Some lib function should be available to do this ...
1044 static gchar *
1045 filename_add_extension (const gchar *filename, const gchar *extension)
1046 {
1047 const gchar *dot;
1049 dot = strrchr (filename, '.');
1050 if ( !dot )
1051 return g_strconcat (filename, ".", extension, NULL);
1052 {
1053 if (dot[1] == '\0')
1054 return g_strconcat (filename, extension, NULL);
1055 else
1056 {
1057 if (g_strcasecmp (dot + 1, extension) == 0)
1058 return g_strdup (filename);
1059 else
1060 {
1061 return g_strconcat (filename, ".", extension, NULL);
1062 }
1063 }
1064 }
1065 }
1067 gchar *absolutize_path_from_document_location (SPDocument *doc, const gchar *filename)
1068 {
1069 gchar *path = 0;
1070 //Make relative paths go from the document location, if possible:
1071 if (!g_path_is_absolute(filename) && doc->getURI()) {
1072 gchar *dirname = g_path_get_dirname(doc->getURI());
1073 if (dirname) {
1074 path = g_build_filename(dirname, filename, NULL);
1075 g_free(dirname);
1076 }
1077 }
1078 if (!path) {
1079 path = g_strdup(filename);
1080 }
1081 return path;
1082 }
1084 /// Called when export button is clicked
1085 static void
1086 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1087 {
1088 if (!SP_ACTIVE_DESKTOP) return;
1090 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1091 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1093 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1094 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1095 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1096 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1097 // Batch export of selected objects
1099 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1100 gint n = 0;
1102 if (num < 1)
1103 return;
1105 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1106 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1107 g_free (progress_text);
1109 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1110 i != NULL;
1111 i = i->next) {
1112 SPItem *item = reinterpret_cast<SPItem *>(i->data);
1114 // retrieve export filename hint
1115 const gchar *filename = item->getRepr()->attribute("inkscape:export-filename");
1116 gchar *path = 0;
1117 if (!filename) {
1118 path = create_filepath_from_id(item->getId(), NULL);
1119 } else {
1120 path = absolutize_path_from_document_location(doc, filename);
1121 }
1123 // retrieve export dpi hints
1124 const gchar *dpi_hint = item->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1125 gdouble dpi = 0.0;
1126 if (dpi_hint) {
1127 dpi = atof(dpi_hint);
1128 }
1129 if (dpi == 0.0) {
1130 dpi = DPI_BASE;
1131 }
1133 Geom::OptRect area;
1134 item->invoke_bbox( area, item->i2d_affine(), TRUE );
1135 if (area) {
1136 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1137 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1139 if (width > 1 && height > 1) {
1140 /* Do export */
1141 if (!sp_export_png_file (doc, path,
1142 *area, width, height, dpi, dpi,
1143 nv->pagecolor,
1144 NULL, NULL, TRUE, // overwrite without asking
1145 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1146 )) {
1147 gchar * error;
1148 gchar * safeFile = Inkscape::IO::sanitizeString(path);
1149 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1150 sp_ui_error_dialog(error);
1151 g_free(safeFile);
1152 g_free(error);
1153 }
1154 }
1155 }
1156 n++;
1157 g_free(path);
1158 sp_export_progress_callback((float)n/num, base);
1159 }
1161 gtk_widget_destroy (prog_dlg);
1162 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1164 } else {
1166 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1167 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1169 float const x0 = sp_export_value_get_px(base, "x0");
1170 float const y0 = sp_export_value_get_px(base, "y0");
1171 float const x1 = sp_export_value_get_px(base, "x1");
1172 float const y1 = sp_export_value_get_px(base, "y1");
1173 float const xdpi = sp_export_value_get(base, "xdpi");
1174 float const ydpi = sp_export_value_get(base, "ydpi");
1175 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1176 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1178 if (filename == NULL || *filename == '\0') {
1179 sp_ui_error_dialog(_("You have to enter a filename"));
1180 return;
1181 }
1183 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1184 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1185 return;
1186 }
1188 // make sure that .png is the extension of the file:
1189 gchar * filename_ext = filename_add_extension(filename, "png");
1190 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1192 gchar *path = absolutize_path_from_document_location(doc, filename_ext);
1194 gchar *dirname = g_path_get_dirname(path);
1195 if ( dirname == NULL
1196 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1197 {
1198 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1199 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1200 safeDir);
1201 sp_ui_error_dialog(error);
1202 g_free(safeDir);
1203 g_free(error);
1204 g_free(dirname);
1205 g_free(path);
1206 return;
1207 }
1208 g_free(dirname);
1210 gchar *fn = g_path_get_basename (path);
1211 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1212 g_free (fn);
1214 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1215 g_free (progress_text);
1217 /* Do export */
1218 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), path,
1219 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1220 nv->pagecolor,
1221 sp_export_progress_callback, base, FALSE,
1222 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1223 )) {
1224 gchar * error;
1225 gchar * safeFile = Inkscape::IO::sanitizeString(path);
1226 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1227 sp_ui_error_dialog(error);
1228 g_free(safeFile);
1229 g_free(error);
1230 }
1232 /* Reset the filename so that it can be changed again by changing
1233 selections and all that */
1234 g_free(original_name);
1235 original_name = g_strdup(filename_ext);
1236 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1238 gtk_widget_destroy (prog_dlg);
1239 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1241 /* Setup the values in the document */
1242 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1243 case SELECTION_PAGE:
1244 case SELECTION_DRAWING: {
1245 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1246 Inkscape::XML::Node * repr = doc->getReprRoot();
1247 bool modified = false;
1249 bool saved = DocumentUndo::getUndoSensitive(doc);
1250 DocumentUndo::setUndoSensitive(doc, false);
1252 gchar const *temp_string = repr->attribute("inkscape:export-filename");
1253 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1254 repr->setAttribute("inkscape:export-filename", filename_ext);
1255 modified = true;
1256 }
1257 temp_string = repr->attribute("inkscape:export-xdpi");
1258 if (temp_string == NULL || xdpi != atof(temp_string)) {
1259 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1260 modified = true;
1261 }
1262 temp_string = repr->attribute("inkscape:export-ydpi");
1263 if (temp_string == NULL || xdpi != atof(temp_string)) {
1264 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1265 modified = true;
1266 }
1267 DocumentUndo::setUndoSensitive(doc, saved);
1269 if (modified) {
1270 doc->setModifiedSinceSave();
1271 }
1272 break;
1273 }
1274 case SELECTION_SELECTION: {
1275 const GSList * reprlst;
1276 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1277 bool modified = false;
1279 bool saved = DocumentUndo::getUndoSensitive(doc);
1280 DocumentUndo::setUndoSensitive(doc, false);
1281 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1283 for(; reprlst != NULL; reprlst = reprlst->next) {
1284 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1285 const gchar * temp_string;
1287 if (repr->attribute("id") == NULL ||
1288 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1289 ( !SP_ACTIVE_DOCUMENT->getURI() ||
1290 strcmp(g_dirname(filename), g_dirname(SP_ACTIVE_DOCUMENT->getURI())) == 0))) {
1291 temp_string = repr->attribute("inkscape:export-filename");
1292 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1293 repr->setAttribute("inkscape:export-filename", filename_ext);
1294 modified = true;
1295 }
1296 }
1297 temp_string = repr->attribute("inkscape:export-xdpi");
1298 if (temp_string == NULL || xdpi != atof(temp_string)) {
1299 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1300 modified = true;
1301 }
1302 temp_string = repr->attribute("inkscape:export-ydpi");
1303 if (temp_string == NULL || xdpi != atof(temp_string)) {
1304 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1305 modified = true;
1306 }
1307 }
1308 DocumentUndo::setUndoSensitive(doc, saved);
1310 if (modified) {
1311 doc->setModifiedSinceSave();
1312 }
1313 break;
1314 }
1315 default:
1316 break;
1317 }
1319 g_free (filename_ext);
1320 g_free (path);
1322 }
1324 } // end of sp_export_export_clicked()
1326 /// Called when Browse button is clicked
1327 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1328 static void
1329 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1330 {
1331 GtkWidget *fs, *fe;
1332 const gchar *filename;
1334 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1335 (GtkWindow*)dlg,
1336 GTK_FILE_CHOOSER_ACTION_SAVE,
1337 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1338 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1339 NULL );
1341 #ifdef WITH_GNOME_VFS
1342 if (gnome_vfs_initialized()) {
1343 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1344 }
1345 #endif
1347 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1349 sp_transientize (fs);
1351 gtk_window_set_modal(GTK_WINDOW (fs), true);
1353 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1355 if (*filename == '\0') {
1356 filename = create_filepath_from_id(NULL, NULL);
1357 }
1359 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1361 #ifdef WIN32
1362 // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1363 OPENFILENAMEW opf;
1364 WCHAR filter_string[20];
1365 wcsncpy(filter_string, L"PNG#*.png##", 11);
1366 filter_string[3] = L'\0';
1367 filter_string[9] = L'\0';
1368 filter_string[10] = L'\0';
1369 WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1370 WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1371 // Copy the selected file name, converting from UTF-8 to UTF-16
1372 WCHAR _filename[_MAX_PATH + 1];
1373 memset(_filename, 0, sizeof(_filename));
1374 gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1375 wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1376 g_free(utf16_path_string);
1378 opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1379 opf.lpstrFilter = filter_string;
1380 opf.lpstrCustomFilter = 0;
1381 opf.nMaxCustFilter = 0L;
1382 opf.nFilterIndex = 1L;
1383 opf.lpstrFile = _filename;
1384 opf.nMaxFile = _MAX_PATH;
1385 opf.lpstrFileTitle = NULL;
1386 opf.nMaxFileTitle=0;
1387 opf.lpstrInitialDir = 0;
1388 opf.lpstrTitle = title_string;
1389 opf.nFileOffset = 0;
1390 opf.nFileExtension = 2;
1391 opf.lpstrDefExt = extension_string;
1392 opf.lpfnHook = NULL;
1393 opf.lCustData = 0;
1394 opf.Flags = OFN_PATHMUSTEXIST;
1395 opf.lStructSize = sizeof(OPENFILENAMEW);
1396 if (GetSaveFileNameW(&opf) != 0)
1397 {
1398 // Copy the selected file name, converting from UTF-16 to UTF-8
1399 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1400 gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1401 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1402 g_free(utf8string);
1404 }
1405 g_free(extension_string);
1406 g_free(title_string);
1408 #else
1409 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1410 {
1411 gchar *file;
1413 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1415 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1416 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1418 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1420 g_free(utf8file);
1421 g_free(file);
1422 }
1423 #endif
1425 gtk_widget_destroy (fs);
1427 return;
1428 } // end of sp_export_browse_clicked()
1430 // TODO: Move this to nr-rect-fns.h.
1431 static bool
1432 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1433 {
1434 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1435 return (
1436 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1437 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1438 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1439 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1440 );
1441 }
1443 /**
1444 \brief This function is used to detect the current selection setting
1445 based on the values in the x0, y0, x1 and y0 fields.
1446 \param base The export dialog itself
1448 One of the most confusing parts of this function is why the array
1449 is built at the beginning. What needs to happen here is that we
1450 should always check the current selection to see if it is the valid
1451 one. While this is a performance improvement it is also a usability
1452 one during the cases where things like selections and drawings match
1453 size. This way buttons change less 'randomly' (atleast in the eyes
1454 of the user). To do this an array is built where the current selection
1455 type is placed first, and then the others in an order from smallest
1456 to largest (this can be configured by reshuffling \c test_order).
1458 All of the values in this function are rounded to two decimal places
1459 because that is what is shown to the user. While everything is kept
1460 more accurate than that, the user can't control more acurrate than
1461 that, so for this to work for them - it needs to check on that level
1462 of accuracy.
1464 \todo finish writing this up
1465 */
1466 static void
1467 sp_export_detect_size(GtkObject * base) {
1468 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1469 selection_type this_test[SELECTION_NUMBER_OF + 1];
1470 selection_type key = SELECTION_NUMBER_OF;
1472 Geom::Point x(sp_export_value_get_px (base, "x0"),
1473 sp_export_value_get_px (base, "y0"));
1474 Geom::Point y(sp_export_value_get_px (base, "x1"),
1475 sp_export_value_get_px (base, "y1"));
1476 Geom::Rect current_bbox(x, y);
1477 //std::cout << "Current " << current_bbox;
1479 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1480 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1481 this_test[i + 1] = test_order[i];
1482 }
1484 for (int i = 0;
1485 i < SELECTION_NUMBER_OF + 1 &&
1486 key == SELECTION_NUMBER_OF &&
1487 SP_ACTIVE_DESKTOP != NULL;
1488 i++) {
1489 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1490 switch (this_test[i]) {
1491 case SELECTION_SELECTION:
1492 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1493 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(SPItem::RENDERING_BBOX);
1495 //std::cout << "Selection " << bbox;
1496 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1497 key = SELECTION_SELECTION;
1498 }
1499 }
1500 break;
1501 case SELECTION_DRAWING: {
1502 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1504 Geom::OptRect bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
1506 // std::cout << "Drawing " << bbox2;
1507 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1508 key = SELECTION_DRAWING;
1509 }
1510 break;
1511 }
1513 case SELECTION_PAGE: {
1514 SPDocument *doc;
1516 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1518 Geom::Point x(0.0, 0.0);
1519 Geom::Point y(doc->getWidth(),
1520 doc->getHeight());
1521 Geom::Rect bbox(x, y);
1523 // std::cout << "Page " << bbox;
1524 if (sp_export_bbox_equal(bbox,current_bbox)) {
1525 key = SELECTION_PAGE;
1526 }
1528 break;
1529 }
1530 default:
1531 break;
1532 }
1533 }
1534 // std::cout << std::endl;
1536 if (key == SELECTION_NUMBER_OF) {
1537 key = SELECTION_CUSTOM;
1538 }
1540 /* We're now using a custom size, not a fixed one */
1541 /* printf("Detecting state: %s\n", selection_names[key]); */
1542 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1543 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1544 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1545 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1547 return;
1548 } /* sp_export_detect_size */
1550 /// Called when area x0 value is changed
1551 static void
1552 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1553 {
1554 float x0, x1, xdpi, width;
1556 if (gtk_object_get_data (base, "update"))
1557 return;
1559 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1560 (base, "units")))
1561 {
1562 return;
1563 }
1565 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1567 x0 = sp_export_value_get_px (base, "x0");
1568 x1 = sp_export_value_get_px (base, "x1");
1569 xdpi = sp_export_value_get (base, "xdpi");
1571 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1573 if (width < SP_EXPORT_MIN_SIZE) {
1574 const gchar *key;
1575 width = SP_EXPORT_MIN_SIZE;
1576 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1578 if (!strcmp (key, "x0")) {
1579 x1 = x0 + width * DPI_BASE / xdpi;
1580 sp_export_value_set_px (base, "x1", x1);
1581 } else {
1582 x0 = x1 - width * DPI_BASE / xdpi;
1583 sp_export_value_set_px (base, "x0", x0);
1584 }
1585 }
1587 sp_export_value_set_px (base, "width", x1 - x0);
1588 sp_export_value_set (base, "bmwidth", width);
1590 sp_export_detect_size(base);
1592 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594 return;
1595 } // end of sp_export_area_x_value_changed()
1597 /// Called when area y0 value is changed.
1598 static void
1599 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1600 {
1601 float y0, y1, ydpi, height;
1603 if (gtk_object_get_data (base, "update"))
1604 return;
1606 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1607 (base, "units")))
1608 {
1609 return;
1610 }
1612 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1614 y0 = sp_export_value_get_px (base, "y0");
1615 y1 = sp_export_value_get_px (base, "y1");
1616 ydpi = sp_export_value_get (base, "ydpi");
1618 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1620 if (height < SP_EXPORT_MIN_SIZE) {
1621 const gchar *key;
1622 height = SP_EXPORT_MIN_SIZE;
1623 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1624 if (!strcmp (key, "y0")) {
1625 y1 = y0 + height * DPI_BASE / ydpi;
1626 sp_export_value_set_px (base, "y1", y1);
1627 } else {
1628 y0 = y1 - height * DPI_BASE / ydpi;
1629 sp_export_value_set_px (base, "y0", y0);
1630 }
1631 }
1633 sp_export_value_set_px (base, "height", y1 - y0);
1634 sp_export_value_set (base, "bmheight", height);
1636 sp_export_detect_size(base);
1638 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1640 return;
1641 } // end of sp_export_area_y_value_changed()
1643 /// Called when x1-x0 or area width is changed
1644 static void
1645 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1646 {
1647 float x0, x1, xdpi, width, bmwidth;
1649 if (gtk_object_get_data (base, "update"))
1650 return;
1652 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1653 (base, "units"))) {
1654 return;
1655 }
1657 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1659 x0 = sp_export_value_get_px (base, "x0");
1660 x1 = sp_export_value_get_px (base, "x1");
1661 xdpi = sp_export_value_get (base, "xdpi");
1662 width = sp_export_value_get_px (base, "width");
1663 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1665 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1667 bmwidth = SP_EXPORT_MIN_SIZE;
1668 width = bmwidth * DPI_BASE / xdpi;
1669 sp_export_value_set_px (base, "width", width);
1670 }
1672 sp_export_value_set_px (base, "x1", x0 + width);
1673 sp_export_value_set (base, "bmwidth", bmwidth);
1675 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1677 return;
1678 } // end of sp_export_area_width_value_changed()
1680 /// Called when y1-y0 or area height is changed.
1681 static void
1682 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1683 {
1685 float y0, y1, ydpi, height, bmheight;
1687 if (gtk_object_get_data (base, "update"))
1688 return;
1690 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1691 (base, "units"))) {
1692 return;
1693 }
1695 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1697 y0 = sp_export_value_get_px (base, "y0");
1698 y1 = sp_export_value_get_px (base, "y1");
1699 ydpi = sp_export_value_get (base, "ydpi");
1700 height = sp_export_value_get_px (base, "height");
1701 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1703 if (bmheight < SP_EXPORT_MIN_SIZE) {
1704 bmheight = SP_EXPORT_MIN_SIZE;
1705 height = bmheight * DPI_BASE / ydpi;
1706 sp_export_value_set_px (base, "height", height);
1707 }
1709 sp_export_value_set_px (base, "y1", y0 + height);
1710 sp_export_value_set (base, "bmheight", bmheight);
1712 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1714 return;
1715 } // end of sp_export_area_height_value_changed()
1717 /**
1718 \brief A function to set the ydpi
1719 \param base The export dialog
1721 This function grabs all of the y values and then figures out the
1722 new bitmap size based on the changing dpi value. The dpi value is
1723 gotten from the xdpi setting as these can not currently be independent.
1724 */
1725 static void
1726 sp_export_set_image_y (GtkObject *base)
1727 {
1728 float y0, y1, xdpi;
1730 y0 = sp_export_value_get_px (base, "y0");
1731 y1 = sp_export_value_get_px (base, "y1");
1732 xdpi = sp_export_value_get (base, "xdpi");
1734 sp_export_value_set (base, "ydpi", xdpi);
1735 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1737 return;
1738 } // end of sp_export_set_image_y()
1740 /**
1741 \brief A function to set the xdpi
1742 \param base The export dialog
1744 This function grabs all of the x values and then figures out the
1745 new bitmap size based on the changing dpi value. The dpi value is
1746 gotten from the xdpi setting as these can not currently be independent.
1747 */
1748 static void
1749 sp_export_set_image_x (GtkObject *base)
1750 {
1751 float x0, x1, xdpi;
1753 x0 = sp_export_value_get_px (base, "x0");
1754 x1 = sp_export_value_get_px (base, "x1");
1755 xdpi = sp_export_value_get (base, "xdpi");
1757 sp_export_value_set (base, "ydpi", xdpi);
1758 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1760 return;
1761 } // end of sp_export_set_image_x()
1763 /// Called when pixel width is changed
1764 static void
1765 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1766 {
1767 float x0, x1, bmwidth, xdpi;
1769 if (gtk_object_get_data (base, "update"))
1770 return;
1772 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1773 (base, "units"))) {
1774 return;
1775 }
1777 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1779 x0 = sp_export_value_get_px (base, "x0");
1780 x1 = sp_export_value_get_px (base, "x1");
1781 bmwidth = sp_export_value_get (base, "bmwidth");
1783 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1784 bmwidth = SP_EXPORT_MIN_SIZE;
1785 sp_export_value_set (base, "bmwidth", bmwidth);
1786 }
1788 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1789 sp_export_value_set (base, "xdpi", xdpi);
1791 sp_export_set_image_y (base);
1793 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1795 return;
1796 } // end of sp_export_bitmap_width_value_changed()
1798 /// Called when pixel height is changed
1799 static void
1800 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1801 {
1802 float y0, y1, bmheight, xdpi;
1804 if (gtk_object_get_data (base, "update"))
1805 return;
1807 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1808 (base, "units"))) {
1809 return;
1810 }
1812 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1814 y0 = sp_export_value_get_px (base, "y0");
1815 y1 = sp_export_value_get_px (base, "y1");
1816 bmheight = sp_export_value_get (base, "bmheight");
1818 if (bmheight < SP_EXPORT_MIN_SIZE) {
1819 bmheight = SP_EXPORT_MIN_SIZE;
1820 sp_export_value_set (base, "bmheight", bmheight);
1821 }
1823 xdpi = bmheight * DPI_BASE / (y1 - y0);
1824 sp_export_value_set (base, "xdpi", xdpi);
1826 sp_export_set_image_x (base);
1828 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1830 return;
1831 } // end of sp_export_bitmap_width_value_changed()
1833 /**
1834 \brief A function to adjust the bitmap width when the xdpi value changes
1835 \param adj The adjustment that was changed
1836 \param base The export dialog itself
1838 The first thing this function checks is to see if we are doing an
1839 update. If we are, this function just returns because there is another
1840 instance of it that will handle everything for us. If there is a
1841 units change, we also assume that everyone is being updated appropriately
1842 and there is nothing for us to do.
1844 If we're the highest level function, we set the update flag, and
1845 continue on our way.
1847 All of the values are grabbed using the \c sp_export_value_get functions
1848 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1849 xdpi value is saved in the preferences for the next time the dialog
1850 is opened. (does the selection dpi need to be set here?)
1852 A check is done to to ensure that we aren't outputing an invalid width,
1853 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1854 changed to make it valid.
1856 After all of this the bitmap width is changed.
1858 We also change the ydpi. This is a temporary hack as these can not
1859 currently be independent. This is likely to change in the future.
1860 */
1861 void
1862 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1863 {
1864 float x0, x1, xdpi, bmwidth;
1866 if (gtk_object_get_data (base, "update"))
1867 return;
1869 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1870 (base, "units"))) {
1871 return;
1872 }
1874 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1876 x0 = sp_export_value_get_px (base, "x0");
1877 x1 = sp_export_value_get_px (base, "x1");
1878 xdpi = sp_export_value_get (base, "xdpi");
1880 // remember xdpi setting
1881 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1882 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1884 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1886 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1887 bmwidth = SP_EXPORT_MIN_SIZE;
1888 if (x1 != x0)
1889 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1890 else
1891 xdpi = DPI_BASE;
1892 sp_export_value_set (base, "xdpi", xdpi);
1893 }
1895 sp_export_value_set (base, "bmwidth", bmwidth);
1897 sp_export_set_image_y (base);
1899 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1901 return;
1902 } // end of sp_export_xdpi_value_changed()
1905 /**
1906 \brief A function to change the area that is used for the exported
1907 bitmap.
1908 \param base This is the export dialog
1909 \param x0 Horizontal upper left hand corner of the picture in points
1910 \param y0 Vertical upper left hand corner of the picture in points
1911 \param x1 Horizontal lower right hand corner of the picture in points
1912 \param y1 Vertical lower right hand corner of the picture in points
1914 This function just calls \c sp_export_value_set_px for each of the
1915 parameters that is passed in. This allows for setting them all in
1916 one convient area.
1918 Update is set to suspend all of the other test running while all the
1919 values are being set up. This allows for a performance increase, but
1920 it also means that the wrong type won't be detected with only some of
1921 the values set. After all the values are set everyone is told that
1922 there has been an update.
1923 */
1924 static void
1925 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1926 {
1927 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1928 sp_export_value_set_px (base, "x1", x1);
1929 sp_export_value_set_px (base, "y1", y1);
1930 sp_export_value_set_px (base, "x0", x0);
1931 sp_export_value_set_px (base, "y0", y0);
1932 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1934 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1935 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1937 return;
1938 }
1940 /**
1941 \brief Sets the value of an adjustment
1942 \param base The export dialog
1943 \param key Which adjustment to set
1944 \param val What value to set it to
1946 This function finds the adjustment using the data stored in the
1947 export dialog. After finding the adjustment it then sets
1948 the value of it.
1949 */
1950 static void
1951 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1952 {
1953 GtkAdjustment *adj;
1955 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1957 gtk_adjustment_set_value (adj, val);
1958 }
1960 /**
1961 \brief A function to set a value using the units points
1962 \param base The export dialog
1963 \param key Which value should be set
1964 \param val What the value should be in points
1966 This function first gets the adjustment for the key that is passed
1967 in. It then figures out what units are currently being used in the
1968 dialog. After doing all of that, it then converts the incoming
1969 value and sets the adjustment.
1970 */
1971 static void
1972 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1973 {
1974 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1976 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1978 return;
1979 }
1981 /**
1982 \brief Get the value of an adjustment in the export dialog
1983 \param base The export dialog
1984 \param key Which adjustment is being looked for
1985 \return The value in the specified adjustment
1987 This function gets the adjustment from the data field in the export
1988 dialog. It then grabs the value from the adjustment.
1989 */
1990 static float
1991 sp_export_value_get ( GtkObject *base, const gchar *key )
1992 {
1993 GtkAdjustment *adj;
1995 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1997 return adj->value;
1998 }
2000 /**
2001 \brief Grabs a value in the export dialog and converts the unit
2002 to points
2003 \param base The export dialog
2004 \param key Which value should be returned
2005 \return The value in the adjustment in points
2007 This function, at its most basic, is a call to \c sp_export_value_get
2008 to get the value of the adjustment. It then finds the units that
2009 are being used by looking at the "units" attribute of the export
2010 dialog. Using that it converts the returned value into points.
2011 */
2012 static float
2013 sp_export_value_get_px ( GtkObject *base, const gchar *key )
2014 {
2015 float value = sp_export_value_get(base, key);
2016 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
2018 return sp_units_get_pixels (value, *unit);
2019 } // end of sp_export_value_get_px()
2021 /**
2022 \brief This function is called when the filename is changed by
2023 anyone. It resets the virgin bit.
2024 \param object Text entry box
2025 \param data The export dialog
2026 \return None
2028 This function gets called when the text area is modified. It is
2029 looking for the case where the text area is modified from its
2030 original value. In that case it sets the "filename-modified" bit
2031 to TRUE. If the text dialog returns back to the original text, the
2032 bit gets reset. This should stop simple mistakes.
2033 */
2034 static void
2035 sp_export_filename_modified (GtkObject * object, gpointer data)
2036 {
2037 GtkWidget * text_entry = (GtkWidget *)object;
2038 GtkWidget * export_dialog = (GtkWidget *)data;
2040 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2041 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2042 // printf("Modified: FALSE\n");
2043 } else {
2044 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2045 // printf("Modified: TRUE\n");
2046 }
2048 return;
2049 } // end sp_export_filename_modified
2051 /*
2052 Local Variables:
2053 mode:c++
2054 c-file-style:"stroustrup"
2055 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2056 indent-tabs-mode:nil
2057 fill-column:99
2058 End:
2059 */
2060 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :