Code

Allow setting bitmap size by adjusting height as well as width
[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_bitmap_height_value_changed ( GtkAdjustment *adj, 
77                                                    GtkObject *base);
78                                                    
79 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
80                                                    GtkObject *base);
81                                            
82 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
83                                           Inkscape::Selection *selection, 
84                                           GtkObject *base);
85 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
86                                            Inkscape::Selection *selection, 
87                                            guint flags,
88                                            GtkObject *base );
90 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
91 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
92 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
93 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
94 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
96 static void sp_export_filename_modified (GtkObject * object, gpointer data);
97 static inline void sp_export_find_default_selection(GtkWidget * dlg);
98 static void sp_export_detect_size(GtkObject * base);
100 static const gchar *prefs_path = "dialogs.export";
102 // these all need to be reinitialized to their defaults during dialog_destroy
103 static GtkWidget *dlg = NULL;
104 static win_data wd;
105 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
106 static gchar * original_name = NULL;
107 static gchar * doc_export_name = NULL;
108 static bool was_empty = TRUE;
110 /** What type of button is being pressed */
111 enum selection_type {
112     SELECTION_PAGE = 0,  /**< Export the whole page */
113     SELECTION_DRAWING,   /**< Export everything drawn on the page */
114     SELECTION_SELECTION, /**< Export everything that is selected */
115     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
116     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
117 };
119 /** A list of strings that is used both in the preferences, and in the
120     data fields to describe the various values of \c selection_type. */
121 static const char * selection_names[SELECTION_NUMBER_OF] = {
122     "page", "drawing", "selection", "custom"};
124 /** The names on the buttons for the various selection types. */
125 static const char * selection_labels[SELECTION_NUMBER_OF] = {
126     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
128 static void
129 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
131     sp_signal_disconnect_by_data (INKSCAPE, dlg);
133     wd.win = dlg = NULL;
134     wd.stop = 0;
135     x = -1000; y = -1000; w = 0; h = 0;
136     g_free(original_name);
137     original_name = NULL;
138     g_free(doc_export_name);
139     doc_export_name = NULL;
140     was_empty = TRUE;
142     return;
143 } // end of sp_export_dialog_destroy()
145 /// Called when dialog is closed or inkscape is shut down.
146 static bool
147 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
150     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
151     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
153     if (x<0) x=0;
154     if (y<0) y=0;
156     prefs_set_int_attribute (prefs_path, "x", x);
157     prefs_set_int_attribute (prefs_path, "y", y);
158     prefs_set_int_attribute (prefs_path, "w", w);
159     prefs_set_int_attribute (prefs_path, "h", h);
161     return FALSE; // which means, go ahead and destroy it
163 } // end of sp_export_dialog_delete()
165 /**
166     \brief  Creates a new spin button for the export dialog
167     \param  key  The name of the spin button
168     \param  val  A default value for the spin button
169     \param  min  Minimum value for the spin button
170     \param  max  Maximum value for the spin button
171     \param  step The step size for the spin button
172     \param  page Size of the page increment
173     \param  us   Unit selector that effects this spin button
174     \param  t    Table to put the spin button in
175     \param  x    X location in the table \c t to start with
176     \param  y    Y location in the table \c t to start with
177     \param  ll   Text to put on the left side of the spin button (optional)
178     \param  lr   Text to put on the right side of the spin button (optional)
179     \param  digits  Number of digits to display after the decimal
180     \param  sensitive  Whether the spin button is sensitive or not
181     \param  cb   Callback for when this spin button is changed (optional)
182     \param  dlg  Export dialog the spin button is being placed in
184 */
185 static void
186 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
187                            float step, float page, GtkWidget *us,
188                            GtkWidget *t, int x, int y,
189                            const gchar *ll, const gchar *lr,
190                            int digits, unsigned int sensitive,
191                            GCallback cb, GtkWidget *dlg )
193     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
194     gtk_object_set_data (a, "key", key);
195     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
197     if (us) {
198         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
199                                           GTK_ADJUSTMENT (a) );
200     }
202     int pos = 0;
204     GtkWidget *l = NULL;
206     if (ll) {
208         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
209         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
210         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
211                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
212         gtk_widget_set_sensitive (l, sensitive);
213         pos += 1;
215     }
217     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
218     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
219                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
220     gtk_widget_set_size_request (sb, 80, -1);
221     gtk_widget_set_sensitive (sb, sensitive);
222     pos += 1;
224     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
226     if (lr) {
228         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
229         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
230         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
231                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
232         gtk_widget_set_sensitive (l, sensitive);
233         pos += 1;
235         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
236     }
238     if (cb)
239         gtk_signal_connect (a, "value_changed", cb, dlg);
241     return;
242 } // end of sp_export_spinbutton_new()
245 static GtkWidget *
246 sp_export_dialog_area_frame (GtkWidget * dlg)
248     GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
250     f = gtk_frame_new (_("Export area"));
251     vb = gtk_vbox_new (FALSE, 2);
252     gtk_container_add (GTK_CONTAINER (f), vb);
254     /* Units box */
255     unitbox = gtk_hbox_new (FALSE, 0);
256     gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
257     /* gets added to the vbox later, but the unit selector is needed
258        earlier than that */
260     us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
261     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
262     if (desktop)
263         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
264     gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
265     l = gtk_label_new (_("Units:"));
266     gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
267     gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
269     hb = gtk_hbox_new (TRUE, 0);
270     gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
271     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
273     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
274         b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
275         gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
276         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
277         gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
278         gtk_signal_connect ( GTK_OBJECT (b), "clicked", 
279                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
280     }
282     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
283                        G_CALLBACK (sp_export_selection_changed), dlg );
284     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
285                        G_CALLBACK (sp_export_selection_modified), dlg );
286     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
287                        G_CALLBACK (sp_export_selection_changed), dlg );
288     
289     t = gtk_table_new (2, 6, FALSE);
290     gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
291     gtk_table_set_row_spacings (GTK_TABLE (t), 4);
292     gtk_table_set_col_spacings (GTK_TABLE (t), 4);
293     gtk_container_set_border_width (GTK_CONTAINER (t), 4);
295     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
296                                t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
297                                G_CALLBACK ( sp_export_area_x_value_changed), 
298                                dlg );
300     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
301                                t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
302                                G_CALLBACK (sp_export_area_x_value_changed), 
303                                dlg );
305     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
306                                us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
307                                G_CALLBACK 
308                                    (sp_export_area_width_value_changed), 
309                                dlg );
311     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
312                                t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
313                                G_CALLBACK (sp_export_area_y_value_changed), 
314                                dlg );
316     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
317                                t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
318                                G_CALLBACK (sp_export_area_y_value_changed), 
319                                dlg );
321     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
322                                us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
323                                G_CALLBACK (sp_export_area_height_value_changed), 
324                                dlg );
326     /* Adding in the unit box */
327     gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
329     return f;
330 } // end of sp_export_dialog_area_frame
333 void
334 sp_export_dialog (void)
336     if (!dlg) {
337         GtkWidget *vb, *hb;
339         gchar title[500];
340         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
342         dlg = sp_window_new (title, TRUE);
344         if (x == -1000 || y == -1000) {
345             x = prefs_get_int_attribute (prefs_path, "x", 0);
346             y = prefs_get_int_attribute (prefs_path, "y", 0);
347         }
349         if (w ==0 || h == 0) {
350             w = prefs_get_int_attribute (prefs_path, "w", 0);
351             h = prefs_get_int_attribute (prefs_path, "h", 0);
352         }
354         if (x<0) x=0;
355         if (y<0) y=0;
357         if (x != 0 || y != 0) {
358             gtk_window_move ((GtkWindow *) dlg, x, y);
359         } else {
360             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
361         }
363         if (w && h)
364             gtk_window_resize ((GtkWindow *) dlg, w, h);
366         sp_transientize (dlg);
367         wd.win = dlg;
368         wd.stop = 0;
370         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
371                              G_CALLBACK (sp_transientize_callback), &wd);
373         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
374                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
376         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
377                              G_CALLBACK (sp_export_dialog_destroy), dlg);
379         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
380                              G_CALLBACK (sp_export_dialog_delete), dlg);
382         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
383                              G_CALLBACK (sp_export_dialog_delete), dlg);
385         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
386                              G_CALLBACK (sp_dialog_hide), dlg);
388         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
389                              G_CALLBACK (sp_dialog_unhide), dlg);
391         GtkTooltips *tt = gtk_tooltips_new();
393         vb = gtk_vbox_new (FALSE, 4);
394         gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
395         gtk_container_add (GTK_CONTAINER (dlg), vb);
397         /* Export area frame */
398         {
399             GtkWidget *f = sp_export_dialog_area_frame(dlg);
400             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
401         }
403         /* Bitmap size frame */
404         {
405             GtkWidget *f = gtk_frame_new (_("Bitmap size"));
406             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
407             GtkWidget *t = gtk_table_new (2, 5, FALSE);
408             gtk_table_set_row_spacings (GTK_TABLE (t), 4);
409             gtk_table_set_col_spacings (GTK_TABLE (t), 4);
410             gtk_container_set_border_width (GTK_CONTAINER (t), 4);
411             gtk_container_add (GTK_CONTAINER (f), t);
413             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
414                                        NULL, t, 0, 0,
415                                        _("_Width:"), _("pixels at"), 0, 1,
416                                        G_CALLBACK 
417                                        (sp_export_bitmap_width_value_changed), 
418                                        dlg );
420             sp_export_spinbutton_new ( "xdpi", 
421                                        prefs_get_double_attribute 
422                                        ( "dialogs.export.defaultxdpi", 
423                                          "value", DPI_BASE), 
424                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
425                                        NULL, _("dp_i"), 2, 1,
426                                        G_CALLBACK (sp_export_xdpi_value_changed), 
427                                        dlg );
429             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
430                                        NULL, t, 0, 1, 
431                                        _("Height:"), _("pixels at"), 0, 1, 
432                                        G_CALLBACK
433                                        (sp_export_bitmap_height_value_changed), 
434                                        dlg );
436             /** \todo
437              * Needs fixing: there's no way to set ydpi currently, so we use  
438              *       the defaultxdpi value here, too...
439              */
440             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
441                                        ( "dialogs.export.defaultxdpi", 
442                                          "value", DPI_BASE), 
443                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
444                                        NULL, _("dpi"), 2, 0, NULL, dlg );
445         }
447         /* File entry */
448         {
449             GtkWidget *frame = gtk_frame_new ("");
450             GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
451             gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
452             gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
454             GtkWidget *fe = gtk_entry_new ();
456             /*
457              * set the default filename to be that of the current path + document
458              * with .png extension
459              *
460              * One thing to notice here is that this filename may get
461              * overwritten, but it won't happen here.  The filename gets
462              * written into the text field, but then the button to select
463              * the area gets set.  In that code the filename can be changed
464              * if there are some with presidence in the document.  So, while
465              * this code sets the name first, it may not be the one users
466              * really see.
467              */
468             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
469             {
470                 gchar *name;
471                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
472                 const gchar *uri = SP_DOCUMENT_URI (doc);
473                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
474                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
475                 Inkscape::Extension::Output * oextension = NULL;
477                 if (text_extension != NULL) {
478                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
479                 }
481                 if (oextension != NULL) {
482                     gchar * old_extension = oextension->get_extension();
483                     if (g_str_has_suffix(uri, old_extension)) {
484                         gchar * uri_copy;
485                         gchar * extension_point;
486                         gchar * final_name;
488                         uri_copy = g_strdup(uri);
489                         extension_point = g_strrstr(uri_copy, old_extension);
490                         extension_point[0] = '\0';
492                         final_name = g_strconcat(uri_copy, ".png", NULL);
493                         gtk_entry_set_text (GTK_ENTRY (fe), final_name);
495                         g_free(final_name);
496                         g_free(uri_copy);
497                     }
498                 } else {
499                     name = g_strconcat(uri, ".png", NULL);
500                     gtk_entry_set_text (GTK_ENTRY (fe), name);
501                     g_free(name);
502                 }
504                 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
505             }
506             g_signal_connect ( G_OBJECT (fe), "changed",
507                                G_CALLBACK (sp_export_filename_modified), dlg);
509             hb = gtk_hbox_new (FALSE, 5);
510             gtk_container_add (GTK_CONTAINER (frame), hb);
511             gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
513             {
514                 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
515                 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
516                 g_signal_connect ( G_OBJECT (b), "clicked",
517                                    G_CALLBACK (sp_export_browse_clicked), NULL );
518             }
520             gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
521             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
522             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
523             original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
524             // pressing enter in the filename field is the same as clicking export:
525             g_signal_connect ( G_OBJECT (fe), "activate",
526                                G_CALLBACK (sp_export_export_clicked), dlg );
527             // focus is in the filename initially:
528             gtk_widget_grab_focus (GTK_WIDGET (fe));
530             // mnemonic in frame label moves focus to filename:
531             gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
532         }
534         /* Buttons */
535         hb = gtk_hbox_new (FALSE, 0);
536         gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
538         {
539             GtkWidget *b = gtk_button_new ();
540             GtkWidget *l = gtk_label_new ("");
541             gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
542             gtk_container_add (GTK_CONTAINER(b), l);
543             gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
544             gtk_signal_connect ( GTK_OBJECT (b), "clicked",
545                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
546             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
547         }
549         gtk_widget_show_all (vb);
551     } // end of if (!dlg)
553     sp_export_find_default_selection(dlg);
555     gtk_window_present ((GtkWindow *) dlg);
557     return;
558 } // end of sp_export_dialog()
560 static inline void
561 sp_export_find_default_selection(GtkWidget * dlg)
563     selection_type key = SELECTION_NUMBER_OF;
565     if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
566         key = SELECTION_SELECTION;
567     }
569     /* Try using the preferences */
570     if (key == SELECTION_NUMBER_OF) {
571         const gchar *what = NULL;
572         int i = SELECTION_NUMBER_OF;
574         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
576         if (what != NULL) {
577             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
578                 if (!strcmp (what, selection_names[i])) {
579                     break;
580                 }
581             }
582         }
584         key = (selection_type)i;
585     }
587     if (key == SELECTION_NUMBER_OF) {
588         key = SELECTION_SELECTION;
589     }
591     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
592                                                        selection_names[key]);
593     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
595     return;
599 /**
600  * \brief  If selection changed or a different document activated, we must 
601  * recalculate any chosen areas
602  *
603  */
604 static void
605 sp_export_selection_changed ( Inkscape::Application *inkscape, 
606                               Inkscape::Selection *selection, 
607                               GtkObject *base )
609     selection_type current_key;
610     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
612     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
613             (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
614             was_empty) {
615         gtk_toggle_button_set_active
616             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
617               TRUE );
618     }
619     was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
621     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
623     if (inkscape &&
624             SP_IS_INKSCAPE (inkscape) &&
625             selection &&
626             SELECTION_CUSTOM != current_key) {
627         GtkToggleButton * button;
628         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
629         sp_export_area_toggled(button, base);
630     } // end of if()
632     return;
633 } // end of sp_export_selection_changed()
635 static void
636 sp_export_selection_modified ( Inkscape::Application *inkscape, 
637                                Inkscape::Selection *selection, 
638                                guint flags,
639                                GtkObject *base )
641     selection_type current_key;
642     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
644     switch (current_key) {
645         case SELECTION_DRAWING:
646             if ( SP_ACTIVE_DESKTOP ) {
647                 SPDocument *doc;
648                 NRRect bbox;
649                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
650                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
652                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
653                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
654                 }
655             }
656             break;
657         case SELECTION_SELECTION:
658             if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
659                 NRRect bbox;
660                 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
661                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
662             }
663             break;
664         default:
665             /* Do nothing for page or for custom */
666             break;
667     }
669     return;
672 /// Called when one of the selection buttons was toggled.
673 static void
674 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
676     if (gtk_object_get_data (base, "update"))
677         return;
679     selection_type key, old_key;
680     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
681     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
683     /* Ignore all "turned off" events unless we're the only active button */
684     if (!gtk_toggle_button_get_active (tb) ) {
686         /* Don't let the current selection be deactived - but rerun the
687            activate to allow the user to renew the values */
688         if (key == old_key) {
689             gtk_toggle_button_set_active ( tb, TRUE );
690         }
692         return;
693     }
695     /* Turn off the currently active button unless it's us */
696     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
698     if (old_key != key) {
699         gtk_toggle_button_set_active
700             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
701               FALSE );
702     }
704     if ( SP_ACTIVE_DESKTOP )
705     {
706         SPDocument *doc;
707         NRRect bbox;
708         doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
710         /* Notice how the switch is used to 'fall through' here to get
711            various backups.  If you modify this without noticing you'll
712            probabaly screw something up. */
713         switch (key) {
714             case SELECTION_SELECTION:
715                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
716                 {
717                     (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
718                     /* Only if there is a selection that we can set
719                        do we break, otherwise we fall through to the
720                        drawing */
721                     // std::cout << "Using selection: SELECTION" << std::endl;
722                     key = SELECTION_SELECTION;
723                     break;
724                 }
725             case SELECTION_DRAWING:
726                 /** \todo 
727                  * This returns wrong values if the document has a viewBox.
728                  */
729                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
730                 
731                 /* If the drawing is valid, then we'll use it and break
732                    otherwise we drop through to the page settings */
733                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
734                     // std::cout << "Using selection: DRAWING" << std::endl;
735                     key = SELECTION_DRAWING;
736                     break;
737                 }
738             case SELECTION_PAGE:
739                 bbox.x0 = 0.0;
740                 bbox.y0 = 0.0;
741                 bbox.x1 = sp_document_width (doc);
742                 bbox.y1 = sp_document_height (doc);
743                 // std::cout << "Using selection: PAGE" << std::endl;
744                 key = SELECTION_PAGE;
745                 break;
746             case SELECTION_CUSTOM:
747             default:
748                 break;
749         } // switch
750         
751         // remember area setting
752         prefs_set_string_attribute ( "dialogs.export.exportarea", 
753                                      "value", selection_names[key]);
755         if (key != SELECTION_CUSTOM) {
756             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
757         }
758     
759     } // end of if ( SP_ACTIVE_DESKTOP )
762     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
763         GtkWidget * file_entry;
764         const gchar * filename = NULL;
765         float xdpi = 0.0, ydpi = 0.0;
767         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
769         switch (key) {
770             case SELECTION_PAGE:
771             case SELECTION_DRAWING: {
772                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
773                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
774                 const gchar * dpi_string;
776                 filename = repr->attribute("inkscape:export-filename");
778                 dpi_string = NULL;
779                 dpi_string = repr->attribute("inkscape:export-xdpi");
780                 if (dpi_string != NULL) {
781                     xdpi = atof(dpi_string);
782                 }
784                 dpi_string = NULL;
785                 dpi_string = repr->attribute("inkscape:export-ydpi");
786                 if (dpi_string != NULL) {
787                     ydpi = atof(dpi_string);
788                 }
790                 if (filename == NULL) {
791                     if (doc_export_name != NULL) {
792                         filename = g_strdup(doc_export_name);
793                     } else {
794                         filename = g_strdup("");
795                     }
796                 }
798                 break;
799             }
800             case SELECTION_SELECTION:
801                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
802                     const GSList * reprlst;
803                     bool filename_search = TRUE;
804                     bool xdpi_search = TRUE;
805                     bool ydpi_search = TRUE;
807                     reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
808                     for(; reprlst != NULL &&
809                             filename_search &&
810                             xdpi_search &&
811                             ydpi_search;
812                             reprlst = reprlst->next) {
813                         const gchar * dpi_string;
814                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
816                         if (filename_search) {
817                             filename = repr->attribute("inkscape:export-filename");
818                             if (filename != NULL)
819                                 filename_search = FALSE;
820                         }
822                         if (xdpi_search) {
823                             dpi_string = NULL;
824                             dpi_string = repr->attribute("inkscape:export-xdpi");
825                             if (dpi_string != NULL) {
826                                 xdpi = atof(dpi_string);
827                                 xdpi_search = FALSE;
828                             }
829                         }
831                         if (ydpi_search) {
832                             dpi_string = NULL;
833                             dpi_string = repr->attribute("inkscape:export-ydpi");
834                             if (dpi_string != NULL) {
835                                 ydpi = atof(dpi_string);
836                                 ydpi_search = FALSE;
837                             }
838                         }
839                     }
841                     /* If we still don't have a filename -- let's build
842                        one that's nice */
843                     if (filename == NULL) {
844                         const gchar * id = NULL;
845                         reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
846                         for(; reprlst != NULL; reprlst = reprlst->next) {
847                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
848                             if (repr->attribute("id")) {
849                                 id = repr->attribute("id");
850                                 break;
851                             }
852                         }
853                         if (id == NULL) /* This should never happen */
854                             id = "bitmap";
856                         gchar * directory = NULL;
857                         const gchar * file_entry_text;
859                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
860                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
861                             // std::cout << "Directory from dialog" << std::endl;
862                             directory = g_dirname(file_entry_text);
863                         }
865                         if (directory == NULL) {
866                             /* Grab document directory */
867                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
868                                 // std::cout << "Directory from document" << std::endl;
869                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
870                             }
871                         }
873                         if (directory == NULL) {
874                             // std::cout << "Home Directory" << std::endl;
875                             directory = homedir_path(NULL);
876                         }
878                         gchar * id_ext = g_strconcat(id, ".png", NULL);
879                         filename = g_build_filename(directory, id_ext, NULL);
880                         g_free(directory);
881                         g_free(id_ext);
882                     }
883                 }
884                 break;
885             case SELECTION_CUSTOM:
886             default:
887                 break;
888         }
890         if (filename != NULL) {
891             g_free(original_name);
892             original_name = g_strdup(filename);
893             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
894         }
896         if (xdpi != 0.0) {
897             sp_export_value_set(base, "xdpi", xdpi);
898         }
900         /* These can't be seperate, and setting x sets y, so for
901            now setting this is disabled.  Hopefully it won't be in
902            the future */
903         if (FALSE && ydpi != 0.0) {
904             sp_export_value_set(base, "ydpi", ydpi);
905         }
906     }
908     return;
909 } // end of sp_export_area_toggled()
911 /// Called when dialog is deleted
912 static gint
913 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
915     g_object_set_data (base, "cancel", (gpointer) 1);
916     return TRUE;
917 } // end of sp_export_progress_delete()
919 /// Called when progress is cancelled
920 static void
921 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
923     g_object_set_data (base, "cancel", (gpointer) 1);
924 } // end of sp_export_progress_cancel()
926 /// Called for every progress iteration
927 static unsigned int
928 sp_export_progress_callback (float value, void *data)
930     GtkWidget *prg;
931     int evtcount;
933     if (g_object_get_data ((GObject *) data, "cancel"))
934         return FALSE;
936     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
937     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
939     evtcount = 0;
940     while ((evtcount < 16) && gdk_events_pending ()) {
941             gtk_main_iteration_do (FALSE);
942             evtcount += 1;
943     }
945     gtk_main_iteration_do (FALSE);
947     return TRUE;
949 } // end of sp_export_progress_callback()
951 /// Called when export button is clicked
952 static void
953 sp_export_export_clicked (GtkButton *button, GtkObject *base)
955     if (!SP_ACTIVE_DESKTOP) return;
957     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
958     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
960     float const x0 = sp_export_value_get_px(base, "x0");
961     float const y0 = sp_export_value_get_px(base, "y0");
962     float const x1 = sp_export_value_get_px(base, "x1");
963     float const y1 = sp_export_value_get_px(base, "y1");
964     float const xdpi = sp_export_value_get(base, "xdpi");
965     float const ydpi = sp_export_value_get(base, "ydpi");
966     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
967     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
969     if (filename == NULL || *filename == '\0') {
970         sp_ui_error_dialog(_("You have to enter a filename"));
971         return;
972     }
974     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
975         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
976         return;
977     }
979     gchar *dirname = g_dirname(filename);
980     if ( dirname == NULL
981          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
982     {
983         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
984         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
985                                        safeDir);
986         sp_ui_error_dialog(error);
987         g_free(safeDir);
988         g_free(error);
989         g_free(dirname);
990         return;
991     }
992     g_free(dirname);
994     SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
995     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
996     char *fn;
997     gchar *text;
999     dlg = gtk_dialog_new ();
1000     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1001     prg = gtk_progress_bar_new ();
1002     sp_transientize (dlg);
1003     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1004     g_object_set_data ((GObject *) base, "progress", prg);
1005     fn = g_path_get_basename (filename);
1006     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
1007                              fn, width, height);
1008     g_free (fn);
1009     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1010     g_free (text);
1011     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1012                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1013     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1014                         prg, FALSE, FALSE, 4 );
1015     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1016                                   GTK_STOCK_CANCEL, 
1017                                   GTK_RESPONSE_CANCEL );
1018                                   
1019     g_signal_connect ( (GObject *) dlg, "delete_event", 
1020                        (GCallback) sp_export_progress_delete, base);
1021     g_signal_connect ( (GObject *) btn, "clicked", 
1022                        (GCallback) sp_export_progress_cancel, base);
1023     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1024     gtk_widget_show_all (dlg);
1025     
1026     /* Do export */
1027     if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename, 
1028                              x0, y0, x1, y1, width, height, 
1029                              nv->pagecolor, 
1030                              sp_export_progress_callback, base)) {
1031         gchar * error;
1032         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1033         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1034         sp_ui_error_dialog(error);
1035         g_free(safeFile);
1036         g_free(error);
1037     }
1039     /* Reset the filename so that it can be changed again by changing
1040        selections and all that */
1041     g_free(original_name);
1042     original_name = g_strdup(filename);
1043     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1045     gtk_widget_destroy (dlg);
1046     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1048     /* Setup the values in the document */
1049     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1050         case SELECTION_PAGE:
1051         case SELECTION_DRAWING: {
1052             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1053             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1054             bool modified = FALSE;
1055             const gchar * temp_string;
1057             bool saved = sp_document_get_undo_sensitive(doc);
1058             sp_document_set_undo_sensitive(doc, FALSE);
1060             temp_string = repr->attribute("inkscape:export-filename");
1061             if (temp_string == NULL || strcmp(temp_string, filename)) {
1062                 repr->setAttribute("inkscape:export-filename", filename);
1063                 modified = TRUE;
1064             }
1065             temp_string = repr->attribute("inkscape:export-xdpi");
1066             if (temp_string == NULL || xdpi != atof(temp_string)) {
1067                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1068                 modified = TRUE;
1069             }
1070             temp_string = repr->attribute("inkscape:export-ydpi");
1071             if (temp_string == NULL || xdpi != atof(temp_string)) {
1072                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1073                 modified = TRUE;
1074             }
1076             if (modified)
1077                 repr->setAttribute("sodipodi:modified", "TRUE");
1078             sp_document_set_undo_sensitive(doc, saved);
1079             break;
1080         }
1081         case SELECTION_SELECTION: {
1082             const GSList * reprlst;
1083             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1084             bool modified = FALSE;
1086             bool saved = sp_document_get_undo_sensitive(doc);
1087             sp_document_set_undo_sensitive(doc, FALSE);
1088             reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1090             for(; reprlst != NULL; reprlst = reprlst->next) {
1091                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1092                 const gchar * temp_string;
1094                 if (repr->attribute("id") == NULL ||
1095                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1096                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1097                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1098                     temp_string = repr->attribute("inkscape:export-filename");
1099                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1100                         repr->setAttribute("inkscape:export-filename", filename);
1101                         modified = TRUE;
1102                     }
1103                 }
1104                 temp_string = repr->attribute("inkscape:export-xdpi");
1105                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1106                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1107                     modified = TRUE;
1108                 }
1109                 temp_string = repr->attribute("inkscape:export-ydpi");
1110                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1111                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1112                     modified = TRUE;
1113                 }
1114             }
1116             if (modified) {
1117                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1118                 repr->setAttribute("sodipodi:modified", "TRUE");
1119             }
1121             sp_document_set_undo_sensitive(doc, saved);
1122             break;
1123         }
1124         default:
1125             break;
1126     }
1129     return;
1130 } // end of sp_export_export_clicked()
1132 /// Called when Browse button is clicked
1133 static void
1134 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1136     GtkWidget *fs, *fe;
1137     const gchar *filename;
1139     fs = gtk_file_selection_new (_("Select a filename for exporting"));
1140     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1142     sp_transientize (fs);
1144     gtk_window_set_modal(GTK_WINDOW (fs), true);
1146     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1148     if (*filename == '\0') {
1149         filename = homedir_path(NULL);
1150     }
1152     gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1154     g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1155                        "clicked",
1156                        G_CALLBACK (sp_export_browse_store),
1157                        (gpointer) fs );
1159     g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1160                                "clicked",
1161                                G_CALLBACK (gtk_widget_destroy),
1162                                (gpointer) fs );
1164     g_signal_connect_swapped ( GTK_OBJECT
1165                                    (GTK_FILE_SELECTION (fs)->cancel_button),
1166                                "clicked",
1167                                G_CALLBACK (gtk_widget_destroy),
1168                                (gpointer) fs );
1170     gtk_widget_show (fs);
1172     return;
1173 } // end of sp_export_browse_clicked()
1175 /// Called when OK clicked in file dialog
1176 static void
1177 sp_export_browse_store (GtkButton *button, gpointer userdata)
1179     GtkWidget *fs = (GtkWidget *)userdata, *fe;
1180     const gchar *file;
1182     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1184     file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1185     gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1186     gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1187     g_free(utf8file);
1189     g_object_set_data (G_OBJECT (dlg), "filename", fe);
1191     return;
1192 } // end of sp_export_browse_store()
1194 // TODO: Move this to nr-rect-fns.h.
1195 static bool
1196 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1197
1198     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1199     return (
1200         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1201         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1202         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1203         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1204         );
1207 /**
1208     \brief  This function is used to detect the current selection setting
1209             based on the values in the x0, y0, x1 and y0 fields.
1210     \param  base  The export dialog itself
1212     One of the most confusing parts of this function is why the array
1213     is built at the beginning.  What needs to happen here is that we
1214     should always check the current selection to see if it is the valid
1215     one.  While this is a performance improvement it is also a usability
1216     one during the cases where things like selections and drawings match
1217     size.  This way buttons change less 'randomly' (atleast in the eyes
1218     of the user).  To do this an array is built where the current selection
1219     type is placed first, and then the others in an order from smallest
1220     to largest (this can be configured by reshuffling \c test_order).
1222     All of the values in this function are rounded to two decimal places
1223     because that is what is shown to the user.  While everything is kept
1224     more accurate than that, the user can't control more acurrate than
1225     that, so for this to work for them - it needs to check on that level
1226     of accuracy.
1228     \todo finish writing this up
1229 */
1230 static void
1231 sp_export_detect_size(GtkObject * base) {
1232     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1233     selection_type this_test[SELECTION_NUMBER_OF + 1];
1234     selection_type key = SELECTION_NUMBER_OF;
1236     NR::Point x(sp_export_value_get_px (base, "x0"),
1237                 sp_export_value_get_px (base, "y0"));
1238     NR::Point y(sp_export_value_get_px (base, "x1"),
1239                 sp_export_value_get_px (base, "y1"));
1240     NR::Rect current_bbox(x, y);
1241     //std::cout << "Current " << current_bbox;
1243     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1244     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1245         this_test[i + 1] = test_order[i];
1246     }
1248     for (int i = 0;
1249             i < SELECTION_NUMBER_OF + 1 &&
1250                 key == SELECTION_NUMBER_OF &&
1251                 SP_ACTIVE_DESKTOP != NULL;
1252             i++) {
1253         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1254         switch (this_test[i]) {
1255             case SELECTION_SELECTION:
1256                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1257                     NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1259                     //std::cout << "Selection " << bbox;
1260                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1261                         key = SELECTION_SELECTION;
1262                     }
1263                 }
1264                 break;
1265             case SELECTION_DRAWING: {
1266                 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1268                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1270                 // std::cout << "Drawing " << bbox2;
1271                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1272                     key = SELECTION_DRAWING;
1273                 }
1274                 break;
1275             }
1277             case SELECTION_PAGE: {
1278                 SPDocument *doc;
1280                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1282                 NR::Point x(0.0, 0.0);
1283                 NR::Point y(sp_document_width(doc),
1284                             sp_document_height(doc));
1285                 NR::Rect bbox(x, y);
1287                 // std::cout << "Page " << bbox;
1288                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1289                     key = SELECTION_PAGE;
1290                 }
1292                 break;
1293            }
1294         default:
1295            break;
1296         }
1297     }
1298     // std::cout << std::endl;
1300     if (key == SELECTION_NUMBER_OF) {
1301         key = SELECTION_CUSTOM;
1302     }
1304     /* We're now using a custom size, not a fixed one */
1305     /* printf("Detecting state: %s\n", selection_names[key]); */
1306     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1307     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1308     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1309     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1311     return;
1312 } /* sp_export_detect_size */
1314 /// Called when area x0 value is changed
1315 static void
1316 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1318     float x0, x1, xdpi, width;
1320     if (gtk_object_get_data (base, "update"))
1321         return;
1323     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1324             (base, "units")))
1325     {
1326         return;
1327     }
1329     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1331     x0 = sp_export_value_get_px (base, "x0");
1332     x1 = sp_export_value_get_px (base, "x1");
1333     xdpi = sp_export_value_get (base, "xdpi");
1335     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1337     if (width < SP_EXPORT_MIN_SIZE) {
1338         const gchar *key;
1339         width = SP_EXPORT_MIN_SIZE;
1340         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1342         if (!strcmp (key, "x0")) {
1343             x1 = x0 + width * DPI_BASE / xdpi;
1344             sp_export_value_set_px (base, "x1", x1);
1345         } else {
1346             x0 = x1 - width * DPI_BASE / xdpi;
1347             sp_export_value_set_px (base, "x0", x0);
1348         }
1349     }
1351     sp_export_value_set_px (base, "width", x1 - x0);
1352     sp_export_value_set (base, "bmwidth", width);
1354     sp_export_detect_size(base);
1356     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1358     return;
1359 } // end of sp_export_area_x_value_changed()
1361 /// Called when area y0 value is changed.
1362 static void
1363 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1365     float y0, y1, ydpi, height;
1367     if (gtk_object_get_data (base, "update"))
1368         return;
1370     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1371            (base, "units")))
1372     {
1373         return;
1374     }
1376     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1378     y0 = sp_export_value_get_px (base, "y0");
1379     y1 = sp_export_value_get_px (base, "y1");
1380     ydpi = sp_export_value_get (base, "ydpi");
1382     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1384     if (height < SP_EXPORT_MIN_SIZE) {
1385         const gchar *key;
1386         height = SP_EXPORT_MIN_SIZE;
1387         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1388         if (!strcmp (key, "y0")) {
1389             y1 = y0 + height * DPI_BASE / ydpi;
1390             sp_export_value_set_px (base, "y1", y1);
1391         } else {
1392             y0 = y1 - height * DPI_BASE / ydpi;
1393             sp_export_value_set_px (base, "y0", y0);
1394         }
1395     }
1397     sp_export_value_set_px (base, "height", y1 - y0);
1398     sp_export_value_set (base, "bmheight", height);
1400     sp_export_detect_size(base);
1402     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1404     return;
1405 } // end of sp_export_area_y_value_changed()
1407 /// Called when x1-x0 or area width is changed
1408 static void
1409 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1411     float x0, x1, xdpi, width, bmwidth;
1413     if (gtk_object_get_data (base, "update"))
1414         return;
1416     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1417            (base, "units"))) {
1418         return;
1419     }
1421     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1423     x0 = sp_export_value_get_px (base, "x0");
1424     x1 = sp_export_value_get_px (base, "x1");
1425     xdpi = sp_export_value_get (base, "xdpi");
1426     width = sp_export_value_get_px (base, "width");
1427     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1429     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1431         bmwidth = SP_EXPORT_MIN_SIZE;
1432         width = bmwidth * DPI_BASE / xdpi;
1433         sp_export_value_set_px (base, "width", width);
1434     }
1436     sp_export_value_set_px (base, "x1", x0 + width);
1437     sp_export_value_set (base, "bmwidth", bmwidth);
1439     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1441     return;
1442 } // end of sp_export_area_width_value_changed()
1444 /// Called when y1-y0 or area height is changed.
1445 static void
1446 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1449     float y0, y1, ydpi, height, bmheight;
1451     if (gtk_object_get_data (base, "update"))
1452         return;
1454     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1455            (base, "units"))) {
1456         return;
1457     }
1459     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1461     y0 = sp_export_value_get_px (base, "y0");
1462     y1 = sp_export_value_get_px (base, "y1");
1463     ydpi = sp_export_value_get (base, "ydpi");
1464     height = sp_export_value_get_px (base, "height");
1465     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1467     if (bmheight < SP_EXPORT_MIN_SIZE) {
1468         bmheight = SP_EXPORT_MIN_SIZE;
1469         height = bmheight * DPI_BASE / ydpi;
1470         sp_export_value_set_px (base, "height", height);
1471     }
1473     sp_export_value_set_px (base, "y1", y0 + height);
1474     sp_export_value_set (base, "bmheight", bmheight);
1476     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1478     return;
1479 } // end of sp_export_area_height_value_changed()
1481 /**
1482     \brief  A function to set the ydpi
1483     \param  base  The export dialog
1485     This function grabs all of the y values and then figures out the
1486     new bitmap size based on the changing dpi value.  The dpi value is
1487     gotten from the xdpi setting as these can not currently be independent.
1488 */
1489 static void
1490 sp_export_set_image_y (GtkObject *base)
1492     float y0, y1, xdpi;
1494     y0 = sp_export_value_get_px (base, "y0");
1495     y1 = sp_export_value_get_px (base, "y1");
1496     xdpi = sp_export_value_get (base, "xdpi");
1498     sp_export_value_set (base, "ydpi", xdpi);
1499     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1501     return;
1502 } // end of sp_export_set_image_y()
1504 /**
1505     \brief  A function to set the xdpi
1506     \param  base  The export dialog
1508     This function grabs all of the x values and then figures out the
1509     new bitmap size based on the changing dpi value.  The dpi value is
1510     gotten from the xdpi setting as these can not currently be independent.
1511 */
1512 static void
1513 sp_export_set_image_x (GtkObject *base)
1515     float x0, x1, xdpi;
1517     x0 = sp_export_value_get_px (base, "x0");
1518     x1 = sp_export_value_get_px (base, "x1");
1519     xdpi = sp_export_value_get (base, "xdpi");
1521     sp_export_value_set (base, "ydpi", xdpi);
1522     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1524     return;
1525 } // end of sp_export_set_image_x()
1527 /// Called when pixel width is changed
1528 static void
1529 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1531     float x0, x1, bmwidth, xdpi;
1533     if (gtk_object_get_data (base, "update"))
1534         return;
1536     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1537            (base, "units"))) {
1538        return;
1539     }
1541     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1543     x0 = sp_export_value_get_px (base, "x0");
1544     x1 = sp_export_value_get_px (base, "x1");
1545     bmwidth = sp_export_value_get (base, "bmwidth");
1547     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1548         bmwidth = SP_EXPORT_MIN_SIZE;
1549         sp_export_value_set (base, "bmwidth", bmwidth);
1550     }
1552     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1553     sp_export_value_set (base, "xdpi", xdpi);
1555     sp_export_set_image_y (base);
1557     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1559     return;
1560 } // end of sp_export_bitmap_width_value_changed()
1562 /// Called when pixel height is changed
1563 static void
1564 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1566     float y0, y1, bmheight, xdpi;
1568     if (gtk_object_get_data (base, "update"))
1569         return;
1571     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1572            (base, "units"))) {
1573        return;
1574     }
1576     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1578     y0 = sp_export_value_get_px (base, "y0");
1579     y1 = sp_export_value_get_px (base, "y1");
1580     bmheight = sp_export_value_get (base, "bmheight");
1582     if (bmheight < SP_EXPORT_MIN_SIZE) {
1583         bmheight = SP_EXPORT_MIN_SIZE;
1584         sp_export_value_set (base, "bmheight", bmheight);
1585     }
1587     xdpi = bmheight * DPI_BASE / (y1 - y0);
1588     sp_export_value_set (base, "xdpi", xdpi);
1590     sp_export_set_image_x (base);
1592     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594     return;
1595 } // end of sp_export_bitmap_width_value_changed()
1597 /**
1598     \brief  A function to adjust the bitmap width when the xdpi value changes
1599     \param  adj  The adjustment that was changed
1600     \param  base The export dialog itself
1602     The first thing this function checks is to see if we are doing an
1603     update.  If we are, this function just returns because there is another
1604     instance of it that will handle everything for us.  If there is a
1605     units change, we also assume that everyone is being updated appropriately
1606     and there is nothing for us to do.
1608     If we're the highest level function, we set the update flag, and
1609     continue on our way.
1611     All of the values are grabbed using the \c sp_export_value_get functions
1612     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1613     xdpi value is saved in the preferences for the next time the dialog
1614     is opened.  (does the selection dpi need to be set here?)
1616     A check is done to to ensure that we aren't outputing an invalid width,
1617     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1618     changed to make it valid.
1620     After all of this the bitmap width is changed.
1622     We also change the ydpi.  This is a temporary hack as these can not
1623     currently be independent.  This is likely to change in the future.
1624 */
1625 void
1626 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1628     float x0, x1, xdpi, bmwidth;
1630     if (gtk_object_get_data (base, "update"))
1631         return;
1633     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1634            (base, "units"))) {
1635        return;
1636     }
1638     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1640     x0 = sp_export_value_get_px (base, "x0");
1641     x1 = sp_export_value_get_px (base, "x1");
1642     xdpi = sp_export_value_get (base, "xdpi");
1644     // remember xdpi setting
1645     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1647     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1649     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1650         bmwidth = SP_EXPORT_MIN_SIZE;
1651         if (x1 != x0)
1652             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1653         else
1654             xdpi = DPI_BASE;
1655         sp_export_value_set (base, "xdpi", xdpi);
1656     }
1658     sp_export_value_set (base, "bmwidth", bmwidth);
1660     sp_export_set_image_y (base);
1662     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1664     return;
1665 } // end of sp_export_xdpi_value_changed()
1668 /**
1669     \brief  A function to change the area that is used for the exported
1670             bitmap.
1671     \param  base  This is the export dialog
1672     \param  x0    Horizontal upper left hand corner of the picture in points
1673     \param  y0    Vertical upper left hand corner of the picture in points
1674     \param  x1    Horizontal lower right hand corner of the picture in points
1675     \param  y1    Vertical lower right hand corner of the picture in points
1677     This function just calls \c sp_export_value_set_px for each of the
1678     parameters that is passed in.  This allows for setting them all in
1679     one convient area.
1681     Update is set to suspend all of the other test running while all the
1682     values are being set up.  This allows for a performance increase, but
1683     it also means that the wrong type won't be detected with only some of
1684     the values set.  After all the values are set everyone is told that
1685     there has been an update.
1686 */
1687 static void
1688 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1690     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1691     sp_export_value_set_px (base, "x1", x1);
1692     sp_export_value_set_px (base, "y1", y1);
1693     sp_export_value_set_px (base, "x0", x0);
1694     sp_export_value_set_px (base, "y0", y0);
1695     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1697     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1698     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1700     return;
1703 /**
1704     \brief  Sets the value of an adjustment
1705     \param  base  The export dialog
1706     \param  key   Which adjustment to set
1707     \param  val   What value to set it to
1709     This function finds the adjustment using the data stored in the
1710     export dialog.  After finding the adjustment it then sets
1711     the value of it.
1712 */
1713 static void
1714 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1716     GtkAdjustment *adj;
1718     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1720     gtk_adjustment_set_value (adj, val);
1723 /**
1724     \brief  A function to set a value using the units points
1725     \param  base  The export dialog
1726     \param  key   Which value should be set
1727     \param  val   What the value should be in points
1729     This function first gets the adjustment for the key that is passed
1730     in.  It then figures out what units are currently being used in the
1731     dialog.  After doing all of that, it then converts the incoming
1732     value and sets the adjustment.
1733 */
1734 static void
1735 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1737     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1739     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1741     return;
1744 /**
1745     \brief  Get the value of an adjustment in the export dialog
1746     \param  base  The export dialog
1747     \param  key   Which adjustment is being looked for
1748     \return The value in the specified adjustment
1750     This function gets the adjustment from the data field in the export
1751     dialog.  It then grabs the value from the adjustment.
1752 */
1753 static float
1754 sp_export_value_get ( GtkObject *base, const gchar *key )
1756     GtkAdjustment *adj;
1758     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1760     return adj->value;
1761 } // end of sp_export_value_get()
1763 /**
1764     \brief  Grabs a value in the export dialog and converts the unit
1765             to points
1766     \param  base  The export dialog
1767     \param  key   Which value should be returned
1768     \return The value in the adjustment in points
1770     This function, at its most basic, is a call to \c sp_export_value_get
1771     to get the value of the adjustment.  It then finds the units that
1772     are being used by looking at the "units" attribute of the export
1773     dialog.  Using that it converts the returned value into points.
1774 */
1775 static float
1776 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1778     float value = sp_export_value_get(base, key);
1779     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1781     return sp_units_get_pixels (value, *unit);
1782 } // end of sp_export_value_get_px()
1784 /**
1785     \brief  This function is called when the filename is changed by
1786             anyone.  It resets the virgin bit.
1787     \param  object  Text entry box
1788     \param  data    The export dialog
1789     \return None
1791     This function gets called when the text area is modified.  It is
1792     looking for the case where the text area is modified from its
1793     original value.  In that case it sets the "filename-modified" bit
1794     to TRUE.  If the text dialog returns back to the original text, the
1795     bit gets reset.  This should stop simple mistakes.
1796 */
1797 static void
1798 sp_export_filename_modified (GtkObject * object, gpointer data)
1800     GtkWidget * text_entry = (GtkWidget *)object;
1801     GtkWidget * export_dialog = (GtkWidget *)data;
1803     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1804         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1805 //        printf("Modified: FALSE\n");
1806     } else {
1807         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1808 //        printf("Modified: TRUE\n");
1809     }
1811     return;
1812 } // end sp_export_filename_modified
1814 /*
1815   Local Variables:
1816   mode:c++
1817   c-file-style:"stroustrup"
1818   c-file-offsets:((innamespace . 0)(inline-open . 0))
1819   indent-tabs-mode:nil
1820   fill-column:99
1821   End:
1822 */
1823 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :