Code

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