Code

Add blur
[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  *
12  * Copyright (C) 1999-2005 Authors
13  * Copyright (C) 2001-2002 Ximian, Inc.
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
33 #include <glibmm/i18n.h>
34 #include "helper/unit-menu.h"
35 #include "helper/units.h"
36 #include "unit-constants.h"
37 #include "helper/window.h"
38 #include "inkscape-private.h"
39 #include "document.h"
40 #include "desktop-handles.h"
41 #include "sp-item.h"
42 #include "selection.h"
43 #include "file.h"
44 #include "macros.h"
45 #include "sp-namedview.h"
47 #include "dialog-events.h"
48 #include "../prefs-utils.h"
49 #include "../verbs.h"
50 #include "../interface.h"
52 #include "extension/output.h"
53 #include "extension/db.h"
55 #include "io/sys.h"
57 #include "helper/png-write.h"
60 #define SP_EXPORT_MIN_SIZE 1.0
62 #define DPI_BASE PX_PER_IN
64 #define EXPORT_COORD_PRECISION 3
66 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
67 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
68 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
70 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
71                                                    GtkObject *base);
72                                              
73 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
74                                                    GtkObject *base);
75                                              
76 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
77                                                    GtkObject *base);
78                                                  
79 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
80                                                    GtkObject *base);
81                                                   
82 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
83                                                    GtkObject *base);
84                                                    
85 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
86                                                    GtkObject *base);
87                                                    
88 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
89                                                    GtkObject *base);
90                                            
91 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
92                                           Inkscape::Selection *selection, 
93                                           GtkObject *base);
94 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
95                                            Inkscape::Selection *selection, 
96                                            guint flags,
97                                            GtkObject *base );
99 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
100 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
101 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
102 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
103 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
105 static void sp_export_filename_modified (GtkObject * object, gpointer data);
106 static inline void sp_export_find_default_selection(GtkWidget * dlg);
107 static void sp_export_detect_size(GtkObject * base);
109 static const gchar *prefs_path = "dialogs.export";
111 // these all need to be reinitialized to their defaults during dialog_destroy
112 static GtkWidget *dlg = NULL;
113 static win_data wd;
114 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
115 static gchar * original_name = NULL;
116 static gchar * doc_export_name = NULL;
117 static bool was_empty = TRUE;
119 /** What type of button is being pressed */
120 enum selection_type {
121     SELECTION_PAGE = 0,  /**< Export the whole page */
122     SELECTION_DRAWING,   /**< Export everything drawn on the page */
123     SELECTION_SELECTION, /**< Export everything that is selected */
124     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
125     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
126 };
128 /** A list of strings that is used both in the preferences, and in the
129     data fields to describe the various values of \c selection_type. */
130 static const char * selection_names[SELECTION_NUMBER_OF] = {
131     "page", "drawing", "selection", "custom"};
133 /** The names on the buttons for the various selection types. */
134 static const char * selection_labels[SELECTION_NUMBER_OF] = {
135     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
137 static void
138 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
140     sp_signal_disconnect_by_data (INKSCAPE, dlg);
142     wd.win = dlg = NULL;
143     wd.stop = 0;
144     x = -1000; y = -1000; w = 0; h = 0;
145     g_free(original_name);
146     original_name = NULL;
147     g_free(doc_export_name);
148     doc_export_name = NULL;
149     was_empty = TRUE;
151     return;
152 } // end of sp_export_dialog_destroy()
154 /// Called when dialog is closed or inkscape is shut down.
155 static bool
156 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
159     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
160     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
162     if (x<0) x=0;
163     if (y<0) y=0;
165     prefs_set_int_attribute (prefs_path, "x", x);
166     prefs_set_int_attribute (prefs_path, "y", y);
167     prefs_set_int_attribute (prefs_path, "w", w);
168     prefs_set_int_attribute (prefs_path, "h", h);
170     return FALSE; // which means, go ahead and destroy it
172 } // end of sp_export_dialog_delete()
174 /**
175     \brief  Creates a new spin button for the export dialog
176     \param  key  The name of the spin button
177     \param  val  A default value for the spin button
178     \param  min  Minimum value for the spin button
179     \param  max  Maximum value for the spin button
180     \param  step The step size for the spin button
181     \param  page Size of the page increment
182     \param  us   Unit selector that effects this spin button
183     \param  t    Table to put the spin button in
184     \param  x    X location in the table \c t to start with
185     \param  y    Y location in the table \c t to start with
186     \param  ll   Text to put on the left side of the spin button (optional)
187     \param  lr   Text to put on the right side of the spin button (optional)
188     \param  digits  Number of digits to display after the decimal
189     \param  sensitive  Whether the spin button is sensitive or not
190     \param  cb   Callback for when this spin button is changed (optional)
191     \param  dlg  Export dialog the spin button is being placed in
193 */
194 static void
195 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
196                            float step, float page, GtkWidget *us,
197                            GtkWidget *t, int x, int y,
198                            const gchar *ll, const gchar *lr,
199                            int digits, unsigned int sensitive,
200                            GCallback cb, GtkWidget *dlg )
202     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
203     gtk_object_set_data (a, "key", key);
204     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
206     if (us) {
207         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
208                                           GTK_ADJUSTMENT (a) );
209     }
211     int pos = 0;
213     GtkWidget *l = NULL;
215     if (ll) {
217         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
218         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
219         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
220                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
221         gtk_widget_set_sensitive (l, sensitive);
222         pos += 1;
224     }
226     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
227     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
228                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229     gtk_widget_set_size_request (sb, 80, -1);
230     gtk_widget_set_sensitive (sb, sensitive);
231     pos += 1;
233     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
235     if (lr) {
237         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
238         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
239         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
240                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
241         gtk_widget_set_sensitive (l, sensitive);
242         pos += 1;
244         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
245     }
247     if (cb)
248         gtk_signal_connect (a, "value_changed", cb, dlg);
250     return;
251 } // end of sp_export_spinbutton_new()
254 static Gtk::VBox *
255 sp_export_dialog_area_box (GtkWidget * dlg)
257     Gtk::VBox* vb = new Gtk::VBox(false, 3);
259     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
260     lbl->set_use_markup(true);
261     vb->pack_start(*lbl);
263     /* Units box */
264     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
265     /* gets added to the vbox later, but the unit selector is needed
266        earlier than that */
268     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
269     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
270     if (desktop)
271         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
272     unitbox->pack_end(*us, false, false, 0);
273     Gtk::Label* l = new Gtk::Label(_("Units:"));
274     unitbox->pack_end(*l, false, false, 3);
275     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
277     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
279     Gtk::ToggleButton* b;
280     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
281         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
282         b->set_data("key", GINT_TO_POINTER(i));
283         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
284         togglebox->pack_start(*b, false, true, 0);
285         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
286                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
287     }
289     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
290                        G_CALLBACK (sp_export_selection_changed), dlg );
291     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
292                        G_CALLBACK (sp_export_selection_modified), dlg );
293     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
294                        G_CALLBACK (sp_export_selection_changed), dlg );
295     
296     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
297     t->set_row_spacings (4);
298     t->set_col_spacings (4);
300     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
301                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
302                                G_CALLBACK ( sp_export_area_x_value_changed), 
303                                dlg );
305     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
306                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
307                                G_CALLBACK (sp_export_area_x_value_changed), 
308                                dlg );
310     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
311                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
312                                G_CALLBACK 
313                                    (sp_export_area_width_value_changed), 
314                                dlg );
316     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
317                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
318                                G_CALLBACK (sp_export_area_y_value_changed), 
319                                dlg );
321     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
322                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
323                                G_CALLBACK (sp_export_area_y_value_changed), 
324                                dlg );
326     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
327                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
328                                G_CALLBACK (sp_export_area_height_value_changed), 
329                                dlg );
331     vb->pack_start(*togglebox, false, false, 3);
332     vb->pack_start(*t, false, false, 0);
333     vb->pack_start(*unitbox, false, false, 0);
335     return vb;
336 } // end of sp_export_dialog_area_box
339 void
340 sp_export_dialog (void)
342     if (!dlg) {
343         Gtk::VBox* vb;
344         Gtk::HBox* hb;
346         gchar title[500];
347         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
349         dlg = sp_window_new (title, TRUE);
351         if (x == -1000 || y == -1000) {
352             x = prefs_get_int_attribute (prefs_path, "x", 0);
353             y = prefs_get_int_attribute (prefs_path, "y", 0);
354         }
356         if (w ==0 || h == 0) {
357             w = prefs_get_int_attribute (prefs_path, "w", 0);
358             h = prefs_get_int_attribute (prefs_path, "h", 0);
359         }
361         if (x<0) x=0;
362         if (y<0) y=0;
364         if (x != 0 || y != 0) {
365             gtk_window_move ((GtkWindow *) dlg, x, y);
366         } else {
367             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
368         }
370         if (w && h)
371             gtk_window_resize ((GtkWindow *) dlg, w, h);
373         sp_transientize (dlg);
374         wd.win = dlg;
375         wd.stop = 0;
377         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
378                              G_CALLBACK (sp_transientize_callback), &wd);
380         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
381                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
383         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
384                              G_CALLBACK (sp_export_dialog_destroy), dlg);
386         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
387                              G_CALLBACK (sp_export_dialog_delete), dlg);
389         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
390                              G_CALLBACK (sp_export_dialog_delete), dlg);
392         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
393                              G_CALLBACK (sp_dialog_hide), dlg);
395         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
396                              G_CALLBACK (sp_dialog_unhide), dlg);
398         GtkTooltips *tt = gtk_tooltips_new();
400         vb = new Gtk::VBox(false, 3);
401         vb->set_border_width(3);
402         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
404         /* Export area frame */
405         {
406             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
407             area_box->set_border_width(3);
408             vb->pack_start(*area_box, false, false, 0);
409         }
411         /* Bitmap size frame */
412         {
413             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
414             size_box->set_border_width(3);
416             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
417             lbl->set_use_markup(true);
418             size_box->pack_start(*lbl, false, false, 0);
419             const int rows = 2;
420             const int cols = 5;
421             const bool homogeneous = false;
422             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
423             t->set_row_spacings (4);
424             t->set_col_spacings (4);
425             size_box->pack_start(*t);
427             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
428                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
429                                        _("_Width:"), _("pixels at"), 0, 1,
430                                        G_CALLBACK 
431                                        (sp_export_bitmap_width_value_changed), 
432                                        dlg );
434             sp_export_spinbutton_new ( "xdpi", 
435                                        prefs_get_double_attribute 
436                                        ( "dialogs.export.defaultxdpi", 
437                                          "value", DPI_BASE), 
438                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
439                                        NULL, _("dp_i"), 2, 1,
440                                        G_CALLBACK (sp_export_xdpi_value_changed), 
441                                        dlg );
443             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
444                                        NULL, GTK_WIDGET(t->gobj()), 0, 1, 
445                                        _("Height:"), _("pixels at"), 0, 1, 
446                                        G_CALLBACK
447                                        (sp_export_bitmap_height_value_changed), 
448                                        dlg );
450             /** \todo
451              * Needs fixing: there's no way to set ydpi currently, so we use  
452              *       the defaultxdpi value here, too...
453              */
454             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
455                                        ( "dialogs.export.defaultxdpi", 
456                                          "value", DPI_BASE), 
457                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
458                                        NULL, _("dpi"), 2, 0, NULL, dlg );
460             vb->pack_start(*size_box);
461         }
463         /* File entry */
464         {
465             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
466             file_box->set_border_width(3);
468             // true = has mnemonic
469             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
470             flabel->set_use_markup(true);
471             file_box->pack_start(*flabel, false, false, 0);
473             Gtk::Entry *fe = new Gtk::Entry();
475             /*
476              * set the default filename to be that of the current path + document
477              * with .png extension
478              *
479              * One thing to notice here is that this filename may get
480              * overwritten, but it won't happen here.  The filename gets
481              * written into the text field, but then the button to select
482              * the area gets set.  In that code the filename can be changed
483              * if there are some with presidence in the document.  So, while
484              * this code sets the name first, it may not be the one users
485              * really see.
486              */
487             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
488             {
489                 gchar *name;
490                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
491                 const gchar *uri = SP_DOCUMENT_URI (doc);
492                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
493                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
494                 Inkscape::Extension::Output * oextension = NULL;
496                 if (text_extension != NULL) {
497                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
498                 }
500                 if (oextension != NULL) {
501                     gchar * old_extension = oextension->get_extension();
502                     if (g_str_has_suffix(uri, old_extension)) {
503                         gchar * uri_copy;
504                         gchar * extension_point;
505                         gchar * final_name;
507                         uri_copy = g_strdup(uri);
508                         extension_point = g_strrstr(uri_copy, old_extension);
509                         extension_point[0] = '\0';
511                         final_name = g_strconcat(uri_copy, ".png", NULL);
512                         fe->set_text(final_name);
514                         g_free(final_name);
515                         g_free(uri_copy);
516                     }
517                 } else {
518                     name = g_strconcat(uri, ".png", NULL);
519                     fe->set_text(name);
520                     g_free(name);
521                 }
523                 doc_export_name = g_strdup(fe->get_text().c_str());
524             }
525             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
526                                G_CALLBACK (sp_export_filename_modified), dlg);
528             hb = new Gtk::HBox(FALSE, 5);
530             {
531                 // true = has mnemonic
532                 Gtk::Button *b = new Gtk::Button();
534                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
535                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
536                         Gtk::ICON_SIZE_BUTTON);
537                 pixlabel->pack_start(*im);
539                 Gtk::Label *l = new Gtk::Label();
540                 l->set_markup_with_mnemonic(_("_Browse..."));
541                 pixlabel->pack_start(*l);
543                 b->add(*pixlabel);
545                 hb->pack_end (*b, false, false, 4);
546                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
547                                    G_CALLBACK (sp_export_browse_clicked), NULL );
548             }
550             hb->pack_start (*fe, true, true, 0);
551             file_box->add(*hb);
552             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
553             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
554             original_name = g_strdup(fe->get_text().c_str());
555             // pressing enter in the filename field is the same as clicking export:
556             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
557                                G_CALLBACK (sp_export_export_clicked), dlg );
558             // focus is in the filename initially:
559             fe->grab_focus();
561             // mnemonic in frame label moves focus to filename:
562             flabel->set_mnemonic_widget(*fe);
564             vb->pack_start(*file_box);
565         }
567         /* Buttons */
568         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
569         bb->set_border_width(3);
571         {
572             Gtk::Button *b = new Gtk::Button();
573             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
574             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
575                     Gtk::ICON_SIZE_BUTTON);
576             image_label->pack_start(*im);
578             Gtk::Label *l = new Gtk::Label();
579             l->set_markup_with_mnemonic(_("_Export"));
580             image_label->pack_start(*l);
582             b->add(*image_label);
583             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
584             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
585                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
586             bb->pack_end(*b, false, false, 0);
587         }
589         vb->pack_end(*bb, false, false, 0);
590         vb->show_all();
592     } // end of if (!dlg)
594     sp_export_find_default_selection(dlg);
596     gtk_window_present ((GtkWindow *) dlg);
598     return;
599 } // end of sp_export_dialog()
601 static inline void
602 sp_export_find_default_selection(GtkWidget * dlg)
604     selection_type key = SELECTION_NUMBER_OF;
606     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
607         key = SELECTION_SELECTION;
608     }
610     /* Try using the preferences */
611     if (key == SELECTION_NUMBER_OF) {
612         const gchar *what = NULL;
613         int i = SELECTION_NUMBER_OF;
615         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
617         if (what != NULL) {
618             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
619                 if (!strcmp (what, selection_names[i])) {
620                     break;
621                 }
622             }
623         }
625         key = (selection_type)i;
626     }
628     if (key == SELECTION_NUMBER_OF) {
629         key = SELECTION_SELECTION;
630     }
632     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
633                                                        selection_names[key]);
634     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
636     return;
640 /**
641  * \brief  If selection changed or a different document activated, we must 
642  * recalculate any chosen areas
643  *
644  */
645 static void
646 sp_export_selection_changed ( Inkscape::Application *inkscape, 
647                               Inkscape::Selection *selection, 
648                               GtkObject *base )
650     selection_type current_key;
651     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
653     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
654             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
655             was_empty) {
656         gtk_toggle_button_set_active
657             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
658               TRUE );
659     }
660     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
662     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
664     if (inkscape &&
665             SP_IS_INKSCAPE (inkscape) &&
666             selection &&
667             SELECTION_CUSTOM != current_key) {
668         GtkToggleButton * button;
669         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
670         sp_export_area_toggled(button, base);
671     } // end of if()
673     return;
674 } // end of sp_export_selection_changed()
676 static void
677 sp_export_selection_modified ( Inkscape::Application *inkscape, 
678                                Inkscape::Selection *selection, 
679                                guint flags,
680                                GtkObject *base )
682     selection_type current_key;
683     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
685     switch (current_key) {
686         case SELECTION_DRAWING:
687             if ( SP_ACTIVE_DESKTOP ) {
688                 SPDocument *doc;
689                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
690                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
692                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
693                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
694                     sp_export_set_area (base, bbox.min()[NR::X],
695                                               bbox.min()[NR::Y],
696                                               bbox.max()[NR::X],
697                                               bbox.max()[NR::Y]);
698                 }
699             }
700             break;
701         case SELECTION_SELECTION:
702             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
703                 NRRect bbox;
704                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
705                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
706             }
707             break;
708         default:
709             /* Do nothing for page or for custom */
710             break;
711     }
713     return;
716 /// Called when one of the selection buttons was toggled.
717 static void
718 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
720     if (gtk_object_get_data (base, "update"))
721         return;
723     selection_type key, old_key;
724     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
725     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
727     /* Ignore all "turned off" events unless we're the only active button */
728     if (!gtk_toggle_button_get_active (tb) ) {
730         /* Don't let the current selection be deactived - but rerun the
731            activate to allow the user to renew the values */
732         if (key == old_key) {
733             gtk_toggle_button_set_active ( tb, TRUE );
734         }
736         return;
737     }
739     /* Turn off the currently active button unless it's us */
740     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
742     if (old_key != key) {
743         gtk_toggle_button_set_active
744             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
745               FALSE );
746     }
748     if ( SP_ACTIVE_DESKTOP )
749     {
750         SPDocument *doc;
751         NR::Rect bbox;
752         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
754         /* Notice how the switch is used to 'fall through' here to get
755            various backups.  If you modify this without noticing you'll
756            probabaly screw something up. */
757         switch (key) {
758             case SELECTION_SELECTION:
759                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
760                 {
761                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
762                     /* Only if there is a selection that we can set
763                        do we break, otherwise we fall through to the
764                        drawing */
765                     // std::cout << "Using selection: SELECTION" << std::endl;
766                     key = SELECTION_SELECTION;
767                     break;
768                 }
769             case SELECTION_DRAWING:
770                 /** \todo 
771                  * This returns wrong values if the document has a viewBox.
772                  */
773                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
774                 
775                 /* If the drawing is valid, then we'll use it and break
776                    otherwise we drop through to the page settings */
777                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
778                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) { 
779                     // std::cout << "Using selection: DRAWING" << std::endl;
780                     key = SELECTION_DRAWING;
781                     break;
782                 }
783             case SELECTION_PAGE:
784                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
785                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
786                                 );
788                 // std::cout << "Using selection: PAGE" << std::endl;
789                 key = SELECTION_PAGE;
790                 break;
791             case SELECTION_CUSTOM:
792             default:
793                 break;
794         } // switch
795         
796         // remember area setting
797         prefs_set_string_attribute ( "dialogs.export.exportarea", 
798                                      "value", selection_names[key]);
800         if (key != SELECTION_CUSTOM) {
801             sp_export_set_area (base, bbox.min()[NR::X],
802                                       bbox.min()[NR::Y],
803                                       bbox.max()[NR::X],
804                                       bbox.max()[NR::Y]);
805         }
806     
807     } // end of if ( SP_ACTIVE_DESKTOP )
810     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
811         GtkWidget * file_entry;
812         const gchar * filename = NULL;
813         float xdpi = 0.0, ydpi = 0.0;
815         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
817         switch (key) {
818             case SELECTION_PAGE:
819             case SELECTION_DRAWING: {
820                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
821                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
822                 const gchar * dpi_string;
824                 filename = repr->attribute("inkscape:export-filename");
826                 dpi_string = NULL;
827                 dpi_string = repr->attribute("inkscape:export-xdpi");
828                 if (dpi_string != NULL) {
829                     xdpi = atof(dpi_string);
830                 }
832                 dpi_string = NULL;
833                 dpi_string = repr->attribute("inkscape:export-ydpi");
834                 if (dpi_string != NULL) {
835                     ydpi = atof(dpi_string);
836                 }
838                 if (filename == NULL) {
839                     if (doc_export_name != NULL) {
840                         filename = g_strdup(doc_export_name);
841                     } else {
842                         filename = g_strdup("");
843                     }
844                 }
845                 break;
846             }
847             case SELECTION_SELECTION:
848                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
849                     const GSList * reprlst;
850                     bool filename_search = TRUE;
851                     bool xdpi_search = TRUE;
852                     bool ydpi_search = TRUE;
854                     reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
855                     for(; reprlst != NULL &&
856                             filename_search &&
857                             xdpi_search &&
858                             ydpi_search;
859                             reprlst = reprlst->next) {
860                         const gchar * dpi_string;
861                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
863                         if (filename_search) {
864                             filename = repr->attribute("inkscape:export-filename");
865                             if (filename != NULL)
866                                 filename_search = FALSE;
867                         }
869                         if (xdpi_search) {
870                             dpi_string = NULL;
871                             dpi_string = repr->attribute("inkscape:export-xdpi");
872                             if (dpi_string != NULL) {
873                                 xdpi = atof(dpi_string);
874                                 xdpi_search = FALSE;
875                             }
876                         }
878                         if (ydpi_search) {
879                             dpi_string = NULL;
880                             dpi_string = repr->attribute("inkscape:export-ydpi");
881                             if (dpi_string != NULL) {
882                                 ydpi = atof(dpi_string);
883                                 ydpi_search = FALSE;
884                             }
885                         }
886                     }
888                     /* If we still don't have a filename -- let's build
889                        one that's nice */
890                     if (filename == NULL) {
891                         const gchar * id = NULL;
892                         reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
893                         for(; reprlst != NULL; reprlst = reprlst->next) {
894                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
895                             if (repr->attribute("id")) {
896                                 id = repr->attribute("id");
897                                 break;
898                             }
899                         }
900                         if (id == NULL) /* This should never happen */
901                             id = "bitmap";
903                         gchar * directory = NULL;
904                         const gchar * file_entry_text;
906                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
907                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
908                             // std::cout << "Directory from dialog" << std::endl;
909                             directory = g_dirname(file_entry_text);
910                         }
912                         if (directory == NULL) {
913                             /* Grab document directory */
914                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
915                                 // std::cout << "Directory from document" << std::endl;
916                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
917                             }
918                         }
920                         if (directory == NULL) {
921                             // std::cout << "Home Directory" << std::endl;
922                             directory = homedir_path(NULL);
923                         }
925                         gchar * id_ext = g_strconcat(id, ".png", NULL);
926                         filename = g_build_filename(directory, id_ext, NULL);
927                         g_free(directory);
928                         g_free(id_ext);
929                     }
930                 }
931                 break;
932             case SELECTION_CUSTOM:
933             default:
934                 break;
935         }
937         if (filename != NULL) {
938             g_free(original_name);
939             original_name = g_strdup(filename);
940             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
941         }
943         if (xdpi != 0.0) {
944             sp_export_value_set(base, "xdpi", xdpi);
945         }
947         /* These can't be seperate, and setting x sets y, so for
948            now setting this is disabled.  Hopefully it won't be in
949            the future */
950         if (FALSE && ydpi != 0.0) {
951             sp_export_value_set(base, "ydpi", ydpi);
952         }
953     }
955     return;
956 } // end of sp_export_area_toggled()
958 /// Called when dialog is deleted
959 static gint
960 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
962     g_object_set_data (base, "cancel", (gpointer) 1);
963     return TRUE;
964 } // end of sp_export_progress_delete()
966 /// Called when progress is cancelled
967 static void
968 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
970     g_object_set_data (base, "cancel", (gpointer) 1);
971 } // end of sp_export_progress_cancel()
973 /// Called for every progress iteration
974 static unsigned int
975 sp_export_progress_callback (float value, void *data)
977     GtkWidget *prg;
978     int evtcount;
980     if (g_object_get_data ((GObject *) data, "cancel"))
981         return FALSE;
983     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
984     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
986     evtcount = 0;
987     while ((evtcount < 16) && gdk_events_pending ()) {
988             gtk_main_iteration_do (FALSE);
989             evtcount += 1;
990     }
992     gtk_main_iteration_do (FALSE);
994     return TRUE;
996 } // end of sp_export_progress_callback()
998 /// Called when export button is clicked
999 static void
1000 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1002     if (!SP_ACTIVE_DESKTOP) return;
1004     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1005     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1007     float const x0 = sp_export_value_get_px(base, "x0");
1008     float const y0 = sp_export_value_get_px(base, "y0");
1009     float const x1 = sp_export_value_get_px(base, "x1");
1010     float const y1 = sp_export_value_get_px(base, "y1");
1011     float const xdpi = sp_export_value_get(base, "xdpi");
1012     float const ydpi = sp_export_value_get(base, "ydpi");
1013     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1014     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1016     if (filename == NULL || *filename == '\0') {
1017         sp_ui_error_dialog(_("You have to enter a filename"));
1018         return;
1019     }
1021     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1022         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1023         return;
1024     }
1026     gchar *dirname = g_dirname(filename);
1027     if ( dirname == NULL
1028          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1029     {
1030         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1031         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1032                                        safeDir);
1033         sp_ui_error_dialog(error);
1034         g_free(safeDir);
1035         g_free(error);
1036         g_free(dirname);
1037         return;
1038     }
1039     g_free(dirname);
1041     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1042     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1043     char *fn;
1044     gchar *text;
1046     dlg = gtk_dialog_new ();
1047     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1048     prg = gtk_progress_bar_new ();
1049     sp_transientize (dlg);
1050     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1051     g_object_set_data ((GObject *) base, "progress", prg);
1052     fn = g_path_get_basename (filename);
1053     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1054                              fn, width, height);
1055     g_free (fn);
1056     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1057     g_free (text);
1058     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1059                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1060     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1061                         prg, FALSE, FALSE, 4 );
1062     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1063                                   GTK_STOCK_CANCEL, 
1064                                   GTK_RESPONSE_CANCEL );
1065                                   
1066     g_signal_connect ( (GObject *) dlg, "delete_event", 
1067                        (GCallback) sp_export_progress_delete, base);
1068     g_signal_connect ( (GObject *) btn, "clicked", 
1069                        (GCallback) sp_export_progress_cancel, base);
1070     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1071     gtk_widget_show_all (dlg);
1072     
1073     /* Do export */
1074     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1075                              x0, y0, x1, y1, width, height, xdpi, ydpi, 
1076                              nv->pagecolor, 
1077                              sp_export_progress_callback, base)) {
1078         gchar * error;
1079         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1080         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1081         sp_ui_error_dialog(error);
1082         g_free(safeFile);
1083         g_free(error);
1084     }
1086     /* Reset the filename so that it can be changed again by changing
1087        selections and all that */
1088     g_free(original_name);
1089     original_name = g_strdup(filename);
1090     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1092     gtk_widget_destroy (dlg);
1093     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1095     /* Setup the values in the document */
1096     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1097         case SELECTION_PAGE:
1098         case SELECTION_DRAWING: {
1099             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1100             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1101             bool modified = false;
1102             const gchar * temp_string;
1104             bool saved = sp_document_get_undo_sensitive(doc);
1105             sp_document_set_undo_sensitive(doc, false);
1107             temp_string = repr->attribute("inkscape:export-filename");
1108             if (temp_string == NULL || strcmp(temp_string, filename)) {
1109                 repr->setAttribute("inkscape:export-filename", filename);
1110                 modified = true;
1111             }
1112             temp_string = repr->attribute("inkscape:export-xdpi");
1113             if (temp_string == NULL || xdpi != atof(temp_string)) {
1114                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1115                 modified = true;
1116             }
1117             temp_string = repr->attribute("inkscape:export-ydpi");
1118             if (temp_string == NULL || xdpi != atof(temp_string)) {
1119                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1120                 modified = true;
1121             }
1123             if (modified)
1124                 repr->setAttribute("sodipodi:modified", "TRUE");
1125             sp_document_set_undo_sensitive(doc, saved);
1126             break;
1127         }
1128         case SELECTION_SELECTION: {
1129             const GSList * reprlst;
1130             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1131             bool modified = false;
1133             bool saved = sp_document_get_undo_sensitive(doc);
1134             sp_document_set_undo_sensitive(doc, false);
1135             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1137             for(; reprlst != NULL; reprlst = reprlst->next) {
1138                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1139                 const gchar * temp_string;
1141                 if (repr->attribute("id") == NULL ||
1142                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1143                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1144                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1145                     temp_string = repr->attribute("inkscape:export-filename");
1146                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1147                         repr->setAttribute("inkscape:export-filename", filename);
1148                         modified = true;
1149                     }
1150                 }
1151                 temp_string = repr->attribute("inkscape:export-xdpi");
1152                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1153                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1154                     modified = true;
1155                 }
1156                 temp_string = repr->attribute("inkscape:export-ydpi");
1157                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1158                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1159                     modified = true;
1160                 }
1161             }
1163             if (modified) {
1164                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1165                 repr->setAttribute("sodipodi:modified", "TRUE");
1166             }
1168             sp_document_set_undo_sensitive(doc, saved);
1169             break;
1170         }
1171         default:
1172             break;
1173     }
1176     return;
1177 } // end of sp_export_export_clicked()
1179 /// Called when Browse button is clicked
1180 static void
1181 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1183     GtkWidget *fs, *fe;
1184     const gchar *filename;
1186     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1187                                       NULL,
1188                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1189                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1190                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1191                                       NULL );
1193     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1195     sp_transientize (fs);
1197     gtk_window_set_modal(GTK_WINDOW (fs), true);
1199     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1201     if (*filename == '\0') {
1202         filename = homedir_path(NULL);
1203     }
1205     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1207     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1208     {
1209         gchar *file;
1211         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1212         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1213         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1214         g_free(utf8file);
1216         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1217         g_free(file);
1218     }
1220     gtk_widget_destroy (fs);
1222     return;
1223 } // end of sp_export_browse_clicked()
1225 // TODO: Move this to nr-rect-fns.h.
1226 static bool
1227 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1228
1229     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1230     return (
1231         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1232         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1233         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1234         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1235         );
1238 /**
1239     \brief  This function is used to detect the current selection setting
1240             based on the values in the x0, y0, x1 and y0 fields.
1241     \param  base  The export dialog itself
1243     One of the most confusing parts of this function is why the array
1244     is built at the beginning.  What needs to happen here is that we
1245     should always check the current selection to see if it is the valid
1246     one.  While this is a performance improvement it is also a usability
1247     one during the cases where things like selections and drawings match
1248     size.  This way buttons change less 'randomly' (atleast in the eyes
1249     of the user).  To do this an array is built where the current selection
1250     type is placed first, and then the others in an order from smallest
1251     to largest (this can be configured by reshuffling \c test_order).
1253     All of the values in this function are rounded to two decimal places
1254     because that is what is shown to the user.  While everything is kept
1255     more accurate than that, the user can't control more acurrate than
1256     that, so for this to work for them - it needs to check on that level
1257     of accuracy.
1259     \todo finish writing this up
1260 */
1261 static void
1262 sp_export_detect_size(GtkObject * base) {
1263     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1264     selection_type this_test[SELECTION_NUMBER_OF + 1];
1265     selection_type key = SELECTION_NUMBER_OF;
1267     NR::Point x(sp_export_value_get_px (base, "x0"),
1268                 sp_export_value_get_px (base, "y0"));
1269     NR::Point y(sp_export_value_get_px (base, "x1"),
1270                 sp_export_value_get_px (base, "y1"));
1271     NR::Rect current_bbox(x, y);
1272     //std::cout << "Current " << current_bbox;
1274     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1275     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1276         this_test[i + 1] = test_order[i];
1277     }
1279     for (int i = 0;
1280             i < SELECTION_NUMBER_OF + 1 &&
1281                 key == SELECTION_NUMBER_OF &&
1282                 SP_ACTIVE_DESKTOP != NULL;
1283             i++) {
1284         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1285         switch (this_test[i]) {
1286             case SELECTION_SELECTION:
1287                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1288                     NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1290                     //std::cout << "Selection " << bbox;
1291                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1292                         key = SELECTION_SELECTION;
1293                     }
1294                 }
1295                 break;
1296             case SELECTION_DRAWING: {
1297                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1299                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1301                 // std::cout << "Drawing " << bbox2;
1302                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1303                     key = SELECTION_DRAWING;
1304                 }
1305                 break;
1306             }
1308             case SELECTION_PAGE: {
1309                 SPDocument *doc;
1311                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1313                 NR::Point x(0.0, 0.0);
1314                 NR::Point y(sp_document_width(doc),
1315                             sp_document_height(doc));
1316                 NR::Rect bbox(x, y);
1318                 // std::cout << "Page " << bbox;
1319                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1320                     key = SELECTION_PAGE;
1321                 }
1323                 break;
1324            }
1325         default:
1326            break;
1327         }
1328     }
1329     // std::cout << std::endl;
1331     if (key == SELECTION_NUMBER_OF) {
1332         key = SELECTION_CUSTOM;
1333     }
1335     /* We're now using a custom size, not a fixed one */
1336     /* printf("Detecting state: %s\n", selection_names[key]); */
1337     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1338     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1339     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1340     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1342     return;
1343 } /* sp_export_detect_size */
1345 /// Called when area x0 value is changed
1346 static void
1347 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1349     float x0, x1, xdpi, width;
1351     if (gtk_object_get_data (base, "update"))
1352         return;
1354     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1355             (base, "units")))
1356     {
1357         return;
1358     }
1360     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1362     x0 = sp_export_value_get_px (base, "x0");
1363     x1 = sp_export_value_get_px (base, "x1");
1364     xdpi = sp_export_value_get (base, "xdpi");
1366     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1368     if (width < SP_EXPORT_MIN_SIZE) {
1369         const gchar *key;
1370         width = SP_EXPORT_MIN_SIZE;
1371         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1373         if (!strcmp (key, "x0")) {
1374             x1 = x0 + width * DPI_BASE / xdpi;
1375             sp_export_value_set_px (base, "x1", x1);
1376         } else {
1377             x0 = x1 - width * DPI_BASE / xdpi;
1378             sp_export_value_set_px (base, "x0", x0);
1379         }
1380     }
1382     sp_export_value_set_px (base, "width", x1 - x0);
1383     sp_export_value_set (base, "bmwidth", width);
1385     sp_export_detect_size(base);
1387     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1389     return;
1390 } // end of sp_export_area_x_value_changed()
1392 /// Called when area y0 value is changed.
1393 static void
1394 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1396     float y0, y1, ydpi, height;
1398     if (gtk_object_get_data (base, "update"))
1399         return;
1401     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1402            (base, "units")))
1403     {
1404         return;
1405     }
1407     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1409     y0 = sp_export_value_get_px (base, "y0");
1410     y1 = sp_export_value_get_px (base, "y1");
1411     ydpi = sp_export_value_get (base, "ydpi");
1413     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1415     if (height < SP_EXPORT_MIN_SIZE) {
1416         const gchar *key;
1417         height = SP_EXPORT_MIN_SIZE;
1418         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1419         if (!strcmp (key, "y0")) {
1420             y1 = y0 + height * DPI_BASE / ydpi;
1421             sp_export_value_set_px (base, "y1", y1);
1422         } else {
1423             y0 = y1 - height * DPI_BASE / ydpi;
1424             sp_export_value_set_px (base, "y0", y0);
1425         }
1426     }
1428     sp_export_value_set_px (base, "height", y1 - y0);
1429     sp_export_value_set (base, "bmheight", height);
1431     sp_export_detect_size(base);
1433     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1435     return;
1436 } // end of sp_export_area_y_value_changed()
1438 /// Called when x1-x0 or area width is changed
1439 static void
1440 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1442     float x0, x1, xdpi, width, bmwidth;
1444     if (gtk_object_get_data (base, "update"))
1445         return;
1447     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1448            (base, "units"))) {
1449         return;
1450     }
1452     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1454     x0 = sp_export_value_get_px (base, "x0");
1455     x1 = sp_export_value_get_px (base, "x1");
1456     xdpi = sp_export_value_get (base, "xdpi");
1457     width = sp_export_value_get_px (base, "width");
1458     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1460     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1462         bmwidth = SP_EXPORT_MIN_SIZE;
1463         width = bmwidth * DPI_BASE / xdpi;
1464         sp_export_value_set_px (base, "width", width);
1465     }
1467     sp_export_value_set_px (base, "x1", x0 + width);
1468     sp_export_value_set (base, "bmwidth", bmwidth);
1470     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1472     return;
1473 } // end of sp_export_area_width_value_changed()
1475 /// Called when y1-y0 or area height is changed.
1476 static void
1477 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1480     float y0, y1, ydpi, height, bmheight;
1482     if (gtk_object_get_data (base, "update"))
1483         return;
1485     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1486            (base, "units"))) {
1487         return;
1488     }
1490     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1492     y0 = sp_export_value_get_px (base, "y0");
1493     y1 = sp_export_value_get_px (base, "y1");
1494     ydpi = sp_export_value_get (base, "ydpi");
1495     height = sp_export_value_get_px (base, "height");
1496     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1498     if (bmheight < SP_EXPORT_MIN_SIZE) {
1499         bmheight = SP_EXPORT_MIN_SIZE;
1500         height = bmheight * DPI_BASE / ydpi;
1501         sp_export_value_set_px (base, "height", height);
1502     }
1504     sp_export_value_set_px (base, "y1", y0 + height);
1505     sp_export_value_set (base, "bmheight", bmheight);
1507     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1509     return;
1510 } // end of sp_export_area_height_value_changed()
1512 /**
1513     \brief  A function to set the ydpi
1514     \param  base  The export dialog
1516     This function grabs all of the y values and then figures out the
1517     new bitmap size based on the changing dpi value.  The dpi value is
1518     gotten from the xdpi setting as these can not currently be independent.
1519 */
1520 static void
1521 sp_export_set_image_y (GtkObject *base)
1523     float y0, y1, xdpi;
1525     y0 = sp_export_value_get_px (base, "y0");
1526     y1 = sp_export_value_get_px (base, "y1");
1527     xdpi = sp_export_value_get (base, "xdpi");
1529     sp_export_value_set (base, "ydpi", xdpi);
1530     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1532     return;
1533 } // end of sp_export_set_image_y()
1535 /**
1536     \brief  A function to set the xdpi
1537     \param  base  The export dialog
1539     This function grabs all of the x values and then figures out the
1540     new bitmap size based on the changing dpi value.  The dpi value is
1541     gotten from the xdpi setting as these can not currently be independent.
1542 */
1543 static void
1544 sp_export_set_image_x (GtkObject *base)
1546     float x0, x1, xdpi;
1548     x0 = sp_export_value_get_px (base, "x0");
1549     x1 = sp_export_value_get_px (base, "x1");
1550     xdpi = sp_export_value_get (base, "xdpi");
1552     sp_export_value_set (base, "ydpi", xdpi);
1553     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1555     return;
1556 } // end of sp_export_set_image_x()
1558 /// Called when pixel width is changed
1559 static void
1560 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1562     float x0, x1, bmwidth, xdpi;
1564     if (gtk_object_get_data (base, "update"))
1565         return;
1567     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1568            (base, "units"))) {
1569        return;
1570     }
1572     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1574     x0 = sp_export_value_get_px (base, "x0");
1575     x1 = sp_export_value_get_px (base, "x1");
1576     bmwidth = sp_export_value_get (base, "bmwidth");
1578     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1579         bmwidth = SP_EXPORT_MIN_SIZE;
1580         sp_export_value_set (base, "bmwidth", bmwidth);
1581     }
1583     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1584     sp_export_value_set (base, "xdpi", xdpi);
1586     sp_export_set_image_y (base);
1588     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1590     return;
1591 } // end of sp_export_bitmap_width_value_changed()
1593 /// Called when pixel height is changed
1594 static void
1595 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1597     float y0, y1, bmheight, xdpi;
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     bmheight = sp_export_value_get (base, "bmheight");
1613     if (bmheight < SP_EXPORT_MIN_SIZE) {
1614         bmheight = SP_EXPORT_MIN_SIZE;
1615         sp_export_value_set (base, "bmheight", bmheight);
1616     }
1618     xdpi = bmheight * DPI_BASE / (y1 - y0);
1619     sp_export_value_set (base, "xdpi", xdpi);
1621     sp_export_set_image_x (base);
1623     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1625     return;
1626 } // end of sp_export_bitmap_width_value_changed()
1628 /**
1629     \brief  A function to adjust the bitmap width when the xdpi value changes
1630     \param  adj  The adjustment that was changed
1631     \param  base The export dialog itself
1633     The first thing this function checks is to see if we are doing an
1634     update.  If we are, this function just returns because there is another
1635     instance of it that will handle everything for us.  If there is a
1636     units change, we also assume that everyone is being updated appropriately
1637     and there is nothing for us to do.
1639     If we're the highest level function, we set the update flag, and
1640     continue on our way.
1642     All of the values are grabbed using the \c sp_export_value_get functions
1643     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1644     xdpi value is saved in the preferences for the next time the dialog
1645     is opened.  (does the selection dpi need to be set here?)
1647     A check is done to to ensure that we aren't outputing an invalid width,
1648     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1649     changed to make it valid.
1651     After all of this the bitmap width is changed.
1653     We also change the ydpi.  This is a temporary hack as these can not
1654     currently be independent.  This is likely to change in the future.
1655 */
1656 void
1657 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1659     float x0, x1, xdpi, bmwidth;
1661     if (gtk_object_get_data (base, "update"))
1662         return;
1664     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1665            (base, "units"))) {
1666        return;
1667     }
1669     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1671     x0 = sp_export_value_get_px (base, "x0");
1672     x1 = sp_export_value_get_px (base, "x1");
1673     xdpi = sp_export_value_get (base, "xdpi");
1675     // remember xdpi setting
1676     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1678     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1680     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1681         bmwidth = SP_EXPORT_MIN_SIZE;
1682         if (x1 != x0)
1683             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1684         else
1685             xdpi = DPI_BASE;
1686         sp_export_value_set (base, "xdpi", xdpi);
1687     }
1689     sp_export_value_set (base, "bmwidth", bmwidth);
1691     sp_export_set_image_y (base);
1693     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1695     return;
1696 } // end of sp_export_xdpi_value_changed()
1699 /**
1700     \brief  A function to change the area that is used for the exported
1701             bitmap.
1702     \param  base  This is the export dialog
1703     \param  x0    Horizontal upper left hand corner of the picture in points
1704     \param  y0    Vertical upper left hand corner of the picture in points
1705     \param  x1    Horizontal lower right hand corner of the picture in points
1706     \param  y1    Vertical lower right hand corner of the picture in points
1708     This function just calls \c sp_export_value_set_px for each of the
1709     parameters that is passed in.  This allows for setting them all in
1710     one convient area.
1712     Update is set to suspend all of the other test running while all the
1713     values are being set up.  This allows for a performance increase, but
1714     it also means that the wrong type won't be detected with only some of
1715     the values set.  After all the values are set everyone is told that
1716     there has been an update.
1717 */
1718 static void
1719 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1721     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1722     sp_export_value_set_px (base, "x1", x1);
1723     sp_export_value_set_px (base, "y1", y1);
1724     sp_export_value_set_px (base, "x0", x0);
1725     sp_export_value_set_px (base, "y0", y0);
1726     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1728     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1729     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1731     return;
1734 /**
1735     \brief  Sets the value of an adjustment
1736     \param  base  The export dialog
1737     \param  key   Which adjustment to set
1738     \param  val   What value to set it to
1740     This function finds the adjustment using the data stored in the
1741     export dialog.  After finding the adjustment it then sets
1742     the value of it.
1743 */
1744 static void
1745 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1747     GtkAdjustment *adj;
1749     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1751     gtk_adjustment_set_value (adj, val);
1754 /**
1755     \brief  A function to set a value using the units points
1756     \param  base  The export dialog
1757     \param  key   Which value should be set
1758     \param  val   What the value should be in points
1760     This function first gets the adjustment for the key that is passed
1761     in.  It then figures out what units are currently being used in the
1762     dialog.  After doing all of that, it then converts the incoming
1763     value and sets the adjustment.
1764 */
1765 static void
1766 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1768     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1770     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1772     return;
1775 /**
1776     \brief  Get the value of an adjustment in the export dialog
1777     \param  base  The export dialog
1778     \param  key   Which adjustment is being looked for
1779     \return The value in the specified adjustment
1781     This function gets the adjustment from the data field in the export
1782     dialog.  It then grabs the value from the adjustment.
1783 */
1784 static float
1785 sp_export_value_get ( GtkObject *base, const gchar *key )
1787     GtkAdjustment *adj;
1789     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1791     return adj->value;
1794 /**
1795     \brief  Grabs a value in the export dialog and converts the unit
1796             to points
1797     \param  base  The export dialog
1798     \param  key   Which value should be returned
1799     \return The value in the adjustment in points
1801     This function, at its most basic, is a call to \c sp_export_value_get
1802     to get the value of the adjustment.  It then finds the units that
1803     are being used by looking at the "units" attribute of the export
1804     dialog.  Using that it converts the returned value into points.
1805 */
1806 static float
1807 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1809     float value = sp_export_value_get(base, key);
1810     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1812     return sp_units_get_pixels (value, *unit);
1813 } // end of sp_export_value_get_px()
1815 /**
1816     \brief  This function is called when the filename is changed by
1817             anyone.  It resets the virgin bit.
1818     \param  object  Text entry box
1819     \param  data    The export dialog
1820     \return None
1822     This function gets called when the text area is modified.  It is
1823     looking for the case where the text area is modified from its
1824     original value.  In that case it sets the "filename-modified" bit
1825     to TRUE.  If the text dialog returns back to the original text, the
1826     bit gets reset.  This should stop simple mistakes.
1827 */
1828 static void
1829 sp_export_filename_modified (GtkObject * object, gpointer data)
1831     GtkWidget * text_entry = (GtkWidget *)object;
1832     GtkWidget * export_dialog = (GtkWidget *)data;
1834     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1835         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1836 //        printf("Modified: FALSE\n");
1837     } else {
1838         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1839 //        printf("Modified: TRUE\n");
1840     }
1842     return;
1843 } // end sp_export_filename_modified
1845 /*
1846   Local Variables:
1847   mode:c++
1848   c-file-style:"stroustrup"
1849   c-file-offsets:((innamespace . 0)(inline-open . 0))
1850   indent-tabs-mode:nil
1851   fill-column:99
1852   End:
1853 */
1854 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :