Code

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