Code

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