Code

move extension adding to the right place so it's added on export, not on browse
[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>
34 #include <glibmm/i18n.h>
35 #include "helper/unit-menu.h"
36 #include "helper/units.h"
37 #include "unit-constants.h"
38 #include "helper/window.h"
39 #include "inkscape-private.h"
40 #include "document.h"
41 #include "desktop-handles.h"
42 #include "sp-item.h"
43 #include "selection.h"
44 #include "file.h"
45 #include "macros.h"
46 #include "sp-namedview.h"
47 #include "selection-chemistry.h"
49 #include "dialog-events.h"
50 #include "../prefs-utils.h"
51 #include "../verbs.h"
52 #include "../interface.h"
54 #include "extension/output.h"
55 #include "extension/db.h"
57 #include "io/sys.h"
59 #include "helper/png-write.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);
76                                              
77 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
78                                                    GtkObject *base);
79                                              
80 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
81                                                    GtkObject *base);
82                                                  
83 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
84                                                    GtkObject *base);
85                                                   
86 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
87                                                    GtkObject *base);
88                                                    
89 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
90                                                    GtkObject *base);
91                                                    
92 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
93                                                    GtkObject *base);
94                                            
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 const gchar *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;
169     prefs_set_int_attribute (prefs_path, "x", x);
170     prefs_set_int_attribute (prefs_path, "y", y);
171     prefs_set_int_attribute (prefs_path, "w", w);
172     prefs_set_int_attribute (prefs_path, "h", h);
174     return FALSE; // which means, go ahead and destroy it
176 } // end of sp_export_dialog_delete()
178 /**
179     \brief  Creates a new spin button for the export dialog
180     \param  key  The name of the spin button
181     \param  val  A default value for the spin button
182     \param  min  Minimum value for the spin button
183     \param  max  Maximum value for the spin button
184     \param  step The step size for the spin button
185     \param  page Size of the page increment
186     \param  us   Unit selector that effects this spin button
187     \param  t    Table to put the spin button in
188     \param  x    X location in the table \c t to start with
189     \param  y    Y location in the table \c t to start with
190     \param  ll   Text to put on the left side of the spin button (optional)
191     \param  lr   Text to put on the right side of the spin button (optional)
192     \param  digits  Number of digits to display after the decimal
193     \param  sensitive  Whether the spin button is sensitive or not
194     \param  cb   Callback for when this spin button is changed (optional)
195     \param  dlg  Export dialog the spin button is being placed in
197 */
198 static void
199 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
200                            float step, float page, GtkWidget *us,
201                            GtkWidget *t, int x, int y,
202                            const gchar *ll, const gchar *lr,
203                            int digits, unsigned int sensitive,
204                            GCallback cb, GtkWidget *dlg )
206     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
207     gtk_object_set_data (a, "key", key);
208     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
210     if (us) {
211         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
212                                           GTK_ADJUSTMENT (a) );
213     }
215     int pos = 0;
217     GtkWidget *l = NULL;
219     if (ll) {
221         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
222         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
223         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
224                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
225         gtk_widget_set_sensitive (l, sensitive);
226         pos += 1;
228     }
230     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
231     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
232                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
233     gtk_widget_set_size_request (sb, 80, -1);
234     gtk_widget_set_sensitive (sb, sensitive);
235     pos += 1;
237     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
239     if (lr) {
241         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
242         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
243         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
244                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
245         gtk_widget_set_sensitive (l, sensitive);
246         pos += 1;
248         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
249     }
251     if (cb)
252         gtk_signal_connect (a, "value_changed", cb, dlg);
254     return;
255 } // end of sp_export_spinbutton_new()
258 static Gtk::VBox *
259 sp_export_dialog_area_box (GtkWidget * dlg)
261     Gtk::VBox* vb = new Gtk::VBox(false, 3);
263     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
264     lbl->set_use_markup(true);
265     vb->pack_start(*lbl);
267     /* Units box */
268     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
269     /* gets added to the vbox later, but the unit selector is needed
270        earlier than that */
272     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
273     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
274     if (desktop)
275         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
276     unitbox->pack_end(*us, false, false, 0);
277     Gtk::Label* l = new Gtk::Label(_("Units:"));
278     unitbox->pack_end(*l, false, false, 3);
279     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
281     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
283     Gtk::ToggleButton* b;
284     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
285         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
286         b->set_data("key", GINT_TO_POINTER(i));
287         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
288         togglebox->pack_start(*b, false, true, 0);
289         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
290                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
291     }
293     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
294                        G_CALLBACK (sp_export_selection_changed), dlg );
295     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
296                        G_CALLBACK (sp_export_selection_modified), dlg );
297     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
298                        G_CALLBACK (sp_export_selection_changed), dlg );
299     
300     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
301     t->set_row_spacings (4);
302     t->set_col_spacings (4);
304     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
305                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
306                                G_CALLBACK ( sp_export_area_x_value_changed), 
307                                dlg );
309     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
310                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
311                                G_CALLBACK (sp_export_area_x_value_changed), 
312                                dlg );
314     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
315                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
316                                G_CALLBACK 
317                                    (sp_export_area_width_value_changed), 
318                                dlg );
320     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
321                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
322                                G_CALLBACK (sp_export_area_y_value_changed), 
323                                dlg );
325     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
326                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
327                                G_CALLBACK (sp_export_area_y_value_changed), 
328                                dlg );
330     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
331                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
332                                G_CALLBACK (sp_export_area_height_value_changed), 
333                                dlg );
335     vb->pack_start(*togglebox, false, false, 3);
336     vb->pack_start(*t, false, false, 0);
337     vb->pack_start(*unitbox, false, false, 0);
339     return vb;
340 } // end of sp_export_dialog_area_box
343 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
345     if (id == NULL) /* This should never happen */
346         id = "bitmap";
348     gchar * directory = NULL;
350     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
351         // std::cout << "Directory from dialog" << std::endl;
352         directory = g_dirname(file_entry_text);
353     }
355     if (directory == NULL) {
356         /* Grab document directory */
357         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
358             // std::cout << "Directory from document" << std::endl;
359             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
360         }
361     }
363     if (directory == NULL) {
364         // std::cout << "Home Directory" << std::endl;
365         directory = homedir_path(NULL);
366     }
368     gchar * id_ext = g_strconcat(id, ".png", NULL);
369     gchar *filename = g_build_filename(directory, id_ext, NULL);
370     g_free(directory);
371     g_free(id_ext);
372     return filename;
375 static void
376 batch_export_clicked (GtkWidget *widget, GtkObject *base)
378     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
379     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
380         vb_singleexport->set_sensitive(false);
381     } else {
382         vb_singleexport->set_sensitive(true);
383     }
386 void
387 sp_export_dialog (void)
389     if (!dlg) {
391         gchar title[500];
392         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
394         dlg = sp_window_new (title, TRUE);
396         if (x == -1000 || y == -1000) {
397             x = prefs_get_int_attribute (prefs_path, "x", 0);
398             y = prefs_get_int_attribute (prefs_path, "y", 0);
399         }
401         if (w ==0 || h == 0) {
402             w = prefs_get_int_attribute (prefs_path, "w", 0);
403             h = prefs_get_int_attribute (prefs_path, "h", 0);
404         }
406 //        if (x<0) x=0;
407 //        if (y<0) y=0;
409         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
410         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
411             gtk_window_move ((GtkWindow *) dlg, x, y);
412         else
413             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
414         sp_transientize (dlg);
415         wd.win = dlg;
416         wd.stop = 0;
418         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
419                              G_CALLBACK (sp_transientize_callback), &wd);
421         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
422                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
424         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
425                              G_CALLBACK (sp_export_dialog_destroy), dlg);
427         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
428                              G_CALLBACK (sp_export_dialog_delete), dlg);
430         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
431                              G_CALLBACK (sp_export_dialog_delete), dlg);
433         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
434                              G_CALLBACK (sp_dialog_hide), dlg);
436         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
437                              G_CALLBACK (sp_dialog_unhide), dlg);
439         GtkTooltips *tt = gtk_tooltips_new();
441         Gtk::VBox *vb = new Gtk::VBox(false, 3);
442         vb->set_border_width(3);
443         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
445         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
446         vb_singleexport->set_border_width(0);
447         vb->pack_start(*vb_singleexport);
448         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
450         /* Export area frame */
451         {
452             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
453             area_box->set_border_width(3);
454             vb_singleexport->pack_start(*area_box, false, false, 0);
455         }
457         /* Bitmap size frame */
458         {
459             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
460             size_box->set_border_width(3);
462             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
463             lbl->set_use_markup(true);
464             size_box->pack_start(*lbl, false, false, 0);
465             const int rows = 2;
466             const int cols = 5;
467             const bool homogeneous = false;
468             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
469             t->set_row_spacings (4);
470             t->set_col_spacings (4);
471             size_box->pack_start(*t);
473             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
474                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
475                                        _("_Width:"), _("pixels at"), 0, 1,
476                                        G_CALLBACK 
477                                        (sp_export_bitmap_width_value_changed), 
478                                        dlg );
480             sp_export_spinbutton_new ( "xdpi", 
481                                        prefs_get_double_attribute 
482                                        ( "dialogs.export.defaultxdpi", 
483                                          "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_get_double_attribute 
501                                        ( "dialogs.export.defaultxdpi", 
502                                          "value", DPI_BASE), 
503                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
504                                        NULL, _("dpi"), 2, 0, NULL, dlg );
506             vb_singleexport->pack_start(*size_box);
507         }
509         /* File entry */
510         {
511             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
512             file_box->set_border_width(3);
514             // true = has mnemonic
515             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
516             flabel->set_use_markup(true);
517             file_box->pack_start(*flabel, false, false, 0);
519             Gtk::Entry *fe = new Gtk::Entry();
521             /*
522              * set the default filename to be that of the current path + document
523              * with .png extension
524              *
525              * One thing to notice here is that this filename may get
526              * overwritten, but it won't happen here.  The filename gets
527              * written into the text field, but then the button to select
528              * the area gets set.  In that code the filename can be changed
529              * if there are some with presidence in the document.  So, while
530              * this code sets the name first, it may not be the one users
531              * really see.
532              */
533             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
534             {
535                 gchar *name;
536                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
537                 const gchar *uri = SP_DOCUMENT_URI (doc);
538                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
539                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
540                 Inkscape::Extension::Output * oextension = NULL;
542                 if (text_extension != NULL) {
543                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
544                 }
546                 if (oextension != NULL) {
547                     gchar * old_extension = oextension->get_extension();
548                     if (g_str_has_suffix(uri, old_extension)) {
549                         gchar * uri_copy;
550                         gchar * extension_point;
551                         gchar * final_name;
553                         uri_copy = g_strdup(uri);
554                         extension_point = g_strrstr(uri_copy, old_extension);
555                         extension_point[0] = '\0';
557                         final_name = g_strconcat(uri_copy, ".png", NULL);
558                         fe->set_text(final_name);
560                         g_free(final_name);
561                         g_free(uri_copy);
562                     }
563                 } else {
564                     name = g_strconcat(uri, ".png", NULL);
565                     fe->set_text(name);
566                     g_free(name);
567                 }
569                 doc_export_name = g_strdup(fe->get_text().c_str());
570             }
571             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
572                                G_CALLBACK (sp_export_filename_modified), dlg);
574             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
576             {
577                 // true = has mnemonic
578                 Gtk::Button *b = new Gtk::Button();
580                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
581                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
582                         Gtk::ICON_SIZE_BUTTON);
583                 pixlabel->pack_start(*im);
585                 Gtk::Label *l = new Gtk::Label();
586                 l->set_markup_with_mnemonic(_("_Browse..."));
587                 pixlabel->pack_start(*l);
589                 b->add(*pixlabel);
591                 hb->pack_end (*b, false, false, 4);
592                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
593                                    G_CALLBACK (sp_export_browse_clicked), NULL );
594             }
596             hb->pack_start (*fe, true, true, 0);
597             file_box->add(*hb);
598             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
599             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
600             original_name = g_strdup(fe->get_text().c_str());
601             // pressing enter in the filename field is the same as clicking export:
602             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
603                                G_CALLBACK (sp_export_export_clicked), dlg );
604             // focus is in the filename initially:
605             fe->grab_focus();
607             // mnemonic in frame label moves focus to filename:
608             flabel->set_mnemonic_widget(*fe);
610             vb_singleexport->pack_start(*file_box);
611         }
613         {
614             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
615             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
616             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
617             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
618             batch_box->pack_start(*Glib::wrap(be), false, false);
619             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);
620             batch_box->show_all();
621             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
622             vb->pack_start(*batch_box);
623         }
625         {
626             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
627             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
628             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
629             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
630             hide_box->pack_start(*Glib::wrap(he), false, false);
631             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
632             hide_box->show_all();
633             vb->pack_start(*hide_box);
634         }
636         /* Buttons */
637         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
638         bb->set_border_width(3);
640         {
641             Gtk::Button *b = new Gtk::Button();
642             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
643             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
644                     Gtk::ICON_SIZE_BUTTON);
645             image_label->pack_start(*im);
647             Gtk::Label *l = new Gtk::Label();
648             l->set_markup_with_mnemonic(_("_Export"));
649             image_label->pack_start(*l);
651             b->add(*image_label);
652             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
653             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
654                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
655             bb->pack_end(*b, false, false, 0);
656         }
658         vb->pack_end(*bb, false, false, 0);
659         vb->show_all();
661     } // end of if (!dlg)
663     sp_export_find_default_selection(dlg);
665     gtk_window_present ((GtkWindow *) dlg);
667     return;
668 } // end of sp_export_dialog()
670 static void
671 sp_export_update_checkbuttons (GtkObject *base)
673     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
674     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
675     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
676     if (num >= 2) {
677         gtk_widget_set_sensitive (be, true);
678         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (_("Batch export %d selected objects"), num));
679     } else {
680         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
681         gtk_widget_set_sensitive (be, FALSE);
682     }
683     if (num > 0) {
684         gtk_widget_set_sensitive (he, true);
685     } else {
686         gtk_widget_set_sensitive (he, false);
687     }
690 static inline void
691 sp_export_find_default_selection(GtkWidget * dlg)
693     selection_type key = SELECTION_NUMBER_OF;
695     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
696         key = SELECTION_SELECTION;
697     }
699     /* Try using the preferences */
700     if (key == SELECTION_NUMBER_OF) {
701         const gchar *what = NULL;
702         int i = SELECTION_NUMBER_OF;
704         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
706         if (what != NULL) {
707             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
708                 if (!strcmp (what, selection_names[i])) {
709                     break;
710                 }
711             }
712         }
714         key = (selection_type)i;
715     }
717     if (key == SELECTION_NUMBER_OF) {
718         key = SELECTION_SELECTION;
719     }
721     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
722                                                        selection_names[key]);
723     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
725     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
729 /**
730  * \brief  If selection changed or a different document activated, we must 
731  * recalculate any chosen areas
732  *
733  */
734 static void
735 sp_export_selection_changed ( Inkscape::Application *inkscape, 
736                               Inkscape::Selection *selection, 
737                               GtkObject *base )
739     selection_type current_key;
740     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
742     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
743             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
744             was_empty) {
745         gtk_toggle_button_set_active
746             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
747               TRUE );
748     }
749     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
751     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
753     if (inkscape &&
754             SP_IS_INKSCAPE (inkscape) &&
755             selection &&
756             SELECTION_CUSTOM != current_key) {
757         GtkToggleButton * button;
758         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
759         sp_export_area_toggled(button, base);
760     } 
762     sp_export_update_checkbuttons (base);
763
765 static void
766 sp_export_selection_modified ( Inkscape::Application *inkscape, 
767                                Inkscape::Selection *selection, 
768                                guint flags,
769                                GtkObject *base )
771     selection_type current_key;
772     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
774     switch (current_key) {
775         case SELECTION_DRAWING:
776             if ( SP_ACTIVE_DESKTOP ) {
777                 SPDocument *doc;
778                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
779                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
780                 if (bbox) {
781                     sp_export_set_area (base, bbox->min()[NR::X],
782                                               bbox->min()[NR::Y],
783                                               bbox->max()[NR::X],
784                                               bbox->max()[NR::Y]);
785                 }
786             }
787             break;
788         case SELECTION_SELECTION:
789             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790                 NRRect bbox;
791                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
792                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
793             }
794             break;
795         default:
796             /* Do nothing for page or for custom */
797             break;
798     }
800     return;
803 /// Called when one of the selection buttons was toggled.
804 static void
805 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
807     if (gtk_object_get_data (base, "update"))
808         return;
810     selection_type key, old_key;
811     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
812     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
814     /* Ignore all "turned off" events unless we're the only active button */
815     if (!gtk_toggle_button_get_active (tb) ) {
817         /* Don't let the current selection be deactived - but rerun the
818            activate to allow the user to renew the values */
819         if (key == old_key) {
820             gtk_toggle_button_set_active ( tb, TRUE );
821         }
823         return;
824     }
826     /* Turn off the currently active button unless it's us */
827     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
829     if (old_key != key) {
830         gtk_toggle_button_set_active
831             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
832               FALSE );
833     }
835     if ( SP_ACTIVE_DESKTOP )
836     {
837         SPDocument *doc;
838         NR::Maybe<NR::Rect> bbox;
839         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
841         /* Notice how the switch is used to 'fall through' here to get
842            various backups.  If you modify this without noticing you'll
843            probabaly screw something up. */
844         switch (key) {
845             case SELECTION_SELECTION:
846                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
847                 {
848                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
849                     /* Only if there is a selection that we can set
850                        do we break, otherwise we fall through to the
851                        drawing */
852                     // std::cout << "Using selection: SELECTION" << std::endl;
853                     key = SELECTION_SELECTION;
854                     break;
855                 }
856             case SELECTION_DRAWING:
857                 /** \todo 
858                  * This returns wrong values if the document has a viewBox.
859                  */
860                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
861                 /* If the drawing is valid, then we'll use it and break
862                    otherwise we drop through to the page settings */
863                 if (bbox) {
864                     // std::cout << "Using selection: DRAWING" << std::endl;
865                     key = SELECTION_DRAWING;
866                     break;
867                 }
868             case SELECTION_PAGE:
869                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
870                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
871                                 );
873                 // std::cout << "Using selection: PAGE" << std::endl;
874                 key = SELECTION_PAGE;
875                 break;
876             case SELECTION_CUSTOM:
877             default:
878                 break;
879         } // switch
880         
881         // remember area setting
882         prefs_set_string_attribute ( "dialogs.export.exportarea", 
883                                      "value", selection_names[key]);
885         if ( key != SELECTION_CUSTOM && bbox ) {
886             sp_export_set_area (base, bbox->min()[NR::X],
887                                       bbox->min()[NR::Y],
888                                       bbox->max()[NR::X],
889                                       bbox->max()[NR::Y]);
890         }
891     
892     } // end of if ( SP_ACTIVE_DESKTOP )
895     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
896         GtkWidget * file_entry;
897         const gchar * filename = NULL;
898         float xdpi = 0.0, ydpi = 0.0;
900         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
902         switch (key) {
903             case SELECTION_PAGE:
904             case SELECTION_DRAWING: {
905                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
906                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
908                 if (filename == NULL) {
909                     if (doc_export_name != NULL) {
910                         filename = g_strdup(doc_export_name);
911                     } else {
912                         filename = g_strdup("");
913                     }
914                 }
915                 break;
916             }
917             case SELECTION_SELECTION:
918                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
920                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
922                     /* If we still don't have a filename -- let's build
923                        one that's nice */
924                     if (filename == NULL) {
925                         const gchar * id = NULL;
926                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
927                         for(; reprlst != NULL; reprlst = reprlst->next) {
928                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
929                             if (repr->attribute("id")) {
930                                 id = repr->attribute("id");
931                                 break;
932                             }
933                         }
935                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
936                     }
937                 }
938                 break;
939             case SELECTION_CUSTOM:
940             default:
941                 break;
942         }
944         if (filename != NULL) {
945             g_free(original_name);
946             original_name = g_strdup(filename);
947             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
948         }
950         if (xdpi != 0.0) {
951             sp_export_value_set(base, "xdpi", xdpi);
952         }
954         /* These can't be separate, and setting x sets y, so for
955            now setting this is disabled.  Hopefully it won't be in
956            the future */
957         if (FALSE && ydpi != 0.0) {
958             sp_export_value_set(base, "ydpi", ydpi);
959         }
960     }
962     return;
963 } // end of sp_export_area_toggled()
965 /// Called when dialog is deleted
966 static gint
967 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
969     g_object_set_data (base, "cancel", (gpointer) 1);
970     return TRUE;
971 } // end of sp_export_progress_delete()
973 /// Called when progress is cancelled
974 static void
975 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
977     g_object_set_data (base, "cancel", (gpointer) 1);
978 } // end of sp_export_progress_cancel()
980 /// Called for every progress iteration
981 static unsigned int
982 sp_export_progress_callback (float value, void *data)
984     GtkWidget *prg;
985     int evtcount;
987     if (g_object_get_data ((GObject *) data, "cancel"))
988         return FALSE;
990     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
991     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
993     evtcount = 0;
994     while ((evtcount < 16) && gdk_events_pending ()) {
995             gtk_main_iteration_do (FALSE);
996             evtcount += 1;
997     }
999     gtk_main_iteration_do (FALSE);
1001     return TRUE;
1003 } // end of sp_export_progress_callback()
1005 GtkWidget *
1006 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1007     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1008     
1009     dlg = gtk_dialog_new ();
1010     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1011     prg = gtk_progress_bar_new ();
1012     sp_transientize (dlg);
1013     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1014     g_object_set_data ((GObject *) base, "progress", prg);
1016     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1018     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1019                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1020     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1021                         prg, FALSE, FALSE, 4 );
1022     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1023                                   GTK_STOCK_CANCEL, 
1024                                   GTK_RESPONSE_CANCEL );
1025                                   
1026     g_signal_connect ( (GObject *) dlg, "delete_event", 
1027                        (GCallback) sp_export_progress_delete, base);
1028     g_signal_connect ( (GObject *) btn, "clicked", 
1029                        (GCallback) sp_export_progress_cancel, base);
1030     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1031     gtk_widget_show_all (dlg);
1033     return dlg;
1036 // FIXME: Some lib function should be available to do this ...
1037 static gchar *
1038 filename_add_extension (const gchar *filename, const gchar *extension)
1040   gchar *dot;
1042   dot = strrchr (filename, '.');
1043   if ( !dot )
1044     return g_strconcat (filename, ".", extension, NULL);
1045   {
1046     if (dot[1] == '\0')
1047       return g_strconcat (filename, extension, NULL);
1048     else
1049     {
1050       if (g_strcasecmp (dot + 1, extension) == 0)
1051         return g_strdup (filename);
1052       else
1053       {
1054         return g_strconcat (filename, ".", extension, NULL);
1055       }
1056     }
1057   }
1060 /// Called when export button is clicked
1061 static void
1062 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1064     if (!SP_ACTIVE_DESKTOP) return;
1066     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1068     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1069     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1070     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1071     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1072         // Batch export of selected objects
1074         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1075         gint n = 0;
1077         if (num < 1) 
1078             return;
1080         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1081         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1082         g_free (progress_text);
1084         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1085              i != NULL;
1086              i = i->next) {
1087             SPItem *item = (SPItem *) i->data;
1088             // retrieve export filename hint
1089             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1090             if (!fn) {
1091                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1092             }
1094             // retrieve export dpi hints
1095             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1096             gdouble dpi = 0.0;
1097             if (dpi_hint) {
1098                 dpi = atof(dpi_hint);
1099             }
1100             if (dpi == 0.0) {
1101                 dpi = DPI_BASE;
1102             }
1104             NRRect area;
1105             sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1107             gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1108             gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1110             if (width > 1 && height > 1) {
1111                 /* Do export */
1112                 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn, 
1113                                          area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, 
1114                                          nv->pagecolor, 
1115                                          NULL, NULL, TRUE,  // overwrite without asking 
1116                                          hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1117                         )) {
1118                     gchar * error;
1119                     gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1120                     error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1121                     sp_ui_error_dialog(error);
1122                     g_free(safeFile);
1123                     g_free(error);
1124                 }
1125             }
1126             n++;
1127             sp_export_progress_callback((float)n/num, base);
1128         }
1130         gtk_widget_destroy (prog_dlg);
1131         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1133     } else {
1135     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1136     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1138     float const x0 = sp_export_value_get_px(base, "x0");
1139     float const y0 = sp_export_value_get_px(base, "y0");
1140     float const x1 = sp_export_value_get_px(base, "x1");
1141     float const y1 = sp_export_value_get_px(base, "y1");
1142     float const xdpi = sp_export_value_get(base, "xdpi");
1143     float const ydpi = sp_export_value_get(base, "ydpi");
1144     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1145     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1147     if (filename == NULL || *filename == '\0') {
1148         sp_ui_error_dialog(_("You have to enter a filename"));
1149         return;
1150     }
1152     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1153         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1154         return;
1155     }
1157     gchar *dirname = g_path_get_dirname(filename);
1158     if ( dirname == NULL
1159          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1160     {
1161         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1162         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1163                                        safeDir);
1164         sp_ui_error_dialog(error);
1165         g_free(safeDir);
1166         g_free(error);
1167         g_free(dirname);
1168         return;
1169     }
1170     g_free(dirname);
1172     // make sure that .png is the extension of the file:
1173     gchar * filename_ext = filename_add_extension(filename, "png");
1174     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1176     gchar *fn = g_path_get_basename (filename_ext);
1178     gchar *progress_text = g_strdup_printf (_("Exporting %s (%d x %d)"), fn, width, height);
1179     g_free (fn);
1180     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1181     g_free (progress_text);
1182     
1183     /* Do export */
1184     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext, 
1185                              x0, y0, x1, y1, width, height, xdpi, ydpi, 
1186                              nv->pagecolor, 
1187                              sp_export_progress_callback, base, FALSE,
1188                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1189             )) {
1190         gchar * error;
1191         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1192         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1193         sp_ui_error_dialog(error);
1194         g_free(safeFile);
1195         g_free(error);
1196     }
1198     /* Reset the filename so that it can be changed again by changing
1199        selections and all that */
1200     g_free(original_name);
1201     original_name = g_strdup(filename_ext);
1202     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1204     gtk_widget_destroy (prog_dlg);
1205     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1207     /* Setup the values in the document */
1208     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1209         case SELECTION_PAGE:
1210         case SELECTION_DRAWING: {
1211             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1212             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1213             bool modified = false;
1214             const gchar * temp_string;
1216             bool saved = sp_document_get_undo_sensitive(doc);
1217             sp_document_set_undo_sensitive(doc, false);
1219             temp_string = repr->attribute("inkscape:export-filename");
1220             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1221                 repr->setAttribute("inkscape:export-filename", filename_ext);
1222                 modified = true;
1223             }
1224             temp_string = repr->attribute("inkscape:export-xdpi");
1225             if (temp_string == NULL || xdpi != atof(temp_string)) {
1226                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1227                 modified = true;
1228             }
1229             temp_string = repr->attribute("inkscape:export-ydpi");
1230             if (temp_string == NULL || xdpi != atof(temp_string)) {
1231                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1232                 modified = true;
1233             }
1235             if (modified)
1236                 repr->setAttribute("sodipodi:modified", "TRUE");
1237             sp_document_set_undo_sensitive(doc, saved);
1238             break;
1239         }
1240         case SELECTION_SELECTION: {
1241             const GSList * reprlst;
1242             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1243             bool modified = false;
1245             bool saved = sp_document_get_undo_sensitive(doc);
1246             sp_document_set_undo_sensitive(doc, false);
1247             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1249             for(; reprlst != NULL; reprlst = reprlst->next) {
1250                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1251                 const gchar * temp_string;
1253                 if (repr->attribute("id") == NULL ||
1254                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1255                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1256                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1257                     temp_string = repr->attribute("inkscape:export-filename");
1258                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1259                         repr->setAttribute("inkscape:export-filename", filename_ext);
1260                         modified = true;
1261                     }
1262                 }
1263                 temp_string = repr->attribute("inkscape:export-xdpi");
1264                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1265                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1266                     modified = true;
1267                 }
1268                 temp_string = repr->attribute("inkscape:export-ydpi");
1269                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1270                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1271                     modified = true;
1272                 }
1273             }
1275             if (modified) {
1276                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1277                 repr->setAttribute("sodipodi:modified", "TRUE");
1278             }
1280             sp_document_set_undo_sensitive(doc, saved);
1281             break;
1282         }
1283         default:
1284             break;
1285     }
1287     g_free (filename_ext);
1289     }
1291 } // end of sp_export_export_clicked()
1293 /// Called when Browse button is clicked
1294 static void
1295 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1297     GtkWidget *fs, *fe;
1298     const gchar *filename;
1300     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1301                                       NULL,
1302                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1303                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1304                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1305                                       NULL );
1307 #ifdef WITH_GNOME_VFS
1308     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER (fs), false);
1309 #endif
1311     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1313     sp_transientize (fs);
1315     gtk_window_set_modal(GTK_WINDOW (fs), true);
1317     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1319     if (*filename == '\0') {
1320         filename = homedir_path(NULL);
1321     }
1323     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1325     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1326     {
1327         gchar *file;
1329         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1331         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1332         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1334         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1336         g_free(utf8file);
1337         g_free(file);
1338     }
1340     gtk_widget_destroy (fs);
1342     return;
1343 } // end of sp_export_browse_clicked()
1345 // TODO: Move this to nr-rect-fns.h.
1346 static bool
1347 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1348
1349     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1350     return (
1351         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1352         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1353         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1354         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1355         );
1358 /**
1359     \brief  This function is used to detect the current selection setting
1360             based on the values in the x0, y0, x1 and y0 fields.
1361     \param  base  The export dialog itself
1363     One of the most confusing parts of this function is why the array
1364     is built at the beginning.  What needs to happen here is that we
1365     should always check the current selection to see if it is the valid
1366     one.  While this is a performance improvement it is also a usability
1367     one during the cases where things like selections and drawings match
1368     size.  This way buttons change less 'randomly' (atleast in the eyes
1369     of the user).  To do this an array is built where the current selection
1370     type is placed first, and then the others in an order from smallest
1371     to largest (this can be configured by reshuffling \c test_order).
1373     All of the values in this function are rounded to two decimal places
1374     because that is what is shown to the user.  While everything is kept
1375     more accurate than that, the user can't control more acurrate than
1376     that, so for this to work for them - it needs to check on that level
1377     of accuracy.
1379     \todo finish writing this up
1380 */
1381 static void
1382 sp_export_detect_size(GtkObject * base) {
1383     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1384     selection_type this_test[SELECTION_NUMBER_OF + 1];
1385     selection_type key = SELECTION_NUMBER_OF;
1387     NR::Point x(sp_export_value_get_px (base, "x0"),
1388                 sp_export_value_get_px (base, "y0"));
1389     NR::Point y(sp_export_value_get_px (base, "x1"),
1390                 sp_export_value_get_px (base, "y1"));
1391     NR::Rect current_bbox(x, y);
1392     //std::cout << "Current " << current_bbox;
1394     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1395     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1396         this_test[i + 1] = test_order[i];
1397     }
1399     for (int i = 0;
1400             i < SELECTION_NUMBER_OF + 1 &&
1401                 key == SELECTION_NUMBER_OF &&
1402                 SP_ACTIVE_DESKTOP != NULL;
1403             i++) {
1404         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1405         switch (this_test[i]) {
1406             case SELECTION_SELECTION:
1407                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1408                     NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1410                     //std::cout << "Selection " << bbox;
1411                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1412                         key = SELECTION_SELECTION;
1413                     }
1414                 }
1415                 break;
1416             case SELECTION_DRAWING: {
1417                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1419                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1421                 // std::cout << "Drawing " << bbox2;
1422                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1423                     key = SELECTION_DRAWING;
1424                 }
1425                 break;
1426             }
1428             case SELECTION_PAGE: {
1429                 SPDocument *doc;
1431                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1433                 NR::Point x(0.0, 0.0);
1434                 NR::Point y(sp_document_width(doc),
1435                             sp_document_height(doc));
1436                 NR::Rect bbox(x, y);
1438                 // std::cout << "Page " << bbox;
1439                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1440                     key = SELECTION_PAGE;
1441                 }
1443                 break;
1444            }
1445         default:
1446            break;
1447         }
1448     }
1449     // std::cout << std::endl;
1451     if (key == SELECTION_NUMBER_OF) {
1452         key = SELECTION_CUSTOM;
1453     }
1455     /* We're now using a custom size, not a fixed one */
1456     /* printf("Detecting state: %s\n", selection_names[key]); */
1457     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1458     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1459     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1460     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1462     return;
1463 } /* sp_export_detect_size */
1465 /// Called when area x0 value is changed
1466 static void
1467 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1469     float x0, x1, xdpi, width;
1471     if (gtk_object_get_data (base, "update"))
1472         return;
1474     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1475             (base, "units")))
1476     {
1477         return;
1478     }
1480     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1482     x0 = sp_export_value_get_px (base, "x0");
1483     x1 = sp_export_value_get_px (base, "x1");
1484     xdpi = sp_export_value_get (base, "xdpi");
1486     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1488     if (width < SP_EXPORT_MIN_SIZE) {
1489         const gchar *key;
1490         width = SP_EXPORT_MIN_SIZE;
1491         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1493         if (!strcmp (key, "x0")) {
1494             x1 = x0 + width * DPI_BASE / xdpi;
1495             sp_export_value_set_px (base, "x1", x1);
1496         } else {
1497             x0 = x1 - width * DPI_BASE / xdpi;
1498             sp_export_value_set_px (base, "x0", x0);
1499         }
1500     }
1502     sp_export_value_set_px (base, "width", x1 - x0);
1503     sp_export_value_set (base, "bmwidth", width);
1505     sp_export_detect_size(base);
1507     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1509     return;
1510 } // end of sp_export_area_x_value_changed()
1512 /// Called when area y0 value is changed.
1513 static void
1514 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1516     float y0, y1, ydpi, height;
1518     if (gtk_object_get_data (base, "update"))
1519         return;
1521     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1522            (base, "units")))
1523     {
1524         return;
1525     }
1527     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1529     y0 = sp_export_value_get_px (base, "y0");
1530     y1 = sp_export_value_get_px (base, "y1");
1531     ydpi = sp_export_value_get (base, "ydpi");
1533     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1535     if (height < SP_EXPORT_MIN_SIZE) {
1536         const gchar *key;
1537         height = SP_EXPORT_MIN_SIZE;
1538         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1539         if (!strcmp (key, "y0")) {
1540             y1 = y0 + height * DPI_BASE / ydpi;
1541             sp_export_value_set_px (base, "y1", y1);
1542         } else {
1543             y0 = y1 - height * DPI_BASE / ydpi;
1544             sp_export_value_set_px (base, "y0", y0);
1545         }
1546     }
1548     sp_export_value_set_px (base, "height", y1 - y0);
1549     sp_export_value_set (base, "bmheight", height);
1551     sp_export_detect_size(base);
1553     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1555     return;
1556 } // end of sp_export_area_y_value_changed()
1558 /// Called when x1-x0 or area width is changed
1559 static void
1560 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1562     float x0, x1, xdpi, width, bmwidth;
1564     if (gtk_object_get_data (base, "update"))
1565         return;
1567     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1568            (base, "units"))) {
1569         return;
1570     }
1572     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1574     x0 = sp_export_value_get_px (base, "x0");
1575     x1 = sp_export_value_get_px (base, "x1");
1576     xdpi = sp_export_value_get (base, "xdpi");
1577     width = sp_export_value_get_px (base, "width");
1578     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1580     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1582         bmwidth = SP_EXPORT_MIN_SIZE;
1583         width = bmwidth * DPI_BASE / xdpi;
1584         sp_export_value_set_px (base, "width", width);
1585     }
1587     sp_export_value_set_px (base, "x1", x0 + width);
1588     sp_export_value_set (base, "bmwidth", bmwidth);
1590     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1592     return;
1593 } // end of sp_export_area_width_value_changed()
1595 /// Called when y1-y0 or area height is changed.
1596 static void
1597 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1600     float y0, y1, ydpi, height, bmheight;
1602     if (gtk_object_get_data (base, "update"))
1603         return;
1605     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1606            (base, "units"))) {
1607         return;
1608     }
1610     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1612     y0 = sp_export_value_get_px (base, "y0");
1613     y1 = sp_export_value_get_px (base, "y1");
1614     ydpi = sp_export_value_get (base, "ydpi");
1615     height = sp_export_value_get_px (base, "height");
1616     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1618     if (bmheight < SP_EXPORT_MIN_SIZE) {
1619         bmheight = SP_EXPORT_MIN_SIZE;
1620         height = bmheight * DPI_BASE / ydpi;
1621         sp_export_value_set_px (base, "height", height);
1622     }
1624     sp_export_value_set_px (base, "y1", y0 + height);
1625     sp_export_value_set (base, "bmheight", bmheight);
1627     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1629     return;
1630 } // end of sp_export_area_height_value_changed()
1632 /**
1633     \brief  A function to set the ydpi
1634     \param  base  The export dialog
1636     This function grabs all of the y values and then figures out the
1637     new bitmap size based on the changing dpi value.  The dpi value is
1638     gotten from the xdpi setting as these can not currently be independent.
1639 */
1640 static void
1641 sp_export_set_image_y (GtkObject *base)
1643     float y0, y1, xdpi;
1645     y0 = sp_export_value_get_px (base, "y0");
1646     y1 = sp_export_value_get_px (base, "y1");
1647     xdpi = sp_export_value_get (base, "xdpi");
1649     sp_export_value_set (base, "ydpi", xdpi);
1650     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1652     return;
1653 } // end of sp_export_set_image_y()
1655 /**
1656     \brief  A function to set the xdpi
1657     \param  base  The export dialog
1659     This function grabs all of the x values and then figures out the
1660     new bitmap size based on the changing dpi value.  The dpi value is
1661     gotten from the xdpi setting as these can not currently be independent.
1662 */
1663 static void
1664 sp_export_set_image_x (GtkObject *base)
1666     float x0, x1, xdpi;
1668     x0 = sp_export_value_get_px (base, "x0");
1669     x1 = sp_export_value_get_px (base, "x1");
1670     xdpi = sp_export_value_get (base, "xdpi");
1672     sp_export_value_set (base, "ydpi", xdpi);
1673     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1675     return;
1676 } // end of sp_export_set_image_x()
1678 /// Called when pixel width is changed
1679 static void
1680 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1682     float x0, x1, bmwidth, xdpi;
1684     if (gtk_object_get_data (base, "update"))
1685         return;
1687     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1688            (base, "units"))) {
1689        return;
1690     }
1692     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1694     x0 = sp_export_value_get_px (base, "x0");
1695     x1 = sp_export_value_get_px (base, "x1");
1696     bmwidth = sp_export_value_get (base, "bmwidth");
1698     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1699         bmwidth = SP_EXPORT_MIN_SIZE;
1700         sp_export_value_set (base, "bmwidth", bmwidth);
1701     }
1703     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1704     sp_export_value_set (base, "xdpi", xdpi);
1706     sp_export_set_image_y (base);
1708     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1710     return;
1711 } // end of sp_export_bitmap_width_value_changed()
1713 /// Called when pixel height is changed
1714 static void
1715 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1717     float y0, y1, bmheight, xdpi;
1719     if (gtk_object_get_data (base, "update"))
1720         return;
1722     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1723            (base, "units"))) {
1724        return;
1725     }
1727     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1729     y0 = sp_export_value_get_px (base, "y0");
1730     y1 = sp_export_value_get_px (base, "y1");
1731     bmheight = sp_export_value_get (base, "bmheight");
1733     if (bmheight < SP_EXPORT_MIN_SIZE) {
1734         bmheight = SP_EXPORT_MIN_SIZE;
1735         sp_export_value_set (base, "bmheight", bmheight);
1736     }
1738     xdpi = bmheight * DPI_BASE / (y1 - y0);
1739     sp_export_value_set (base, "xdpi", xdpi);
1741     sp_export_set_image_x (base);
1743     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1745     return;
1746 } // end of sp_export_bitmap_width_value_changed()
1748 /**
1749     \brief  A function to adjust the bitmap width when the xdpi value changes
1750     \param  adj  The adjustment that was changed
1751     \param  base The export dialog itself
1753     The first thing this function checks is to see if we are doing an
1754     update.  If we are, this function just returns because there is another
1755     instance of it that will handle everything for us.  If there is a
1756     units change, we also assume that everyone is being updated appropriately
1757     and there is nothing for us to do.
1759     If we're the highest level function, we set the update flag, and
1760     continue on our way.
1762     All of the values are grabbed using the \c sp_export_value_get functions
1763     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1764     xdpi value is saved in the preferences for the next time the dialog
1765     is opened.  (does the selection dpi need to be set here?)
1767     A check is done to to ensure that we aren't outputing an invalid width,
1768     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1769     changed to make it valid.
1771     After all of this the bitmap width is changed.
1773     We also change the ydpi.  This is a temporary hack as these can not
1774     currently be independent.  This is likely to change in the future.
1775 */
1776 void
1777 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1779     float x0, x1, xdpi, bmwidth;
1781     if (gtk_object_get_data (base, "update"))
1782         return;
1784     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1785            (base, "units"))) {
1786        return;
1787     }
1789     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1791     x0 = sp_export_value_get_px (base, "x0");
1792     x1 = sp_export_value_get_px (base, "x1");
1793     xdpi = sp_export_value_get (base, "xdpi");
1795     // remember xdpi setting
1796     prefs_set_double_attribute ("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))
1970   indent-tabs-mode:nil
1971   fill-column:99
1972   End:
1973 */
1974 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :