adadcd2f65ab13fc7ab86992228779e3c7a5f628
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 = (WCHAR*)g_utf8_to_utf16("PNG\0*.png\0", 10, NULL, NULL, NULL);
1362 WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1363 WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1364 // Copy the selected file name, converting from UTF-8 to UTF-16
1365 WCHAR _filename[_MAX_PATH + 1];
1366 memset(_filename, 0, sizeof(_filename));
1367 gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1368 wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1369 g_free(utf16_path_string);
1371 opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1372 opf.lpstrFilter = filter_string;
1373 opf.lpstrCustomFilter = 0;
1374 opf.nMaxCustFilter = 0L;
1375 opf.nFilterIndex = 1L;
1376 opf.lpstrFile = _filename;
1377 opf.nMaxFile = _MAX_PATH;
1378 opf.lpstrFileTitle = NULL;
1379 opf.nMaxFileTitle=0;
1380 opf.lpstrInitialDir = 0;
1381 opf.lpstrTitle = title_string;
1382 opf.nFileOffset = 0;
1383 opf.nFileExtension = 2;
1384 opf.lpstrDefExt = extension_string;
1385 opf.lpfnHook = NULL;
1386 opf.lCustData = 0;
1387 opf.Flags = OFN_PATHMUSTEXIST;
1388 opf.lStructSize = sizeof(OPENFILENAMEW);
1389 if (GetSaveFileNameW(&opf) != 0)
1390 {
1391 // Copy the selected file name, converting from UTF-16 to UTF-8
1392 gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1393 gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1394 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1395 g_free(utf8string);
1397 }
1398 g_free(extension_string);
1399 g_free(title_string);
1400 g_free(filter_string);
1401 #else
1402 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1403 {
1404 gchar *file;
1406 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1408 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1409 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1411 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1413 g_free(utf8file);
1414 g_free(file);
1415 }
1416 #endif
1418 gtk_widget_destroy (fs);
1420 return;
1421 } // end of sp_export_browse_clicked()
1423 // TODO: Move this to nr-rect-fns.h.
1424 static bool
1425 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1426 {
1427 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1428 return (
1429 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1430 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1431 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1432 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1433 );
1434 }
1436 /**
1437 \brief This function is used to detect the current selection setting
1438 based on the values in the x0, y0, x1 and y0 fields.
1439 \param base The export dialog itself
1441 One of the most confusing parts of this function is why the array
1442 is built at the beginning. What needs to happen here is that we
1443 should always check the current selection to see if it is the valid
1444 one. While this is a performance improvement it is also a usability
1445 one during the cases where things like selections and drawings match
1446 size. This way buttons change less 'randomly' (atleast in the eyes
1447 of the user). To do this an array is built where the current selection
1448 type is placed first, and then the others in an order from smallest
1449 to largest (this can be configured by reshuffling \c test_order).
1451 All of the values in this function are rounded to two decimal places
1452 because that is what is shown to the user. While everything is kept
1453 more accurate than that, the user can't control more acurrate than
1454 that, so for this to work for them - it needs to check on that level
1455 of accuracy.
1457 \todo finish writing this up
1458 */
1459 static void
1460 sp_export_detect_size(GtkObject * base) {
1461 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1462 selection_type this_test[SELECTION_NUMBER_OF + 1];
1463 selection_type key = SELECTION_NUMBER_OF;
1465 Geom::Point x(sp_export_value_get_px (base, "x0"),
1466 sp_export_value_get_px (base, "y0"));
1467 Geom::Point y(sp_export_value_get_px (base, "x1"),
1468 sp_export_value_get_px (base, "y1"));
1469 Geom::Rect current_bbox(x, y);
1470 //std::cout << "Current " << current_bbox;
1472 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1473 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1474 this_test[i + 1] = test_order[i];
1475 }
1477 for (int i = 0;
1478 i < SELECTION_NUMBER_OF + 1 &&
1479 key == SELECTION_NUMBER_OF &&
1480 SP_ACTIVE_DESKTOP != NULL;
1481 i++) {
1482 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1483 switch (this_test[i]) {
1484 case SELECTION_SELECTION:
1485 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1486 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1488 //std::cout << "Selection " << bbox;
1489 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1490 key = SELECTION_SELECTION;
1491 }
1492 }
1493 break;
1494 case SELECTION_DRAWING: {
1495 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1497 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1499 // std::cout << "Drawing " << bbox2;
1500 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1501 key = SELECTION_DRAWING;
1502 }
1503 break;
1504 }
1506 case SELECTION_PAGE: {
1507 SPDocument *doc;
1509 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1511 Geom::Point x(0.0, 0.0);
1512 Geom::Point y(sp_document_width(doc),
1513 sp_document_height(doc));
1514 Geom::Rect bbox(x, y);
1516 // std::cout << "Page " << bbox;
1517 if (sp_export_bbox_equal(bbox,current_bbox)) {
1518 key = SELECTION_PAGE;
1519 }
1521 break;
1522 }
1523 default:
1524 break;
1525 }
1526 }
1527 // std::cout << std::endl;
1529 if (key == SELECTION_NUMBER_OF) {
1530 key = SELECTION_CUSTOM;
1531 }
1533 /* We're now using a custom size, not a fixed one */
1534 /* printf("Detecting state: %s\n", selection_names[key]); */
1535 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1536 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1537 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1538 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1540 return;
1541 } /* sp_export_detect_size */
1543 /// Called when area x0 value is changed
1544 static void
1545 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1546 {
1547 float x0, x1, xdpi, width;
1549 if (gtk_object_get_data (base, "update"))
1550 return;
1552 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1553 (base, "units")))
1554 {
1555 return;
1556 }
1558 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1560 x0 = sp_export_value_get_px (base, "x0");
1561 x1 = sp_export_value_get_px (base, "x1");
1562 xdpi = sp_export_value_get (base, "xdpi");
1564 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1566 if (width < SP_EXPORT_MIN_SIZE) {
1567 const gchar *key;
1568 width = SP_EXPORT_MIN_SIZE;
1569 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1571 if (!strcmp (key, "x0")) {
1572 x1 = x0 + width * DPI_BASE / xdpi;
1573 sp_export_value_set_px (base, "x1", x1);
1574 } else {
1575 x0 = x1 - width * DPI_BASE / xdpi;
1576 sp_export_value_set_px (base, "x0", x0);
1577 }
1578 }
1580 sp_export_value_set_px (base, "width", x1 - x0);
1581 sp_export_value_set (base, "bmwidth", width);
1583 sp_export_detect_size(base);
1585 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1587 return;
1588 } // end of sp_export_area_x_value_changed()
1590 /// Called when area y0 value is changed.
1591 static void
1592 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1593 {
1594 float y0, y1, ydpi, height;
1596 if (gtk_object_get_data (base, "update"))
1597 return;
1599 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1600 (base, "units")))
1601 {
1602 return;
1603 }
1605 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1607 y0 = sp_export_value_get_px (base, "y0");
1608 y1 = sp_export_value_get_px (base, "y1");
1609 ydpi = sp_export_value_get (base, "ydpi");
1611 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1613 if (height < SP_EXPORT_MIN_SIZE) {
1614 const gchar *key;
1615 height = SP_EXPORT_MIN_SIZE;
1616 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1617 if (!strcmp (key, "y0")) {
1618 y1 = y0 + height * DPI_BASE / ydpi;
1619 sp_export_value_set_px (base, "y1", y1);
1620 } else {
1621 y0 = y1 - height * DPI_BASE / ydpi;
1622 sp_export_value_set_px (base, "y0", y0);
1623 }
1624 }
1626 sp_export_value_set_px (base, "height", y1 - y0);
1627 sp_export_value_set (base, "bmheight", height);
1629 sp_export_detect_size(base);
1631 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1633 return;
1634 } // end of sp_export_area_y_value_changed()
1636 /// Called when x1-x0 or area width is changed
1637 static void
1638 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1639 {
1640 float x0, x1, xdpi, width, bmwidth;
1642 if (gtk_object_get_data (base, "update"))
1643 return;
1645 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1646 (base, "units"))) {
1647 return;
1648 }
1650 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1652 x0 = sp_export_value_get_px (base, "x0");
1653 x1 = sp_export_value_get_px (base, "x1");
1654 xdpi = sp_export_value_get (base, "xdpi");
1655 width = sp_export_value_get_px (base, "width");
1656 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1658 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1660 bmwidth = SP_EXPORT_MIN_SIZE;
1661 width = bmwidth * DPI_BASE / xdpi;
1662 sp_export_value_set_px (base, "width", width);
1663 }
1665 sp_export_value_set_px (base, "x1", x0 + width);
1666 sp_export_value_set (base, "bmwidth", bmwidth);
1668 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1670 return;
1671 } // end of sp_export_area_width_value_changed()
1673 /// Called when y1-y0 or area height is changed.
1674 static void
1675 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1676 {
1678 float y0, y1, ydpi, height, bmheight;
1680 if (gtk_object_get_data (base, "update"))
1681 return;
1683 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1684 (base, "units"))) {
1685 return;
1686 }
1688 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1690 y0 = sp_export_value_get_px (base, "y0");
1691 y1 = sp_export_value_get_px (base, "y1");
1692 ydpi = sp_export_value_get (base, "ydpi");
1693 height = sp_export_value_get_px (base, "height");
1694 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1696 if (bmheight < SP_EXPORT_MIN_SIZE) {
1697 bmheight = SP_EXPORT_MIN_SIZE;
1698 height = bmheight * DPI_BASE / ydpi;
1699 sp_export_value_set_px (base, "height", height);
1700 }
1702 sp_export_value_set_px (base, "y1", y0 + height);
1703 sp_export_value_set (base, "bmheight", bmheight);
1705 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1707 return;
1708 } // end of sp_export_area_height_value_changed()
1710 /**
1711 \brief A function to set the ydpi
1712 \param base The export dialog
1714 This function grabs all of the y values and then figures out the
1715 new bitmap size based on the changing dpi value. The dpi value is
1716 gotten from the xdpi setting as these can not currently be independent.
1717 */
1718 static void
1719 sp_export_set_image_y (GtkObject *base)
1720 {
1721 float y0, y1, xdpi;
1723 y0 = sp_export_value_get_px (base, "y0");
1724 y1 = sp_export_value_get_px (base, "y1");
1725 xdpi = sp_export_value_get (base, "xdpi");
1727 sp_export_value_set (base, "ydpi", xdpi);
1728 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1730 return;
1731 } // end of sp_export_set_image_y()
1733 /**
1734 \brief A function to set the xdpi
1735 \param base The export dialog
1737 This function grabs all of the x values and then figures out the
1738 new bitmap size based on the changing dpi value. The dpi value is
1739 gotten from the xdpi setting as these can not currently be independent.
1740 */
1741 static void
1742 sp_export_set_image_x (GtkObject *base)
1743 {
1744 float x0, x1, xdpi;
1746 x0 = sp_export_value_get_px (base, "x0");
1747 x1 = sp_export_value_get_px (base, "x1");
1748 xdpi = sp_export_value_get (base, "xdpi");
1750 sp_export_value_set (base, "ydpi", xdpi);
1751 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1753 return;
1754 } // end of sp_export_set_image_x()
1756 /// Called when pixel width is changed
1757 static void
1758 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1759 {
1760 float x0, x1, bmwidth, xdpi;
1762 if (gtk_object_get_data (base, "update"))
1763 return;
1765 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1766 (base, "units"))) {
1767 return;
1768 }
1770 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1772 x0 = sp_export_value_get_px (base, "x0");
1773 x1 = sp_export_value_get_px (base, "x1");
1774 bmwidth = sp_export_value_get (base, "bmwidth");
1776 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1777 bmwidth = SP_EXPORT_MIN_SIZE;
1778 sp_export_value_set (base, "bmwidth", bmwidth);
1779 }
1781 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1782 sp_export_value_set (base, "xdpi", xdpi);
1784 sp_export_set_image_y (base);
1786 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1788 return;
1789 } // end of sp_export_bitmap_width_value_changed()
1791 /// Called when pixel height is changed
1792 static void
1793 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1794 {
1795 float y0, y1, bmheight, xdpi;
1797 if (gtk_object_get_data (base, "update"))
1798 return;
1800 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1801 (base, "units"))) {
1802 return;
1803 }
1805 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1807 y0 = sp_export_value_get_px (base, "y0");
1808 y1 = sp_export_value_get_px (base, "y1");
1809 bmheight = sp_export_value_get (base, "bmheight");
1811 if (bmheight < SP_EXPORT_MIN_SIZE) {
1812 bmheight = SP_EXPORT_MIN_SIZE;
1813 sp_export_value_set (base, "bmheight", bmheight);
1814 }
1816 xdpi = bmheight * DPI_BASE / (y1 - y0);
1817 sp_export_value_set (base, "xdpi", xdpi);
1819 sp_export_set_image_x (base);
1821 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1823 return;
1824 } // end of sp_export_bitmap_width_value_changed()
1826 /**
1827 \brief A function to adjust the bitmap width when the xdpi value changes
1828 \param adj The adjustment that was changed
1829 \param base The export dialog itself
1831 The first thing this function checks is to see if we are doing an
1832 update. If we are, this function just returns because there is another
1833 instance of it that will handle everything for us. If there is a
1834 units change, we also assume that everyone is being updated appropriately
1835 and there is nothing for us to do.
1837 If we're the highest level function, we set the update flag, and
1838 continue on our way.
1840 All of the values are grabbed using the \c sp_export_value_get functions
1841 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1842 xdpi value is saved in the preferences for the next time the dialog
1843 is opened. (does the selection dpi need to be set here?)
1845 A check is done to to ensure that we aren't outputing an invalid width,
1846 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1847 changed to make it valid.
1849 After all of this the bitmap width is changed.
1851 We also change the ydpi. This is a temporary hack as these can not
1852 currently be independent. This is likely to change in the future.
1853 */
1854 void
1855 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1856 {
1857 float x0, x1, xdpi, bmwidth;
1859 if (gtk_object_get_data (base, "update"))
1860 return;
1862 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1863 (base, "units"))) {
1864 return;
1865 }
1867 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1869 x0 = sp_export_value_get_px (base, "x0");
1870 x1 = sp_export_value_get_px (base, "x1");
1871 xdpi = sp_export_value_get (base, "xdpi");
1873 // remember xdpi setting
1874 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1875 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1877 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1879 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1880 bmwidth = SP_EXPORT_MIN_SIZE;
1881 if (x1 != x0)
1882 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1883 else
1884 xdpi = DPI_BASE;
1885 sp_export_value_set (base, "xdpi", xdpi);
1886 }
1888 sp_export_value_set (base, "bmwidth", bmwidth);
1890 sp_export_set_image_y (base);
1892 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1894 return;
1895 } // end of sp_export_xdpi_value_changed()
1898 /**
1899 \brief A function to change the area that is used for the exported
1900 bitmap.
1901 \param base This is the export dialog
1902 \param x0 Horizontal upper left hand corner of the picture in points
1903 \param y0 Vertical upper left hand corner of the picture in points
1904 \param x1 Horizontal lower right hand corner of the picture in points
1905 \param y1 Vertical lower right hand corner of the picture in points
1907 This function just calls \c sp_export_value_set_px for each of the
1908 parameters that is passed in. This allows for setting them all in
1909 one convient area.
1911 Update is set to suspend all of the other test running while all the
1912 values are being set up. This allows for a performance increase, but
1913 it also means that the wrong type won't be detected with only some of
1914 the values set. After all the values are set everyone is told that
1915 there has been an update.
1916 */
1917 static void
1918 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1919 {
1920 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1921 sp_export_value_set_px (base, "x1", x1);
1922 sp_export_value_set_px (base, "y1", y1);
1923 sp_export_value_set_px (base, "x0", x0);
1924 sp_export_value_set_px (base, "y0", y0);
1925 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1927 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1928 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1930 return;
1931 }
1933 /**
1934 \brief Sets the value of an adjustment
1935 \param base The export dialog
1936 \param key Which adjustment to set
1937 \param val What value to set it to
1939 This function finds the adjustment using the data stored in the
1940 export dialog. After finding the adjustment it then sets
1941 the value of it.
1942 */
1943 static void
1944 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1945 {
1946 GtkAdjustment *adj;
1948 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1950 gtk_adjustment_set_value (adj, val);
1951 }
1953 /**
1954 \brief A function to set a value using the units points
1955 \param base The export dialog
1956 \param key Which value should be set
1957 \param val What the value should be in points
1959 This function first gets the adjustment for the key that is passed
1960 in. It then figures out what units are currently being used in the
1961 dialog. After doing all of that, it then converts the incoming
1962 value and sets the adjustment.
1963 */
1964 static void
1965 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1966 {
1967 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1969 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1971 return;
1972 }
1974 /**
1975 \brief Get the value of an adjustment in the export dialog
1976 \param base The export dialog
1977 \param key Which adjustment is being looked for
1978 \return The value in the specified adjustment
1980 This function gets the adjustment from the data field in the export
1981 dialog. It then grabs the value from the adjustment.
1982 */
1983 static float
1984 sp_export_value_get ( GtkObject *base, const gchar *key )
1985 {
1986 GtkAdjustment *adj;
1988 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1990 return adj->value;
1991 }
1993 /**
1994 \brief Grabs a value in the export dialog and converts the unit
1995 to points
1996 \param base The export dialog
1997 \param key Which value should be returned
1998 \return The value in the adjustment in points
2000 This function, at its most basic, is a call to \c sp_export_value_get
2001 to get the value of the adjustment. It then finds the units that
2002 are being used by looking at the "units" attribute of the export
2003 dialog. Using that it converts the returned value into points.
2004 */
2005 static float
2006 sp_export_value_get_px ( GtkObject *base, const gchar *key )
2007 {
2008 float value = sp_export_value_get(base, key);
2009 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
2011 return sp_units_get_pixels (value, *unit);
2012 } // end of sp_export_value_get_px()
2014 /**
2015 \brief This function is called when the filename is changed by
2016 anyone. It resets the virgin bit.
2017 \param object Text entry box
2018 \param data The export dialog
2019 \return None
2021 This function gets called when the text area is modified. It is
2022 looking for the case where the text area is modified from its
2023 original value. In that case it sets the "filename-modified" bit
2024 to TRUE. If the text dialog returns back to the original text, the
2025 bit gets reset. This should stop simple mistakes.
2026 */
2027 static void
2028 sp_export_filename_modified (GtkObject * object, gpointer data)
2029 {
2030 GtkWidget * text_entry = (GtkWidget *)object;
2031 GtkWidget * export_dialog = (GtkWidget *)data;
2033 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2034 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2035 // printf("Modified: FALSE\n");
2036 } else {
2037 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2038 // printf("Modified: TRUE\n");
2039 }
2041 return;
2042 } // end sp_export_filename_modified
2044 /*
2045 Local Variables:
2046 mode:c++
2047 c-file-style:"stroustrup"
2048 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2049 indent-tabs-mode:nil
2050 fill-column:99
2051 End:
2052 */
2053 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :