Code

Export dialog is now always shown in view (not outside of screen)
[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"
46 #include "selection-chemistry.h"
48 #include "dialog-events.h"
49 #include "../prefs-utils.h"
50 #include "../verbs.h"
51 #include "../interface.h"
53 #include "extension/output.h"
54 #include "extension/db.h"
56 #include "io/sys.h"
58 #include "helper/png-write.h"
61 #define SP_EXPORT_MIN_SIZE 1.0
63 #define DPI_BASE PX_PER_IN
65 #define EXPORT_COORD_PRECISION 3    
67 #define MIN_ONSCREEN_DISTANCE 50
69 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
70 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
71 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
73 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
74                                                    GtkObject *base);
75                                              
76 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
77                                                    GtkObject *base);
78                                              
79 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
80                                                    GtkObject *base);
81                                                  
82 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
83                                                    GtkObject *base);
84                                                   
85 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
86                                                    GtkObject *base);
87                                                    
88 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
89                                                    GtkObject *base);
90                                                    
91 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
92                                                    GtkObject *base);
93                                            
94 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
95                                           Inkscape::Selection *selection, 
96                                           GtkObject *base);
97 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
98                                            Inkscape::Selection *selection, 
99                                            guint flags,
100                                            GtkObject *base );
102 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
103 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
104 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
105 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
106 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
108 static void sp_export_filename_modified (GtkObject * object, gpointer data);
109 static inline void sp_export_find_default_selection(GtkWidget * dlg);
110 static void sp_export_detect_size(GtkObject * base);
112 static const gchar *prefs_path = "dialogs.export";
114 // these all need to be reinitialized to their defaults during dialog_destroy
115 static GtkWidget *dlg = NULL;
116 static win_data wd;
117 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
118 static gchar * original_name = NULL;
119 static gchar * doc_export_name = NULL;
120 static bool was_empty = TRUE;
122 /** What type of button is being pressed */
123 enum selection_type {
124     SELECTION_PAGE = 0,  /**< Export the whole page */
125     SELECTION_DRAWING,   /**< Export everything drawn on the page */
126     SELECTION_SELECTION, /**< Export everything that is selected */
127     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
128     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
129 };
131 /** A list of strings that is used both in the preferences, and in the
132     data fields to describe the various values of \c selection_type. */
133 static const char * selection_names[SELECTION_NUMBER_OF] = {
134     "page", "drawing", "selection", "custom"};
136 /** The names on the buttons for the various selection types. */
137 static const char * selection_labels[SELECTION_NUMBER_OF] = {
138     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
140 static void
141 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
143     sp_signal_disconnect_by_data (INKSCAPE, dlg);
145     wd.win = dlg = NULL;
146     wd.stop = 0;
147     x = -1000; y = -1000; w = 0; h = 0;
148     g_free(original_name);
149     original_name = NULL;
150     g_free(doc_export_name);
151     doc_export_name = NULL;
152     was_empty = TRUE;
154     return;
155 } // end of sp_export_dialog_destroy()
157 /// Called when dialog is closed or inkscape is shut down.
158 static bool
159 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
162     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
163     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
165     if (x<0) x=0;
166     if (y<0) y=0;
168     prefs_set_int_attribute (prefs_path, "x", x);
169     prefs_set_int_attribute (prefs_path, "y", y);
170     prefs_set_int_attribute (prefs_path, "w", w);
171     prefs_set_int_attribute (prefs_path, "h", h);
173     return FALSE; // which means, go ahead and destroy it
175 } // end of sp_export_dialog_delete()
177 /**
178     \brief  Creates a new spin button for the export dialog
179     \param  key  The name of the spin button
180     \param  val  A default value for the spin button
181     \param  min  Minimum value for the spin button
182     \param  max  Maximum value for the spin button
183     \param  step The step size for the spin button
184     \param  page Size of the page increment
185     \param  us   Unit selector that effects this spin button
186     \param  t    Table to put the spin button in
187     \param  x    X location in the table \c t to start with
188     \param  y    Y location in the table \c t to start with
189     \param  ll   Text to put on the left side of the spin button (optional)
190     \param  lr   Text to put on the right side of the spin button (optional)
191     \param  digits  Number of digits to display after the decimal
192     \param  sensitive  Whether the spin button is sensitive or not
193     \param  cb   Callback for when this spin button is changed (optional)
194     \param  dlg  Export dialog the spin button is being placed in
196 */
197 static void
198 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
199                            float step, float page, GtkWidget *us,
200                            GtkWidget *t, int x, int y,
201                            const gchar *ll, const gchar *lr,
202                            int digits, unsigned int sensitive,
203                            GCallback cb, GtkWidget *dlg )
205     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
206     gtk_object_set_data (a, "key", key);
207     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
209     if (us) {
210         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
211                                           GTK_ADJUSTMENT (a) );
212     }
214     int pos = 0;
216     GtkWidget *l = NULL;
218     if (ll) {
220         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
221         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
222         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
223                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
224         gtk_widget_set_sensitive (l, sensitive);
225         pos += 1;
227     }
229     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
230     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
231                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
232     gtk_widget_set_size_request (sb, 80, -1);
233     gtk_widget_set_sensitive (sb, sensitive);
234     pos += 1;
236     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
238     if (lr) {
240         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
241         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
242         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
243                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
244         gtk_widget_set_sensitive (l, sensitive);
245         pos += 1;
247         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
248     }
250     if (cb)
251         gtk_signal_connect (a, "value_changed", cb, dlg);
253     return;
254 } // end of sp_export_spinbutton_new()
257 static Gtk::VBox *
258 sp_export_dialog_area_box (GtkWidget * dlg)
260     Gtk::VBox* vb = new Gtk::VBox(false, 3);
262     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
263     lbl->set_use_markup(true);
264     vb->pack_start(*lbl);
266     /* Units box */
267     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
268     /* gets added to the vbox later, but the unit selector is needed
269        earlier than that */
271     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
272     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
273     if (desktop)
274         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
275     unitbox->pack_end(*us, false, false, 0);
276     Gtk::Label* l = new Gtk::Label(_("Units:"));
277     unitbox->pack_end(*l, false, false, 3);
278     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
280     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
282     Gtk::ToggleButton* b;
283     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
284         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
285         b->set_data("key", GINT_TO_POINTER(i));
286         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
287         togglebox->pack_start(*b, false, true, 0);
288         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked", 
289                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
290     }
292     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
293                        G_CALLBACK (sp_export_selection_changed), dlg );
294     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
295                        G_CALLBACK (sp_export_selection_modified), dlg );
296     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
297                        G_CALLBACK (sp_export_selection_changed), dlg );
298     
299     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
300     t->set_row_spacings (4);
301     t->set_col_spacings (4);
303     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
304                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
305                                G_CALLBACK ( sp_export_area_x_value_changed), 
306                                dlg );
308     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
309                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
310                                G_CALLBACK (sp_export_area_x_value_changed), 
311                                dlg );
313     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
314                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
315                                G_CALLBACK 
316                                    (sp_export_area_width_value_changed), 
317                                dlg );
319     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
320                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
321                                G_CALLBACK (sp_export_area_y_value_changed), 
322                                dlg );
324     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(), 
325                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
326                                G_CALLBACK (sp_export_area_y_value_changed), 
327                                dlg );
329     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
330                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
331                                G_CALLBACK (sp_export_area_height_value_changed), 
332                                dlg );
334     vb->pack_start(*togglebox, false, false, 3);
335     vb->pack_start(*t, false, false, 0);
336     vb->pack_start(*unitbox, false, false, 0);
338     return vb;
339 } // end of sp_export_dialog_area_box
342 void
343 sp_export_dialog (void)
345     if (!dlg) {
346         Gtk::VBox* vb;
347         Gtk::HBox* hb;
349         gchar title[500];
350         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
352         dlg = sp_window_new (title, TRUE);
354         if (x == -1000 || y == -1000) {
355             x = prefs_get_int_attribute (prefs_path, "x", 0);
356             y = prefs_get_int_attribute (prefs_path, "y", 0);
357         }
359         if (w ==0 || h == 0) {
360             w = prefs_get_int_attribute (prefs_path, "w", 0);
361             h = prefs_get_int_attribute (prefs_path, "h", 0);
362         }
364 //        if (x<0) x=0;
365 //        if (y<0) y=0;
367         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
368         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
369             gtk_window_move ((GtkWindow *) dlg, x, y);
370         else
371             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
372         sp_transientize (dlg);
373         wd.win = dlg;
374         wd.stop = 0;
376         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
377                              G_CALLBACK (sp_transientize_callback), &wd);
379         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
380                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
382         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
383                              G_CALLBACK (sp_export_dialog_destroy), dlg);
385         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
386                              G_CALLBACK (sp_export_dialog_delete), dlg);
388         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
389                              G_CALLBACK (sp_export_dialog_delete), dlg);
391         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
392                              G_CALLBACK (sp_dialog_hide), dlg);
394         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
395                              G_CALLBACK (sp_dialog_unhide), dlg);
397         GtkTooltips *tt = gtk_tooltips_new();
399         vb = new Gtk::VBox(false, 3);
400         vb->set_border_width(3);
401         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
403         /* Export area frame */
404         {
405             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
406             area_box->set_border_width(3);
407             vb->pack_start(*area_box, false, false, 0);
408         }
410         /* Bitmap size frame */
411         {
412             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
413             size_box->set_border_width(3);
415             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
416             lbl->set_use_markup(true);
417             size_box->pack_start(*lbl, false, false, 0);
418             const int rows = 2;
419             const int cols = 5;
420             const bool homogeneous = false;
421             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
422             t->set_row_spacings (4);
423             t->set_col_spacings (4);
424             size_box->pack_start(*t);
426             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
427                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
428                                        _("_Width:"), _("pixels at"), 0, 1,
429                                        G_CALLBACK 
430                                        (sp_export_bitmap_width_value_changed), 
431                                        dlg );
433             sp_export_spinbutton_new ( "xdpi", 
434                                        prefs_get_double_attribute 
435                                        ( "dialogs.export.defaultxdpi", 
436                                          "value", DPI_BASE), 
437                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
438                                        NULL, _("dp_i"), 2, 1,
439                                        G_CALLBACK (sp_export_xdpi_value_changed), 
440                                        dlg );
442             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
443                                        NULL, GTK_WIDGET(t->gobj()), 0, 1, 
444                                        _("Height:"), _("pixels at"), 0, 1, 
445                                        G_CALLBACK
446                                        (sp_export_bitmap_height_value_changed), 
447                                        dlg );
449             /** \todo
450              * Needs fixing: there's no way to set ydpi currently, so we use  
451              *       the defaultxdpi value here, too...
452              */
453             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
454                                        ( "dialogs.export.defaultxdpi", 
455                                          "value", DPI_BASE), 
456                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
457                                        NULL, _("dpi"), 2, 0, NULL, dlg );
459             vb->pack_start(*size_box);
460         }
462         /* File entry */
463         {
464             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
465             file_box->set_border_width(3);
467             // true = has mnemonic
468             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
469             flabel->set_use_markup(true);
470             file_box->pack_start(*flabel, false, false, 0);
472             Gtk::Entry *fe = new Gtk::Entry();
474             /*
475              * set the default filename to be that of the current path + document
476              * with .png extension
477              *
478              * One thing to notice here is that this filename may get
479              * overwritten, but it won't happen here.  The filename gets
480              * written into the text field, but then the button to select
481              * the area gets set.  In that code the filename can be changed
482              * if there are some with presidence in the document.  So, while
483              * this code sets the name first, it may not be the one users
484              * really see.
485              */
486             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
487             {
488                 gchar *name;
489                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
490                 const gchar *uri = SP_DOCUMENT_URI (doc);
491                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
492                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
493                 Inkscape::Extension::Output * oextension = NULL;
495                 if (text_extension != NULL) {
496                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
497                 }
499                 if (oextension != NULL) {
500                     gchar * old_extension = oextension->get_extension();
501                     if (g_str_has_suffix(uri, old_extension)) {
502                         gchar * uri_copy;
503                         gchar * extension_point;
504                         gchar * final_name;
506                         uri_copy = g_strdup(uri);
507                         extension_point = g_strrstr(uri_copy, old_extension);
508                         extension_point[0] = '\0';
510                         final_name = g_strconcat(uri_copy, ".png", NULL);
511                         fe->set_text(final_name);
513                         g_free(final_name);
514                         g_free(uri_copy);
515                     }
516                 } else {
517                     name = g_strconcat(uri, ".png", NULL);
518                     fe->set_text(name);
519                     g_free(name);
520                 }
522                 doc_export_name = g_strdup(fe->get_text().c_str());
523             }
524             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
525                                G_CALLBACK (sp_export_filename_modified), dlg);
527             hb = new Gtk::HBox(FALSE, 5);
529             {
530                 // true = has mnemonic
531                 Gtk::Button *b = new Gtk::Button();
533                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
534                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
535                         Gtk::ICON_SIZE_BUTTON);
536                 pixlabel->pack_start(*im);
538                 Gtk::Label *l = new Gtk::Label();
539                 l->set_markup_with_mnemonic(_("_Browse..."));
540                 pixlabel->pack_start(*l);
542                 b->add(*pixlabel);
544                 hb->pack_end (*b, false, false, 4);
545                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
546                                    G_CALLBACK (sp_export_browse_clicked), NULL );
547             }
549             hb->pack_start (*fe, true, true, 0);
550             file_box->add(*hb);
551             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
552             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
553             original_name = g_strdup(fe->get_text().c_str());
554             // pressing enter in the filename field is the same as clicking export:
555             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
556                                G_CALLBACK (sp_export_export_clicked), dlg );
557             // focus is in the filename initially:
558             fe->grab_focus();
560             // mnemonic in frame label moves focus to filename:
561             flabel->set_mnemonic_widget(*fe);
563             vb->pack_start(*file_box);
564         }
566         /* Buttons */
567         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
568         bb->set_border_width(3);
570         {
571             Gtk::Button *b = new Gtk::Button();
572             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
573             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
574                     Gtk::ICON_SIZE_BUTTON);
575             image_label->pack_start(*im);
577             Gtk::Label *l = new Gtk::Label();
578             l->set_markup_with_mnemonic(_("_Export"));
579             image_label->pack_start(*l);
581             b->add(*image_label);
582             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
583             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
584                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
585             bb->pack_end(*b, false, false, 0);
586         }
588         vb->pack_end(*bb, false, false, 0);
589         vb->show_all();
591     } // end of if (!dlg)
593     sp_export_find_default_selection(dlg);
595     gtk_window_present ((GtkWindow *) dlg);
597     return;
598 } // end of sp_export_dialog()
600 static inline void
601 sp_export_find_default_selection(GtkWidget * dlg)
603     selection_type key = SELECTION_NUMBER_OF;
605     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
606         key = SELECTION_SELECTION;
607     }
609     /* Try using the preferences */
610     if (key == SELECTION_NUMBER_OF) {
611         const gchar *what = NULL;
612         int i = SELECTION_NUMBER_OF;
614         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
616         if (what != NULL) {
617             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
618                 if (!strcmp (what, selection_names[i])) {
619                     break;
620                 }
621             }
622         }
624         key = (selection_type)i;
625     }
627     if (key == SELECTION_NUMBER_OF) {
628         key = SELECTION_SELECTION;
629     }
631     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
632                                                        selection_names[key]);
633     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
635     return;
639 /**
640  * \brief  If selection changed or a different document activated, we must 
641  * recalculate any chosen areas
642  *
643  */
644 static void
645 sp_export_selection_changed ( Inkscape::Application *inkscape, 
646                               Inkscape::Selection *selection, 
647                               GtkObject *base )
649     selection_type current_key;
650     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
652     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
653             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
654             was_empty) {
655         gtk_toggle_button_set_active
656             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
657               TRUE );
658     }
659     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
661     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
663     if (inkscape &&
664             SP_IS_INKSCAPE (inkscape) &&
665             selection &&
666             SELECTION_CUSTOM != current_key) {
667         GtkToggleButton * button;
668         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
669         sp_export_area_toggled(button, base);
670     } // end of if()
672     return;
673 } // end of sp_export_selection_changed()
675 static void
676 sp_export_selection_modified ( Inkscape::Application *inkscape, 
677                                Inkscape::Selection *selection, 
678                                guint flags,
679                                GtkObject *base )
681     selection_type current_key;
682     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
684     switch (current_key) {
685         case SELECTION_DRAWING:
686             if ( SP_ACTIVE_DESKTOP ) {
687                 SPDocument *doc;
688                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
689                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
691                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
692                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
693                     sp_export_set_area (base, bbox.min()[NR::X],
694                                               bbox.min()[NR::Y],
695                                               bbox.max()[NR::X],
696                                               bbox.max()[NR::Y]);
697                 }
698             }
699             break;
700         case SELECTION_SELECTION:
701             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
702                 NRRect bbox;
703                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
704                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
705             }
706             break;
707         default:
708             /* Do nothing for page or for custom */
709             break;
710     }
712     return;
715 /// Called when one of the selection buttons was toggled.
716 static void
717 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
719     if (gtk_object_get_data (base, "update"))
720         return;
722     selection_type key, old_key;
723     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
724     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
726     /* Ignore all "turned off" events unless we're the only active button */
727     if (!gtk_toggle_button_get_active (tb) ) {
729         /* Don't let the current selection be deactived - but rerun the
730            activate to allow the user to renew the values */
731         if (key == old_key) {
732             gtk_toggle_button_set_active ( tb, TRUE );
733         }
735         return;
736     }
738     /* Turn off the currently active button unless it's us */
739     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
741     if (old_key != key) {
742         gtk_toggle_button_set_active
743             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
744               FALSE );
745     }
747     if ( SP_ACTIVE_DESKTOP )
748     {
749         SPDocument *doc;
750         NR::Rect bbox;
751         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
753         /* Notice how the switch is used to 'fall through' here to get
754            various backups.  If you modify this without noticing you'll
755            probabaly screw something up. */
756         switch (key) {
757             case SELECTION_SELECTION:
758                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
759                 {
760                     bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
761                     /* Only if there is a selection that we can set
762                        do we break, otherwise we fall through to the
763                        drawing */
764                     // std::cout << "Using selection: SELECTION" << std::endl;
765                     key = SELECTION_SELECTION;
766                     break;
767                 }
768             case SELECTION_DRAWING:
769                 /** \todo 
770                  * This returns wrong values if the document has a viewBox.
771                  */
772                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
773                 
774                 /* If the drawing is valid, then we'll use it and break
775                    otherwise we drop through to the page settings */
776                 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
777                       bbox.min()[NR::Y] > bbox.max()[NR::Y])) { 
778                     // std::cout << "Using selection: DRAWING" << std::endl;
779                     key = SELECTION_DRAWING;
780                     break;
781                 }
782             case SELECTION_PAGE:
783                 bbox = NR::Rect(NR::Point(0.0, 0.0), 
784                                 NR::Point(sp_document_width(doc), sp_document_height(doc))
785                                 );
787                 // std::cout << "Using selection: PAGE" << std::endl;
788                 key = SELECTION_PAGE;
789                 break;
790             case SELECTION_CUSTOM:
791             default:
792                 break;
793         } // switch
794         
795         // remember area setting
796         prefs_set_string_attribute ( "dialogs.export.exportarea", 
797                                      "value", selection_names[key]);
799         if (key != SELECTION_CUSTOM) {
800             sp_export_set_area (base, bbox.min()[NR::X],
801                                       bbox.min()[NR::Y],
802                                       bbox.max()[NR::X],
803                                       bbox.max()[NR::Y]);
804         }
805     
806     } // end of if ( SP_ACTIVE_DESKTOP )
809     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
810         GtkWidget * file_entry;
811         const gchar * filename = NULL;
812         float xdpi = 0.0, ydpi = 0.0;
814         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
816         switch (key) {
817             case SELECTION_PAGE:
818             case SELECTION_DRAWING: {
819                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
820                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
822                 if (filename == NULL) {
823                     if (doc_export_name != NULL) {
824                         filename = g_strdup(doc_export_name);
825                     } else {
826                         filename = g_strdup("");
827                     }
828                 }
829                 break;
830             }
831             case SELECTION_SELECTION:
832                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
834                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
836                     /* If we still don't have a filename -- let's build
837                        one that's nice */
838                     if (filename == NULL) {
839                         const gchar * id = NULL;
840                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
841                         for(; reprlst != NULL; reprlst = reprlst->next) {
842                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
843                             if (repr->attribute("id")) {
844                                 id = repr->attribute("id");
845                                 break;
846                             }
847                         }
848                         if (id == NULL) /* This should never happen */
849                             id = "bitmap";
851                         gchar * directory = NULL;
852                         const gchar * file_entry_text;
854                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
855                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
856                             // std::cout << "Directory from dialog" << std::endl;
857                             directory = g_dirname(file_entry_text);
858                         }
860                         if (directory == NULL) {
861                             /* Grab document directory */
862                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
863                                 // std::cout << "Directory from document" << std::endl;
864                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
865                             }
866                         }
868                         if (directory == NULL) {
869                             // std::cout << "Home Directory" << std::endl;
870                             directory = homedir_path(NULL);
871                         }
873                         gchar * id_ext = g_strconcat(id, ".png", NULL);
874                         filename = g_build_filename(directory, id_ext, NULL);
875                         g_free(directory);
876                         g_free(id_ext);
877                     }
878                 }
879                 break;
880             case SELECTION_CUSTOM:
881             default:
882                 break;
883         }
885         if (filename != NULL) {
886             g_free(original_name);
887             original_name = g_strdup(filename);
888             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
889         }
891         if (xdpi != 0.0) {
892             sp_export_value_set(base, "xdpi", xdpi);
893         }
895         /* These can't be separate, and setting x sets y, so for
896            now setting this is disabled.  Hopefully it won't be in
897            the future */
898         if (FALSE && ydpi != 0.0) {
899             sp_export_value_set(base, "ydpi", ydpi);
900         }
901     }
903     return;
904 } // end of sp_export_area_toggled()
906 /// Called when dialog is deleted
907 static gint
908 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
910     g_object_set_data (base, "cancel", (gpointer) 1);
911     return TRUE;
912 } // end of sp_export_progress_delete()
914 /// Called when progress is cancelled
915 static void
916 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
918     g_object_set_data (base, "cancel", (gpointer) 1);
919 } // end of sp_export_progress_cancel()
921 /// Called for every progress iteration
922 static unsigned int
923 sp_export_progress_callback (float value, void *data)
925     GtkWidget *prg;
926     int evtcount;
928     if (g_object_get_data ((GObject *) data, "cancel"))
929         return FALSE;
931     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
932     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
934     evtcount = 0;
935     while ((evtcount < 16) && gdk_events_pending ()) {
936             gtk_main_iteration_do (FALSE);
937             evtcount += 1;
938     }
940     gtk_main_iteration_do (FALSE);
942     return TRUE;
944 } // end of sp_export_progress_callback()
946 /// Called when export button is clicked
947 static void
948 sp_export_export_clicked (GtkButton *button, GtkObject *base)
950     if (!SP_ACTIVE_DESKTOP) return;
952     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
953     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
955     float const x0 = sp_export_value_get_px(base, "x0");
956     float const y0 = sp_export_value_get_px(base, "y0");
957     float const x1 = sp_export_value_get_px(base, "x1");
958     float const y1 = sp_export_value_get_px(base, "y1");
959     float const xdpi = sp_export_value_get(base, "xdpi");
960     float const ydpi = sp_export_value_get(base, "ydpi");
961     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
962     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
964     if (filename == NULL || *filename == '\0') {
965         sp_ui_error_dialog(_("You have to enter a filename"));
966         return;
967     }
969     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
970         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
971         return;
972     }
974     gchar *dirname = g_dirname(filename);
975     if ( dirname == NULL
976          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
977     {
978         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
979         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
980                                        safeDir);
981         sp_ui_error_dialog(error);
982         g_free(safeDir);
983         g_free(error);
984         g_free(dirname);
985         return;
986     }
987     g_free(dirname);
989     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
990     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
991     char *fn;
992     gchar *text;
994     dlg = gtk_dialog_new ();
995     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
996     prg = gtk_progress_bar_new ();
997     sp_transientize (dlg);
998     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
999     g_object_set_data ((GObject *) base, "progress", prg);
1000     fn = g_path_get_basename (filename);
1001     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1002                              fn, width, height);
1003     g_free (fn);
1004     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1005     g_free (text);
1006     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1007                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1008     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1009                         prg, FALSE, FALSE, 4 );
1010     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1011                                   GTK_STOCK_CANCEL, 
1012                                   GTK_RESPONSE_CANCEL );
1013                                   
1014     g_signal_connect ( (GObject *) dlg, "delete_event", 
1015                        (GCallback) sp_export_progress_delete, base);
1016     g_signal_connect ( (GObject *) btn, "clicked", 
1017                        (GCallback) sp_export_progress_cancel, base);
1018     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1019     gtk_widget_show_all (dlg);
1020     
1021     /* Do export */
1022     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1023                              x0, y0, x1, y1, width, height, xdpi, ydpi, 
1024                              nv->pagecolor, 
1025                              sp_export_progress_callback, base)) {
1026         gchar * error;
1027         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1028         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1029         sp_ui_error_dialog(error);
1030         g_free(safeFile);
1031         g_free(error);
1032     }
1034     /* Reset the filename so that it can be changed again by changing
1035        selections and all that */
1036     g_free(original_name);
1037     original_name = g_strdup(filename);
1038     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1040     gtk_widget_destroy (dlg);
1041     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1043     /* Setup the values in the document */
1044     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1045         case SELECTION_PAGE:
1046         case SELECTION_DRAWING: {
1047             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1048             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1049             bool modified = false;
1050             const gchar * temp_string;
1052             bool saved = sp_document_get_undo_sensitive(doc);
1053             sp_document_set_undo_sensitive(doc, false);
1055             temp_string = repr->attribute("inkscape:export-filename");
1056             if (temp_string == NULL || strcmp(temp_string, filename)) {
1057                 repr->setAttribute("inkscape:export-filename", filename);
1058                 modified = true;
1059             }
1060             temp_string = repr->attribute("inkscape:export-xdpi");
1061             if (temp_string == NULL || xdpi != atof(temp_string)) {
1062                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1063                 modified = true;
1064             }
1065             temp_string = repr->attribute("inkscape:export-ydpi");
1066             if (temp_string == NULL || xdpi != atof(temp_string)) {
1067                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1068                 modified = true;
1069             }
1071             if (modified)
1072                 repr->setAttribute("sodipodi:modified", "TRUE");
1073             sp_document_set_undo_sensitive(doc, saved);
1074             break;
1075         }
1076         case SELECTION_SELECTION: {
1077             const GSList * reprlst;
1078             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1079             bool modified = false;
1081             bool saved = sp_document_get_undo_sensitive(doc);
1082             sp_document_set_undo_sensitive(doc, false);
1083             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1085             for(; reprlst != NULL; reprlst = reprlst->next) {
1086                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1087                 const gchar * temp_string;
1089                 if (repr->attribute("id") == NULL ||
1090                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1091                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1092                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1093                     temp_string = repr->attribute("inkscape:export-filename");
1094                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1095                         repr->setAttribute("inkscape:export-filename", filename);
1096                         modified = true;
1097                     }
1098                 }
1099                 temp_string = repr->attribute("inkscape:export-xdpi");
1100                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1101                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1102                     modified = true;
1103                 }
1104                 temp_string = repr->attribute("inkscape:export-ydpi");
1105                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1106                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1107                     modified = true;
1108                 }
1109             }
1111             if (modified) {
1112                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1113                 repr->setAttribute("sodipodi:modified", "TRUE");
1114             }
1116             sp_document_set_undo_sensitive(doc, saved);
1117             break;
1118         }
1119         default:
1120             break;
1121     }
1124     return;
1125 } // end of sp_export_export_clicked()
1127 /// Called when Browse button is clicked
1128 static void
1129 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1131     GtkWidget *fs, *fe;
1132     const gchar *filename;
1134     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1135                                       NULL,
1136                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1137                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1138                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1139                                       NULL );
1141     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1143     sp_transientize (fs);
1145     gtk_window_set_modal(GTK_WINDOW (fs), true);
1147     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1149     if (*filename == '\0') {
1150         filename = homedir_path(NULL);
1151     }
1153     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1155     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1156     {
1157         gchar *file;
1159         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1160         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1161         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1162         g_free(utf8file);
1164         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1165         g_free(file);
1166     }
1168     gtk_widget_destroy (fs);
1170     return;
1171 } // end of sp_export_browse_clicked()
1173 // TODO: Move this to nr-rect-fns.h.
1174 static bool
1175 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1176
1177     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1178     return (
1179         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1180         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1181         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1182         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1183         );
1186 /**
1187     \brief  This function is used to detect the current selection setting
1188             based on the values in the x0, y0, x1 and y0 fields.
1189     \param  base  The export dialog itself
1191     One of the most confusing parts of this function is why the array
1192     is built at the beginning.  What needs to happen here is that we
1193     should always check the current selection to see if it is the valid
1194     one.  While this is a performance improvement it is also a usability
1195     one during the cases where things like selections and drawings match
1196     size.  This way buttons change less 'randomly' (atleast in the eyes
1197     of the user).  To do this an array is built where the current selection
1198     type is placed first, and then the others in an order from smallest
1199     to largest (this can be configured by reshuffling \c test_order).
1201     All of the values in this function are rounded to two decimal places
1202     because that is what is shown to the user.  While everything is kept
1203     more accurate than that, the user can't control more acurrate than
1204     that, so for this to work for them - it needs to check on that level
1205     of accuracy.
1207     \todo finish writing this up
1208 */
1209 static void
1210 sp_export_detect_size(GtkObject * base) {
1211     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1212     selection_type this_test[SELECTION_NUMBER_OF + 1];
1213     selection_type key = SELECTION_NUMBER_OF;
1215     NR::Point x(sp_export_value_get_px (base, "x0"),
1216                 sp_export_value_get_px (base, "y0"));
1217     NR::Point y(sp_export_value_get_px (base, "x1"),
1218                 sp_export_value_get_px (base, "y1"));
1219     NR::Rect current_bbox(x, y);
1220     //std::cout << "Current " << current_bbox;
1222     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1223     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1224         this_test[i + 1] = test_order[i];
1225     }
1227     for (int i = 0;
1228             i < SELECTION_NUMBER_OF + 1 &&
1229                 key == SELECTION_NUMBER_OF &&
1230                 SP_ACTIVE_DESKTOP != NULL;
1231             i++) {
1232         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1233         switch (this_test[i]) {
1234             case SELECTION_SELECTION:
1235                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1236                     NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1238                     //std::cout << "Selection " << bbox;
1239                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1240                         key = SELECTION_SELECTION;
1241                     }
1242                 }
1243                 break;
1244             case SELECTION_DRAWING: {
1245                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1247                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1249                 // std::cout << "Drawing " << bbox2;
1250                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1251                     key = SELECTION_DRAWING;
1252                 }
1253                 break;
1254             }
1256             case SELECTION_PAGE: {
1257                 SPDocument *doc;
1259                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1261                 NR::Point x(0.0, 0.0);
1262                 NR::Point y(sp_document_width(doc),
1263                             sp_document_height(doc));
1264                 NR::Rect bbox(x, y);
1266                 // std::cout << "Page " << bbox;
1267                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1268                     key = SELECTION_PAGE;
1269                 }
1271                 break;
1272            }
1273         default:
1274            break;
1275         }
1276     }
1277     // std::cout << std::endl;
1279     if (key == SELECTION_NUMBER_OF) {
1280         key = SELECTION_CUSTOM;
1281     }
1283     /* We're now using a custom size, not a fixed one */
1284     /* printf("Detecting state: %s\n", selection_names[key]); */
1285     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1286     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1287     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1288     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1290     return;
1291 } /* sp_export_detect_size */
1293 /// Called when area x0 value is changed
1294 static void
1295 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1297     float x0, x1, xdpi, width;
1299     if (gtk_object_get_data (base, "update"))
1300         return;
1302     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1303             (base, "units")))
1304     {
1305         return;
1306     }
1308     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1310     x0 = sp_export_value_get_px (base, "x0");
1311     x1 = sp_export_value_get_px (base, "x1");
1312     xdpi = sp_export_value_get (base, "xdpi");
1314     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1316     if (width < SP_EXPORT_MIN_SIZE) {
1317         const gchar *key;
1318         width = SP_EXPORT_MIN_SIZE;
1319         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1321         if (!strcmp (key, "x0")) {
1322             x1 = x0 + width * DPI_BASE / xdpi;
1323             sp_export_value_set_px (base, "x1", x1);
1324         } else {
1325             x0 = x1 - width * DPI_BASE / xdpi;
1326             sp_export_value_set_px (base, "x0", x0);
1327         }
1328     }
1330     sp_export_value_set_px (base, "width", x1 - x0);
1331     sp_export_value_set (base, "bmwidth", width);
1333     sp_export_detect_size(base);
1335     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1337     return;
1338 } // end of sp_export_area_x_value_changed()
1340 /// Called when area y0 value is changed.
1341 static void
1342 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1344     float y0, y1, ydpi, height;
1346     if (gtk_object_get_data (base, "update"))
1347         return;
1349     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1350            (base, "units")))
1351     {
1352         return;
1353     }
1355     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1357     y0 = sp_export_value_get_px (base, "y0");
1358     y1 = sp_export_value_get_px (base, "y1");
1359     ydpi = sp_export_value_get (base, "ydpi");
1361     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1363     if (height < SP_EXPORT_MIN_SIZE) {
1364         const gchar *key;
1365         height = SP_EXPORT_MIN_SIZE;
1366         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1367         if (!strcmp (key, "y0")) {
1368             y1 = y0 + height * DPI_BASE / ydpi;
1369             sp_export_value_set_px (base, "y1", y1);
1370         } else {
1371             y0 = y1 - height * DPI_BASE / ydpi;
1372             sp_export_value_set_px (base, "y0", y0);
1373         }
1374     }
1376     sp_export_value_set_px (base, "height", y1 - y0);
1377     sp_export_value_set (base, "bmheight", height);
1379     sp_export_detect_size(base);
1381     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1383     return;
1384 } // end of sp_export_area_y_value_changed()
1386 /// Called when x1-x0 or area width is changed
1387 static void
1388 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1390     float x0, x1, xdpi, width, bmwidth;
1392     if (gtk_object_get_data (base, "update"))
1393         return;
1395     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1396            (base, "units"))) {
1397         return;
1398     }
1400     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1402     x0 = sp_export_value_get_px (base, "x0");
1403     x1 = sp_export_value_get_px (base, "x1");
1404     xdpi = sp_export_value_get (base, "xdpi");
1405     width = sp_export_value_get_px (base, "width");
1406     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1408     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1410         bmwidth = SP_EXPORT_MIN_SIZE;
1411         width = bmwidth * DPI_BASE / xdpi;
1412         sp_export_value_set_px (base, "width", width);
1413     }
1415     sp_export_value_set_px (base, "x1", x0 + width);
1416     sp_export_value_set (base, "bmwidth", bmwidth);
1418     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1420     return;
1421 } // end of sp_export_area_width_value_changed()
1423 /// Called when y1-y0 or area height is changed.
1424 static void
1425 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1428     float y0, y1, ydpi, height, bmheight;
1430     if (gtk_object_get_data (base, "update"))
1431         return;
1433     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1434            (base, "units"))) {
1435         return;
1436     }
1438     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1440     y0 = sp_export_value_get_px (base, "y0");
1441     y1 = sp_export_value_get_px (base, "y1");
1442     ydpi = sp_export_value_get (base, "ydpi");
1443     height = sp_export_value_get_px (base, "height");
1444     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1446     if (bmheight < SP_EXPORT_MIN_SIZE) {
1447         bmheight = SP_EXPORT_MIN_SIZE;
1448         height = bmheight * DPI_BASE / ydpi;
1449         sp_export_value_set_px (base, "height", height);
1450     }
1452     sp_export_value_set_px (base, "y1", y0 + height);
1453     sp_export_value_set (base, "bmheight", bmheight);
1455     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1457     return;
1458 } // end of sp_export_area_height_value_changed()
1460 /**
1461     \brief  A function to set the ydpi
1462     \param  base  The export dialog
1464     This function grabs all of the y values and then figures out the
1465     new bitmap size based on the changing dpi value.  The dpi value is
1466     gotten from the xdpi setting as these can not currently be independent.
1467 */
1468 static void
1469 sp_export_set_image_y (GtkObject *base)
1471     float y0, y1, xdpi;
1473     y0 = sp_export_value_get_px (base, "y0");
1474     y1 = sp_export_value_get_px (base, "y1");
1475     xdpi = sp_export_value_get (base, "xdpi");
1477     sp_export_value_set (base, "ydpi", xdpi);
1478     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1480     return;
1481 } // end of sp_export_set_image_y()
1483 /**
1484     \brief  A function to set the xdpi
1485     \param  base  The export dialog
1487     This function grabs all of the x values and then figures out the
1488     new bitmap size based on the changing dpi value.  The dpi value is
1489     gotten from the xdpi setting as these can not currently be independent.
1490 */
1491 static void
1492 sp_export_set_image_x (GtkObject *base)
1494     float x0, x1, xdpi;
1496     x0 = sp_export_value_get_px (base, "x0");
1497     x1 = sp_export_value_get_px (base, "x1");
1498     xdpi = sp_export_value_get (base, "xdpi");
1500     sp_export_value_set (base, "ydpi", xdpi);
1501     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1503     return;
1504 } // end of sp_export_set_image_x()
1506 /// Called when pixel width is changed
1507 static void
1508 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1510     float x0, x1, bmwidth, xdpi;
1512     if (gtk_object_get_data (base, "update"))
1513         return;
1515     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1516            (base, "units"))) {
1517        return;
1518     }
1520     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1522     x0 = sp_export_value_get_px (base, "x0");
1523     x1 = sp_export_value_get_px (base, "x1");
1524     bmwidth = sp_export_value_get (base, "bmwidth");
1526     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1527         bmwidth = SP_EXPORT_MIN_SIZE;
1528         sp_export_value_set (base, "bmwidth", bmwidth);
1529     }
1531     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1532     sp_export_value_set (base, "xdpi", xdpi);
1534     sp_export_set_image_y (base);
1536     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1538     return;
1539 } // end of sp_export_bitmap_width_value_changed()
1541 /// Called when pixel height is changed
1542 static void
1543 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1545     float y0, y1, bmheight, 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     y0 = sp_export_value_get_px (base, "y0");
1558     y1 = sp_export_value_get_px (base, "y1");
1559     bmheight = sp_export_value_get (base, "bmheight");
1561     if (bmheight < SP_EXPORT_MIN_SIZE) {
1562         bmheight = SP_EXPORT_MIN_SIZE;
1563         sp_export_value_set (base, "bmheight", bmheight);
1564     }
1566     xdpi = bmheight * DPI_BASE / (y1 - y0);
1567     sp_export_value_set (base, "xdpi", xdpi);
1569     sp_export_set_image_x (base);
1571     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1573     return;
1574 } // end of sp_export_bitmap_width_value_changed()
1576 /**
1577     \brief  A function to adjust the bitmap width when the xdpi value changes
1578     \param  adj  The adjustment that was changed
1579     \param  base The export dialog itself
1581     The first thing this function checks is to see if we are doing an
1582     update.  If we are, this function just returns because there is another
1583     instance of it that will handle everything for us.  If there is a
1584     units change, we also assume that everyone is being updated appropriately
1585     and there is nothing for us to do.
1587     If we're the highest level function, we set the update flag, and
1588     continue on our way.
1590     All of the values are grabbed using the \c sp_export_value_get functions
1591     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1592     xdpi value is saved in the preferences for the next time the dialog
1593     is opened.  (does the selection dpi need to be set here?)
1595     A check is done to to ensure that we aren't outputing an invalid width,
1596     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1597     changed to make it valid.
1599     After all of this the bitmap width is changed.
1601     We also change the ydpi.  This is a temporary hack as these can not
1602     currently be independent.  This is likely to change in the future.
1603 */
1604 void
1605 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1607     float x0, x1, xdpi, bmwidth;
1609     if (gtk_object_get_data (base, "update"))
1610         return;
1612     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1613            (base, "units"))) {
1614        return;
1615     }
1617     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1619     x0 = sp_export_value_get_px (base, "x0");
1620     x1 = sp_export_value_get_px (base, "x1");
1621     xdpi = sp_export_value_get (base, "xdpi");
1623     // remember xdpi setting
1624     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1626     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1628     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1629         bmwidth = SP_EXPORT_MIN_SIZE;
1630         if (x1 != x0)
1631             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1632         else
1633             xdpi = DPI_BASE;
1634         sp_export_value_set (base, "xdpi", xdpi);
1635     }
1637     sp_export_value_set (base, "bmwidth", bmwidth);
1639     sp_export_set_image_y (base);
1641     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1643     return;
1644 } // end of sp_export_xdpi_value_changed()
1647 /**
1648     \brief  A function to change the area that is used for the exported
1649             bitmap.
1650     \param  base  This is the export dialog
1651     \param  x0    Horizontal upper left hand corner of the picture in points
1652     \param  y0    Vertical upper left hand corner of the picture in points
1653     \param  x1    Horizontal lower right hand corner of the picture in points
1654     \param  y1    Vertical lower right hand corner of the picture in points
1656     This function just calls \c sp_export_value_set_px for each of the
1657     parameters that is passed in.  This allows for setting them all in
1658     one convient area.
1660     Update is set to suspend all of the other test running while all the
1661     values are being set up.  This allows for a performance increase, but
1662     it also means that the wrong type won't be detected with only some of
1663     the values set.  After all the values are set everyone is told that
1664     there has been an update.
1665 */
1666 static void
1667 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1669     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1670     sp_export_value_set_px (base, "x1", x1);
1671     sp_export_value_set_px (base, "y1", y1);
1672     sp_export_value_set_px (base, "x0", x0);
1673     sp_export_value_set_px (base, "y0", y0);
1674     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1676     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1677     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1679     return;
1682 /**
1683     \brief  Sets the value of an adjustment
1684     \param  base  The export dialog
1685     \param  key   Which adjustment to set
1686     \param  val   What value to set it to
1688     This function finds the adjustment using the data stored in the
1689     export dialog.  After finding the adjustment it then sets
1690     the value of it.
1691 */
1692 static void
1693 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1695     GtkAdjustment *adj;
1697     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1699     gtk_adjustment_set_value (adj, val);
1702 /**
1703     \brief  A function to set a value using the units points
1704     \param  base  The export dialog
1705     \param  key   Which value should be set
1706     \param  val   What the value should be in points
1708     This function first gets the adjustment for the key that is passed
1709     in.  It then figures out what units are currently being used in the
1710     dialog.  After doing all of that, it then converts the incoming
1711     value and sets the adjustment.
1712 */
1713 static void
1714 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1716     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1718     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1720     return;
1723 /**
1724     \brief  Get the value of an adjustment in the export dialog
1725     \param  base  The export dialog
1726     \param  key   Which adjustment is being looked for
1727     \return The value in the specified adjustment
1729     This function gets the adjustment from the data field in the export
1730     dialog.  It then grabs the value from the adjustment.
1731 */
1732 static float
1733 sp_export_value_get ( GtkObject *base, const gchar *key )
1735     GtkAdjustment *adj;
1737     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1739     return adj->value;
1742 /**
1743     \brief  Grabs a value in the export dialog and converts the unit
1744             to points
1745     \param  base  The export dialog
1746     \param  key   Which value should be returned
1747     \return The value in the adjustment in points
1749     This function, at its most basic, is a call to \c sp_export_value_get
1750     to get the value of the adjustment.  It then finds the units that
1751     are being used by looking at the "units" attribute of the export
1752     dialog.  Using that it converts the returned value into points.
1753 */
1754 static float
1755 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1757     float value = sp_export_value_get(base, key);
1758     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1760     return sp_units_get_pixels (value, *unit);
1761 } // end of sp_export_value_get_px()
1763 /**
1764     \brief  This function is called when the filename is changed by
1765             anyone.  It resets the virgin bit.
1766     \param  object  Text entry box
1767     \param  data    The export dialog
1768     \return None
1770     This function gets called when the text area is modified.  It is
1771     looking for the case where the text area is modified from its
1772     original value.  In that case it sets the "filename-modified" bit
1773     to TRUE.  If the text dialog returns back to the original text, the
1774     bit gets reset.  This should stop simple mistakes.
1775 */
1776 static void
1777 sp_export_filename_modified (GtkObject * object, gpointer data)
1779     GtkWidget * text_entry = (GtkWidget *)object;
1780     GtkWidget * export_dialog = (GtkWidget *)data;
1782     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1783         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1784 //        printf("Modified: FALSE\n");
1785     } else {
1786         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1787 //        printf("Modified: TRUE\n");
1788     }
1790     return;
1791 } // end sp_export_filename_modified
1793 /*
1794   Local Variables:
1795   mode:c++
1796   c-file-style:"stroustrup"
1797   c-file-offsets:((innamespace . 0)(inline-open . 0))
1798   indent-tabs-mode:nil
1799   fill-column:99
1800   End:
1801 */
1802 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :