Code

Refactored preferences handling into a new version of
[inkscape.git] / src / dialogs / export.cpp
1 #define __SP_EXPORT_C__
3 /** \file
4  * \brief  PNG export dialog
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   bulia byak <buliabyak@users.sf.net>
11  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
12  *
13  * Copyright (C) 1999-2007 Authors
14  * Copyright (C) 2001-2002 Ximian, Inc.
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/buttonbox.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/togglebutton.h>
29 #include <gtkmm/entry.h>
30 #include <gtkmm/image.h>
31 #include <gtkmm/stockid.h>
32 #include <gtkmm/stock.h>
33 #ifdef WITH_GNOME_VFS
34 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
35 #endif
37 #include <glibmm/i18n.h>
38 #include "helper/unit-menu.h"
39 #include "helper/units.h"
40 #include "unit-constants.h"
41 #include "helper/window.h"
42 #include "inkscape-private.h"
43 #include "document.h"
44 #include "desktop-handles.h"
45 #include "sp-item.h"
46 #include "selection.h"
47 #include "file.h"
48 #include "macros.h"
49 #include "sp-namedview.h"
50 #include "selection-chemistry.h"
52 #include "dialog-events.h"
53 #include "preferences.h"
54 #include "verbs.h"
55 #include "interface.h"
57 #include "extension/output.h"
58 #include "extension/db.h"
60 #include "io/sys.h"
62 #include "helper/png-write.h"
63 #include <png.h>
66 #define SP_EXPORT_MIN_SIZE 1.0
68 #define DPI_BASE PX_PER_IN
70 #define EXPORT_COORD_PRECISION 3
72 #define MIN_ONSCREEN_DISTANCE 50
74 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
75 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
76 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
78 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj,
79                                                    GtkObject *base);
81 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj,
82                                                    GtkObject *base);
84 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj,
85                                                    GtkObject *base);
87 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj,
88                                                    GtkObject *base);
90 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
91                                                    GtkObject *base);
93 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
94                                                    GtkObject *base);
96 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj,
97                                                    GtkObject *base);
99 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
100                                           Inkscape::Selection *selection,
101                                           GtkObject *base);
102 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
103                                            Inkscape::Selection *selection,
104                                            guint flags,
105                                            GtkObject *base );
107 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
108 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
109 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
110 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
111 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
113 static void sp_export_filename_modified (GtkObject * object, gpointer data);
114 static inline void sp_export_find_default_selection(GtkWidget * dlg);
115 static void sp_export_detect_size(GtkObject * base);
117 static const gchar *prefs_path = "dialogs.export";
119 // these all need to be reinitialized to their defaults during dialog_destroy
120 static GtkWidget *dlg = NULL;
121 static win_data wd;
122 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
123 static gchar * original_name = NULL;
124 static gchar * doc_export_name = NULL;
125 static bool was_empty = TRUE;
127 /** What type of button is being pressed */
128 enum selection_type {
129     SELECTION_PAGE = 0,  /**< Export the whole page */
130     SELECTION_DRAWING,   /**< Export everything drawn on the page */
131     SELECTION_SELECTION, /**< Export everything that is selected */
132     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
133     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
134 };
136 /** A list of strings that is used both in the preferences, and in the
137     data fields to describe the various values of \c selection_type. */
138 static const char * selection_names[SELECTION_NUMBER_OF] = {
139     "page", "drawing", "selection", "custom"};
141 /** The names on the buttons for the various selection types. */
142 static const char * selection_labels[SELECTION_NUMBER_OF] = {
143     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
145 static void
146 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
148     sp_signal_disconnect_by_data (INKSCAPE, dlg);
150     wd.win = dlg = NULL;
151     wd.stop = 0;
152     x = -1000; y = -1000; w = 0; h = 0;
153     g_free(original_name);
154     original_name = NULL;
155     g_free(doc_export_name);
156     doc_export_name = NULL;
157     was_empty = TRUE;
159     return;
160 } // end of sp_export_dialog_destroy()
162 /// Called when dialog is closed or inkscape is shut down.
163 static bool
164 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
167     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
168     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
170     if (x<0) x=0;
171     if (y<0) y=0;
172     
173     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174     prefs->setInt(prefs_path, "x", x);
175     prefs->setInt(prefs_path, "y", y);
176     prefs->setInt(prefs_path, "w", w);
177     prefs->setInt(prefs_path, "h", h);
179     return FALSE; // which means, go ahead and destroy it
181 } // end of sp_export_dialog_delete()
183 /**
184     \brief  Creates a new spin button for the export dialog
185     \param  key  The name of the spin button
186     \param  val  A default value for the spin button
187     \param  min  Minimum value for the spin button
188     \param  max  Maximum value for the spin button
189     \param  step The step size for the spin button
190     \param  page Size of the page increment
191     \param  us   Unit selector that effects this spin button
192     \param  t    Table to put the spin button in
193     \param  x    X location in the table \c t to start with
194     \param  y    Y location in the table \c t to start with
195     \param  ll   Text to put on the left side of the spin button (optional)
196     \param  lr   Text to put on the right side of the spin button (optional)
197     \param  digits  Number of digits to display after the decimal
198     \param  sensitive  Whether the spin button is sensitive or not
199     \param  cb   Callback for when this spin button is changed (optional)
200     \param  dlg  Export dialog the spin button is being placed in
202 */
203 static void
204 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
205                            float step, float page, GtkWidget *us,
206                            GtkWidget *t, int x, int y,
207                            const gchar *ll, const gchar *lr,
208                            int digits, unsigned int sensitive,
209                            GCallback cb, GtkWidget *dlg )
211     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
212     gtk_object_set_data (a, "key", const_cast<gchar *>(key));
213     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
215     if (us) {
216         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
217                                           GTK_ADJUSTMENT (a) );
218     }
220     int pos = 0;
222     GtkWidget *l = NULL;
224     if (ll) {
226         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
227         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
228         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
229                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
230         gtk_widget_set_sensitive (l, sensitive);
231         pos += 1;
233     }
235     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
236     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
237                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
238     gtk_widget_set_size_request (sb, 80, -1);
239     gtk_widget_set_sensitive (sb, sensitive);
240     pos += 1;
242     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
244     if (lr) {
246         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
247         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
248         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
249                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
250         gtk_widget_set_sensitive (l, sensitive);
251         pos += 1;
253         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
254     }
256     if (cb)
257         gtk_signal_connect (a, "value_changed", cb, dlg);
259     return;
260 } // end of sp_export_spinbutton_new()
263 static Gtk::VBox *
264 sp_export_dialog_area_box (GtkWidget * dlg)
266     Gtk::VBox* vb = new Gtk::VBox(false, 3);
268     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
269     lbl->set_use_markup(true);
270     vb->pack_start(*lbl);
272     /* Units box */
273     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
274     /* gets added to the vbox later, but the unit selector is needed
275        earlier than that */
277     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
278     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
279     if (desktop)
280         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
281     unitbox->pack_end(*us, false, false, 0);
282     Gtk::Label* l = new Gtk::Label(_("Units:"));
283     unitbox->pack_end(*l, false, false, 3);
284     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
286     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
288     Gtk::ToggleButton* b;
289     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
290         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
291         b->set_data("key", GINT_TO_POINTER(i));
292         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
293         togglebox->pack_start(*b, false, true, 0);
294         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
295                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
296     }
298     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
299                        G_CALLBACK (sp_export_selection_changed), dlg );
300     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
301                        G_CALLBACK (sp_export_selection_modified), dlg );
302     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
303                        G_CALLBACK (sp_export_selection_changed), dlg );
305     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
306     t->set_row_spacings (4);
307     t->set_col_spacings (4);
309     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
310                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
311                                G_CALLBACK ( sp_export_area_x_value_changed),
312                                dlg );
314     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
315                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
316                                G_CALLBACK (sp_export_area_x_value_changed),
317                                dlg );
319     sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
320                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
321                                G_CALLBACK
322                                    (sp_export_area_width_value_changed),
323                                dlg );
325     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
326                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
327                                G_CALLBACK (sp_export_area_y_value_changed),
328                                dlg );
330     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
331                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
332                                G_CALLBACK (sp_export_area_y_value_changed),
333                                dlg );
335     sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
336                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
337                                G_CALLBACK (sp_export_area_height_value_changed),
338                                dlg );
340     vb->pack_start(*togglebox, false, false, 3);
341     vb->pack_start(*t, false, false, 0);
342     vb->pack_start(*unitbox, false, false, 0);
344     return vb;
345 } // end of sp_export_dialog_area_box
348 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
350     if (id == NULL) /* This should never happen */
351         id = "bitmap";
353     gchar * directory = NULL;
355     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
356         // std::cout << "Directory from dialog" << std::endl;
357         directory = g_dirname(file_entry_text);
358     }
360     if (directory == NULL) {
361         /* Grab document directory */
362         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
363             // std::cout << "Directory from document" << std::endl;
364             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
365         }
366     }
368     if (directory == NULL) {
369         // std::cout << "Home Directory" << std::endl;
370         directory = homedir_path(NULL);
371     }
373     gchar * id_ext = g_strconcat(id, ".png", NULL);
374     gchar *filename = g_build_filename(directory, id_ext, NULL);
375     g_free(directory);
376     g_free(id_ext);
377     return filename;
380 static void
381 batch_export_clicked (GtkWidget *widget, GtkObject *base)
383     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
384     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
385         vb_singleexport->set_sensitive(false);
386     } else {
387         vb_singleexport->set_sensitive(true);
388     }
391 void
392 sp_export_dialog (void)
394     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
395     if (!dlg) {
397         gchar title[500];
398         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
400         dlg = sp_window_new (title, TRUE);
402         if (x == -1000 || y == -1000) {
403             x = prefs->getInt(prefs_path, "x", 0);
404             y = prefs->getInt(prefs_path, "y", 0);
405         }
407         if (w ==0 || h == 0) {
408             w = prefs->getInt(prefs_path, "w", 0);
409             h = prefs->getInt(prefs_path, "h", 0);
410         }
412 //        if (x<0) x=0;
413 //        if (y<0) y=0;
415         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
416         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
417             gtk_window_move ((GtkWindow *) dlg, x, y);
418         else
419             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
420         sp_transientize (dlg);
421         wd.win = dlg;
422         wd.stop = 0;
424         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
425                              G_CALLBACK (sp_transientize_callback), &wd);
427         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
428                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
430         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
431                              G_CALLBACK (sp_export_dialog_destroy), dlg);
433         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
434                              G_CALLBACK (sp_export_dialog_delete), dlg);
436         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
437                              G_CALLBACK (sp_export_dialog_delete), dlg);
439         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
440                              G_CALLBACK (sp_dialog_hide), dlg);
442         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
443                              G_CALLBACK (sp_dialog_unhide), dlg);
445         GtkTooltips *tt = gtk_tooltips_new();
447         Gtk::VBox *vb = new Gtk::VBox(false, 3);
448         vb->set_border_width(3);
449         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
451         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
452         vb_singleexport->set_border_width(0);
453         vb->pack_start(*vb_singleexport);
454         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
456         /* Export area frame */
457         {
458             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
459             area_box->set_border_width(3);
460             vb_singleexport->pack_start(*area_box, false, false, 0);
461         }
463         /* Bitmap size frame */
464         {
465             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
466             size_box->set_border_width(3);
468             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
469             lbl->set_use_markup(true);
470             size_box->pack_start(*lbl, false, false, 0);
471             const int rows = 2;
472             const int cols = 5;
473             const bool homogeneous = false;
474             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
475             t->set_row_spacings (4);
476             t->set_col_spacings (4);
477             size_box->pack_start(*t);
479             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
480                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
481                                        _("_Width:"), _("pixels at"), 0, 1,
482                                        G_CALLBACK
483                                        (sp_export_bitmap_width_value_changed),
484                                        dlg );
486             sp_export_spinbutton_new ( "xdpi",
487                                        prefs->getDouble("dialogs.export.defaultxdpi", "value", DPI_BASE),
488                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
489                                        NULL, _("dp_i"), 2, 1,
490                                        G_CALLBACK (sp_export_xdpi_value_changed),
491                                        dlg );
493             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
494                                        NULL, GTK_WIDGET(t->gobj()), 0, 1,
495                                        _("Height:"), _("pixels at"), 0, 1,
496                                        G_CALLBACK
497                                        (sp_export_bitmap_height_value_changed),
498                                        dlg );
500             /** \todo
501              * Needs fixing: there's no way to set ydpi currently, so we use
502              *       the defaultxdpi value here, too...
503              */
504             sp_export_spinbutton_new ( "ydpi", prefs->getDouble("dialogs.export.defaultxdpi", "value", DPI_BASE),
505                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
506                                        NULL, _("dpi"), 2, 0, NULL, dlg );
508             vb_singleexport->pack_start(*size_box);
509         }
511         /* File entry */
512         {
513             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
514             file_box->set_border_width(3);
516             // true = has mnemonic
517             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
518             flabel->set_use_markup(true);
519             file_box->pack_start(*flabel, false, false, 0);
521             Gtk::Entry *fe = new Gtk::Entry();
523             /*
524              * set the default filename to be that of the current path + document
525              * with .png extension
526              *
527              * One thing to notice here is that this filename may get
528              * overwritten, but it won't happen here.  The filename gets
529              * written into the text field, but then the button to select
530              * the area gets set.  In that code the filename can be changed
531              * if there are some with presidence in the document.  So, while
532              * this code sets the name first, it may not be the one users
533              * really see.
534              */
535             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
536             {
537                 gchar *name;
538                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
539                 const gchar *uri = SP_DOCUMENT_URI (doc);
540                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
541                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
542                 Inkscape::Extension::Output * oextension = NULL;
544                 if (text_extension != NULL) {
545                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
546                 }
548                 if (oextension != NULL) {
549                     gchar * old_extension = oextension->get_extension();
550                     if (g_str_has_suffix(uri, old_extension)) {
551                         gchar * uri_copy;
552                         gchar * extension_point;
553                         gchar * final_name;
555                         uri_copy = g_strdup(uri);
556                         extension_point = g_strrstr(uri_copy, old_extension);
557                         extension_point[0] = '\0';
559                         final_name = g_strconcat(uri_copy, ".png", NULL);
560                         fe->set_text(final_name);
562                         g_free(final_name);
563                         g_free(uri_copy);
564                     }
565                 } else {
566                     name = g_strconcat(uri, ".png", NULL);
567                     fe->set_text(name);
568                     g_free(name);
569                 }
571                 doc_export_name = g_strdup(fe->get_text().c_str());
572             }
573             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
574                                G_CALLBACK (sp_export_filename_modified), dlg);
576             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
578             {
579                 // true = has mnemonic
580                 Gtk::Button *b = new Gtk::Button();
582                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
583                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
584                         Gtk::ICON_SIZE_BUTTON);
585                 pixlabel->pack_start(*im);
587                 Gtk::Label *l = new Gtk::Label();
588                 l->set_markup_with_mnemonic(_("_Browse..."));
589                 pixlabel->pack_start(*l);
591                 b->add(*pixlabel);
593                 hb->pack_end (*b, false, false, 4);
594                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
595                                    G_CALLBACK (sp_export_browse_clicked), NULL );
596             }
598             hb->pack_start (*fe, true, true, 0);
599             file_box->add(*hb);
600             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
601             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
602             original_name = g_strdup(fe->get_text().c_str());
603             // pressing enter in the filename field is the same as clicking export:
604             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
605                                G_CALLBACK (sp_export_export_clicked), dlg );
606             // focus is in the filename initially:
607             fe->grab_focus();
609             // mnemonic in frame label moves focus to filename:
610             flabel->set_mnemonic_widget(*fe);
612             vb_singleexport->pack_start(*file_box);
613         }
615         {
616             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
617             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
618             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
619             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
620             batch_box->pack_start(*Glib::wrap(be), false, false);
621             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);
622             batch_box->show_all();
623             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
624             vb->pack_start(*batch_box);
625         }
627         {
628             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
629             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
630             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
631             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
632             hide_box->pack_start(*Glib::wrap(he), false, false);
633             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
634             hide_box->show_all();
635             vb->pack_start(*hide_box);
636         }
638         /* Buttons */
639         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
640         bb->set_border_width(3);
642         {
643             Gtk::Button *b = new Gtk::Button();
644             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
645             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
646                     Gtk::ICON_SIZE_BUTTON);
647             image_label->pack_start(*im);
649             Gtk::Label *l = new Gtk::Label();
650             l->set_markup_with_mnemonic(_("_Export"));
651             image_label->pack_start(*l);
653             b->add(*image_label);
654             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
655             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
656                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
657             bb->pack_end(*b, false, false, 0);
658         }
660         vb->pack_end(*bb, false, false, 0);
661         vb->show_all();
663     } // end of if (!dlg)
665     sp_export_find_default_selection(dlg);
667     gtk_window_present ((GtkWindow *) dlg);
669     return;
670 } // end of sp_export_dialog()
672 static void
673 sp_export_update_checkbuttons (GtkObject *base)
675     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
676     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
677     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
678     if (num >= 2) {
679         gtk_widget_set_sensitive (be, true);
680         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
681     } else {
682         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
683         gtk_widget_set_sensitive (be, FALSE);
684     }
685     if (num > 0) {
686         gtk_widget_set_sensitive (he, true);
687     } else {
688         gtk_widget_set_sensitive (he, false);
689     }
692 static inline void
693 sp_export_find_default_selection(GtkWidget * dlg)
695     selection_type key = SELECTION_NUMBER_OF;
697     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
698         key = SELECTION_SELECTION;
699     }
701     /* Try using the preferences */
702     if (key == SELECTION_NUMBER_OF) {
703         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
704         int i = SELECTION_NUMBER_OF;
706         Glib::ustring what = prefs->getString("dialogs.export.exportarea", "value");
708         if (!what.empty()) {
709             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
710                 if (what == selection_names[i]) {
711                     break;
712                 }
713             }
714         }
716         key = (selection_type)i;
717     }
719     if (key == SELECTION_NUMBER_OF) {
720         key = SELECTION_SELECTION;
721     }
723     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
724                                                        selection_names[key]);
725     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
727     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
731 /**
732  * \brief  If selection changed or a different document activated, we must
733  * recalculate any chosen areas
734  *
735  */
736 static void
737 sp_export_selection_changed ( Inkscape::Application *inkscape,
738                               Inkscape::Selection *selection,
739                               GtkObject *base )
741     selection_type current_key;
742     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
744     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
745             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
746             was_empty) {
747         gtk_toggle_button_set_active
748             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
749               TRUE );
750     }
751     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
753     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
755     if (inkscape &&
756             SP_IS_INKSCAPE (inkscape) &&
757             selection &&
758             SELECTION_CUSTOM != current_key) {
759         GtkToggleButton * button;
760         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
761         sp_export_area_toggled(button, base);
762     }
764     sp_export_update_checkbuttons (base);
767 static void
768 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
769                                Inkscape::Selection */*selection*/,
770                                guint /*flags*/,
771                                GtkObject *base )
773     selection_type current_key;
774     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
776     switch (current_key) {
777         case SELECTION_DRAWING:
778             if ( SP_ACTIVE_DESKTOP ) {
779                 SPDocument *doc;
780                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
781                 boost::optional<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
782                 if (bbox) {
783                     sp_export_set_area (base, bbox->min()[NR::X],
784                                               bbox->min()[NR::Y],
785                                               bbox->max()[NR::X],
786                                               bbox->max()[NR::Y]);
787                 }
788             }
789             break;
790         case SELECTION_SELECTION:
791             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
792                 NRRect bbox;
793                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
794                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
795             }
796             break;
797         default:
798             /* Do nothing for page or for custom */
799             break;
800     }
802     return;
805 /// Called when one of the selection buttons was toggled.
806 static void
807 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
809     if (gtk_object_get_data (base, "update"))
810         return;
812     selection_type key, old_key;
813     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
814     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
816     /* Ignore all "turned off" events unless we're the only active button */
817     if (!gtk_toggle_button_get_active (tb) ) {
819         /* Don't let the current selection be deactived - but rerun the
820            activate to allow the user to renew the values */
821         if (key == old_key) {
822             gtk_toggle_button_set_active ( tb, TRUE );
823         }
825         return;
826     }
828     /* Turn off the currently active button unless it's us */
829     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
831     if (old_key != key) {
832         gtk_toggle_button_set_active
833             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
834               FALSE );
835     }
837     if ( SP_ACTIVE_DESKTOP )
838     {
839         SPDocument *doc;
840         boost::optional<NR::Rect> bbox;
841         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
843         /* Notice how the switch is used to 'fall through' here to get
844            various backups.  If you modify this without noticing you'll
845            probabaly screw something up. */
846         switch (key) {
847             case SELECTION_SELECTION:
848                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
849                 {
850                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
851                     /* Only if there is a selection that we can set
852                        do we break, otherwise we fall through to the
853                        drawing */
854                     // std::cout << "Using selection: SELECTION" << std::endl;
855                     key = SELECTION_SELECTION;
856                     break;
857                 }
858             case SELECTION_DRAWING:
859                 /** \todo
860                  * This returns wrong values if the document has a viewBox.
861                  */
862                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
863                 /* If the drawing is valid, then we'll use it and break
864                    otherwise we drop through to the page settings */
865                 if (bbox) {
866                     // std::cout << "Using selection: DRAWING" << std::endl;
867                     key = SELECTION_DRAWING;
868                     break;
869                 }
870             case SELECTION_PAGE:
871                 bbox = NR::Rect(NR::Point(0.0, 0.0),
872                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
873                                 );
875                 // std::cout << "Using selection: PAGE" << std::endl;
876                 key = SELECTION_PAGE;
877                 break;
878             case SELECTION_CUSTOM:
879             default:
880                 break;
881         } // switch
883         // remember area setting
884         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
885         prefs->setString("dialogs.export.exportarea", "value", selection_names[key]);
887         if ( key != SELECTION_CUSTOM && bbox ) {
888             sp_export_set_area (base, bbox->min()[NR::X],
889                                       bbox->min()[NR::Y],
890                                       bbox->max()[NR::X],
891                                       bbox->max()[NR::Y]);
892         }
894     } // end of if ( SP_ACTIVE_DESKTOP )
897     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
898         GtkWidget * file_entry;
899         const gchar * filename = NULL;
900         float xdpi = 0.0, ydpi = 0.0;
902         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
904         switch (key) {
905             case SELECTION_PAGE:
906             case SELECTION_DRAWING: {
907                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
908                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
910                 if (filename == NULL) {
911                     if (doc_export_name != NULL) {
912                         filename = g_strdup(doc_export_name);
913                     } else {
914                         filename = g_strdup("");
915                     }
916                 }
917                 break;
918             }
919             case SELECTION_SELECTION:
920                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
922                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
924                     /* If we still don't have a filename -- let's build
925                        one that's nice */
926                     if (filename == NULL) {
927                         const gchar * id = NULL;
928                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
929                         for(; reprlst != NULL; reprlst = reprlst->next) {
930                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
931                             if (repr->attribute("id")) {
932                                 id = repr->attribute("id");
933                                 break;
934                             }
935                         }
937                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
938                     }
939                 }
940                 break;
941             case SELECTION_CUSTOM:
942             default:
943                 break;
944         }
946         if (filename != NULL) {
947             g_free(original_name);
948             original_name = g_strdup(filename);
949             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
950         }
952         if (xdpi != 0.0) {
953             sp_export_value_set(base, "xdpi", xdpi);
954         }
956         /* These can't be separate, and setting x sets y, so for
957            now setting this is disabled.  Hopefully it won't be in
958            the future */
959         if (FALSE && ydpi != 0.0) {
960             sp_export_value_set(base, "ydpi", ydpi);
961         }
962     }
964     return;
965 } // end of sp_export_area_toggled()
967 /// Called when dialog is deleted
968 static gint
969 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
971     g_object_set_data (base, "cancel", (gpointer) 1);
972     return TRUE;
973 } // end of sp_export_progress_delete()
975 /// Called when progress is cancelled
976 static void
977 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
979     g_object_set_data (base, "cancel", (gpointer) 1);
980 } // end of sp_export_progress_cancel()
982 /// Called for every progress iteration
983 static unsigned int
984 sp_export_progress_callback (float value, void *data)
986     GtkWidget *prg;
987     int evtcount;
989     if (g_object_get_data ((GObject *) data, "cancel"))
990         return FALSE;
992     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
993     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
995     evtcount = 0;
996     while ((evtcount < 16) && gdk_events_pending ()) {
997             gtk_main_iteration_do (FALSE);
998             evtcount += 1;
999     }
1001     gtk_main_iteration_do (FALSE);
1003     return TRUE;
1005 } // end of sp_export_progress_callback()
1007 GtkWidget *
1008 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1009     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1011     dlg = gtk_dialog_new ();
1012     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1013     prg = gtk_progress_bar_new ();
1014     sp_transientize (dlg);
1015     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1016     g_object_set_data ((GObject *) base, "progress", prg);
1018     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1020     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1021                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1022     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1023                         prg, FALSE, FALSE, 4 );
1024     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1025                                   GTK_STOCK_CANCEL,
1026                                   GTK_RESPONSE_CANCEL );
1028     g_signal_connect ( (GObject *) dlg, "delete_event",
1029                        (GCallback) sp_export_progress_delete, base);
1030     g_signal_connect ( (GObject *) btn, "clicked",
1031                        (GCallback) sp_export_progress_cancel, base);
1032     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1033     gtk_widget_show_all (dlg);
1035     return dlg;
1038 // FIXME: Some lib function should be available to do this ...
1039 static gchar *
1040 filename_add_extension (const gchar *filename, const gchar *extension)
1042   gchar *dot;
1044   dot = strrchr (filename, '.');
1045   if ( !dot )
1046     return g_strconcat (filename, ".", extension, NULL);
1047   {
1048     if (dot[1] == '\0')
1049       return g_strconcat (filename, extension, NULL);
1050     else
1051     {
1052       if (g_strcasecmp (dot + 1, extension) == 0)
1053         return g_strdup (filename);
1054       else
1055       {
1056         return g_strconcat (filename, ".", extension, NULL);
1057       }
1058     }
1059   }
1062 /// Called when export button is clicked
1063 static void
1064 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1066     if (!SP_ACTIVE_DESKTOP) return;
1068     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1070     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1071     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1072     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1073     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1074         // Batch export of selected objects
1076         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1077         gint n = 0;
1079         if (num < 1)
1080             return;
1082         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1083         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1084         g_free (progress_text);
1086         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1087              i != NULL;
1088              i = i->next) {
1089             SPItem *item = (SPItem *) i->data;
1090             // retrieve export filename hint
1091             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1092             if (!fn) {
1093                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1094             }
1096             // retrieve export dpi hints
1097             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1098             gdouble dpi = 0.0;
1099             if (dpi_hint) {
1100                 dpi = atof(dpi_hint);
1101             }
1102             if (dpi == 0.0) {
1103                 dpi = DPI_BASE;
1104             }
1106             NRRect area;
1107             sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1109             gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1110             gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1112             if (width > 1 && height > 1) {
1113                 /* Do export */
1114                 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1115                                          area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1116                                          nv->pagecolor,
1117                                          NULL, NULL, TRUE,  // overwrite without asking
1118                                          hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1119                         )) {
1120                     gchar * error;
1121                     gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1122                     error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1123                     sp_ui_error_dialog(error);
1124                     g_free(safeFile);
1125                     g_free(error);
1126                 }
1127             }
1128             n++;
1129             sp_export_progress_callback((float)n/num, base);
1130         }
1132         gtk_widget_destroy (prog_dlg);
1133         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1135     } else {
1137     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1138     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1140     float const x0 = sp_export_value_get_px(base, "x0");
1141     float const y0 = sp_export_value_get_px(base, "y0");
1142     float const x1 = sp_export_value_get_px(base, "x1");
1143     float const y1 = sp_export_value_get_px(base, "y1");
1144     float const xdpi = sp_export_value_get(base, "xdpi");
1145     float const ydpi = sp_export_value_get(base, "ydpi");
1146     unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1147     unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1149     if (filename == NULL || *filename == '\0') {
1150         sp_ui_error_dialog(_("You have to enter a filename"));
1151         return;
1152     }
1154     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1155         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1156         return;
1157     }
1159     gchar *dirname = g_path_get_dirname(filename);
1160     if ( dirname == NULL
1161          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1162     {
1163         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1164         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1165                                        safeDir);
1166         sp_ui_error_dialog(error);
1167         g_free(safeDir);
1168         g_free(error);
1169         g_free(dirname);
1170         return;
1171     }
1172     g_free(dirname);
1174     // make sure that .png is the extension of the file:
1175     gchar * filename_ext = filename_add_extension(filename, "png");
1176     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1178     gchar *fn = g_path_get_basename (filename_ext);
1180     gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1181     g_free (fn);
1182     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1183     g_free (progress_text);
1185     /* Do export */
1186     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1187                              x0, y0, x1, y1, width, height, xdpi, ydpi,
1188                              nv->pagecolor,
1189                              sp_export_progress_callback, base, FALSE,
1190                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1191             )) {
1192         gchar * error;
1193         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1194         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1195         sp_ui_error_dialog(error);
1196         g_free(safeFile);
1197         g_free(error);
1198     }
1200     /* Reset the filename so that it can be changed again by changing
1201        selections and all that */
1202     g_free(original_name);
1203     original_name = g_strdup(filename_ext);
1204     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1206     gtk_widget_destroy (prog_dlg);
1207     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1209     /* Setup the values in the document */
1210     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1211         case SELECTION_PAGE:
1212         case SELECTION_DRAWING: {
1213             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1214             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1215             bool modified = false;
1216             const gchar * temp_string;
1218             bool saved = sp_document_get_undo_sensitive(doc);
1219             sp_document_set_undo_sensitive(doc, false);
1221             temp_string = repr->attribute("inkscape:export-filename");
1222             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1223                 repr->setAttribute("inkscape:export-filename", filename_ext);
1224                 modified = true;
1225             }
1226             temp_string = repr->attribute("inkscape:export-xdpi");
1227             if (temp_string == NULL || xdpi != atof(temp_string)) {
1228                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1229                 modified = true;
1230             }
1231             temp_string = repr->attribute("inkscape:export-ydpi");
1232             if (temp_string == NULL || xdpi != atof(temp_string)) {
1233                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1234                 modified = true;
1235             }
1236             sp_document_set_undo_sensitive(doc, saved);
1238             if (modified) {
1239                 doc->setModifiedSinceSave();
1240             }
1241             break;
1242         }
1243         case SELECTION_SELECTION: {
1244             const GSList * reprlst;
1245             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1246             bool modified = false;
1248             bool saved = sp_document_get_undo_sensitive(doc);
1249             sp_document_set_undo_sensitive(doc, false);
1250             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1252             for(; reprlst != NULL; reprlst = reprlst->next) {
1253                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1254                 const gchar * temp_string;
1256                 if (repr->attribute("id") == NULL ||
1257                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1258                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1259                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1260                     temp_string = repr->attribute("inkscape:export-filename");
1261                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1262                         repr->setAttribute("inkscape:export-filename", filename_ext);
1263                         modified = true;
1264                     }
1265                 }
1266                 temp_string = repr->attribute("inkscape:export-xdpi");
1267                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1268                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1269                     modified = true;
1270                 }
1271                 temp_string = repr->attribute("inkscape:export-ydpi");
1272                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1273                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1274                     modified = true;
1275                 }
1276             }
1277             sp_document_set_undo_sensitive(doc, saved);
1279             if (modified) {
1280                 doc->setModifiedSinceSave();
1281             }
1282             break;
1283         }
1284         default:
1285             break;
1286     }
1288     g_free (filename_ext);
1290     }
1292 } // end of sp_export_export_clicked()
1294 /// Called when Browse button is clicked
1295 static void
1296 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1298     GtkWidget *fs, *fe;
1299     const gchar *filename;
1301     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1302                                       (GtkWindow*)dlg,
1303                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1304                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1305                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1306                                       NULL );
1308 #ifdef WITH_GNOME_VFS
1309     if (gnome_vfs_initialized()) {
1310         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1311     }
1312 #endif
1314     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1316     sp_transientize (fs);
1318     gtk_window_set_modal(GTK_WINDOW (fs), true);
1320     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1322     if (*filename == '\0') {
1323         filename = homedir_path(NULL);
1324     }
1326     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1328     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1329     {
1330         gchar *file;
1332         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1334         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1335         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1337         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1339         g_free(utf8file);
1340         g_free(file);
1341     }
1343     gtk_widget_destroy (fs);
1345     return;
1346 } // end of sp_export_browse_clicked()
1348 // TODO: Move this to nr-rect-fns.h.
1349 static bool
1350 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1352     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1353     return (
1354         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1355         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1356         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1357         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1358         );
1361 /**
1362     \brief  This function is used to detect the current selection setting
1363             based on the values in the x0, y0, x1 and y0 fields.
1364     \param  base  The export dialog itself
1366     One of the most confusing parts of this function is why the array
1367     is built at the beginning.  What needs to happen here is that we
1368     should always check the current selection to see if it is the valid
1369     one.  While this is a performance improvement it is also a usability
1370     one during the cases where things like selections and drawings match
1371     size.  This way buttons change less 'randomly' (atleast in the eyes
1372     of the user).  To do this an array is built where the current selection
1373     type is placed first, and then the others in an order from smallest
1374     to largest (this can be configured by reshuffling \c test_order).
1376     All of the values in this function are rounded to two decimal places
1377     because that is what is shown to the user.  While everything is kept
1378     more accurate than that, the user can't control more acurrate than
1379     that, so for this to work for them - it needs to check on that level
1380     of accuracy.
1382     \todo finish writing this up
1383 */
1384 static void
1385 sp_export_detect_size(GtkObject * base) {
1386     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1387     selection_type this_test[SELECTION_NUMBER_OF + 1];
1388     selection_type key = SELECTION_NUMBER_OF;
1390     NR::Point x(sp_export_value_get_px (base, "x0"),
1391                 sp_export_value_get_px (base, "y0"));
1392     NR::Point y(sp_export_value_get_px (base, "x1"),
1393                 sp_export_value_get_px (base, "y1"));
1394     NR::Rect current_bbox(x, y);
1395     //std::cout << "Current " << current_bbox;
1397     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1398     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1399         this_test[i + 1] = test_order[i];
1400     }
1402     for (int i = 0;
1403             i < SELECTION_NUMBER_OF + 1 &&
1404                 key == SELECTION_NUMBER_OF &&
1405                 SP_ACTIVE_DESKTOP != NULL;
1406             i++) {
1407         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1408         switch (this_test[i]) {
1409             case SELECTION_SELECTION:
1410                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1411                     boost::optional<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1413                     //std::cout << "Selection " << bbox;
1414                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1415                         key = SELECTION_SELECTION;
1416                     }
1417                 }
1418                 break;
1419             case SELECTION_DRAWING: {
1420                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1422                 boost::optional<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1424                 // std::cout << "Drawing " << bbox2;
1425                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1426                     key = SELECTION_DRAWING;
1427                 }
1428                 break;
1429             }
1431             case SELECTION_PAGE: {
1432                 SPDocument *doc;
1434                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1436                 NR::Point x(0.0, 0.0);
1437                 NR::Point y(sp_document_width(doc),
1438                             sp_document_height(doc));
1439                 NR::Rect bbox(x, y);
1441                 // std::cout << "Page " << bbox;
1442                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1443                     key = SELECTION_PAGE;
1444                 }
1446                 break;
1447            }
1448         default:
1449            break;
1450         }
1451     }
1452     // std::cout << std::endl;
1454     if (key == SELECTION_NUMBER_OF) {
1455         key = SELECTION_CUSTOM;
1456     }
1458     /* We're now using a custom size, not a fixed one */
1459     /* printf("Detecting state: %s\n", selection_names[key]); */
1460     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1461     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1462     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1463     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1465     return;
1466 } /* sp_export_detect_size */
1468 /// Called when area x0 value is changed
1469 static void
1470 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1472     float x0, x1, xdpi, width;
1474     if (gtk_object_get_data (base, "update"))
1475         return;
1477     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1478             (base, "units")))
1479     {
1480         return;
1481     }
1483     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1485     x0 = sp_export_value_get_px (base, "x0");
1486     x1 = sp_export_value_get_px (base, "x1");
1487     xdpi = sp_export_value_get (base, "xdpi");
1489     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1491     if (width < SP_EXPORT_MIN_SIZE) {
1492         const gchar *key;
1493         width = SP_EXPORT_MIN_SIZE;
1494         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1496         if (!strcmp (key, "x0")) {
1497             x1 = x0 + width * DPI_BASE / xdpi;
1498             sp_export_value_set_px (base, "x1", x1);
1499         } else {
1500             x0 = x1 - width * DPI_BASE / xdpi;
1501             sp_export_value_set_px (base, "x0", x0);
1502         }
1503     }
1505     sp_export_value_set_px (base, "width", x1 - x0);
1506     sp_export_value_set (base, "bmwidth", width);
1508     sp_export_detect_size(base);
1510     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1512     return;
1513 } // end of sp_export_area_x_value_changed()
1515 /// Called when area y0 value is changed.
1516 static void
1517 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1519     float y0, y1, ydpi, height;
1521     if (gtk_object_get_data (base, "update"))
1522         return;
1524     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1525            (base, "units")))
1526     {
1527         return;
1528     }
1530     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1532     y0 = sp_export_value_get_px (base, "y0");
1533     y1 = sp_export_value_get_px (base, "y1");
1534     ydpi = sp_export_value_get (base, "ydpi");
1536     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1538     if (height < SP_EXPORT_MIN_SIZE) {
1539         const gchar *key;
1540         height = SP_EXPORT_MIN_SIZE;
1541         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1542         if (!strcmp (key, "y0")) {
1543             y1 = y0 + height * DPI_BASE / ydpi;
1544             sp_export_value_set_px (base, "y1", y1);
1545         } else {
1546             y0 = y1 - height * DPI_BASE / ydpi;
1547             sp_export_value_set_px (base, "y0", y0);
1548         }
1549     }
1551     sp_export_value_set_px (base, "height", y1 - y0);
1552     sp_export_value_set (base, "bmheight", height);
1554     sp_export_detect_size(base);
1556     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1558     return;
1559 } // end of sp_export_area_y_value_changed()
1561 /// Called when x1-x0 or area width is changed
1562 static void
1563 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1565     float x0, x1, xdpi, width, bmwidth;
1567     if (gtk_object_get_data (base, "update"))
1568         return;
1570     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1571            (base, "units"))) {
1572         return;
1573     }
1575     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1577     x0 = sp_export_value_get_px (base, "x0");
1578     x1 = sp_export_value_get_px (base, "x1");
1579     xdpi = sp_export_value_get (base, "xdpi");
1580     width = sp_export_value_get_px (base, "width");
1581     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1583     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1585         bmwidth = SP_EXPORT_MIN_SIZE;
1586         width = bmwidth * DPI_BASE / xdpi;
1587         sp_export_value_set_px (base, "width", width);
1588     }
1590     sp_export_value_set_px (base, "x1", x0 + width);
1591     sp_export_value_set (base, "bmwidth", bmwidth);
1593     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1595     return;
1596 } // end of sp_export_area_width_value_changed()
1598 /// Called when y1-y0 or area height is changed.
1599 static void
1600 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1603     float y0, y1, ydpi, height, bmheight;
1605     if (gtk_object_get_data (base, "update"))
1606         return;
1608     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1609            (base, "units"))) {
1610         return;
1611     }
1613     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1615     y0 = sp_export_value_get_px (base, "y0");
1616     y1 = sp_export_value_get_px (base, "y1");
1617     ydpi = sp_export_value_get (base, "ydpi");
1618     height = sp_export_value_get_px (base, "height");
1619     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1621     if (bmheight < SP_EXPORT_MIN_SIZE) {
1622         bmheight = SP_EXPORT_MIN_SIZE;
1623         height = bmheight * DPI_BASE / ydpi;
1624         sp_export_value_set_px (base, "height", height);
1625     }
1627     sp_export_value_set_px (base, "y1", y0 + height);
1628     sp_export_value_set (base, "bmheight", bmheight);
1630     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1632     return;
1633 } // end of sp_export_area_height_value_changed()
1635 /**
1636     \brief  A function to set the ydpi
1637     \param  base  The export dialog
1639     This function grabs all of the y values and then figures out the
1640     new bitmap size based on the changing dpi value.  The dpi value is
1641     gotten from the xdpi setting as these can not currently be independent.
1642 */
1643 static void
1644 sp_export_set_image_y (GtkObject *base)
1646     float y0, y1, xdpi;
1648     y0 = sp_export_value_get_px (base, "y0");
1649     y1 = sp_export_value_get_px (base, "y1");
1650     xdpi = sp_export_value_get (base, "xdpi");
1652     sp_export_value_set (base, "ydpi", xdpi);
1653     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1655     return;
1656 } // end of sp_export_set_image_y()
1658 /**
1659     \brief  A function to set the xdpi
1660     \param  base  The export dialog
1662     This function grabs all of the x values and then figures out the
1663     new bitmap size based on the changing dpi value.  The dpi value is
1664     gotten from the xdpi setting as these can not currently be independent.
1665 */
1666 static void
1667 sp_export_set_image_x (GtkObject *base)
1669     float x0, x1, xdpi;
1671     x0 = sp_export_value_get_px (base, "x0");
1672     x1 = sp_export_value_get_px (base, "x1");
1673     xdpi = sp_export_value_get (base, "xdpi");
1675     sp_export_value_set (base, "ydpi", xdpi);
1676     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1678     return;
1679 } // end of sp_export_set_image_x()
1681 /// Called when pixel width is changed
1682 static void
1683 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1685     float x0, x1, bmwidth, xdpi;
1687     if (gtk_object_get_data (base, "update"))
1688         return;
1690     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1691            (base, "units"))) {
1692        return;
1693     }
1695     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1697     x0 = sp_export_value_get_px (base, "x0");
1698     x1 = sp_export_value_get_px (base, "x1");
1699     bmwidth = sp_export_value_get (base, "bmwidth");
1701     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1702         bmwidth = SP_EXPORT_MIN_SIZE;
1703         sp_export_value_set (base, "bmwidth", bmwidth);
1704     }
1706     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1707     sp_export_value_set (base, "xdpi", xdpi);
1709     sp_export_set_image_y (base);
1711     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1713     return;
1714 } // end of sp_export_bitmap_width_value_changed()
1716 /// Called when pixel height is changed
1717 static void
1718 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1720     float y0, y1, bmheight, xdpi;
1722     if (gtk_object_get_data (base, "update"))
1723         return;
1725     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1726            (base, "units"))) {
1727        return;
1728     }
1730     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1732     y0 = sp_export_value_get_px (base, "y0");
1733     y1 = sp_export_value_get_px (base, "y1");
1734     bmheight = sp_export_value_get (base, "bmheight");
1736     if (bmheight < SP_EXPORT_MIN_SIZE) {
1737         bmheight = SP_EXPORT_MIN_SIZE;
1738         sp_export_value_set (base, "bmheight", bmheight);
1739     }
1741     xdpi = bmheight * DPI_BASE / (y1 - y0);
1742     sp_export_value_set (base, "xdpi", xdpi);
1744     sp_export_set_image_x (base);
1746     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1748     return;
1749 } // end of sp_export_bitmap_width_value_changed()
1751 /**
1752     \brief  A function to adjust the bitmap width when the xdpi value changes
1753     \param  adj  The adjustment that was changed
1754     \param  base The export dialog itself
1756     The first thing this function checks is to see if we are doing an
1757     update.  If we are, this function just returns because there is another
1758     instance of it that will handle everything for us.  If there is a
1759     units change, we also assume that everyone is being updated appropriately
1760     and there is nothing for us to do.
1762     If we're the highest level function, we set the update flag, and
1763     continue on our way.
1765     All of the values are grabbed using the \c sp_export_value_get functions
1766     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1767     xdpi value is saved in the preferences for the next time the dialog
1768     is opened.  (does the selection dpi need to be set here?)
1770     A check is done to to ensure that we aren't outputing an invalid width,
1771     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1772     changed to make it valid.
1774     After all of this the bitmap width is changed.
1776     We also change the ydpi.  This is a temporary hack as these can not
1777     currently be independent.  This is likely to change in the future.
1778 */
1779 void
1780 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1782     float x0, x1, xdpi, bmwidth;
1784     if (gtk_object_get_data (base, "update"))
1785         return;
1787     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1788            (base, "units"))) {
1789        return;
1790     }
1792     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1794     x0 = sp_export_value_get_px (base, "x0");
1795     x1 = sp_export_value_get_px (base, "x1");
1796     xdpi = sp_export_value_get (base, "xdpi");
1798     // remember xdpi setting
1799     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1800     prefs->setDouble("dialogs.export.defaultxdpi", "value", xdpi);
1802     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1804     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1805         bmwidth = SP_EXPORT_MIN_SIZE;
1806         if (x1 != x0)
1807             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1808         else
1809             xdpi = DPI_BASE;
1810         sp_export_value_set (base, "xdpi", xdpi);
1811     }
1813     sp_export_value_set (base, "bmwidth", bmwidth);
1815     sp_export_set_image_y (base);
1817     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1819     return;
1820 } // end of sp_export_xdpi_value_changed()
1823 /**
1824     \brief  A function to change the area that is used for the exported
1825             bitmap.
1826     \param  base  This is the export dialog
1827     \param  x0    Horizontal upper left hand corner of the picture in points
1828     \param  y0    Vertical upper left hand corner of the picture in points
1829     \param  x1    Horizontal lower right hand corner of the picture in points
1830     \param  y1    Vertical lower right hand corner of the picture in points
1832     This function just calls \c sp_export_value_set_px for each of the
1833     parameters that is passed in.  This allows for setting them all in
1834     one convient area.
1836     Update is set to suspend all of the other test running while all the
1837     values are being set up.  This allows for a performance increase, but
1838     it also means that the wrong type won't be detected with only some of
1839     the values set.  After all the values are set everyone is told that
1840     there has been an update.
1841 */
1842 static void
1843 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1845     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1846     sp_export_value_set_px (base, "x1", x1);
1847     sp_export_value_set_px (base, "y1", y1);
1848     sp_export_value_set_px (base, "x0", x0);
1849     sp_export_value_set_px (base, "y0", y0);
1850     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1852     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1853     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1855     return;
1858 /**
1859     \brief  Sets the value of an adjustment
1860     \param  base  The export dialog
1861     \param  key   Which adjustment to set
1862     \param  val   What value to set it to
1864     This function finds the adjustment using the data stored in the
1865     export dialog.  After finding the adjustment it then sets
1866     the value of it.
1867 */
1868 static void
1869 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1871     GtkAdjustment *adj;
1873     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1875     gtk_adjustment_set_value (adj, val);
1878 /**
1879     \brief  A function to set a value using the units points
1880     \param  base  The export dialog
1881     \param  key   Which value should be set
1882     \param  val   What the value should be in points
1884     This function first gets the adjustment for the key that is passed
1885     in.  It then figures out what units are currently being used in the
1886     dialog.  After doing all of that, it then converts the incoming
1887     value and sets the adjustment.
1888 */
1889 static void
1890 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1892     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1894     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1896     return;
1899 /**
1900     \brief  Get the value of an adjustment in the export dialog
1901     \param  base  The export dialog
1902     \param  key   Which adjustment is being looked for
1903     \return The value in the specified adjustment
1905     This function gets the adjustment from the data field in the export
1906     dialog.  It then grabs the value from the adjustment.
1907 */
1908 static float
1909 sp_export_value_get ( GtkObject *base, const gchar *key )
1911     GtkAdjustment *adj;
1913     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1915     return adj->value;
1918 /**
1919     \brief  Grabs a value in the export dialog and converts the unit
1920             to points
1921     \param  base  The export dialog
1922     \param  key   Which value should be returned
1923     \return The value in the adjustment in points
1925     This function, at its most basic, is a call to \c sp_export_value_get
1926     to get the value of the adjustment.  It then finds the units that
1927     are being used by looking at the "units" attribute of the export
1928     dialog.  Using that it converts the returned value into points.
1929 */
1930 static float
1931 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1933     float value = sp_export_value_get(base, key);
1934     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1936     return sp_units_get_pixels (value, *unit);
1937 } // end of sp_export_value_get_px()
1939 /**
1940     \brief  This function is called when the filename is changed by
1941             anyone.  It resets the virgin bit.
1942     \param  object  Text entry box
1943     \param  data    The export dialog
1944     \return None
1946     This function gets called when the text area is modified.  It is
1947     looking for the case where the text area is modified from its
1948     original value.  In that case it sets the "filename-modified" bit
1949     to TRUE.  If the text dialog returns back to the original text, the
1950     bit gets reset.  This should stop simple mistakes.
1951 */
1952 static void
1953 sp_export_filename_modified (GtkObject * object, gpointer data)
1955     GtkWidget * text_entry = (GtkWidget *)object;
1956     GtkWidget * export_dialog = (GtkWidget *)data;
1958     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1959         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1960 //        printf("Modified: FALSE\n");
1961     } else {
1962         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1963 //        printf("Modified: TRUE\n");
1964     }
1966     return;
1967 } // end sp_export_filename_modified
1969 /*
1970   Local Variables:
1971   mode:c++
1972   c-file-style:"stroustrup"
1973   c-file-offsets:((innamespace . 0)(inline-open . 0))
1974   indent-tabs-mode:nil
1975   fill-column:99
1976   End:
1977 */
1978 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :