Code

Handle the case of gnome_vfs_init failing. (Fixes Debian bug http://bugs.debian...
[inkscape.git] / src / dialogs / export.cpp
1 #define __SP_EXPORT_C__
3 /** \file
4  * \brief  PNG export dialog
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   bulia byak <buliabyak@users.sf.net>
11  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
12  *
13  * Copyright (C) 1999-2007 Authors
14  * Copyright (C) 2001-2002 Ximian, Inc.
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/buttonbox.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/togglebutton.h>
29 #include <gtkmm/entry.h>
30 #include <gtkmm/image.h>
31 #include <gtkmm/stockid.h>
32 #include <gtkmm/stock.h>
33 #ifdef WITH_GNOME_VFS
34 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
35 #endif
37 #include <glibmm/i18n.h>
38 #include "helper/unit-menu.h"
39 #include "helper/units.h"
40 #include "unit-constants.h"
41 #include "helper/window.h"
42 #include "inkscape-private.h"
43 #include "document.h"
44 #include "desktop-handles.h"
45 #include "sp-item.h"
46 #include "selection.h"
47 #include "file.h"
48 #include "macros.h"
49 #include "sp-namedview.h"
50 #include "selection-chemistry.h"
52 #include "dialog-events.h"
53 #include "../prefs-utils.h"
54 #include "../verbs.h"
55 #include "../interface.h"
57 #include "extension/output.h"
58 #include "extension/db.h"
60 #include "io/sys.h"
62 #include "helper/png-write.h"
65 #define SP_EXPORT_MIN_SIZE 1.0
67 #define DPI_BASE PX_PER_IN
69 #define EXPORT_COORD_PRECISION 3    
71 #define MIN_ONSCREEN_DISTANCE 50
73 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
74 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
75 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
77 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
78                                                    GtkObject *base);
79                                              
80 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
81                                                    GtkObject *base);
82                                              
83 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
84                                                    GtkObject *base);
85                                                  
86 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
87                                                    GtkObject *base);
88                                                   
89 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
90                                                    GtkObject *base);
91                                                    
92 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
93                                                    GtkObject *base);
94                                                    
95 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
96                                                    GtkObject *base);
97                                            
98 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
99                                           Inkscape::Selection *selection, 
100                                           GtkObject *base);
101 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
102                                            Inkscape::Selection *selection, 
103                                            guint flags,
104                                            GtkObject *base );
106 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
107 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
108 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
109 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
110 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
112 static void sp_export_filename_modified (GtkObject * object, gpointer data);
113 static inline void sp_export_find_default_selection(GtkWidget * dlg);
114 static void sp_export_detect_size(GtkObject * base);
116 static const gchar *prefs_path = "dialogs.export";
118 // these all need to be reinitialized to their defaults during dialog_destroy
119 static GtkWidget *dlg = NULL;
120 static win_data wd;
121 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
122 static gchar * original_name = NULL;
123 static gchar * doc_export_name = NULL;
124 static bool was_empty = TRUE;
126 /** What type of button is being pressed */
127 enum selection_type {
128     SELECTION_PAGE = 0,  /**< Export the whole page */
129     SELECTION_DRAWING,   /**< Export everything drawn on the page */
130     SELECTION_SELECTION, /**< Export everything that is selected */
131     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
132     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
133 };
135 /** A list of strings that is used both in the preferences, and in the
136     data fields to describe the various values of \c selection_type. */
137 static const char * selection_names[SELECTION_NUMBER_OF] = {
138     "page", "drawing", "selection", "custom"};
140 /** The names on the buttons for the various selection types. */
141 static const char * selection_labels[SELECTION_NUMBER_OF] = {
142     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
144 static void
145 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
147     sp_signal_disconnect_by_data (INKSCAPE, dlg);
149     wd.win = dlg = NULL;
150     wd.stop = 0;
151     x = -1000; y = -1000; w = 0; h = 0;
152     g_free(original_name);
153     original_name = NULL;
154     g_free(doc_export_name);
155     doc_export_name = NULL;
156     was_empty = TRUE;
158     return;
159 } // end of sp_export_dialog_destroy()
161 /// Called when dialog is closed or inkscape is shut down.
162 static bool
163 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
166     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
167     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
169     if (x<0) x=0;
170     if (y<0) y=0;
172     prefs_set_int_attribute (prefs_path, "x", x);
173     prefs_set_int_attribute (prefs_path, "y", y);
174     prefs_set_int_attribute (prefs_path, "w", w);
175     prefs_set_int_attribute (prefs_path, "h", h);
177     return FALSE; // which means, go ahead and destroy it
179 } // end of sp_export_dialog_delete()
181 /**
182     \brief  Creates a new spin button for the export dialog
183     \param  key  The name of the spin button
184     \param  val  A default value for the spin button
185     \param  min  Minimum value for the spin button
186     \param  max  Maximum value for the spin button
187     \param  step The step size for the spin button
188     \param  page Size of the page increment
189     \param  us   Unit selector that effects this spin button
190     \param  t    Table to put the spin button in
191     \param  x    X location in the table \c t to start with
192     \param  y    Y location in the table \c t to start with
193     \param  ll   Text to put on the left side of the spin button (optional)
194     \param  lr   Text to put on the right side of the spin button (optional)
195     \param  digits  Number of digits to display after the decimal
196     \param  sensitive  Whether the spin button is sensitive or not
197     \param  cb   Callback for when this spin button is changed (optional)
198     \param  dlg  Export dialog the spin button is being placed in
200 */
201 static void
202 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
203                            float step, float page, GtkWidget *us,
204                            GtkWidget *t, int x, int y,
205                            const gchar *ll, const gchar *lr,
206                            int digits, unsigned int sensitive,
207                            GCallback cb, GtkWidget *dlg )
209     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
210     gtk_object_set_data (a, "key", key);
211     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
213     if (us) {
214         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
215                                           GTK_ADJUSTMENT (a) );
216     }
218     int pos = 0;
220     GtkWidget *l = NULL;
222     if (ll) {
224         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
225         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
226         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
227                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
228         gtk_widget_set_sensitive (l, sensitive);
229         pos += 1;
231     }
233     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
234     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
235                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
236     gtk_widget_set_size_request (sb, 80, -1);
237     gtk_widget_set_sensitive (sb, sensitive);
238     pos += 1;
240     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
242     if (lr) {
244         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
245         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
246         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
247                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
248         gtk_widget_set_sensitive (l, sensitive);
249         pos += 1;
251         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
252     }
254     if (cb)
255         gtk_signal_connect (a, "value_changed", cb, dlg);
257     return;
258 } // end of sp_export_spinbutton_new()
261 static Gtk::VBox *
262 sp_export_dialog_area_box (GtkWidget * dlg)
264     Gtk::VBox* vb = new Gtk::VBox(false, 3);
266     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
267     lbl->set_use_markup(true);
268     vb->pack_start(*lbl);
270     /* Units box */
271     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
272     /* gets added to the vbox later, but the unit selector is needed
273        earlier than that */
275     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
276     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
277     if (desktop)
278         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
279     unitbox->pack_end(*us, false, false, 0);
280     Gtk::Label* l = new Gtk::Label(_("Units:"));
281     unitbox->pack_end(*l, false, false, 3);
282     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
284     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
286     Gtk::ToggleButton* b;
287     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
288         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
289         b->set_data("key", GINT_TO_POINTER(i));
290         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
291         togglebox->pack_start(*b, false, true, 0);
292         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
293                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
294     }
296     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
297                        G_CALLBACK (sp_export_selection_changed), dlg );
298     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
299                        G_CALLBACK (sp_export_selection_modified), dlg );
300     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
301                        G_CALLBACK (sp_export_selection_changed), dlg );
302     
303     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
304     t->set_row_spacings (4);
305     t->set_col_spacings (4);
307     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
308                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
309                                G_CALLBACK ( sp_export_area_x_value_changed), 
310                                dlg );
312     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
313                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
314                                G_CALLBACK (sp_export_area_x_value_changed), 
315                                dlg );
317     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
318                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
319                                G_CALLBACK 
320                                    (sp_export_area_width_value_changed), 
321                                dlg );
323     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
324                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
325                                G_CALLBACK (sp_export_area_y_value_changed), 
326                                dlg );
328     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
329                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
330                                G_CALLBACK (sp_export_area_y_value_changed), 
331                                dlg );
333     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
334                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
335                                G_CALLBACK (sp_export_area_height_value_changed), 
336                                dlg );
338     vb->pack_start(*togglebox, false, false, 3);
339     vb->pack_start(*t, false, false, 0);
340     vb->pack_start(*unitbox, false, false, 0);
342     return vb;
343 } // end of sp_export_dialog_area_box
346 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
348     if (id == NULL) /* This should never happen */
349         id = "bitmap";
351     gchar * directory = NULL;
353     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
354         // std::cout << "Directory from dialog" << std::endl;
355         directory = g_dirname(file_entry_text);
356     }
358     if (directory == NULL) {
359         /* Grab document directory */
360         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
361             // std::cout << "Directory from document" << std::endl;
362             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
363         }
364     }
366     if (directory == NULL) {
367         // std::cout << "Home Directory" << std::endl;
368         directory = homedir_path(NULL);
369     }
371     gchar * id_ext = g_strconcat(id, ".png", NULL);
372     gchar *filename = g_build_filename(directory, id_ext, NULL);
373     g_free(directory);
374     g_free(id_ext);
375     return filename;
378 static void
379 batch_export_clicked (GtkWidget *widget, GtkObject *base)
381     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
382     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
383         vb_singleexport->set_sensitive(false);
384     } else {
385         vb_singleexport->set_sensitive(true);
386     }
389 void
390 sp_export_dialog (void)
392     if (!dlg) {
394         gchar title[500];
395         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
397         dlg = sp_window_new (title, TRUE);
399         if (x == -1000 || y == -1000) {
400             x = prefs_get_int_attribute (prefs_path, "x", 0);
401             y = prefs_get_int_attribute (prefs_path, "y", 0);
402         }
404         if (w ==0 || h == 0) {
405             w = prefs_get_int_attribute (prefs_path, "w", 0);
406             h = prefs_get_int_attribute (prefs_path, "h", 0);
407         }
409 //        if (x<0) x=0;
410 //        if (y<0) y=0;
412         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
413         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
414             gtk_window_move ((GtkWindow *) dlg, x, y);
415         else
416             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
417         sp_transientize (dlg);
418         wd.win = dlg;
419         wd.stop = 0;
421         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
422                              G_CALLBACK (sp_transientize_callback), &wd);
424         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
425                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
427         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
428                              G_CALLBACK (sp_export_dialog_destroy), dlg);
430         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
431                              G_CALLBACK (sp_export_dialog_delete), dlg);
433         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
434                              G_CALLBACK (sp_export_dialog_delete), dlg);
436         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
437                              G_CALLBACK (sp_dialog_hide), dlg);
439         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
440                              G_CALLBACK (sp_dialog_unhide), dlg);
442         GtkTooltips *tt = gtk_tooltips_new();
444         Gtk::VBox *vb = new Gtk::VBox(false, 3);
445         vb->set_border_width(3);
446         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
448         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
449         vb_singleexport->set_border_width(0);
450         vb->pack_start(*vb_singleexport);
451         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
453         /* Export area frame */
454         {
455             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
456             area_box->set_border_width(3);
457             vb_singleexport->pack_start(*area_box, false, false, 0);
458         }
460         /* Bitmap size frame */
461         {
462             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
463             size_box->set_border_width(3);
465             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
466             lbl->set_use_markup(true);
467             size_box->pack_start(*lbl, false, false, 0);
468             const int rows = 2;
469             const int cols = 5;
470             const bool homogeneous = false;
471             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
472             t->set_row_spacings (4);
473             t->set_col_spacings (4);
474             size_box->pack_start(*t);
476             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
477                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
478                                        _("_Width:"), _("pixels at"), 0, 1,
479                                        G_CALLBACK 
480                                        (sp_export_bitmap_width_value_changed), 
481                                        dlg );
483             sp_export_spinbutton_new ( "xdpi", 
484                                        prefs_get_double_attribute 
485                                        ( "dialogs.export.defaultxdpi", 
486                                          "value", DPI_BASE), 
487                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
488                                        NULL, _("dp_i"), 2, 1,
489                                        G_CALLBACK (sp_export_xdpi_value_changed), 
490                                        dlg );
492             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
493                                        NULL, GTK_WIDGET(t->gobj()), 0, 1, 
494                                        _("Height:"), _("pixels at"), 0, 1, 
495                                        G_CALLBACK
496                                        (sp_export_bitmap_height_value_changed), 
497                                        dlg );
499             /** \todo
500              * Needs fixing: there's no way to set ydpi currently, so we use  
501              *       the defaultxdpi value here, too...
502              */
503             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
504                                        ( "dialogs.export.defaultxdpi", 
505                                          "value", DPI_BASE), 
506                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
507                                        NULL, _("dpi"), 2, 0, NULL, dlg );
509             vb_singleexport->pack_start(*size_box);
510         }
512         /* File entry */
513         {
514             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
515             file_box->set_border_width(3);
517             // true = has mnemonic
518             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
519             flabel->set_use_markup(true);
520             file_box->pack_start(*flabel, false, false, 0);
522             Gtk::Entry *fe = new Gtk::Entry();
524             /*
525              * set the default filename to be that of the current path + document
526              * with .png extension
527              *
528              * One thing to notice here is that this filename may get
529              * overwritten, but it won't happen here.  The filename gets
530              * written into the text field, but then the button to select
531              * the area gets set.  In that code the filename can be changed
532              * if there are some with presidence in the document.  So, while
533              * this code sets the name first, it may not be the one users
534              * really see.
535              */
536             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
537             {
538                 gchar *name;
539                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
540                 const gchar *uri = SP_DOCUMENT_URI (doc);
541                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
542                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
543                 Inkscape::Extension::Output * oextension = NULL;
545                 if (text_extension != NULL) {
546                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
547                 }
549                 if (oextension != NULL) {
550                     gchar * old_extension = oextension->get_extension();
551                     if (g_str_has_suffix(uri, old_extension)) {
552                         gchar * uri_copy;
553                         gchar * extension_point;
554                         gchar * final_name;
556                         uri_copy = g_strdup(uri);
557                         extension_point = g_strrstr(uri_copy, old_extension);
558                         extension_point[0] = '\0';
560                         final_name = g_strconcat(uri_copy, ".png", NULL);
561                         fe->set_text(final_name);
563                         g_free(final_name);
564                         g_free(uri_copy);
565                     }
566                 } else {
567                     name = g_strconcat(uri, ".png", NULL);
568                     fe->set_text(name);
569                     g_free(name);
570                 }
572                 doc_export_name = g_strdup(fe->get_text().c_str());
573             }
574             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
575                                G_CALLBACK (sp_export_filename_modified), dlg);
577             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
579             {
580                 // true = has mnemonic
581                 Gtk::Button *b = new Gtk::Button();
583                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
584                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
585                         Gtk::ICON_SIZE_BUTTON);
586                 pixlabel->pack_start(*im);
588                 Gtk::Label *l = new Gtk::Label();
589                 l->set_markup_with_mnemonic(_("_Browse..."));
590                 pixlabel->pack_start(*l);
592                 b->add(*pixlabel);
594                 hb->pack_end (*b, false, false, 4);
595                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
596                                    G_CALLBACK (sp_export_browse_clicked), NULL );
597             }
599             hb->pack_start (*fe, true, true, 0);
600             file_box->add(*hb);
601             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
602             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
603             original_name = g_strdup(fe->get_text().c_str());
604             // pressing enter in the filename field is the same as clicking export:
605             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
606                                G_CALLBACK (sp_export_export_clicked), dlg );
607             // focus is in the filename initially:
608             fe->grab_focus();
610             // mnemonic in frame label moves focus to filename:
611             flabel->set_mnemonic_widget(*fe);
613             vb_singleexport->pack_start(*file_box);
614         }
616         {
617             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
618             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
619             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
620             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
621             batch_box->pack_start(*Glib::wrap(be), false, false);
622             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);
623             batch_box->show_all();
624             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
625             vb->pack_start(*batch_box);
626         }
628         {
629             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
630             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
631             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
632             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
633             hide_box->pack_start(*Glib::wrap(he), false, false);
634             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
635             hide_box->show_all();
636             vb->pack_start(*hide_box);
637         }
639         /* Buttons */
640         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
641         bb->set_border_width(3);
643         {
644             Gtk::Button *b = new Gtk::Button();
645             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
646             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
647                     Gtk::ICON_SIZE_BUTTON);
648             image_label->pack_start(*im);
650             Gtk::Label *l = new Gtk::Label();
651             l->set_markup_with_mnemonic(_("_Export"));
652             image_label->pack_start(*l);
654             b->add(*image_label);
655             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
656             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
657                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
658             bb->pack_end(*b, false, false, 0);
659         }
661         vb->pack_end(*bb, false, false, 0);
662         vb->show_all();
664     } // end of if (!dlg)
666     sp_export_find_default_selection(dlg);
668     gtk_window_present ((GtkWindow *) dlg);
670     return;
671 } // end of sp_export_dialog()
673 static void
674 sp_export_update_checkbuttons (GtkObject *base)
676     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
677     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
678     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
679     if (num >= 2) {
680         gtk_widget_set_sensitive (be, true);
681         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (_("Batch export %d selected objects"), num));
682     } else {
683         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
684         gtk_widget_set_sensitive (be, FALSE);
685     }
686     if (num > 0) {
687         gtk_widget_set_sensitive (he, true);
688     } else {
689         gtk_widget_set_sensitive (he, false);
690     }
693 static inline void
694 sp_export_find_default_selection(GtkWidget * dlg)
696     selection_type key = SELECTION_NUMBER_OF;
698     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
699         key = SELECTION_SELECTION;
700     }
702     /* Try using the preferences */
703     if (key == SELECTION_NUMBER_OF) {
704         const gchar *what = NULL;
705         int i = SELECTION_NUMBER_OF;
707         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
709         if (what != NULL) {
710             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
711                 if (!strcmp (what, selection_names[i])) {
712                     break;
713                 }
714             }
715         }
717         key = (selection_type)i;
718     }
720     if (key == SELECTION_NUMBER_OF) {
721         key = SELECTION_SELECTION;
722     }
724     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
725                                                        selection_names[key]);
726     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
728     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
732 /**
733  * \brief  If selection changed or a different document activated, we must 
734  * recalculate any chosen areas
735  *
736  */
737 static void
738 sp_export_selection_changed ( Inkscape::Application *inkscape, 
739                               Inkscape::Selection *selection, 
740                               GtkObject *base )
742     selection_type current_key;
743     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
745     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
746             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
747             was_empty) {
748         gtk_toggle_button_set_active
749             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
750               TRUE );
751     }
752     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
754     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
756     if (inkscape &&
757             SP_IS_INKSCAPE (inkscape) &&
758             selection &&
759             SELECTION_CUSTOM != current_key) {
760         GtkToggleButton * button;
761         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
762         sp_export_area_toggled(button, base);
763     } 
765     sp_export_update_checkbuttons (base);
766
768 static void
769 sp_export_selection_modified ( Inkscape::Application *inkscape, 
770                                Inkscape::Selection *selection, 
771                                guint flags,
772                                GtkObject *base )
774     selection_type current_key;
775     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
777     switch (current_key) {
778         case SELECTION_DRAWING:
779             if ( SP_ACTIVE_DESKTOP ) {
780                 SPDocument *doc;
781                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
782                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
783                 if (bbox) {
784                     sp_export_set_area (base, bbox->min()[NR::X],
785                                               bbox->min()[NR::Y],
786                                               bbox->max()[NR::X],
787                                               bbox->max()[NR::Y]);
788                 }
789             }
790             break;
791         case SELECTION_SELECTION:
792             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
793                 NRRect bbox;
794                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
795                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
796             }
797             break;
798         default:
799             /* Do nothing for page or for custom */
800             break;
801     }
803     return;
806 /// Called when one of the selection buttons was toggled.
807 static void
808 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
810     if (gtk_object_get_data (base, "update"))
811         return;
813     selection_type key, old_key;
814     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
815     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
817     /* Ignore all "turned off" events unless we're the only active button */
818     if (!gtk_toggle_button_get_active (tb) ) {
820         /* Don't let the current selection be deactived - but rerun the
821            activate to allow the user to renew the values */
822         if (key == old_key) {
823             gtk_toggle_button_set_active ( tb, TRUE );
824         }
826         return;
827     }
829     /* Turn off the currently active button unless it's us */
830     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
832     if (old_key != key) {
833         gtk_toggle_button_set_active
834             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
835               FALSE );
836     }
838     if ( SP_ACTIVE_DESKTOP )
839     {
840         SPDocument *doc;
841         NR::Maybe<NR::Rect> bbox;
842         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
844         /* Notice how the switch is used to 'fall through' here to get
845            various backups.  If you modify this without noticing you'll
846            probabaly screw something up. */
847         switch (key) {
848             case SELECTION_SELECTION:
849                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
850                 {
851                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
852                     /* Only if there is a selection that we can set
853                        do we break, otherwise we fall through to the
854                        drawing */
855                     // std::cout << "Using selection: SELECTION" << std::endl;
856                     key = SELECTION_SELECTION;
857                     break;
858                 }
859             case SELECTION_DRAWING:
860                 /** \todo 
861                  * This returns wrong values if the document has a viewBox.
862                  */
863                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
864                 /* If the drawing is valid, then we'll use it and break
865                    otherwise we drop through to the page settings */
866                 if (bbox) {
867                     // std::cout << "Using selection: DRAWING" << std::endl;
868                     key = SELECTION_DRAWING;
869                     break;
870                 }
871             case SELECTION_PAGE:
872                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
873                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
874                                 );
876                 // std::cout << "Using selection: PAGE" << std::endl;
877                 key = SELECTION_PAGE;
878                 break;
879             case SELECTION_CUSTOM:
880             default:
881                 break;
882         } // switch
883         
884         // remember area setting
885         prefs_set_string_attribute ( "dialogs.export.exportarea", 
886                                      "value", selection_names[key]);
888         if ( key != SELECTION_CUSTOM && bbox ) {
889             sp_export_set_area (base, bbox->min()[NR::X],
890                                       bbox->min()[NR::Y],
891                                       bbox->max()[NR::X],
892                                       bbox->max()[NR::Y]);
893         }
894     
895     } // end of if ( SP_ACTIVE_DESKTOP )
898     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
899         GtkWidget * file_entry;
900         const gchar * filename = NULL;
901         float xdpi = 0.0, ydpi = 0.0;
903         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
905         switch (key) {
906             case SELECTION_PAGE:
907             case SELECTION_DRAWING: {
908                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
909                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
911                 if (filename == NULL) {
912                     if (doc_export_name != NULL) {
913                         filename = g_strdup(doc_export_name);
914                     } else {
915                         filename = g_strdup("");
916                     }
917                 }
918                 break;
919             }
920             case SELECTION_SELECTION:
921                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
923                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
925                     /* If we still don't have a filename -- let's build
926                        one that's nice */
927                     if (filename == NULL) {
928                         const gchar * id = NULL;
929                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
930                         for(; reprlst != NULL; reprlst = reprlst->next) {
931                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
932                             if (repr->attribute("id")) {
933                                 id = repr->attribute("id");
934                                 break;
935                             }
936                         }
938                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
939                     }
940                 }
941                 break;
942             case SELECTION_CUSTOM:
943             default:
944                 break;
945         }
947         if (filename != NULL) {
948             g_free(original_name);
949             original_name = g_strdup(filename);
950             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
951         }
953         if (xdpi != 0.0) {
954             sp_export_value_set(base, "xdpi", xdpi);
955         }
957         /* These can't be separate, and setting x sets y, so for
958            now setting this is disabled.  Hopefully it won't be in
959            the future */
960         if (FALSE && ydpi != 0.0) {
961             sp_export_value_set(base, "ydpi", ydpi);
962         }
963     }
965     return;
966 } // end of sp_export_area_toggled()
968 /// Called when dialog is deleted
969 static gint
970 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
972     g_object_set_data (base, "cancel", (gpointer) 1);
973     return TRUE;
974 } // end of sp_export_progress_delete()
976 /// Called when progress is cancelled
977 static void
978 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
980     g_object_set_data (base, "cancel", (gpointer) 1);
981 } // end of sp_export_progress_cancel()
983 /// Called for every progress iteration
984 static unsigned int
985 sp_export_progress_callback (float value, void *data)
987     GtkWidget *prg;
988     int evtcount;
990     if (g_object_get_data ((GObject *) data, "cancel"))
991         return FALSE;
993     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
994     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
996     evtcount = 0;
997     while ((evtcount < 16) && gdk_events_pending ()) {
998             gtk_main_iteration_do (FALSE);
999             evtcount += 1;
1000     }
1002     gtk_main_iteration_do (FALSE);
1004     return TRUE;
1006 } // end of sp_export_progress_callback()
1008 GtkWidget *
1009 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1010     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1011     
1012     dlg = gtk_dialog_new ();
1013     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1014     prg = gtk_progress_bar_new ();
1015     sp_transientize (dlg);
1016     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1017     g_object_set_data ((GObject *) base, "progress", prg);
1019     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1021     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1022                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1023     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1024                         prg, FALSE, FALSE, 4 );
1025     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1026                                   GTK_STOCK_CANCEL, 
1027                                   GTK_RESPONSE_CANCEL );
1028                                   
1029     g_signal_connect ( (GObject *) dlg, "delete_event", 
1030                        (GCallback) sp_export_progress_delete, base);
1031     g_signal_connect ( (GObject *) btn, "clicked", 
1032                        (GCallback) sp_export_progress_cancel, base);
1033     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1034     gtk_widget_show_all (dlg);
1036     return dlg;
1039 // FIXME: Some lib function should be available to do this ...
1040 static gchar *
1041 filename_add_extension (const gchar *filename, const gchar *extension)
1043   gchar *dot;
1045   dot = strrchr (filename, '.');
1046   if ( !dot )
1047     return g_strconcat (filename, ".", extension, NULL);
1048   {
1049     if (dot[1] == '\0')
1050       return g_strconcat (filename, extension, NULL);
1051     else
1052     {
1053       if (g_strcasecmp (dot + 1, extension) == 0)
1054         return g_strdup (filename);
1055       else
1056       {
1057         return g_strconcat (filename, ".", extension, NULL);
1058       }
1059     }
1060   }
1063 /// Called when export button is clicked
1064 static void
1065 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1067     if (!SP_ACTIVE_DESKTOP) return;
1069     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1071     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1072     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1073     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1074     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1075         // Batch export of selected objects
1077         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1078         gint n = 0;
1080         if (num < 1) 
1081             return;
1083         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1084         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1085         g_free (progress_text);
1087         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1088              i != NULL;
1089              i = i->next) {
1090             SPItem *item = (SPItem *) i->data;
1091             // retrieve export filename hint
1092             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1093             if (!fn) {
1094                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1095             }
1097             // retrieve export dpi hints
1098             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1099             gdouble dpi = 0.0;
1100             if (dpi_hint) {
1101                 dpi = atof(dpi_hint);
1102             }
1103             if (dpi == 0.0) {
1104                 dpi = DPI_BASE;
1105             }
1107             NRRect area;
1108             sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1110             gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1111             gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1113             if (width > 1 && height > 1) {
1114                 /* Do export */
1115                 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn, 
1116                                          area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, 
1117                                          nv->pagecolor, 
1118                                          NULL, NULL, TRUE,  // overwrite without asking 
1119                                          hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1120                         )) {
1121                     gchar * error;
1122                     gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1123                     error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1124                     sp_ui_error_dialog(error);
1125                     g_free(safeFile);
1126                     g_free(error);
1127                 }
1128             }
1129             n++;
1130             sp_export_progress_callback((float)n/num, base);
1131         }
1133         gtk_widget_destroy (prog_dlg);
1134         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1136     } else {
1138     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1139     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1141     float const x0 = sp_export_value_get_px(base, "x0");
1142     float const y0 = sp_export_value_get_px(base, "y0");
1143     float const x1 = sp_export_value_get_px(base, "x1");
1144     float const y1 = sp_export_value_get_px(base, "y1");
1145     float const xdpi = sp_export_value_get(base, "xdpi");
1146     float const ydpi = sp_export_value_get(base, "ydpi");
1147     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1148     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1150     if (filename == NULL || *filename == '\0') {
1151         sp_ui_error_dialog(_("You have to enter a filename"));
1152         return;
1153     }
1155     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1156         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1157         return;
1158     }
1160     gchar *dirname = g_path_get_dirname(filename);
1161     if ( dirname == NULL
1162          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1163     {
1164         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1165         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1166                                        safeDir);
1167         sp_ui_error_dialog(error);
1168         g_free(safeDir);
1169         g_free(error);
1170         g_free(dirname);
1171         return;
1172     }
1173     g_free(dirname);
1175     // make sure that .png is the extension of the file:
1176     gchar * filename_ext = filename_add_extension(filename, "png");
1177     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1179     gchar *fn = g_path_get_basename (filename_ext);
1181     gchar *progress_text = g_strdup_printf (_("Exporting %s (%d x %d)"), fn, width, height);
1182     g_free (fn);
1183     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1184     g_free (progress_text);
1185     
1186     /* Do export */
1187     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext, 
1188                              x0, y0, x1, y1, width, height, xdpi, ydpi, 
1189                              nv->pagecolor, 
1190                              sp_export_progress_callback, base, FALSE,
1191                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1192             )) {
1193         gchar * error;
1194         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1195         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1196         sp_ui_error_dialog(error);
1197         g_free(safeFile);
1198         g_free(error);
1199     }
1201     /* Reset the filename so that it can be changed again by changing
1202        selections and all that */
1203     g_free(original_name);
1204     original_name = g_strdup(filename_ext);
1205     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1207     gtk_widget_destroy (prog_dlg);
1208     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1210     /* Setup the values in the document */
1211     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1212         case SELECTION_PAGE:
1213         case SELECTION_DRAWING: {
1214             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1215             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1216             bool modified = false;
1217             const gchar * temp_string;
1219             bool saved = sp_document_get_undo_sensitive(doc);
1220             sp_document_set_undo_sensitive(doc, false);
1222             temp_string = repr->attribute("inkscape:export-filename");
1223             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1224                 repr->setAttribute("inkscape:export-filename", filename_ext);
1225                 modified = true;
1226             }
1227             temp_string = repr->attribute("inkscape:export-xdpi");
1228             if (temp_string == NULL || xdpi != atof(temp_string)) {
1229                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1230                 modified = true;
1231             }
1232             temp_string = repr->attribute("inkscape:export-ydpi");
1233             if (temp_string == NULL || xdpi != atof(temp_string)) {
1234                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1235                 modified = true;
1236             }
1238             if (modified)
1239                 repr->setAttribute("sodipodi:modified", "TRUE");
1240             sp_document_set_undo_sensitive(doc, saved);
1241             break;
1242         }
1243         case SELECTION_SELECTION: {
1244             const GSList * reprlst;
1245             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1246             bool modified = false;
1248             bool saved = sp_document_get_undo_sensitive(doc);
1249             sp_document_set_undo_sensitive(doc, false);
1250             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1252             for(; reprlst != NULL; reprlst = reprlst->next) {
1253                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1254                 const gchar * temp_string;
1256                 if (repr->attribute("id") == NULL ||
1257                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1258                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1259                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1260                     temp_string = repr->attribute("inkscape:export-filename");
1261                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1262                         repr->setAttribute("inkscape:export-filename", filename_ext);
1263                         modified = true;
1264                     }
1265                 }
1266                 temp_string = repr->attribute("inkscape:export-xdpi");
1267                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1268                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1269                     modified = true;
1270                 }
1271                 temp_string = repr->attribute("inkscape:export-ydpi");
1272                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1273                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1274                     modified = true;
1275                 }
1276             }
1278             if (modified) {
1279                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1280                 repr->setAttribute("sodipodi:modified", "TRUE");
1281             }
1283             sp_document_set_undo_sensitive(doc, saved);
1284             break;
1285         }
1286         default:
1287             break;
1288     }
1290     g_free (filename_ext);
1292     }
1294 } // end of sp_export_export_clicked()
1296 /// Called when Browse button is clicked
1297 static void
1298 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1300     GtkWidget *fs, *fe;
1301     const gchar *filename;
1303     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1304                                       NULL,
1305                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1306                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1307                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1308                                       NULL );
1310 #ifdef WITH_GNOME_VFS
1311     if (gnome_vfs_initialized()) {
1312         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1313     }
1314 #endif
1316     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1318     sp_transientize (fs);
1320     gtk_window_set_modal(GTK_WINDOW (fs), true);
1322     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1324     if (*filename == '\0') {
1325         filename = homedir_path(NULL);
1326     }
1328     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1330     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1331     {
1332         gchar *file;
1334         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1336         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1337         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1339         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1341         g_free(utf8file);
1342         g_free(file);
1343     }
1345     gtk_widget_destroy (fs);
1347     return;
1348 } // end of sp_export_browse_clicked()
1350 // TODO: Move this to nr-rect-fns.h.
1351 static bool
1352 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1353
1354     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1355     return (
1356         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1357         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1358         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1359         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1360         );
1363 /**
1364     \brief  This function is used to detect the current selection setting
1365             based on the values in the x0, y0, x1 and y0 fields.
1366     \param  base  The export dialog itself
1368     One of the most confusing parts of this function is why the array
1369     is built at the beginning.  What needs to happen here is that we
1370     should always check the current selection to see if it is the valid
1371     one.  While this is a performance improvement it is also a usability
1372     one during the cases where things like selections and drawings match
1373     size.  This way buttons change less 'randomly' (atleast in the eyes
1374     of the user).  To do this an array is built where the current selection
1375     type is placed first, and then the others in an order from smallest
1376     to largest (this can be configured by reshuffling \c test_order).
1378     All of the values in this function are rounded to two decimal places
1379     because that is what is shown to the user.  While everything is kept
1380     more accurate than that, the user can't control more acurrate than
1381     that, so for this to work for them - it needs to check on that level
1382     of accuracy.
1384     \todo finish writing this up
1385 */
1386 static void
1387 sp_export_detect_size(GtkObject * base) {
1388     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1389     selection_type this_test[SELECTION_NUMBER_OF + 1];
1390     selection_type key = SELECTION_NUMBER_OF;
1392     NR::Point x(sp_export_value_get_px (base, "x0"),
1393                 sp_export_value_get_px (base, "y0"));
1394     NR::Point y(sp_export_value_get_px (base, "x1"),
1395                 sp_export_value_get_px (base, "y1"));
1396     NR::Rect current_bbox(x, y);
1397     //std::cout << "Current " << current_bbox;
1399     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1400     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1401         this_test[i + 1] = test_order[i];
1402     }
1404     for (int i = 0;
1405             i < SELECTION_NUMBER_OF + 1 &&
1406                 key == SELECTION_NUMBER_OF &&
1407                 SP_ACTIVE_DESKTOP != NULL;
1408             i++) {
1409         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1410         switch (this_test[i]) {
1411             case SELECTION_SELECTION:
1412                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1413                     NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1415                     //std::cout << "Selection " << bbox;
1416                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1417                         key = SELECTION_SELECTION;
1418                     }
1419                 }
1420                 break;
1421             case SELECTION_DRAWING: {
1422                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1424                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1426                 // std::cout << "Drawing " << bbox2;
1427                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1428                     key = SELECTION_DRAWING;
1429                 }
1430                 break;
1431             }
1433             case SELECTION_PAGE: {
1434                 SPDocument *doc;
1436                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1438                 NR::Point x(0.0, 0.0);
1439                 NR::Point y(sp_document_width(doc),
1440                             sp_document_height(doc));
1441                 NR::Rect bbox(x, y);
1443                 // std::cout << "Page " << bbox;
1444                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1445                     key = SELECTION_PAGE;
1446                 }
1448                 break;
1449            }
1450         default:
1451            break;
1452         }
1453     }
1454     // std::cout << std::endl;
1456     if (key == SELECTION_NUMBER_OF) {
1457         key = SELECTION_CUSTOM;
1458     }
1460     /* We're now using a custom size, not a fixed one */
1461     /* printf("Detecting state: %s\n", selection_names[key]); */
1462     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1463     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1464     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1465     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1467     return;
1468 } /* sp_export_detect_size */
1470 /// Called when area x0 value is changed
1471 static void
1472 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1474     float x0, x1, xdpi, width;
1476     if (gtk_object_get_data (base, "update"))
1477         return;
1479     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1480             (base, "units")))
1481     {
1482         return;
1483     }
1485     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1487     x0 = sp_export_value_get_px (base, "x0");
1488     x1 = sp_export_value_get_px (base, "x1");
1489     xdpi = sp_export_value_get (base, "xdpi");
1491     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1493     if (width < SP_EXPORT_MIN_SIZE) {
1494         const gchar *key;
1495         width = SP_EXPORT_MIN_SIZE;
1496         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1498         if (!strcmp (key, "x0")) {
1499             x1 = x0 + width * DPI_BASE / xdpi;
1500             sp_export_value_set_px (base, "x1", x1);
1501         } else {
1502             x0 = x1 - width * DPI_BASE / xdpi;
1503             sp_export_value_set_px (base, "x0", x0);
1504         }
1505     }
1507     sp_export_value_set_px (base, "width", x1 - x0);
1508     sp_export_value_set (base, "bmwidth", width);
1510     sp_export_detect_size(base);
1512     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1514     return;
1515 } // end of sp_export_area_x_value_changed()
1517 /// Called when area y0 value is changed.
1518 static void
1519 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1521     float y0, y1, ydpi, height;
1523     if (gtk_object_get_data (base, "update"))
1524         return;
1526     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1527            (base, "units")))
1528     {
1529         return;
1530     }
1532     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1534     y0 = sp_export_value_get_px (base, "y0");
1535     y1 = sp_export_value_get_px (base, "y1");
1536     ydpi = sp_export_value_get (base, "ydpi");
1538     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1540     if (height < SP_EXPORT_MIN_SIZE) {
1541         const gchar *key;
1542         height = SP_EXPORT_MIN_SIZE;
1543         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1544         if (!strcmp (key, "y0")) {
1545             y1 = y0 + height * DPI_BASE / ydpi;
1546             sp_export_value_set_px (base, "y1", y1);
1547         } else {
1548             y0 = y1 - height * DPI_BASE / ydpi;
1549             sp_export_value_set_px (base, "y0", y0);
1550         }
1551     }
1553     sp_export_value_set_px (base, "height", y1 - y0);
1554     sp_export_value_set (base, "bmheight", height);
1556     sp_export_detect_size(base);
1558     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1560     return;
1561 } // end of sp_export_area_y_value_changed()
1563 /// Called when x1-x0 or area width is changed
1564 static void
1565 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1567     float x0, x1, xdpi, width, bmwidth;
1569     if (gtk_object_get_data (base, "update"))
1570         return;
1572     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1573            (base, "units"))) {
1574         return;
1575     }
1577     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1579     x0 = sp_export_value_get_px (base, "x0");
1580     x1 = sp_export_value_get_px (base, "x1");
1581     xdpi = sp_export_value_get (base, "xdpi");
1582     width = sp_export_value_get_px (base, "width");
1583     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1585     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1587         bmwidth = SP_EXPORT_MIN_SIZE;
1588         width = bmwidth * DPI_BASE / xdpi;
1589         sp_export_value_set_px (base, "width", width);
1590     }
1592     sp_export_value_set_px (base, "x1", x0 + width);
1593     sp_export_value_set (base, "bmwidth", bmwidth);
1595     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1597     return;
1598 } // end of sp_export_area_width_value_changed()
1600 /// Called when y1-y0 or area height is changed.
1601 static void
1602 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1605     float y0, y1, ydpi, height, bmheight;
1607     if (gtk_object_get_data (base, "update"))
1608         return;
1610     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1611            (base, "units"))) {
1612         return;
1613     }
1615     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1617     y0 = sp_export_value_get_px (base, "y0");
1618     y1 = sp_export_value_get_px (base, "y1");
1619     ydpi = sp_export_value_get (base, "ydpi");
1620     height = sp_export_value_get_px (base, "height");
1621     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1623     if (bmheight < SP_EXPORT_MIN_SIZE) {
1624         bmheight = SP_EXPORT_MIN_SIZE;
1625         height = bmheight * DPI_BASE / ydpi;
1626         sp_export_value_set_px (base, "height", height);
1627     }
1629     sp_export_value_set_px (base, "y1", y0 + height);
1630     sp_export_value_set (base, "bmheight", bmheight);
1632     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1634     return;
1635 } // end of sp_export_area_height_value_changed()
1637 /**
1638     \brief  A function to set the ydpi
1639     \param  base  The export dialog
1641     This function grabs all of the y values and then figures out the
1642     new bitmap size based on the changing dpi value.  The dpi value is
1643     gotten from the xdpi setting as these can not currently be independent.
1644 */
1645 static void
1646 sp_export_set_image_y (GtkObject *base)
1648     float y0, y1, xdpi;
1650     y0 = sp_export_value_get_px (base, "y0");
1651     y1 = sp_export_value_get_px (base, "y1");
1652     xdpi = sp_export_value_get (base, "xdpi");
1654     sp_export_value_set (base, "ydpi", xdpi);
1655     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1657     return;
1658 } // end of sp_export_set_image_y()
1660 /**
1661     \brief  A function to set the xdpi
1662     \param  base  The export dialog
1664     This function grabs all of the x values and then figures out the
1665     new bitmap size based on the changing dpi value.  The dpi value is
1666     gotten from the xdpi setting as these can not currently be independent.
1667 */
1668 static void
1669 sp_export_set_image_x (GtkObject *base)
1671     float x0, x1, xdpi;
1673     x0 = sp_export_value_get_px (base, "x0");
1674     x1 = sp_export_value_get_px (base, "x1");
1675     xdpi = sp_export_value_get (base, "xdpi");
1677     sp_export_value_set (base, "ydpi", xdpi);
1678     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1680     return;
1681 } // end of sp_export_set_image_x()
1683 /// Called when pixel width is changed
1684 static void
1685 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1687     float x0, x1, bmwidth, xdpi;
1689     if (gtk_object_get_data (base, "update"))
1690         return;
1692     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1693            (base, "units"))) {
1694        return;
1695     }
1697     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1699     x0 = sp_export_value_get_px (base, "x0");
1700     x1 = sp_export_value_get_px (base, "x1");
1701     bmwidth = sp_export_value_get (base, "bmwidth");
1703     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1704         bmwidth = SP_EXPORT_MIN_SIZE;
1705         sp_export_value_set (base, "bmwidth", bmwidth);
1706     }
1708     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1709     sp_export_value_set (base, "xdpi", xdpi);
1711     sp_export_set_image_y (base);
1713     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1715     return;
1716 } // end of sp_export_bitmap_width_value_changed()
1718 /// Called when pixel height is changed
1719 static void
1720 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1722     float y0, y1, bmheight, xdpi;
1724     if (gtk_object_get_data (base, "update"))
1725         return;
1727     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1728            (base, "units"))) {
1729        return;
1730     }
1732     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1734     y0 = sp_export_value_get_px (base, "y0");
1735     y1 = sp_export_value_get_px (base, "y1");
1736     bmheight = sp_export_value_get (base, "bmheight");
1738     if (bmheight < SP_EXPORT_MIN_SIZE) {
1739         bmheight = SP_EXPORT_MIN_SIZE;
1740         sp_export_value_set (base, "bmheight", bmheight);
1741     }
1743     xdpi = bmheight * DPI_BASE / (y1 - y0);
1744     sp_export_value_set (base, "xdpi", xdpi);
1746     sp_export_set_image_x (base);
1748     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1750     return;
1751 } // end of sp_export_bitmap_width_value_changed()
1753 /**
1754     \brief  A function to adjust the bitmap width when the xdpi value changes
1755     \param  adj  The adjustment that was changed
1756     \param  base The export dialog itself
1758     The first thing this function checks is to see if we are doing an
1759     update.  If we are, this function just returns because there is another
1760     instance of it that will handle everything for us.  If there is a
1761     units change, we also assume that everyone is being updated appropriately
1762     and there is nothing for us to do.
1764     If we're the highest level function, we set the update flag, and
1765     continue on our way.
1767     All of the values are grabbed using the \c sp_export_value_get functions
1768     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1769     xdpi value is saved in the preferences for the next time the dialog
1770     is opened.  (does the selection dpi need to be set here?)
1772     A check is done to to ensure that we aren't outputing an invalid width,
1773     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1774     changed to make it valid.
1776     After all of this the bitmap width is changed.
1778     We also change the ydpi.  This is a temporary hack as these can not
1779     currently be independent.  This is likely to change in the future.
1780 */
1781 void
1782 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1784     float x0, x1, xdpi, bmwidth;
1786     if (gtk_object_get_data (base, "update"))
1787         return;
1789     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1790            (base, "units"))) {
1791        return;
1792     }
1794     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1796     x0 = sp_export_value_get_px (base, "x0");
1797     x1 = sp_export_value_get_px (base, "x1");
1798     xdpi = sp_export_value_get (base, "xdpi");
1800     // remember xdpi setting
1801     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1803     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1805     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1806         bmwidth = SP_EXPORT_MIN_SIZE;
1807         if (x1 != x0)
1808             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1809         else
1810             xdpi = DPI_BASE;
1811         sp_export_value_set (base, "xdpi", xdpi);
1812     }
1814     sp_export_value_set (base, "bmwidth", bmwidth);
1816     sp_export_set_image_y (base);
1818     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1820     return;
1821 } // end of sp_export_xdpi_value_changed()
1824 /**
1825     \brief  A function to change the area that is used for the exported
1826             bitmap.
1827     \param  base  This is the export dialog
1828     \param  x0    Horizontal upper left hand corner of the picture in points
1829     \param  y0    Vertical upper left hand corner of the picture in points
1830     \param  x1    Horizontal lower right hand corner of the picture in points
1831     \param  y1    Vertical lower right hand corner of the picture in points
1833     This function just calls \c sp_export_value_set_px for each of the
1834     parameters that is passed in.  This allows for setting them all in
1835     one convient area.
1837     Update is set to suspend all of the other test running while all the
1838     values are being set up.  This allows for a performance increase, but
1839     it also means that the wrong type won't be detected with only some of
1840     the values set.  After all the values are set everyone is told that
1841     there has been an update.
1842 */
1843 static void
1844 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1846     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1847     sp_export_value_set_px (base, "x1", x1);
1848     sp_export_value_set_px (base, "y1", y1);
1849     sp_export_value_set_px (base, "x0", x0);
1850     sp_export_value_set_px (base, "y0", y0);
1851     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1853     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1854     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1856     return;
1859 /**
1860     \brief  Sets the value of an adjustment
1861     \param  base  The export dialog
1862     \param  key   Which adjustment to set
1863     \param  val   What value to set it to
1865     This function finds the adjustment using the data stored in the
1866     export dialog.  After finding the adjustment it then sets
1867     the value of it.
1868 */
1869 static void
1870 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1872     GtkAdjustment *adj;
1874     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1876     gtk_adjustment_set_value (adj, val);
1879 /**
1880     \brief  A function to set a value using the units points
1881     \param  base  The export dialog
1882     \param  key   Which value should be set
1883     \param  val   What the value should be in points
1885     This function first gets the adjustment for the key that is passed
1886     in.  It then figures out what units are currently being used in the
1887     dialog.  After doing all of that, it then converts the incoming
1888     value and sets the adjustment.
1889 */
1890 static void
1891 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1893     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1895     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1897     return;
1900 /**
1901     \brief  Get the value of an adjustment in the export dialog
1902     \param  base  The export dialog
1903     \param  key   Which adjustment is being looked for
1904     \return The value in the specified adjustment
1906     This function gets the adjustment from the data field in the export
1907     dialog.  It then grabs the value from the adjustment.
1908 */
1909 static float
1910 sp_export_value_get ( GtkObject *base, const gchar *key )
1912     GtkAdjustment *adj;
1914     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1916     return adj->value;
1919 /**
1920     \brief  Grabs a value in the export dialog and converts the unit
1921             to points
1922     \param  base  The export dialog
1923     \param  key   Which value should be returned
1924     \return The value in the adjustment in points
1926     This function, at its most basic, is a call to \c sp_export_value_get
1927     to get the value of the adjustment.  It then finds the units that
1928     are being used by looking at the "units" attribute of the export
1929     dialog.  Using that it converts the returned value into points.
1930 */
1931 static float
1932 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1934     float value = sp_export_value_get(base, key);
1935     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1937     return sp_units_get_pixels (value, *unit);
1938 } // end of sp_export_value_get_px()
1940 /**
1941     \brief  This function is called when the filename is changed by
1942             anyone.  It resets the virgin bit.
1943     \param  object  Text entry box
1944     \param  data    The export dialog
1945     \return None
1947     This function gets called when the text area is modified.  It is
1948     looking for the case where the text area is modified from its
1949     original value.  In that case it sets the "filename-modified" bit
1950     to TRUE.  If the text dialog returns back to the original text, the
1951     bit gets reset.  This should stop simple mistakes.
1952 */
1953 static void
1954 sp_export_filename_modified (GtkObject * object, gpointer data)
1956     GtkWidget * text_entry = (GtkWidget *)object;
1957     GtkWidget * export_dialog = (GtkWidget *)data;
1959     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1960         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1961 //        printf("Modified: FALSE\n");
1962     } else {
1963         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1964 //        printf("Modified: TRUE\n");
1965     }
1967     return;
1968 } // end sp_export_filename_modified
1970 /*
1971   Local Variables:
1972   mode:c++
1973   c-file-style:"stroustrup"
1974   c-file-offsets:((innamespace . 0)(inline-open . 0))
1975   indent-tabs-mode:nil
1976   fill-column:99
1977   End:
1978 */
1979 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :