Code

9d50018e9c6d1e9b3780663b4920f992be0416c3
[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"
58 #define SP_EXPORT_MIN_SIZE 1.0
60 #define DPI_BASE PX_PER_IN
62 #define EXPORT_COORD_PRECISION 3
64 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
65 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
66 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
68 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
69                                                    GtkObject *base);
70                                              
71 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
72                                                    GtkObject *base);
73                                              
74 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
75                                                    GtkObject *base);
76                                                  
77 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
78                                                    GtkObject *base);
79                                                   
80 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
81                                                    GtkObject *base);
82                                                    
83 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
84                                                    GtkObject *base);
85                                                    
86 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
87                                                    GtkObject *base);
88                                            
89 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
90                                           Inkscape::Selection *selection, 
91                                           GtkObject *base);
92 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
93                                            Inkscape::Selection *selection, 
94                                            guint flags,
95                                            GtkObject *base );
97 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
98 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
99 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
100 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
101 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
103 static void sp_export_filename_modified (GtkObject * object, gpointer data);
104 static inline void sp_export_find_default_selection(GtkWidget * dlg);
105 static void sp_export_detect_size(GtkObject * base);
107 static const gchar *prefs_path = "dialogs.export";
109 // these all need to be reinitialized to their defaults during dialog_destroy
110 static GtkWidget *dlg = NULL;
111 static win_data wd;
112 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
113 static gchar * original_name = NULL;
114 static gchar * doc_export_name = NULL;
115 static bool was_empty = TRUE;
117 /** What type of button is being pressed */
118 enum selection_type {
119     SELECTION_PAGE = 0,  /**< Export the whole page */
120     SELECTION_DRAWING,   /**< Export everything drawn on the page */
121     SELECTION_SELECTION, /**< Export everything that is selected */
122     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
123     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
124 };
126 /** A list of strings that is used both in the preferences, and in the
127     data fields to describe the various values of \c selection_type. */
128 static const char * selection_names[SELECTION_NUMBER_OF] = {
129     "page", "drawing", "selection", "custom"};
131 /** The names on the buttons for the various selection types. */
132 static const char * selection_labels[SELECTION_NUMBER_OF] = {
133     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
135 static void
136 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
138     sp_signal_disconnect_by_data (INKSCAPE, dlg);
140     wd.win = dlg = NULL;
141     wd.stop = 0;
142     x = -1000; y = -1000; w = 0; h = 0;
143     g_free(original_name);
144     original_name = NULL;
145     g_free(doc_export_name);
146     doc_export_name = NULL;
147     was_empty = TRUE;
149     return;
150 } // end of sp_export_dialog_destroy()
152 /// Called when dialog is closed or inkscape is shut down.
153 static bool
154 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
157     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
158     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
160     if (x<0) x=0;
161     if (y<0) y=0;
163     prefs_set_int_attribute (prefs_path, "x", x);
164     prefs_set_int_attribute (prefs_path, "y", y);
165     prefs_set_int_attribute (prefs_path, "w", w);
166     prefs_set_int_attribute (prefs_path, "h", h);
168     return FALSE; // which means, go ahead and destroy it
170 } // end of sp_export_dialog_delete()
172 /**
173     \brief  Creates a new spin button for the export dialog
174     \param  key  The name of the spin button
175     \param  val  A default value for the spin button
176     \param  min  Minimum value for the spin button
177     \param  max  Maximum value for the spin button
178     \param  step The step size for the spin button
179     \param  page Size of the page increment
180     \param  us   Unit selector that effects this spin button
181     \param  t    Table to put the spin button in
182     \param  x    X location in the table \c t to start with
183     \param  y    Y location in the table \c t to start with
184     \param  ll   Text to put on the left side of the spin button (optional)
185     \param  lr   Text to put on the right side of the spin button (optional)
186     \param  digits  Number of digits to display after the decimal
187     \param  sensitive  Whether the spin button is sensitive or not
188     \param  cb   Callback for when this spin button is changed (optional)
189     \param  dlg  Export dialog the spin button is being placed in
191 */
192 static void
193 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
194                            float step, float page, GtkWidget *us,
195                            GtkWidget *t, int x, int y,
196                            const gchar *ll, const gchar *lr,
197                            int digits, unsigned int sensitive,
198                            GCallback cb, GtkWidget *dlg )
200     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
201     gtk_object_set_data (a, "key", key);
202     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
204     if (us) {
205         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
206                                           GTK_ADJUSTMENT (a) );
207     }
209     int pos = 0;
211     GtkWidget *l = NULL;
213     if (ll) {
215         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
216         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
217         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
218                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
219         gtk_widget_set_sensitive (l, sensitive);
220         pos += 1;
222     }
224     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
225     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
226                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
227     gtk_widget_set_size_request (sb, 80, -1);
228     gtk_widget_set_sensitive (sb, sensitive);
229     pos += 1;
231     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
233     if (lr) {
235         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
236         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
237         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
238                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
239         gtk_widget_set_sensitive (l, sensitive);
240         pos += 1;
242         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
243     }
245     if (cb)
246         gtk_signal_connect (a, "value_changed", cb, dlg);
248     return;
249 } // end of sp_export_spinbutton_new()
252 static Gtk::VBox *
253 sp_export_dialog_area_box (GtkWidget * dlg)
255     Gtk::VBox* vb = new Gtk::VBox(false, 3);
257     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
258     lbl->set_use_markup(true);
259     vb->pack_start(*lbl);
261     /* Units box */
262     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
263     /* gets added to the vbox later, but the unit selector is needed
264        earlier than that */
266     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
267     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
268     if (desktop)
269         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
270     unitbox->pack_end(*us, false, false, 0);
271     Gtk::Label* l = new Gtk::Label(_("Units:"));
272     unitbox->pack_end(*l, false, false, 3);
273     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
275     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
277     Gtk::ToggleButton* b;
278     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
279         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
280         b->set_data("key", GINT_TO_POINTER(i));
281         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
282         togglebox->pack_start(*b, false, true, 0);
283         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
284                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
285     }
287     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
288                        G_CALLBACK (sp_export_selection_changed), dlg );
289     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
290                        G_CALLBACK (sp_export_selection_modified), dlg );
291     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
292                        G_CALLBACK (sp_export_selection_changed), dlg );
293     
294     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
295     t->set_row_spacings (4);
296     t->set_col_spacings (4);
298     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
299                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
300                                G_CALLBACK ( sp_export_area_x_value_changed), 
301                                dlg );
303     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
304                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
305                                G_CALLBACK (sp_export_area_x_value_changed), 
306                                dlg );
308     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
309                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
310                                G_CALLBACK 
311                                    (sp_export_area_width_value_changed), 
312                                dlg );
314     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
315                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
316                                G_CALLBACK (sp_export_area_y_value_changed), 
317                                dlg );
319     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
320                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
321                                G_CALLBACK (sp_export_area_y_value_changed), 
322                                dlg );
324     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
325                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
326                                G_CALLBACK (sp_export_area_height_value_changed), 
327                                dlg );
329     vb->pack_start(*togglebox, false, false, 3);
330     vb->pack_start(*t, false, false, 0);
331     vb->pack_start(*unitbox, false, false, 0);
333     return vb;
334 } // end of sp_export_dialog_area_box
337 void
338 sp_export_dialog (void)
340     if (!dlg) {
341         Gtk::VBox* vb;
342         Gtk::HBox* hb;
344         gchar title[500];
345         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
347         dlg = sp_window_new (title, TRUE);
349         if (x == -1000 || y == -1000) {
350             x = prefs_get_int_attribute (prefs_path, "x", 0);
351             y = prefs_get_int_attribute (prefs_path, "y", 0);
352         }
354         if (w ==0 || h == 0) {
355             w = prefs_get_int_attribute (prefs_path, "w", 0);
356             h = prefs_get_int_attribute (prefs_path, "h", 0);
357         }
359         if (x<0) x=0;
360         if (y<0) y=0;
362         if (x != 0 || y != 0) {
363             gtk_window_move ((GtkWindow *) dlg, x, y);
364         } else {
365             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
366         }
368         if (w && h)
369             gtk_window_resize ((GtkWindow *) dlg, w, h);
371         sp_transientize (dlg);
372         wd.win = dlg;
373         wd.stop = 0;
375         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
376                              G_CALLBACK (sp_transientize_callback), &wd);
378         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
379                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
381         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
382                              G_CALLBACK (sp_export_dialog_destroy), dlg);
384         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
385                              G_CALLBACK (sp_export_dialog_delete), dlg);
387         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
388                              G_CALLBACK (sp_export_dialog_delete), dlg);
390         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
391                              G_CALLBACK (sp_dialog_hide), dlg);
393         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
394                              G_CALLBACK (sp_dialog_unhide), dlg);
396         GtkTooltips *tt = gtk_tooltips_new();
398         vb = new Gtk::VBox(false, 3);
399         vb->set_border_width(3);
400         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
402         /* Export area frame */
403         {
404             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
405             area_box->set_border_width(3);
406             vb->pack_start(*area_box, false, false, 0);
407         }
409         /* Bitmap size frame */
410         {
411             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
412             size_box->set_border_width(3);
414             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
415             lbl->set_use_markup(true);
416             size_box->pack_start(*lbl, false, false, 0);
417             const int rows = 2;
418             const int cols = 5;
419             const bool homogeneous = false;
420             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
421             t->set_row_spacings (4);
422             t->set_col_spacings (4);
423             size_box->pack_start(*t);
425             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
426                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
427                                        _("_Width:"), _("pixels at"), 0, 1,
428                                        G_CALLBACK 
429                                        (sp_export_bitmap_width_value_changed), 
430                                        dlg );
432             sp_export_spinbutton_new ( "xdpi", 
433                                        prefs_get_double_attribute 
434                                        ( "dialogs.export.defaultxdpi", 
435                                          "value", DPI_BASE), 
436                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
437                                        NULL, _("dp_i"), 2, 1,
438                                        G_CALLBACK (sp_export_xdpi_value_changed), 
439                                        dlg );
441             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
442                                        NULL, GTK_WIDGET(t->gobj()), 0, 1, 
443                                        _("Height:"), _("pixels at"), 0, 1, 
444                                        G_CALLBACK
445                                        (sp_export_bitmap_height_value_changed), 
446                                        dlg );
448             /** \todo
449              * Needs fixing: there's no way to set ydpi currently, so we use  
450              *       the defaultxdpi value here, too...
451              */
452             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
453                                        ( "dialogs.export.defaultxdpi", 
454                                          "value", DPI_BASE), 
455                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
456                                        NULL, _("dpi"), 2, 0, NULL, dlg );
458             vb->pack_start(*size_box);
459         }
461         /* File entry */
462         {
463             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
464             file_box->set_border_width(3);
466             // true = has mnemonic
467             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
468             flabel->set_use_markup(true);
469             file_box->pack_start(*flabel, false, false, 0);
471             Gtk::Entry *fe = new Gtk::Entry();
473             /*
474              * set the default filename to be that of the current path + document
475              * with .png extension
476              *
477              * One thing to notice here is that this filename may get
478              * overwritten, but it won't happen here.  The filename gets
479              * written into the text field, but then the button to select
480              * the area gets set.  In that code the filename can be changed
481              * if there are some with presidence in the document.  So, while
482              * this code sets the name first, it may not be the one users
483              * really see.
484              */
485             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
486             {
487                 gchar *name;
488                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
489                 const gchar *uri = SP_DOCUMENT_URI (doc);
490                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
491                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
492                 Inkscape::Extension::Output * oextension = NULL;
494                 if (text_extension != NULL) {
495                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
496                 }
498                 if (oextension != NULL) {
499                     gchar * old_extension = oextension->get_extension();
500                     if (g_str_has_suffix(uri, old_extension)) {
501                         gchar * uri_copy;
502                         gchar * extension_point;
503                         gchar * final_name;
505                         uri_copy = g_strdup(uri);
506                         extension_point = g_strrstr(uri_copy, old_extension);
507                         extension_point[0] = '\0';
509                         final_name = g_strconcat(uri_copy, ".png", NULL);
510                         fe->set_text(final_name);
512                         g_free(final_name);
513                         g_free(uri_copy);
514                     }
515                 } else {
516                     name = g_strconcat(uri, ".png", NULL);
517                     fe->set_text(name);
518                     g_free(name);
519                 }
521                 doc_export_name = g_strdup(fe->get_text().c_str());
522             }
523             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
524                                G_CALLBACK (sp_export_filename_modified), dlg);
526             hb = new Gtk::HBox(FALSE, 5);
528             {
529                 // true = has mnemonic
530                 Gtk::Button *b = new Gtk::Button();
532                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
533                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::DIRECTORY),
534                         Gtk::ICON_SIZE_BUTTON);
535                 pixlabel->pack_start(*im);
537                 Gtk::Label *l = new Gtk::Label();
538                 l->set_markup_with_mnemonic(_("_Browse..."));
539                 pixlabel->pack_start(*l);
541                 b->add(*pixlabel);
543                 hb->pack_end (*b, false, false, 4);
544                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
545                                    G_CALLBACK (sp_export_browse_clicked), NULL );
546             }
548             hb->pack_start (*fe, true, true, 0);
549             file_box->add(*hb);
550             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
551             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
552             original_name = g_strdup(fe->get_text().c_str());
553             // pressing enter in the filename field is the same as clicking export:
554             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
555                                G_CALLBACK (sp_export_export_clicked), dlg );
556             // focus is in the filename initially:
557             fe->grab_focus();
559             // mnemonic in frame label moves focus to filename:
560             flabel->set_mnemonic_widget(*fe);
562             vb->pack_start(*file_box);
563         }
565         /* Buttons */
566         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
567         bb->set_border_width(3);
569         {
570             Gtk::Button *b = new Gtk::Button();
571             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
572             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
573                     Gtk::ICON_SIZE_BUTTON);
574             image_label->pack_start(*im);
576             Gtk::Label *l = new Gtk::Label();
577             l->set_markup_with_mnemonic(_("_Export"));
578             image_label->pack_start(*l);
580             b->add(*image_label);
581             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
582             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
583                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
584             bb->pack_end(*b, false, false, 0);
585         }
587         vb->pack_end(*bb, false, false, 0);
588         vb->show_all();
590     } // end of if (!dlg)
592     sp_export_find_default_selection(dlg);
594     gtk_window_present ((GtkWindow *) dlg);
596     return;
597 } // end of sp_export_dialog()
599 static inline void
600 sp_export_find_default_selection(GtkWidget * dlg)
602     selection_type key = SELECTION_NUMBER_OF;
604     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
605         key = SELECTION_SELECTION;
606     }
608     /* Try using the preferences */
609     if (key == SELECTION_NUMBER_OF) {
610         const gchar *what = NULL;
611         int i = SELECTION_NUMBER_OF;
613         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
615         if (what != NULL) {
616             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
617                 if (!strcmp (what, selection_names[i])) {
618                     break;
619                 }
620             }
621         }
623         key = (selection_type)i;
624     }
626     if (key == SELECTION_NUMBER_OF) {
627         key = SELECTION_SELECTION;
628     }
630     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
631                                                        selection_names[key]);
632     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
634     return;
638 /**
639  * \brief  If selection changed or a different document activated, we must 
640  * recalculate any chosen areas
641  *
642  */
643 static void
644 sp_export_selection_changed ( Inkscape::Application *inkscape, 
645                               Inkscape::Selection *selection, 
646                               GtkObject *base )
648     selection_type current_key;
649     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
651     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
652             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
653             was_empty) {
654         gtk_toggle_button_set_active
655             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
656               TRUE );
657     }
658     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
660     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
662     if (inkscape &&
663             SP_IS_INKSCAPE (inkscape) &&
664             selection &&
665             SELECTION_CUSTOM != current_key) {
666         GtkToggleButton * button;
667         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
668         sp_export_area_toggled(button, base);
669     } // end of if()
671     return;
672 } // end of sp_export_selection_changed()
674 static void
675 sp_export_selection_modified ( Inkscape::Application *inkscape, 
676                                Inkscape::Selection *selection, 
677                                guint flags,
678                                GtkObject *base )
680     selection_type current_key;
681     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
683     switch (current_key) {
684         case SELECTION_DRAWING:
685             if ( SP_ACTIVE_DESKTOP ) {
686                 SPDocument *doc;
687                 NRRect bbox;
688                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
689                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
691                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
692                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
693                 }
694             }
695             break;
696         case SELECTION_SELECTION:
697             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
698                 NRRect bbox;
699                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
700                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
701             }
702             break;
703         default:
704             /* Do nothing for page or for custom */
705             break;
706     }
708     return;
711 /// Called when one of the selection buttons was toggled.
712 static void
713 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
715     if (gtk_object_get_data (base, "update"))
716         return;
718     selection_type key, old_key;
719     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
720     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
722     /* Ignore all "turned off" events unless we're the only active button */
723     if (!gtk_toggle_button_get_active (tb) ) {
725         /* Don't let the current selection be deactived - but rerun the
726            activate to allow the user to renew the values */
727         if (key == old_key) {
728             gtk_toggle_button_set_active ( tb, TRUE );
729         }
731         return;
732     }
734     /* Turn off the currently active button unless it's us */
735     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
737     if (old_key != key) {
738         gtk_toggle_button_set_active
739             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
740               FALSE );
741     }
743     if ( SP_ACTIVE_DESKTOP )
744     {
745         SPDocument *doc;
746         NRRect bbox;
747         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
749         /* Notice how the switch is used to 'fall through' here to get
750            various backups.  If you modify this without noticing you'll
751            probabaly screw something up. */
752         switch (key) {
753             case SELECTION_SELECTION:
754                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
755                 {
756                     (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
757                     /* Only if there is a selection that we can set
758                        do we break, otherwise we fall through to the
759                        drawing */
760                     // std::cout << "Using selection: SELECTION" << std::endl;
761                     key = SELECTION_SELECTION;
762                     break;
763                 }
764             case SELECTION_DRAWING:
765                 /** \todo 
766                  * This returns wrong values if the document has a viewBox.
767                  */
768                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
769                 
770                 /* If the drawing is valid, then we'll use it and break
771                    otherwise we drop through to the page settings */
772                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
773                     // std::cout << "Using selection: DRAWING" << std::endl;
774                     key = SELECTION_DRAWING;
775                     break;
776                 }
777             case SELECTION_PAGE:
778                 bbox.x0 = 0.0;
779                 bbox.y0 = 0.0;
780                 bbox.x1 = sp_document_width (doc);
781                 bbox.y1 = sp_document_height (doc);
782                 // std::cout << "Using selection: PAGE" << std::endl;
783                 key = SELECTION_PAGE;
784                 break;
785             case SELECTION_CUSTOM:
786             default:
787                 break;
788         } // switch
789         
790         // remember area setting
791         prefs_set_string_attribute ( "dialogs.export.exportarea", 
792                                      "value", selection_names[key]);
794         if (key != SELECTION_CUSTOM) {
795             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
796         }
797     
798     } // end of if ( SP_ACTIVE_DESKTOP )
801     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
802         GtkWidget * file_entry;
803         const gchar * filename = NULL;
804         float xdpi = 0.0, ydpi = 0.0;
806         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
808         switch (key) {
809             case SELECTION_PAGE:
810             case SELECTION_DRAWING: {
811                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
812                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
813                 const gchar * dpi_string;
815                 filename = repr->attribute("inkscape:export-filename");
817                 dpi_string = NULL;
818                 dpi_string = repr->attribute("inkscape:export-xdpi");
819                 if (dpi_string != NULL) {
820                     xdpi = atof(dpi_string);
821                 }
823                 dpi_string = NULL;
824                 dpi_string = repr->attribute("inkscape:export-ydpi");
825                 if (dpi_string != NULL) {
826                     ydpi = atof(dpi_string);
827                 }
829                 if (filename == NULL) {
830                     if (doc_export_name != NULL) {
831                         filename = g_strdup(doc_export_name);
832                     } else {
833                         filename = g_strdup("");
834                     }
835                 }
836                 break;
837             }
838             case SELECTION_SELECTION:
839                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
840                     const GSList * reprlst;
841                     bool filename_search = TRUE;
842                     bool xdpi_search = TRUE;
843                     bool ydpi_search = TRUE;
845                     reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
846                     for(; reprlst != NULL &&
847                             filename_search &&
848                             xdpi_search &&
849                             ydpi_search;
850                             reprlst = reprlst->next) {
851                         const gchar * dpi_string;
852                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
854                         if (filename_search) {
855                             filename = repr->attribute("inkscape:export-filename");
856                             if (filename != NULL)
857                                 filename_search = FALSE;
858                         }
860                         if (xdpi_search) {
861                             dpi_string = NULL;
862                             dpi_string = repr->attribute("inkscape:export-xdpi");
863                             if (dpi_string != NULL) {
864                                 xdpi = atof(dpi_string);
865                                 xdpi_search = FALSE;
866                             }
867                         }
869                         if (ydpi_search) {
870                             dpi_string = NULL;
871                             dpi_string = repr->attribute("inkscape:export-ydpi");
872                             if (dpi_string != NULL) {
873                                 ydpi = atof(dpi_string);
874                                 ydpi_search = FALSE;
875                             }
876                         }
877                     }
879                     /* If we still don't have a filename -- let's build
880                        one that's nice */
881                     if (filename == NULL) {
882                         const gchar * id = NULL;
883                         reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
884                         for(; reprlst != NULL; reprlst = reprlst->next) {
885                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
886                             if (repr->attribute("id")) {
887                                 id = repr->attribute("id");
888                                 break;
889                             }
890                         }
891                         if (id == NULL) /* This should never happen */
892                             id = "bitmap";
894                         gchar * directory = NULL;
895                         const gchar * file_entry_text;
897                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
898                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
899                             // std::cout << "Directory from dialog" << std::endl;
900                             directory = g_dirname(file_entry_text);
901                         }
903                         if (directory == NULL) {
904                             /* Grab document directory */
905                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
906                                 // std::cout << "Directory from document" << std::endl;
907                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
908                             }
909                         }
911                         if (directory == NULL) {
912                             // std::cout << "Home Directory" << std::endl;
913                             directory = homedir_path(NULL);
914                         }
916                         gchar * id_ext = g_strconcat(id, ".png", NULL);
917                         filename = g_build_filename(directory, id_ext, NULL);
918                         g_free(directory);
919                         g_free(id_ext);
920                     }
921                 }
922                 break;
923             case SELECTION_CUSTOM:
924             default:
925                 break;
926         }
928         if (filename != NULL) {
929             g_free(original_name);
930             original_name = g_strdup(filename);
931             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
932         }
934         if (xdpi != 0.0) {
935             sp_export_value_set(base, "xdpi", xdpi);
936         }
938         /* These can't be seperate, and setting x sets y, so for
939            now setting this is disabled.  Hopefully it won't be in
940            the future */
941         if (FALSE && ydpi != 0.0) {
942             sp_export_value_set(base, "ydpi", ydpi);
943         }
944     }
946     return;
947 } // end of sp_export_area_toggled()
949 /// Called when dialog is deleted
950 static gint
951 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
953     g_object_set_data (base, "cancel", (gpointer) 1);
954     return TRUE;
955 } // end of sp_export_progress_delete()
957 /// Called when progress is cancelled
958 static void
959 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
961     g_object_set_data (base, "cancel", (gpointer) 1);
962 } // end of sp_export_progress_cancel()
964 /// Called for every progress iteration
965 static unsigned int
966 sp_export_progress_callback (float value, void *data)
968     GtkWidget *prg;
969     int evtcount;
971     if (g_object_get_data ((GObject *) data, "cancel"))
972         return FALSE;
974     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
975     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
977     evtcount = 0;
978     while ((evtcount < 16) && gdk_events_pending ()) {
979             gtk_main_iteration_do (FALSE);
980             evtcount += 1;
981     }
983     gtk_main_iteration_do (FALSE);
985     return TRUE;
987 } // end of sp_export_progress_callback()
989 /// Called when export button is clicked
990 static void
991 sp_export_export_clicked (GtkButton *button, GtkObject *base)
993     if (!SP_ACTIVE_DESKTOP) return;
995     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
996     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
998     float const x0 = sp_export_value_get_px(base, "x0");
999     float const y0 = sp_export_value_get_px(base, "y0");
1000     float const x1 = sp_export_value_get_px(base, "x1");
1001     float const y1 = sp_export_value_get_px(base, "y1");
1002     float const xdpi = sp_export_value_get(base, "xdpi");
1003     float const ydpi = sp_export_value_get(base, "ydpi");
1004     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1005     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1007     if (filename == NULL || *filename == '\0') {
1008         sp_ui_error_dialog(_("You have to enter a filename"));
1009         return;
1010     }
1012     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1013         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1014         return;
1015     }
1017     gchar *dirname = g_dirname(filename);
1018     if ( dirname == NULL
1019          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1020     {
1021         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1022         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1023                                        safeDir);
1024         sp_ui_error_dialog(error);
1025         g_free(safeDir);
1026         g_free(error);
1027         g_free(dirname);
1028         return;
1029     }
1030     g_free(dirname);
1032     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1033     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1034     char *fn;
1035     gchar *text;
1037     dlg = gtk_dialog_new ();
1038     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1039     prg = gtk_progress_bar_new ();
1040     sp_transientize (dlg);
1041     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1042     g_object_set_data ((GObject *) base, "progress", prg);
1043     fn = g_path_get_basename (filename);
1044     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1045                              fn, width, height);
1046     g_free (fn);
1047     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1048     g_free (text);
1049     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1050                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1051     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1052                         prg, FALSE, FALSE, 4 );
1053     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1054                                   GTK_STOCK_CANCEL, 
1055                                   GTK_RESPONSE_CANCEL );
1056                                   
1057     g_signal_connect ( (GObject *) dlg, "delete_event", 
1058                        (GCallback) sp_export_progress_delete, base);
1059     g_signal_connect ( (GObject *) btn, "clicked", 
1060                        (GCallback) sp_export_progress_cancel, base);
1061     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1062     gtk_widget_show_all (dlg);
1063     
1064     /* Do export */
1065     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1066                              x0, y0, x1, y1, width, height, 
1067                              nv->pagecolor, 
1068                              sp_export_progress_callback, base)) {
1069         gchar * error;
1070         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1071         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1072         sp_ui_error_dialog(error);
1073         g_free(safeFile);
1074         g_free(error);
1075     }
1077     /* Reset the filename so that it can be changed again by changing
1078        selections and all that */
1079     g_free(original_name);
1080     original_name = g_strdup(filename);
1081     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1083     gtk_widget_destroy (dlg);
1084     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1086     /* Setup the values in the document */
1087     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1088         case SELECTION_PAGE:
1089         case SELECTION_DRAWING: {
1090             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1091             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1092             bool modified = FALSE;
1093             const gchar * temp_string;
1095             bool saved = sp_document_get_undo_sensitive(doc);
1096             sp_document_set_undo_sensitive(doc, FALSE);
1098             temp_string = repr->attribute("inkscape:export-filename");
1099             if (temp_string == NULL || strcmp(temp_string, filename)) {
1100                 repr->setAttribute("inkscape:export-filename", filename);
1101                 modified = TRUE;
1102             }
1103             temp_string = repr->attribute("inkscape:export-xdpi");
1104             if (temp_string == NULL || xdpi != atof(temp_string)) {
1105                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1106                 modified = TRUE;
1107             }
1108             temp_string = repr->attribute("inkscape:export-ydpi");
1109             if (temp_string == NULL || xdpi != atof(temp_string)) {
1110                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1111                 modified = TRUE;
1112             }
1114             if (modified)
1115                 repr->setAttribute("sodipodi:modified", "TRUE");
1116             sp_document_set_undo_sensitive(doc, saved);
1117             break;
1118         }
1119         case SELECTION_SELECTION: {
1120             const GSList * reprlst;
1121             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1122             bool modified = FALSE;
1124             bool saved = sp_document_get_undo_sensitive(doc);
1125             sp_document_set_undo_sensitive(doc, FALSE);
1126             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1128             for(; reprlst != NULL; reprlst = reprlst->next) {
1129                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1130                 const gchar * temp_string;
1132                 if (repr->attribute("id") == NULL ||
1133                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1134                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1135                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1136                     temp_string = repr->attribute("inkscape:export-filename");
1137                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1138                         repr->setAttribute("inkscape:export-filename", filename);
1139                         modified = TRUE;
1140                     }
1141                 }
1142                 temp_string = repr->attribute("inkscape:export-xdpi");
1143                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1144                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1145                     modified = TRUE;
1146                 }
1147                 temp_string = repr->attribute("inkscape:export-ydpi");
1148                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1149                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1150                     modified = TRUE;
1151                 }
1152             }
1154             if (modified) {
1155                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1156                 repr->setAttribute("sodipodi:modified", "TRUE");
1157             }
1159             sp_document_set_undo_sensitive(doc, saved);
1160             break;
1161         }
1162         default:
1163             break;
1164     }
1167     return;
1168 } // end of sp_export_export_clicked()
1170 /// Called when Browse button is clicked
1171 static void
1172 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1174     GtkWidget *fs, *fe;
1175     const gchar *filename;
1177     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1178                                       NULL,
1179                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1180                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1181                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1182                                       NULL );
1184     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1186     sp_transientize (fs);
1188     gtk_window_set_modal(GTK_WINDOW (fs), true);
1190     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1192     if (*filename == '\0') {
1193         filename = homedir_path(NULL);
1194     }
1196     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1198     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1199     {
1200         gchar *file;
1202         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1203         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1204         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1205         g_free(utf8file);
1207         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1208         g_free(file);
1209     }
1211     gtk_widget_destroy (fs);
1213     return;
1214 } // end of sp_export_browse_clicked()
1216 // TODO: Move this to nr-rect-fns.h.
1217 static bool
1218 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1219
1220     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1221     return (
1222         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1223         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1224         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1225         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1226         );
1229 /**
1230     \brief  This function is used to detect the current selection setting
1231             based on the values in the x0, y0, x1 and y0 fields.
1232     \param  base  The export dialog itself
1234     One of the most confusing parts of this function is why the array
1235     is built at the beginning.  What needs to happen here is that we
1236     should always check the current selection to see if it is the valid
1237     one.  While this is a performance improvement it is also a usability
1238     one during the cases where things like selections and drawings match
1239     size.  This way buttons change less 'randomly' (atleast in the eyes
1240     of the user).  To do this an array is built where the current selection
1241     type is placed first, and then the others in an order from smallest
1242     to largest (this can be configured by reshuffling \c test_order).
1244     All of the values in this function are rounded to two decimal places
1245     because that is what is shown to the user.  While everything is kept
1246     more accurate than that, the user can't control more acurrate than
1247     that, so for this to work for them - it needs to check on that level
1248     of accuracy.
1250     \todo finish writing this up
1251 */
1252 static void
1253 sp_export_detect_size(GtkObject * base) {
1254     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1255     selection_type this_test[SELECTION_NUMBER_OF + 1];
1256     selection_type key = SELECTION_NUMBER_OF;
1258     NR::Point x(sp_export_value_get_px (base, "x0"),
1259                 sp_export_value_get_px (base, "y0"));
1260     NR::Point y(sp_export_value_get_px (base, "x1"),
1261                 sp_export_value_get_px (base, "y1"));
1262     NR::Rect current_bbox(x, y);
1263     //std::cout << "Current " << current_bbox;
1265     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1266     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1267         this_test[i + 1] = test_order[i];
1268     }
1270     for (int i = 0;
1271             i < SELECTION_NUMBER_OF + 1 &&
1272                 key == SELECTION_NUMBER_OF &&
1273                 SP_ACTIVE_DESKTOP != NULL;
1274             i++) {
1275         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1276         switch (this_test[i]) {
1277             case SELECTION_SELECTION:
1278                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1279                     NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1281                     //std::cout << "Selection " << bbox;
1282                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1283                         key = SELECTION_SELECTION;
1284                     }
1285                 }
1286                 break;
1287             case SELECTION_DRAWING: {
1288                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1290                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1292                 // std::cout << "Drawing " << bbox2;
1293                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1294                     key = SELECTION_DRAWING;
1295                 }
1296                 break;
1297             }
1299             case SELECTION_PAGE: {
1300                 SPDocument *doc;
1302                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1304                 NR::Point x(0.0, 0.0);
1305                 NR::Point y(sp_document_width(doc),
1306                             sp_document_height(doc));
1307                 NR::Rect bbox(x, y);
1309                 // std::cout << "Page " << bbox;
1310                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1311                     key = SELECTION_PAGE;
1312                 }
1314                 break;
1315            }
1316         default:
1317            break;
1318         }
1319     }
1320     // std::cout << std::endl;
1322     if (key == SELECTION_NUMBER_OF) {
1323         key = SELECTION_CUSTOM;
1324     }
1326     /* We're now using a custom size, not a fixed one */
1327     /* printf("Detecting state: %s\n", selection_names[key]); */
1328     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1329     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1330     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1331     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1333     return;
1334 } /* sp_export_detect_size */
1336 /// Called when area x0 value is changed
1337 static void
1338 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1340     float x0, x1, xdpi, width;
1342     if (gtk_object_get_data (base, "update"))
1343         return;
1345     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1346             (base, "units")))
1347     {
1348         return;
1349     }
1351     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1353     x0 = sp_export_value_get_px (base, "x0");
1354     x1 = sp_export_value_get_px (base, "x1");
1355     xdpi = sp_export_value_get (base, "xdpi");
1357     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1359     if (width < SP_EXPORT_MIN_SIZE) {
1360         const gchar *key;
1361         width = SP_EXPORT_MIN_SIZE;
1362         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1364         if (!strcmp (key, "x0")) {
1365             x1 = x0 + width * DPI_BASE / xdpi;
1366             sp_export_value_set_px (base, "x1", x1);
1367         } else {
1368             x0 = x1 - width * DPI_BASE / xdpi;
1369             sp_export_value_set_px (base, "x0", x0);
1370         }
1371     }
1373     sp_export_value_set_px (base, "width", x1 - x0);
1374     sp_export_value_set (base, "bmwidth", width);
1376     sp_export_detect_size(base);
1378     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1380     return;
1381 } // end of sp_export_area_x_value_changed()
1383 /// Called when area y0 value is changed.
1384 static void
1385 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1387     float y0, y1, ydpi, height;
1389     if (gtk_object_get_data (base, "update"))
1390         return;
1392     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1393            (base, "units")))
1394     {
1395         return;
1396     }
1398     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1400     y0 = sp_export_value_get_px (base, "y0");
1401     y1 = sp_export_value_get_px (base, "y1");
1402     ydpi = sp_export_value_get (base, "ydpi");
1404     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1406     if (height < SP_EXPORT_MIN_SIZE) {
1407         const gchar *key;
1408         height = SP_EXPORT_MIN_SIZE;
1409         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1410         if (!strcmp (key, "y0")) {
1411             y1 = y0 + height * DPI_BASE / ydpi;
1412             sp_export_value_set_px (base, "y1", y1);
1413         } else {
1414             y0 = y1 - height * DPI_BASE / ydpi;
1415             sp_export_value_set_px (base, "y0", y0);
1416         }
1417     }
1419     sp_export_value_set_px (base, "height", y1 - y0);
1420     sp_export_value_set (base, "bmheight", height);
1422     sp_export_detect_size(base);
1424     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1426     return;
1427 } // end of sp_export_area_y_value_changed()
1429 /// Called when x1-x0 or area width is changed
1430 static void
1431 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1433     float x0, x1, xdpi, width, bmwidth;
1435     if (gtk_object_get_data (base, "update"))
1436         return;
1438     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1439            (base, "units"))) {
1440         return;
1441     }
1443     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1445     x0 = sp_export_value_get_px (base, "x0");
1446     x1 = sp_export_value_get_px (base, "x1");
1447     xdpi = sp_export_value_get (base, "xdpi");
1448     width = sp_export_value_get_px (base, "width");
1449     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1451     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1453         bmwidth = SP_EXPORT_MIN_SIZE;
1454         width = bmwidth * DPI_BASE / xdpi;
1455         sp_export_value_set_px (base, "width", width);
1456     }
1458     sp_export_value_set_px (base, "x1", x0 + width);
1459     sp_export_value_set (base, "bmwidth", bmwidth);
1461     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1463     return;
1464 } // end of sp_export_area_width_value_changed()
1466 /// Called when y1-y0 or area height is changed.
1467 static void
1468 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1471     float y0, y1, ydpi, height, bmheight;
1473     if (gtk_object_get_data (base, "update"))
1474         return;
1476     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1477            (base, "units"))) {
1478         return;
1479     }
1481     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1483     y0 = sp_export_value_get_px (base, "y0");
1484     y1 = sp_export_value_get_px (base, "y1");
1485     ydpi = sp_export_value_get (base, "ydpi");
1486     height = sp_export_value_get_px (base, "height");
1487     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1489     if (bmheight < SP_EXPORT_MIN_SIZE) {
1490         bmheight = SP_EXPORT_MIN_SIZE;
1491         height = bmheight * DPI_BASE / ydpi;
1492         sp_export_value_set_px (base, "height", height);
1493     }
1495     sp_export_value_set_px (base, "y1", y0 + height);
1496     sp_export_value_set (base, "bmheight", bmheight);
1498     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1500     return;
1501 } // end of sp_export_area_height_value_changed()
1503 /**
1504     \brief  A function to set the ydpi
1505     \param  base  The export dialog
1507     This function grabs all of the y values and then figures out the
1508     new bitmap size based on the changing dpi value.  The dpi value is
1509     gotten from the xdpi setting as these can not currently be independent.
1510 */
1511 static void
1512 sp_export_set_image_y (GtkObject *base)
1514     float y0, y1, xdpi;
1516     y0 = sp_export_value_get_px (base, "y0");
1517     y1 = sp_export_value_get_px (base, "y1");
1518     xdpi = sp_export_value_get (base, "xdpi");
1520     sp_export_value_set (base, "ydpi", xdpi);
1521     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1523     return;
1524 } // end of sp_export_set_image_y()
1526 /**
1527     \brief  A function to set the xdpi
1528     \param  base  The export dialog
1530     This function grabs all of the x values and then figures out the
1531     new bitmap size based on the changing dpi value.  The dpi value is
1532     gotten from the xdpi setting as these can not currently be independent.
1533 */
1534 static void
1535 sp_export_set_image_x (GtkObject *base)
1537     float x0, x1, xdpi;
1539     x0 = sp_export_value_get_px (base, "x0");
1540     x1 = sp_export_value_get_px (base, "x1");
1541     xdpi = sp_export_value_get (base, "xdpi");
1543     sp_export_value_set (base, "ydpi", xdpi);
1544     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1546     return;
1547 } // end of sp_export_set_image_x()
1549 /// Called when pixel width is changed
1550 static void
1551 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1553     float x0, x1, bmwidth, xdpi;
1555     if (gtk_object_get_data (base, "update"))
1556         return;
1558     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1559            (base, "units"))) {
1560        return;
1561     }
1563     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1565     x0 = sp_export_value_get_px (base, "x0");
1566     x1 = sp_export_value_get_px (base, "x1");
1567     bmwidth = sp_export_value_get (base, "bmwidth");
1569     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1570         bmwidth = SP_EXPORT_MIN_SIZE;
1571         sp_export_value_set (base, "bmwidth", bmwidth);
1572     }
1574     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1575     sp_export_value_set (base, "xdpi", xdpi);
1577     sp_export_set_image_y (base);
1579     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1581     return;
1582 } // end of sp_export_bitmap_width_value_changed()
1584 /// Called when pixel height is changed
1585 static void
1586 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1588     float y0, y1, bmheight, xdpi;
1590     if (gtk_object_get_data (base, "update"))
1591         return;
1593     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1594            (base, "units"))) {
1595        return;
1596     }
1598     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1600     y0 = sp_export_value_get_px (base, "y0");
1601     y1 = sp_export_value_get_px (base, "y1");
1602     bmheight = sp_export_value_get (base, "bmheight");
1604     if (bmheight < SP_EXPORT_MIN_SIZE) {
1605         bmheight = SP_EXPORT_MIN_SIZE;
1606         sp_export_value_set (base, "bmheight", bmheight);
1607     }
1609     xdpi = bmheight * DPI_BASE / (y1 - y0);
1610     sp_export_value_set (base, "xdpi", xdpi);
1612     sp_export_set_image_x (base);
1614     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1616     return;
1617 } // end of sp_export_bitmap_width_value_changed()
1619 /**
1620     \brief  A function to adjust the bitmap width when the xdpi value changes
1621     \param  adj  The adjustment that was changed
1622     \param  base The export dialog itself
1624     The first thing this function checks is to see if we are doing an
1625     update.  If we are, this function just returns because there is another
1626     instance of it that will handle everything for us.  If there is a
1627     units change, we also assume that everyone is being updated appropriately
1628     and there is nothing for us to do.
1630     If we're the highest level function, we set the update flag, and
1631     continue on our way.
1633     All of the values are grabbed using the \c sp_export_value_get functions
1634     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1635     xdpi value is saved in the preferences for the next time the dialog
1636     is opened.  (does the selection dpi need to be set here?)
1638     A check is done to to ensure that we aren't outputing an invalid width,
1639     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1640     changed to make it valid.
1642     After all of this the bitmap width is changed.
1644     We also change the ydpi.  This is a temporary hack as these can not
1645     currently be independent.  This is likely to change in the future.
1646 */
1647 void
1648 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1650     float x0, x1, xdpi, bmwidth;
1652     if (gtk_object_get_data (base, "update"))
1653         return;
1655     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1656            (base, "units"))) {
1657        return;
1658     }
1660     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1662     x0 = sp_export_value_get_px (base, "x0");
1663     x1 = sp_export_value_get_px (base, "x1");
1664     xdpi = sp_export_value_get (base, "xdpi");
1666     // remember xdpi setting
1667     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1669     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1671     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1672         bmwidth = SP_EXPORT_MIN_SIZE;
1673         if (x1 != x0)
1674             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1675         else
1676             xdpi = DPI_BASE;
1677         sp_export_value_set (base, "xdpi", xdpi);
1678     }
1680     sp_export_value_set (base, "bmwidth", bmwidth);
1682     sp_export_set_image_y (base);
1684     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1686     return;
1687 } // end of sp_export_xdpi_value_changed()
1690 /**
1691     \brief  A function to change the area that is used for the exported
1692             bitmap.
1693     \param  base  This is the export dialog
1694     \param  x0    Horizontal upper left hand corner of the picture in points
1695     \param  y0    Vertical upper left hand corner of the picture in points
1696     \param  x1    Horizontal lower right hand corner of the picture in points
1697     \param  y1    Vertical lower right hand corner of the picture in points
1699     This function just calls \c sp_export_value_set_px for each of the
1700     parameters that is passed in.  This allows for setting them all in
1701     one convient area.
1703     Update is set to suspend all of the other test running while all the
1704     values are being set up.  This allows for a performance increase, but
1705     it also means that the wrong type won't be detected with only some of
1706     the values set.  After all the values are set everyone is told that
1707     there has been an update.
1708 */
1709 static void
1710 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1712     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1713     sp_export_value_set_px (base, "x1", x1);
1714     sp_export_value_set_px (base, "y1", y1);
1715     sp_export_value_set_px (base, "x0", x0);
1716     sp_export_value_set_px (base, "y0", y0);
1717     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1719     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1720     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1722     return;
1725 /**
1726     \brief  Sets the value of an adjustment
1727     \param  base  The export dialog
1728     \param  key   Which adjustment to set
1729     \param  val   What value to set it to
1731     This function finds the adjustment using the data stored in the
1732     export dialog.  After finding the adjustment it then sets
1733     the value of it.
1734 */
1735 static void
1736 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1738     GtkAdjustment *adj;
1740     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1742     gtk_adjustment_set_value (adj, val);
1745 /**
1746     \brief  A function to set a value using the units points
1747     \param  base  The export dialog
1748     \param  key   Which value should be set
1749     \param  val   What the value should be in points
1751     This function first gets the adjustment for the key that is passed
1752     in.  It then figures out what units are currently being used in the
1753     dialog.  After doing all of that, it then converts the incoming
1754     value and sets the adjustment.
1755 */
1756 static void
1757 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1759     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1761     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1763     return;
1766 /**
1767     \brief  Get the value of an adjustment in the export dialog
1768     \param  base  The export dialog
1769     \param  key   Which adjustment is being looked for
1770     \return The value in the specified adjustment
1772     This function gets the adjustment from the data field in the export
1773     dialog.  It then grabs the value from the adjustment.
1774 */
1775 static float
1776 sp_export_value_get ( GtkObject *base, const gchar *key )
1778     GtkAdjustment *adj;
1780     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1782     return adj->value;
1785 /**
1786     \brief  Grabs a value in the export dialog and converts the unit
1787             to points
1788     \param  base  The export dialog
1789     \param  key   Which value should be returned
1790     \return The value in the adjustment in points
1792     This function, at its most basic, is a call to \c sp_export_value_get
1793     to get the value of the adjustment.  It then finds the units that
1794     are being used by looking at the "units" attribute of the export
1795     dialog.  Using that it converts the returned value into points.
1796 */
1797 static float
1798 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1800     float value = sp_export_value_get(base, key);
1801     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1803     return sp_units_get_pixels (value, *unit);
1804 } // end of sp_export_value_get_px()
1806 /**
1807     \brief  This function is called when the filename is changed by
1808             anyone.  It resets the virgin bit.
1809     \param  object  Text entry box
1810     \param  data    The export dialog
1811     \return None
1813     This function gets called when the text area is modified.  It is
1814     looking for the case where the text area is modified from its
1815     original value.  In that case it sets the "filename-modified" bit
1816     to TRUE.  If the text dialog returns back to the original text, the
1817     bit gets reset.  This should stop simple mistakes.
1818 */
1819 static void
1820 sp_export_filename_modified (GtkObject * object, gpointer data)
1822     GtkWidget * text_entry = (GtkWidget *)object;
1823     GtkWidget * export_dialog = (GtkWidget *)data;
1825     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1826         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1827 //        printf("Modified: FALSE\n");
1828     } else {
1829         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1830 //        printf("Modified: TRUE\n");
1831     }
1833     return;
1834 } // end sp_export_filename_modified
1836 /*
1837   Local Variables:
1838   mode:c++
1839   c-file-style:"stroustrup"
1840   c-file-offsets:((innamespace . 0)(inline-open . 0))
1841   indent-tabs-mode:nil
1842   fill-column:99
1843   End:
1844 */
1845 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :