Code

updated spanish.nsh and inkscape.nsi to reflect latest file-changes
[inkscape.git] / trunk / 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 #include <gtk/gtk.h>
20 #include <gtkmm/box.h>
21 #include <gtkmm/buttonbox.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/widget.h>
24 #include <gtkmm/togglebutton.h>
25 #include <gtkmm/entry.h>
26 #include <gtkmm/image.h>
27 #include <gtkmm/stockid.h>
28 #include <gtkmm/stock.h>
29 #ifdef WITH_GNOME_VFS
30 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
31 #endif
33 #include <glibmm/i18n.h>
34 #include "helper/unit-menu.h"
35 #include "helper/units.h"
36 #include "unit-constants.h"
37 #include "helper/window.h"
38 #include "inkscape-private.h"
39 #include "document.h"
40 #include "desktop-handles.h"
41 #include "sp-item.h"
42 #include "selection.h"
43 #include "file.h"
44 #include "macros.h"
45 #include "sp-namedview.h"
46 #include "selection-chemistry.h"
48 #include "dialog-events.h"
49 #include "preferences.h"
50 #include "verbs.h"
51 #include "interface.h"
53 #include "extension/output.h"
54 #include "extension/db.h"
56 #include "io/sys.h"
58 #include "helper/png-write.h"
59 #include <png.h>
62 #define SP_EXPORT_MIN_SIZE 1.0
64 #define DPI_BASE PX_PER_IN
66 #define EXPORT_COORD_PRECISION 3
68 #define MIN_ONSCREEN_DISTANCE 50
70 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
71 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
72 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
74 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj,
75                                                    GtkObject *base);
77 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj,
78                                                    GtkObject *base);
80 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj,
81                                                    GtkObject *base);
83 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj,
84                                                    GtkObject *base);
86 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
87                                                    GtkObject *base);
89 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
90                                                    GtkObject *base);
92 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj,
93                                                    GtkObject *base);
95 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
96                                           Inkscape::Selection *selection,
97                                           GtkObject *base);
98 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
99                                            Inkscape::Selection *selection,
100                                            guint flags,
101                                            GtkObject *base );
103 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
104 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
105 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
106 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
107 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
109 static void sp_export_filename_modified (GtkObject * object, gpointer data);
110 static inline void sp_export_find_default_selection(GtkWidget * dlg);
111 static void sp_export_detect_size(GtkObject * base);
113 static Glib::ustring const prefs_path = "/dialogs/export/";
115 // these all need to be reinitialized to their defaults during dialog_destroy
116 static GtkWidget *dlg = NULL;
117 static win_data wd;
118 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
119 static gchar * original_name = NULL;
120 static gchar * doc_export_name = NULL;
121 static bool was_empty = TRUE;
123 /** What type of button is being pressed */
124 enum selection_type {
125     SELECTION_PAGE = 0,  /**< Export the whole page */
126     SELECTION_DRAWING,   /**< Export everything drawn on the page */
127     SELECTION_SELECTION, /**< Export everything that is selected */
128     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
129     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
130 };
132 /** A list of strings that is used both in the preferences, and in the
133     data fields to describe the various values of \c selection_type. */
134 static const char * selection_names[SELECTION_NUMBER_OF] = {
135     "page", "drawing", "selection", "custom"};
137 /** The names on the buttons for the various selection types. */
138 static const char * selection_labels[SELECTION_NUMBER_OF] = {
139     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
141 static void
142 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
144     sp_signal_disconnect_by_data (INKSCAPE, dlg);
146     wd.win = dlg = NULL;
147     wd.stop = 0;
148     x = -1000; y = -1000; w = 0; h = 0;
149     g_free(original_name);
150     original_name = NULL;
151     g_free(doc_export_name);
152     doc_export_name = NULL;
153     was_empty = TRUE;
155     return;
156 } // end of sp_export_dialog_destroy()
158 /// Called when dialog is closed or inkscape is shut down.
159 static bool
160 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
163     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
164     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
166     if (x<0) x=0;
167     if (y<0) y=0;
168     
169     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
170     prefs->setInt(prefs_path + "x", x);
171     prefs->setInt(prefs_path + "y", y);
172     prefs->setInt(prefs_path + "w", w);
173     prefs->setInt(prefs_path + "h", h);
175     return FALSE; // which means, go ahead and destroy it
177 } // end of sp_export_dialog_delete()
179 /**
180     \brief  Creates a new spin button for the export dialog
181     \param  key  The name of the spin button
182     \param  val  A default value for the spin button
183     \param  min  Minimum value for the spin button
184     \param  max  Maximum value for the spin button
185     \param  step The step size for the spin button
186     \param  page Size of the page increment
187     \param  us   Unit selector that effects this spin button
188     \param  t    Table to put the spin button in
189     \param  x    X location in the table \c t to start with
190     \param  y    Y location in the table \c t to start with
191     \param  ll   Text to put on the left side of the spin button (optional)
192     \param  lr   Text to put on the right side of the spin button (optional)
193     \param  digits  Number of digits to display after the decimal
194     \param  sensitive  Whether the spin button is sensitive or not
195     \param  cb   Callback for when this spin button is changed (optional)
196     \param  dlg  Export dialog the spin button is being placed in
198 */
199 static void
200 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
201                            float step, float page, GtkWidget *us,
202                            GtkWidget *t, int x, int y,
203                            const gchar *ll, const gchar *lr,
204                            int digits, unsigned int sensitive,
205                            GCallback cb, GtkWidget *dlg )
207     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
208     gtk_object_set_data (a, "key", const_cast<gchar *>(key));
209     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
211     if (us) {
212         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
213                                           GTK_ADJUSTMENT (a) );
214     }
216     int pos = 0;
218     GtkWidget *l = NULL;
220     if (ll) {
222         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
223         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
224         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
225                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
226         gtk_widget_set_sensitive (l, sensitive);
227         pos += 1;
229     }
231     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
232     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
233                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
234     gtk_widget_set_size_request (sb, 80, -1);
235     gtk_widget_set_sensitive (sb, sensitive);
236     pos += 1;
238     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
240     if (lr) {
242         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
243         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
244         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
245                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
246         gtk_widget_set_sensitive (l, sensitive);
247         pos += 1;
249         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
250     }
252     if (cb)
253         gtk_signal_connect (a, "value_changed", cb, dlg);
255     return;
256 } // end of sp_export_spinbutton_new()
259 static Gtk::VBox *
260 sp_export_dialog_area_box (GtkWidget * dlg)
262     Gtk::VBox* vb = new Gtk::VBox(false, 3);
264     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
265     lbl->set_use_markup(true);
266     vb->pack_start(*lbl);
268     /* Units box */
269     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
270     /* gets added to the vbox later, but the unit selector is needed
271        earlier than that */
273     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
274     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
275     if (desktop)
276         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
277     unitbox->pack_end(*us, false, false, 0);
278     Gtk::Label* l = new Gtk::Label(_("Units:"));
279     unitbox->pack_end(*l, false, false, 3);
280     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
282     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
284     Gtk::ToggleButton* b;
285     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
286         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
287         b->set_data("key", GINT_TO_POINTER(i));
288         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
289         togglebox->pack_start(*b, false, true, 0);
290         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
291                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
292     }
294     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
295                        G_CALLBACK (sp_export_selection_changed), dlg );
296     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
297                        G_CALLBACK (sp_export_selection_modified), dlg );
298     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
299                        G_CALLBACK (sp_export_selection_changed), dlg );
301     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
302     t->set_row_spacings (4);
303     t->set_col_spacings (4);
305     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
306                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
307                                G_CALLBACK ( sp_export_area_x_value_changed),
308                                dlg );
310     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
311                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
312                                G_CALLBACK (sp_export_area_x_value_changed),
313                                dlg );
315     sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
316                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
317                                G_CALLBACK
318                                    (sp_export_area_width_value_changed),
319                                dlg );
321     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
322                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
323                                G_CALLBACK (sp_export_area_y_value_changed),
324                                dlg );
326     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
327                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
328                                G_CALLBACK (sp_export_area_y_value_changed),
329                                dlg );
331     sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
332                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
333                                G_CALLBACK (sp_export_area_height_value_changed),
334                                dlg );
336     vb->pack_start(*togglebox, false, false, 3);
337     vb->pack_start(*t, false, false, 0);
338     vb->pack_start(*unitbox, false, false, 0);
340     return vb;
341 } // end of sp_export_dialog_area_box
344 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
346     if (id == NULL) /* This should never happen */
347         id = "bitmap";
349     gchar * directory = NULL;
351     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
352         // std::cout << "Directory from dialog" << std::endl;
353         directory = g_dirname(file_entry_text);
354     }
356     if (directory == NULL) {
357         /* Grab document directory */
358         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
359             // std::cout << "Directory from document" << std::endl;
360             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
361         }
362     }
364     if (directory == NULL) {
365         // std::cout << "Home Directory" << std::endl;
366         directory = homedir_path(NULL);
367     }
369     gchar * id_ext = g_strconcat(id, ".png", NULL);
370     gchar *filename = g_build_filename(directory, id_ext, NULL);
371     g_free(directory);
372     g_free(id_ext);
373     return filename;
376 static void
377 batch_export_clicked (GtkWidget *widget, GtkObject *base)
379     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
380     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
381         vb_singleexport->set_sensitive(false);
382     } else {
383         vb_singleexport->set_sensitive(true);
384     }
387 void
388 sp_export_dialog (void)
390     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
391     if (!dlg) {
393         gchar title[500];
394         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
396         dlg = sp_window_new (title, TRUE);
398         if (x == -1000 || y == -1000) {
399             x = prefs->getInt(prefs_path + "x", 0);
400             y = prefs->getInt(prefs_path + "y", 0);
401         }
403         if (w ==0 || h == 0) {
404             w = prefs->getInt(prefs_path + "w", 0);
405             h = prefs->getInt(prefs_path + "h", 0);
406         }
408 //        if (x<0) x=0;
409 //        if (y<0) y=0;
411         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
412         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
413             gtk_window_move ((GtkWindow *) dlg, x, y);
414         else
415             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
416         sp_transientize (dlg);
417         wd.win = dlg;
418         wd.stop = 0;
420         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
421                              G_CALLBACK (sp_transientize_callback), &wd);
423         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
424                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
426         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
427                              G_CALLBACK (sp_export_dialog_destroy), dlg);
429         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
430                              G_CALLBACK (sp_export_dialog_delete), dlg);
432         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
433                              G_CALLBACK (sp_export_dialog_delete), dlg);
435         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
436                              G_CALLBACK (sp_dialog_hide), dlg);
438         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
439                              G_CALLBACK (sp_dialog_unhide), dlg);
441         GtkTooltips *tt = gtk_tooltips_new();
443         Gtk::VBox *vb = new Gtk::VBox(false, 3);
444         vb->set_border_width(3);
445         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
447         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
448         vb_singleexport->set_border_width(0);
449         vb->pack_start(*vb_singleexport);
450         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
452         /* Export area frame */
453         {
454             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
455             area_box->set_border_width(3);
456             vb_singleexport->pack_start(*area_box, false, false, 0);
457         }
459         /* Bitmap size frame */
460         {
461             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
462             size_box->set_border_width(3);
464             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
465             lbl->set_use_markup(true);
466             size_box->pack_start(*lbl, false, false, 0);
467             const int rows = 2;
468             const int cols = 5;
469             const bool homogeneous = false;
470             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
471             t->set_row_spacings (4);
472             t->set_col_spacings (4);
473             size_box->pack_start(*t);
475             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
476                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
477                                        _("_Width:"), _("pixels at"), 0, 1,
478                                        G_CALLBACK
479                                        (sp_export_bitmap_width_value_changed),
480                                        dlg );
482             sp_export_spinbutton_new ( "xdpi",
483                                        prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
484                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
485                                        NULL, _("dp_i"), 2, 1,
486                                        G_CALLBACK (sp_export_xdpi_value_changed),
487                                        dlg );
489             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
490                                        NULL, GTK_WIDGET(t->gobj()), 0, 1,
491                                        _("_Height:"), _("pixels at"), 0, 1,
492                                        G_CALLBACK
493                                        (sp_export_bitmap_height_value_changed),
494                                        dlg );
496             /** \todo
497              * Needs fixing: there's no way to set ydpi currently, so we use
498              *       the defaultxdpi value here, too...
499              */
500             sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
501                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
502                                        NULL, _("dpi"), 2, 0, NULL, dlg );
504             vb_singleexport->pack_start(*size_box);
505         }
507         /* File entry */
508         {
509             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
510             file_box->set_border_width(3);
512             // true = has mnemonic
513             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
514             flabel->set_use_markup(true);
515             file_box->pack_start(*flabel, false, false, 0);
517             Gtk::Entry *fe = new Gtk::Entry();
519             /*
520              * set the default filename to be that of the current path + document
521              * with .png extension
522              *
523              * One thing to notice here is that this filename may get
524              * overwritten, but it won't happen here.  The filename gets
525              * written into the text field, but then the button to select
526              * the area gets set.  In that code the filename can be changed
527              * if there are some with presidence in the document.  So, while
528              * this code sets the name first, it may not be the one users
529              * really see.
530              */
531             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
532             {
533                 gchar *name;
534                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
535                 const gchar *uri = SP_DOCUMENT_URI (doc);
536                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
537                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
538                 Inkscape::Extension::Output * oextension = NULL;
540                 if (text_extension != NULL) {
541                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
542                 }
544                 if (oextension != NULL) {
545                     gchar * old_extension = oextension->get_extension();
546                     if (g_str_has_suffix(uri, old_extension)) {
547                         gchar * uri_copy;
548                         gchar * extension_point;
549                         gchar * final_name;
551                         uri_copy = g_strdup(uri);
552                         extension_point = g_strrstr(uri_copy, old_extension);
553                         extension_point[0] = '\0';
555                         final_name = g_strconcat(uri_copy, ".png", NULL);
556                         fe->set_text(final_name);
558                         g_free(final_name);
559                         g_free(uri_copy);
560                     }
561                 } else {
562                     name = g_strconcat(uri, ".png", NULL);
563                     fe->set_text(name);
564                     g_free(name);
565                 }
567                 doc_export_name = g_strdup(fe->get_text().c_str());
568             }
569             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
570                                G_CALLBACK (sp_export_filename_modified), dlg);
572             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
574             {
575                 // true = has mnemonic
576                 Gtk::Button *b = new Gtk::Button();
578                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
579                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
580                         Gtk::ICON_SIZE_BUTTON);
581                 pixlabel->pack_start(*im);
583                 Gtk::Label *l = new Gtk::Label();
584                 l->set_markup_with_mnemonic(_("_Browse..."));
585                 pixlabel->pack_start(*l);
587                 b->add(*pixlabel);
589                 hb->pack_end (*b, false, false, 4);
590                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
591                                    G_CALLBACK (sp_export_browse_clicked), NULL );
592             }
594             hb->pack_start (*fe, true, true, 0);
595             file_box->add(*hb);
596             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
597             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
598             original_name = g_strdup(fe->get_text().c_str());
599             // pressing enter in the filename field is the same as clicking export:
600             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
601                                G_CALLBACK (sp_export_export_clicked), dlg );
602             // focus is in the filename initially:
603             fe->grab_focus();
605             // mnemonic in frame label moves focus to filename:
606             flabel->set_mnemonic_widget(*fe);
608             vb_singleexport->pack_start(*file_box);
609         }
611         {
612             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
613             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
614             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
615             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
616             batch_box->pack_start(*Glib::wrap(be), false, false);
617             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);
618             batch_box->show_all();
619             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
620             vb->pack_start(*batch_box);
621         }
623         {
624             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
625             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
626             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
627             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
628             hide_box->pack_start(*Glib::wrap(he), false, false);
629             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
630             hide_box->show_all();
631             vb->pack_start(*hide_box);
632         }
634         /* Buttons */
635         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
636         bb->set_border_width(3);
638         {
639             Gtk::Button *b = new Gtk::Button();
640             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
641             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
642                     Gtk::ICON_SIZE_BUTTON);
643             image_label->pack_start(*im);
645             Gtk::Label *l = new Gtk::Label();
646             l->set_markup_with_mnemonic(_("_Export"));
647             image_label->pack_start(*l);
649             b->add(*image_label);
650             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
651             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
652                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
653             bb->pack_end(*b, false, false, 0);
654         }
656         vb->pack_end(*bb, false, false, 0);
657         vb->show_all();
659     } // end of if (!dlg)
661     sp_export_find_default_selection(dlg);
663     gtk_window_present ((GtkWindow *) dlg);
665     return;
666 } // end of sp_export_dialog()
668 static void
669 sp_export_update_checkbuttons (GtkObject *base)
671     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
672     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
673     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
674     if (num >= 2) {
675         gtk_widget_set_sensitive (be, true);
676         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
677     } else {
678         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
679         gtk_widget_set_sensitive (be, FALSE);
680     }
681     if (num > 0) {
682         gtk_widget_set_sensitive (he, true);
683     } else {
684         gtk_widget_set_sensitive (he, false);
685     }
688 static inline void
689 sp_export_find_default_selection(GtkWidget * dlg)
691     selection_type key = SELECTION_NUMBER_OF;
693     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
694         key = SELECTION_SELECTION;
695     }
697     /* Try using the preferences */
698     if (key == SELECTION_NUMBER_OF) {
699         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
700         int i = SELECTION_NUMBER_OF;
702         Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
704         if (!what.empty()) {
705             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
706                 if (what == selection_names[i]) {
707                     break;
708                 }
709             }
710         }
712         key = (selection_type)i;
713     }
715     if (key == SELECTION_NUMBER_OF) {
716         key = SELECTION_SELECTION;
717     }
719     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
720                                                        selection_names[key]);
721     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
723     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
727 /**
728  * \brief  If selection changed or a different document activated, we must
729  * recalculate any chosen areas
730  *
731  */
732 static void
733 sp_export_selection_changed ( Inkscape::Application *inkscape,
734                               Inkscape::Selection *selection,
735                               GtkObject *base )
737     selection_type current_key;
738     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
740     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
741             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
742             was_empty) {
743         gtk_toggle_button_set_active
744             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
745               TRUE );
746     }
747     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
749     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
751     if (inkscape &&
752             SP_IS_INKSCAPE (inkscape) &&
753             selection &&
754             SELECTION_CUSTOM != current_key) {
755         GtkToggleButton * button;
756         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
757         sp_export_area_toggled(button, base);
758     }
760     sp_export_update_checkbuttons (base);
763 static void
764 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
765                                Inkscape::Selection */*selection*/,
766                                guint /*flags*/,
767                                GtkObject *base )
769     selection_type current_key;
770     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
772     switch (current_key) {
773         case SELECTION_DRAWING:
774             if ( SP_ACTIVE_DESKTOP ) {
775                 SPDocument *doc;
776                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
777                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
778                 if (bbox) {
779                     sp_export_set_area (base, bbox->min()[Geom::X],
780                                               bbox->min()[Geom::Y],
781                                               bbox->max()[Geom::X],
782                                               bbox->max()[Geom::Y]);
783                 }
784             }
785             break;
786         case SELECTION_SELECTION:
787             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
788                 NRRect bbox;
789                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
790                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
791             }
792             break;
793         default:
794             /* Do nothing for page or for custom */
795             break;
796     }
798     return;
801 /// Called when one of the selection buttons was toggled.
802 static void
803 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
805     if (gtk_object_get_data (base, "update"))
806         return;
808     selection_type key, old_key;
809     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
810     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
812     /* Ignore all "turned off" events unless we're the only active button */
813     if (!gtk_toggle_button_get_active (tb) ) {
815         /* Don't let the current selection be deactived - but rerun the
816            activate to allow the user to renew the values */
817         if (key == old_key) {
818             gtk_toggle_button_set_active ( tb, TRUE );
819         }
821         return;
822     }
824     /* Turn off the currently active button unless it's us */
825     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
827     if (old_key != key) {
828         gtk_toggle_button_set_active
829             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
830               FALSE );
831     }
833     if ( SP_ACTIVE_DESKTOP )
834     {
835         SPDocument *doc;
836         Geom::OptRect bbox;
837         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
839         /* Notice how the switch is used to 'fall through' here to get
840            various backups.  If you modify this without noticing you'll
841            probabaly screw something up. */
842         switch (key) {
843             case SELECTION_SELECTION:
844                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
845                 {
846                     bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds();
847                     /* Only if there is a selection that we can set
848                        do we break, otherwise we fall through to the
849                        drawing */
850                     // std::cout << "Using selection: SELECTION" << std::endl;
851                     key = SELECTION_SELECTION;
852                     break;
853                 }
854             case SELECTION_DRAWING:
855                 /** \todo
856                  * This returns wrong values if the document has a viewBox.
857                  */
858                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
859                 /* If the drawing is valid, then we'll use it and break
860                    otherwise we drop through to the page settings */
861                 if (bbox) {
862                     // std::cout << "Using selection: DRAWING" << std::endl;
863                     key = SELECTION_DRAWING;
864                     break;
865                 }
866             case SELECTION_PAGE:
867                 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
868                                   Geom::Point(sp_document_width(doc), sp_document_height(doc)));
870                 // std::cout << "Using selection: PAGE" << std::endl;
871                 key = SELECTION_PAGE;
872                 break;
873             case SELECTION_CUSTOM:
874             default:
875                 break;
876         } // switch
878         // remember area setting
879         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
880         prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
882         if ( key != SELECTION_CUSTOM && bbox ) {
883             sp_export_set_area (base, bbox->min()[Geom::X],
884                                       bbox->min()[Geom::Y],
885                                       bbox->max()[Geom::X],
886                                       bbox->max()[Geom::Y]);
887         }
889     } // end of if ( SP_ACTIVE_DESKTOP )
892     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
893         GtkWidget * file_entry;
894         const gchar * filename = NULL;
895         float xdpi = 0.0, ydpi = 0.0;
897         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
899         switch (key) {
900             case SELECTION_PAGE:
901             case SELECTION_DRAWING: {
902                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
903                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
905                 if (filename == NULL) {
906                     if (doc_export_name != NULL) {
907                         filename = g_strdup(doc_export_name);
908                     } else {
909                         filename = g_strdup("");
910                     }
911                 }
912                 break;
913             }
914             case SELECTION_SELECTION:
915                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
917                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
919                     /* If we still don't have a filename -- let's build
920                        one that's nice */
921                     if (filename == NULL) {
922                         const gchar * id = NULL;
923                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
924                         for(; reprlst != NULL; reprlst = reprlst->next) {
925                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
926                             if (repr->attribute("id")) {
927                                 id = repr->attribute("id");
928                                 break;
929                             }
930                         }
932                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
933                     }
934                 }
935                 break;
936             case SELECTION_CUSTOM:
937             default:
938                 break;
939         }
941         if (filename != NULL) {
942             g_free(original_name);
943             original_name = g_strdup(filename);
944             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
945         }
947         if (xdpi != 0.0) {
948             sp_export_value_set(base, "xdpi", xdpi);
949         }
951         /* These can't be separate, and setting x sets y, so for
952            now setting this is disabled.  Hopefully it won't be in
953            the future */
954         if (FALSE && ydpi != 0.0) {
955             sp_export_value_set(base, "ydpi", ydpi);
956         }
957     }
959     return;
960 } // end of sp_export_area_toggled()
962 /// Called when dialog is deleted
963 static gint
964 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
966     g_object_set_data (base, "cancel", (gpointer) 1);
967     return TRUE;
968 } // end of sp_export_progress_delete()
970 /// Called when progress is cancelled
971 static void
972 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
974     g_object_set_data (base, "cancel", (gpointer) 1);
975 } // end of sp_export_progress_cancel()
977 /// Called for every progress iteration
978 static unsigned int
979 sp_export_progress_callback (float value, void *data)
981     GtkWidget *prg;
982     int evtcount;
984     if (g_object_get_data ((GObject *) data, "cancel"))
985         return FALSE;
987     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
988     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
990     evtcount = 0;
991     while ((evtcount < 16) && gdk_events_pending ()) {
992             gtk_main_iteration_do (FALSE);
993             evtcount += 1;
994     }
996     gtk_main_iteration_do (FALSE);
998     return TRUE;
1000 } // end of sp_export_progress_callback()
1002 GtkWidget *
1003 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1004     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1006     dlg = gtk_dialog_new ();
1007     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1008     prg = gtk_progress_bar_new ();
1009     sp_transientize (dlg);
1010     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1011     g_object_set_data ((GObject *) base, "progress", prg);
1013     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1015     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1016                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1017     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1018                         prg, FALSE, FALSE, 4 );
1019     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1020                                   GTK_STOCK_CANCEL,
1021                                   GTK_RESPONSE_CANCEL );
1023     g_signal_connect ( (GObject *) dlg, "delete_event",
1024                        (GCallback) sp_export_progress_delete, base);
1025     g_signal_connect ( (GObject *) btn, "clicked",
1026                        (GCallback) sp_export_progress_cancel, base);
1027     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1028     gtk_widget_show_all (dlg);
1030     return dlg;
1033 // FIXME: Some lib function should be available to do this ...
1034 static gchar *
1035 filename_add_extension (const gchar *filename, const gchar *extension)
1037   gchar *dot;
1039   dot = strrchr (filename, '.');
1040   if ( !dot )
1041     return g_strconcat (filename, ".", extension, NULL);
1042   {
1043     if (dot[1] == '\0')
1044       return g_strconcat (filename, extension, NULL);
1045     else
1046     {
1047       if (g_strcasecmp (dot + 1, extension) == 0)
1048         return g_strdup (filename);
1049       else
1050       {
1051         return g_strconcat (filename, ".", extension, NULL);
1052       }
1053     }
1054   }
1057 /// Called when export button is clicked
1058 static void
1059 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1061     if (!SP_ACTIVE_DESKTOP) return;
1063     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1065     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1066     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1067     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1068     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1069         // Batch export of selected objects
1071         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1072         gint n = 0;
1074         if (num < 1)
1075             return;
1077         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1078         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1079         g_free (progress_text);
1081         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1082              i != NULL;
1083              i = i->next) {
1084             SPItem *item = (SPItem *) i->data;
1085             // retrieve export filename hint
1086             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1087             if (!fn) {
1088                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1089             }
1091             // retrieve export dpi hints
1092             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1093             gdouble dpi = 0.0;
1094             if (dpi_hint) {
1095                 dpi = atof(dpi_hint);
1096             }
1097             if (dpi == 0.0) {
1098                 dpi = DPI_BASE;
1099             }
1101             Geom::OptRect area;
1102             sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1103             if (area) {
1104                 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1105                 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1107                 if (width > 1 && height > 1) {
1108                     /* Do export */
1109                     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1110                                              *area, width, height, dpi, dpi,
1111                                              nv->pagecolor,
1112                                              NULL, NULL, TRUE,  // overwrite without asking
1113                                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1114                             )) {
1115                         gchar * error;
1116                         gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1117                         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1118                         sp_ui_error_dialog(error);
1119                         g_free(safeFile);
1120                         g_free(error);
1121                     }
1122                 }
1123             }
1124             n++;
1125             sp_export_progress_callback((float)n/num, base);
1126         }
1128         gtk_widget_destroy (prog_dlg);
1129         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1131     } else {
1133     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1134     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1136     float const x0 = sp_export_value_get_px(base, "x0");
1137     float const y0 = sp_export_value_get_px(base, "y0");
1138     float const x1 = sp_export_value_get_px(base, "x1");
1139     float const y1 = sp_export_value_get_px(base, "y1");
1140     float const xdpi = sp_export_value_get(base, "xdpi");
1141     float const ydpi = sp_export_value_get(base, "ydpi");
1142     unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1143     unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1145     if (filename == NULL || *filename == '\0') {
1146         sp_ui_error_dialog(_("You have to enter a filename"));
1147         return;
1148     }
1150     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1151         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1152         return;
1153     }
1155     gchar *dirname = g_path_get_dirname(filename);
1156     if ( dirname == NULL
1157          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1158     {
1159         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1160         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1161                                        safeDir);
1162         sp_ui_error_dialog(error);
1163         g_free(safeDir);
1164         g_free(error);
1165         g_free(dirname);
1166         return;
1167     }
1168     g_free(dirname);
1170     // make sure that .png is the extension of the file:
1171     gchar * filename_ext = filename_add_extension(filename, "png");
1172     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1174     gchar *fn = g_path_get_basename (filename_ext);
1176     gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1177     g_free (fn);
1178     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1179     g_free (progress_text);
1181     /* Do export */
1182     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1183                              Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1184                              nv->pagecolor,
1185                              sp_export_progress_callback, base, FALSE,
1186                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1187             )) {
1188         gchar * error;
1189         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1190         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1191         sp_ui_error_dialog(error);
1192         g_free(safeFile);
1193         g_free(error);
1194     }
1196     /* Reset the filename so that it can be changed again by changing
1197        selections and all that */
1198     g_free(original_name);
1199     original_name = g_strdup(filename_ext);
1200     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1202     gtk_widget_destroy (prog_dlg);
1203     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1205     /* Setup the values in the document */
1206     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1207         case SELECTION_PAGE:
1208         case SELECTION_DRAWING: {
1209             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1210             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1211             bool modified = false;
1212             const gchar * temp_string;
1214             bool saved = sp_document_get_undo_sensitive(doc);
1215             sp_document_set_undo_sensitive(doc, false);
1217             temp_string = repr->attribute("inkscape:export-filename");
1218             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1219                 repr->setAttribute("inkscape:export-filename", filename_ext);
1220                 modified = true;
1221             }
1222             temp_string = repr->attribute("inkscape:export-xdpi");
1223             if (temp_string == NULL || xdpi != atof(temp_string)) {
1224                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1225                 modified = true;
1226             }
1227             temp_string = repr->attribute("inkscape:export-ydpi");
1228             if (temp_string == NULL || xdpi != atof(temp_string)) {
1229                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1230                 modified = true;
1231             }
1232             sp_document_set_undo_sensitive(doc, saved);
1234             if (modified) {
1235                 doc->setModifiedSinceSave();
1236             }
1237             break;
1238         }
1239         case SELECTION_SELECTION: {
1240             const GSList * reprlst;
1241             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1242             bool modified = false;
1244             bool saved = sp_document_get_undo_sensitive(doc);
1245             sp_document_set_undo_sensitive(doc, false);
1246             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1248             for(; reprlst != NULL; reprlst = reprlst->next) {
1249                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1250                 const gchar * temp_string;
1252                 if (repr->attribute("id") == NULL ||
1253                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1254                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1255                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1256                     temp_string = repr->attribute("inkscape:export-filename");
1257                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1258                         repr->setAttribute("inkscape:export-filename", filename_ext);
1259                         modified = true;
1260                     }
1261                 }
1262                 temp_string = repr->attribute("inkscape:export-xdpi");
1263                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1264                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1265                     modified = true;
1266                 }
1267                 temp_string = repr->attribute("inkscape:export-ydpi");
1268                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1269                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1270                     modified = true;
1271                 }
1272             }
1273             sp_document_set_undo_sensitive(doc, saved);
1275             if (modified) {
1276                 doc->setModifiedSinceSave();
1277             }
1278             break;
1279         }
1280         default:
1281             break;
1282     }
1284     g_free (filename_ext);
1286     }
1288 } // end of sp_export_export_clicked()
1290 /// Called when Browse button is clicked
1291 static void
1292 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1294     GtkWidget *fs, *fe;
1295     const gchar *filename;
1297     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1298                                       (GtkWindow*)dlg,
1299                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1300                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1301                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1302                                       NULL );
1304 #ifdef WITH_GNOME_VFS
1305     if (gnome_vfs_initialized()) {
1306         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1307     }
1308 #endif
1310     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1312     sp_transientize (fs);
1314     gtk_window_set_modal(GTK_WINDOW (fs), true);
1316     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1318     if (*filename == '\0') {
1319         filename = homedir_path(NULL);
1320     }
1322     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1324     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1325     {
1326         gchar *file;
1328         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1330         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1331         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1333         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1335         g_free(utf8file);
1336         g_free(file);
1337     }
1339     gtk_widget_destroy (fs);
1341     return;
1342 } // end of sp_export_browse_clicked()
1344 // TODO: Move this to nr-rect-fns.h.
1345 static bool
1346 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1348     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1349     return (
1350         (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1351         (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1352         (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1353         (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1354         );
1357 /**
1358     \brief  This function is used to detect the current selection setting
1359             based on the values in the x0, y0, x1 and y0 fields.
1360     \param  base  The export dialog itself
1362     One of the most confusing parts of this function is why the array
1363     is built at the beginning.  What needs to happen here is that we
1364     should always check the current selection to see if it is the valid
1365     one.  While this is a performance improvement it is also a usability
1366     one during the cases where things like selections and drawings match
1367     size.  This way buttons change less 'randomly' (atleast in the eyes
1368     of the user).  To do this an array is built where the current selection
1369     type is placed first, and then the others in an order from smallest
1370     to largest (this can be configured by reshuffling \c test_order).
1372     All of the values in this function are rounded to two decimal places
1373     because that is what is shown to the user.  While everything is kept
1374     more accurate than that, the user can't control more acurrate than
1375     that, so for this to work for them - it needs to check on that level
1376     of accuracy.
1378     \todo finish writing this up
1379 */
1380 static void
1381 sp_export_detect_size(GtkObject * base) {
1382     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1383     selection_type this_test[SELECTION_NUMBER_OF + 1];
1384     selection_type key = SELECTION_NUMBER_OF;
1386     Geom::Point x(sp_export_value_get_px (base, "x0"),
1387                   sp_export_value_get_px (base, "y0"));
1388     Geom::Point y(sp_export_value_get_px (base, "x1"),
1389                   sp_export_value_get_px (base, "y1"));
1390     Geom::Rect current_bbox(x, y);
1391     //std::cout << "Current " << current_bbox;
1393     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1394     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1395         this_test[i + 1] = test_order[i];
1396     }
1398     for (int i = 0;
1399             i < SELECTION_NUMBER_OF + 1 &&
1400                 key == SELECTION_NUMBER_OF &&
1401                 SP_ACTIVE_DESKTOP != NULL;
1402             i++) {
1403         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1404         switch (this_test[i]) {
1405             case SELECTION_SELECTION:
1406                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1407                     Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1409                     //std::cout << "Selection " << bbox;
1410                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1411                         key = SELECTION_SELECTION;
1412                     }
1413                 }
1414                 break;
1415             case SELECTION_DRAWING: {
1416                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1418                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1420                 // std::cout << "Drawing " << bbox2;
1421                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1422                     key = SELECTION_DRAWING;
1423                 }
1424                 break;
1425             }
1427             case SELECTION_PAGE: {
1428                 SPDocument *doc;
1430                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1432                 Geom::Point x(0.0, 0.0);
1433                 Geom::Point y(sp_document_width(doc),
1434                               sp_document_height(doc));
1435                 Geom::Rect bbox(x, y);
1437                 // std::cout << "Page " << bbox;
1438                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1439                     key = SELECTION_PAGE;
1440                 }
1442                 break;
1443            }
1444         default:
1445            break;
1446         }
1447     }
1448     // std::cout << std::endl;
1450     if (key == SELECTION_NUMBER_OF) {
1451         key = SELECTION_CUSTOM;
1452     }
1454     /* We're now using a custom size, not a fixed one */
1455     /* printf("Detecting state: %s\n", selection_names[key]); */
1456     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1457     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1458     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1459     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1461     return;
1462 } /* sp_export_detect_size */
1464 /// Called when area x0 value is changed
1465 static void
1466 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1468     float x0, x1, xdpi, width;
1470     if (gtk_object_get_data (base, "update"))
1471         return;
1473     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1474             (base, "units")))
1475     {
1476         return;
1477     }
1479     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1481     x0 = sp_export_value_get_px (base, "x0");
1482     x1 = sp_export_value_get_px (base, "x1");
1483     xdpi = sp_export_value_get (base, "xdpi");
1485     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1487     if (width < SP_EXPORT_MIN_SIZE) {
1488         const gchar *key;
1489         width = SP_EXPORT_MIN_SIZE;
1490         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1492         if (!strcmp (key, "x0")) {
1493             x1 = x0 + width * DPI_BASE / xdpi;
1494             sp_export_value_set_px (base, "x1", x1);
1495         } else {
1496             x0 = x1 - width * DPI_BASE / xdpi;
1497             sp_export_value_set_px (base, "x0", x0);
1498         }
1499     }
1501     sp_export_value_set_px (base, "width", x1 - x0);
1502     sp_export_value_set (base, "bmwidth", width);
1504     sp_export_detect_size(base);
1506     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1508     return;
1509 } // end of sp_export_area_x_value_changed()
1511 /// Called when area y0 value is changed.
1512 static void
1513 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1515     float y0, y1, ydpi, height;
1517     if (gtk_object_get_data (base, "update"))
1518         return;
1520     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1521            (base, "units")))
1522     {
1523         return;
1524     }
1526     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1528     y0 = sp_export_value_get_px (base, "y0");
1529     y1 = sp_export_value_get_px (base, "y1");
1530     ydpi = sp_export_value_get (base, "ydpi");
1532     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1534     if (height < SP_EXPORT_MIN_SIZE) {
1535         const gchar *key;
1536         height = SP_EXPORT_MIN_SIZE;
1537         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1538         if (!strcmp (key, "y0")) {
1539             y1 = y0 + height * DPI_BASE / ydpi;
1540             sp_export_value_set_px (base, "y1", y1);
1541         } else {
1542             y0 = y1 - height * DPI_BASE / ydpi;
1543             sp_export_value_set_px (base, "y0", y0);
1544         }
1545     }
1547     sp_export_value_set_px (base, "height", y1 - y0);
1548     sp_export_value_set (base, "bmheight", height);
1550     sp_export_detect_size(base);
1552     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1554     return;
1555 } // end of sp_export_area_y_value_changed()
1557 /// Called when x1-x0 or area width is changed
1558 static void
1559 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1561     float x0, x1, xdpi, width, bmwidth;
1563     if (gtk_object_get_data (base, "update"))
1564         return;
1566     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1567            (base, "units"))) {
1568         return;
1569     }
1571     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1573     x0 = sp_export_value_get_px (base, "x0");
1574     x1 = sp_export_value_get_px (base, "x1");
1575     xdpi = sp_export_value_get (base, "xdpi");
1576     width = sp_export_value_get_px (base, "width");
1577     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1579     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1581         bmwidth = SP_EXPORT_MIN_SIZE;
1582         width = bmwidth * DPI_BASE / xdpi;
1583         sp_export_value_set_px (base, "width", width);
1584     }
1586     sp_export_value_set_px (base, "x1", x0 + width);
1587     sp_export_value_set (base, "bmwidth", bmwidth);
1589     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1591     return;
1592 } // end of sp_export_area_width_value_changed()
1594 /// Called when y1-y0 or area height is changed.
1595 static void
1596 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1599     float y0, y1, ydpi, height, bmheight;
1601     if (gtk_object_get_data (base, "update"))
1602         return;
1604     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1605            (base, "units"))) {
1606         return;
1607     }
1609     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1611     y0 = sp_export_value_get_px (base, "y0");
1612     y1 = sp_export_value_get_px (base, "y1");
1613     ydpi = sp_export_value_get (base, "ydpi");
1614     height = sp_export_value_get_px (base, "height");
1615     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1617     if (bmheight < SP_EXPORT_MIN_SIZE) {
1618         bmheight = SP_EXPORT_MIN_SIZE;
1619         height = bmheight * DPI_BASE / ydpi;
1620         sp_export_value_set_px (base, "height", height);
1621     }
1623     sp_export_value_set_px (base, "y1", y0 + height);
1624     sp_export_value_set (base, "bmheight", bmheight);
1626     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1628     return;
1629 } // end of sp_export_area_height_value_changed()
1631 /**
1632     \brief  A function to set the ydpi
1633     \param  base  The export dialog
1635     This function grabs all of the y values and then figures out the
1636     new bitmap size based on the changing dpi value.  The dpi value is
1637     gotten from the xdpi setting as these can not currently be independent.
1638 */
1639 static void
1640 sp_export_set_image_y (GtkObject *base)
1642     float y0, y1, xdpi;
1644     y0 = sp_export_value_get_px (base, "y0");
1645     y1 = sp_export_value_get_px (base, "y1");
1646     xdpi = sp_export_value_get (base, "xdpi");
1648     sp_export_value_set (base, "ydpi", xdpi);
1649     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1651     return;
1652 } // end of sp_export_set_image_y()
1654 /**
1655     \brief  A function to set the xdpi
1656     \param  base  The export dialog
1658     This function grabs all of the x values and then figures out the
1659     new bitmap size based on the changing dpi value.  The dpi value is
1660     gotten from the xdpi setting as these can not currently be independent.
1661 */
1662 static void
1663 sp_export_set_image_x (GtkObject *base)
1665     float x0, x1, xdpi;
1667     x0 = sp_export_value_get_px (base, "x0");
1668     x1 = sp_export_value_get_px (base, "x1");
1669     xdpi = sp_export_value_get (base, "xdpi");
1671     sp_export_value_set (base, "ydpi", xdpi);
1672     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1674     return;
1675 } // end of sp_export_set_image_x()
1677 /// Called when pixel width is changed
1678 static void
1679 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1681     float x0, x1, bmwidth, xdpi;
1683     if (gtk_object_get_data (base, "update"))
1684         return;
1686     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1687            (base, "units"))) {
1688        return;
1689     }
1691     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1693     x0 = sp_export_value_get_px (base, "x0");
1694     x1 = sp_export_value_get_px (base, "x1");
1695     bmwidth = sp_export_value_get (base, "bmwidth");
1697     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1698         bmwidth = SP_EXPORT_MIN_SIZE;
1699         sp_export_value_set (base, "bmwidth", bmwidth);
1700     }
1702     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1703     sp_export_value_set (base, "xdpi", xdpi);
1705     sp_export_set_image_y (base);
1707     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1709     return;
1710 } // end of sp_export_bitmap_width_value_changed()
1712 /// Called when pixel height is changed
1713 static void
1714 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1716     float y0, y1, bmheight, xdpi;
1718     if (gtk_object_get_data (base, "update"))
1719         return;
1721     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1722            (base, "units"))) {
1723        return;
1724     }
1726     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1728     y0 = sp_export_value_get_px (base, "y0");
1729     y1 = sp_export_value_get_px (base, "y1");
1730     bmheight = sp_export_value_get (base, "bmheight");
1732     if (bmheight < SP_EXPORT_MIN_SIZE) {
1733         bmheight = SP_EXPORT_MIN_SIZE;
1734         sp_export_value_set (base, "bmheight", bmheight);
1735     }
1737     xdpi = bmheight * DPI_BASE / (y1 - y0);
1738     sp_export_value_set (base, "xdpi", xdpi);
1740     sp_export_set_image_x (base);
1742     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1744     return;
1745 } // end of sp_export_bitmap_width_value_changed()
1747 /**
1748     \brief  A function to adjust the bitmap width when the xdpi value changes
1749     \param  adj  The adjustment that was changed
1750     \param  base The export dialog itself
1752     The first thing this function checks is to see if we are doing an
1753     update.  If we are, this function just returns because there is another
1754     instance of it that will handle everything for us.  If there is a
1755     units change, we also assume that everyone is being updated appropriately
1756     and there is nothing for us to do.
1758     If we're the highest level function, we set the update flag, and
1759     continue on our way.
1761     All of the values are grabbed using the \c sp_export_value_get functions
1762     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1763     xdpi value is saved in the preferences for the next time the dialog
1764     is opened.  (does the selection dpi need to be set here?)
1766     A check is done to to ensure that we aren't outputing an invalid width,
1767     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1768     changed to make it valid.
1770     After all of this the bitmap width is changed.
1772     We also change the ydpi.  This is a temporary hack as these can not
1773     currently be independent.  This is likely to change in the future.
1774 */
1775 void
1776 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1778     float x0, x1, xdpi, bmwidth;
1780     if (gtk_object_get_data (base, "update"))
1781         return;
1783     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1784            (base, "units"))) {
1785        return;
1786     }
1788     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1790     x0 = sp_export_value_get_px (base, "x0");
1791     x1 = sp_export_value_get_px (base, "x1");
1792     xdpi = sp_export_value_get (base, "xdpi");
1794     // remember xdpi setting
1795     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1796     prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1798     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1800     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1801         bmwidth = SP_EXPORT_MIN_SIZE;
1802         if (x1 != x0)
1803             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1804         else
1805             xdpi = DPI_BASE;
1806         sp_export_value_set (base, "xdpi", xdpi);
1807     }
1809     sp_export_value_set (base, "bmwidth", bmwidth);
1811     sp_export_set_image_y (base);
1813     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1815     return;
1816 } // end of sp_export_xdpi_value_changed()
1819 /**
1820     \brief  A function to change the area that is used for the exported
1821             bitmap.
1822     \param  base  This is the export dialog
1823     \param  x0    Horizontal upper left hand corner of the picture in points
1824     \param  y0    Vertical upper left hand corner of the picture in points
1825     \param  x1    Horizontal lower right hand corner of the picture in points
1826     \param  y1    Vertical lower right hand corner of the picture in points
1828     This function just calls \c sp_export_value_set_px for each of the
1829     parameters that is passed in.  This allows for setting them all in
1830     one convient area.
1832     Update is set to suspend all of the other test running while all the
1833     values are being set up.  This allows for a performance increase, but
1834     it also means that the wrong type won't be detected with only some of
1835     the values set.  After all the values are set everyone is told that
1836     there has been an update.
1837 */
1838 static void
1839 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1841     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1842     sp_export_value_set_px (base, "x1", x1);
1843     sp_export_value_set_px (base, "y1", y1);
1844     sp_export_value_set_px (base, "x0", x0);
1845     sp_export_value_set_px (base, "y0", y0);
1846     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1848     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1849     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1851     return;
1854 /**
1855     \brief  Sets the value of an adjustment
1856     \param  base  The export dialog
1857     \param  key   Which adjustment to set
1858     \param  val   What value to set it to
1860     This function finds the adjustment using the data stored in the
1861     export dialog.  After finding the adjustment it then sets
1862     the value of it.
1863 */
1864 static void
1865 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1867     GtkAdjustment *adj;
1869     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1871     gtk_adjustment_set_value (adj, val);
1874 /**
1875     \brief  A function to set a value using the units points
1876     \param  base  The export dialog
1877     \param  key   Which value should be set
1878     \param  val   What the value should be in points
1880     This function first gets the adjustment for the key that is passed
1881     in.  It then figures out what units are currently being used in the
1882     dialog.  After doing all of that, it then converts the incoming
1883     value and sets the adjustment.
1884 */
1885 static void
1886 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1888     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1890     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1892     return;
1895 /**
1896     \brief  Get the value of an adjustment in the export dialog
1897     \param  base  The export dialog
1898     \param  key   Which adjustment is being looked for
1899     \return The value in the specified adjustment
1901     This function gets the adjustment from the data field in the export
1902     dialog.  It then grabs the value from the adjustment.
1903 */
1904 static float
1905 sp_export_value_get ( GtkObject *base, const gchar *key )
1907     GtkAdjustment *adj;
1909     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1911     return adj->value;
1914 /**
1915     \brief  Grabs a value in the export dialog and converts the unit
1916             to points
1917     \param  base  The export dialog
1918     \param  key   Which value should be returned
1919     \return The value in the adjustment in points
1921     This function, at its most basic, is a call to \c sp_export_value_get
1922     to get the value of the adjustment.  It then finds the units that
1923     are being used by looking at the "units" attribute of the export
1924     dialog.  Using that it converts the returned value into points.
1925 */
1926 static float
1927 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1929     float value = sp_export_value_get(base, key);
1930     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1932     return sp_units_get_pixels (value, *unit);
1933 } // end of sp_export_value_get_px()
1935 /**
1936     \brief  This function is called when the filename is changed by
1937             anyone.  It resets the virgin bit.
1938     \param  object  Text entry box
1939     \param  data    The export dialog
1940     \return None
1942     This function gets called when the text area is modified.  It is
1943     looking for the case where the text area is modified from its
1944     original value.  In that case it sets the "filename-modified" bit
1945     to TRUE.  If the text dialog returns back to the original text, the
1946     bit gets reset.  This should stop simple mistakes.
1947 */
1948 static void
1949 sp_export_filename_modified (GtkObject * object, gpointer data)
1951     GtkWidget * text_entry = (GtkWidget *)object;
1952     GtkWidget * export_dialog = (GtkWidget *)data;
1954     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1955         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1956 //        printf("Modified: FALSE\n");
1957     } else {
1958         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1959 //        printf("Modified: TRUE\n");
1960     }
1962     return;
1963 } // end sp_export_filename_modified
1965 /*
1966   Local Variables:
1967   mode:c++
1968   c-file-style:"stroustrup"
1969   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1970   indent-tabs-mode:nil
1971   fill-column:99
1972   End:
1973 */
1974 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :