Code

gtk adjustment fix
[inkscape.git] / src / dialogs / export.cpp
1 /** @file
2  * @brief  PNG export dialog
3  */
4 /* Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   bulia byak <buliabyak@users.sf.net>
7  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
8  *
9  * Copyright (C) 1999-2007 Authors
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
20 #include <png.h>
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
32 #ifdef WITH_GNOME_VFS
33 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
34 #endif
36 #include <glibmm/i18n.h>
37 #include "helper/unit-menu.h"
38 #include "helper/units.h"
39 #include "unit-constants.h"
40 #include "helper/window.h"
41 #include "inkscape-private.h"
42 #include "document.h"
43 #include "desktop-handles.h"
44 #include "sp-item.h"
45 #include "selection.h"
46 #include "file.h"
47 #include "macros.h"
48 #include "sp-namedview.h"
49 #include "selection-chemistry.h"
51 #include "dialog-events.h"
52 #include "preferences.h"
53 #include "verbs.h"
54 #include "interface.h"
56 #include "extension/output.h"
57 #include "extension/db.h"
59 #include "io/sys.h"
61 #include "helper/png-write.h"
64 #define SP_EXPORT_MIN_SIZE 1.0
66 #define DPI_BASE PX_PER_IN
68 #define EXPORT_COORD_PRECISION 3
70 #define MIN_ONSCREEN_DISTANCE 50
72 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
73 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
74 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
76 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj,
77                                                    GtkObject *base);
79 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj,
80                                                    GtkObject *base);
82 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj,
83                                                    GtkObject *base);
85 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj,
86                                                    GtkObject *base);
88 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
89                                                    GtkObject *base);
91 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
92                                                    GtkObject *base);
94 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj,
95                                                    GtkObject *base);
97 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
98                                           Inkscape::Selection *selection,
99                                           GtkObject *base);
100 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
101                                            Inkscape::Selection *selection,
102                                            guint flags,
103                                            GtkObject *base );
105 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
106 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
107 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
108 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
109 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
111 static void sp_export_filename_modified (GtkObject * object, gpointer data);
112 static inline void sp_export_find_default_selection(GtkWidget * dlg);
113 static void sp_export_detect_size(GtkObject * base);
115 static Glib::ustring const prefs_path = "/dialogs/export/";
117 // these all need to be reinitialized to their defaults during dialog_destroy
118 static GtkWidget *dlg = NULL;
119 static win_data wd;
120 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
121 static gchar * original_name = NULL;
122 static gchar * doc_export_name = NULL;
123 static bool was_empty = TRUE;
125 /** What type of button is being pressed */
126 enum selection_type {
127     SELECTION_PAGE = 0,  /**< Export the whole page */
128     SELECTION_DRAWING,   /**< Export everything drawn on the page */
129     SELECTION_SELECTION, /**< Export everything that is selected */
130     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
131     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
132 };
134 /** A list of strings that is used both in the preferences, and in the
135     data fields to describe the various values of \c selection_type. */
136 static const char * selection_names[SELECTION_NUMBER_OF] = {
137     "page", "drawing", "selection", "custom"};
139 /** The names on the buttons for the various selection types. */
140 static const char * selection_labels[SELECTION_NUMBER_OF] = {
141     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
143 static void
144 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
146     sp_signal_disconnect_by_data (INKSCAPE, dlg);
148     wd.win = dlg = NULL;
149     wd.stop = 0;
150     x = -1000; y = -1000; w = 0; h = 0;
151     g_free(original_name);
152     original_name = NULL;
153     g_free(doc_export_name);
154     doc_export_name = NULL;
155     was_empty = TRUE;
157     return;
158 } // end of sp_export_dialog_destroy()
160 /// Called when dialog is closed or inkscape is shut down.
161 static bool
162 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
165     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
166     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
168     if (x<0) x=0;
169     if (y<0) y=0;
170     
171     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
172     prefs->setInt(prefs_path + "x", x);
173     prefs->setInt(prefs_path + "y", y);
174     prefs->setInt(prefs_path + "w", w);
175     prefs->setInt(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 const *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 *adj = gtk_adjustment_new( val, min, max, step, page, 0 );
210     gtk_object_set_data( adj, "key", const_cast<gchar *>(key) );
211     gtk_object_set_data( GTK_OBJECT (dlg), (const gchar *)key, adj );
213     if (us) {
214         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
215                                           GTK_ADJUSTMENT (adj) );
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 (adj), 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 (adj, "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 );
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, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
318                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), 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, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
334                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), 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     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
393     if (!dlg) {
395         gchar title[500];
396         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
398         dlg = sp_window_new (title, TRUE);
400         if (x == -1000 || y == -1000) {
401             x = prefs->getInt(prefs_path + "x", 0);
402             y = prefs->getInt(prefs_path + "y", 0);
403         }
405         if (w ==0 || h == 0) {
406             w = prefs->getInt(prefs_path + "w", 0);
407             h = prefs->getInt(prefs_path + "h", 0);
408         }
410 //        if (x<0) x=0;
411 //        if (y<0) y=0;
413         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
414         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
415             gtk_window_move ((GtkWindow *) dlg, x, y);
416         else
417             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
418         sp_transientize (dlg);
419         wd.win = dlg;
420         wd.stop = 0;
422         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
423                              G_CALLBACK (sp_transientize_callback), &wd);
425         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
426                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
428         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
429                              G_CALLBACK (sp_export_dialog_destroy), dlg);
431         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
432                              G_CALLBACK (sp_export_dialog_delete), dlg);
434         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
435                              G_CALLBACK (sp_export_dialog_delete), dlg);
437         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
438                              G_CALLBACK (sp_dialog_hide), dlg);
440         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
441                              G_CALLBACK (sp_dialog_unhide), dlg);
443         GtkTooltips *tt = gtk_tooltips_new();
445         Gtk::VBox *vb = new Gtk::VBox(false, 3);
446         vb->set_border_width(3);
447         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
449         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
450         vb_singleexport->set_border_width(0);
451         vb->pack_start(*vb_singleexport);
452         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
454         /* Export area frame */
455         {
456             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
457             area_box->set_border_width(3);
458             vb_singleexport->pack_start(*area_box, false, false, 0);
459         }
461         /* Bitmap size frame */
462         {
463             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
464             size_box->set_border_width(3);
466             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
467             lbl->set_use_markup(true);
468             size_box->pack_start(*lbl, false, false, 0);
469             const int rows = 2;
470             const int cols = 5;
471             const bool homogeneous = false;
472             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
473             t->set_row_spacings (4);
474             t->set_col_spacings (4);
475             size_box->pack_start(*t);
477             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
478                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
479                                        _("_Width:"), _("pixels at"), 0, 1,
480                                        G_CALLBACK
481                                        (sp_export_bitmap_width_value_changed),
482                                        dlg );
484             sp_export_spinbutton_new ( "xdpi",
485                                        prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
486                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
487                                        NULL, _("dp_i"), 2, 1,
488                                        G_CALLBACK (sp_export_xdpi_value_changed),
489                                        dlg );
491             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
492                                        NULL, GTK_WIDGET(t->gobj()), 0, 1,
493                                        _("_Height:"), _("pixels at"), 0, 1,
494                                        G_CALLBACK
495                                        (sp_export_bitmap_height_value_changed),
496                                        dlg );
498             /** \todo
499              * Needs fixing: there's no way to set ydpi currently, so we use
500              *       the defaultxdpi value here, too...
501              */
502             sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/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 (ngettext("Batch export %d selected object","Batch export %d selected objects",num), 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         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
702         int i = SELECTION_NUMBER_OF;
704         Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
706         if (!what.empty()) {
707             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
708                 if (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);
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                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
780                 if (bbox) {
781                     sp_export_set_area (base, bbox->min()[Geom::X],
782                                               bbox->min()[Geom::Y],
783                                               bbox->max()[Geom::X],
784                                               bbox->max()[Geom::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         Geom::OptRect 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 = Geom::Rect(Geom::Point(0.0, 0.0),
870                                   Geom::Point(sp_document_width(doc), sp_document_height(doc)));
872                 // std::cout << "Using selection: PAGE" << std::endl;
873                 key = SELECTION_PAGE;
874                 break;
875             case SELECTION_CUSTOM:
876             default:
877                 break;
878         } // switch
880         // remember area setting
881         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
882         prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
884         if ( key != SELECTION_CUSTOM && bbox ) {
885             sp_export_set_area (base, bbox->min()[Geom::X],
886                                       bbox->min()[Geom::Y],
887                                       bbox->max()[Geom::X],
888                                       bbox->max()[Geom::Y]);
889         }
891     } // end of if ( SP_ACTIVE_DESKTOP )
894     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
895         GtkWidget * file_entry;
896         const gchar * filename = NULL;
897         float xdpi = 0.0, ydpi = 0.0;
899         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
901         switch (key) {
902             case SELECTION_PAGE:
903             case SELECTION_DRAWING: {
904                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
905                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
907                 if (filename == NULL) {
908                     if (doc_export_name != NULL) {
909                         filename = g_strdup(doc_export_name);
910                     } else {
911                         filename = g_strdup("");
912                     }
913                 }
914                 break;
915             }
916             case SELECTION_SELECTION:
917                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
919                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
921                     /* If we still don't have a filename -- let's build
922                        one that's nice */
923                     if (filename == NULL) {
924                         const gchar * id = NULL;
925                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
926                         for(; reprlst != NULL; reprlst = reprlst->next) {
927                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
928                             if (repr->attribute("id")) {
929                                 id = repr->attribute("id");
930                                 break;
931                             }
932                         }
934                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
935                     }
936                 }
937                 break;
938             case SELECTION_CUSTOM:
939             default:
940                 break;
941         }
943         if (filename != NULL) {
944             g_free(original_name);
945             original_name = g_strdup(filename);
946             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
947         }
949         if (xdpi != 0.0) {
950             sp_export_value_set(base, "xdpi", xdpi);
951         }
953         /* These can't be separate, and setting x sets y, so for
954            now setting this is disabled.  Hopefully it won't be in
955            the future */
956         if (FALSE && ydpi != 0.0) {
957             sp_export_value_set(base, "ydpi", ydpi);
958         }
959     }
961     return;
962 } // end of sp_export_area_toggled()
964 /// Called when dialog is deleted
965 static gint
966 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
968     g_object_set_data (base, "cancel", (gpointer) 1);
969     return TRUE;
970 } // end of sp_export_progress_delete()
972 /// Called when progress is cancelled
973 static void
974 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
976     g_object_set_data (base, "cancel", (gpointer) 1);
977 } // end of sp_export_progress_cancel()
979 /// Called for every progress iteration
980 static unsigned int
981 sp_export_progress_callback (float value, void *data)
983     GtkWidget *prg;
984     int evtcount;
986     if (g_object_get_data ((GObject *) data, "cancel"))
987         return FALSE;
989     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
990     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
992     evtcount = 0;
993     while ((evtcount < 16) && gdk_events_pending ()) {
994             gtk_main_iteration_do (FALSE);
995             evtcount += 1;
996     }
998     gtk_main_iteration_do (FALSE);
1000     return TRUE;
1002 } // end of sp_export_progress_callback()
1004 GtkWidget *
1005 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1006     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1008     dlg = gtk_dialog_new ();
1009     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1010     prg = gtk_progress_bar_new ();
1011     sp_transientize (dlg);
1012     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1013     g_object_set_data ((GObject *) base, "progress", prg);
1015     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1017     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1018                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1019     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1020                         prg, FALSE, FALSE, 4 );
1021     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1022                                   GTK_STOCK_CANCEL,
1023                                   GTK_RESPONSE_CANCEL );
1025     g_signal_connect ( (GObject *) dlg, "delete_event",
1026                        (GCallback) sp_export_progress_delete, base);
1027     g_signal_connect ( (GObject *) btn, "clicked",
1028                        (GCallback) sp_export_progress_cancel, base);
1029     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1030     gtk_widget_show_all (dlg);
1032     return dlg;
1035 // FIXME: Some lib function should be available to do this ...
1036 static gchar *
1037 filename_add_extension (const gchar *filename, const gchar *extension)
1039   const gchar *dot;
1041   dot = strrchr (filename, '.');
1042   if ( !dot )
1043     return g_strconcat (filename, ".", extension, NULL);
1044   {
1045     if (dot[1] == '\0')
1046       return g_strconcat (filename, extension, NULL);
1047     else
1048     {
1049       if (g_strcasecmp (dot + 1, extension) == 0)
1050         return g_strdup (filename);
1051       else
1052       {
1053         return g_strconcat (filename, ".", extension, NULL);
1054       }
1055     }
1056   }
1059 /// Called when export button is clicked
1060 static void
1061 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1063     if (!SP_ACTIVE_DESKTOP) return;
1065     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1067     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1068     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1069     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1070     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1071         // Batch export of selected objects
1073         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1074         gint n = 0;
1076         if (num < 1)
1077             return;
1079         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1080         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1081         g_free (progress_text);
1083         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1084              i != NULL;
1085              i = i->next) {
1086             SPItem *item = (SPItem *) i->data;
1087             // retrieve export filename hint
1088             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1089             if (!fn) {
1090                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1091             }
1093             // retrieve export dpi hints
1094             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1095             gdouble dpi = 0.0;
1096             if (dpi_hint) {
1097                 dpi = atof(dpi_hint);
1098             }
1099             if (dpi == 0.0) {
1100                 dpi = DPI_BASE;
1101             }
1103             Geom::OptRect area;
1104             sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1105             if (area) {
1106                 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1107                 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1109                 if (width > 1 && height > 1) {
1110                     /* Do export */
1111                     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1112                                              *area, width, height, dpi, dpi,
1113                                              nv->pagecolor,
1114                                              NULL, NULL, TRUE,  // overwrite without asking
1115                                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1116                             )) {
1117                         gchar * error;
1118                         gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1119                         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1120                         sp_ui_error_dialog(error);
1121                         g_free(safeFile);
1122                         g_free(error);
1123                     }
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     unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1145     unsigned long 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 (%lu x %lu)"), fn, width, height);
1179     g_free (fn);
1180     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1181     g_free (progress_text);
1183     /* Do export */
1184     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1185                              Geom::Rect(Geom::Point(x0, y0), Geom::Point(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             }
1234             sp_document_set_undo_sensitive(doc, saved);
1236             if (modified) {
1237                 doc->setModifiedSinceSave();
1238             }
1239             break;
1240         }
1241         case SELECTION_SELECTION: {
1242             const GSList * reprlst;
1243             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1244             bool modified = false;
1246             bool saved = sp_document_get_undo_sensitive(doc);
1247             sp_document_set_undo_sensitive(doc, false);
1248             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1250             for(; reprlst != NULL; reprlst = reprlst->next) {
1251                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1252                 const gchar * temp_string;
1254                 if (repr->attribute("id") == NULL ||
1255                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1256                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1257                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1258                     temp_string = repr->attribute("inkscape:export-filename");
1259                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1260                         repr->setAttribute("inkscape:export-filename", filename_ext);
1261                         modified = true;
1262                     }
1263                 }
1264                 temp_string = repr->attribute("inkscape:export-xdpi");
1265                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1266                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1267                     modified = true;
1268                 }
1269                 temp_string = repr->attribute("inkscape:export-ydpi");
1270                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1271                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1272                     modified = true;
1273                 }
1274             }
1275             sp_document_set_undo_sensitive(doc, saved);
1277             if (modified) {
1278                 doc->setModifiedSinceSave();
1279             }
1280             break;
1281         }
1282         default:
1283             break;
1284     }
1286     g_free (filename_ext);
1288     }
1290 } // end of sp_export_export_clicked()
1292 /// Called when Browse button is clicked
1293 static void
1294 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1296     GtkWidget *fs, *fe;
1297     const gchar *filename;
1299     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1300                                       (GtkWindow*)dlg,
1301                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1302                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1303                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1304                                       NULL );
1306 #ifdef WITH_GNOME_VFS
1307     if (gnome_vfs_initialized()) {
1308         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1309     }
1310 #endif
1312     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1314     sp_transientize (fs);
1316     gtk_window_set_modal(GTK_WINDOW (fs), true);
1318     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1320     if (*filename == '\0') {
1321         filename = homedir_path(NULL);
1322     }
1324     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1326     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1327     {
1328         gchar *file;
1330         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1332         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1333         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1335         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1337         g_free(utf8file);
1338         g_free(file);
1339     }
1341     gtk_widget_destroy (fs);
1343     return;
1344 } // end of sp_export_browse_clicked()
1346 // TODO: Move this to nr-rect-fns.h.
1347 static bool
1348 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1350     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1351     return (
1352         (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1353         (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1354         (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1355         (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1356         );
1359 /**
1360     \brief  This function is used to detect the current selection setting
1361             based on the values in the x0, y0, x1 and y0 fields.
1362     \param  base  The export dialog itself
1364     One of the most confusing parts of this function is why the array
1365     is built at the beginning.  What needs to happen here is that we
1366     should always check the current selection to see if it is the valid
1367     one.  While this is a performance improvement it is also a usability
1368     one during the cases where things like selections and drawings match
1369     size.  This way buttons change less 'randomly' (atleast in the eyes
1370     of the user).  To do this an array is built where the current selection
1371     type is placed first, and then the others in an order from smallest
1372     to largest (this can be configured by reshuffling \c test_order).
1374     All of the values in this function are rounded to two decimal places
1375     because that is what is shown to the user.  While everything is kept
1376     more accurate than that, the user can't control more acurrate than
1377     that, so for this to work for them - it needs to check on that level
1378     of accuracy.
1380     \todo finish writing this up
1381 */
1382 static void
1383 sp_export_detect_size(GtkObject * base) {
1384     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1385     selection_type this_test[SELECTION_NUMBER_OF + 1];
1386     selection_type key = SELECTION_NUMBER_OF;
1388     Geom::Point x(sp_export_value_get_px (base, "x0"),
1389                   sp_export_value_get_px (base, "y0"));
1390     Geom::Point y(sp_export_value_get_px (base, "x1"),
1391                   sp_export_value_get_px (base, "y1"));
1392     Geom::Rect current_bbox(x, y);
1393     //std::cout << "Current " << current_bbox;
1395     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1396     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1397         this_test[i + 1] = test_order[i];
1398     }
1400     for (int i = 0;
1401             i < SELECTION_NUMBER_OF + 1 &&
1402                 key == SELECTION_NUMBER_OF &&
1403                 SP_ACTIVE_DESKTOP != NULL;
1404             i++) {
1405         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1406         switch (this_test[i]) {
1407             case SELECTION_SELECTION:
1408                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1409                     Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1411                     //std::cout << "Selection " << bbox;
1412                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1413                         key = SELECTION_SELECTION;
1414                     }
1415                 }
1416                 break;
1417             case SELECTION_DRAWING: {
1418                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1420                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1422                 // std::cout << "Drawing " << bbox2;
1423                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1424                     key = SELECTION_DRAWING;
1425                 }
1426                 break;
1427             }
1429             case SELECTION_PAGE: {
1430                 SPDocument *doc;
1432                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1434                 Geom::Point x(0.0, 0.0);
1435                 Geom::Point y(sp_document_width(doc),
1436                               sp_document_height(doc));
1437                 Geom::Rect bbox(x, y);
1439                 // std::cout << "Page " << bbox;
1440                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1441                     key = SELECTION_PAGE;
1442                 }
1444                 break;
1445            }
1446         default:
1447            break;
1448         }
1449     }
1450     // std::cout << std::endl;
1452     if (key == SELECTION_NUMBER_OF) {
1453         key = SELECTION_CUSTOM;
1454     }
1456     /* We're now using a custom size, not a fixed one */
1457     /* printf("Detecting state: %s\n", selection_names[key]); */
1458     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1459     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1460     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1461     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1463     return;
1464 } /* sp_export_detect_size */
1466 /// Called when area x0 value is changed
1467 static void
1468 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1470     float x0, x1, xdpi, width;
1472     if (gtk_object_get_data (base, "update"))
1473         return;
1475     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1476             (base, "units")))
1477     {
1478         return;
1479     }
1481     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1483     x0 = sp_export_value_get_px (base, "x0");
1484     x1 = sp_export_value_get_px (base, "x1");
1485     xdpi = sp_export_value_get (base, "xdpi");
1487     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1489     if (width < SP_EXPORT_MIN_SIZE) {
1490         const gchar *key;
1491         width = SP_EXPORT_MIN_SIZE;
1492         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1494         if (!strcmp (key, "x0")) {
1495             x1 = x0 + width * DPI_BASE / xdpi;
1496             sp_export_value_set_px (base, "x1", x1);
1497         } else {
1498             x0 = x1 - width * DPI_BASE / xdpi;
1499             sp_export_value_set_px (base, "x0", x0);
1500         }
1501     }
1503     sp_export_value_set_px (base, "width", x1 - x0);
1504     sp_export_value_set (base, "bmwidth", width);
1506     sp_export_detect_size(base);
1508     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1510     return;
1511 } // end of sp_export_area_x_value_changed()
1513 /// Called when area y0 value is changed.
1514 static void
1515 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1517     float y0, y1, ydpi, height;
1519     if (gtk_object_get_data (base, "update"))
1520         return;
1522     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1523            (base, "units")))
1524     {
1525         return;
1526     }
1528     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1530     y0 = sp_export_value_get_px (base, "y0");
1531     y1 = sp_export_value_get_px (base, "y1");
1532     ydpi = sp_export_value_get (base, "ydpi");
1534     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1536     if (height < SP_EXPORT_MIN_SIZE) {
1537         const gchar *key;
1538         height = SP_EXPORT_MIN_SIZE;
1539         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1540         if (!strcmp (key, "y0")) {
1541             y1 = y0 + height * DPI_BASE / ydpi;
1542             sp_export_value_set_px (base, "y1", y1);
1543         } else {
1544             y0 = y1 - height * DPI_BASE / ydpi;
1545             sp_export_value_set_px (base, "y0", y0);
1546         }
1547     }
1549     sp_export_value_set_px (base, "height", y1 - y0);
1550     sp_export_value_set (base, "bmheight", height);
1552     sp_export_detect_size(base);
1554     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1556     return;
1557 } // end of sp_export_area_y_value_changed()
1559 /// Called when x1-x0 or area width is changed
1560 static void
1561 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1563     float x0, x1, xdpi, width, bmwidth;
1565     if (gtk_object_get_data (base, "update"))
1566         return;
1568     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1569            (base, "units"))) {
1570         return;
1571     }
1573     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1575     x0 = sp_export_value_get_px (base, "x0");
1576     x1 = sp_export_value_get_px (base, "x1");
1577     xdpi = sp_export_value_get (base, "xdpi");
1578     width = sp_export_value_get_px (base, "width");
1579     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1581     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1583         bmwidth = SP_EXPORT_MIN_SIZE;
1584         width = bmwidth * DPI_BASE / xdpi;
1585         sp_export_value_set_px (base, "width", width);
1586     }
1588     sp_export_value_set_px (base, "x1", x0 + width);
1589     sp_export_value_set (base, "bmwidth", bmwidth);
1591     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1593     return;
1594 } // end of sp_export_area_width_value_changed()
1596 /// Called when y1-y0 or area height is changed.
1597 static void
1598 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1601     float y0, y1, ydpi, height, bmheight;
1603     if (gtk_object_get_data (base, "update"))
1604         return;
1606     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1607            (base, "units"))) {
1608         return;
1609     }
1611     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1613     y0 = sp_export_value_get_px (base, "y0");
1614     y1 = sp_export_value_get_px (base, "y1");
1615     ydpi = sp_export_value_get (base, "ydpi");
1616     height = sp_export_value_get_px (base, "height");
1617     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1619     if (bmheight < SP_EXPORT_MIN_SIZE) {
1620         bmheight = SP_EXPORT_MIN_SIZE;
1621         height = bmheight * DPI_BASE / ydpi;
1622         sp_export_value_set_px (base, "height", height);
1623     }
1625     sp_export_value_set_px (base, "y1", y0 + height);
1626     sp_export_value_set (base, "bmheight", bmheight);
1628     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1630     return;
1631 } // end of sp_export_area_height_value_changed()
1633 /**
1634     \brief  A function to set the ydpi
1635     \param  base  The export dialog
1637     This function grabs all of the y values and then figures out the
1638     new bitmap size based on the changing dpi value.  The dpi value is
1639     gotten from the xdpi setting as these can not currently be independent.
1640 */
1641 static void
1642 sp_export_set_image_y (GtkObject *base)
1644     float y0, y1, xdpi;
1646     y0 = sp_export_value_get_px (base, "y0");
1647     y1 = sp_export_value_get_px (base, "y1");
1648     xdpi = sp_export_value_get (base, "xdpi");
1650     sp_export_value_set (base, "ydpi", xdpi);
1651     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1653     return;
1654 } // end of sp_export_set_image_y()
1656 /**
1657     \brief  A function to set the xdpi
1658     \param  base  The export dialog
1660     This function grabs all of the x values and then figures out the
1661     new bitmap size based on the changing dpi value.  The dpi value is
1662     gotten from the xdpi setting as these can not currently be independent.
1663 */
1664 static void
1665 sp_export_set_image_x (GtkObject *base)
1667     float x0, x1, xdpi;
1669     x0 = sp_export_value_get_px (base, "x0");
1670     x1 = sp_export_value_get_px (base, "x1");
1671     xdpi = sp_export_value_get (base, "xdpi");
1673     sp_export_value_set (base, "ydpi", xdpi);
1674     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1676     return;
1677 } // end of sp_export_set_image_x()
1679 /// Called when pixel width is changed
1680 static void
1681 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1683     float x0, x1, bmwidth, xdpi;
1685     if (gtk_object_get_data (base, "update"))
1686         return;
1688     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1689            (base, "units"))) {
1690        return;
1691     }
1693     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1695     x0 = sp_export_value_get_px (base, "x0");
1696     x1 = sp_export_value_get_px (base, "x1");
1697     bmwidth = sp_export_value_get (base, "bmwidth");
1699     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1700         bmwidth = SP_EXPORT_MIN_SIZE;
1701         sp_export_value_set (base, "bmwidth", bmwidth);
1702     }
1704     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1705     sp_export_value_set (base, "xdpi", xdpi);
1707     sp_export_set_image_y (base);
1709     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1711     return;
1712 } // end of sp_export_bitmap_width_value_changed()
1714 /// Called when pixel height is changed
1715 static void
1716 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1718     float y0, y1, bmheight, xdpi;
1720     if (gtk_object_get_data (base, "update"))
1721         return;
1723     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1724            (base, "units"))) {
1725        return;
1726     }
1728     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1730     y0 = sp_export_value_get_px (base, "y0");
1731     y1 = sp_export_value_get_px (base, "y1");
1732     bmheight = sp_export_value_get (base, "bmheight");
1734     if (bmheight < SP_EXPORT_MIN_SIZE) {
1735         bmheight = SP_EXPORT_MIN_SIZE;
1736         sp_export_value_set (base, "bmheight", bmheight);
1737     }
1739     xdpi = bmheight * DPI_BASE / (y1 - y0);
1740     sp_export_value_set (base, "xdpi", xdpi);
1742     sp_export_set_image_x (base);
1744     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1746     return;
1747 } // end of sp_export_bitmap_width_value_changed()
1749 /**
1750     \brief  A function to adjust the bitmap width when the xdpi value changes
1751     \param  adj  The adjustment that was changed
1752     \param  base The export dialog itself
1754     The first thing this function checks is to see if we are doing an
1755     update.  If we are, this function just returns because there is another
1756     instance of it that will handle everything for us.  If there is a
1757     units change, we also assume that everyone is being updated appropriately
1758     and there is nothing for us to do.
1760     If we're the highest level function, we set the update flag, and
1761     continue on our way.
1763     All of the values are grabbed using the \c sp_export_value_get functions
1764     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1765     xdpi value is saved in the preferences for the next time the dialog
1766     is opened.  (does the selection dpi need to be set here?)
1768     A check is done to to ensure that we aren't outputing an invalid width,
1769     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1770     changed to make it valid.
1772     After all of this the bitmap width is changed.
1774     We also change the ydpi.  This is a temporary hack as these can not
1775     currently be independent.  This is likely to change in the future.
1776 */
1777 void
1778 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1780     float x0, x1, xdpi, bmwidth;
1782     if (gtk_object_get_data (base, "update"))
1783         return;
1785     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1786            (base, "units"))) {
1787        return;
1788     }
1790     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1792     x0 = sp_export_value_get_px (base, "x0");
1793     x1 = sp_export_value_get_px (base, "x1");
1794     xdpi = sp_export_value_get (base, "xdpi");
1796     // remember xdpi setting
1797     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1798     prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1800     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1802     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1803         bmwidth = SP_EXPORT_MIN_SIZE;
1804         if (x1 != x0)
1805             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1806         else
1807             xdpi = DPI_BASE;
1808         sp_export_value_set (base, "xdpi", xdpi);
1809     }
1811     sp_export_value_set (base, "bmwidth", bmwidth);
1813     sp_export_set_image_y (base);
1815     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1817     return;
1818 } // end of sp_export_xdpi_value_changed()
1821 /**
1822     \brief  A function to change the area that is used for the exported
1823             bitmap.
1824     \param  base  This is the export dialog
1825     \param  x0    Horizontal upper left hand corner of the picture in points
1826     \param  y0    Vertical upper left hand corner of the picture in points
1827     \param  x1    Horizontal lower right hand corner of the picture in points
1828     \param  y1    Vertical lower right hand corner of the picture in points
1830     This function just calls \c sp_export_value_set_px for each of the
1831     parameters that is passed in.  This allows for setting them all in
1832     one convient area.
1834     Update is set to suspend all of the other test running while all the
1835     values are being set up.  This allows for a performance increase, but
1836     it also means that the wrong type won't be detected with only some of
1837     the values set.  After all the values are set everyone is told that
1838     there has been an update.
1839 */
1840 static void
1841 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1843     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1844     sp_export_value_set_px (base, "x1", x1);
1845     sp_export_value_set_px (base, "y1", y1);
1846     sp_export_value_set_px (base, "x0", x0);
1847     sp_export_value_set_px (base, "y0", y0);
1848     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1850     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1851     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1853     return;
1856 /**
1857     \brief  Sets the value of an adjustment
1858     \param  base  The export dialog
1859     \param  key   Which adjustment to set
1860     \param  val   What value to set it to
1862     This function finds the adjustment using the data stored in the
1863     export dialog.  After finding the adjustment it then sets
1864     the value of it.
1865 */
1866 static void
1867 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1869     GtkAdjustment *adj;
1871     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1873     gtk_adjustment_set_value (adj, val);
1876 /**
1877     \brief  A function to set a value using the units points
1878     \param  base  The export dialog
1879     \param  key   Which value should be set
1880     \param  val   What the value should be in points
1882     This function first gets the adjustment for the key that is passed
1883     in.  It then figures out what units are currently being used in the
1884     dialog.  After doing all of that, it then converts the incoming
1885     value and sets the adjustment.
1886 */
1887 static void
1888 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1890     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1892     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1894     return;
1897 /**
1898     \brief  Get the value of an adjustment in the export dialog
1899     \param  base  The export dialog
1900     \param  key   Which adjustment is being looked for
1901     \return The value in the specified adjustment
1903     This function gets the adjustment from the data field in the export
1904     dialog.  It then grabs the value from the adjustment.
1905 */
1906 static float
1907 sp_export_value_get ( GtkObject *base, const gchar *key )
1909     GtkAdjustment *adj;
1911     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1913     return adj->value;
1916 /**
1917     \brief  Grabs a value in the export dialog and converts the unit
1918             to points
1919     \param  base  The export dialog
1920     \param  key   Which value should be returned
1921     \return The value in the adjustment in points
1923     This function, at its most basic, is a call to \c sp_export_value_get
1924     to get the value of the adjustment.  It then finds the units that
1925     are being used by looking at the "units" attribute of the export
1926     dialog.  Using that it converts the returned value into points.
1927 */
1928 static float
1929 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1931     float value = sp_export_value_get(base, key);
1932     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1934     return sp_units_get_pixels (value, *unit);
1935 } // end of sp_export_value_get_px()
1937 /**
1938     \brief  This function is called when the filename is changed by
1939             anyone.  It resets the virgin bit.
1940     \param  object  Text entry box
1941     \param  data    The export dialog
1942     \return None
1944     This function gets called when the text area is modified.  It is
1945     looking for the case where the text area is modified from its
1946     original value.  In that case it sets the "filename-modified" bit
1947     to TRUE.  If the text dialog returns back to the original text, the
1948     bit gets reset.  This should stop simple mistakes.
1949 */
1950 static void
1951 sp_export_filename_modified (GtkObject * object, gpointer data)
1953     GtkWidget * text_entry = (GtkWidget *)object;
1954     GtkWidget * export_dialog = (GtkWidget *)data;
1956     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1957         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1958 //        printf("Modified: FALSE\n");
1959     } else {
1960         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1961 //        printf("Modified: TRUE\n");
1962     }
1964     return;
1965 } // end sp_export_filename_modified
1967 /*
1968   Local Variables:
1969   mode:c++
1970   c-file-style:"stroustrup"
1971   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1972   indent-tabs-mode:nil
1973   fill-column:99
1974   End:
1975 */
1976 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :