Code

Fix for mssing error incoude
[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>
24 #include <glibmm/i18n.h>
25 #include "helper/unit-menu.h"
26 #include "helper/units.h"
27 #include "unit-constants.h"
28 #include "helper/window.h"
29 #include "inkscape-private.h"
30 #include "document.h"
31 #include "desktop-handles.h"
32 #include "sp-item.h"
33 #include "selection.h"
34 #include "file.h"
35 #include "macros.h"
36 #include "sp-namedview.h"
38 #include "dialog-events.h"
39 #include "../prefs-utils.h"
40 #include "../verbs.h"
41 #include "../interface.h"
43 #include "extension/output.h"
44 #include "extension/db.h"
46 #include "io/sys.h"
49 #define SP_EXPORT_MIN_SIZE 1.0
51 #define DPI_BASE PX_PER_IN
53 #define EXPORT_COORD_PRECISION 3
55 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
56 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
57 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
58 static void sp_export_browse_store   ( GtkButton *button, gpointer userdata );
61 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
62                                                    GtkObject *base);
63                                              
64 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
65                                                    GtkObject *base);
66                                              
67 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
68                                                    GtkObject *base);
69                                                  
70 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
71                                                    GtkObject *base);
72                                                   
73 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
74                                                    GtkObject *base);
75                                                    
76 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
77                                                    GtkObject *base);
78                                            
79 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
80                                           Inkscape::Selection *selection, 
81                                           GtkObject *base);
82 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
83                                            Inkscape::Selection *selection, 
84                                            guint flags,
85                                            GtkObject *base );
87 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
88 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
89 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
90 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
91 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
93 static void sp_export_filename_modified (GtkObject * object, gpointer data);
94 static inline void sp_export_find_default_selection(GtkWidget * dlg);
95 static void sp_export_detect_size(GtkObject * base);
97 static const gchar *prefs_path = "dialogs.export";
99 // these all need to be reinitialized to their defaults during dialog_destroy
100 static GtkWidget *dlg = NULL;
101 static win_data wd;
102 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
103 static gchar * original_name = NULL;
104 static gchar * doc_export_name = NULL;
105 static bool was_empty = TRUE;
107 /** What type of button is being pressed */
108 enum selection_type {
109     SELECTION_PAGE = 0,  /**< Export the whole page */
110     SELECTION_DRAWING,   /**< Export everything drawn on the page */
111     SELECTION_SELECTION, /**< Export everything that is selected */
112     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
113     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
114 };
116 /** A list of strings that is used both in the preferences, and in the
117     data fields to describe the various values of \c selection_type. */
118 static const char * selection_names[SELECTION_NUMBER_OF] = {
119     "page", "drawing", "selection", "custom"};
121 /** The names on the buttons for the various selection types. */
122 static const char * selection_labels[SELECTION_NUMBER_OF] = {
123     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
125 static void
126 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
128     sp_signal_disconnect_by_data (INKSCAPE, dlg);
130     wd.win = dlg = NULL;
131     wd.stop = 0;
132     x = -1000; y = -1000; w = 0; h = 0;
133     g_free(original_name);
134     original_name = NULL;
135     g_free(doc_export_name);
136     doc_export_name = NULL;
137     was_empty = TRUE;
139     return;
140 } // end of sp_export_dialog_destroy()
142 /// Called when dialog is closed or inkscape is shut down.
143 static bool
144 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
147     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
148     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
150     if (x<0) x=0;
151     if (y<0) y=0;
153     prefs_set_int_attribute (prefs_path, "x", x);
154     prefs_set_int_attribute (prefs_path, "y", y);
155     prefs_set_int_attribute (prefs_path, "w", w);
156     prefs_set_int_attribute (prefs_path, "h", h);
158     return FALSE; // which means, go ahead and destroy it
160 } // end of sp_export_dialog_delete()
162 /**
163     \brief  Creates a new spin button for the export dialog
164     \param  key  The name of the spin button
165     \param  val  A default value for the spin button
166     \param  min  Minimum value for the spin button
167     \param  max  Maximum value for the spin button
168     \param  step The step size for the spin button
169     \param  page Size of the page increment
170     \param  us   Unit selector that effects this spin button
171     \param  t    Table to put the spin button in
172     \param  x    X location in the table \c t to start with
173     \param  y    Y location in the table \c t to start with
174     \param  ll   Text to put on the left side of the spin button (optional)
175     \param  lr   Text to put on the right side of the spin button (optional)
176     \param  digits  Number of digits to display after the decimal
177     \param  sensitive  Whether the spin button is sensitive or not
178     \param  cb   Callback for when this spin button is changed (optional)
179     \param  dlg  Export dialog the spin button is being placed in
181 */
182 static void
183 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
184                            float step, float page, GtkWidget *us,
185                            GtkWidget *t, int x, int y,
186                            const gchar *ll, const gchar *lr,
187                            int digits, unsigned int sensitive,
188                            GCallback cb, GtkWidget *dlg )
190     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
191     gtk_object_set_data (a, "key", key);
192     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
194     if (us) {
195         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
196                                           GTK_ADJUSTMENT (a) );
197     }
199     int pos = 0;
201     GtkWidget *l = NULL;
203     if (ll) {
205         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
206         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
207         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
208                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
209         gtk_widget_set_sensitive (l, sensitive);
210         pos += 1;
212     }
214     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
215     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
216                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
217     gtk_widget_set_size_request (sb, 80, -1);
218     gtk_widget_set_sensitive (sb, sensitive);
219     pos += 1;
221     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
223     if (lr) {
225         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
226         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
227         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
228                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229         gtk_widget_set_sensitive (l, sensitive);
230         pos += 1;
232         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
233     }
235     if (cb)
236         gtk_signal_connect (a, "value_changed", cb, dlg);
238     return;
239 } // end of sp_export_spinbutton_new()
242 static GtkWidget *
243 sp_export_dialog_area_frame (GtkWidget * dlg)
245     GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
247     f = gtk_frame_new (_("Export area"));
248     vb = gtk_vbox_new (FALSE, 2);
249     gtk_container_add (GTK_CONTAINER (f), vb);
251     /* Units box */
252     unitbox = gtk_hbox_new (FALSE, 0);
253     gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
254     /* gets added to the vbox later, but the unit selector is needed
255        earlier than that */
257     us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
258     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
259     if (desktop)
260         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
261     gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
262     l = gtk_label_new (_("Units:"));
263     gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
264     gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
266     hb = gtk_hbox_new (TRUE, 0);
267     gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
268     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
270     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
271         b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
272         gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
273         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
274         gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
275         gtk_signal_connect ( GTK_OBJECT (b), "clicked", 
276                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
277     }
279     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
280                        G_CALLBACK (sp_export_selection_changed), dlg );
281     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
282                        G_CALLBACK (sp_export_selection_modified), dlg );
283     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
284                        G_CALLBACK (sp_export_selection_changed), dlg );
285     
286     t = gtk_table_new (2, 6, FALSE);
287     gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
288     gtk_table_set_row_spacings (GTK_TABLE (t), 4);
289     gtk_table_set_col_spacings (GTK_TABLE (t), 4);
290     gtk_container_set_border_width (GTK_CONTAINER (t), 4);
292     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
293                                t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
294                                G_CALLBACK ( sp_export_area_x_value_changed), 
295                                dlg );
297     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
298                                t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
299                                G_CALLBACK (sp_export_area_x_value_changed), 
300                                dlg );
302     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
303                                us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
304                                G_CALLBACK 
305                                    (sp_export_area_width_value_changed), 
306                                dlg );
308     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
309                                t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
310                                G_CALLBACK (sp_export_area_y_value_changed), 
311                                dlg );
313     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
314                                t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
315                                G_CALLBACK (sp_export_area_y_value_changed), 
316                                dlg );
318     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
319                                us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
320                                G_CALLBACK (sp_export_area_height_value_changed), 
321                                dlg );
323     /* Adding in the unit box */
324     gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
326     return f;
327 } // end of sp_export_dialog_area_frame
330 void
331 sp_export_dialog (void)
333     if (!dlg) {
334         GtkWidget *vb, *hb;
336         gchar title[500];
337         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
339         dlg = sp_window_new (title, TRUE);
341         if (x == -1000 || y == -1000) {
342             x = prefs_get_int_attribute (prefs_path, "x", 0);
343             y = prefs_get_int_attribute (prefs_path, "y", 0);
344         }
346         if (w ==0 || h == 0) {
347             w = prefs_get_int_attribute (prefs_path, "w", 0);
348             h = prefs_get_int_attribute (prefs_path, "h", 0);
349         }
351         if (x<0) x=0;
352         if (y<0) y=0;
354         if (x != 0 || y != 0) {
355             gtk_window_move ((GtkWindow *) dlg, x, y);
356         } else {
357             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
358         }
360         if (w && h)
361             gtk_window_resize ((GtkWindow *) dlg, w, h);
363         sp_transientize (dlg);
364         wd.win = dlg;
365         wd.stop = 0;
367         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
368                              G_CALLBACK (sp_transientize_callback), &wd);
370         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
371                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
373         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
374                              G_CALLBACK (sp_export_dialog_destroy), dlg);
376         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
377                              G_CALLBACK (sp_export_dialog_delete), dlg);
379         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
380                              G_CALLBACK (sp_export_dialog_delete), dlg);
382         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
383                              G_CALLBACK (sp_dialog_hide), dlg);
385         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
386                              G_CALLBACK (sp_dialog_unhide), dlg);
388         GtkTooltips *tt = gtk_tooltips_new();
390         vb = gtk_vbox_new (FALSE, 4);
391         gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
392         gtk_container_add (GTK_CONTAINER (dlg), vb);
394         /* Export area frame */
395         {
396             GtkWidget *f = sp_export_dialog_area_frame(dlg);
397             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
398         }
400         /* Bitmap size frame */
401         {
402             GtkWidget *f = gtk_frame_new (_("Bitmap size"));
403             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
404             GtkWidget *t = gtk_table_new (2, 5, FALSE);
405             gtk_table_set_row_spacings (GTK_TABLE (t), 4);
406             gtk_table_set_col_spacings (GTK_TABLE (t), 4);
407             gtk_container_set_border_width (GTK_CONTAINER (t), 4);
408             gtk_container_add (GTK_CONTAINER (f), t);
410             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
411                                        NULL, t, 0, 0,
412                                        _("_Width:"), _("pixels at"), 0, 1,
413                                        G_CALLBACK 
414                                        (sp_export_bitmap_width_value_changed), 
415                                        dlg );
417             sp_export_spinbutton_new ( "xdpi", 
418                                        prefs_get_double_attribute 
419                                        ( "dialogs.export.defaultxdpi", 
420                                          "value", DPI_BASE), 
421                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
422                                        NULL, _("dp_i"), 2, 1,
423                                        G_CALLBACK (sp_export_xdpi_value_changed), 
424                                        dlg );
426             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0, 
427                                        NULL, t, 0, 1, _("Height:"), _("pixels at"), 
428                                        0, 0, NULL, dlg );
430             /** \todo
431              * Needs fixing: there's no way to set ydpi currently, so we use  
432              *       the defaultxdpi value here, too...
433              */
434             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
435                                        ( "dialogs.export.defaultxdpi", 
436                                          "value", DPI_BASE), 
437                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
438                                        NULL, _("dpi"), 2, 0, NULL, dlg );
439         }
441         /* File entry */
442         {
443             GtkWidget *frame = gtk_frame_new ("");
444             GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
445             gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
446             gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
448             GtkWidget *fe = gtk_entry_new ();
450             /*
451              * set the default filename to be that of the current path + document
452              * with .png extension
453              *
454              * One thing to notice here is that this filename may get
455              * overwritten, but it won't happen here.  The filename gets
456              * written into the text field, but then the button to select
457              * the area gets set.  In that code the filename can be changed
458              * if there are some with presidence in the document.  So, while
459              * this code sets the name first, it may not be the one users
460              * really see.
461              */
462             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
463             {
464                 gchar *name;
465                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
466                 const gchar *uri = SP_DOCUMENT_URI (doc);
467                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
468                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
469                 Inkscape::Extension::Output * oextension = NULL;
471                 if (text_extension != NULL) {
472                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
473                 }
475                 if (oextension != NULL) {
476                     gchar * old_extension = oextension->get_extension();
477                     if (g_str_has_suffix(uri, old_extension)) {
478                         gchar * uri_copy;
479                         gchar * extension_point;
480                         gchar * final_name;
482                         uri_copy = g_strdup(uri);
483                         extension_point = g_strrstr(uri_copy, old_extension);
484                         extension_point[0] = '\0';
486                         final_name = g_strconcat(uri_copy, ".png", NULL);
487                         gtk_entry_set_text (GTK_ENTRY (fe), final_name);
489                         g_free(final_name);
490                         g_free(uri_copy);
491                     }
492                 } else {
493                     name = g_strconcat(uri, ".png", NULL);
494                     gtk_entry_set_text (GTK_ENTRY (fe), name);
495                     g_free(name);
496                 }
498                 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
499             }
500             g_signal_connect ( G_OBJECT (fe), "changed",
501                                G_CALLBACK (sp_export_filename_modified), dlg);
503             hb = gtk_hbox_new (FALSE, 5);
504             gtk_container_add (GTK_CONTAINER (frame), hb);
505             gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
507             {
508                 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
509                 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
510                 g_signal_connect ( G_OBJECT (b), "clicked",
511                                    G_CALLBACK (sp_export_browse_clicked), NULL );
512             }
514             gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
515             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
516             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
517             original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
518             // pressing enter in the filename field is the same as clicking export:
519             g_signal_connect ( G_OBJECT (fe), "activate",
520                                G_CALLBACK (sp_export_export_clicked), dlg );
521             // focus is in the filename initially:
522             gtk_widget_grab_focus (GTK_WIDGET (fe));
524             // mnemonic in frame label moves focus to filename:
525             gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
526         }
528         /* Buttons */
529         hb = gtk_hbox_new (FALSE, 0);
530         gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
532         {
533             GtkWidget *b = gtk_button_new ();
534             GtkWidget *l = gtk_label_new ("");
535             gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
536             gtk_container_add (GTK_CONTAINER(b), l);
537             gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
538             gtk_signal_connect ( GTK_OBJECT (b), "clicked",
539                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
540             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
541         }
543         gtk_widget_show_all (vb);
545     } // end of if (!dlg)
547     sp_export_find_default_selection(dlg);
549     gtk_window_present ((GtkWindow *) dlg);
551     return;
552 } // end of sp_export_dialog()
554 static inline void
555 sp_export_find_default_selection(GtkWidget * dlg)
557     selection_type key = SELECTION_NUMBER_OF;
559     if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
560         key = SELECTION_SELECTION;
561     }
563     /* Try using the preferences */
564     if (key == SELECTION_NUMBER_OF) {
565         const gchar *what = NULL;
566         int i = SELECTION_NUMBER_OF;
568         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
570         if (what != NULL) {
571             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
572                 if (!strcmp (what, selection_names[i])) {
573                     break;
574                 }
575             }
576         }
578         key = (selection_type)i;
579     }
581     if (key == SELECTION_NUMBER_OF) {
582         key = SELECTION_SELECTION;
583     }
585     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
586                                                        selection_names[key]);
587     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
589     return;
593 /**
594  * \brief  If selection changed or a different document activated, we must 
595  * recalculate any chosen areas
596  *
597  */
598 static void
599 sp_export_selection_changed ( Inkscape::Application *inkscape, 
600                               Inkscape::Selection *selection, 
601                               GtkObject *base )
603     selection_type current_key;
604     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
606     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
607             (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
608             was_empty) {
609         gtk_toggle_button_set_active
610             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
611               TRUE );
612     }
613     was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
615     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
617     if (inkscape &&
618             SP_IS_INKSCAPE (inkscape) &&
619             selection &&
620             SELECTION_CUSTOM != current_key) {
621         GtkToggleButton * button;
622         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
623         sp_export_area_toggled(button, base);
624     } // end of if()
626     return;
627 } // end of sp_export_selection_changed()
629 static void
630 sp_export_selection_modified ( Inkscape::Application *inkscape, 
631                                Inkscape::Selection *selection, 
632                                guint flags,
633                                GtkObject *base )
635     selection_type current_key;
636     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
638     switch (current_key) {
639         case SELECTION_DRAWING:
640             if ( SP_ACTIVE_DESKTOP ) {
641                 SPDocument *doc;
642                 NRRect bbox;
643                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
644                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
646                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
647                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
648                 }
649             }
650             break;
651         case SELECTION_SELECTION:
652             if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
653                 NRRect bbox;
654                 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
655                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
656             }
657             break;
658         default:
659             /* Do nothing for page or for custom */
660             break;
661     }
663     return;
666 /// Called when one of the selection buttons was toggled.
667 static void
668 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
670     if (gtk_object_get_data (base, "update"))
671         return;
673     selection_type key, old_key;
674     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
675     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
677     /* Ignore all "turned off" events unless we're the only active button */
678     if (!gtk_toggle_button_get_active (tb) ) {
680         /* Don't let the current selection be deactived - but rerun the
681            activate to allow the user to renew the values */
682         if (key == old_key) {
683             gtk_toggle_button_set_active ( tb, TRUE );
684         }
686         return;
687     }
689     /* Turn off the currently active button unless it's us */
690     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
692     if (old_key != key) {
693         gtk_toggle_button_set_active
694             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
695               FALSE );
696     }
698     if ( SP_ACTIVE_DESKTOP )
699     {
700         SPDocument *doc;
701         NRRect bbox;
702         doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
704         /* Notice how the switch is used to 'fall through' here to get
705            various backups.  If you modify this without noticing you'll
706            probabaly screw something up. */
707         switch (key) {
708             case SELECTION_SELECTION:
709                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
710                 {
711                     (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
712                     /* Only if there is a selection that we can set
713                        do we break, otherwise we fall through to the
714                        drawing */
715                     // std::cout << "Using selection: SELECTION" << std::endl;
716                     key = SELECTION_SELECTION;
717                     break;
718                 }
719             case SELECTION_DRAWING:
720                 /** \todo 
721                  * This returns wrong values if the document has a viewBox.
722                  */
723                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
724                 
725                 /* If the drawing is valid, then we'll use it and break
726                    otherwise we drop through to the page settings */
727                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
728                     // std::cout << "Using selection: DRAWING" << std::endl;
729                     key = SELECTION_DRAWING;
730                     break;
731                 }
732             case SELECTION_PAGE:
733                 bbox.x0 = 0.0;
734                 bbox.y0 = 0.0;
735                 bbox.x1 = sp_document_width (doc);
736                 bbox.y1 = sp_document_height (doc);
737                 // std::cout << "Using selection: PAGE" << std::endl;
738                 key = SELECTION_PAGE;
739                 break;
740             case SELECTION_CUSTOM:
741             default:
742                 break;
743         } // switch
744         
745         // remember area setting
746         prefs_set_string_attribute ( "dialogs.export.exportarea", 
747                                      "value", selection_names[key]);
749         if (key != SELECTION_CUSTOM) {
750             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
751         }
752     
753     } // end of if ( SP_ACTIVE_DESKTOP )
756     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
757         GtkWidget * file_entry;
758         const gchar * filename = NULL;
759         float xdpi = 0.0, ydpi = 0.0;
761         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
763         switch (key) {
764             case SELECTION_PAGE:
765             case SELECTION_DRAWING: {
766                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
767                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
768                 const gchar * dpi_string;
770                 filename = repr->attribute("inkscape:export-filename");
772                 dpi_string = NULL;
773                 dpi_string = repr->attribute("inkscape:export-xdpi");
774                 if (dpi_string != NULL) {
775                     xdpi = atof(dpi_string);
776                 }
778                 dpi_string = NULL;
779                 dpi_string = repr->attribute("inkscape:export-ydpi");
780                 if (dpi_string != NULL) {
781                     ydpi = atof(dpi_string);
782                 }
784                 if (filename == NULL) {
785                     if (doc_export_name != NULL) {
786                         filename = g_strdup(doc_export_name);
787                     } else {
788                         filename = g_strdup("");
789                     }
790                 }
792                 break;
793             }
794             case SELECTION_SELECTION:
795                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
796                     const GSList * reprlst;
797                     bool filename_search = TRUE;
798                     bool xdpi_search = TRUE;
799                     bool ydpi_search = TRUE;
801                     reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
802                     for(; reprlst != NULL &&
803                             filename_search &&
804                             xdpi_search &&
805                             ydpi_search;
806                             reprlst = reprlst->next) {
807                         const gchar * dpi_string;
808                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
810                         if (filename_search) {
811                             filename = repr->attribute("inkscape:export-filename");
812                             if (filename != NULL)
813                                 filename_search = FALSE;
814                         }
816                         if (xdpi_search) {
817                             dpi_string = NULL;
818                             dpi_string = repr->attribute("inkscape:export-xdpi");
819                             if (dpi_string != NULL) {
820                                 xdpi = atof(dpi_string);
821                                 xdpi_search = FALSE;
822                             }
823                         }
825                         if (ydpi_search) {
826                             dpi_string = NULL;
827                             dpi_string = repr->attribute("inkscape:export-ydpi");
828                             if (dpi_string != NULL) {
829                                 ydpi = atof(dpi_string);
830                                 ydpi_search = FALSE;
831                             }
832                         }
833                     }
835                     /* If we still don't have a filename -- let's build
836                        one that's nice */
837                     if (filename == NULL) {
838                         const gchar * id = NULL;
839                         reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
840                         for(; reprlst != NULL; reprlst = reprlst->next) {
841                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
842                             if (repr->attribute("id")) {
843                                 id = repr->attribute("id");
844                                 break;
845                             }
846                         }
847                         if (id == NULL) /* This should never happen */
848                             id = "bitmap";
850                         gchar * directory = NULL;
851                         const gchar * file_entry_text;
853                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
854                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
855                             // std::cout << "Directory from dialog" << std::endl;
856                             directory = g_dirname(file_entry_text);
857                         }
859                         if (directory == NULL) {
860                             /* Grab document directory */
861                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
862                                 // std::cout << "Directory from document" << std::endl;
863                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
864                             }
865                         }
867                         if (directory == NULL) {
868                             // std::cout << "Home Directory" << std::endl;
869                             directory = homedir_path(NULL);
870                         }
872                         gchar * id_ext = g_strconcat(id, ".png", NULL);
873                         filename = g_build_filename(directory, id_ext, NULL);
874                         g_free(directory);
875                         g_free(id_ext);
876                     }
877                 }
878                 break;
879             case SELECTION_CUSTOM:
880             default:
881                 break;
882         }
884         if (filename != NULL) {
885             g_free(original_name);
886             original_name = g_strdup(filename);
887             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
888         }
890         if (xdpi != 0.0) {
891             sp_export_value_set(base, "xdpi", xdpi);
892         }
894         /* These can't be seperate, and setting x sets y, so for
895            now setting this is disabled.  Hopefully it won't be in
896            the future */
897         if (FALSE && ydpi != 0.0) {
898             sp_export_value_set(base, "ydpi", ydpi);
899         }
900     }
902     return;
903 } // end of sp_export_area_toggled()
905 /// Called when dialog is deleted
906 static gint
907 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
909     g_object_set_data (base, "cancel", (gpointer) 1);
910     return TRUE;
911 } // end of sp_export_progress_delete()
913 /// Called when progress is cancelled
914 static void
915 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
917     g_object_set_data (base, "cancel", (gpointer) 1);
918 } // end of sp_export_progress_cancel()
920 /// Called for every progress iteration
921 static unsigned int
922 sp_export_progress_callback (float value, void *data)
924     GtkWidget *prg;
925     int evtcount;
927     if (g_object_get_data ((GObject *) data, "cancel"))
928         return FALSE;
930     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
931     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
933     evtcount = 0;
934     while ((evtcount < 16) && gdk_events_pending ()) {
935             gtk_main_iteration_do (FALSE);
936             evtcount += 1;
937     }
939     gtk_main_iteration_do (FALSE);
941     return TRUE;
943 } // end of sp_export_progress_callback()
945 /// Called when export button is clicked
946 static void
947 sp_export_export_clicked (GtkButton *button, GtkObject *base)
949     if (!SP_ACTIVE_DESKTOP) return;
951     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
952     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
954     float const x0 = sp_export_value_get_px(base, "x0");
955     float const y0 = sp_export_value_get_px(base, "y0");
956     float const x1 = sp_export_value_get_px(base, "x1");
957     float const y1 = sp_export_value_get_px(base, "y1");
958     float const xdpi = sp_export_value_get(base, "xdpi");
959     float const ydpi = sp_export_value_get(base, "ydpi");
960     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
961     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
963     if (filename == NULL || *filename == '\0') {
964         sp_ui_error_dialog(_("You have to enter a filename"));
965         return;
966     }
968     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
969         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
970         return;
971     }
973     gchar *dirname = g_dirname(filename);
974     if ( dirname == NULL
975          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
976     {
977         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
978         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
979                                        safeDir);
980         sp_ui_error_dialog(error);
981         g_free(safeDir);
982         g_free(error);
983         g_free(dirname);
984         return;
985     }
986     g_free(dirname);
988     SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
989     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
990     char *fn;
991     gchar *text;
993     dlg = gtk_dialog_new ();
994     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
995     prg = gtk_progress_bar_new ();
996     sp_transientize (dlg);
997     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
998     g_object_set_data ((GObject *) base, "progress", prg);
999     fn = g_path_get_basename (filename);
1000     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1001                              fn, width, height);
1002     g_free (fn);
1003     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1004     g_free (text);
1005     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1006                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1007     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1008                         prg, FALSE, FALSE, 4 );
1009     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1010                                   GTK_STOCK_CANCEL, 
1011                                   GTK_RESPONSE_CANCEL );
1012                                   
1013     g_signal_connect ( (GObject *) dlg, "delete_event", 
1014                        (GCallback) sp_export_progress_delete, base);
1015     g_signal_connect ( (GObject *) btn, "clicked", 
1016                        (GCallback) sp_export_progress_cancel, base);
1017     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1018     gtk_widget_show_all (dlg);
1019     
1020     /* Do export */
1021     if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename, 
1022                              x0, y0, x1, y1, width, height, 
1023                              nv->pagecolor, 
1024                              sp_export_progress_callback, base)) {
1025         gchar * error;
1026         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1027         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1028         sp_ui_error_dialog(error);
1029         g_free(safeFile);
1030         g_free(error);
1031     }
1033     /* Reset the filename so that it can be changed again by changing
1034        selections and all that */
1035     g_free(original_name);
1036     original_name = g_strdup(filename);
1037     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1039     gtk_widget_destroy (dlg);
1040     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1042     /* Setup the values in the document */
1043     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1044         case SELECTION_PAGE:
1045         case SELECTION_DRAWING: {
1046             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1047             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1048             bool modified = FALSE;
1049             const gchar * temp_string;
1051             bool saved = sp_document_get_undo_sensitive(doc);
1052             sp_document_set_undo_sensitive(doc, FALSE);
1054             temp_string = repr->attribute("inkscape:export-filename");
1055             if (temp_string == NULL || strcmp(temp_string, filename)) {
1056                 repr->setAttribute("inkscape:export-filename", filename);
1057                 modified = TRUE;
1058             }
1059             temp_string = repr->attribute("inkscape:export-xdpi");
1060             if (temp_string == NULL || xdpi != atof(temp_string)) {
1061                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1062                 modified = TRUE;
1063             }
1064             temp_string = repr->attribute("inkscape:export-ydpi");
1065             if (temp_string == NULL || xdpi != atof(temp_string)) {
1066                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1067                 modified = TRUE;
1068             }
1070             if (modified)
1071                 repr->setAttribute("sodipodi:modified", "TRUE");
1072             sp_document_set_undo_sensitive(doc, saved);
1073             break;
1074         }
1075         case SELECTION_SELECTION: {
1076             const GSList * reprlst;
1077             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1078             bool modified = FALSE;
1080             bool saved = sp_document_get_undo_sensitive(doc);
1081             sp_document_set_undo_sensitive(doc, FALSE);
1082             reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1084             for(; reprlst != NULL; reprlst = reprlst->next) {
1085                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1086                 const gchar * temp_string;
1088                 if (repr->attribute("id") == NULL ||
1089                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1090                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1091                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1092                     temp_string = repr->attribute("inkscape:export-filename");
1093                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1094                         repr->setAttribute("inkscape:export-filename", filename);
1095                         modified = TRUE;
1096                     }
1097                 }
1098                 temp_string = repr->attribute("inkscape:export-xdpi");
1099                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1100                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1101                     modified = TRUE;
1102                 }
1103                 temp_string = repr->attribute("inkscape:export-ydpi");
1104                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1105                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1106                     modified = TRUE;
1107                 }
1108             }
1110             if (modified) {
1111                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1112                 repr->setAttribute("sodipodi:modified", "TRUE");
1113             }
1115             sp_document_set_undo_sensitive(doc, saved);
1116             break;
1117         }
1118         default:
1119             break;
1120     }
1123     return;
1124 } // end of sp_export_export_clicked()
1126 /// Called when Browse button is clicked
1127 static void
1128 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1130     GtkWidget *fs, *fe;
1131     const gchar *filename;
1133     fs = gtk_file_selection_new (_("Select a filename for exporting"));
1134     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1136     sp_transientize (fs);
1138     gtk_window_set_modal(GTK_WINDOW (fs), true);
1140     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1142     if (*filename == '\0') {
1143         filename = homedir_path(NULL);
1144     }
1146     gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1148     g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1149                        "clicked",
1150                        G_CALLBACK (sp_export_browse_store),
1151                        (gpointer) fs );
1153     g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1154                                "clicked",
1155                                G_CALLBACK (gtk_widget_destroy),
1156                                (gpointer) fs );
1158     g_signal_connect_swapped ( GTK_OBJECT
1159                                    (GTK_FILE_SELECTION (fs)->cancel_button),
1160                                "clicked",
1161                                G_CALLBACK (gtk_widget_destroy),
1162                                (gpointer) fs );
1164     gtk_widget_show (fs);
1166     return;
1167 } // end of sp_export_browse_clicked()
1169 /// Called when OK clicked in file dialog
1170 static void
1171 sp_export_browse_store (GtkButton *button, gpointer userdata)
1173     GtkWidget *fs = (GtkWidget *)userdata, *fe;
1174     const gchar *file;
1176     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1178     file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1179     gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1180     gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1181     g_free(utf8file);
1183     g_object_set_data (G_OBJECT (dlg), "filename", fe);
1185     return;
1186 } // end of sp_export_browse_store()
1188 // TODO: Move this to nr-rect-fns.h.
1189 static bool
1190 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1191
1192     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1193     return (
1194         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1195         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1196         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1197         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1198         );
1201 /**
1202     \brief  This function is used to detect the current selection setting
1203             based on the values in the x0, y0, x1 and y0 fields.
1204     \param  base  The export dialog itself
1206     One of the most confusing parts of this function is why the array
1207     is built at the beginning.  What needs to happen here is that we
1208     should always check the current selection to see if it is the valid
1209     one.  While this is a performance improvement it is also a usability
1210     one during the cases where things like selections and drawings match
1211     size.  This way buttons change less 'randomly' (atleast in the eyes
1212     of the user).  To do this an array is built where the current selection
1213     type is placed first, and then the others in an order from smallest
1214     to largest (this can be configured by reshuffling \c test_order).
1216     All of the values in this function are rounded to two decimal places
1217     because that is what is shown to the user.  While everything is kept
1218     more accurate than that, the user can't control more acurrate than
1219     that, so for this to work for them - it needs to check on that level
1220     of accuracy.
1222     \todo finish writing this up
1223 */
1224 static void
1225 sp_export_detect_size(GtkObject * base) {
1226     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1227     selection_type this_test[SELECTION_NUMBER_OF + 1];
1228     selection_type key = SELECTION_NUMBER_OF;
1230     NR::Point x(sp_export_value_get_px (base, "x0"),
1231                 sp_export_value_get_px (base, "y0"));
1232     NR::Point y(sp_export_value_get_px (base, "x1"),
1233                 sp_export_value_get_px (base, "y1"));
1234     NR::Rect current_bbox(x, y);
1235     //std::cout << "Current " << current_bbox;
1237     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1238     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1239         this_test[i + 1] = test_order[i];
1240     }
1242     for (int i = 0;
1243             i < SELECTION_NUMBER_OF + 1 &&
1244                 key == SELECTION_NUMBER_OF &&
1245                 SP_ACTIVE_DESKTOP != NULL;
1246             i++) {
1247         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1248         switch (this_test[i]) {
1249             case SELECTION_SELECTION:
1250                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1251                     NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1253                     //std::cout << "Selection " << bbox;
1254                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1255                         key = SELECTION_SELECTION;
1256                     }
1257                 }
1258                 break;
1259             case SELECTION_DRAWING: {
1260                 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1262                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1264                 // std::cout << "Drawing " << bbox2;
1265                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1266                     key = SELECTION_DRAWING;
1267                 }
1268                 break;
1269             }
1271             case SELECTION_PAGE: {
1272                 SPDocument *doc;
1274                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1276                 NR::Point x(0.0, 0.0);
1277                 NR::Point y(sp_document_width(doc),
1278                             sp_document_height(doc));
1279                 NR::Rect bbox(x, y);
1281                 // std::cout << "Page " << bbox;
1282                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1283                     key = SELECTION_PAGE;
1284                 }
1286                 break;
1287            }
1288         default:
1289            break;
1290         }
1291     }
1292     // std::cout << std::endl;
1294     if (key == SELECTION_NUMBER_OF) {
1295         key = SELECTION_CUSTOM;
1296     }
1298     /* We're now using a custom size, not a fixed one */
1299     /* printf("Detecting state: %s\n", selection_names[key]); */
1300     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1301     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1302     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1303     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1305     return;
1306 } /* sp_export_detect_size */
1308 /// Called when area x0 value is changed
1309 static void
1310 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1312     float x0, x1, xdpi, width;
1314     if (gtk_object_get_data (base, "update"))
1315         return;
1317     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1318             (base, "units")))
1319     {
1320         return;
1321     }
1323     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1325     x0 = sp_export_value_get_px (base, "x0");
1326     x1 = sp_export_value_get_px (base, "x1");
1327     xdpi = sp_export_value_get (base, "xdpi");
1329     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1331     if (width < SP_EXPORT_MIN_SIZE) {
1332         const gchar *key;
1333         width = SP_EXPORT_MIN_SIZE;
1334         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1336         if (!strcmp (key, "x0")) {
1337             x1 = x0 + width * DPI_BASE / xdpi;
1338             sp_export_value_set_px (base, "x1", x1);
1339         } else {
1340             x0 = x1 - width * DPI_BASE / xdpi;
1341             sp_export_value_set_px (base, "x0", x0);
1342         }
1343     }
1345     sp_export_value_set_px (base, "width", x1 - x0);
1346     sp_export_value_set (base, "bmwidth", width);
1348     sp_export_detect_size(base);
1350     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1352     return;
1353 } // end of sp_export_area_x_value_changed()
1355 /// Called when area y0 value is changed.
1356 static void
1357 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1359     float y0, y1, ydpi, height;
1361     if (gtk_object_get_data (base, "update"))
1362         return;
1364     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1365            (base, "units")))
1366     {
1367         return;
1368     }
1370     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1372     y0 = sp_export_value_get_px (base, "y0");
1373     y1 = sp_export_value_get_px (base, "y1");
1374     ydpi = sp_export_value_get (base, "ydpi");
1376     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1378     if (height < SP_EXPORT_MIN_SIZE) {
1379         const gchar *key;
1380         height = SP_EXPORT_MIN_SIZE;
1381         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1382         if (!strcmp (key, "y0")) {
1383             y1 = y0 + height * DPI_BASE / ydpi;
1384             sp_export_value_set_px (base, "y1", y1);
1385         } else {
1386             y0 = y1 - height * DPI_BASE / ydpi;
1387             sp_export_value_set_px (base, "y0", y0);
1388         }
1389     }
1391     sp_export_value_set_px (base, "height", y1 - y0);
1392     sp_export_value_set (base, "bmheight", height);
1394     sp_export_detect_size(base);
1396     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1398     return;
1399 } // end of sp_export_area_y_value_changed()
1401 /// Called when x1-x0 or area width is changed
1402 static void
1403 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1405     float x0, x1, xdpi, width, bmwidth;
1407     if (gtk_object_get_data (base, "update"))
1408         return;
1410     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1411            (base, "units"))) {
1412         return;
1413     }
1415     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1417     x0 = sp_export_value_get_px (base, "x0");
1418     x1 = sp_export_value_get_px (base, "x1");
1419     xdpi = sp_export_value_get (base, "xdpi");
1420     width = sp_export_value_get_px (base, "width");
1421     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1423     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1425         bmwidth = SP_EXPORT_MIN_SIZE;
1426         width = bmwidth * DPI_BASE / xdpi;
1427         sp_export_value_set_px (base, "width", width);
1428     }
1430     sp_export_value_set_px (base, "x1", x0 + width);
1431     sp_export_value_set (base, "bmwidth", bmwidth);
1433     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1435     return;
1436 } // end of sp_export_area_width_value_changed()
1438 /// Called when y1-y0 or area height is changed.
1439 static void
1440 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1443     float y0, y1, ydpi, height, bmheight;
1445     if (gtk_object_get_data (base, "update"))
1446         return;
1448     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1449            (base, "units"))) {
1450         return;
1451     }
1453     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1455     y0 = sp_export_value_get_px (base, "y0");
1456     y1 = sp_export_value_get_px (base, "y1");
1457     ydpi = sp_export_value_get (base, "ydpi");
1458     height = sp_export_value_get_px (base, "height");
1459     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1461     if (bmheight < SP_EXPORT_MIN_SIZE) {
1462         bmheight = SP_EXPORT_MIN_SIZE;
1463         height = bmheight * DPI_BASE / ydpi;
1464         sp_export_value_set_px (base, "height", height);
1465     }
1467     sp_export_value_set_px (base, "y1", y0 + height);
1468     sp_export_value_set (base, "bmheight", bmheight);
1470     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1472     return;
1473 } // end of sp_export_area_height_value_changed()
1475 /**
1476     \brief  A function to set the ydpi
1477     \param  base  The export dialog
1479     This function grabs all of the y values and then figures out the
1480     new bitmap size based on the changing dpi value.  The dpi value is
1481     gotten from the xdpi setting as these can not currently be independent.
1482 */
1483 static void
1484 sp_export_set_image_y (GtkObject *base)
1486     float y0, y1, xdpi;
1488     y0 = sp_export_value_get_px (base, "y0");
1489     y1 = sp_export_value_get_px (base, "y1");
1490     xdpi = sp_export_value_get (base, "xdpi");
1492     sp_export_value_set (base, "ydpi", xdpi);
1493     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1495     return;
1496 } // end of sp_export_set_image_y()
1498 /// Called when pixel width is changed
1499 static void
1500 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1502     float x0, x1, bmwidth, xdpi;
1504     if (gtk_object_get_data (base, "update"))
1505         return;
1507     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1508            (base, "units"))) {
1509        return;
1510     }
1512     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1514     x0 = sp_export_value_get_px (base, "x0");
1515     x1 = sp_export_value_get_px (base, "x1");
1516     bmwidth = sp_export_value_get (base, "bmwidth");
1518     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1519         bmwidth = SP_EXPORT_MIN_SIZE;
1520         sp_export_value_set (base, "bmwidth", bmwidth);
1521     }
1523     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1524     sp_export_value_set (base, "xdpi", xdpi);
1526     sp_export_set_image_y (base);
1528     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1530     return;
1531 } // end of sp_export_bitmap_width_value_changed()
1533 /**
1534     \brief  A function to adjust the bitmap width when the xdpi value changes
1535     \param  adj  The adjustment that was changed
1536     \param  base The export dialog itself
1538     The first thing this function checks is to see if we are doing an
1539     update.  If we are, this function just returns because there is another
1540     instance of it that will handle everything for us.  If there is a
1541     units change, we also assume that everyone is being updated appropriately
1542     and there is nothing for us to do.
1544     If we're the highest level function, we set the update flag, and
1545     continue on our way.
1547     All of the values are grabbed using the \c sp_export_value_get functions
1548     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1549     xdpi value is saved in the preferences for the next time the dialog
1550     is opened.  (does the selection dpi need to be set here?)
1552     A check is done to to ensure that we aren't outputing an invalid width,
1553     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1554     changed to make it valid.
1556     After all of this the bitmap width is changed.
1558     We also change the ydpi.  This is a temporary hack as these can not
1559     currently be independent.  This is likely to change in the future.
1560 */
1561 void
1562 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1564     float x0, x1, xdpi, bmwidth;
1566     if (gtk_object_get_data (base, "update"))
1567         return;
1569     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1570            (base, "units"))) {
1571        return;
1572     }
1574     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1576     x0 = sp_export_value_get_px (base, "x0");
1577     x1 = sp_export_value_get_px (base, "x1");
1578     xdpi = sp_export_value_get (base, "xdpi");
1580     // remember xdpi setting
1581     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1583     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1585     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1586         bmwidth = SP_EXPORT_MIN_SIZE;
1587         if (x1 != x0)
1588             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1589         else
1590             xdpi = DPI_BASE;
1591         sp_export_value_set (base, "xdpi", xdpi);
1592     }
1594     sp_export_value_set (base, "bmwidth", bmwidth);
1596     sp_export_set_image_y (base);
1598     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1600     return;
1601 } // end of sp_export_xdpi_value_changed()
1604 /**
1605     \brief  A function to change the area that is used for the exported
1606             bitmap.
1607     \param  base  This is the export dialog
1608     \param  x0    Horizontal upper left hand corner of the picture in points
1609     \param  y0    Vertical upper left hand corner of the picture in points
1610     \param  x1    Horizontal lower right hand corner of the picture in points
1611     \param  y1    Vertical lower right hand corner of the picture in points
1613     This function just calls \c sp_export_value_set_px for each of the
1614     parameters that is passed in.  This allows for setting them all in
1615     one convient area.
1617     Update is set to suspend all of the other test running while all the
1618     values are being set up.  This allows for a performance increase, but
1619     it also means that the wrong type won't be detected with only some of
1620     the values set.  After all the values are set everyone is told that
1621     there has been an update.
1622 */
1623 static void
1624 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1626     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1627     sp_export_value_set_px (base, "x1", x1);
1628     sp_export_value_set_px (base, "y1", y1);
1629     sp_export_value_set_px (base, "x0", x0);
1630     sp_export_value_set_px (base, "y0", y0);
1631     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1633     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1634     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1636     return;
1639 /**
1640     \brief  Sets the value of an adjustment
1641     \param  base  The export dialog
1642     \param  key   Which adjustment to set
1643     \param  val   What value to set it to
1645     This function finds the adjustment using the data stored in the
1646     export dialog.  After finding the adjustment it then sets
1647     the value of it.
1648 */
1649 static void
1650 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1652     GtkAdjustment *adj;
1654     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1656     gtk_adjustment_set_value (adj, val);
1659 /**
1660     \brief  A function to set a value using the units points
1661     \param  base  The export dialog
1662     \param  key   Which value should be set
1663     \param  val   What the value should be in points
1665     This function first gets the adjustment for the key that is passed
1666     in.  It then figures out what units are currently being used in the
1667     dialog.  After doing all of that, it then converts the incoming
1668     value and sets the adjustment.
1669 */
1670 static void
1671 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1673     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1675     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1677     return;
1680 /**
1681     \brief  Get the value of an adjustment in the export dialog
1682     \param  base  The export dialog
1683     \param  key   Which adjustment is being looked for
1684     \return The value in the specified adjustment
1686     This function gets the adjustment from the data field in the export
1687     dialog.  It then grabs the value from the adjustment.
1688 */
1689 static float
1690 sp_export_value_get ( GtkObject *base, const gchar *key )
1692     GtkAdjustment *adj;
1694     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1696     return adj->value;
1697 } // end of sp_export_value_get()
1699 /**
1700     \brief  Grabs a value in the export dialog and converts the unit
1701             to points
1702     \param  base  The export dialog
1703     \param  key   Which value should be returned
1704     \return The value in the adjustment in points
1706     This function, at its most basic, is a call to \c sp_export_value_get
1707     to get the value of the adjustment.  It then finds the units that
1708     are being used by looking at the "units" attribute of the export
1709     dialog.  Using that it converts the returned value into points.
1710 */
1711 static float
1712 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1714     float value = sp_export_value_get(base, key);
1715     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1717     return sp_units_get_pixels (value, *unit);
1718 } // end of sp_export_value_get_px()
1720 /**
1721     \brief  This function is called when the filename is changed by
1722             anyone.  It resets the virgin bit.
1723     \param  object  Text entry box
1724     \param  data    The export dialog
1725     \return None
1727     This function gets called when the text area is modified.  It is
1728     looking for the case where the text area is modified from its
1729     original value.  In that case it sets the "filename-modified" bit
1730     to TRUE.  If the text dialog returns back to the original text, the
1731     bit gets reset.  This should stop simple mistakes.
1732 */
1733 static void
1734 sp_export_filename_modified (GtkObject * object, gpointer data)
1736     GtkWidget * text_entry = (GtkWidget *)object;
1737     GtkWidget * export_dialog = (GtkWidget *)data;
1739     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1740         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1741 //        printf("Modified: FALSE\n");
1742     } else {
1743         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1744 //        printf("Modified: TRUE\n");
1745     }
1747     return;
1748 } // end sp_export_filename_modified
1750 /*
1751   Local Variables:
1752   mode:c++
1753   c-file-style:"stroustrup"
1754   c-file-offsets:((innamespace . 0)(inline-open . 0))
1755   indent-tabs-mode:nil
1756   fill-column:99
1757   End:
1758 */
1759 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :