Code

Switch selection bounds and center to use NR::Maybe, addressing most of the
[inkscape.git] / src / dialogs / export.cpp
1 #define __SP_EXPORT_C__
3 /** \file
4  * \brief  PNG export dialog
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   bulia byak <buliabyak@users.sf.net>
11  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
12  *
13  * Copyright (C) 1999-2007 Authors
14  * Copyright (C) 2001-2002 Ximian, Inc.
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/buttonbox.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/togglebutton.h>
29 #include <gtkmm/entry.h>
30 #include <gtkmm/image.h>
31 #include <gtkmm/stockid.h>
32 #include <gtkmm/stock.h>
34 #include <glibmm/i18n.h>
35 #include "helper/unit-menu.h"
36 #include "helper/units.h"
37 #include "unit-constants.h"
38 #include "helper/window.h"
39 #include "inkscape-private.h"
40 #include "document.h"
41 #include "desktop-handles.h"
42 #include "sp-item.h"
43 #include "selection.h"
44 #include "file.h"
45 #include "macros.h"
46 #include "sp-namedview.h"
47 #include "selection-chemistry.h"
49 #include "dialog-events.h"
50 #include "../prefs-utils.h"
51 #include "../verbs.h"
52 #include "../interface.h"
54 #include "extension/output.h"
55 #include "extension/db.h"
57 #include "io/sys.h"
59 #include "helper/png-write.h"
62 #define SP_EXPORT_MIN_SIZE 1.0
64 #define DPI_BASE PX_PER_IN
66 #define EXPORT_COORD_PRECISION 3    
68 #define MIN_ONSCREEN_DISTANCE 50
70 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
71 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
72 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
74 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
75                                                    GtkObject *base);
76                                              
77 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
78                                                    GtkObject *base);
79                                              
80 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
81                                                    GtkObject *base);
82                                                  
83 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
84                                                    GtkObject *base);
85                                                   
86 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
87                                                    GtkObject *base);
88                                                    
89 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
90                                                    GtkObject *base);
91                                                    
92 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
93                                                    GtkObject *base);
94                                            
95 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
96                                           Inkscape::Selection *selection, 
97                                           GtkObject *base);
98 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
99                                            Inkscape::Selection *selection, 
100                                            guint flags,
101                                            GtkObject *base );
103 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
104 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
105 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
106 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
107 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
109 static void sp_export_filename_modified (GtkObject * object, gpointer data);
110 static inline void sp_export_find_default_selection(GtkWidget * dlg);
111 static void sp_export_detect_size(GtkObject * base);
113 static const gchar *prefs_path = "dialogs.export";
115 // these all need to be reinitialized to their defaults during dialog_destroy
116 static GtkWidget *dlg = NULL;
117 static win_data wd;
118 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
119 static gchar * original_name = NULL;
120 static gchar * doc_export_name = NULL;
121 static bool was_empty = TRUE;
123 /** What type of button is being pressed */
124 enum selection_type {
125     SELECTION_PAGE = 0,  /**< Export the whole page */
126     SELECTION_DRAWING,   /**< Export everything drawn on the page */
127     SELECTION_SELECTION, /**< Export everything that is selected */
128     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
129     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
130 };
132 /** A list of strings that is used both in the preferences, and in the
133     data fields to describe the various values of \c selection_type. */
134 static const char * selection_names[SELECTION_NUMBER_OF] = {
135     "page", "drawing", "selection", "custom"};
137 /** The names on the buttons for the various selection types. */
138 static const char * selection_labels[SELECTION_NUMBER_OF] = {
139     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
141 static void
142 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
144     sp_signal_disconnect_by_data (INKSCAPE, dlg);
146     wd.win = dlg = NULL;
147     wd.stop = 0;
148     x = -1000; y = -1000; w = 0; h = 0;
149     g_free(original_name);
150     original_name = NULL;
151     g_free(doc_export_name);
152     doc_export_name = NULL;
153     was_empty = TRUE;
155     return;
156 } // end of sp_export_dialog_destroy()
158 /// Called when dialog is closed or inkscape is shut down.
159 static bool
160 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
163     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
164     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
166     if (x<0) x=0;
167     if (y<0) y=0;
169     prefs_set_int_attribute (prefs_path, "x", x);
170     prefs_set_int_attribute (prefs_path, "y", y);
171     prefs_set_int_attribute (prefs_path, "w", w);
172     prefs_set_int_attribute (prefs_path, "h", h);
174     return FALSE; // which means, go ahead and destroy it
176 } // end of sp_export_dialog_delete()
178 /**
179     \brief  Creates a new spin button for the export dialog
180     \param  key  The name of the spin button
181     \param  val  A default value for the spin button
182     \param  min  Minimum value for the spin button
183     \param  max  Maximum value for the spin button
184     \param  step The step size for the spin button
185     \param  page Size of the page increment
186     \param  us   Unit selector that effects this spin button
187     \param  t    Table to put the spin button in
188     \param  x    X location in the table \c t to start with
189     \param  y    Y location in the table \c t to start with
190     \param  ll   Text to put on the left side of the spin button (optional)
191     \param  lr   Text to put on the right side of the spin button (optional)
192     \param  digits  Number of digits to display after the decimal
193     \param  sensitive  Whether the spin button is sensitive or not
194     \param  cb   Callback for when this spin button is changed (optional)
195     \param  dlg  Export dialog the spin button is being placed in
197 */
198 static void
199 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
200                            float step, float page, GtkWidget *us,
201                            GtkWidget *t, int x, int y,
202                            const gchar *ll, const gchar *lr,
203                            int digits, unsigned int sensitive,
204                            GCallback cb, GtkWidget *dlg )
206     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
207     gtk_object_set_data (a, "key", key);
208     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
210     if (us) {
211         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
212                                           GTK_ADJUSTMENT (a) );
213     }
215     int pos = 0;
217     GtkWidget *l = NULL;
219     if (ll) {
221         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
222         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
223         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
224                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
225         gtk_widget_set_sensitive (l, sensitive);
226         pos += 1;
228     }
230     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
231     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
232                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
233     gtk_widget_set_size_request (sb, 80, -1);
234     gtk_widget_set_sensitive (sb, sensitive);
235     pos += 1;
237     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
239     if (lr) {
241         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
242         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
243         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
244                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
245         gtk_widget_set_sensitive (l, sensitive);
246         pos += 1;
248         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
249     }
251     if (cb)
252         gtk_signal_connect (a, "value_changed", cb, dlg);
254     return;
255 } // end of sp_export_spinbutton_new()
258 static Gtk::VBox *
259 sp_export_dialog_area_box (GtkWidget * dlg)
261     Gtk::VBox* vb = new Gtk::VBox(false, 3);
263     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
264     lbl->set_use_markup(true);
265     vb->pack_start(*lbl);
267     /* Units box */
268     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
269     /* gets added to the vbox later, but the unit selector is needed
270        earlier than that */
272     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
273     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
274     if (desktop)
275         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
276     unitbox->pack_end(*us, false, false, 0);
277     Gtk::Label* l = new Gtk::Label(_("Units:"));
278     unitbox->pack_end(*l, false, false, 3);
279     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
281     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
283     Gtk::ToggleButton* b;
284     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
285         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
286         b->set_data("key", GINT_TO_POINTER(i));
287         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
288         togglebox->pack_start(*b, false, true, 0);
289         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
290                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
291     }
293     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
294                        G_CALLBACK (sp_export_selection_changed), dlg );
295     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
296                        G_CALLBACK (sp_export_selection_modified), dlg );
297     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
298                        G_CALLBACK (sp_export_selection_changed), dlg );
299     
300     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
301     t->set_row_spacings (4);
302     t->set_col_spacings (4);
304     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
305                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
306                                G_CALLBACK ( sp_export_area_x_value_changed), 
307                                dlg );
309     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
310                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
311                                G_CALLBACK (sp_export_area_x_value_changed), 
312                                dlg );
314     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
315                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
316                                G_CALLBACK 
317                                    (sp_export_area_width_value_changed), 
318                                dlg );
320     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
321                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
322                                G_CALLBACK (sp_export_area_y_value_changed), 
323                                dlg );
325     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
326                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
327                                G_CALLBACK (sp_export_area_y_value_changed), 
328                                dlg );
330     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
331                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
332                                G_CALLBACK (sp_export_area_height_value_changed), 
333                                dlg );
335     vb->pack_start(*togglebox, false, false, 3);
336     vb->pack_start(*t, false, false, 0);
337     vb->pack_start(*unitbox, false, false, 0);
339     return vb;
340 } // end of sp_export_dialog_area_box
343 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
345     if (id == NULL) /* This should never happen */
346         id = "bitmap";
348     gchar * directory = NULL;
350     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
351         // std::cout << "Directory from dialog" << std::endl;
352         directory = g_dirname(file_entry_text);
353     }
355     if (directory == NULL) {
356         /* Grab document directory */
357         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
358             // std::cout << "Directory from document" << std::endl;
359             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
360         }
361     }
363     if (directory == NULL) {
364         // std::cout << "Home Directory" << std::endl;
365         directory = homedir_path(NULL);
366     }
368     gchar * id_ext = g_strconcat(id, ".png", NULL);
369     gchar *filename = g_build_filename(directory, id_ext, NULL);
370     g_free(directory);
371     g_free(id_ext);
372     return filename;
375 static void
376 batch_export_clicked (GtkWidget *widget, GtkObject *base)
378     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
379     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
380         vb_singleexport->set_sensitive(false);
381     } else {
382         vb_singleexport->set_sensitive(true);
383     }
386 void
387 sp_export_dialog (void)
389     if (!dlg) {
391         gchar title[500];
392         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
394         dlg = sp_window_new (title, TRUE);
396         if (x == -1000 || y == -1000) {
397             x = prefs_get_int_attribute (prefs_path, "x", 0);
398             y = prefs_get_int_attribute (prefs_path, "y", 0);
399         }
401         if (w ==0 || h == 0) {
402             w = prefs_get_int_attribute (prefs_path, "w", 0);
403             h = prefs_get_int_attribute (prefs_path, "h", 0);
404         }
406 //        if (x<0) x=0;
407 //        if (y<0) y=0;
409         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
410         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
411             gtk_window_move ((GtkWindow *) dlg, x, y);
412         else
413             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
414         sp_transientize (dlg);
415         wd.win = dlg;
416         wd.stop = 0;
418         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
419                              G_CALLBACK (sp_transientize_callback), &wd);
421         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
422                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
424         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
425                              G_CALLBACK (sp_export_dialog_destroy), dlg);
427         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
428                              G_CALLBACK (sp_export_dialog_delete), dlg);
430         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
431                              G_CALLBACK (sp_export_dialog_delete), dlg);
433         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
434                              G_CALLBACK (sp_dialog_hide), dlg);
436         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
437                              G_CALLBACK (sp_dialog_unhide), dlg);
439         GtkTooltips *tt = gtk_tooltips_new();
441         Gtk::VBox *vb = new Gtk::VBox(false, 3);
442         vb->set_border_width(3);
443         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
445         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
446         vb_singleexport->set_border_width(0);
447         vb->pack_start(*vb_singleexport);
448         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
450         /* Export area frame */
451         {
452             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
453             area_box->set_border_width(3);
454             vb_singleexport->pack_start(*area_box, false, false, 0);
455         }
457         /* Bitmap size frame */
458         {
459             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
460             size_box->set_border_width(3);
462             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
463             lbl->set_use_markup(true);
464             size_box->pack_start(*lbl, false, false, 0);
465             const int rows = 2;
466             const int cols = 5;
467             const bool homogeneous = false;
468             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
469             t->set_row_spacings (4);
470             t->set_col_spacings (4);
471             size_box->pack_start(*t);
473             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
474                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
475                                        _("_Width:"), _("pixels at"), 0, 1,
476                                        G_CALLBACK 
477                                        (sp_export_bitmap_width_value_changed), 
478                                        dlg );
480             sp_export_spinbutton_new ( "xdpi", 
481                                        prefs_get_double_attribute 
482                                        ( "dialogs.export.defaultxdpi", 
483                                          "value", DPI_BASE), 
484                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
485                                        NULL, _("dp_i"), 2, 1,
486                                        G_CALLBACK (sp_export_xdpi_value_changed), 
487                                        dlg );
489             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
490                                        NULL, GTK_WIDGET(t->gobj()), 0, 1, 
491                                        _("Height:"), _("pixels at"), 0, 1, 
492                                        G_CALLBACK
493                                        (sp_export_bitmap_height_value_changed), 
494                                        dlg );
496             /** \todo
497              * Needs fixing: there's no way to set ydpi currently, so we use  
498              *       the defaultxdpi value here, too...
499              */
500             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
501                                        ( "dialogs.export.defaultxdpi", 
502                                          "value", DPI_BASE), 
503                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
504                                        NULL, _("dpi"), 2, 0, NULL, dlg );
506             vb_singleexport->pack_start(*size_box);
507         }
509         /* File entry */
510         {
511             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
512             file_box->set_border_width(3);
514             // true = has mnemonic
515             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
516             flabel->set_use_markup(true);
517             file_box->pack_start(*flabel, false, false, 0);
519             Gtk::Entry *fe = new Gtk::Entry();
521             /*
522              * set the default filename to be that of the current path + document
523              * with .png extension
524              *
525              * One thing to notice here is that this filename may get
526              * overwritten, but it won't happen here.  The filename gets
527              * written into the text field, but then the button to select
528              * the area gets set.  In that code the filename can be changed
529              * if there are some with presidence in the document.  So, while
530              * this code sets the name first, it may not be the one users
531              * really see.
532              */
533             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
534             {
535                 gchar *name;
536                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
537                 const gchar *uri = SP_DOCUMENT_URI (doc);
538                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
539                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
540                 Inkscape::Extension::Output * oextension = NULL;
542                 if (text_extension != NULL) {
543                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
544                 }
546                 if (oextension != NULL) {
547                     gchar * old_extension = oextension->get_extension();
548                     if (g_str_has_suffix(uri, old_extension)) {
549                         gchar * uri_copy;
550                         gchar * extension_point;
551                         gchar * final_name;
553                         uri_copy = g_strdup(uri);
554                         extension_point = g_strrstr(uri_copy, old_extension);
555                         extension_point[0] = '\0';
557                         final_name = g_strconcat(uri_copy, ".png", NULL);
558                         fe->set_text(final_name);
560                         g_free(final_name);
561                         g_free(uri_copy);
562                     }
563                 } else {
564                     name = g_strconcat(uri, ".png", NULL);
565                     fe->set_text(name);
566                     g_free(name);
567                 }
569                 doc_export_name = g_strdup(fe->get_text().c_str());
570             }
571             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
572                                G_CALLBACK (sp_export_filename_modified), dlg);
574             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
576             {
577                 // true = has mnemonic
578                 Gtk::Button *b = new Gtk::Button();
580                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
581                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
582                         Gtk::ICON_SIZE_BUTTON);
583                 pixlabel->pack_start(*im);
585                 Gtk::Label *l = new Gtk::Label();
586                 l->set_markup_with_mnemonic(_("_Browse..."));
587                 pixlabel->pack_start(*l);
589                 b->add(*pixlabel);
591                 hb->pack_end (*b, false, false, 4);
592                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
593                                    G_CALLBACK (sp_export_browse_clicked), NULL );
594             }
596             hb->pack_start (*fe, true, true, 0);
597             file_box->add(*hb);
598             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
599             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
600             original_name = g_strdup(fe->get_text().c_str());
601             // pressing enter in the filename field is the same as clicking export:
602             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
603                                G_CALLBACK (sp_export_export_clicked), dlg );
604             // focus is in the filename initially:
605             fe->grab_focus();
607             // mnemonic in frame label moves focus to filename:
608             flabel->set_mnemonic_widget(*fe);
610             vb_singleexport->pack_start(*file_box);
611         }
613         {
614             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
615             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
616             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
617             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
618             batch_box->pack_start(*Glib::wrap(be), false, false);
619             gtk_tooltips_set_tip(tt, be, _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"), NULL);
620             batch_box->show_all();
621             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
622             vb->pack_start(*batch_box);
623         }
625         {
626             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
627             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
628             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
629             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
630             hide_box->pack_start(*Glib::wrap(he), false, false);
631             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
632             hide_box->show_all();
633             vb->pack_start(*hide_box);
634         }
636         /* Buttons */
637         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
638         bb->set_border_width(3);
640         {
641             Gtk::Button *b = new Gtk::Button();
642             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
643             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
644                     Gtk::ICON_SIZE_BUTTON);
645             image_label->pack_start(*im);
647             Gtk::Label *l = new Gtk::Label();
648             l->set_markup_with_mnemonic(_("_Export"));
649             image_label->pack_start(*l);
651             b->add(*image_label);
652             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
653             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
654                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
655             bb->pack_end(*b, false, false, 0);
656         }
658         vb->pack_end(*bb, false, false, 0);
659         vb->show_all();
661     } // end of if (!dlg)
663     sp_export_find_default_selection(dlg);
665     gtk_window_present ((GtkWindow *) dlg);
667     return;
668 } // end of sp_export_dialog()
670 static void
671 sp_export_update_checkbuttons (GtkObject *base)
673     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
674     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
675     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
676     if (num >= 2) {
677         gtk_widget_set_sensitive (be, true);
678         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (_("Batch export %d selected objects"), num));
679     } else {
680         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
681         gtk_widget_set_sensitive (be, FALSE);
682     }
683     if (num > 0) {
684         gtk_widget_set_sensitive (he, true);
685     } else {
686         gtk_widget_set_sensitive (he, false);
687     }
690 static inline void
691 sp_export_find_default_selection(GtkWidget * dlg)
693     selection_type key = SELECTION_NUMBER_OF;
695     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
696         key = SELECTION_SELECTION;
697     }
699     /* Try using the preferences */
700     if (key == SELECTION_NUMBER_OF) {
701         const gchar *what = NULL;
702         int i = SELECTION_NUMBER_OF;
704         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
706         if (what != NULL) {
707             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
708                 if (!strcmp (what, selection_names[i])) {
709                     break;
710                 }
711             }
712         }
714         key = (selection_type)i;
715     }
717     if (key == SELECTION_NUMBER_OF) {
718         key = SELECTION_SELECTION;
719     }
721     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
722                                                        selection_names[key]);
723     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
725     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
729 /**
730  * \brief  If selection changed or a different document activated, we must 
731  * recalculate any chosen areas
732  *
733  */
734 static void
735 sp_export_selection_changed ( Inkscape::Application *inkscape, 
736                               Inkscape::Selection *selection, 
737                               GtkObject *base )
739     selection_type current_key;
740     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
742     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
743             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
744             was_empty) {
745         gtk_toggle_button_set_active
746             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
747               TRUE );
748     }
749     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
751     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
753     if (inkscape &&
754             SP_IS_INKSCAPE (inkscape) &&
755             selection &&
756             SELECTION_CUSTOM != current_key) {
757         GtkToggleButton * button;
758         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
759         sp_export_area_toggled(button, base);
760     } 
762     sp_export_update_checkbuttons (base);
763
765 static void
766 sp_export_selection_modified ( Inkscape::Application *inkscape, 
767                                Inkscape::Selection *selection, 
768                                guint flags,
769                                GtkObject *base )
771     selection_type current_key;
772     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
774     switch (current_key) {
775         case SELECTION_DRAWING:
776             if ( SP_ACTIVE_DESKTOP ) {
777                 SPDocument *doc;
778                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
779                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
780                 if (bbox) {
781                     sp_export_set_area (base, bbox->min()[NR::X],
782                                               bbox->min()[NR::Y],
783                                               bbox->max()[NR::X],
784                                               bbox->max()[NR::Y]);
785                 }
786             }
787             break;
788         case SELECTION_SELECTION:
789             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790                 NRRect bbox;
791                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
792                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
793             }
794             break;
795         default:
796             /* Do nothing for page or for custom */
797             break;
798     }
800     return;
803 /// Called when one of the selection buttons was toggled.
804 static void
805 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
807     if (gtk_object_get_data (base, "update"))
808         return;
810     selection_type key, old_key;
811     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
812     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
814     /* Ignore all "turned off" events unless we're the only active button */
815     if (!gtk_toggle_button_get_active (tb) ) {
817         /* Don't let the current selection be deactived - but rerun the
818            activate to allow the user to renew the values */
819         if (key == old_key) {
820             gtk_toggle_button_set_active ( tb, TRUE );
821         }
823         return;
824     }
826     /* Turn off the currently active button unless it's us */
827     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
829     if (old_key != key) {
830         gtk_toggle_button_set_active
831             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
832               FALSE );
833     }
835     if ( SP_ACTIVE_DESKTOP )
836     {
837         SPDocument *doc;
838         NR::Maybe<NR::Rect> bbox;
839         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
841         /* Notice how the switch is used to 'fall through' here to get
842            various backups.  If you modify this without noticing you'll
843            probabaly screw something up. */
844         switch (key) {
845             case SELECTION_SELECTION:
846                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
847                 {
848                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
849                     /* Only if there is a selection that we can set
850                        do we break, otherwise we fall through to the
851                        drawing */
852                     // std::cout << "Using selection: SELECTION" << std::endl;
853                     key = SELECTION_SELECTION;
854                     break;
855                 }
856             case SELECTION_DRAWING:
857                 /** \todo 
858                  * This returns wrong values if the document has a viewBox.
859                  */
860                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
861                 /* If the drawing is valid, then we'll use it and break
862                    otherwise we drop through to the page settings */
863                 if (bbox) {
864                     // std::cout << "Using selection: DRAWING" << std::endl;
865                     key = SELECTION_DRAWING;
866                     break;
867                 }
868             case SELECTION_PAGE:
869                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
870                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
871                                 );
873                 // std::cout << "Using selection: PAGE" << std::endl;
874                 key = SELECTION_PAGE;
875                 break;
876             case SELECTION_CUSTOM:
877             default:
878                 break;
879         } // switch
880         
881         // remember area setting
882         prefs_set_string_attribute ( "dialogs.export.exportarea", 
883                                      "value", selection_names[key]);
885         if ( key != SELECTION_CUSTOM && bbox ) {
886             sp_export_set_area (base, bbox->min()[NR::X],
887                                       bbox->min()[NR::Y],
888                                       bbox->max()[NR::X],
889                                       bbox->max()[NR::Y]);
890         }
891     
892     } // end of if ( SP_ACTIVE_DESKTOP )
895     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
896         GtkWidget * file_entry;
897         const gchar * filename = NULL;
898         float xdpi = 0.0, ydpi = 0.0;
900         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
902         switch (key) {
903             case SELECTION_PAGE:
904             case SELECTION_DRAWING: {
905                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
906                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
908                 if (filename == NULL) {
909                     if (doc_export_name != NULL) {
910                         filename = g_strdup(doc_export_name);
911                     } else {
912                         filename = g_strdup("");
913                     }
914                 }
915                 break;
916             }
917             case SELECTION_SELECTION:
918                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
920                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
922                     /* If we still don't have a filename -- let's build
923                        one that's nice */
924                     if (filename == NULL) {
925                         const gchar * id = NULL;
926                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
927                         for(; reprlst != NULL; reprlst = reprlst->next) {
928                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
929                             if (repr->attribute("id")) {
930                                 id = repr->attribute("id");
931                                 break;
932                             }
933                         }
935                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
936                     }
937                 }
938                 break;
939             case SELECTION_CUSTOM:
940             default:
941                 break;
942         }
944         if (filename != NULL) {
945             g_free(original_name);
946             original_name = g_strdup(filename);
947             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
948         }
950         if (xdpi != 0.0) {
951             sp_export_value_set(base, "xdpi", xdpi);
952         }
954         /* These can't be separate, and setting x sets y, so for
955            now setting this is disabled.  Hopefully it won't be in
956            the future */
957         if (FALSE && ydpi != 0.0) {
958             sp_export_value_set(base, "ydpi", ydpi);
959         }
960     }
962     return;
963 } // end of sp_export_area_toggled()
965 /// Called when dialog is deleted
966 static gint
967 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
969     g_object_set_data (base, "cancel", (gpointer) 1);
970     return TRUE;
971 } // end of sp_export_progress_delete()
973 /// Called when progress is cancelled
974 static void
975 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
977     g_object_set_data (base, "cancel", (gpointer) 1);
978 } // end of sp_export_progress_cancel()
980 /// Called for every progress iteration
981 static unsigned int
982 sp_export_progress_callback (float value, void *data)
984     GtkWidget *prg;
985     int evtcount;
987     if (g_object_get_data ((GObject *) data, "cancel"))
988         return FALSE;
990     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
991     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
993     evtcount = 0;
994     while ((evtcount < 16) && gdk_events_pending ()) {
995             gtk_main_iteration_do (FALSE);
996             evtcount += 1;
997     }
999     gtk_main_iteration_do (FALSE);
1001     return TRUE;
1003 } // end of sp_export_progress_callback()
1005 GtkWidget *
1006 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1007     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1008     
1009     dlg = gtk_dialog_new ();
1010     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1011     prg = gtk_progress_bar_new ();
1012     sp_transientize (dlg);
1013     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1014     g_object_set_data ((GObject *) base, "progress", prg);
1016     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1018     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1019                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1020     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1021                         prg, FALSE, FALSE, 4 );
1022     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1023                                   GTK_STOCK_CANCEL, 
1024                                   GTK_RESPONSE_CANCEL );
1025                                   
1026     g_signal_connect ( (GObject *) dlg, "delete_event", 
1027                        (GCallback) sp_export_progress_delete, base);
1028     g_signal_connect ( (GObject *) btn, "clicked", 
1029                        (GCallback) sp_export_progress_cancel, base);
1030     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1031     gtk_widget_show_all (dlg);
1033     return dlg;
1036 /// Called when export button is clicked
1037 static void
1038 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1040     if (!SP_ACTIVE_DESKTOP) return;
1042     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1044     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1045     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1046     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1047     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1048         // Batch export of selected objects
1050         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1051         gint n = 0;
1053         if (num < 1) 
1054             return;
1056         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1057         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1058         g_free (progress_text);
1060         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1061              i != NULL;
1062              i = i->next) {
1063             SPItem *item = (SPItem *) i->data;
1064             // retrieve export filename hint
1065             const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1066             if (!fn) {
1067                 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1068             }
1070             // retrieve export dpi hints
1071             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1072             gdouble dpi = 0.0;
1073             if (dpi_hint) {
1074                 dpi = atof(dpi_hint);
1075             }
1076             if (dpi == 0.0) {
1077                 dpi = DPI_BASE;
1078             }
1080             NRRect area;
1081             sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1083             gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1084             gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1086             if (width > 1 && height > 1) {
1087                 /* Do export */
1088                 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn, 
1089                                          area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, 
1090                                          nv->pagecolor, 
1091                                          NULL, NULL, TRUE,  // overwrite without asking 
1092                                          hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1093                         )) {
1094                     gchar * error;
1095                     gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1096                     error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1097                     sp_ui_error_dialog(error);
1098                     g_free(safeFile);
1099                     g_free(error);
1100                 }
1101             }
1102             n++;
1103             sp_export_progress_callback((float)n/num, base);
1104         }
1106         gtk_widget_destroy (prog_dlg);
1107         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1109     } else {
1111     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1112     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1114     float const x0 = sp_export_value_get_px(base, "x0");
1115     float const y0 = sp_export_value_get_px(base, "y0");
1116     float const x1 = sp_export_value_get_px(base, "x1");
1117     float const y1 = sp_export_value_get_px(base, "y1");
1118     float const xdpi = sp_export_value_get(base, "xdpi");
1119     float const ydpi = sp_export_value_get(base, "ydpi");
1120     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1121     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1123     if (filename == NULL || *filename == '\0') {
1124         sp_ui_error_dialog(_("You have to enter a filename"));
1125         return;
1126     }
1128     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1129         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1130         return;
1131     }
1133     gchar *dirname = g_dirname(filename);
1134     if ( dirname == NULL
1135          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1136     {
1137         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1138         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1139                                        safeDir);
1140         sp_ui_error_dialog(error);
1141         g_free(safeDir);
1142         g_free(error);
1143         g_free(dirname);
1144         return;
1145     }
1146     g_free(dirname);
1148     gchar *fn = g_path_get_basename (filename);
1149     gchar *progress_text = g_strdup_printf (_("Exporting %s (%d x %d)"), fn, width, height);
1150     g_free (fn);
1151     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1152     g_free (progress_text);
1153     
1154     /* Do export */
1155     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1156                              x0, y0, x1, y1, width, height, xdpi, ydpi, 
1157                              nv->pagecolor, 
1158                              sp_export_progress_callback, base, FALSE,
1159                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1160             )) {
1161         gchar * error;
1162         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1163         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1164         sp_ui_error_dialog(error);
1165         g_free(safeFile);
1166         g_free(error);
1167     }
1169     /* Reset the filename so that it can be changed again by changing
1170        selections and all that */
1171     g_free(original_name);
1172     original_name = g_strdup(filename);
1173     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1175     gtk_widget_destroy (prog_dlg);
1176     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1178     /* Setup the values in the document */
1179     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1180         case SELECTION_PAGE:
1181         case SELECTION_DRAWING: {
1182             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1183             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1184             bool modified = false;
1185             const gchar * temp_string;
1187             bool saved = sp_document_get_undo_sensitive(doc);
1188             sp_document_set_undo_sensitive(doc, false);
1190             temp_string = repr->attribute("inkscape:export-filename");
1191             if (temp_string == NULL || strcmp(temp_string, filename)) {
1192                 repr->setAttribute("inkscape:export-filename", filename);
1193                 modified = true;
1194             }
1195             temp_string = repr->attribute("inkscape:export-xdpi");
1196             if (temp_string == NULL || xdpi != atof(temp_string)) {
1197                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1198                 modified = true;
1199             }
1200             temp_string = repr->attribute("inkscape:export-ydpi");
1201             if (temp_string == NULL || xdpi != atof(temp_string)) {
1202                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1203                 modified = true;
1204             }
1206             if (modified)
1207                 repr->setAttribute("sodipodi:modified", "TRUE");
1208             sp_document_set_undo_sensitive(doc, saved);
1209             break;
1210         }
1211         case SELECTION_SELECTION: {
1212             const GSList * reprlst;
1213             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1214             bool modified = false;
1216             bool saved = sp_document_get_undo_sensitive(doc);
1217             sp_document_set_undo_sensitive(doc, false);
1218             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1220             for(; reprlst != NULL; reprlst = reprlst->next) {
1221                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1222                 const gchar * temp_string;
1224                 if (repr->attribute("id") == NULL ||
1225                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1226                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1227                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1228                     temp_string = repr->attribute("inkscape:export-filename");
1229                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1230                         repr->setAttribute("inkscape:export-filename", filename);
1231                         modified = true;
1232                     }
1233                 }
1234                 temp_string = repr->attribute("inkscape:export-xdpi");
1235                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1236                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1237                     modified = true;
1238                 }
1239                 temp_string = repr->attribute("inkscape:export-ydpi");
1240                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1241                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1242                     modified = true;
1243                 }
1244             }
1246             if (modified) {
1247                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1248                 repr->setAttribute("sodipodi:modified", "TRUE");
1249             }
1251             sp_document_set_undo_sensitive(doc, saved);
1252             break;
1253         }
1254         default:
1255             break;
1256     }
1258     }
1260 } // end of sp_export_export_clicked()
1263 // FIXME: Some lib function should be available to do this ...
1264 static gchar *
1265 filename_add_extension (const gchar *filename, const gchar *extension)
1267   gchar *dot;
1269   dot = strrchr (filename, '.');
1270   if ( !dot )
1271     return g_strconcat (filename, ".", extension, NULL);
1272   {
1273     if (dot[1] == '\0')
1274       return g_strconcat (filename, extension, NULL);
1275     else
1276     {
1277       if (g_strcasecmp (dot + 1, extension) == 0)
1278         return g_strdup (filename);
1279       else
1280       {
1281         return g_strconcat (filename, ".", extension, NULL);
1282       }
1283     }
1284   }
1287 /// Called when Browse button is clicked
1288 static void
1289 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1291     GtkWidget *fs, *fe;
1292     const gchar *filename;
1294     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1295                                       NULL,
1296                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1297                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1298                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1299                                       NULL );
1301 #ifdef WITH_GNOME_VFS
1302     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER (fs), false);
1303 #endif
1305     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1307     sp_transientize (fs);
1309     gtk_window_set_modal(GTK_WINDOW (fs), true);
1311     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1313     if (*filename == '\0') {
1314         filename = homedir_path(NULL);
1315     }
1317     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1319     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1320     {
1321         gchar *file;
1323         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1324         // make sure that .png is the extension of the file:
1325         gchar * file_ext = filename_add_extension(file, "png");
1327         gchar * utf8file = g_filename_to_utf8( file_ext, -1, NULL, NULL, NULL );
1328         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1330         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1332         g_free(file_ext);
1333         g_free(utf8file);
1334         g_free(file);
1335     }
1337     gtk_widget_destroy (fs);
1339     return;
1340 } // end of sp_export_browse_clicked()
1342 // TODO: Move this to nr-rect-fns.h.
1343 static bool
1344 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1345
1346     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1347     return (
1348         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1349         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1350         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1351         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1352         );
1355 /**
1356     \brief  This function is used to detect the current selection setting
1357             based on the values in the x0, y0, x1 and y0 fields.
1358     \param  base  The export dialog itself
1360     One of the most confusing parts of this function is why the array
1361     is built at the beginning.  What needs to happen here is that we
1362     should always check the current selection to see if it is the valid
1363     one.  While this is a performance improvement it is also a usability
1364     one during the cases where things like selections and drawings match
1365     size.  This way buttons change less 'randomly' (atleast in the eyes
1366     of the user).  To do this an array is built where the current selection
1367     type is placed first, and then the others in an order from smallest
1368     to largest (this can be configured by reshuffling \c test_order).
1370     All of the values in this function are rounded to two decimal places
1371     because that is what is shown to the user.  While everything is kept
1372     more accurate than that, the user can't control more acurrate than
1373     that, so for this to work for them - it needs to check on that level
1374     of accuracy.
1376     \todo finish writing this up
1377 */
1378 static void
1379 sp_export_detect_size(GtkObject * base) {
1380     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1381     selection_type this_test[SELECTION_NUMBER_OF + 1];
1382     selection_type key = SELECTION_NUMBER_OF;
1384     NR::Point x(sp_export_value_get_px (base, "x0"),
1385                 sp_export_value_get_px (base, "y0"));
1386     NR::Point y(sp_export_value_get_px (base, "x1"),
1387                 sp_export_value_get_px (base, "y1"));
1388     NR::Rect current_bbox(x, y);
1389     //std::cout << "Current " << current_bbox;
1391     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1392     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1393         this_test[i + 1] = test_order[i];
1394     }
1396     for (int i = 0;
1397             i < SELECTION_NUMBER_OF + 1 &&
1398                 key == SELECTION_NUMBER_OF &&
1399                 SP_ACTIVE_DESKTOP != NULL;
1400             i++) {
1401         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1402         switch (this_test[i]) {
1403             case SELECTION_SELECTION:
1404                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1405                     NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1407                     //std::cout << "Selection " << bbox;
1408                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1409                         key = SELECTION_SELECTION;
1410                     }
1411                 }
1412                 break;
1413             case SELECTION_DRAWING: {
1414                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1416                 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1418                 // std::cout << "Drawing " << bbox2;
1419                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1420                     key = SELECTION_DRAWING;
1421                 }
1422                 break;
1423             }
1425             case SELECTION_PAGE: {
1426                 SPDocument *doc;
1428                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1430                 NR::Point x(0.0, 0.0);
1431                 NR::Point y(sp_document_width(doc),
1432                             sp_document_height(doc));
1433                 NR::Rect bbox(x, y);
1435                 // std::cout << "Page " << bbox;
1436                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1437                     key = SELECTION_PAGE;
1438                 }
1440                 break;
1441            }
1442         default:
1443            break;
1444         }
1445     }
1446     // std::cout << std::endl;
1448     if (key == SELECTION_NUMBER_OF) {
1449         key = SELECTION_CUSTOM;
1450     }
1452     /* We're now using a custom size, not a fixed one */
1453     /* printf("Detecting state: %s\n", selection_names[key]); */
1454     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1455     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1456     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1457     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1459     return;
1460 } /* sp_export_detect_size */
1462 /// Called when area x0 value is changed
1463 static void
1464 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1466     float x0, x1, xdpi, width;
1468     if (gtk_object_get_data (base, "update"))
1469         return;
1471     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1472             (base, "units")))
1473     {
1474         return;
1475     }
1477     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1479     x0 = sp_export_value_get_px (base, "x0");
1480     x1 = sp_export_value_get_px (base, "x1");
1481     xdpi = sp_export_value_get (base, "xdpi");
1483     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1485     if (width < SP_EXPORT_MIN_SIZE) {
1486         const gchar *key;
1487         width = SP_EXPORT_MIN_SIZE;
1488         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1490         if (!strcmp (key, "x0")) {
1491             x1 = x0 + width * DPI_BASE / xdpi;
1492             sp_export_value_set_px (base, "x1", x1);
1493         } else {
1494             x0 = x1 - width * DPI_BASE / xdpi;
1495             sp_export_value_set_px (base, "x0", x0);
1496         }
1497     }
1499     sp_export_value_set_px (base, "width", x1 - x0);
1500     sp_export_value_set (base, "bmwidth", width);
1502     sp_export_detect_size(base);
1504     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1506     return;
1507 } // end of sp_export_area_x_value_changed()
1509 /// Called when area y0 value is changed.
1510 static void
1511 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1513     float y0, y1, ydpi, height;
1515     if (gtk_object_get_data (base, "update"))
1516         return;
1518     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1519            (base, "units")))
1520     {
1521         return;
1522     }
1524     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1526     y0 = sp_export_value_get_px (base, "y0");
1527     y1 = sp_export_value_get_px (base, "y1");
1528     ydpi = sp_export_value_get (base, "ydpi");
1530     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1532     if (height < SP_EXPORT_MIN_SIZE) {
1533         const gchar *key;
1534         height = SP_EXPORT_MIN_SIZE;
1535         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1536         if (!strcmp (key, "y0")) {
1537             y1 = y0 + height * DPI_BASE / ydpi;
1538             sp_export_value_set_px (base, "y1", y1);
1539         } else {
1540             y0 = y1 - height * DPI_BASE / ydpi;
1541             sp_export_value_set_px (base, "y0", y0);
1542         }
1543     }
1545     sp_export_value_set_px (base, "height", y1 - y0);
1546     sp_export_value_set (base, "bmheight", height);
1548     sp_export_detect_size(base);
1550     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1552     return;
1553 } // end of sp_export_area_y_value_changed()
1555 /// Called when x1-x0 or area width is changed
1556 static void
1557 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1559     float x0, x1, xdpi, width, bmwidth;
1561     if (gtk_object_get_data (base, "update"))
1562         return;
1564     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1565            (base, "units"))) {
1566         return;
1567     }
1569     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1571     x0 = sp_export_value_get_px (base, "x0");
1572     x1 = sp_export_value_get_px (base, "x1");
1573     xdpi = sp_export_value_get (base, "xdpi");
1574     width = sp_export_value_get_px (base, "width");
1575     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1577     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1579         bmwidth = SP_EXPORT_MIN_SIZE;
1580         width = bmwidth * DPI_BASE / xdpi;
1581         sp_export_value_set_px (base, "width", width);
1582     }
1584     sp_export_value_set_px (base, "x1", x0 + width);
1585     sp_export_value_set (base, "bmwidth", bmwidth);
1587     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1589     return;
1590 } // end of sp_export_area_width_value_changed()
1592 /// Called when y1-y0 or area height is changed.
1593 static void
1594 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1597     float y0, y1, ydpi, height, bmheight;
1599     if (gtk_object_get_data (base, "update"))
1600         return;
1602     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1603            (base, "units"))) {
1604         return;
1605     }
1607     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1609     y0 = sp_export_value_get_px (base, "y0");
1610     y1 = sp_export_value_get_px (base, "y1");
1611     ydpi = sp_export_value_get (base, "ydpi");
1612     height = sp_export_value_get_px (base, "height");
1613     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1615     if (bmheight < SP_EXPORT_MIN_SIZE) {
1616         bmheight = SP_EXPORT_MIN_SIZE;
1617         height = bmheight * DPI_BASE / ydpi;
1618         sp_export_value_set_px (base, "height", height);
1619     }
1621     sp_export_value_set_px (base, "y1", y0 + height);
1622     sp_export_value_set (base, "bmheight", bmheight);
1624     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1626     return;
1627 } // end of sp_export_area_height_value_changed()
1629 /**
1630     \brief  A function to set the ydpi
1631     \param  base  The export dialog
1633     This function grabs all of the y values and then figures out the
1634     new bitmap size based on the changing dpi value.  The dpi value is
1635     gotten from the xdpi setting as these can not currently be independent.
1636 */
1637 static void
1638 sp_export_set_image_y (GtkObject *base)
1640     float y0, y1, xdpi;
1642     y0 = sp_export_value_get_px (base, "y0");
1643     y1 = sp_export_value_get_px (base, "y1");
1644     xdpi = sp_export_value_get (base, "xdpi");
1646     sp_export_value_set (base, "ydpi", xdpi);
1647     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1649     return;
1650 } // end of sp_export_set_image_y()
1652 /**
1653     \brief  A function to set the xdpi
1654     \param  base  The export dialog
1656     This function grabs all of the x values and then figures out the
1657     new bitmap size based on the changing dpi value.  The dpi value is
1658     gotten from the xdpi setting as these can not currently be independent.
1659 */
1660 static void
1661 sp_export_set_image_x (GtkObject *base)
1663     float x0, x1, xdpi;
1665     x0 = sp_export_value_get_px (base, "x0");
1666     x1 = sp_export_value_get_px (base, "x1");
1667     xdpi = sp_export_value_get (base, "xdpi");
1669     sp_export_value_set (base, "ydpi", xdpi);
1670     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1672     return;
1673 } // end of sp_export_set_image_x()
1675 /// Called when pixel width is changed
1676 static void
1677 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1679     float x0, x1, bmwidth, xdpi;
1681     if (gtk_object_get_data (base, "update"))
1682         return;
1684     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1685            (base, "units"))) {
1686        return;
1687     }
1689     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1691     x0 = sp_export_value_get_px (base, "x0");
1692     x1 = sp_export_value_get_px (base, "x1");
1693     bmwidth = sp_export_value_get (base, "bmwidth");
1695     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1696         bmwidth = SP_EXPORT_MIN_SIZE;
1697         sp_export_value_set (base, "bmwidth", bmwidth);
1698     }
1700     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1701     sp_export_value_set (base, "xdpi", xdpi);
1703     sp_export_set_image_y (base);
1705     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1707     return;
1708 } // end of sp_export_bitmap_width_value_changed()
1710 /// Called when pixel height is changed
1711 static void
1712 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1714     float y0, y1, bmheight, xdpi;
1716     if (gtk_object_get_data (base, "update"))
1717         return;
1719     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1720            (base, "units"))) {
1721        return;
1722     }
1724     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1726     y0 = sp_export_value_get_px (base, "y0");
1727     y1 = sp_export_value_get_px (base, "y1");
1728     bmheight = sp_export_value_get (base, "bmheight");
1730     if (bmheight < SP_EXPORT_MIN_SIZE) {
1731         bmheight = SP_EXPORT_MIN_SIZE;
1732         sp_export_value_set (base, "bmheight", bmheight);
1733     }
1735     xdpi = bmheight * DPI_BASE / (y1 - y0);
1736     sp_export_value_set (base, "xdpi", xdpi);
1738     sp_export_set_image_x (base);
1740     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1742     return;
1743 } // end of sp_export_bitmap_width_value_changed()
1745 /**
1746     \brief  A function to adjust the bitmap width when the xdpi value changes
1747     \param  adj  The adjustment that was changed
1748     \param  base The export dialog itself
1750     The first thing this function checks is to see if we are doing an
1751     update.  If we are, this function just returns because there is another
1752     instance of it that will handle everything for us.  If there is a
1753     units change, we also assume that everyone is being updated appropriately
1754     and there is nothing for us to do.
1756     If we're the highest level function, we set the update flag, and
1757     continue on our way.
1759     All of the values are grabbed using the \c sp_export_value_get functions
1760     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1761     xdpi value is saved in the preferences for the next time the dialog
1762     is opened.  (does the selection dpi need to be set here?)
1764     A check is done to to ensure that we aren't outputing an invalid width,
1765     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1766     changed to make it valid.
1768     After all of this the bitmap width is changed.
1770     We also change the ydpi.  This is a temporary hack as these can not
1771     currently be independent.  This is likely to change in the future.
1772 */
1773 void
1774 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1776     float x0, x1, xdpi, bmwidth;
1778     if (gtk_object_get_data (base, "update"))
1779         return;
1781     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1782            (base, "units"))) {
1783        return;
1784     }
1786     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1788     x0 = sp_export_value_get_px (base, "x0");
1789     x1 = sp_export_value_get_px (base, "x1");
1790     xdpi = sp_export_value_get (base, "xdpi");
1792     // remember xdpi setting
1793     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1795     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1797     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1798         bmwidth = SP_EXPORT_MIN_SIZE;
1799         if (x1 != x0)
1800             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1801         else
1802             xdpi = DPI_BASE;
1803         sp_export_value_set (base, "xdpi", xdpi);
1804     }
1806     sp_export_value_set (base, "bmwidth", bmwidth);
1808     sp_export_set_image_y (base);
1810     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1812     return;
1813 } // end of sp_export_xdpi_value_changed()
1816 /**
1817     \brief  A function to change the area that is used for the exported
1818             bitmap.
1819     \param  base  This is the export dialog
1820     \param  x0    Horizontal upper left hand corner of the picture in points
1821     \param  y0    Vertical upper left hand corner of the picture in points
1822     \param  x1    Horizontal lower right hand corner of the picture in points
1823     \param  y1    Vertical lower right hand corner of the picture in points
1825     This function just calls \c sp_export_value_set_px for each of the
1826     parameters that is passed in.  This allows for setting them all in
1827     one convient area.
1829     Update is set to suspend all of the other test running while all the
1830     values are being set up.  This allows for a performance increase, but
1831     it also means that the wrong type won't be detected with only some of
1832     the values set.  After all the values are set everyone is told that
1833     there has been an update.
1834 */
1835 static void
1836 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1838     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1839     sp_export_value_set_px (base, "x1", x1);
1840     sp_export_value_set_px (base, "y1", y1);
1841     sp_export_value_set_px (base, "x0", x0);
1842     sp_export_value_set_px (base, "y0", y0);
1843     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1845     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1846     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1848     return;
1851 /**
1852     \brief  Sets the value of an adjustment
1853     \param  base  The export dialog
1854     \param  key   Which adjustment to set
1855     \param  val   What value to set it to
1857     This function finds the adjustment using the data stored in the
1858     export dialog.  After finding the adjustment it then sets
1859     the value of it.
1860 */
1861 static void
1862 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1864     GtkAdjustment *adj;
1866     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1868     gtk_adjustment_set_value (adj, val);
1871 /**
1872     \brief  A function to set a value using the units points
1873     \param  base  The export dialog
1874     \param  key   Which value should be set
1875     \param  val   What the value should be in points
1877     This function first gets the adjustment for the key that is passed
1878     in.  It then figures out what units are currently being used in the
1879     dialog.  After doing all of that, it then converts the incoming
1880     value and sets the adjustment.
1881 */
1882 static void
1883 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1885     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1887     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1889     return;
1892 /**
1893     \brief  Get the value of an adjustment in the export dialog
1894     \param  base  The export dialog
1895     \param  key   Which adjustment is being looked for
1896     \return The value in the specified adjustment
1898     This function gets the adjustment from the data field in the export
1899     dialog.  It then grabs the value from the adjustment.
1900 */
1901 static float
1902 sp_export_value_get ( GtkObject *base, const gchar *key )
1904     GtkAdjustment *adj;
1906     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1908     return adj->value;
1911 /**
1912     \brief  Grabs a value in the export dialog and converts the unit
1913             to points
1914     \param  base  The export dialog
1915     \param  key   Which value should be returned
1916     \return The value in the adjustment in points
1918     This function, at its most basic, is a call to \c sp_export_value_get
1919     to get the value of the adjustment.  It then finds the units that
1920     are being used by looking at the "units" attribute of the export
1921     dialog.  Using that it converts the returned value into points.
1922 */
1923 static float
1924 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1926     float value = sp_export_value_get(base, key);
1927     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1929     return sp_units_get_pixels (value, *unit);
1930 } // end of sp_export_value_get_px()
1932 /**
1933     \brief  This function is called when the filename is changed by
1934             anyone.  It resets the virgin bit.
1935     \param  object  Text entry box
1936     \param  data    The export dialog
1937     \return None
1939     This function gets called when the text area is modified.  It is
1940     looking for the case where the text area is modified from its
1941     original value.  In that case it sets the "filename-modified" bit
1942     to TRUE.  If the text dialog returns back to the original text, the
1943     bit gets reset.  This should stop simple mistakes.
1944 */
1945 static void
1946 sp_export_filename_modified (GtkObject * object, gpointer data)
1948     GtkWidget * text_entry = (GtkWidget *)object;
1949     GtkWidget * export_dialog = (GtkWidget *)data;
1951     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1952         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1953 //        printf("Modified: FALSE\n");
1954     } else {
1955         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1956 //        printf("Modified: TRUE\n");
1957     }
1959     return;
1960 } // end sp_export_filename_modified
1962 /*
1963   Local Variables:
1964   mode:c++
1965   c-file-style:"stroustrup"
1966   c-file-offsets:((innamespace . 0)(inline-open . 0))
1967   indent-tabs-mode:nil
1968   fill-column:99
1969   End:
1970 */
1971 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :