Code

remove deprecation warnings
[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::INDEX),
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                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
688                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
690                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
691                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
692                     sp_export_set_area (base, bbox.min()[NR::X],
693                                               bbox.min()[NR::Y],
694                                               bbox.max()[NR::X],
695                                               bbox.max()[NR::Y]);
696                 }
697             }
698             break;
699         case SELECTION_SELECTION:
700             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
701                 NRRect bbox;
702                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
703                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
704             }
705             break;
706         default:
707             /* Do nothing for page or for custom */
708             break;
709     }
711     return;
714 /// Called when one of the selection buttons was toggled.
715 static void
716 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
718     if (gtk_object_get_data (base, "update"))
719         return;
721     selection_type key, old_key;
722     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
723     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
725     /* Ignore all "turned off" events unless we're the only active button */
726     if (!gtk_toggle_button_get_active (tb) ) {
728         /* Don't let the current selection be deactived - but rerun the
729            activate to allow the user to renew the values */
730         if (key == old_key) {
731             gtk_toggle_button_set_active ( tb, TRUE );
732         }
734         return;
735     }
737     /* Turn off the currently active button unless it's us */
738     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
740     if (old_key != key) {
741         gtk_toggle_button_set_active
742             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
743               FALSE );
744     }
746     if ( SP_ACTIVE_DESKTOP )
747     {
748         SPDocument *doc;
749         NR::Rect bbox;
750         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
752         /* Notice how the switch is used to 'fall through' here to get
753            various backups.  If you modify this without noticing you'll
754            probabaly screw something up. */
755         switch (key) {
756             case SELECTION_SELECTION:
757                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
758                 {
759                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
760                     /* Only if there is a selection that we can set
761                        do we break, otherwise we fall through to the
762                        drawing */
763                     // std::cout << "Using selection: SELECTION" << std::endl;
764                     key = SELECTION_SELECTION;
765                     break;
766                 }
767             case SELECTION_DRAWING:
768                 /** \todo 
769                  * This returns wrong values if the document has a viewBox.
770                  */
771                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
772                 
773                 /* If the drawing is valid, then we'll use it and break
774                    otherwise we drop through to the page settings */
775                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
776                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) { 
777                     // std::cout << "Using selection: DRAWING" << std::endl;
778                     key = SELECTION_DRAWING;
779                     break;
780                 }
781             case SELECTION_PAGE:
782                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
783                                 NR::Point(sp_document_width(doc)),
784                                           sp_document_height(doc));
786                 // std::cout << "Using selection: PAGE" << std::endl;
787                 key = SELECTION_PAGE;
788                 break;
789             case SELECTION_CUSTOM:
790             default:
791                 break;
792         } // switch
793         
794         // remember area setting
795         prefs_set_string_attribute ( "dialogs.export.exportarea", 
796                                      "value", selection_names[key]);
798         if (key != SELECTION_CUSTOM) {
799             sp_export_set_area (base, bbox.min()[NR::X],
800                                       bbox.min()[NR::Y],
801                                       bbox.max()[NR::X],
802                                       bbox.max()[NR::Y]);
803         }
804     
805     } // end of if ( SP_ACTIVE_DESKTOP )
808     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
809         GtkWidget * file_entry;
810         const gchar * filename = NULL;
811         float xdpi = 0.0, ydpi = 0.0;
813         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
815         switch (key) {
816             case SELECTION_PAGE:
817             case SELECTION_DRAWING: {
818                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
819                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
820                 const gchar * dpi_string;
822                 filename = repr->attribute("inkscape:export-filename");
824                 dpi_string = NULL;
825                 dpi_string = repr->attribute("inkscape:export-xdpi");
826                 if (dpi_string != NULL) {
827                     xdpi = atof(dpi_string);
828                 }
830                 dpi_string = NULL;
831                 dpi_string = repr->attribute("inkscape:export-ydpi");
832                 if (dpi_string != NULL) {
833                     ydpi = atof(dpi_string);
834                 }
836                 if (filename == NULL) {
837                     if (doc_export_name != NULL) {
838                         filename = g_strdup(doc_export_name);
839                     } else {
840                         filename = g_strdup("");
841                     }
842                 }
843                 break;
844             }
845             case SELECTION_SELECTION:
846                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
847                     const GSList * reprlst;
848                     bool filename_search = TRUE;
849                     bool xdpi_search = TRUE;
850                     bool ydpi_search = TRUE;
852                     reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
853                     for(; reprlst != NULL &&
854                             filename_search &&
855                             xdpi_search &&
856                             ydpi_search;
857                             reprlst = reprlst->next) {
858                         const gchar * dpi_string;
859                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
861                         if (filename_search) {
862                             filename = repr->attribute("inkscape:export-filename");
863                             if (filename != NULL)
864                                 filename_search = FALSE;
865                         }
867                         if (xdpi_search) {
868                             dpi_string = NULL;
869                             dpi_string = repr->attribute("inkscape:export-xdpi");
870                             if (dpi_string != NULL) {
871                                 xdpi = atof(dpi_string);
872                                 xdpi_search = FALSE;
873                             }
874                         }
876                         if (ydpi_search) {
877                             dpi_string = NULL;
878                             dpi_string = repr->attribute("inkscape:export-ydpi");
879                             if (dpi_string != NULL) {
880                                 ydpi = atof(dpi_string);
881                                 ydpi_search = FALSE;
882                             }
883                         }
884                     }
886                     /* If we still don't have a filename -- let's build
887                        one that's nice */
888                     if (filename == NULL) {
889                         const gchar * id = NULL;
890                         reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
891                         for(; reprlst != NULL; reprlst = reprlst->next) {
892                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
893                             if (repr->attribute("id")) {
894                                 id = repr->attribute("id");
895                                 break;
896                             }
897                         }
898                         if (id == NULL) /* This should never happen */
899                             id = "bitmap";
901                         gchar * directory = NULL;
902                         const gchar * file_entry_text;
904                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
905                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
906                             // std::cout << "Directory from dialog" << std::endl;
907                             directory = g_dirname(file_entry_text);
908                         }
910                         if (directory == NULL) {
911                             /* Grab document directory */
912                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
913                                 // std::cout << "Directory from document" << std::endl;
914                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
915                             }
916                         }
918                         if (directory == NULL) {
919                             // std::cout << "Home Directory" << std::endl;
920                             directory = homedir_path(NULL);
921                         }
923                         gchar * id_ext = g_strconcat(id, ".png", NULL);
924                         filename = g_build_filename(directory, id_ext, NULL);
925                         g_free(directory);
926                         g_free(id_ext);
927                     }
928                 }
929                 break;
930             case SELECTION_CUSTOM:
931             default:
932                 break;
933         }
935         if (filename != NULL) {
936             g_free(original_name);
937             original_name = g_strdup(filename);
938             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
939         }
941         if (xdpi != 0.0) {
942             sp_export_value_set(base, "xdpi", xdpi);
943         }
945         /* These can't be seperate, and setting x sets y, so for
946            now setting this is disabled.  Hopefully it won't be in
947            the future */
948         if (FALSE && ydpi != 0.0) {
949             sp_export_value_set(base, "ydpi", ydpi);
950         }
951     }
953     return;
954 } // end of sp_export_area_toggled()
956 /// Called when dialog is deleted
957 static gint
958 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
960     g_object_set_data (base, "cancel", (gpointer) 1);
961     return TRUE;
962 } // end of sp_export_progress_delete()
964 /// Called when progress is cancelled
965 static void
966 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
968     g_object_set_data (base, "cancel", (gpointer) 1);
969 } // end of sp_export_progress_cancel()
971 /// Called for every progress iteration
972 static unsigned int
973 sp_export_progress_callback (float value, void *data)
975     GtkWidget *prg;
976     int evtcount;
978     if (g_object_get_data ((GObject *) data, "cancel"))
979         return FALSE;
981     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
982     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
984     evtcount = 0;
985     while ((evtcount < 16) && gdk_events_pending ()) {
986             gtk_main_iteration_do (FALSE);
987             evtcount += 1;
988     }
990     gtk_main_iteration_do (FALSE);
992     return TRUE;
994 } // end of sp_export_progress_callback()
996 /// Called when export button is clicked
997 static void
998 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1000     if (!SP_ACTIVE_DESKTOP) return;
1002     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1003     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1005     float const x0 = sp_export_value_get_px(base, "x0");
1006     float const y0 = sp_export_value_get_px(base, "y0");
1007     float const x1 = sp_export_value_get_px(base, "x1");
1008     float const y1 = sp_export_value_get_px(base, "y1");
1009     float const xdpi = sp_export_value_get(base, "xdpi");
1010     float const ydpi = sp_export_value_get(base, "ydpi");
1011     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1012     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1014     if (filename == NULL || *filename == '\0') {
1015         sp_ui_error_dialog(_("You have to enter a filename"));
1016         return;
1017     }
1019     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1020         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1021         return;
1022     }
1024     gchar *dirname = g_dirname(filename);
1025     if ( dirname == NULL
1026          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1027     {
1028         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1029         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1030                                        safeDir);
1031         sp_ui_error_dialog(error);
1032         g_free(safeDir);
1033         g_free(error);
1034         g_free(dirname);
1035         return;
1036     }
1037     g_free(dirname);
1039     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1040     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1041     char *fn;
1042     gchar *text;
1044     dlg = gtk_dialog_new ();
1045     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1046     prg = gtk_progress_bar_new ();
1047     sp_transientize (dlg);
1048     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1049     g_object_set_data ((GObject *) base, "progress", prg);
1050     fn = g_path_get_basename (filename);
1051     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1052                              fn, width, height);
1053     g_free (fn);
1054     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1055     g_free (text);
1056     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1057                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1058     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1059                         prg, FALSE, FALSE, 4 );
1060     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1061                                   GTK_STOCK_CANCEL, 
1062                                   GTK_RESPONSE_CANCEL );
1063                                   
1064     g_signal_connect ( (GObject *) dlg, "delete_event", 
1065                        (GCallback) sp_export_progress_delete, base);
1066     g_signal_connect ( (GObject *) btn, "clicked", 
1067                        (GCallback) sp_export_progress_cancel, base);
1068     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1069     gtk_widget_show_all (dlg);
1070     
1071     /* Do export */
1072     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1073                              x0, y0, x1, y1, width, height, 
1074                              nv->pagecolor, 
1075                              sp_export_progress_callback, base)) {
1076         gchar * error;
1077         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1078         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1079         sp_ui_error_dialog(error);
1080         g_free(safeFile);
1081         g_free(error);
1082     }
1084     /* Reset the filename so that it can be changed again by changing
1085        selections and all that */
1086     g_free(original_name);
1087     original_name = g_strdup(filename);
1088     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1090     gtk_widget_destroy (dlg);
1091     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1093     /* Setup the values in the document */
1094     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1095         case SELECTION_PAGE:
1096         case SELECTION_DRAWING: {
1097             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1098             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1099             bool modified = FALSE;
1100             const gchar * temp_string;
1102             bool saved = sp_document_get_undo_sensitive(doc);
1103             sp_document_set_undo_sensitive(doc, FALSE);
1105             temp_string = repr->attribute("inkscape:export-filename");
1106             if (temp_string == NULL || strcmp(temp_string, filename)) {
1107                 repr->setAttribute("inkscape:export-filename", filename);
1108                 modified = TRUE;
1109             }
1110             temp_string = repr->attribute("inkscape:export-xdpi");
1111             if (temp_string == NULL || xdpi != atof(temp_string)) {
1112                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1113                 modified = TRUE;
1114             }
1115             temp_string = repr->attribute("inkscape:export-ydpi");
1116             if (temp_string == NULL || xdpi != atof(temp_string)) {
1117                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1118                 modified = TRUE;
1119             }
1121             if (modified)
1122                 repr->setAttribute("sodipodi:modified", "TRUE");
1123             sp_document_set_undo_sensitive(doc, saved);
1124             break;
1125         }
1126         case SELECTION_SELECTION: {
1127             const GSList * reprlst;
1128             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1129             bool modified = FALSE;
1131             bool saved = sp_document_get_undo_sensitive(doc);
1132             sp_document_set_undo_sensitive(doc, FALSE);
1133             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1135             for(; reprlst != NULL; reprlst = reprlst->next) {
1136                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1137                 const gchar * temp_string;
1139                 if (repr->attribute("id") == NULL ||
1140                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1141                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1142                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1143                     temp_string = repr->attribute("inkscape:export-filename");
1144                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1145                         repr->setAttribute("inkscape:export-filename", filename);
1146                         modified = TRUE;
1147                     }
1148                 }
1149                 temp_string = repr->attribute("inkscape:export-xdpi");
1150                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1151                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1152                     modified = TRUE;
1153                 }
1154                 temp_string = repr->attribute("inkscape:export-ydpi");
1155                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1156                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1157                     modified = TRUE;
1158                 }
1159             }
1161             if (modified) {
1162                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1163                 repr->setAttribute("sodipodi:modified", "TRUE");
1164             }
1166             sp_document_set_undo_sensitive(doc, saved);
1167             break;
1168         }
1169         default:
1170             break;
1171     }
1174     return;
1175 } // end of sp_export_export_clicked()
1177 /// Called when Browse button is clicked
1178 static void
1179 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1181     GtkWidget *fs, *fe;
1182     const gchar *filename;
1184     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1185                                       NULL,
1186                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1187                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1188                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1189                                       NULL );
1191     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1193     sp_transientize (fs);
1195     gtk_window_set_modal(GTK_WINDOW (fs), true);
1197     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1199     if (*filename == '\0') {
1200         filename = homedir_path(NULL);
1201     }
1203     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1205     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1206     {
1207         gchar *file;
1209         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1210         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1211         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1212         g_free(utf8file);
1214         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1215         g_free(file);
1216     }
1218     gtk_widget_destroy (fs);
1220     return;
1221 } // end of sp_export_browse_clicked()
1223 // TODO: Move this to nr-rect-fns.h.
1224 static bool
1225 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1226
1227     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1228     return (
1229         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1230         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1231         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1232         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1233         );
1236 /**
1237     \brief  This function is used to detect the current selection setting
1238             based on the values in the x0, y0, x1 and y0 fields.
1239     \param  base  The export dialog itself
1241     One of the most confusing parts of this function is why the array
1242     is built at the beginning.  What needs to happen here is that we
1243     should always check the current selection to see if it is the valid
1244     one.  While this is a performance improvement it is also a usability
1245     one during the cases where things like selections and drawings match
1246     size.  This way buttons change less 'randomly' (atleast in the eyes
1247     of the user).  To do this an array is built where the current selection
1248     type is placed first, and then the others in an order from smallest
1249     to largest (this can be configured by reshuffling \c test_order).
1251     All of the values in this function are rounded to two decimal places
1252     because that is what is shown to the user.  While everything is kept
1253     more accurate than that, the user can't control more acurrate than
1254     that, so for this to work for them - it needs to check on that level
1255     of accuracy.
1257     \todo finish writing this up
1258 */
1259 static void
1260 sp_export_detect_size(GtkObject * base) {
1261     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1262     selection_type this_test[SELECTION_NUMBER_OF + 1];
1263     selection_type key = SELECTION_NUMBER_OF;
1265     NR::Point x(sp_export_value_get_px (base, "x0"),
1266                 sp_export_value_get_px (base, "y0"));
1267     NR::Point y(sp_export_value_get_px (base, "x1"),
1268                 sp_export_value_get_px (base, "y1"));
1269     NR::Rect current_bbox(x, y);
1270     //std::cout << "Current " << current_bbox;
1272     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1273     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1274         this_test[i + 1] = test_order[i];
1275     }
1277     for (int i = 0;
1278             i < SELECTION_NUMBER_OF + 1 &&
1279                 key == SELECTION_NUMBER_OF &&
1280                 SP_ACTIVE_DESKTOP != NULL;
1281             i++) {
1282         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1283         switch (this_test[i]) {
1284             case SELECTION_SELECTION:
1285                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1286                     NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1288                     //std::cout << "Selection " << bbox;
1289                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1290                         key = SELECTION_SELECTION;
1291                     }
1292                 }
1293                 break;
1294             case SELECTION_DRAWING: {
1295                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1297                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1299                 // std::cout << "Drawing " << bbox2;
1300                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1301                     key = SELECTION_DRAWING;
1302                 }
1303                 break;
1304             }
1306             case SELECTION_PAGE: {
1307                 SPDocument *doc;
1309                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1311                 NR::Point x(0.0, 0.0);
1312                 NR::Point y(sp_document_width(doc),
1313                             sp_document_height(doc));
1314                 NR::Rect bbox(x, y);
1316                 // std::cout << "Page " << bbox;
1317                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1318                     key = SELECTION_PAGE;
1319                 }
1321                 break;
1322            }
1323         default:
1324            break;
1325         }
1326     }
1327     // std::cout << std::endl;
1329     if (key == SELECTION_NUMBER_OF) {
1330         key = SELECTION_CUSTOM;
1331     }
1333     /* We're now using a custom size, not a fixed one */
1334     /* printf("Detecting state: %s\n", selection_names[key]); */
1335     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1336     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1337     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1338     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1340     return;
1341 } /* sp_export_detect_size */
1343 /// Called when area x0 value is changed
1344 static void
1345 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1347     float x0, x1, xdpi, width;
1349     if (gtk_object_get_data (base, "update"))
1350         return;
1352     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1353             (base, "units")))
1354     {
1355         return;
1356     }
1358     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1360     x0 = sp_export_value_get_px (base, "x0");
1361     x1 = sp_export_value_get_px (base, "x1");
1362     xdpi = sp_export_value_get (base, "xdpi");
1364     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1366     if (width < SP_EXPORT_MIN_SIZE) {
1367         const gchar *key;
1368         width = SP_EXPORT_MIN_SIZE;
1369         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1371         if (!strcmp (key, "x0")) {
1372             x1 = x0 + width * DPI_BASE / xdpi;
1373             sp_export_value_set_px (base, "x1", x1);
1374         } else {
1375             x0 = x1 - width * DPI_BASE / xdpi;
1376             sp_export_value_set_px (base, "x0", x0);
1377         }
1378     }
1380     sp_export_value_set_px (base, "width", x1 - x0);
1381     sp_export_value_set (base, "bmwidth", width);
1383     sp_export_detect_size(base);
1385     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1387     return;
1388 } // end of sp_export_area_x_value_changed()
1390 /// Called when area y0 value is changed.
1391 static void
1392 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1394     float y0, y1, ydpi, height;
1396     if (gtk_object_get_data (base, "update"))
1397         return;
1399     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1400            (base, "units")))
1401     {
1402         return;
1403     }
1405     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1407     y0 = sp_export_value_get_px (base, "y0");
1408     y1 = sp_export_value_get_px (base, "y1");
1409     ydpi = sp_export_value_get (base, "ydpi");
1411     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1413     if (height < SP_EXPORT_MIN_SIZE) {
1414         const gchar *key;
1415         height = SP_EXPORT_MIN_SIZE;
1416         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1417         if (!strcmp (key, "y0")) {
1418             y1 = y0 + height * DPI_BASE / ydpi;
1419             sp_export_value_set_px (base, "y1", y1);
1420         } else {
1421             y0 = y1 - height * DPI_BASE / ydpi;
1422             sp_export_value_set_px (base, "y0", y0);
1423         }
1424     }
1426     sp_export_value_set_px (base, "height", y1 - y0);
1427     sp_export_value_set (base, "bmheight", height);
1429     sp_export_detect_size(base);
1431     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1433     return;
1434 } // end of sp_export_area_y_value_changed()
1436 /// Called when x1-x0 or area width is changed
1437 static void
1438 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1440     float x0, x1, xdpi, width, bmwidth;
1442     if (gtk_object_get_data (base, "update"))
1443         return;
1445     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1446            (base, "units"))) {
1447         return;
1448     }
1450     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1452     x0 = sp_export_value_get_px (base, "x0");
1453     x1 = sp_export_value_get_px (base, "x1");
1454     xdpi = sp_export_value_get (base, "xdpi");
1455     width = sp_export_value_get_px (base, "width");
1456     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1458     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1460         bmwidth = SP_EXPORT_MIN_SIZE;
1461         width = bmwidth * DPI_BASE / xdpi;
1462         sp_export_value_set_px (base, "width", width);
1463     }
1465     sp_export_value_set_px (base, "x1", x0 + width);
1466     sp_export_value_set (base, "bmwidth", bmwidth);
1468     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1470     return;
1471 } // end of sp_export_area_width_value_changed()
1473 /// Called when y1-y0 or area height is changed.
1474 static void
1475 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1478     float y0, y1, ydpi, height, bmheight;
1480     if (gtk_object_get_data (base, "update"))
1481         return;
1483     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1484            (base, "units"))) {
1485         return;
1486     }
1488     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1490     y0 = sp_export_value_get_px (base, "y0");
1491     y1 = sp_export_value_get_px (base, "y1");
1492     ydpi = sp_export_value_get (base, "ydpi");
1493     height = sp_export_value_get_px (base, "height");
1494     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1496     if (bmheight < SP_EXPORT_MIN_SIZE) {
1497         bmheight = SP_EXPORT_MIN_SIZE;
1498         height = bmheight * DPI_BASE / ydpi;
1499         sp_export_value_set_px (base, "height", height);
1500     }
1502     sp_export_value_set_px (base, "y1", y0 + height);
1503     sp_export_value_set (base, "bmheight", bmheight);
1505     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1507     return;
1508 } // end of sp_export_area_height_value_changed()
1510 /**
1511     \brief  A function to set the ydpi
1512     \param  base  The export dialog
1514     This function grabs all of the y values and then figures out the
1515     new bitmap size based on the changing dpi value.  The dpi value is
1516     gotten from the xdpi setting as these can not currently be independent.
1517 */
1518 static void
1519 sp_export_set_image_y (GtkObject *base)
1521     float y0, y1, xdpi;
1523     y0 = sp_export_value_get_px (base, "y0");
1524     y1 = sp_export_value_get_px (base, "y1");
1525     xdpi = sp_export_value_get (base, "xdpi");
1527     sp_export_value_set (base, "ydpi", xdpi);
1528     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1530     return;
1531 } // end of sp_export_set_image_y()
1533 /**
1534     \brief  A function to set the xdpi
1535     \param  base  The export dialog
1537     This function grabs all of the x values and then figures out the
1538     new bitmap size based on the changing dpi value.  The dpi value is
1539     gotten from the xdpi setting as these can not currently be independent.
1540 */
1541 static void
1542 sp_export_set_image_x (GtkObject *base)
1544     float x0, x1, xdpi;
1546     x0 = sp_export_value_get_px (base, "x0");
1547     x1 = sp_export_value_get_px (base, "x1");
1548     xdpi = sp_export_value_get (base, "xdpi");
1550     sp_export_value_set (base, "ydpi", xdpi);
1551     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1553     return;
1554 } // end of sp_export_set_image_x()
1556 /// Called when pixel width is changed
1557 static void
1558 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1560     float x0, x1, bmwidth, xdpi;
1562     if (gtk_object_get_data (base, "update"))
1563         return;
1565     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1566            (base, "units"))) {
1567        return;
1568     }
1570     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1572     x0 = sp_export_value_get_px (base, "x0");
1573     x1 = sp_export_value_get_px (base, "x1");
1574     bmwidth = sp_export_value_get (base, "bmwidth");
1576     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1577         bmwidth = SP_EXPORT_MIN_SIZE;
1578         sp_export_value_set (base, "bmwidth", bmwidth);
1579     }
1581     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1582     sp_export_value_set (base, "xdpi", xdpi);
1584     sp_export_set_image_y (base);
1586     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1588     return;
1589 } // end of sp_export_bitmap_width_value_changed()
1591 /// Called when pixel height is changed
1592 static void
1593 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1595     float y0, y1, bmheight, xdpi;
1597     if (gtk_object_get_data (base, "update"))
1598         return;
1600     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1601            (base, "units"))) {
1602        return;
1603     }
1605     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1607     y0 = sp_export_value_get_px (base, "y0");
1608     y1 = sp_export_value_get_px (base, "y1");
1609     bmheight = sp_export_value_get (base, "bmheight");
1611     if (bmheight < SP_EXPORT_MIN_SIZE) {
1612         bmheight = SP_EXPORT_MIN_SIZE;
1613         sp_export_value_set (base, "bmheight", bmheight);
1614     }
1616     xdpi = bmheight * DPI_BASE / (y1 - y0);
1617     sp_export_value_set (base, "xdpi", xdpi);
1619     sp_export_set_image_x (base);
1621     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1623     return;
1624 } // end of sp_export_bitmap_width_value_changed()
1626 /**
1627     \brief  A function to adjust the bitmap width when the xdpi value changes
1628     \param  adj  The adjustment that was changed
1629     \param  base The export dialog itself
1631     The first thing this function checks is to see if we are doing an
1632     update.  If we are, this function just returns because there is another
1633     instance of it that will handle everything for us.  If there is a
1634     units change, we also assume that everyone is being updated appropriately
1635     and there is nothing for us to do.
1637     If we're the highest level function, we set the update flag, and
1638     continue on our way.
1640     All of the values are grabbed using the \c sp_export_value_get functions
1641     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1642     xdpi value is saved in the preferences for the next time the dialog
1643     is opened.  (does the selection dpi need to be set here?)
1645     A check is done to to ensure that we aren't outputing an invalid width,
1646     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1647     changed to make it valid.
1649     After all of this the bitmap width is changed.
1651     We also change the ydpi.  This is a temporary hack as these can not
1652     currently be independent.  This is likely to change in the future.
1653 */
1654 void
1655 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1657     float x0, x1, xdpi, bmwidth;
1659     if (gtk_object_get_data (base, "update"))
1660         return;
1662     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1663            (base, "units"))) {
1664        return;
1665     }
1667     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1669     x0 = sp_export_value_get_px (base, "x0");
1670     x1 = sp_export_value_get_px (base, "x1");
1671     xdpi = sp_export_value_get (base, "xdpi");
1673     // remember xdpi setting
1674     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1676     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1678     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1679         bmwidth = SP_EXPORT_MIN_SIZE;
1680         if (x1 != x0)
1681             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1682         else
1683             xdpi = DPI_BASE;
1684         sp_export_value_set (base, "xdpi", xdpi);
1685     }
1687     sp_export_value_set (base, "bmwidth", bmwidth);
1689     sp_export_set_image_y (base);
1691     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1693     return;
1694 } // end of sp_export_xdpi_value_changed()
1697 /**
1698     \brief  A function to change the area that is used for the exported
1699             bitmap.
1700     \param  base  This is the export dialog
1701     \param  x0    Horizontal upper left hand corner of the picture in points
1702     \param  y0    Vertical upper left hand corner of the picture in points
1703     \param  x1    Horizontal lower right hand corner of the picture in points
1704     \param  y1    Vertical lower right hand corner of the picture in points
1706     This function just calls \c sp_export_value_set_px for each of the
1707     parameters that is passed in.  This allows for setting them all in
1708     one convient area.
1710     Update is set to suspend all of the other test running while all the
1711     values are being set up.  This allows for a performance increase, but
1712     it also means that the wrong type won't be detected with only some of
1713     the values set.  After all the values are set everyone is told that
1714     there has been an update.
1715 */
1716 static void
1717 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1719     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1720     sp_export_value_set_px (base, "x1", x1);
1721     sp_export_value_set_px (base, "y1", y1);
1722     sp_export_value_set_px (base, "x0", x0);
1723     sp_export_value_set_px (base, "y0", y0);
1724     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1726     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1727     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1729     return;
1732 /**
1733     \brief  Sets the value of an adjustment
1734     \param  base  The export dialog
1735     \param  key   Which adjustment to set
1736     \param  val   What value to set it to
1738     This function finds the adjustment using the data stored in the
1739     export dialog.  After finding the adjustment it then sets
1740     the value of it.
1741 */
1742 static void
1743 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1745     GtkAdjustment *adj;
1747     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1749     gtk_adjustment_set_value (adj, val);
1752 /**
1753     \brief  A function to set a value using the units points
1754     \param  base  The export dialog
1755     \param  key   Which value should be set
1756     \param  val   What the value should be in points
1758     This function first gets the adjustment for the key that is passed
1759     in.  It then figures out what units are currently being used in the
1760     dialog.  After doing all of that, it then converts the incoming
1761     value and sets the adjustment.
1762 */
1763 static void
1764 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1766     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1768     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1770     return;
1773 /**
1774     \brief  Get the value of an adjustment in the export dialog
1775     \param  base  The export dialog
1776     \param  key   Which adjustment is being looked for
1777     \return The value in the specified adjustment
1779     This function gets the adjustment from the data field in the export
1780     dialog.  It then grabs the value from the adjustment.
1781 */
1782 static float
1783 sp_export_value_get ( GtkObject *base, const gchar *key )
1785     GtkAdjustment *adj;
1787     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1789     return adj->value;
1792 /**
1793     \brief  Grabs a value in the export dialog and converts the unit
1794             to points
1795     \param  base  The export dialog
1796     \param  key   Which value should be returned
1797     \return The value in the adjustment in points
1799     This function, at its most basic, is a call to \c sp_export_value_get
1800     to get the value of the adjustment.  It then finds the units that
1801     are being used by looking at the "units" attribute of the export
1802     dialog.  Using that it converts the returned value into points.
1803 */
1804 static float
1805 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1807     float value = sp_export_value_get(base, key);
1808     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1810     return sp_units_get_pixels (value, *unit);
1811 } // end of sp_export_value_get_px()
1813 /**
1814     \brief  This function is called when the filename is changed by
1815             anyone.  It resets the virgin bit.
1816     \param  object  Text entry box
1817     \param  data    The export dialog
1818     \return None
1820     This function gets called when the text area is modified.  It is
1821     looking for the case where the text area is modified from its
1822     original value.  In that case it sets the "filename-modified" bit
1823     to TRUE.  If the text dialog returns back to the original text, the
1824     bit gets reset.  This should stop simple mistakes.
1825 */
1826 static void
1827 sp_export_filename_modified (GtkObject * object, gpointer data)
1829     GtkWidget * text_entry = (GtkWidget *)object;
1830     GtkWidget * export_dialog = (GtkWidget *)data;
1832     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1833         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1834 //        printf("Modified: FALSE\n");
1835     } else {
1836         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1837 //        printf("Modified: TRUE\n");
1838     }
1840     return;
1841 } // end sp_export_filename_modified
1843 /*
1844   Local Variables:
1845   mode:c++
1846   c-file-style:"stroustrup"
1847   c-file-offsets:((innamespace . 0)(inline-open . 0))
1848   indent-tabs-mode:nil
1849   fill-column:99
1850   End:
1851 */
1852 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :