Code

make sure no negative dialog position is written into preferences,
[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 || y != 0) {
352             gtk_window_move ((GtkWindow *) dlg, x, y);
353         } else {
354             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
355         }
357         if (w && h)
358             gtk_window_resize ((GtkWindow *) dlg, w, h);
360         sp_transientize (dlg);
361         wd.win = dlg;
362         wd.stop = 0;
364         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
365                              G_CALLBACK (sp_transientize_callback), &wd);
367         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
368                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
370         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
371                              G_CALLBACK (sp_export_dialog_destroy), dlg);
373         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
374                              G_CALLBACK (sp_export_dialog_delete), dlg);
376         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
377                              G_CALLBACK (sp_export_dialog_delete), dlg);
379         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
380                              G_CALLBACK (sp_dialog_hide), dlg);
382         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
383                              G_CALLBACK (sp_dialog_unhide), dlg);
385         GtkTooltips *tt = gtk_tooltips_new();
387         vb = gtk_vbox_new (FALSE, 4);
388         gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
389         gtk_container_add (GTK_CONTAINER (dlg), vb);
391         /* Export area frame */
392         {
393             GtkWidget *f = sp_export_dialog_area_frame(dlg);
394             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
395         }
397         /* Bitmap size frame */
398         {
399             GtkWidget *f = gtk_frame_new (_("Bitmap size"));
400             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
401             GtkWidget *t = gtk_table_new (2, 5, FALSE);
402             gtk_table_set_row_spacings (GTK_TABLE (t), 4);
403             gtk_table_set_col_spacings (GTK_TABLE (t), 4);
404             gtk_container_set_border_width (GTK_CONTAINER (t), 4);
405             gtk_container_add (GTK_CONTAINER (f), t);
407             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
408                                        NULL, t, 0, 0,
409                                        _("_Width:"), _("pixels at"), 0, 1,
410                                        G_CALLBACK 
411                                        (sp_export_bitmap_width_value_changed), 
412                                        dlg );
414             sp_export_spinbutton_new ( "xdpi", 
415                                        prefs_get_double_attribute 
416                                        ( "dialogs.export.defaultxdpi", 
417                                          "value", DPI_BASE), 
418                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
419                                        NULL, _("dp_i"), 2, 1,
420                                        G_CALLBACK (sp_export_xdpi_value_changed), 
421                                        dlg );
423             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0, 
424                                        NULL, t, 0, 1, _("Height:"), _("pixels at"), 
425                                        0, 0, NULL, dlg );
427             /** \todo
428              * Needs fixing: there's no way to set ydpi currently, so we use  
429              *       the defaultxdpi value here, too...
430              */
431             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
432                                        ( "dialogs.export.defaultxdpi", 
433                                          "value", DPI_BASE), 
434                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
435                                        NULL, _("dpi"), 2, 0, NULL, dlg );
436         }
438         /* File entry */
439         {
440             GtkWidget *frame = gtk_frame_new ("");
441             GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
442             gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
443             gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
445             GtkWidget *fe = gtk_entry_new ();
447             /*
448              * set the default filename to be that of the current path + document
449              * with .png extension
450              *
451              * One thing to notice here is that this filename may get
452              * overwritten, but it won't happen here.  The filename gets
453              * written into the text field, but then the button to select
454              * the area gets set.  In that code the filename can be changed
455              * if there are some with presidence in the document.  So, while
456              * this code sets the name first, it may not be the one users
457              * really see.
458              */
459             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
460             {
461                 gchar *name;
462                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
463                 const gchar *uri = SP_DOCUMENT_URI (doc);
464                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
465                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
466                 Inkscape::Extension::Output * oextension = NULL;
468                 if (text_extension != NULL) {
469                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
470                 }
472                 if (oextension != NULL) {
473                     gchar * old_extension = oextension->get_extension();
474                     if (g_str_has_suffix(uri, old_extension)) {
475                         gchar * uri_copy;
476                         gchar * extension_point;
477                         gchar * final_name;
479                         uri_copy = g_strdup(uri);
480                         extension_point = g_strrstr(uri_copy, old_extension);
481                         extension_point[0] = '\0';
483                         final_name = g_strconcat(uri_copy, ".png", NULL);
484                         gtk_entry_set_text (GTK_ENTRY (fe), final_name);
486                         g_free(final_name);
487                         g_free(uri_copy);
488                     }
489                 } else {
490                     name = g_strconcat(uri, ".png", NULL);
491                     gtk_entry_set_text (GTK_ENTRY (fe), name);
492                     g_free(name);
493                 }
495                 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
496             }
497             g_signal_connect ( G_OBJECT (fe), "changed",
498                                G_CALLBACK (sp_export_filename_modified), dlg);
500             hb = gtk_hbox_new (FALSE, 5);
501             gtk_container_add (GTK_CONTAINER (frame), hb);
502             gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
504             {
505                 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
506                 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
507                 g_signal_connect ( G_OBJECT (b), "clicked",
508                                    G_CALLBACK (sp_export_browse_clicked), NULL );
509             }
511             gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
512             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
513             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
514             original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
515             // pressing enter in the filename field is the same as clicking export:
516             g_signal_connect ( G_OBJECT (fe), "activate",
517                                G_CALLBACK (sp_export_export_clicked), dlg );
518             // focus is in the filename initially:
519             gtk_widget_grab_focus (GTK_WIDGET (fe));
521             // mnemonic in frame label moves focus to filename:
522             gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
523         }
525         /* Buttons */
526         hb = gtk_hbox_new (FALSE, 0);
527         gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
529         {
530             GtkWidget *b = gtk_button_new ();
531             GtkWidget *l = gtk_label_new ("");
532             gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
533             gtk_container_add (GTK_CONTAINER(b), l);
534             gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
535             gtk_signal_connect ( GTK_OBJECT (b), "clicked",
536                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
537             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
538         }
540         gtk_widget_show_all (vb);
542     } // end of if (!dlg)
544     sp_export_find_default_selection(dlg);
546     gtk_window_present ((GtkWindow *) dlg);
548     return;
549 } // end of sp_export_dialog()
551 static inline void
552 sp_export_find_default_selection(GtkWidget * dlg)
554     selection_type key = SELECTION_NUMBER_OF;
556     if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
557         key = SELECTION_SELECTION;
558     }
560     /* Try using the preferences */
561     if (key == SELECTION_NUMBER_OF) {
562         const gchar *what = NULL;
563         int i = SELECTION_NUMBER_OF;
565         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
567         if (what != NULL) {
568             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
569                 if (!strcmp (what, selection_names[i])) {
570                     break;
571                 }
572             }
573         }
575         key = (selection_type)i;
576     }
578     if (key == SELECTION_NUMBER_OF) {
579         key = SELECTION_SELECTION;
580     }
582     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
583                                                        selection_names[key]);
584     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
586     return;
590 /**
591  * \brief  If selection changed or a different document activated, we must 
592  * recalculate any chosen areas
593  *
594  */
595 static void
596 sp_export_selection_changed ( Inkscape::Application *inkscape, 
597                               Inkscape::Selection *selection, 
598                               GtkObject *base )
600     selection_type current_key;
601     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
603     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
604             (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
605             was_empty) {
606         gtk_toggle_button_set_active
607             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
608               TRUE );
609     }
610     was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
612     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
614     if (inkscape &&
615             SP_IS_INKSCAPE (inkscape) &&
616             selection &&
617             SELECTION_CUSTOM != current_key) {
618         GtkToggleButton * button;
619         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
620         sp_export_area_toggled(button, base);
621     } // end of if()
623     return;
624 } // end of sp_export_selection_changed()
626 static void
627 sp_export_selection_modified ( Inkscape::Application *inkscape, 
628                                Inkscape::Selection *selection, 
629                                guint flags,
630                                GtkObject *base )
632     selection_type current_key;
633     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
635     switch (current_key) {
636         case SELECTION_DRAWING:
637             if ( SP_ACTIVE_DESKTOP ) {
638                 SPDocument *doc;
639                 NRRect bbox;
640                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
641                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
643                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
644                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
645                 }
646             }
647             break;
648         case SELECTION_SELECTION:
649             if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
650                 NRRect bbox;
651                 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
652                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
653             }
654             break;
655         default:
656             /* Do nothing for page or for custom */
657             break;
658     }
660     return;
663 /// Called when one of the selection buttons was toggled.
664 static void
665 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
667     if (gtk_object_get_data (base, "update"))
668         return;
670     selection_type key, old_key;
671     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
672     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
674     /* Ignore all "turned off" events unless we're the only active button */
675     if (!gtk_toggle_button_get_active (tb) ) {
677         /* Don't let the current selection be deactived - but rerun the
678            activate to allow the user to renew the values */
679         if (key == old_key) {
680             gtk_toggle_button_set_active ( tb, TRUE );
681         }
683         return;
684     }
686     /* Turn off the currently active button unless it's us */
687     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
689     if (old_key != key) {
690         gtk_toggle_button_set_active
691             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
692               FALSE );
693     }
695     if ( SP_ACTIVE_DESKTOP )
696     {
697         SPDocument *doc;
698         NRRect bbox;
699         doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
701         /* Notice how the switch is used to 'fall through' here to get
702            various backups.  If you modify this without noticing you'll
703            probabaly screw something up. */
704         switch (key) {
705             case SELECTION_SELECTION:
706                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
707                 {
708                     (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
709                     /* Only if there is a selection that we can set
710                        do we break, otherwise we fall through to the
711                        drawing */
712                     // std::cout << "Using selection: SELECTION" << std::endl;
713                     key = SELECTION_SELECTION;
714                     break;
715                 }
716             case SELECTION_DRAWING:
717                 /** \todo 
718                  * This returns wrong values if the document has a viewBox.
719                  */
720                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
721                 
722                 /* If the drawing is valid, then we'll use it and break
723                    otherwise we drop through to the page settings */
724                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
725                     // std::cout << "Using selection: DRAWING" << std::endl;
726                     key = SELECTION_DRAWING;
727                     break;
728                 }
729             case SELECTION_PAGE:
730                 bbox.x0 = 0.0;
731                 bbox.y0 = 0.0;
732                 bbox.x1 = sp_document_width (doc);
733                 bbox.y1 = sp_document_height (doc);
734                 // std::cout << "Using selection: PAGE" << std::endl;
735                 key = SELECTION_PAGE;
736                 break;
737             case SELECTION_CUSTOM:
738             default:
739                 break;
740         } // switch
741         
742         // remember area setting
743         prefs_set_string_attribute ( "dialogs.export.exportarea", 
744                                      "value", selection_names[key]);
746         if (key != SELECTION_CUSTOM) {
747             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
748         }
749     
750     } // end of if ( SP_ACTIVE_DESKTOP )
753     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
754         GtkWidget * file_entry;
755         const gchar * filename = NULL;
756         float xdpi = 0.0, ydpi = 0.0;
758         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
760         switch (key) {
761             case SELECTION_PAGE:
762             case SELECTION_DRAWING: {
763                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
764                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
765                 const gchar * dpi_string;
767                 filename = repr->attribute("inkscape:export-filename");
769                 dpi_string = NULL;
770                 dpi_string = repr->attribute("inkscape:export-xdpi");
771                 if (dpi_string != NULL) {
772                     xdpi = atof(dpi_string);
773                 }
775                 dpi_string = NULL;
776                 dpi_string = repr->attribute("inkscape:export-ydpi");
777                 if (dpi_string != NULL) {
778                     ydpi = atof(dpi_string);
779                 }
781                 if (filename == NULL) {
782                     if (doc_export_name != NULL) {
783                         filename = g_strdup(doc_export_name);
784                     } else {
785                         filename = g_strdup("");
786                     }
787                 }
789                 break;
790             }
791             case SELECTION_SELECTION:
792                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
793                     const GSList * reprlst;
794                     bool filename_search = TRUE;
795                     bool xdpi_search = TRUE;
796                     bool ydpi_search = TRUE;
798                     reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
799                     for(; reprlst != NULL &&
800                             filename_search &&
801                             xdpi_search &&
802                             ydpi_search;
803                             reprlst = reprlst->next) {
804                         const gchar * dpi_string;
805                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
807                         if (filename_search) {
808                             filename = repr->attribute("inkscape:export-filename");
809                             if (filename != NULL)
810                                 filename_search = FALSE;
811                         }
813                         if (xdpi_search) {
814                             dpi_string = NULL;
815                             dpi_string = repr->attribute("inkscape:export-xdpi");
816                             if (dpi_string != NULL) {
817                                 xdpi = atof(dpi_string);
818                                 xdpi_search = FALSE;
819                             }
820                         }
822                         if (ydpi_search) {
823                             dpi_string = NULL;
824                             dpi_string = repr->attribute("inkscape:export-ydpi");
825                             if (dpi_string != NULL) {
826                                 ydpi = atof(dpi_string);
827                                 ydpi_search = FALSE;
828                             }
829                         }
830                     }
832                     /* If we still don't have a filename -- let's build
833                        one that's nice */
834                     if (filename == NULL) {
835                         const gchar * id = NULL;
836                         reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
837                         for(; reprlst != NULL; reprlst = reprlst->next) {
838                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
839                             if (repr->attribute("id")) {
840                                 id = repr->attribute("id");
841                                 break;
842                             }
843                         }
844                         if (id == NULL) /* This should never happen */
845                             id = "bitmap";
847                         gchar * directory = NULL;
848                         const gchar * file_entry_text;
850                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
851                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
852                             // std::cout << "Directory from dialog" << std::endl;
853                             directory = g_dirname(file_entry_text);
854                         }
856                         if (directory == NULL) {
857                             /* Grab document directory */
858                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
859                                 // std::cout << "Directory from document" << std::endl;
860                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
861                             }
862                         }
864                         if (directory == NULL) {
865                             // std::cout << "Home Directory" << std::endl;
866                             directory = homedir_path(NULL);
867                         }
869                         gchar * id_ext = g_strconcat(id, ".png", NULL);
870                         filename = g_build_filename(directory, id_ext, NULL);
871                         g_free(directory);
872                         g_free(id_ext);
873                     }
874                 }
875                 break;
876             case SELECTION_CUSTOM:
877             default:
878                 break;
879         }
881         if (filename != NULL) {
882             g_free(original_name);
883             original_name = g_strdup(filename);
884             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
885         }
887         if (xdpi != 0.0) {
888             sp_export_value_set(base, "xdpi", xdpi);
889         }
891         /* These can't be seperate, and setting x sets y, so for
892            now setting this is disabled.  Hopefully it won't be in
893            the future */
894         if (FALSE && ydpi != 0.0) {
895             sp_export_value_set(base, "ydpi", ydpi);
896         }
897     }
899     return;
900 } // end of sp_export_area_toggled()
902 /// Called when dialog is deleted
903 static gint
904 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
906     g_object_set_data (base, "cancel", (gpointer) 1);
907     return TRUE;
908 } // end of sp_export_progress_delete()
910 /// Called when progress is cancelled
911 static void
912 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
914     g_object_set_data (base, "cancel", (gpointer) 1);
915 } // end of sp_export_progress_cancel()
917 /// Called for every progress iteration
918 static unsigned int
919 sp_export_progress_callback (float value, void *data)
921     GtkWidget *prg;
922     int evtcount;
924     if (g_object_get_data ((GObject *) data, "cancel"))
925         return FALSE;
927     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
928     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
930     evtcount = 0;
931     while ((evtcount < 16) && gdk_events_pending ()) {
932             gtk_main_iteration_do (FALSE);
933             evtcount += 1;
934     }
936     gtk_main_iteration_do (FALSE);
938     return TRUE;
940 } // end of sp_export_progress_callback()
942 /// Called when export button is clicked
943 static void
944 sp_export_export_clicked (GtkButton *button, GtkObject *base)
946     if (!SP_ACTIVE_DESKTOP) return;
948     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
949     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
951     float const x0 = sp_export_value_get_px(base, "x0");
952     float const y0 = sp_export_value_get_px(base, "y0");
953     float const x1 = sp_export_value_get_px(base, "x1");
954     float const y1 = sp_export_value_get_px(base, "y1");
955     float const xdpi = sp_export_value_get(base, "xdpi");
956     float const ydpi = sp_export_value_get(base, "ydpi");
957     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
958     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
960     if (filename == NULL || *filename == '\0') {
961         sp_ui_error_dialog(_("You have to enter a filename"));
962         return;
963     }
965     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
966         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
967         return;
968     }
970     gchar *dirname = g_dirname(filename);
971     if ( dirname == NULL
972          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
973     {
974         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
975         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
976                                        safeDir);
977         sp_ui_error_dialog(error);
978         g_free(safeDir);
979         g_free(error);
980         g_free(dirname);
981         return;
982     }
983     g_free(dirname);
985     SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
986     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
987     char *fn;
988     gchar *text;
990     dlg = gtk_dialog_new ();
991     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
992     prg = gtk_progress_bar_new ();
993     sp_transientize (dlg);
994     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
995     g_object_set_data ((GObject *) base, "progress", prg);
996     fn = g_path_get_basename (filename);
997     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
998                              fn, width, height);
999     g_free (fn);
1000     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1001     g_free (text);
1002     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1003                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1004     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1005                         prg, FALSE, FALSE, 4 );
1006     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1007                                   GTK_STOCK_CANCEL, 
1008                                   GTK_RESPONSE_CANCEL );
1009                                   
1010     g_signal_connect ( (GObject *) dlg, "delete_event", 
1011                        (GCallback) sp_export_progress_delete, base);
1012     g_signal_connect ( (GObject *) btn, "clicked", 
1013                        (GCallback) sp_export_progress_cancel, base);
1014     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1015     gtk_widget_show_all (dlg);
1016     
1017     /* Do export */
1018     if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename, 
1019                              x0, y0, x1, y1, width, height, 
1020                              nv->pagecolor, 
1021                              sp_export_progress_callback, base)) {
1022         gchar * error;
1023         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1024         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1025         sp_ui_error_dialog(error);
1026         g_free(safeFile);
1027         g_free(error);
1028     }
1030     /* Reset the filename so that it can be changed again by changing
1031        selections and all that */
1032     g_free(original_name);
1033     original_name = g_strdup(filename);
1034     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1036     gtk_widget_destroy (dlg);
1037     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1039     /* Setup the values in the document */
1040     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1041         case SELECTION_PAGE:
1042         case SELECTION_DRAWING: {
1043             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1044             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1045             bool modified = FALSE;
1046             const gchar * temp_string;
1048             bool saved = sp_document_get_undo_sensitive(doc);
1049             sp_document_set_undo_sensitive(doc, FALSE);
1051             temp_string = repr->attribute("inkscape:export-filename");
1052             if (temp_string == NULL || strcmp(temp_string, filename)) {
1053                 repr->setAttribute("inkscape:export-filename", filename);
1054                 modified = TRUE;
1055             }
1056             temp_string = repr->attribute("inkscape:export-xdpi");
1057             if (temp_string == NULL || xdpi != atof(temp_string)) {
1058                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1059                 modified = TRUE;
1060             }
1061             temp_string = repr->attribute("inkscape:export-ydpi");
1062             if (temp_string == NULL || xdpi != atof(temp_string)) {
1063                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1064                 modified = TRUE;
1065             }
1067             if (modified)
1068                 repr->setAttribute("sodipodi:modified", "TRUE");
1069             sp_document_set_undo_sensitive(doc, saved);
1070             break;
1071         }
1072         case SELECTION_SELECTION: {
1073             const GSList * reprlst;
1074             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1075             bool modified = FALSE;
1077             bool saved = sp_document_get_undo_sensitive(doc);
1078             sp_document_set_undo_sensitive(doc, FALSE);
1079             reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1081             for(; reprlst != NULL; reprlst = reprlst->next) {
1082                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1083                 const gchar * temp_string;
1085                 if (repr->attribute("id") == NULL ||
1086                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1087                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1088                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1089                     temp_string = repr->attribute("inkscape:export-filename");
1090                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1091                         repr->setAttribute("inkscape:export-filename", filename);
1092                         modified = TRUE;
1093                     }
1094                 }
1095                 temp_string = repr->attribute("inkscape:export-xdpi");
1096                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1097                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1098                     modified = TRUE;
1099                 }
1100                 temp_string = repr->attribute("inkscape:export-ydpi");
1101                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1102                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1103                     modified = TRUE;
1104                 }
1105             }
1107             if (modified) {
1108                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1109                 repr->setAttribute("sodipodi:modified", "TRUE");
1110             }
1112             sp_document_set_undo_sensitive(doc, saved);
1113             break;
1114         }
1115         default:
1116             break;
1117     }
1120     return;
1121 } // end of sp_export_export_clicked()
1123 /// Called when Browse button is clicked
1124 static void
1125 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1127     GtkWidget *fs, *fe;
1128     const gchar *filename;
1130     fs = gtk_file_selection_new (_("Select a filename for exporting"));
1131     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1133     sp_transientize (fs);
1135     gtk_window_set_modal(GTK_WINDOW (fs), true);
1137     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1139     if (*filename == '\0') {
1140         filename = homedir_path(NULL);
1141     }
1143     gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1145     g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1146                        "clicked",
1147                        G_CALLBACK (sp_export_browse_store),
1148                        (gpointer) fs );
1150     g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1151                                "clicked",
1152                                G_CALLBACK (gtk_widget_destroy),
1153                                (gpointer) fs );
1155     g_signal_connect_swapped ( GTK_OBJECT
1156                                    (GTK_FILE_SELECTION (fs)->cancel_button),
1157                                "clicked",
1158                                G_CALLBACK (gtk_widget_destroy),
1159                                (gpointer) fs );
1161     gtk_widget_show (fs);
1163     return;
1164 } // end of sp_export_browse_clicked()
1166 /// Called when OK clicked in file dialog
1167 static void
1168 sp_export_browse_store (GtkButton *button, gpointer userdata)
1170     GtkWidget *fs = (GtkWidget *)userdata, *fe;
1171     const gchar *file;
1173     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1175     file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1176     gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1177     gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1178     g_free(utf8file);
1180     g_object_set_data (G_OBJECT (dlg), "filename", fe);
1182     return;
1183 } // end of sp_export_browse_store()
1185 // TODO: Move this to nr-rect-fns.h.
1186 static bool
1187 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1188
1189     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1190     return (
1191         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1192         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1193         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1194         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1195         );
1198 /**
1199     \brief  This function is used to detect the current selection setting
1200             based on the values in the x0, y0, x1 and y0 fields.
1201     \param  base  The export dialog itself
1203     One of the most confusing parts of this function is why the array
1204     is built at the beginning.  What needs to happen here is that we
1205     should always check the current selection to see if it is the valid
1206     one.  While this is a performance improvement it is also a usability
1207     one during the cases where things like selections and drawings match
1208     size.  This way buttons change less 'randomly' (atleast in the eyes
1209     of the user).  To do this an array is built where the current selection
1210     type is placed first, and then the others in an order from smallest
1211     to largest (this can be configured by reshuffling \c test_order).
1213     All of the values in this function are rounded to two decimal places
1214     because that is what is shown to the user.  While everything is kept
1215     more accurate than that, the user can't control more acurrate than
1216     that, so for this to work for them - it needs to check on that level
1217     of accuracy.
1219     \todo finish writing this up
1220 */
1221 static void
1222 sp_export_detect_size(GtkObject * base) {
1223     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1224     selection_type this_test[SELECTION_NUMBER_OF + 1];
1225     selection_type key = SELECTION_NUMBER_OF;
1227     NR::Point x(sp_export_value_get_px (base, "x0"),
1228                 sp_export_value_get_px (base, "y0"));
1229     NR::Point y(sp_export_value_get_px (base, "x1"),
1230                 sp_export_value_get_px (base, "y1"));
1231     NR::Rect current_bbox(x, y);
1232     //std::cout << "Current " << current_bbox;
1234     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1235     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1236         this_test[i + 1] = test_order[i];
1237     }
1239     for (int i = 0;
1240             i < SELECTION_NUMBER_OF + 1 &&
1241                 key == SELECTION_NUMBER_OF &&
1242                 SP_ACTIVE_DESKTOP != NULL;
1243             i++) {
1244         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1245         switch (this_test[i]) {
1246             case SELECTION_SELECTION:
1247                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1248                     NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1250                     //std::cout << "Selection " << bbox;
1251                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1252                         key = SELECTION_SELECTION;
1253                     }
1254                 }
1255                 break;
1256             case SELECTION_DRAWING: {
1257                 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1259                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1261                 // std::cout << "Drawing " << bbox2;
1262                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1263                     key = SELECTION_DRAWING;
1264                 }
1265                 break;
1266             }
1268             case SELECTION_PAGE: {
1269                 SPDocument *doc;
1271                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1273                 NR::Point x(0.0, 0.0);
1274                 NR::Point y(sp_document_width(doc),
1275                             sp_document_height(doc));
1276                 NR::Rect bbox(x, y);
1278                 // std::cout << "Page " << bbox;
1279                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1280                     key = SELECTION_PAGE;
1281                 }
1283                 break;
1284            }
1285         default:
1286            break;
1287         }
1288     }
1289     // std::cout << std::endl;
1291     if (key == SELECTION_NUMBER_OF) {
1292         key = SELECTION_CUSTOM;
1293     }
1295     /* We're now using a custom size, not a fixed one */
1296     /* printf("Detecting state: %s\n", selection_names[key]); */
1297     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1298     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1299     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1300     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1302     return;
1303 } /* sp_export_detect_size */
1305 /// Called when area x0 value is changed
1306 static void
1307 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1309     float x0, x1, xdpi, width;
1311     if (gtk_object_get_data (base, "update"))
1312         return;
1314     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1315             (base, "units")))
1316     {
1317         return;
1318     }
1320     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1322     x0 = sp_export_value_get_px (base, "x0");
1323     x1 = sp_export_value_get_px (base, "x1");
1324     xdpi = sp_export_value_get (base, "xdpi");
1326     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1328     if (width < SP_EXPORT_MIN_SIZE) {
1329         const gchar *key;
1330         width = SP_EXPORT_MIN_SIZE;
1331         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1333         if (!strcmp (key, "x0")) {
1334             x1 = x0 + width * DPI_BASE / xdpi;
1335             sp_export_value_set_px (base, "x1", x1);
1336         } else {
1337             x0 = x1 - width * DPI_BASE / xdpi;
1338             sp_export_value_set_px (base, "x0", x0);
1339         }
1340     }
1342     sp_export_value_set_px (base, "width", x1 - x0);
1343     sp_export_value_set (base, "bmwidth", width);
1345     sp_export_detect_size(base);
1347     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1349     return;
1350 } // end of sp_export_area_x_value_changed()
1352 /// Called when area y0 value is changed.
1353 static void
1354 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1356     float y0, y1, ydpi, height;
1358     if (gtk_object_get_data (base, "update"))
1359         return;
1361     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1362            (base, "units")))
1363     {
1364         return;
1365     }
1367     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1369     y0 = sp_export_value_get_px (base, "y0");
1370     y1 = sp_export_value_get_px (base, "y1");
1371     ydpi = sp_export_value_get (base, "ydpi");
1373     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1375     if (height < SP_EXPORT_MIN_SIZE) {
1376         const gchar *key;
1377         height = SP_EXPORT_MIN_SIZE;
1378         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1379         if (!strcmp (key, "y0")) {
1380             y1 = y0 + height * DPI_BASE / ydpi;
1381             sp_export_value_set_px (base, "y1", y1);
1382         } else {
1383             y0 = y1 - height * DPI_BASE / ydpi;
1384             sp_export_value_set_px (base, "y0", y0);
1385         }
1386     }
1388     sp_export_value_set_px (base, "height", y1 - y0);
1389     sp_export_value_set (base, "bmheight", height);
1391     sp_export_detect_size(base);
1393     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1395     return;
1396 } // end of sp_export_area_y_value_changed()
1398 /// Called when x1-x0 or area width is changed
1399 static void
1400 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1402     float x0, x1, xdpi, width, bmwidth;
1404     if (gtk_object_get_data (base, "update"))
1405         return;
1407     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1408            (base, "units"))) {
1409         return;
1410     }
1412     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1414     x0 = sp_export_value_get_px (base, "x0");
1415     x1 = sp_export_value_get_px (base, "x1");
1416     xdpi = sp_export_value_get (base, "xdpi");
1417     width = sp_export_value_get_px (base, "width");
1418     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1420     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1422         bmwidth = SP_EXPORT_MIN_SIZE;
1423         width = bmwidth * DPI_BASE / xdpi;
1424         sp_export_value_set_px (base, "width", width);
1425     }
1427     sp_export_value_set_px (base, "x1", x0 + width);
1428     sp_export_value_set (base, "bmwidth", bmwidth);
1430     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1432     return;
1433 } // end of sp_export_area_width_value_changed()
1435 /// Called when y1-y0 or area height is changed.
1436 static void
1437 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1440     float y0, y1, ydpi, height, bmheight;
1442     if (gtk_object_get_data (base, "update"))
1443         return;
1445     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1446            (base, "units"))) {
1447         return;
1448     }
1450     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1452     y0 = sp_export_value_get_px (base, "y0");
1453     y1 = sp_export_value_get_px (base, "y1");
1454     ydpi = sp_export_value_get (base, "ydpi");
1455     height = sp_export_value_get_px (base, "height");
1456     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1458     if (bmheight < SP_EXPORT_MIN_SIZE) {
1459         bmheight = SP_EXPORT_MIN_SIZE;
1460         height = bmheight * DPI_BASE / ydpi;
1461         sp_export_value_set_px (base, "height", height);
1462     }
1464     sp_export_value_set_px (base, "y1", y0 + height);
1465     sp_export_value_set (base, "bmheight", bmheight);
1467     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1469     return;
1470 } // end of sp_export_area_height_value_changed()
1472 /**
1473     \brief  A function to set the ydpi
1474     \param  base  The export dialog
1476     This function grabs all of the y values and then figures out the
1477     new bitmap size based on the changing dpi value.  The dpi value is
1478     gotten from the xdpi setting as these can not currently be independent.
1479 */
1480 static void
1481 sp_export_set_image_y (GtkObject *base)
1483     float y0, y1, xdpi;
1485     y0 = sp_export_value_get_px (base, "y0");
1486     y1 = sp_export_value_get_px (base, "y1");
1487     xdpi = sp_export_value_get (base, "xdpi");
1489     sp_export_value_set (base, "ydpi", xdpi);
1490     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1492     return;
1493 } // end of sp_export_set_image_y()
1495 /// Called when pixel width is changed
1496 static void
1497 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1499     float x0, x1, bmwidth, xdpi;
1501     if (gtk_object_get_data (base, "update"))
1502         return;
1504     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1505            (base, "units"))) {
1506        return;
1507     }
1509     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1511     x0 = sp_export_value_get_px (base, "x0");
1512     x1 = sp_export_value_get_px (base, "x1");
1513     bmwidth = sp_export_value_get (base, "bmwidth");
1515     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1516         bmwidth = SP_EXPORT_MIN_SIZE;
1517         sp_export_value_set (base, "bmwidth", bmwidth);
1518     }
1520     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1521     sp_export_value_set (base, "xdpi", xdpi);
1523     sp_export_set_image_y (base);
1525     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1527     return;
1528 } // end of sp_export_bitmap_width_value_changed()
1530 /**
1531     \brief  A function to adjust the bitmap width when the xdpi value changes
1532     \param  adj  The adjustment that was changed
1533     \param  base The export dialog itself
1535     The first thing this function checks is to see if we are doing an
1536     update.  If we are, this function just returns because there is another
1537     instance of it that will handle everything for us.  If there is a
1538     units change, we also assume that everyone is being updated appropriately
1539     and there is nothing for us to do.
1541     If we're the highest level function, we set the update flag, and
1542     continue on our way.
1544     All of the values are grabbed using the \c sp_export_value_get functions
1545     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1546     xdpi value is saved in the preferences for the next time the dialog
1547     is opened.  (does the selection dpi need to be set here?)
1549     A check is done to to ensure that we aren't outputing an invalid width,
1550     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1551     changed to make it valid.
1553     After all of this the bitmap width is changed.
1555     We also change the ydpi.  This is a temporary hack as these can not
1556     currently be independent.  This is likely to change in the future.
1557 */
1558 void
1559 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1561     float x0, x1, xdpi, bmwidth;
1563     if (gtk_object_get_data (base, "update"))
1564         return;
1566     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1567            (base, "units"))) {
1568        return;
1569     }
1571     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1573     x0 = sp_export_value_get_px (base, "x0");
1574     x1 = sp_export_value_get_px (base, "x1");
1575     xdpi = sp_export_value_get (base, "xdpi");
1577     // remember xdpi setting
1578     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1580     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1582     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1583         bmwidth = SP_EXPORT_MIN_SIZE;
1584         if (x1 != x0)
1585             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1586         else
1587             xdpi = DPI_BASE;
1588         sp_export_value_set (base, "xdpi", xdpi);
1589     }
1591     sp_export_value_set (base, "bmwidth", bmwidth);
1593     sp_export_set_image_y (base);
1595     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1597     return;
1598 } // end of sp_export_xdpi_value_changed()
1601 /**
1602     \brief  A function to change the area that is used for the exported
1603             bitmap.
1604     \param  base  This is the export dialog
1605     \param  x0    Horizontal upper left hand corner of the picture in points
1606     \param  y0    Vertical upper left hand corner of the picture in points
1607     \param  x1    Horizontal lower right hand corner of the picture in points
1608     \param  y1    Vertical lower right hand corner of the picture in points
1610     This function just calls \c sp_export_value_set_px for each of the
1611     parameters that is passed in.  This allows for setting them all in
1612     one convient area.
1614     Update is set to suspend all of the other test running while all the
1615     values are being set up.  This allows for a performance increase, but
1616     it also means that the wrong type won't be detected with only some of
1617     the values set.  After all the values are set everyone is told that
1618     there has been an update.
1619 */
1620 static void
1621 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1623     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1624     sp_export_value_set_px (base, "x1", x1);
1625     sp_export_value_set_px (base, "y1", y1);
1626     sp_export_value_set_px (base, "x0", x0);
1627     sp_export_value_set_px (base, "y0", y0);
1628     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1630     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1631     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1633     return;
1636 /**
1637     \brief  Sets the value of an adjustment
1638     \param  base  The export dialog
1639     \param  key   Which adjustment to set
1640     \param  val   What value to set it to
1642     This function finds the adjustment using the data stored in the
1643     export dialog.  After finding the adjustment it then sets
1644     the value of it.
1645 */
1646 static void
1647 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1649     GtkAdjustment *adj;
1651     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1653     gtk_adjustment_set_value (adj, val);
1656 /**
1657     \brief  A function to set a value using the units points
1658     \param  base  The export dialog
1659     \param  key   Which value should be set
1660     \param  val   What the value should be in points
1662     This function first gets the adjustment for the key that is passed
1663     in.  It then figures out what units are currently being used in the
1664     dialog.  After doing all of that, it then converts the incoming
1665     value and sets the adjustment.
1666 */
1667 static void
1668 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1670     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1672     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1674     return;
1677 /**
1678     \brief  Get the value of an adjustment in the export dialog
1679     \param  base  The export dialog
1680     \param  key   Which adjustment is being looked for
1681     \return The value in the specified adjustment
1683     This function gets the adjustment from the data field in the export
1684     dialog.  It then grabs the value from the adjustment.
1685 */
1686 static float
1687 sp_export_value_get ( GtkObject *base, const gchar *key )
1689     GtkAdjustment *adj;
1691     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1693     return adj->value;
1694 } // end of sp_export_value_get()
1696 /**
1697     \brief  Grabs a value in the export dialog and converts the unit
1698             to points
1699     \param  base  The export dialog
1700     \param  key   Which value should be returned
1701     \return The value in the adjustment in points
1703     This function, at its most basic, is a call to \c sp_export_value_get
1704     to get the value of the adjustment.  It then finds the units that
1705     are being used by looking at the "units" attribute of the export
1706     dialog.  Using that it converts the returned value into points.
1707 */
1708 static float
1709 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1711     float value = sp_export_value_get(base, key);
1712     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1714     return sp_units_get_pixels (value, *unit);
1715 } // end of sp_export_value_get_px()
1717 /**
1718     \brief  This function is called when the filename is changed by
1719             anyone.  It resets the virgin bit.
1720     \param  object  Text entry box
1721     \param  data    The export dialog
1722     \return None
1724     This function gets called when the text area is modified.  It is
1725     looking for the case where the text area is modified from its
1726     original value.  In that case it sets the "filename-modified" bit
1727     to TRUE.  If the text dialog returns back to the original text, the
1728     bit gets reset.  This should stop simple mistakes.
1729 */
1730 static void
1731 sp_export_filename_modified (GtkObject * object, gpointer data)
1733     GtkWidget * text_entry = (GtkWidget *)object;
1734     GtkWidget * export_dialog = (GtkWidget *)data;
1736     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1737         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1738 //        printf("Modified: FALSE\n");
1739     } else {
1740         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1741 //        printf("Modified: TRUE\n");
1742     }
1744     return;
1745 } // end sp_export_filename_modified
1747 /*
1748   Local Variables:
1749   mode:c++
1750   c-file-style:"stroustrup"
1751   c-file-offsets:((innamespace . 0)(inline-open . 0))
1752   indent-tabs-mode:nil
1753   fill-column:99
1754   End:
1755 */
1756 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :