Code

expand the range of dpi to fix 1476741
[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 );
59 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
60                                                    GtkObject *base);
61                                              
62 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
63                                                    GtkObject *base);
64                                              
65 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
66                                                    GtkObject *base);
67                                                  
68 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
69                                                    GtkObject *base);
70                                                   
71 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
72                                                    GtkObject *base);
73                                                    
74 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj, 
75                                                    GtkObject *base);
76                                                    
77 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
78                                                    GtkObject *base);
79                                            
80 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
81                                           Inkscape::Selection *selection, 
82                                           GtkObject *base);
83 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
84                                            Inkscape::Selection *selection, 
85                                            guint flags,
86                                            GtkObject *base );
88 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
89 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
90 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
91 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
92 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
94 static void sp_export_filename_modified (GtkObject * object, gpointer data);
95 static inline void sp_export_find_default_selection(GtkWidget * dlg);
96 static void sp_export_detect_size(GtkObject * base);
98 static const gchar *prefs_path = "dialogs.export";
100 // these all need to be reinitialized to their defaults during dialog_destroy
101 static GtkWidget *dlg = NULL;
102 static win_data wd;
103 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
104 static gchar * original_name = NULL;
105 static gchar * doc_export_name = NULL;
106 static bool was_empty = TRUE;
108 /** What type of button is being pressed */
109 enum selection_type {
110     SELECTION_PAGE = 0,  /**< Export the whole page */
111     SELECTION_DRAWING,   /**< Export everything drawn on the page */
112     SELECTION_SELECTION, /**< Export everything that is selected */
113     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
114     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
115 };
117 /** A list of strings that is used both in the preferences, and in the
118     data fields to describe the various values of \c selection_type. */
119 static const char * selection_names[SELECTION_NUMBER_OF] = {
120     "page", "drawing", "selection", "custom"};
122 /** The names on the buttons for the various selection types. */
123 static const char * selection_labels[SELECTION_NUMBER_OF] = {
124     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
126 static void
127 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
129     sp_signal_disconnect_by_data (INKSCAPE, dlg);
131     wd.win = dlg = NULL;
132     wd.stop = 0;
133     x = -1000; y = -1000; w = 0; h = 0;
134     g_free(original_name);
135     original_name = NULL;
136     g_free(doc_export_name);
137     doc_export_name = NULL;
138     was_empty = TRUE;
140     return;
141 } // end of sp_export_dialog_destroy()
143 /// Called when dialog is closed or inkscape is shut down.
144 static bool
145 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
148     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
149     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
151     if (x<0) x=0;
152     if (y<0) y=0;
154     prefs_set_int_attribute (prefs_path, "x", x);
155     prefs_set_int_attribute (prefs_path, "y", y);
156     prefs_set_int_attribute (prefs_path, "w", w);
157     prefs_set_int_attribute (prefs_path, "h", h);
159     return FALSE; // which means, go ahead and destroy it
161 } // end of sp_export_dialog_delete()
163 /**
164     \brief  Creates a new spin button for the export dialog
165     \param  key  The name of the spin button
166     \param  val  A default value for the spin button
167     \param  min  Minimum value for the spin button
168     \param  max  Maximum value for the spin button
169     \param  step The step size for the spin button
170     \param  page Size of the page increment
171     \param  us   Unit selector that effects this spin button
172     \param  t    Table to put the spin button in
173     \param  x    X location in the table \c t to start with
174     \param  y    Y location in the table \c t to start with
175     \param  ll   Text to put on the left side of the spin button (optional)
176     \param  lr   Text to put on the right side of the spin button (optional)
177     \param  digits  Number of digits to display after the decimal
178     \param  sensitive  Whether the spin button is sensitive or not
179     \param  cb   Callback for when this spin button is changed (optional)
180     \param  dlg  Export dialog the spin button is being placed in
182 */
183 static void
184 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
185                            float step, float page, GtkWidget *us,
186                            GtkWidget *t, int x, int y,
187                            const gchar *ll, const gchar *lr,
188                            int digits, unsigned int sensitive,
189                            GCallback cb, GtkWidget *dlg )
191     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
192     gtk_object_set_data (a, "key", key);
193     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
195     if (us) {
196         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
197                                           GTK_ADJUSTMENT (a) );
198     }
200     int pos = 0;
202     GtkWidget *l = NULL;
204     if (ll) {
206         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
207         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
208         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
209                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
210         gtk_widget_set_sensitive (l, sensitive);
211         pos += 1;
213     }
215     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
216     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
217                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
218     gtk_widget_set_size_request (sb, 80, -1);
219     gtk_widget_set_sensitive (sb, sensitive);
220     pos += 1;
222     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
224     if (lr) {
226         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
227         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
228         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
229                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
230         gtk_widget_set_sensitive (l, sensitive);
231         pos += 1;
233         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
234     }
236     if (cb)
237         gtk_signal_connect (a, "value_changed", cb, dlg);
239     return;
240 } // end of sp_export_spinbutton_new()
243 static GtkWidget *
244 sp_export_dialog_area_frame (GtkWidget * dlg)
246     GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
248     f = gtk_frame_new (_("Export area"));
249     vb = gtk_vbox_new (FALSE, 2);
250     gtk_container_add (GTK_CONTAINER (f), vb);
252     /* Units box */
253     unitbox = gtk_hbox_new (FALSE, 0);
254     gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
255     /* gets added to the vbox later, but the unit selector is needed
256        earlier than that */
258     us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
259     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
260     if (desktop)
261         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
262     gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
263     l = gtk_label_new (_("Units:"));
264     gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
265     gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
267     hb = gtk_hbox_new (TRUE, 0);
268     gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
269     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
271     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
272         b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
273         gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
274         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
275         gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
276         gtk_signal_connect ( GTK_OBJECT (b), "clicked", 
277                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
278     }
280     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
281                        G_CALLBACK (sp_export_selection_changed), dlg );
282     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
283                        G_CALLBACK (sp_export_selection_modified), dlg );
284     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
285                        G_CALLBACK (sp_export_selection_changed), dlg );
286     
287     t = gtk_table_new (2, 6, FALSE);
288     gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
289     gtk_table_set_row_spacings (GTK_TABLE (t), 4);
290     gtk_table_set_col_spacings (GTK_TABLE (t), 4);
291     gtk_container_set_border_width (GTK_CONTAINER (t), 4);
293     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
294                                t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
295                                G_CALLBACK ( sp_export_area_x_value_changed), 
296                                dlg );
298     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
299                                t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
300                                G_CALLBACK (sp_export_area_x_value_changed), 
301                                dlg );
303     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
304                                us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
305                                G_CALLBACK 
306                                    (sp_export_area_width_value_changed), 
307                                dlg );
309     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
310                                t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
311                                G_CALLBACK (sp_export_area_y_value_changed), 
312                                dlg );
314     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
315                                t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
316                                G_CALLBACK (sp_export_area_y_value_changed), 
317                                dlg );
319     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
320                                us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
321                                G_CALLBACK (sp_export_area_height_value_changed), 
322                                dlg );
324     /* Adding in the unit box */
325     gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
327     return f;
328 } // end of sp_export_dialog_area_frame
331 void
332 sp_export_dialog (void)
334     if (!dlg) {
335         GtkWidget *vb, *hb;
337         gchar title[500];
338         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
340         dlg = sp_window_new (title, TRUE);
342         if (x == -1000 || y == -1000) {
343             x = prefs_get_int_attribute (prefs_path, "x", 0);
344             y = prefs_get_int_attribute (prefs_path, "y", 0);
345         }
347         if (w ==0 || h == 0) {
348             w = prefs_get_int_attribute (prefs_path, "w", 0);
349             h = prefs_get_int_attribute (prefs_path, "h", 0);
350         }
352         if (x<0) x=0;
353         if (y<0) y=0;
355         if (x != 0 || y != 0) {
356             gtk_window_move ((GtkWindow *) dlg, x, y);
357         } else {
358             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
359         }
361         if (w && h)
362             gtk_window_resize ((GtkWindow *) dlg, w, h);
364         sp_transientize (dlg);
365         wd.win = dlg;
366         wd.stop = 0;
368         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
369                              G_CALLBACK (sp_transientize_callback), &wd);
371         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
372                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
374         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
375                              G_CALLBACK (sp_export_dialog_destroy), dlg);
377         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
378                              G_CALLBACK (sp_export_dialog_delete), dlg);
380         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
381                              G_CALLBACK (sp_export_dialog_delete), dlg);
383         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
384                              G_CALLBACK (sp_dialog_hide), dlg);
386         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
387                              G_CALLBACK (sp_dialog_unhide), dlg);
389         GtkTooltips *tt = gtk_tooltips_new();
391         vb = gtk_vbox_new (FALSE, 4);
392         gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
393         gtk_container_add (GTK_CONTAINER (dlg), vb);
395         /* Export area frame */
396         {
397             GtkWidget *f = sp_export_dialog_area_frame(dlg);
398             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
399         }
401         /* Bitmap size frame */
402         {
403             GtkWidget *f = gtk_frame_new (_("Bitmap size"));
404             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
405             GtkWidget *t = gtk_table_new (2, 5, FALSE);
406             gtk_table_set_row_spacings (GTK_TABLE (t), 4);
407             gtk_table_set_col_spacings (GTK_TABLE (t), 4);
408             gtk_container_set_border_width (GTK_CONTAINER (t), 4);
409             gtk_container_add (GTK_CONTAINER (f), t);
411             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
412                                        NULL, t, 0, 0,
413                                        _("_Width:"), _("pixels at"), 0, 1,
414                                        G_CALLBACK 
415                                        (sp_export_bitmap_width_value_changed), 
416                                        dlg );
418             sp_export_spinbutton_new ( "xdpi", 
419                                        prefs_get_double_attribute 
420                                        ( "dialogs.export.defaultxdpi", 
421                                          "value", DPI_BASE), 
422                                        0.01, 100000.0, 0.1, 1.0, NULL, t, 3, 0,
423                                        NULL, _("dp_i"), 2, 1,
424                                        G_CALLBACK (sp_export_xdpi_value_changed), 
425                                        dlg );
427             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
428                                        NULL, t, 0, 1, 
429                                        _("Height:"), _("pixels at"), 0, 1, 
430                                        G_CALLBACK
431                                        (sp_export_bitmap_height_value_changed), 
432                                        dlg );
434             /** \todo
435              * Needs fixing: there's no way to set ydpi currently, so we use  
436              *       the defaultxdpi value here, too...
437              */
438             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
439                                        ( "dialogs.export.defaultxdpi", 
440                                          "value", DPI_BASE), 
441                                        0.01, 100000.0, 0.1, 1.0, NULL, t, 3, 1,
442                                        NULL, _("dpi"), 2, 0, NULL, dlg );
443         }
445         /* File entry */
446         {
447             GtkWidget *frame = gtk_frame_new ("");
448             GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
449             gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
450             gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
452             GtkWidget *fe = gtk_entry_new ();
454             /*
455              * set the default filename to be that of the current path + document
456              * with .png extension
457              *
458              * One thing to notice here is that this filename may get
459              * overwritten, but it won't happen here.  The filename gets
460              * written into the text field, but then the button to select
461              * the area gets set.  In that code the filename can be changed
462              * if there are some with presidence in the document.  So, while
463              * this code sets the name first, it may not be the one users
464              * really see.
465              */
466             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
467             {
468                 gchar *name;
469                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
470                 const gchar *uri = SP_DOCUMENT_URI (doc);
471                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
472                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
473                 Inkscape::Extension::Output * oextension = NULL;
475                 if (text_extension != NULL) {
476                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
477                 }
479                 if (oextension != NULL) {
480                     gchar * old_extension = oextension->get_extension();
481                     if (g_str_has_suffix(uri, old_extension)) {
482                         gchar * uri_copy;
483                         gchar * extension_point;
484                         gchar * final_name;
486                         uri_copy = g_strdup(uri);
487                         extension_point = g_strrstr(uri_copy, old_extension);
488                         extension_point[0] = '\0';
490                         final_name = g_strconcat(uri_copy, ".png", NULL);
491                         gtk_entry_set_text (GTK_ENTRY (fe), final_name);
493                         g_free(final_name);
494                         g_free(uri_copy);
495                     }
496                 } else {
497                     name = g_strconcat(uri, ".png", NULL);
498                     gtk_entry_set_text (GTK_ENTRY (fe), name);
499                     g_free(name);
500                 }
502                 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
503             }
504             g_signal_connect ( G_OBJECT (fe), "changed",
505                                G_CALLBACK (sp_export_filename_modified), dlg);
507             hb = gtk_hbox_new (FALSE, 5);
508             gtk_container_add (GTK_CONTAINER (frame), hb);
509             gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
511             {
512                 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
513                 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
514                 g_signal_connect ( G_OBJECT (b), "clicked",
515                                    G_CALLBACK (sp_export_browse_clicked), NULL );
516             }
518             gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
519             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
520             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
521             original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
522             // pressing enter in the filename field is the same as clicking export:
523             g_signal_connect ( G_OBJECT (fe), "activate",
524                                G_CALLBACK (sp_export_export_clicked), dlg );
525             // focus is in the filename initially:
526             gtk_widget_grab_focus (GTK_WIDGET (fe));
528             // mnemonic in frame label moves focus to filename:
529             gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
530         }
532         /* Buttons */
533         hb = gtk_hbox_new (FALSE, 0);
534         gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
536         {
537             GtkWidget *b = gtk_button_new ();
538             GtkWidget *l = gtk_label_new ("");
539             gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
540             gtk_container_add (GTK_CONTAINER(b), l);
541             gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
542             gtk_signal_connect ( GTK_OBJECT (b), "clicked",
543                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
544             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
545         }
547         gtk_widget_show_all (vb);
549     } // end of if (!dlg)
551     sp_export_find_default_selection(dlg);
553     gtk_window_present ((GtkWindow *) dlg);
555     return;
556 } // end of sp_export_dialog()
558 static inline void
559 sp_export_find_default_selection(GtkWidget * dlg)
561     selection_type key = SELECTION_NUMBER_OF;
563     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
564         key = SELECTION_SELECTION;
565     }
567     /* Try using the preferences */
568     if (key == SELECTION_NUMBER_OF) {
569         const gchar *what = NULL;
570         int i = SELECTION_NUMBER_OF;
572         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
574         if (what != NULL) {
575             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
576                 if (!strcmp (what, selection_names[i])) {
577                     break;
578                 }
579             }
580         }
582         key = (selection_type)i;
583     }
585     if (key == SELECTION_NUMBER_OF) {
586         key = SELECTION_SELECTION;
587     }
589     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
590                                                        selection_names[key]);
591     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
593     return;
597 /**
598  * \brief  If selection changed or a different document activated, we must 
599  * recalculate any chosen areas
600  *
601  */
602 static void
603 sp_export_selection_changed ( Inkscape::Application *inkscape, 
604                               Inkscape::Selection *selection, 
605                               GtkObject *base )
607     selection_type current_key;
608     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
610     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
611             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
612             was_empty) {
613         gtk_toggle_button_set_active
614             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
615               TRUE );
616     }
617     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
619     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
621     if (inkscape &&
622             SP_IS_INKSCAPE (inkscape) &&
623             selection &&
624             SELECTION_CUSTOM != current_key) {
625         GtkToggleButton * button;
626         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
627         sp_export_area_toggled(button, base);
628     } // end of if()
630     return;
631 } // end of sp_export_selection_changed()
633 static void
634 sp_export_selection_modified ( Inkscape::Application *inkscape, 
635                                Inkscape::Selection *selection, 
636                                guint flags,
637                                GtkObject *base )
639     selection_type current_key;
640     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
642     switch (current_key) {
643         case SELECTION_DRAWING:
644             if ( SP_ACTIVE_DESKTOP ) {
645                 SPDocument *doc;
646                 NRRect bbox;
647                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
648                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
650                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
651                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
652                 }
653             }
654             break;
655         case SELECTION_SELECTION:
656             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
657                 NRRect bbox;
658                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
659                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
660             }
661             break;
662         default:
663             /* Do nothing for page or for custom */
664             break;
665     }
667     return;
670 /// Called when one of the selection buttons was toggled.
671 static void
672 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
674     if (gtk_object_get_data (base, "update"))
675         return;
677     selection_type key, old_key;
678     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
679     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
681     /* Ignore all "turned off" events unless we're the only active button */
682     if (!gtk_toggle_button_get_active (tb) ) {
684         /* Don't let the current selection be deactived - but rerun the
685            activate to allow the user to renew the values */
686         if (key == old_key) {
687             gtk_toggle_button_set_active ( tb, TRUE );
688         }
690         return;
691     }
693     /* Turn off the currently active button unless it's us */
694     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
696     if (old_key != key) {
697         gtk_toggle_button_set_active
698             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
699               FALSE );
700     }
702     if ( SP_ACTIVE_DESKTOP )
703     {
704         SPDocument *doc;
705         NRRect bbox;
706         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
708         /* Notice how the switch is used to 'fall through' here to get
709            various backups.  If you modify this without noticing you'll
710            probabaly screw something up. */
711         switch (key) {
712             case SELECTION_SELECTION:
713                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
714                 {
715                     (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
716                     /* Only if there is a selection that we can set
717                        do we break, otherwise we fall through to the
718                        drawing */
719                     // std::cout << "Using selection: SELECTION" << std::endl;
720                     key = SELECTION_SELECTION;
721                     break;
722                 }
723             case SELECTION_DRAWING:
724                 /** \todo 
725                  * This returns wrong values if the document has a viewBox.
726                  */
727                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
728                 
729                 /* If the drawing is valid, then we'll use it and break
730                    otherwise we drop through to the page settings */
731                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
732                     // std::cout << "Using selection: DRAWING" << std::endl;
733                     key = SELECTION_DRAWING;
734                     break;
735                 }
736             case SELECTION_PAGE:
737                 bbox.x0 = 0.0;
738                 bbox.y0 = 0.0;
739                 bbox.x1 = sp_document_width (doc);
740                 bbox.y1 = sp_document_height (doc);
741                 // std::cout << "Using selection: PAGE" << std::endl;
742                 key = SELECTION_PAGE;
743                 break;
744             case SELECTION_CUSTOM:
745             default:
746                 break;
747         } // switch
748         
749         // remember area setting
750         prefs_set_string_attribute ( "dialogs.export.exportarea", 
751                                      "value", selection_names[key]);
753         if (key != SELECTION_CUSTOM) {
754             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
755         }
756     
757     } // end of if ( SP_ACTIVE_DESKTOP )
760     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
761         GtkWidget * file_entry;
762         const gchar * filename = NULL;
763         float xdpi = 0.0, ydpi = 0.0;
765         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
767         switch (key) {
768             case SELECTION_PAGE:
769             case SELECTION_DRAWING: {
770                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
771                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
772                 const gchar * dpi_string;
774                 filename = repr->attribute("inkscape:export-filename");
776                 dpi_string = NULL;
777                 dpi_string = repr->attribute("inkscape:export-xdpi");
778                 if (dpi_string != NULL) {
779                     xdpi = atof(dpi_string);
780                 }
782                 dpi_string = NULL;
783                 dpi_string = repr->attribute("inkscape:export-ydpi");
784                 if (dpi_string != NULL) {
785                     ydpi = atof(dpi_string);
786                 }
788                 if (filename == NULL) {
789                     if (doc_export_name != NULL) {
790                         filename = g_strdup(doc_export_name);
791                     } else {
792                         filename = g_strdup("");
793                     }
794                 }
796                 break;
797             }
798             case SELECTION_SELECTION:
799                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
800                     const GSList * reprlst;
801                     bool filename_search = TRUE;
802                     bool xdpi_search = TRUE;
803                     bool ydpi_search = TRUE;
805                     reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
806                     for(; reprlst != NULL &&
807                             filename_search &&
808                             xdpi_search &&
809                             ydpi_search;
810                             reprlst = reprlst->next) {
811                         const gchar * dpi_string;
812                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
814                         if (filename_search) {
815                             filename = repr->attribute("inkscape:export-filename");
816                             if (filename != NULL)
817                                 filename_search = FALSE;
818                         }
820                         if (xdpi_search) {
821                             dpi_string = NULL;
822                             dpi_string = repr->attribute("inkscape:export-xdpi");
823                             if (dpi_string != NULL) {
824                                 xdpi = atof(dpi_string);
825                                 xdpi_search = FALSE;
826                             }
827                         }
829                         if (ydpi_search) {
830                             dpi_string = NULL;
831                             dpi_string = repr->attribute("inkscape:export-ydpi");
832                             if (dpi_string != NULL) {
833                                 ydpi = atof(dpi_string);
834                                 ydpi_search = FALSE;
835                             }
836                         }
837                     }
839                     /* If we still don't have a filename -- let's build
840                        one that's nice */
841                     if (filename == NULL) {
842                         const gchar * id = NULL;
843                         reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
844                         for(; reprlst != NULL; reprlst = reprlst->next) {
845                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
846                             if (repr->attribute("id")) {
847                                 id = repr->attribute("id");
848                                 break;
849                             }
850                         }
851                         if (id == NULL) /* This should never happen */
852                             id = "bitmap";
854                         gchar * directory = NULL;
855                         const gchar * file_entry_text;
857                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
858                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
859                             // std::cout << "Directory from dialog" << std::endl;
860                             directory = g_dirname(file_entry_text);
861                         }
863                         if (directory == NULL) {
864                             /* Grab document directory */
865                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
866                                 // std::cout << "Directory from document" << std::endl;
867                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
868                             }
869                         }
871                         if (directory == NULL) {
872                             // std::cout << "Home Directory" << std::endl;
873                             directory = homedir_path(NULL);
874                         }
876                         gchar * id_ext = g_strconcat(id, ".png", NULL);
877                         filename = g_build_filename(directory, id_ext, NULL);
878                         g_free(directory);
879                         g_free(id_ext);
880                     }
881                 }
882                 break;
883             case SELECTION_CUSTOM:
884             default:
885                 break;
886         }
888         if (filename != NULL) {
889             g_free(original_name);
890             original_name = g_strdup(filename);
891             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
892         }
894         if (xdpi != 0.0) {
895             sp_export_value_set(base, "xdpi", xdpi);
896         }
898         /* These can't be seperate, and setting x sets y, so for
899            now setting this is disabled.  Hopefully it won't be in
900            the future */
901         if (FALSE && ydpi != 0.0) {
902             sp_export_value_set(base, "ydpi", ydpi);
903         }
904     }
906     return;
907 } // end of sp_export_area_toggled()
909 /// Called when dialog is deleted
910 static gint
911 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
913     g_object_set_data (base, "cancel", (gpointer) 1);
914     return TRUE;
915 } // end of sp_export_progress_delete()
917 /// Called when progress is cancelled
918 static void
919 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
921     g_object_set_data (base, "cancel", (gpointer) 1);
922 } // end of sp_export_progress_cancel()
924 /// Called for every progress iteration
925 static unsigned int
926 sp_export_progress_callback (float value, void *data)
928     GtkWidget *prg;
929     int evtcount;
931     if (g_object_get_data ((GObject *) data, "cancel"))
932         return FALSE;
934     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
935     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
937     evtcount = 0;
938     while ((evtcount < 16) && gdk_events_pending ()) {
939             gtk_main_iteration_do (FALSE);
940             evtcount += 1;
941     }
943     gtk_main_iteration_do (FALSE);
945     return TRUE;
947 } // end of sp_export_progress_callback()
949 /// Called when export button is clicked
950 static void
951 sp_export_export_clicked (GtkButton *button, GtkObject *base)
953     if (!SP_ACTIVE_DESKTOP) return;
955     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
956     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
958     float const x0 = sp_export_value_get_px(base, "x0");
959     float const y0 = sp_export_value_get_px(base, "y0");
960     float const x1 = sp_export_value_get_px(base, "x1");
961     float const y1 = sp_export_value_get_px(base, "y1");
962     float const xdpi = sp_export_value_get(base, "xdpi");
963     float const ydpi = sp_export_value_get(base, "ydpi");
964     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
965     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
967     if (filename == NULL || *filename == '\0') {
968         sp_ui_error_dialog(_("You have to enter a filename"));
969         return;
970     }
972     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
973         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
974         return;
975     }
977     gchar *dirname = g_dirname(filename);
978     if ( dirname == NULL
979          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
980     {
981         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
982         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
983                                        safeDir);
984         sp_ui_error_dialog(error);
985         g_free(safeDir);
986         g_free(error);
987         g_free(dirname);
988         return;
989     }
990     g_free(dirname);
992     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
993     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
994     char *fn;
995     gchar *text;
997     dlg = gtk_dialog_new ();
998     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
999     prg = gtk_progress_bar_new ();
1000     sp_transientize (dlg);
1001     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1002     g_object_set_data ((GObject *) base, "progress", prg);
1003     fn = g_path_get_basename (filename);
1004     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1005                              fn, width, height);
1006     g_free (fn);
1007     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1008     g_free (text);
1009     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1010                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1011     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1012                         prg, FALSE, FALSE, 4 );
1013     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1014                                   GTK_STOCK_CANCEL, 
1015                                   GTK_RESPONSE_CANCEL );
1016                                   
1017     g_signal_connect ( (GObject *) dlg, "delete_event", 
1018                        (GCallback) sp_export_progress_delete, base);
1019     g_signal_connect ( (GObject *) btn, "clicked", 
1020                        (GCallback) sp_export_progress_cancel, base);
1021     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1022     gtk_widget_show_all (dlg);
1023     
1024     /* Do export */
1025     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename, 
1026                              x0, y0, x1, y1, width, height, 
1027                              nv->pagecolor, 
1028                              sp_export_progress_callback, base)) {
1029         gchar * error;
1030         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1031         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1032         sp_ui_error_dialog(error);
1033         g_free(safeFile);
1034         g_free(error);
1035     }
1037     /* Reset the filename so that it can be changed again by changing
1038        selections and all that */
1039     g_free(original_name);
1040     original_name = g_strdup(filename);
1041     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1043     gtk_widget_destroy (dlg);
1044     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1046     /* Setup the values in the document */
1047     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1048         case SELECTION_PAGE:
1049         case SELECTION_DRAWING: {
1050             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1051             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1052             bool modified = FALSE;
1053             const gchar * temp_string;
1055             bool saved = sp_document_get_undo_sensitive(doc);
1056             sp_document_set_undo_sensitive(doc, FALSE);
1058             temp_string = repr->attribute("inkscape:export-filename");
1059             if (temp_string == NULL || strcmp(temp_string, filename)) {
1060                 repr->setAttribute("inkscape:export-filename", filename);
1061                 modified = TRUE;
1062             }
1063             temp_string = repr->attribute("inkscape:export-xdpi");
1064             if (temp_string == NULL || xdpi != atof(temp_string)) {
1065                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1066                 modified = TRUE;
1067             }
1068             temp_string = repr->attribute("inkscape:export-ydpi");
1069             if (temp_string == NULL || xdpi != atof(temp_string)) {
1070                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1071                 modified = TRUE;
1072             }
1074             if (modified)
1075                 repr->setAttribute("sodipodi:modified", "TRUE");
1076             sp_document_set_undo_sensitive(doc, saved);
1077             break;
1078         }
1079         case SELECTION_SELECTION: {
1080             const GSList * reprlst;
1081             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1082             bool modified = FALSE;
1084             bool saved = sp_document_get_undo_sensitive(doc);
1085             sp_document_set_undo_sensitive(doc, FALSE);
1086             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1088             for(; reprlst != NULL; reprlst = reprlst->next) {
1089                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1090                 const gchar * temp_string;
1092                 if (repr->attribute("id") == NULL ||
1093                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1094                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1095                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1096                     temp_string = repr->attribute("inkscape:export-filename");
1097                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1098                         repr->setAttribute("inkscape:export-filename", filename);
1099                         modified = TRUE;
1100                     }
1101                 }
1102                 temp_string = repr->attribute("inkscape:export-xdpi");
1103                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1104                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1105                     modified = TRUE;
1106                 }
1107                 temp_string = repr->attribute("inkscape:export-ydpi");
1108                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1109                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1110                     modified = TRUE;
1111                 }
1112             }
1114             if (modified) {
1115                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1116                 repr->setAttribute("sodipodi:modified", "TRUE");
1117             }
1119             sp_document_set_undo_sensitive(doc, saved);
1120             break;
1121         }
1122         default:
1123             break;
1124     }
1127     return;
1128 } // end of sp_export_export_clicked()
1130 /// Called when Browse button is clicked
1131 static void
1132 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1134     GtkWidget *fs, *fe;
1135     const gchar *filename;
1137     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1138                                       NULL,
1139                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1140                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1141                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1142                                       NULL );
1144     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1146     sp_transientize (fs);
1148     gtk_window_set_modal(GTK_WINDOW (fs), true);
1150     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1152     if (*filename == '\0') {
1153         filename = homedir_path(NULL);
1154     }
1156     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1158     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1159     {
1160         gchar *file;
1162         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1163         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1164         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1165         g_free(utf8file);
1167         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1168         g_free(file);
1169     }
1171     gtk_widget_destroy (fs);
1173     return;
1174 } // end of sp_export_browse_clicked()
1176 // TODO: Move this to nr-rect-fns.h.
1177 static bool
1178 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1179
1180     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1181     return (
1182         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1183         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1184         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1185         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1186         );
1189 /**
1190     \brief  This function is used to detect the current selection setting
1191             based on the values in the x0, y0, x1 and y0 fields.
1192     \param  base  The export dialog itself
1194     One of the most confusing parts of this function is why the array
1195     is built at the beginning.  What needs to happen here is that we
1196     should always check the current selection to see if it is the valid
1197     one.  While this is a performance improvement it is also a usability
1198     one during the cases where things like selections and drawings match
1199     size.  This way buttons change less 'randomly' (atleast in the eyes
1200     of the user).  To do this an array is built where the current selection
1201     type is placed first, and then the others in an order from smallest
1202     to largest (this can be configured by reshuffling \c test_order).
1204     All of the values in this function are rounded to two decimal places
1205     because that is what is shown to the user.  While everything is kept
1206     more accurate than that, the user can't control more acurrate than
1207     that, so for this to work for them - it needs to check on that level
1208     of accuracy.
1210     \todo finish writing this up
1211 */
1212 static void
1213 sp_export_detect_size(GtkObject * base) {
1214     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1215     selection_type this_test[SELECTION_NUMBER_OF + 1];
1216     selection_type key = SELECTION_NUMBER_OF;
1218     NR::Point x(sp_export_value_get_px (base, "x0"),
1219                 sp_export_value_get_px (base, "y0"));
1220     NR::Point y(sp_export_value_get_px (base, "x1"),
1221                 sp_export_value_get_px (base, "y1"));
1222     NR::Rect current_bbox(x, y);
1223     //std::cout << "Current " << current_bbox;
1225     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1226     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1227         this_test[i + 1] = test_order[i];
1228     }
1230     for (int i = 0;
1231             i < SELECTION_NUMBER_OF + 1 &&
1232                 key == SELECTION_NUMBER_OF &&
1233                 SP_ACTIVE_DESKTOP != NULL;
1234             i++) {
1235         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1236         switch (this_test[i]) {
1237             case SELECTION_SELECTION:
1238                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1239                     NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1241                     //std::cout << "Selection " << bbox;
1242                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1243                         key = SELECTION_SELECTION;
1244                     }
1245                 }
1246                 break;
1247             case SELECTION_DRAWING: {
1248                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1250                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1252                 // std::cout << "Drawing " << bbox2;
1253                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1254                     key = SELECTION_DRAWING;
1255                 }
1256                 break;
1257             }
1259             case SELECTION_PAGE: {
1260                 SPDocument *doc;
1262                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1264                 NR::Point x(0.0, 0.0);
1265                 NR::Point y(sp_document_width(doc),
1266                             sp_document_height(doc));
1267                 NR::Rect bbox(x, y);
1269                 // std::cout << "Page " << bbox;
1270                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1271                     key = SELECTION_PAGE;
1272                 }
1274                 break;
1275            }
1276         default:
1277            break;
1278         }
1279     }
1280     // std::cout << std::endl;
1282     if (key == SELECTION_NUMBER_OF) {
1283         key = SELECTION_CUSTOM;
1284     }
1286     /* We're now using a custom size, not a fixed one */
1287     /* printf("Detecting state: %s\n", selection_names[key]); */
1288     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1289     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1290     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1291     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1293     return;
1294 } /* sp_export_detect_size */
1296 /// Called when area x0 value is changed
1297 static void
1298 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1300     float x0, x1, xdpi, width;
1302     if (gtk_object_get_data (base, "update"))
1303         return;
1305     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1306             (base, "units")))
1307     {
1308         return;
1309     }
1311     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1313     x0 = sp_export_value_get_px (base, "x0");
1314     x1 = sp_export_value_get_px (base, "x1");
1315     xdpi = sp_export_value_get (base, "xdpi");
1317     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1319     if (width < SP_EXPORT_MIN_SIZE) {
1320         const gchar *key;
1321         width = SP_EXPORT_MIN_SIZE;
1322         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1324         if (!strcmp (key, "x0")) {
1325             x1 = x0 + width * DPI_BASE / xdpi;
1326             sp_export_value_set_px (base, "x1", x1);
1327         } else {
1328             x0 = x1 - width * DPI_BASE / xdpi;
1329             sp_export_value_set_px (base, "x0", x0);
1330         }
1331     }
1333     sp_export_value_set_px (base, "width", x1 - x0);
1334     sp_export_value_set (base, "bmwidth", width);
1336     sp_export_detect_size(base);
1338     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1340     return;
1341 } // end of sp_export_area_x_value_changed()
1343 /// Called when area y0 value is changed.
1344 static void
1345 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1347     float y0, y1, ydpi, height;
1349     if (gtk_object_get_data (base, "update"))
1350         return;
1352     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1353            (base, "units")))
1354     {
1355         return;
1356     }
1358     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1360     y0 = sp_export_value_get_px (base, "y0");
1361     y1 = sp_export_value_get_px (base, "y1");
1362     ydpi = sp_export_value_get (base, "ydpi");
1364     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1366     if (height < SP_EXPORT_MIN_SIZE) {
1367         const gchar *key;
1368         height = SP_EXPORT_MIN_SIZE;
1369         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1370         if (!strcmp (key, "y0")) {
1371             y1 = y0 + height * DPI_BASE / ydpi;
1372             sp_export_value_set_px (base, "y1", y1);
1373         } else {
1374             y0 = y1 - height * DPI_BASE / ydpi;
1375             sp_export_value_set_px (base, "y0", y0);
1376         }
1377     }
1379     sp_export_value_set_px (base, "height", y1 - y0);
1380     sp_export_value_set (base, "bmheight", height);
1382     sp_export_detect_size(base);
1384     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1386     return;
1387 } // end of sp_export_area_y_value_changed()
1389 /// Called when x1-x0 or area width is changed
1390 static void
1391 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1393     float x0, x1, xdpi, width, bmwidth;
1395     if (gtk_object_get_data (base, "update"))
1396         return;
1398     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1399            (base, "units"))) {
1400         return;
1401     }
1403     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1405     x0 = sp_export_value_get_px (base, "x0");
1406     x1 = sp_export_value_get_px (base, "x1");
1407     xdpi = sp_export_value_get (base, "xdpi");
1408     width = sp_export_value_get_px (base, "width");
1409     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1411     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1413         bmwidth = SP_EXPORT_MIN_SIZE;
1414         width = bmwidth * DPI_BASE / xdpi;
1415         sp_export_value_set_px (base, "width", width);
1416     }
1418     sp_export_value_set_px (base, "x1", x0 + width);
1419     sp_export_value_set (base, "bmwidth", bmwidth);
1421     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1423     return;
1424 } // end of sp_export_area_width_value_changed()
1426 /// Called when y1-y0 or area height is changed.
1427 static void
1428 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1431     float y0, y1, ydpi, height, bmheight;
1433     if (gtk_object_get_data (base, "update"))
1434         return;
1436     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1437            (base, "units"))) {
1438         return;
1439     }
1441     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1443     y0 = sp_export_value_get_px (base, "y0");
1444     y1 = sp_export_value_get_px (base, "y1");
1445     ydpi = sp_export_value_get (base, "ydpi");
1446     height = sp_export_value_get_px (base, "height");
1447     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1449     if (bmheight < SP_EXPORT_MIN_SIZE) {
1450         bmheight = SP_EXPORT_MIN_SIZE;
1451         height = bmheight * DPI_BASE / ydpi;
1452         sp_export_value_set_px (base, "height", height);
1453     }
1455     sp_export_value_set_px (base, "y1", y0 + height);
1456     sp_export_value_set (base, "bmheight", bmheight);
1458     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1460     return;
1461 } // end of sp_export_area_height_value_changed()
1463 /**
1464     \brief  A function to set the ydpi
1465     \param  base  The export dialog
1467     This function grabs all of the y values and then figures out the
1468     new bitmap size based on the changing dpi value.  The dpi value is
1469     gotten from the xdpi setting as these can not currently be independent.
1470 */
1471 static void
1472 sp_export_set_image_y (GtkObject *base)
1474     float y0, y1, xdpi;
1476     y0 = sp_export_value_get_px (base, "y0");
1477     y1 = sp_export_value_get_px (base, "y1");
1478     xdpi = sp_export_value_get (base, "xdpi");
1480     sp_export_value_set (base, "ydpi", xdpi);
1481     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1483     return;
1484 } // end of sp_export_set_image_y()
1486 /**
1487     \brief  A function to set the xdpi
1488     \param  base  The export dialog
1490     This function grabs all of the x values and then figures out the
1491     new bitmap size based on the changing dpi value.  The dpi value is
1492     gotten from the xdpi setting as these can not currently be independent.
1493 */
1494 static void
1495 sp_export_set_image_x (GtkObject *base)
1497     float x0, x1, xdpi;
1499     x0 = sp_export_value_get_px (base, "x0");
1500     x1 = sp_export_value_get_px (base, "x1");
1501     xdpi = sp_export_value_get (base, "xdpi");
1503     sp_export_value_set (base, "ydpi", xdpi);
1504     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1506     return;
1507 } // end of sp_export_set_image_x()
1509 /// Called when pixel width is changed
1510 static void
1511 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1513     float x0, x1, bmwidth, xdpi;
1515     if (gtk_object_get_data (base, "update"))
1516         return;
1518     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1519            (base, "units"))) {
1520        return;
1521     }
1523     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1525     x0 = sp_export_value_get_px (base, "x0");
1526     x1 = sp_export_value_get_px (base, "x1");
1527     bmwidth = sp_export_value_get (base, "bmwidth");
1529     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1530         bmwidth = SP_EXPORT_MIN_SIZE;
1531         sp_export_value_set (base, "bmwidth", bmwidth);
1532     }
1534     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1535     sp_export_value_set (base, "xdpi", xdpi);
1537     sp_export_set_image_y (base);
1539     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1541     return;
1542 } // end of sp_export_bitmap_width_value_changed()
1544 /// Called when pixel height is changed
1545 static void
1546 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1548     float y0, y1, bmheight, xdpi;
1550     if (gtk_object_get_data (base, "update"))
1551         return;
1553     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1554            (base, "units"))) {
1555        return;
1556     }
1558     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1560     y0 = sp_export_value_get_px (base, "y0");
1561     y1 = sp_export_value_get_px (base, "y1");
1562     bmheight = sp_export_value_get (base, "bmheight");
1564     if (bmheight < SP_EXPORT_MIN_SIZE) {
1565         bmheight = SP_EXPORT_MIN_SIZE;
1566         sp_export_value_set (base, "bmheight", bmheight);
1567     }
1569     xdpi = bmheight * DPI_BASE / (y1 - y0);
1570     sp_export_value_set (base, "xdpi", xdpi);
1572     sp_export_set_image_x (base);
1574     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1576     return;
1577 } // end of sp_export_bitmap_width_value_changed()
1579 /**
1580     \brief  A function to adjust the bitmap width when the xdpi value changes
1581     \param  adj  The adjustment that was changed
1582     \param  base The export dialog itself
1584     The first thing this function checks is to see if we are doing an
1585     update.  If we are, this function just returns because there is another
1586     instance of it that will handle everything for us.  If there is a
1587     units change, we also assume that everyone is being updated appropriately
1588     and there is nothing for us to do.
1590     If we're the highest level function, we set the update flag, and
1591     continue on our way.
1593     All of the values are grabbed using the \c sp_export_value_get functions
1594     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1595     xdpi value is saved in the preferences for the next time the dialog
1596     is opened.  (does the selection dpi need to be set here?)
1598     A check is done to to ensure that we aren't outputing an invalid width,
1599     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1600     changed to make it valid.
1602     After all of this the bitmap width is changed.
1604     We also change the ydpi.  This is a temporary hack as these can not
1605     currently be independent.  This is likely to change in the future.
1606 */
1607 void
1608 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1610     float x0, x1, xdpi, bmwidth;
1612     if (gtk_object_get_data (base, "update"))
1613         return;
1615     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1616            (base, "units"))) {
1617        return;
1618     }
1620     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1622     x0 = sp_export_value_get_px (base, "x0");
1623     x1 = sp_export_value_get_px (base, "x1");
1624     xdpi = sp_export_value_get (base, "xdpi");
1626     // remember xdpi setting
1627     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1629     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1631     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1632         bmwidth = SP_EXPORT_MIN_SIZE;
1633         if (x1 != x0)
1634             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1635         else
1636             xdpi = DPI_BASE;
1637         sp_export_value_set (base, "xdpi", xdpi);
1638     }
1640     sp_export_value_set (base, "bmwidth", bmwidth);
1642     sp_export_set_image_y (base);
1644     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1646     return;
1647 } // end of sp_export_xdpi_value_changed()
1650 /**
1651     \brief  A function to change the area that is used for the exported
1652             bitmap.
1653     \param  base  This is the export dialog
1654     \param  x0    Horizontal upper left hand corner of the picture in points
1655     \param  y0    Vertical upper left hand corner of the picture in points
1656     \param  x1    Horizontal lower right hand corner of the picture in points
1657     \param  y1    Vertical lower right hand corner of the picture in points
1659     This function just calls \c sp_export_value_set_px for each of the
1660     parameters that is passed in.  This allows for setting them all in
1661     one convient area.
1663     Update is set to suspend all of the other test running while all the
1664     values are being set up.  This allows for a performance increase, but
1665     it also means that the wrong type won't be detected with only some of
1666     the values set.  After all the values are set everyone is told that
1667     there has been an update.
1668 */
1669 static void
1670 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1672     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1673     sp_export_value_set_px (base, "x1", x1);
1674     sp_export_value_set_px (base, "y1", y1);
1675     sp_export_value_set_px (base, "x0", x0);
1676     sp_export_value_set_px (base, "y0", y0);
1677     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1679     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1680     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1682     return;
1685 /**
1686     \brief  Sets the value of an adjustment
1687     \param  base  The export dialog
1688     \param  key   Which adjustment to set
1689     \param  val   What value to set it to
1691     This function finds the adjustment using the data stored in the
1692     export dialog.  After finding the adjustment it then sets
1693     the value of it.
1694 */
1695 static void
1696 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1698     GtkAdjustment *adj;
1700     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1702     gtk_adjustment_set_value (adj, val);
1705 /**
1706     \brief  A function to set a value using the units points
1707     \param  base  The export dialog
1708     \param  key   Which value should be set
1709     \param  val   What the value should be in points
1711     This function first gets the adjustment for the key that is passed
1712     in.  It then figures out what units are currently being used in the
1713     dialog.  After doing all of that, it then converts the incoming
1714     value and sets the adjustment.
1715 */
1716 static void
1717 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1719     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1721     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1723     return;
1726 /**
1727     \brief  Get the value of an adjustment in the export dialog
1728     \param  base  The export dialog
1729     \param  key   Which adjustment is being looked for
1730     \return The value in the specified adjustment
1732     This function gets the adjustment from the data field in the export
1733     dialog.  It then grabs the value from the adjustment.
1734 */
1735 static float
1736 sp_export_value_get ( GtkObject *base, const gchar *key )
1738     GtkAdjustment *adj;
1740     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1742     return adj->value;
1745 /**
1746     \brief  Grabs a value in the export dialog and converts the unit
1747             to points
1748     \param  base  The export dialog
1749     \param  key   Which value should be returned
1750     \return The value in the adjustment in points
1752     This function, at its most basic, is a call to \c sp_export_value_get
1753     to get the value of the adjustment.  It then finds the units that
1754     are being used by looking at the "units" attribute of the export
1755     dialog.  Using that it converts the returned value into points.
1756 */
1757 static float
1758 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1760     float value = sp_export_value_get(base, key);
1761     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1763     return sp_units_get_pixels (value, *unit);
1764 } // end of sp_export_value_get_px()
1766 /**
1767     \brief  This function is called when the filename is changed by
1768             anyone.  It resets the virgin bit.
1769     \param  object  Text entry box
1770     \param  data    The export dialog
1771     \return None
1773     This function gets called when the text area is modified.  It is
1774     looking for the case where the text area is modified from its
1775     original value.  In that case it sets the "filename-modified" bit
1776     to TRUE.  If the text dialog returns back to the original text, the
1777     bit gets reset.  This should stop simple mistakes.
1778 */
1779 static void
1780 sp_export_filename_modified (GtkObject * object, gpointer data)
1782     GtkWidget * text_entry = (GtkWidget *)object;
1783     GtkWidget * export_dialog = (GtkWidget *)data;
1785     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1786         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1787 //        printf("Modified: FALSE\n");
1788     } else {
1789         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1790 //        printf("Modified: TRUE\n");
1791     }
1793     return;
1794 } // end sp_export_filename_modified
1796 /*
1797   Local Variables:
1798   mode:c++
1799   c-file-style:"stroustrup"
1800   c-file-offsets:((innamespace . 0)(inline-open . 0))
1801   indent-tabs-mode:nil
1802   fill-column:99
1803   End:
1804 */
1805 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :