835003e5e621737df23b2c948820b879216611b9
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 = Inkscape::Preferences::get()->getString("/dialogs/save_as/default").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 /// Called when export button is clicked
1064 static void
1065 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1066 {
1067 if (!SP_ACTIVE_DESKTOP) return;
1069 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1071 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1072 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1073 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1074 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1075 // Batch export of selected objects
1077 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1078 gint n = 0;
1080 if (num < 1)
1081 return;
1083 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1084 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1085 g_free (progress_text);
1087 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1088 i != NULL;
1089 i = i->next) {
1090 SPItem *item = (SPItem *) i->data;
1091 // retrieve export filename hint
1092 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1093 if (!fn) {
1094 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1095 }
1097 // retrieve export dpi hints
1098 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1099 gdouble dpi = 0.0;
1100 if (dpi_hint) {
1101 dpi = atof(dpi_hint);
1102 }
1103 if (dpi == 0.0) {
1104 dpi = DPI_BASE;
1105 }
1107 Geom::OptRect area;
1108 sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1109 if (area) {
1110 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1111 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1113 if (width > 1 && height > 1) {
1114 /* Do export */
1115 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1116 *area, width, height, dpi, dpi,
1117 nv->pagecolor,
1118 NULL, NULL, TRUE, // overwrite without asking
1119 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1120 )) {
1121 gchar * error;
1122 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1123 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1124 sp_ui_error_dialog(error);
1125 g_free(safeFile);
1126 g_free(error);
1127 }
1128 }
1129 }
1130 n++;
1131 sp_export_progress_callback((float)n/num, base);
1132 }
1134 gtk_widget_destroy (prog_dlg);
1135 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1137 } else {
1139 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1140 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1142 float const x0 = sp_export_value_get_px(base, "x0");
1143 float const y0 = sp_export_value_get_px(base, "y0");
1144 float const x1 = sp_export_value_get_px(base, "x1");
1145 float const y1 = sp_export_value_get_px(base, "y1");
1146 float const xdpi = sp_export_value_get(base, "xdpi");
1147 float const ydpi = sp_export_value_get(base, "ydpi");
1148 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1149 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1151 if (filename == NULL || *filename == '\0') {
1152 sp_ui_error_dialog(_("You have to enter a filename"));
1153 return;
1154 }
1156 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1157 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1158 return;
1159 }
1161 gchar *dirname = g_path_get_dirname(filename);
1162 if ( dirname == NULL
1163 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1164 {
1165 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1166 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1167 safeDir);
1168 sp_ui_error_dialog(error);
1169 g_free(safeDir);
1170 g_free(error);
1171 g_free(dirname);
1172 return;
1173 }
1174 g_free(dirname);
1176 // make sure that .png is the extension of the file:
1177 gchar * filename_ext = filename_add_extension(filename, "png");
1178 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1180 gchar *fn = g_path_get_basename (filename_ext);
1182 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1183 g_free (fn);
1184 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1185 g_free (progress_text);
1187 /* Do export */
1188 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1189 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1190 nv->pagecolor,
1191 sp_export_progress_callback, base, FALSE,
1192 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1193 )) {
1194 gchar * error;
1195 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1196 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1197 sp_ui_error_dialog(error);
1198 g_free(safeFile);
1199 g_free(error);
1200 }
1202 /* Reset the filename so that it can be changed again by changing
1203 selections and all that */
1204 g_free(original_name);
1205 original_name = g_strdup(filename_ext);
1206 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1208 gtk_widget_destroy (prog_dlg);
1209 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1211 /* Setup the values in the document */
1212 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1213 case SELECTION_PAGE:
1214 case SELECTION_DRAWING: {
1215 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1216 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1217 bool modified = false;
1218 const gchar * temp_string;
1220 bool saved = sp_document_get_undo_sensitive(doc);
1221 sp_document_set_undo_sensitive(doc, false);
1223 temp_string = repr->attribute("inkscape:export-filename");
1224 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1225 repr->setAttribute("inkscape:export-filename", filename_ext);
1226 modified = true;
1227 }
1228 temp_string = repr->attribute("inkscape:export-xdpi");
1229 if (temp_string == NULL || xdpi != atof(temp_string)) {
1230 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1231 modified = true;
1232 }
1233 temp_string = repr->attribute("inkscape:export-ydpi");
1234 if (temp_string == NULL || xdpi != atof(temp_string)) {
1235 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1236 modified = true;
1237 }
1238 sp_document_set_undo_sensitive(doc, saved);
1240 if (modified) {
1241 doc->setModifiedSinceSave();
1242 }
1243 break;
1244 }
1245 case SELECTION_SELECTION: {
1246 const GSList * reprlst;
1247 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1248 bool modified = false;
1250 bool saved = sp_document_get_undo_sensitive(doc);
1251 sp_document_set_undo_sensitive(doc, false);
1252 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1254 for(; reprlst != NULL; reprlst = reprlst->next) {
1255 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1256 const gchar * temp_string;
1258 if (repr->attribute("id") == NULL ||
1259 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1260 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1261 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1262 temp_string = repr->attribute("inkscape:export-filename");
1263 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1264 repr->setAttribute("inkscape:export-filename", filename_ext);
1265 modified = true;
1266 }
1267 }
1268 temp_string = repr->attribute("inkscape:export-xdpi");
1269 if (temp_string == NULL || xdpi != atof(temp_string)) {
1270 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1271 modified = true;
1272 }
1273 temp_string = repr->attribute("inkscape:export-ydpi");
1274 if (temp_string == NULL || xdpi != atof(temp_string)) {
1275 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1276 modified = true;
1277 }
1278 }
1279 sp_document_set_undo_sensitive(doc, saved);
1281 if (modified) {
1282 doc->setModifiedSinceSave();
1283 }
1284 break;
1285 }
1286 default:
1287 break;
1288 }
1290 g_free (filename_ext);
1292 }
1294 } // end of sp_export_export_clicked()
1296 /// Called when Browse button is clicked
1297 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1298 static void
1299 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1300 {
1301 GtkWidget *fs, *fe;
1302 const gchar *filename;
1304 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1305 (GtkWindow*)dlg,
1306 GTK_FILE_CHOOSER_ACTION_SAVE,
1307 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1308 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1309 NULL );
1311 #ifdef WITH_GNOME_VFS
1312 if (gnome_vfs_initialized()) {
1313 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1314 }
1315 #endif
1317 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1319 sp_transientize (fs);
1321 gtk_window_set_modal(GTK_WINDOW (fs), true);
1323 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1325 if (*filename == '\0') {
1326 filename = create_filepath_from_id(NULL, NULL);
1327 }
1329 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1331 #ifdef WIN32
1332 // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1333 OPENFILENAMEW opf;
1334 WCHAR* filter_string = (WCHAR*)g_utf8_to_utf16("PNG\0*.png\0\0", 12, NULL, NULL, NULL);
1335 WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1336 WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1337 // Copy the selected file name, converting from UTF-8 to UTF-16
1338 WCHAR _filename[_MAX_PATH + 1];
1339 memset(_filename, 0, sizeof(_filename));
1340 gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1341 wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1342 g_free(utf16_path_string);
1344 opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1345 opf.lpstrFilter = filter_string;
1346 opf.lpstrCustomFilter = 0;
1347 opf.nMaxCustFilter = 0L;
1348 opf.nFilterIndex = 1L;
1349 opf.lpstrFile = _filename;
1350 opf.nMaxFile = _MAX_PATH;
1351 opf.lpstrFileTitle = NULL;
1352 opf.nMaxFileTitle=0;
1353 opf.lpstrInitialDir = 0;
1354 opf.lpstrTitle = title_string;
1355 opf.nFileOffset = 0;
1356 opf.nFileExtension = 2;
1357 opf.lpstrDefExt = extension_string;
1358 opf.lpfnHook = NULL;
1359 opf.lCustData = 0;
1360 opf.Flags = OFN_PATHMUSTEXIST;
1361 opf.lStructSize = sizeof(OPENFILENAMEW);
1362 if (GetSaveFileNameW(&opf) != 0)
1363 {
1364 // Copy the selected file name, converting from UTF-16 to UTF-8
1365 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1366 gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1367 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1368 g_free(utf8string);
1370 }
1371 g_free(extension_string);
1372 g_free(title_string);
1373 g_free(filter_string);
1374 #else
1375 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1376 {
1377 gchar *file;
1379 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1381 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1382 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1384 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1386 g_free(utf8file);
1387 g_free(file);
1388 }
1389 #endif
1391 gtk_widget_destroy (fs);
1393 return;
1394 } // end of sp_export_browse_clicked()
1396 // TODO: Move this to nr-rect-fns.h.
1397 static bool
1398 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1399 {
1400 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1401 return (
1402 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1403 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1404 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1405 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1406 );
1407 }
1409 /**
1410 \brief This function is used to detect the current selection setting
1411 based on the values in the x0, y0, x1 and y0 fields.
1412 \param base The export dialog itself
1414 One of the most confusing parts of this function is why the array
1415 is built at the beginning. What needs to happen here is that we
1416 should always check the current selection to see if it is the valid
1417 one. While this is a performance improvement it is also a usability
1418 one during the cases where things like selections and drawings match
1419 size. This way buttons change less 'randomly' (atleast in the eyes
1420 of the user). To do this an array is built where the current selection
1421 type is placed first, and then the others in an order from smallest
1422 to largest (this can be configured by reshuffling \c test_order).
1424 All of the values in this function are rounded to two decimal places
1425 because that is what is shown to the user. While everything is kept
1426 more accurate than that, the user can't control more acurrate than
1427 that, so for this to work for them - it needs to check on that level
1428 of accuracy.
1430 \todo finish writing this up
1431 */
1432 static void
1433 sp_export_detect_size(GtkObject * base) {
1434 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1435 selection_type this_test[SELECTION_NUMBER_OF + 1];
1436 selection_type key = SELECTION_NUMBER_OF;
1438 Geom::Point x(sp_export_value_get_px (base, "x0"),
1439 sp_export_value_get_px (base, "y0"));
1440 Geom::Point y(sp_export_value_get_px (base, "x1"),
1441 sp_export_value_get_px (base, "y1"));
1442 Geom::Rect current_bbox(x, y);
1443 //std::cout << "Current " << current_bbox;
1445 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1446 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1447 this_test[i + 1] = test_order[i];
1448 }
1450 for (int i = 0;
1451 i < SELECTION_NUMBER_OF + 1 &&
1452 key == SELECTION_NUMBER_OF &&
1453 SP_ACTIVE_DESKTOP != NULL;
1454 i++) {
1455 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1456 switch (this_test[i]) {
1457 case SELECTION_SELECTION:
1458 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1459 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1461 //std::cout << "Selection " << bbox;
1462 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1463 key = SELECTION_SELECTION;
1464 }
1465 }
1466 break;
1467 case SELECTION_DRAWING: {
1468 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1470 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1472 // std::cout << "Drawing " << bbox2;
1473 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1474 key = SELECTION_DRAWING;
1475 }
1476 break;
1477 }
1479 case SELECTION_PAGE: {
1480 SPDocument *doc;
1482 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1484 Geom::Point x(0.0, 0.0);
1485 Geom::Point y(sp_document_width(doc),
1486 sp_document_height(doc));
1487 Geom::Rect bbox(x, y);
1489 // std::cout << "Page " << bbox;
1490 if (sp_export_bbox_equal(bbox,current_bbox)) {
1491 key = SELECTION_PAGE;
1492 }
1494 break;
1495 }
1496 default:
1497 break;
1498 }
1499 }
1500 // std::cout << std::endl;
1502 if (key == SELECTION_NUMBER_OF) {
1503 key = SELECTION_CUSTOM;
1504 }
1506 /* We're now using a custom size, not a fixed one */
1507 /* printf("Detecting state: %s\n", selection_names[key]); */
1508 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1509 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1510 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1511 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1513 return;
1514 } /* sp_export_detect_size */
1516 /// Called when area x0 value is changed
1517 static void
1518 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1519 {
1520 float x0, x1, xdpi, width;
1522 if (gtk_object_get_data (base, "update"))
1523 return;
1525 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1526 (base, "units")))
1527 {
1528 return;
1529 }
1531 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1533 x0 = sp_export_value_get_px (base, "x0");
1534 x1 = sp_export_value_get_px (base, "x1");
1535 xdpi = sp_export_value_get (base, "xdpi");
1537 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1539 if (width < SP_EXPORT_MIN_SIZE) {
1540 const gchar *key;
1541 width = SP_EXPORT_MIN_SIZE;
1542 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1544 if (!strcmp (key, "x0")) {
1545 x1 = x0 + width * DPI_BASE / xdpi;
1546 sp_export_value_set_px (base, "x1", x1);
1547 } else {
1548 x0 = x1 - width * DPI_BASE / xdpi;
1549 sp_export_value_set_px (base, "x0", x0);
1550 }
1551 }
1553 sp_export_value_set_px (base, "width", x1 - x0);
1554 sp_export_value_set (base, "bmwidth", width);
1556 sp_export_detect_size(base);
1558 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1560 return;
1561 } // end of sp_export_area_x_value_changed()
1563 /// Called when area y0 value is changed.
1564 static void
1565 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1566 {
1567 float y0, y1, ydpi, height;
1569 if (gtk_object_get_data (base, "update"))
1570 return;
1572 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1573 (base, "units")))
1574 {
1575 return;
1576 }
1578 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1580 y0 = sp_export_value_get_px (base, "y0");
1581 y1 = sp_export_value_get_px (base, "y1");
1582 ydpi = sp_export_value_get (base, "ydpi");
1584 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1586 if (height < SP_EXPORT_MIN_SIZE) {
1587 const gchar *key;
1588 height = SP_EXPORT_MIN_SIZE;
1589 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1590 if (!strcmp (key, "y0")) {
1591 y1 = y0 + height * DPI_BASE / ydpi;
1592 sp_export_value_set_px (base, "y1", y1);
1593 } else {
1594 y0 = y1 - height * DPI_BASE / ydpi;
1595 sp_export_value_set_px (base, "y0", y0);
1596 }
1597 }
1599 sp_export_value_set_px (base, "height", y1 - y0);
1600 sp_export_value_set (base, "bmheight", height);
1602 sp_export_detect_size(base);
1604 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1606 return;
1607 } // end of sp_export_area_y_value_changed()
1609 /// Called when x1-x0 or area width is changed
1610 static void
1611 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1612 {
1613 float x0, x1, xdpi, width, bmwidth;
1615 if (gtk_object_get_data (base, "update"))
1616 return;
1618 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1619 (base, "units"))) {
1620 return;
1621 }
1623 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1625 x0 = sp_export_value_get_px (base, "x0");
1626 x1 = sp_export_value_get_px (base, "x1");
1627 xdpi = sp_export_value_get (base, "xdpi");
1628 width = sp_export_value_get_px (base, "width");
1629 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1631 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1633 bmwidth = SP_EXPORT_MIN_SIZE;
1634 width = bmwidth * DPI_BASE / xdpi;
1635 sp_export_value_set_px (base, "width", width);
1636 }
1638 sp_export_value_set_px (base, "x1", x0 + width);
1639 sp_export_value_set (base, "bmwidth", bmwidth);
1641 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1643 return;
1644 } // end of sp_export_area_width_value_changed()
1646 /// Called when y1-y0 or area height is changed.
1647 static void
1648 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1649 {
1651 float y0, y1, ydpi, height, bmheight;
1653 if (gtk_object_get_data (base, "update"))
1654 return;
1656 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1657 (base, "units"))) {
1658 return;
1659 }
1661 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1663 y0 = sp_export_value_get_px (base, "y0");
1664 y1 = sp_export_value_get_px (base, "y1");
1665 ydpi = sp_export_value_get (base, "ydpi");
1666 height = sp_export_value_get_px (base, "height");
1667 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1669 if (bmheight < SP_EXPORT_MIN_SIZE) {
1670 bmheight = SP_EXPORT_MIN_SIZE;
1671 height = bmheight * DPI_BASE / ydpi;
1672 sp_export_value_set_px (base, "height", height);
1673 }
1675 sp_export_value_set_px (base, "y1", y0 + height);
1676 sp_export_value_set (base, "bmheight", bmheight);
1678 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1680 return;
1681 } // end of sp_export_area_height_value_changed()
1683 /**
1684 \brief A function to set the ydpi
1685 \param base The export dialog
1687 This function grabs all of the y values and then figures out the
1688 new bitmap size based on the changing dpi value. The dpi value is
1689 gotten from the xdpi setting as these can not currently be independent.
1690 */
1691 static void
1692 sp_export_set_image_y (GtkObject *base)
1693 {
1694 float y0, y1, xdpi;
1696 y0 = sp_export_value_get_px (base, "y0");
1697 y1 = sp_export_value_get_px (base, "y1");
1698 xdpi = sp_export_value_get (base, "xdpi");
1700 sp_export_value_set (base, "ydpi", xdpi);
1701 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1703 return;
1704 } // end of sp_export_set_image_y()
1706 /**
1707 \brief A function to set the xdpi
1708 \param base The export dialog
1710 This function grabs all of the x values and then figures out the
1711 new bitmap size based on the changing dpi value. The dpi value is
1712 gotten from the xdpi setting as these can not currently be independent.
1713 */
1714 static void
1715 sp_export_set_image_x (GtkObject *base)
1716 {
1717 float x0, x1, xdpi;
1719 x0 = sp_export_value_get_px (base, "x0");
1720 x1 = sp_export_value_get_px (base, "x1");
1721 xdpi = sp_export_value_get (base, "xdpi");
1723 sp_export_value_set (base, "ydpi", xdpi);
1724 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1726 return;
1727 } // end of sp_export_set_image_x()
1729 /// Called when pixel width is changed
1730 static void
1731 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1732 {
1733 float x0, x1, bmwidth, xdpi;
1735 if (gtk_object_get_data (base, "update"))
1736 return;
1738 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1739 (base, "units"))) {
1740 return;
1741 }
1743 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1745 x0 = sp_export_value_get_px (base, "x0");
1746 x1 = sp_export_value_get_px (base, "x1");
1747 bmwidth = sp_export_value_get (base, "bmwidth");
1749 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1750 bmwidth = SP_EXPORT_MIN_SIZE;
1751 sp_export_value_set (base, "bmwidth", bmwidth);
1752 }
1754 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1755 sp_export_value_set (base, "xdpi", xdpi);
1757 sp_export_set_image_y (base);
1759 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1761 return;
1762 } // end of sp_export_bitmap_width_value_changed()
1764 /// Called when pixel height is changed
1765 static void
1766 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1767 {
1768 float y0, y1, bmheight, xdpi;
1770 if (gtk_object_get_data (base, "update"))
1771 return;
1773 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1774 (base, "units"))) {
1775 return;
1776 }
1778 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1780 y0 = sp_export_value_get_px (base, "y0");
1781 y1 = sp_export_value_get_px (base, "y1");
1782 bmheight = sp_export_value_get (base, "bmheight");
1784 if (bmheight < SP_EXPORT_MIN_SIZE) {
1785 bmheight = SP_EXPORT_MIN_SIZE;
1786 sp_export_value_set (base, "bmheight", bmheight);
1787 }
1789 xdpi = bmheight * DPI_BASE / (y1 - y0);
1790 sp_export_value_set (base, "xdpi", xdpi);
1792 sp_export_set_image_x (base);
1794 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1796 return;
1797 } // end of sp_export_bitmap_width_value_changed()
1799 /**
1800 \brief A function to adjust the bitmap width when the xdpi value changes
1801 \param adj The adjustment that was changed
1802 \param base The export dialog itself
1804 The first thing this function checks is to see if we are doing an
1805 update. If we are, this function just returns because there is another
1806 instance of it that will handle everything for us. If there is a
1807 units change, we also assume that everyone is being updated appropriately
1808 and there is nothing for us to do.
1810 If we're the highest level function, we set the update flag, and
1811 continue on our way.
1813 All of the values are grabbed using the \c sp_export_value_get functions
1814 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1815 xdpi value is saved in the preferences for the next time the dialog
1816 is opened. (does the selection dpi need to be set here?)
1818 A check is done to to ensure that we aren't outputing an invalid width,
1819 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1820 changed to make it valid.
1822 After all of this the bitmap width is changed.
1824 We also change the ydpi. This is a temporary hack as these can not
1825 currently be independent. This is likely to change in the future.
1826 */
1827 void
1828 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1829 {
1830 float x0, x1, xdpi, bmwidth;
1832 if (gtk_object_get_data (base, "update"))
1833 return;
1835 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1836 (base, "units"))) {
1837 return;
1838 }
1840 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1842 x0 = sp_export_value_get_px (base, "x0");
1843 x1 = sp_export_value_get_px (base, "x1");
1844 xdpi = sp_export_value_get (base, "xdpi");
1846 // remember xdpi setting
1847 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1848 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1850 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1852 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1853 bmwidth = SP_EXPORT_MIN_SIZE;
1854 if (x1 != x0)
1855 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1856 else
1857 xdpi = DPI_BASE;
1858 sp_export_value_set (base, "xdpi", xdpi);
1859 }
1861 sp_export_value_set (base, "bmwidth", bmwidth);
1863 sp_export_set_image_y (base);
1865 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1867 return;
1868 } // end of sp_export_xdpi_value_changed()
1871 /**
1872 \brief A function to change the area that is used for the exported
1873 bitmap.
1874 \param base This is the export dialog
1875 \param x0 Horizontal upper left hand corner of the picture in points
1876 \param y0 Vertical upper left hand corner of the picture in points
1877 \param x1 Horizontal lower right hand corner of the picture in points
1878 \param y1 Vertical lower right hand corner of the picture in points
1880 This function just calls \c sp_export_value_set_px for each of the
1881 parameters that is passed in. This allows for setting them all in
1882 one convient area.
1884 Update is set to suspend all of the other test running while all the
1885 values are being set up. This allows for a performance increase, but
1886 it also means that the wrong type won't be detected with only some of
1887 the values set. After all the values are set everyone is told that
1888 there has been an update.
1889 */
1890 static void
1891 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1892 {
1893 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1894 sp_export_value_set_px (base, "x1", x1);
1895 sp_export_value_set_px (base, "y1", y1);
1896 sp_export_value_set_px (base, "x0", x0);
1897 sp_export_value_set_px (base, "y0", y0);
1898 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1900 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1901 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1903 return;
1904 }
1906 /**
1907 \brief Sets the value of an adjustment
1908 \param base The export dialog
1909 \param key Which adjustment to set
1910 \param val What value to set it to
1912 This function finds the adjustment using the data stored in the
1913 export dialog. After finding the adjustment it then sets
1914 the value of it.
1915 */
1916 static void
1917 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1918 {
1919 GtkAdjustment *adj;
1921 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1923 gtk_adjustment_set_value (adj, val);
1924 }
1926 /**
1927 \brief A function to set a value using the units points
1928 \param base The export dialog
1929 \param key Which value should be set
1930 \param val What the value should be in points
1932 This function first gets the adjustment for the key that is passed
1933 in. It then figures out what units are currently being used in the
1934 dialog. After doing all of that, it then converts the incoming
1935 value and sets the adjustment.
1936 */
1937 static void
1938 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1939 {
1940 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1942 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1944 return;
1945 }
1947 /**
1948 \brief Get the value of an adjustment in the export dialog
1949 \param base The export dialog
1950 \param key Which adjustment is being looked for
1951 \return The value in the specified adjustment
1953 This function gets the adjustment from the data field in the export
1954 dialog. It then grabs the value from the adjustment.
1955 */
1956 static float
1957 sp_export_value_get ( GtkObject *base, const gchar *key )
1958 {
1959 GtkAdjustment *adj;
1961 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1963 return adj->value;
1964 }
1966 /**
1967 \brief Grabs a value in the export dialog and converts the unit
1968 to points
1969 \param base The export dialog
1970 \param key Which value should be returned
1971 \return The value in the adjustment in points
1973 This function, at its most basic, is a call to \c sp_export_value_get
1974 to get the value of the adjustment. It then finds the units that
1975 are being used by looking at the "units" attribute of the export
1976 dialog. Using that it converts the returned value into points.
1977 */
1978 static float
1979 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1980 {
1981 float value = sp_export_value_get(base, key);
1982 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1984 return sp_units_get_pixels (value, *unit);
1985 } // end of sp_export_value_get_px()
1987 /**
1988 \brief This function is called when the filename is changed by
1989 anyone. It resets the virgin bit.
1990 \param object Text entry box
1991 \param data The export dialog
1992 \return None
1994 This function gets called when the text area is modified. It is
1995 looking for the case where the text area is modified from its
1996 original value. In that case it sets the "filename-modified" bit
1997 to TRUE. If the text dialog returns back to the original text, the
1998 bit gets reset. This should stop simple mistakes.
1999 */
2000 static void
2001 sp_export_filename_modified (GtkObject * object, gpointer data)
2002 {
2003 GtkWidget * text_entry = (GtkWidget *)object;
2004 GtkWidget * export_dialog = (GtkWidget *)data;
2006 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2007 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2008 // printf("Modified: FALSE\n");
2009 } else {
2010 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2011 // printf("Modified: TRUE\n");
2012 }
2014 return;
2015 } // end sp_export_filename_modified
2017 /*
2018 Local Variables:
2019 mode:c++
2020 c-file-style:"stroustrup"
2021 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2022 indent-tabs-mode:nil
2023 fill-column:99
2024 End:
2025 */
2026 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :