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 const gchar *text_extension = get_file_save_extension (Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS).c_str();
544 Inkscape::Extension::Output * oextension = NULL;
546 if (text_extension != NULL) {
547 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
548 }
550 if (oextension != NULL) {
551 gchar * old_extension = oextension->get_extension();
552 if (g_str_has_suffix(uri, old_extension)) {
553 gchar * uri_copy;
554 gchar * extension_point;
555 gchar * final_name;
557 uri_copy = g_strdup(uri);
558 extension_point = g_strrstr(uri_copy, old_extension);
559 extension_point[0] = '\0';
561 final_name = g_strconcat(uri_copy, ".png", NULL);
562 fe->set_text(final_name);
564 g_free(final_name);
565 g_free(uri_copy);
566 }
567 } else {
568 name = g_strconcat(uri, ".png", NULL);
569 fe->set_text(name);
570 g_free(name);
571 }
573 doc_export_name = g_strdup(fe->get_text().c_str());
574 }
575 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
576 G_CALLBACK (sp_export_filename_modified), dlg);
578 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
580 {
581 // true = has mnemonic
582 Gtk::Button *b = new Gtk::Button();
584 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
585 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
586 Gtk::ICON_SIZE_BUTTON);
587 pixlabel->pack_start(*im);
589 Gtk::Label *l = new Gtk::Label();
590 l->set_markup_with_mnemonic(_("_Browse..."));
591 pixlabel->pack_start(*l);
593 b->add(*pixlabel);
595 hb->pack_end (*b, false, false, 4);
596 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
597 G_CALLBACK (sp_export_browse_clicked), NULL );
598 }
600 hb->pack_start (*fe, true, true, 0);
601 file_box->add(*hb);
602 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
603 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
604 original_name = g_strdup(fe->get_text().c_str());
605 // pressing enter in the filename field is the same as clicking export:
606 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
607 G_CALLBACK (sp_export_export_clicked), dlg );
608 // focus is in the filename initially:
609 fe->grab_focus();
611 // mnemonic in frame label moves focus to filename:
612 flabel->set_mnemonic_widget(*fe);
614 vb_singleexport->pack_start(*file_box);
615 }
617 {
618 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
619 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
620 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
621 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
622 batch_box->pack_start(*Glib::wrap(be), false, false);
623 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);
624 batch_box->show_all();
625 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
626 vb->pack_start(*batch_box);
627 }
629 {
630 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
631 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
632 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
633 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
634 hide_box->pack_start(*Glib::wrap(he), false, false);
635 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
636 hide_box->show_all();
637 vb->pack_start(*hide_box);
638 }
640 /* Buttons */
641 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
642 bb->set_border_width(3);
644 {
645 Gtk::Button *b = new Gtk::Button();
646 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
647 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
648 Gtk::ICON_SIZE_BUTTON);
649 image_label->pack_start(*im);
651 Gtk::Label *l = new Gtk::Label();
652 l->set_markup_with_mnemonic(_("_Export"));
653 image_label->pack_start(*l);
655 b->add(*image_label);
656 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
657 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
658 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
659 bb->pack_end(*b, false, false, 0);
660 }
662 vb->pack_end(*bb, false, false, 0);
663 vb->show_all();
665 } // end of if (!dlg)
667 sp_export_find_default_selection(dlg);
669 gtk_window_present ((GtkWindow *) dlg);
671 return;
672 } // end of sp_export_dialog()
674 static void
675 sp_export_update_checkbuttons (GtkObject *base)
676 {
677 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
678 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
679 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
680 if (num >= 2) {
681 gtk_widget_set_sensitive (be, true);
682 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
683 } else {
684 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
685 gtk_widget_set_sensitive (be, FALSE);
686 }
687 if (num > 0) {
688 gtk_widget_set_sensitive (he, true);
689 } else {
690 gtk_widget_set_sensitive (he, false);
691 }
692 }
694 static inline void
695 sp_export_find_default_selection(GtkWidget * dlg)
696 {
697 selection_type key = SELECTION_NUMBER_OF;
699 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
700 key = SELECTION_SELECTION;
701 }
703 /* Try using the preferences */
704 if (key == SELECTION_NUMBER_OF) {
705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
706 int i = SELECTION_NUMBER_OF;
708 Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
710 if (!what.empty()) {
711 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
712 if (what == selection_names[i]) {
713 break;
714 }
715 }
716 }
718 key = (selection_type)i;
719 }
721 if (key == SELECTION_NUMBER_OF) {
722 key = SELECTION_SELECTION;
723 }
725 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
726 selection_names[key]);
727 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
729 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
730 }
733 /**
734 * \brief If selection changed or a different document activated, we must
735 * recalculate any chosen areas
736 *
737 */
738 static void
739 sp_export_selection_changed ( Inkscape::Application *inkscape,
740 Inkscape::Selection *selection,
741 GtkObject *base )
742 {
743 selection_type current_key;
744 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
746 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
747 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
748 was_empty) {
749 gtk_toggle_button_set_active
750 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
751 TRUE );
752 }
753 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
755 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
757 if (inkscape &&
758 SP_IS_INKSCAPE (inkscape) &&
759 selection &&
760 SELECTION_CUSTOM != current_key) {
761 GtkToggleButton * button;
762 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
763 sp_export_area_toggled(button, base);
764 }
766 sp_export_update_checkbuttons (base);
767 }
769 static void
770 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
771 Inkscape::Selection */*selection*/,
772 guint /*flags*/,
773 GtkObject *base )
774 {
775 selection_type current_key;
776 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
778 switch (current_key) {
779 case SELECTION_DRAWING:
780 if ( SP_ACTIVE_DESKTOP ) {
781 SPDocument *doc;
782 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
783 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
784 if (bbox) {
785 sp_export_set_area (base, bbox->min()[Geom::X],
786 bbox->min()[Geom::Y],
787 bbox->max()[Geom::X],
788 bbox->max()[Geom::Y]);
789 }
790 }
791 break;
792 case SELECTION_SELECTION:
793 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
794 NRRect bbox;
795 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
796 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
797 }
798 break;
799 default:
800 /* Do nothing for page or for custom */
801 break;
802 }
804 return;
805 }
807 /// Called when one of the selection buttons was toggled.
808 static void
809 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
810 {
811 if (gtk_object_get_data (base, "update"))
812 return;
814 selection_type key, old_key;
815 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
816 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
818 /* Ignore all "turned off" events unless we're the only active button */
819 if (!gtk_toggle_button_get_active (tb) ) {
821 /* Don't let the current selection be deactived - but rerun the
822 activate to allow the user to renew the values */
823 if (key == old_key) {
824 gtk_toggle_button_set_active ( tb, TRUE );
825 }
827 return;
828 }
830 /* Turn off the currently active button unless it's us */
831 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
833 if (old_key != key) {
834 gtk_toggle_button_set_active
835 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
836 FALSE );
837 }
839 if ( SP_ACTIVE_DESKTOP )
840 {
841 SPDocument *doc;
842 Geom::OptRect bbox;
843 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
845 /* Notice how the switch is used to 'fall through' here to get
846 various backups. If you modify this without noticing you'll
847 probabaly screw something up. */
848 switch (key) {
849 case SELECTION_SELECTION:
850 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
851 {
852 bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds();
853 /* Only if there is a selection that we can set
854 do we break, otherwise we fall through to the
855 drawing */
856 // std::cout << "Using selection: SELECTION" << std::endl;
857 key = SELECTION_SELECTION;
858 break;
859 }
860 case SELECTION_DRAWING:
861 /** \todo
862 * This returns wrong values if the document has a viewBox.
863 */
864 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
865 /* If the drawing is valid, then we'll use it and break
866 otherwise we drop through to the page settings */
867 if (bbox) {
868 // std::cout << "Using selection: DRAWING" << std::endl;
869 key = SELECTION_DRAWING;
870 break;
871 }
872 case SELECTION_PAGE:
873 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
874 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
876 // std::cout << "Using selection: PAGE" << std::endl;
877 key = SELECTION_PAGE;
878 break;
879 case SELECTION_CUSTOM:
880 default:
881 break;
882 } // switch
884 // remember area setting
885 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
886 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
888 if ( key != SELECTION_CUSTOM && bbox ) {
889 sp_export_set_area (base, bbox->min()[Geom::X],
890 bbox->min()[Geom::Y],
891 bbox->max()[Geom::X],
892 bbox->max()[Geom::Y]);
893 }
895 } // end of if ( SP_ACTIVE_DESKTOP )
898 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
899 GtkWidget * file_entry;
900 const gchar * filename = NULL;
901 float xdpi = 0.0, ydpi = 0.0;
903 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
905 switch (key) {
906 case SELECTION_PAGE:
907 case SELECTION_DRAWING: {
908 SPDocument * doc = SP_ACTIVE_DOCUMENT;
909 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
911 if (filename == NULL) {
912 if (doc_export_name != NULL) {
913 filename = g_strdup(doc_export_name);
914 } else {
915 filename = g_strdup("");
916 }
917 }
918 break;
919 }
920 case SELECTION_SELECTION:
921 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
923 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
925 /* If we still don't have a filename -- let's build
926 one that's nice */
927 if (filename == NULL) {
928 const gchar * id = NULL;
929 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
930 for(; reprlst != NULL; reprlst = reprlst->next) {
931 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
932 if (repr->attribute("id")) {
933 id = repr->attribute("id");
934 break;
935 }
936 }
938 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
939 }
940 }
941 break;
942 case SELECTION_CUSTOM:
943 default:
944 break;
945 }
947 if (filename != NULL) {
948 g_free(original_name);
949 original_name = g_strdup(filename);
950 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
951 }
953 if (xdpi != 0.0) {
954 sp_export_value_set(base, "xdpi", xdpi);
955 }
957 /* These can't be separate, and setting x sets y, so for
958 now setting this is disabled. Hopefully it won't be in
959 the future */
960 if (FALSE && ydpi != 0.0) {
961 sp_export_value_set(base, "ydpi", ydpi);
962 }
963 }
965 return;
966 } // end of sp_export_area_toggled()
968 /// Called when dialog is deleted
969 static gint
970 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
971 {
972 g_object_set_data (base, "cancel", (gpointer) 1);
973 return TRUE;
974 } // end of sp_export_progress_delete()
976 /// Called when progress is cancelled
977 static void
978 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
979 {
980 g_object_set_data (base, "cancel", (gpointer) 1);
981 } // end of sp_export_progress_cancel()
983 /// Called for every progress iteration
984 static unsigned int
985 sp_export_progress_callback (float value, void *data)
986 {
987 GtkWidget *prg;
988 int evtcount;
990 if (g_object_get_data ((GObject *) data, "cancel"))
991 return FALSE;
993 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
994 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
996 evtcount = 0;
997 while ((evtcount < 16) && gdk_events_pending ()) {
998 gtk_main_iteration_do (FALSE);
999 evtcount += 1;
1000 }
1002 gtk_main_iteration_do (FALSE);
1004 return TRUE;
1006 } // end of sp_export_progress_callback()
1008 GtkWidget *
1009 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1010 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1012 dlg = gtk_dialog_new ();
1013 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1014 prg = gtk_progress_bar_new ();
1015 sp_transientize (dlg);
1016 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1017 g_object_set_data ((GObject *) base, "progress", prg);
1019 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1021 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1022 GTK_PROGRESS_LEFT_TO_RIGHT);
1023 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1024 prg, FALSE, FALSE, 4 );
1025 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1026 GTK_STOCK_CANCEL,
1027 GTK_RESPONSE_CANCEL );
1029 g_signal_connect ( (GObject *) dlg, "delete_event",
1030 (GCallback) sp_export_progress_delete, base);
1031 g_signal_connect ( (GObject *) btn, "clicked",
1032 (GCallback) sp_export_progress_cancel, base);
1033 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1034 gtk_widget_show_all (dlg);
1036 return dlg;
1037 }
1039 // FIXME: Some lib function should be available to do this ...
1040 static gchar *
1041 filename_add_extension (const gchar *filename, const gchar *extension)
1042 {
1043 const gchar *dot;
1045 dot = strrchr (filename, '.');
1046 if ( !dot )
1047 return g_strconcat (filename, ".", extension, NULL);
1048 {
1049 if (dot[1] == '\0')
1050 return g_strconcat (filename, extension, NULL);
1051 else
1052 {
1053 if (g_strcasecmp (dot + 1, extension) == 0)
1054 return g_strdup (filename);
1055 else
1056 {
1057 return g_strconcat (filename, ".", extension, NULL);
1058 }
1059 }
1060 }
1061 }
1063 gchar *absolutize_path_from_document_location (SPDocument *doc, const gchar *filename)
1064 {
1065 gchar *path = 0;
1066 //Make relative paths go from the document location, if possible:
1067 if (!g_path_is_absolute(filename) && doc->uri) {
1068 gchar *dirname = g_path_get_dirname(doc->uri);
1069 if (dirname) {
1070 path = g_build_filename(dirname, filename, NULL);
1071 g_free(dirname);
1072 }
1073 }
1074 if (!path) {
1075 path = g_strdup(filename);
1076 }
1077 return path;
1078 }
1080 /// Called when export button is clicked
1081 static void
1082 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1083 {
1084 if (!SP_ACTIVE_DESKTOP) return;
1086 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1087 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1089 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1090 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1091 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1092 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1093 // Batch export of selected objects
1095 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1096 gint n = 0;
1098 if (num < 1)
1099 return;
1101 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1102 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1103 g_free (progress_text);
1105 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1106 i != NULL;
1107 i = i->next) {
1108 SPItem *item = (SPItem *) i->data;
1110 // retrieve export filename hint
1111 const gchar *filename = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1112 gchar *path = 0;
1113 if (!filename) {
1114 path = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1115 } else {
1116 path = absolutize_path_from_document_location(doc, filename);
1117 }
1119 // retrieve export dpi hints
1120 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1121 gdouble dpi = 0.0;
1122 if (dpi_hint) {
1123 dpi = atof(dpi_hint);
1124 }
1125 if (dpi == 0.0) {
1126 dpi = DPI_BASE;
1127 }
1129 Geom::OptRect area;
1130 sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1131 if (area) {
1132 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1133 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1135 if (width > 1 && height > 1) {
1136 /* Do export */
1137 if (!sp_export_png_file (doc, path,
1138 *area, width, height, dpi, dpi,
1139 nv->pagecolor,
1140 NULL, NULL, TRUE, // overwrite without asking
1141 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1142 )) {
1143 gchar * error;
1144 gchar * safeFile = Inkscape::IO::sanitizeString(path);
1145 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1146 sp_ui_error_dialog(error);
1147 g_free(safeFile);
1148 g_free(error);
1149 }
1150 }
1151 }
1152 n++;
1153 g_free(path);
1154 sp_export_progress_callback((float)n/num, base);
1155 }
1157 gtk_widget_destroy (prog_dlg);
1158 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1160 } else {
1162 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1163 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1165 float const x0 = sp_export_value_get_px(base, "x0");
1166 float const y0 = sp_export_value_get_px(base, "y0");
1167 float const x1 = sp_export_value_get_px(base, "x1");
1168 float const y1 = sp_export_value_get_px(base, "y1");
1169 float const xdpi = sp_export_value_get(base, "xdpi");
1170 float const ydpi = sp_export_value_get(base, "ydpi");
1171 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1172 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1174 if (filename == NULL || *filename == '\0') {
1175 sp_ui_error_dialog(_("You have to enter a filename"));
1176 return;
1177 }
1179 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1180 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1181 return;
1182 }
1184 // make sure that .png is the extension of the file:
1185 gchar * filename_ext = filename_add_extension(filename, "png");
1186 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1188 gchar *path = absolutize_path_from_document_location(doc, filename_ext);
1190 gchar *dirname = g_path_get_dirname(path);
1191 if ( dirname == NULL
1192 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1193 {
1194 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1195 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1196 safeDir);
1197 sp_ui_error_dialog(error);
1198 g_free(safeDir);
1199 g_free(error);
1200 g_free(dirname);
1201 g_free(path);
1202 return;
1203 }
1204 g_free(dirname);
1206 gchar *fn = g_path_get_basename (path);
1207 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1208 g_free (fn);
1210 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1211 g_free (progress_text);
1213 /* Do export */
1214 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), path,
1215 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1216 nv->pagecolor,
1217 sp_export_progress_callback, base, FALSE,
1218 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1219 )) {
1220 gchar * error;
1221 gchar * safeFile = Inkscape::IO::sanitizeString(path);
1222 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1223 sp_ui_error_dialog(error);
1224 g_free(safeFile);
1225 g_free(error);
1226 }
1228 /* Reset the filename so that it can be changed again by changing
1229 selections and all that */
1230 g_free(original_name);
1231 original_name = g_strdup(filename_ext);
1232 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1234 gtk_widget_destroy (prog_dlg);
1235 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1237 /* Setup the values in the document */
1238 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1239 case SELECTION_PAGE:
1240 case SELECTION_DRAWING: {
1241 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1242 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1243 bool modified = false;
1244 const gchar * temp_string;
1246 bool saved = sp_document_get_undo_sensitive(doc);
1247 sp_document_set_undo_sensitive(doc, false);
1249 temp_string = repr->attribute("inkscape:export-filename");
1250 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1251 repr->setAttribute("inkscape:export-filename", filename_ext);
1252 modified = true;
1253 }
1254 temp_string = repr->attribute("inkscape:export-xdpi");
1255 if (temp_string == NULL || xdpi != atof(temp_string)) {
1256 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1257 modified = true;
1258 }
1259 temp_string = repr->attribute("inkscape:export-ydpi");
1260 if (temp_string == NULL || xdpi != atof(temp_string)) {
1261 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1262 modified = true;
1263 }
1264 sp_document_set_undo_sensitive(doc, saved);
1266 if (modified) {
1267 doc->setModifiedSinceSave();
1268 }
1269 break;
1270 }
1271 case SELECTION_SELECTION: {
1272 const GSList * reprlst;
1273 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1274 bool modified = false;
1276 bool saved = sp_document_get_undo_sensitive(doc);
1277 sp_document_set_undo_sensitive(doc, false);
1278 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1280 for(; reprlst != NULL; reprlst = reprlst->next) {
1281 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1282 const gchar * temp_string;
1284 if (repr->attribute("id") == NULL ||
1285 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1286 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1287 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1288 temp_string = repr->attribute("inkscape:export-filename");
1289 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1290 repr->setAttribute("inkscape:export-filename", filename_ext);
1291 modified = true;
1292 }
1293 }
1294 temp_string = repr->attribute("inkscape:export-xdpi");
1295 if (temp_string == NULL || xdpi != atof(temp_string)) {
1296 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1297 modified = true;
1298 }
1299 temp_string = repr->attribute("inkscape:export-ydpi");
1300 if (temp_string == NULL || xdpi != atof(temp_string)) {
1301 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1302 modified = true;
1303 }
1304 }
1305 sp_document_set_undo_sensitive(doc, saved);
1307 if (modified) {
1308 doc->setModifiedSinceSave();
1309 }
1310 break;
1311 }
1312 default:
1313 break;
1314 }
1316 g_free (filename_ext);
1317 g_free (path);
1319 }
1321 } // end of sp_export_export_clicked()
1323 /// Called when Browse button is clicked
1324 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1325 static void
1326 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1327 {
1328 GtkWidget *fs, *fe;
1329 const gchar *filename;
1331 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1332 (GtkWindow*)dlg,
1333 GTK_FILE_CHOOSER_ACTION_SAVE,
1334 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1335 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1336 NULL );
1338 #ifdef WITH_GNOME_VFS
1339 if (gnome_vfs_initialized()) {
1340 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1341 }
1342 #endif
1344 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1346 sp_transientize (fs);
1348 gtk_window_set_modal(GTK_WINDOW (fs), true);
1350 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1352 if (*filename == '\0') {
1353 filename = create_filepath_from_id(NULL, NULL);
1354 }
1356 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1358 #ifdef WIN32
1359 // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1360 OPENFILENAMEW opf;
1361 WCHAR filter_string[20];
1362 wcsncpy(filter_string, L"PNG#*.png##", 11);
1363 filter_string[3] = L'\0';
1364 filter_string[9] = L'\0';
1365 filter_string[10] = L'\0';
1366 WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1367 WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1368 // Copy the selected file name, converting from UTF-8 to UTF-16
1369 WCHAR _filename[_MAX_PATH + 1];
1370 memset(_filename, 0, sizeof(_filename));
1371 gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1372 wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1373 g_free(utf16_path_string);
1375 opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1376 opf.lpstrFilter = filter_string;
1377 opf.lpstrCustomFilter = 0;
1378 opf.nMaxCustFilter = 0L;
1379 opf.nFilterIndex = 1L;
1380 opf.lpstrFile = _filename;
1381 opf.nMaxFile = _MAX_PATH;
1382 opf.lpstrFileTitle = NULL;
1383 opf.nMaxFileTitle=0;
1384 opf.lpstrInitialDir = 0;
1385 opf.lpstrTitle = title_string;
1386 opf.nFileOffset = 0;
1387 opf.nFileExtension = 2;
1388 opf.lpstrDefExt = extension_string;
1389 opf.lpfnHook = NULL;
1390 opf.lCustData = 0;
1391 opf.Flags = OFN_PATHMUSTEXIST;
1392 opf.lStructSize = sizeof(OPENFILENAMEW);
1393 if (GetSaveFileNameW(&opf) != 0)
1394 {
1395 // Copy the selected file name, converting from UTF-16 to UTF-8
1396 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1397 gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1398 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1399 g_free(utf8string);
1401 }
1402 g_free(extension_string);
1403 g_free(title_string);
1405 #else
1406 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1407 {
1408 gchar *file;
1410 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1412 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1413 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1415 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1417 g_free(utf8file);
1418 g_free(file);
1419 }
1420 #endif
1422 gtk_widget_destroy (fs);
1424 return;
1425 } // end of sp_export_browse_clicked()
1427 // TODO: Move this to nr-rect-fns.h.
1428 static bool
1429 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1430 {
1431 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1432 return (
1433 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1434 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1435 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1436 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1437 );
1438 }
1440 /**
1441 \brief This function is used to detect the current selection setting
1442 based on the values in the x0, y0, x1 and y0 fields.
1443 \param base The export dialog itself
1445 One of the most confusing parts of this function is why the array
1446 is built at the beginning. What needs to happen here is that we
1447 should always check the current selection to see if it is the valid
1448 one. While this is a performance improvement it is also a usability
1449 one during the cases where things like selections and drawings match
1450 size. This way buttons change less 'randomly' (atleast in the eyes
1451 of the user). To do this an array is built where the current selection
1452 type is placed first, and then the others in an order from smallest
1453 to largest (this can be configured by reshuffling \c test_order).
1455 All of the values in this function are rounded to two decimal places
1456 because that is what is shown to the user. While everything is kept
1457 more accurate than that, the user can't control more acurrate than
1458 that, so for this to work for them - it needs to check on that level
1459 of accuracy.
1461 \todo finish writing this up
1462 */
1463 static void
1464 sp_export_detect_size(GtkObject * base) {
1465 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1466 selection_type this_test[SELECTION_NUMBER_OF + 1];
1467 selection_type key = SELECTION_NUMBER_OF;
1469 Geom::Point x(sp_export_value_get_px (base, "x0"),
1470 sp_export_value_get_px (base, "y0"));
1471 Geom::Point y(sp_export_value_get_px (base, "x1"),
1472 sp_export_value_get_px (base, "y1"));
1473 Geom::Rect current_bbox(x, y);
1474 //std::cout << "Current " << current_bbox;
1476 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1477 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1478 this_test[i + 1] = test_order[i];
1479 }
1481 for (int i = 0;
1482 i < SELECTION_NUMBER_OF + 1 &&
1483 key == SELECTION_NUMBER_OF &&
1484 SP_ACTIVE_DESKTOP != NULL;
1485 i++) {
1486 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1487 switch (this_test[i]) {
1488 case SELECTION_SELECTION:
1489 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1490 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1492 //std::cout << "Selection " << bbox;
1493 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1494 key = SELECTION_SELECTION;
1495 }
1496 }
1497 break;
1498 case SELECTION_DRAWING: {
1499 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1501 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1503 // std::cout << "Drawing " << bbox2;
1504 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1505 key = SELECTION_DRAWING;
1506 }
1507 break;
1508 }
1510 case SELECTION_PAGE: {
1511 SPDocument *doc;
1513 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1515 Geom::Point x(0.0, 0.0);
1516 Geom::Point y(sp_document_width(doc),
1517 sp_document_height(doc));
1518 Geom::Rect bbox(x, y);
1520 // std::cout << "Page " << bbox;
1521 if (sp_export_bbox_equal(bbox,current_bbox)) {
1522 key = SELECTION_PAGE;
1523 }
1525 break;
1526 }
1527 default:
1528 break;
1529 }
1530 }
1531 // std::cout << std::endl;
1533 if (key == SELECTION_NUMBER_OF) {
1534 key = SELECTION_CUSTOM;
1535 }
1537 /* We're now using a custom size, not a fixed one */
1538 /* printf("Detecting state: %s\n", selection_names[key]); */
1539 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1540 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1541 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1542 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1544 return;
1545 } /* sp_export_detect_size */
1547 /// Called when area x0 value is changed
1548 static void
1549 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1550 {
1551 float x0, x1, xdpi, width;
1553 if (gtk_object_get_data (base, "update"))
1554 return;
1556 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1557 (base, "units")))
1558 {
1559 return;
1560 }
1562 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1564 x0 = sp_export_value_get_px (base, "x0");
1565 x1 = sp_export_value_get_px (base, "x1");
1566 xdpi = sp_export_value_get (base, "xdpi");
1568 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1570 if (width < SP_EXPORT_MIN_SIZE) {
1571 const gchar *key;
1572 width = SP_EXPORT_MIN_SIZE;
1573 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1575 if (!strcmp (key, "x0")) {
1576 x1 = x0 + width * DPI_BASE / xdpi;
1577 sp_export_value_set_px (base, "x1", x1);
1578 } else {
1579 x0 = x1 - width * DPI_BASE / xdpi;
1580 sp_export_value_set_px (base, "x0", x0);
1581 }
1582 }
1584 sp_export_value_set_px (base, "width", x1 - x0);
1585 sp_export_value_set (base, "bmwidth", width);
1587 sp_export_detect_size(base);
1589 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1591 return;
1592 } // end of sp_export_area_x_value_changed()
1594 /// Called when area y0 value is changed.
1595 static void
1596 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1597 {
1598 float y0, y1, ydpi, height;
1600 if (gtk_object_get_data (base, "update"))
1601 return;
1603 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1604 (base, "units")))
1605 {
1606 return;
1607 }
1609 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1611 y0 = sp_export_value_get_px (base, "y0");
1612 y1 = sp_export_value_get_px (base, "y1");
1613 ydpi = sp_export_value_get (base, "ydpi");
1615 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1617 if (height < SP_EXPORT_MIN_SIZE) {
1618 const gchar *key;
1619 height = SP_EXPORT_MIN_SIZE;
1620 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1621 if (!strcmp (key, "y0")) {
1622 y1 = y0 + height * DPI_BASE / ydpi;
1623 sp_export_value_set_px (base, "y1", y1);
1624 } else {
1625 y0 = y1 - height * DPI_BASE / ydpi;
1626 sp_export_value_set_px (base, "y0", y0);
1627 }
1628 }
1630 sp_export_value_set_px (base, "height", y1 - y0);
1631 sp_export_value_set (base, "bmheight", height);
1633 sp_export_detect_size(base);
1635 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1637 return;
1638 } // end of sp_export_area_y_value_changed()
1640 /// Called when x1-x0 or area width is changed
1641 static void
1642 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1643 {
1644 float x0, x1, xdpi, width, bmwidth;
1646 if (gtk_object_get_data (base, "update"))
1647 return;
1649 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1650 (base, "units"))) {
1651 return;
1652 }
1654 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1656 x0 = sp_export_value_get_px (base, "x0");
1657 x1 = sp_export_value_get_px (base, "x1");
1658 xdpi = sp_export_value_get (base, "xdpi");
1659 width = sp_export_value_get_px (base, "width");
1660 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1662 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1664 bmwidth = SP_EXPORT_MIN_SIZE;
1665 width = bmwidth * DPI_BASE / xdpi;
1666 sp_export_value_set_px (base, "width", width);
1667 }
1669 sp_export_value_set_px (base, "x1", x0 + width);
1670 sp_export_value_set (base, "bmwidth", bmwidth);
1672 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1674 return;
1675 } // end of sp_export_area_width_value_changed()
1677 /// Called when y1-y0 or area height is changed.
1678 static void
1679 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1680 {
1682 float y0, y1, ydpi, height, bmheight;
1684 if (gtk_object_get_data (base, "update"))
1685 return;
1687 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1688 (base, "units"))) {
1689 return;
1690 }
1692 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1694 y0 = sp_export_value_get_px (base, "y0");
1695 y1 = sp_export_value_get_px (base, "y1");
1696 ydpi = sp_export_value_get (base, "ydpi");
1697 height = sp_export_value_get_px (base, "height");
1698 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1700 if (bmheight < SP_EXPORT_MIN_SIZE) {
1701 bmheight = SP_EXPORT_MIN_SIZE;
1702 height = bmheight * DPI_BASE / ydpi;
1703 sp_export_value_set_px (base, "height", height);
1704 }
1706 sp_export_value_set_px (base, "y1", y0 + height);
1707 sp_export_value_set (base, "bmheight", bmheight);
1709 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1711 return;
1712 } // end of sp_export_area_height_value_changed()
1714 /**
1715 \brief A function to set the ydpi
1716 \param base The export dialog
1718 This function grabs all of the y values and then figures out the
1719 new bitmap size based on the changing dpi value. The dpi value is
1720 gotten from the xdpi setting as these can not currently be independent.
1721 */
1722 static void
1723 sp_export_set_image_y (GtkObject *base)
1724 {
1725 float y0, y1, xdpi;
1727 y0 = sp_export_value_get_px (base, "y0");
1728 y1 = sp_export_value_get_px (base, "y1");
1729 xdpi = sp_export_value_get (base, "xdpi");
1731 sp_export_value_set (base, "ydpi", xdpi);
1732 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1734 return;
1735 } // end of sp_export_set_image_y()
1737 /**
1738 \brief A function to set the xdpi
1739 \param base The export dialog
1741 This function grabs all of the x values and then figures out the
1742 new bitmap size based on the changing dpi value. The dpi value is
1743 gotten from the xdpi setting as these can not currently be independent.
1744 */
1745 static void
1746 sp_export_set_image_x (GtkObject *base)
1747 {
1748 float x0, x1, xdpi;
1750 x0 = sp_export_value_get_px (base, "x0");
1751 x1 = sp_export_value_get_px (base, "x1");
1752 xdpi = sp_export_value_get (base, "xdpi");
1754 sp_export_value_set (base, "ydpi", xdpi);
1755 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1757 return;
1758 } // end of sp_export_set_image_x()
1760 /// Called when pixel width is changed
1761 static void
1762 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1763 {
1764 float x0, x1, bmwidth, xdpi;
1766 if (gtk_object_get_data (base, "update"))
1767 return;
1769 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1770 (base, "units"))) {
1771 return;
1772 }
1774 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1776 x0 = sp_export_value_get_px (base, "x0");
1777 x1 = sp_export_value_get_px (base, "x1");
1778 bmwidth = sp_export_value_get (base, "bmwidth");
1780 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1781 bmwidth = SP_EXPORT_MIN_SIZE;
1782 sp_export_value_set (base, "bmwidth", bmwidth);
1783 }
1785 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1786 sp_export_value_set (base, "xdpi", xdpi);
1788 sp_export_set_image_y (base);
1790 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1792 return;
1793 } // end of sp_export_bitmap_width_value_changed()
1795 /// Called when pixel height is changed
1796 static void
1797 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1798 {
1799 float y0, y1, bmheight, xdpi;
1801 if (gtk_object_get_data (base, "update"))
1802 return;
1804 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1805 (base, "units"))) {
1806 return;
1807 }
1809 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1811 y0 = sp_export_value_get_px (base, "y0");
1812 y1 = sp_export_value_get_px (base, "y1");
1813 bmheight = sp_export_value_get (base, "bmheight");
1815 if (bmheight < SP_EXPORT_MIN_SIZE) {
1816 bmheight = SP_EXPORT_MIN_SIZE;
1817 sp_export_value_set (base, "bmheight", bmheight);
1818 }
1820 xdpi = bmheight * DPI_BASE / (y1 - y0);
1821 sp_export_value_set (base, "xdpi", xdpi);
1823 sp_export_set_image_x (base);
1825 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1827 return;
1828 } // end of sp_export_bitmap_width_value_changed()
1830 /**
1831 \brief A function to adjust the bitmap width when the xdpi value changes
1832 \param adj The adjustment that was changed
1833 \param base The export dialog itself
1835 The first thing this function checks is to see if we are doing an
1836 update. If we are, this function just returns because there is another
1837 instance of it that will handle everything for us. If there is a
1838 units change, we also assume that everyone is being updated appropriately
1839 and there is nothing for us to do.
1841 If we're the highest level function, we set the update flag, and
1842 continue on our way.
1844 All of the values are grabbed using the \c sp_export_value_get functions
1845 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1846 xdpi value is saved in the preferences for the next time the dialog
1847 is opened. (does the selection dpi need to be set here?)
1849 A check is done to to ensure that we aren't outputing an invalid width,
1850 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1851 changed to make it valid.
1853 After all of this the bitmap width is changed.
1855 We also change the ydpi. This is a temporary hack as these can not
1856 currently be independent. This is likely to change in the future.
1857 */
1858 void
1859 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1860 {
1861 float x0, x1, xdpi, bmwidth;
1863 if (gtk_object_get_data (base, "update"))
1864 return;
1866 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1867 (base, "units"))) {
1868 return;
1869 }
1871 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1873 x0 = sp_export_value_get_px (base, "x0");
1874 x1 = sp_export_value_get_px (base, "x1");
1875 xdpi = sp_export_value_get (base, "xdpi");
1877 // remember xdpi setting
1878 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1879 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1881 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1883 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1884 bmwidth = SP_EXPORT_MIN_SIZE;
1885 if (x1 != x0)
1886 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1887 else
1888 xdpi = DPI_BASE;
1889 sp_export_value_set (base, "xdpi", xdpi);
1890 }
1892 sp_export_value_set (base, "bmwidth", bmwidth);
1894 sp_export_set_image_y (base);
1896 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1898 return;
1899 } // end of sp_export_xdpi_value_changed()
1902 /**
1903 \brief A function to change the area that is used for the exported
1904 bitmap.
1905 \param base This is the export dialog
1906 \param x0 Horizontal upper left hand corner of the picture in points
1907 \param y0 Vertical upper left hand corner of the picture in points
1908 \param x1 Horizontal lower right hand corner of the picture in points
1909 \param y1 Vertical lower right hand corner of the picture in points
1911 This function just calls \c sp_export_value_set_px for each of the
1912 parameters that is passed in. This allows for setting them all in
1913 one convient area.
1915 Update is set to suspend all of the other test running while all the
1916 values are being set up. This allows for a performance increase, but
1917 it also means that the wrong type won't be detected with only some of
1918 the values set. After all the values are set everyone is told that
1919 there has been an update.
1920 */
1921 static void
1922 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1923 {
1924 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1925 sp_export_value_set_px (base, "x1", x1);
1926 sp_export_value_set_px (base, "y1", y1);
1927 sp_export_value_set_px (base, "x0", x0);
1928 sp_export_value_set_px (base, "y0", y0);
1929 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1931 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1932 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1934 return;
1935 }
1937 /**
1938 \brief Sets the value of an adjustment
1939 \param base The export dialog
1940 \param key Which adjustment to set
1941 \param val What value to set it to
1943 This function finds the adjustment using the data stored in the
1944 export dialog. After finding the adjustment it then sets
1945 the value of it.
1946 */
1947 static void
1948 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1949 {
1950 GtkAdjustment *adj;
1952 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1954 gtk_adjustment_set_value (adj, val);
1955 }
1957 /**
1958 \brief A function to set a value using the units points
1959 \param base The export dialog
1960 \param key Which value should be set
1961 \param val What the value should be in points
1963 This function first gets the adjustment for the key that is passed
1964 in. It then figures out what units are currently being used in the
1965 dialog. After doing all of that, it then converts the incoming
1966 value and sets the adjustment.
1967 */
1968 static void
1969 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1970 {
1971 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1973 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1975 return;
1976 }
1978 /**
1979 \brief Get the value of an adjustment in the export dialog
1980 \param base The export dialog
1981 \param key Which adjustment is being looked for
1982 \return The value in the specified adjustment
1984 This function gets the adjustment from the data field in the export
1985 dialog. It then grabs the value from the adjustment.
1986 */
1987 static float
1988 sp_export_value_get ( GtkObject *base, const gchar *key )
1989 {
1990 GtkAdjustment *adj;
1992 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1994 return adj->value;
1995 }
1997 /**
1998 \brief Grabs a value in the export dialog and converts the unit
1999 to points
2000 \param base The export dialog
2001 \param key Which value should be returned
2002 \return The value in the adjustment in points
2004 This function, at its most basic, is a call to \c sp_export_value_get
2005 to get the value of the adjustment. It then finds the units that
2006 are being used by looking at the "units" attribute of the export
2007 dialog. Using that it converts the returned value into points.
2008 */
2009 static float
2010 sp_export_value_get_px ( GtkObject *base, const gchar *key )
2011 {
2012 float value = sp_export_value_get(base, key);
2013 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
2015 return sp_units_get_pixels (value, *unit);
2016 } // end of sp_export_value_get_px()
2018 /**
2019 \brief This function is called when the filename is changed by
2020 anyone. It resets the virgin bit.
2021 \param object Text entry box
2022 \param data The export dialog
2023 \return None
2025 This function gets called when the text area is modified. It is
2026 looking for the case where the text area is modified from its
2027 original value. In that case it sets the "filename-modified" bit
2028 to TRUE. If the text dialog returns back to the original text, the
2029 bit gets reset. This should stop simple mistakes.
2030 */
2031 static void
2032 sp_export_filename_modified (GtkObject * object, gpointer data)
2033 {
2034 GtkWidget * text_entry = (GtkWidget *)object;
2035 GtkWidget * export_dialog = (GtkWidget *)data;
2037 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2038 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2039 // printf("Modified: FALSE\n");
2040 } else {
2041 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2042 // printf("Modified: TRUE\n");
2043 }
2045 return;
2046 } // end sp_export_filename_modified
2048 /*
2049 Local Variables:
2050 mode:c++
2051 c-file-style:"stroustrup"
2052 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2053 indent-tabs-mode:nil
2054 fill-column:99
2055 End:
2056 */
2057 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :