Code

Fixing scrollbar size for embeded color swatches.
[inkscape.git] / src / dialogs / export.cpp
1 #define __SP_EXPORT_C__
3 /** \file
4  * \brief  PNG export dialog
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   bulia byak <buliabyak@users.sf.net>
11  *
12  * Copyright (C) 1999-2005 Authors
13  * Copyright (C) 2001-2002 Ximian, Inc.
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
24 #include <glibmm/i18n.h>
25 #include "helper/unit-menu.h"
26 #include "helper/units.h"
27 #include "unit-constants.h"
28 #include "helper/window.h"
29 #include "inkscape-private.h"
30 #include "document.h"
31 #include "desktop-handles.h"
32 #include "sp-item.h"
33 #include "selection.h"
34 #include "file.h"
35 #include "macros.h"
36 #include "sp-namedview.h"
38 #include "dialog-events.h"
39 #include "../prefs-utils.h"
40 #include "../verbs.h"
41 #include "../interface.h"
43 #include "extension/output.h"
44 #include "extension/db.h"
46 #include "io/sys.h"
49 #define SP_EXPORT_MIN_SIZE 1.0
51 #define DPI_BASE PX_PER_IN
53 #define EXPORT_COORD_PRECISION 3
55 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
56 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
57 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
58 static void sp_export_browse_store   ( GtkButton *button, gpointer userdata );
61 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj, 
62                                                    GtkObject *base);
63                                              
64 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj, 
65                                                    GtkObject *base);
66                                              
67 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj, 
68                                                    GtkObject *base);
69                                                  
70 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj, 
71                                                    GtkObject *base);
72                                                   
73 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, 
74                                                    GtkObject *base);
75                                                    
76 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj, 
77                                                    GtkObject *base);
78                                            
79 static void sp_export_selection_changed ( Inkscape::Application *inkscape, 
80                                           Inkscape::Selection *selection, 
81                                           GtkObject *base);
82 static void sp_export_selection_modified ( Inkscape::Application *inkscape, 
83                                            Inkscape::Selection *selection, 
84                                            guint flags,
85                                            GtkObject *base );
87 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
88 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
89 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
90 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
91 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
93 static void sp_export_filename_modified (GtkObject * object, gpointer data);
94 static inline void sp_export_find_default_selection(GtkWidget * dlg);
95 static void sp_export_detect_size(GtkObject * base);
97 static const gchar *prefs_path = "dialogs.export";
99 // these all need to be reinitialized to their defaults during dialog_destroy
100 static GtkWidget *dlg = NULL;
101 static win_data wd;
102 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
103 static gchar * original_name = NULL;
104 static gchar * doc_export_name = NULL;
105 static bool was_empty = TRUE;
107 /** What type of button is being pressed */
108 enum selection_type {
109     SELECTION_PAGE = 0,  /**< Export the whole page */
110     SELECTION_DRAWING,   /**< Export everything drawn on the page */
111     SELECTION_SELECTION, /**< Export everything that is selected */
112     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
113     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
114 };
116 /** A list of strings that is used both in the preferences, and in the
117     data fields to describe the various values of \c selection_type. */
118 static const char * selection_names[SELECTION_NUMBER_OF] = {
119     "page", "drawing", "selection", "custom"};
121 /** The names on the buttons for the various selection types. */
122 static const char * selection_labels[SELECTION_NUMBER_OF] = {
123     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
125 static void
126 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
128     sp_signal_disconnect_by_data (INKSCAPE, dlg);
130     wd.win = dlg = NULL;
131     wd.stop = 0;
132     x = -1000; y = -1000; w = 0; h = 0;
133     g_free(original_name);
134     original_name = NULL;
135     g_free(doc_export_name);
136     doc_export_name = NULL;
137     was_empty = TRUE;
139     return;
140 } // end of sp_export_dialog_destroy()
142 /// Called when dialog is closed or inkscape is shut down.
143 static bool
144 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
147     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
148     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
150     prefs_set_int_attribute (prefs_path, "x", x);
151     prefs_set_int_attribute (prefs_path, "y", y);
152     prefs_set_int_attribute (prefs_path, "w", w);
153     prefs_set_int_attribute (prefs_path, "h", h);
155     return FALSE; // which means, go ahead and destroy it
157 } // end of sp_export_dialog_delete()
159 /**
160     \brief  Creates a new spin button for the export dialog
161     \param  key  The name of the spin button
162     \param  val  A default value for the spin button
163     \param  min  Minimum value for the spin button
164     \param  max  Maximum value for the spin button
165     \param  step The step size for the spin button
166     \param  page Size of the page increment
167     \param  us   Unit selector that effects this spin button
168     \param  t    Table to put the spin button in
169     \param  x    X location in the table \c t to start with
170     \param  y    Y location in the table \c t to start with
171     \param  ll   Text to put on the left side of the spin button (optional)
172     \param  lr   Text to put on the right side of the spin button (optional)
173     \param  digits  Number of digits to display after the decimal
174     \param  sensitive  Whether the spin button is sensitive or not
175     \param  cb   Callback for when this spin button is changed (optional)
176     \param  dlg  Export dialog the spin button is being placed in
178 */
179 static void
180 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
181                            float step, float page, GtkWidget *us,
182                            GtkWidget *t, int x, int y,
183                            const gchar *ll, const gchar *lr,
184                            int digits, unsigned int sensitive,
185                            GCallback cb, GtkWidget *dlg )
187     GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
188     gtk_object_set_data (a, "key", key);
189     gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
191     if (us) {
192         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
193                                           GTK_ADJUSTMENT (a) );
194     }
196     int pos = 0;
198     GtkWidget *l = NULL;
200     if (ll) {
202         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
203         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
204         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
205                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
206         gtk_widget_set_sensitive (l, sensitive);
207         pos += 1;
209     }
211     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
212     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
213                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
214     gtk_widget_set_size_request (sb, 80, -1);
215     gtk_widget_set_sensitive (sb, sensitive);
216     pos += 1;
218     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
220     if (lr) {
222         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
223         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
224         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
225                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
226         gtk_widget_set_sensitive (l, sensitive);
227         pos += 1;
229         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
230     }
232     if (cb)
233         gtk_signal_connect (a, "value_changed", cb, dlg);
235     return;
236 } // end of sp_export_spinbutton_new()
239 static GtkWidget *
240 sp_export_dialog_area_frame (GtkWidget * dlg)
242     GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
244     f = gtk_frame_new (_("Export area"));
245     vb = gtk_vbox_new (FALSE, 2);
246     gtk_container_add (GTK_CONTAINER (f), vb);
248     /* Units box */
249     unitbox = gtk_hbox_new (FALSE, 0);
250     gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
251     /* gets added to the vbox later, but the unit selector is needed
252        earlier than that */
254     us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
255     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
256     if (desktop)
257         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
258     gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
259     l = gtk_label_new (_("Units:"));
260     gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
261     gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
263     hb = gtk_hbox_new (TRUE, 0);
264     gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
265     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
267     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
268         b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
269         gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
270         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
271         gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
272         gtk_signal_connect ( GTK_OBJECT (b), "clicked", 
273                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
274     }
276     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
277                        G_CALLBACK (sp_export_selection_changed), dlg );
278     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
279                        G_CALLBACK (sp_export_selection_modified), dlg );
280     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", 
281                        G_CALLBACK (sp_export_selection_changed), dlg );
282     
283     t = gtk_table_new (2, 6, FALSE);
284     gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
285     gtk_table_set_row_spacings (GTK_TABLE (t), 4);
286     gtk_table_set_col_spacings (GTK_TABLE (t), 4);
287     gtk_container_set_border_width (GTK_CONTAINER (t), 4);
289     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
290                                t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
291                                G_CALLBACK ( sp_export_area_x_value_changed), 
292                                dlg );
294     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
295                                t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
296                                G_CALLBACK (sp_export_area_x_value_changed), 
297                                dlg );
299     sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
300                                us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
301                                G_CALLBACK 
302                                    (sp_export_area_width_value_changed), 
303                                dlg );
305     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
306                                t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
307                                G_CALLBACK (sp_export_area_y_value_changed), 
308                                dlg );
310     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, 
311                                t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
312                                G_CALLBACK (sp_export_area_y_value_changed), 
313                                dlg );
315     sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, 
316                                us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
317                                G_CALLBACK (sp_export_area_height_value_changed), 
318                                dlg );
320     /* Adding in the unit box */
321     gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
323     return f;
324 } // end of sp_export_dialog_area_frame
327 void
328 sp_export_dialog (void)
330     if (!dlg) {
331         GtkWidget *vb, *hb;
333         gchar title[500];
334         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
336         dlg = sp_window_new (title, TRUE);
338         if (x == -1000 || y == -1000) {
339             x = prefs_get_int_attribute (prefs_path, "x", 0);
340             y = prefs_get_int_attribute (prefs_path, "y", 0);
341         }
343         if (w ==0 || h == 0) {
344             w = prefs_get_int_attribute (prefs_path, "w", 0);
345             h = prefs_get_int_attribute (prefs_path, "h", 0);
346         }
348         if (x != 0 || y != 0) {
349             gtk_window_move ((GtkWindow *) dlg, x, y);
350         } else {
351             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
352         }
354         if (w && h)
355             gtk_window_resize ((GtkWindow *) dlg, w, h);
357         sp_transientize (dlg);
358         wd.win = dlg;
359         wd.stop = 0;
361         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
362                              G_CALLBACK (sp_transientize_callback), &wd);
364         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
365                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
367         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
368                              G_CALLBACK (sp_export_dialog_destroy), dlg);
370         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
371                              G_CALLBACK (sp_export_dialog_delete), dlg);
373         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
374                              G_CALLBACK (sp_export_dialog_delete), dlg);
376         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
377                              G_CALLBACK (sp_dialog_hide), dlg);
379         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
380                              G_CALLBACK (sp_dialog_unhide), dlg);
382         GtkTooltips *tt = gtk_tooltips_new();
384         vb = gtk_vbox_new (FALSE, 4);
385         gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
386         gtk_container_add (GTK_CONTAINER (dlg), vb);
388         /* Export area frame */
389         {
390             GtkWidget *f = sp_export_dialog_area_frame(dlg);
391             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
392         }
394         /* Bitmap size frame */
395         {
396             GtkWidget *f = gtk_frame_new (_("Bitmap size"));
397             gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
398             GtkWidget *t = gtk_table_new (2, 5, FALSE);
399             gtk_table_set_row_spacings (GTK_TABLE (t), 4);
400             gtk_table_set_col_spacings (GTK_TABLE (t), 4);
401             gtk_container_set_border_width (GTK_CONTAINER (t), 4);
402             gtk_container_add (GTK_CONTAINER (f), t);
404             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, 
405                                        NULL, t, 0, 0,
406                                        _("_Width:"), _("pixels at"), 0, 1,
407                                        G_CALLBACK 
408                                        (sp_export_bitmap_width_value_changed), 
409                                        dlg );
411             sp_export_spinbutton_new ( "xdpi", 
412                                        prefs_get_double_attribute 
413                                        ( "dialogs.export.defaultxdpi", 
414                                          "value", DPI_BASE), 
415                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
416                                        NULL, _("dp_i"), 2, 1,
417                                        G_CALLBACK (sp_export_xdpi_value_changed), 
418                                        dlg );
420             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0, 
421                                        NULL, t, 0, 1, _("Height:"), _("pixels at"), 
422                                        0, 0, NULL, dlg );
424             /** \todo
425              * Needs fixing: there's no way to set ydpi currently, so we use  
426              *       the defaultxdpi value here, too...
427              */
428             sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute 
429                                        ( "dialogs.export.defaultxdpi", 
430                                          "value", DPI_BASE), 
431                                        1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
432                                        NULL, _("dpi"), 2, 0, NULL, dlg );
433         }
435         /* File entry */
436         {
437             GtkWidget *frame = gtk_frame_new ("");
438             GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
439             gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
440             gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
442             GtkWidget *fe = gtk_entry_new ();
444             /*
445              * set the default filename to be that of the current path + document
446              * with .png extension
447              *
448              * One thing to notice here is that this filename may get
449              * overwritten, but it won't happen here.  The filename gets
450              * written into the text field, but then the button to select
451              * the area gets set.  In that code the filename can be changed
452              * if there are some with presidence in the document.  So, while
453              * this code sets the name first, it may not be the one users
454              * really see.
455              */
456             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
457             {
458                 gchar *name;
459                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
460                 const gchar *uri = SP_DOCUMENT_URI (doc);
461                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
462                 const gchar * text_extension = repr->attribute("inkscape:output_extension");
463                 Inkscape::Extension::Output * oextension = NULL;
465                 if (text_extension != NULL) {
466                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
467                 }
469                 if (oextension != NULL) {
470                     gchar * old_extension = oextension->get_extension();
471                     if (g_str_has_suffix(uri, old_extension)) {
472                         gchar * uri_copy;
473                         gchar * extension_point;
474                         gchar * final_name;
476                         uri_copy = g_strdup(uri);
477                         extension_point = g_strrstr(uri_copy, old_extension);
478                         extension_point[0] = '\0';
480                         final_name = g_strconcat(uri_copy, ".png", NULL);
481                         gtk_entry_set_text (GTK_ENTRY (fe), final_name);
483                         g_free(final_name);
484                         g_free(uri_copy);
485                     }
486                 } else {
487                     name = g_strconcat(uri, ".png", NULL);
488                     gtk_entry_set_text (GTK_ENTRY (fe), name);
489                     g_free(name);
490                 }
492                 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
493             }
494             g_signal_connect ( G_OBJECT (fe), "changed",
495                                G_CALLBACK (sp_export_filename_modified), dlg);
497             hb = gtk_hbox_new (FALSE, 5);
498             gtk_container_add (GTK_CONTAINER (frame), hb);
499             gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
501             {
502                 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
503                 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
504                 g_signal_connect ( G_OBJECT (b), "clicked",
505                                    G_CALLBACK (sp_export_browse_clicked), NULL );
506             }
508             gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
509             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
510             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
511             original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
512             // pressing enter in the filename field is the same as clicking export:
513             g_signal_connect ( G_OBJECT (fe), "activate",
514                                G_CALLBACK (sp_export_export_clicked), dlg );
515             // focus is in the filename initially:
516             gtk_widget_grab_focus (GTK_WIDGET (fe));
518             // mnemonic in frame label moves focus to filename:
519             gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
520         }
522         /* Buttons */
523         hb = gtk_hbox_new (FALSE, 0);
524         gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
526         {
527             GtkWidget *b = gtk_button_new ();
528             GtkWidget *l = gtk_label_new ("");
529             gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
530             gtk_container_add (GTK_CONTAINER(b), l);
531             gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
532             gtk_signal_connect ( GTK_OBJECT (b), "clicked",
533                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
534             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
535         }
537         gtk_widget_show_all (vb);
539     } // end of if (!dlg)
541     sp_export_find_default_selection(dlg);
543     gtk_window_present ((GtkWindow *) dlg);
545     return;
546 } // end of sp_export_dialog()
548 static inline void
549 sp_export_find_default_selection(GtkWidget * dlg)
551     selection_type key = SELECTION_NUMBER_OF;
553     if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
554         key = SELECTION_SELECTION;
555     }
557     /* Try using the preferences */
558     if (key == SELECTION_NUMBER_OF) {
559         const gchar *what = NULL;
560         int i = SELECTION_NUMBER_OF;
562         what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
564         if (what != NULL) {
565             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
566                 if (!strcmp (what, selection_names[i])) {
567                     break;
568                 }
569             }
570         }
572         key = (selection_type)i;
573     }
575     if (key == SELECTION_NUMBER_OF) {
576         key = SELECTION_SELECTION;
577     }
579     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
580                                                        selection_names[key]);
581     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
583     return;
587 /**
588  * \brief  If selection changed or a different document activated, we must 
589  * recalculate any chosen areas
590  *
591  */
592 static void
593 sp_export_selection_changed ( Inkscape::Application *inkscape, 
594                               Inkscape::Selection *selection, 
595                               GtkObject *base )
597     selection_type current_key;
598     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
600     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
601             (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
602             was_empty) {
603         gtk_toggle_button_set_active
604             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
605               TRUE );
606     }
607     was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
609     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
611     if (inkscape &&
612             SP_IS_INKSCAPE (inkscape) &&
613             selection &&
614             SELECTION_CUSTOM != current_key) {
615         GtkToggleButton * button;
616         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
617         sp_export_area_toggled(button, base);
618     } // end of if()
620     return;
621 } // end of sp_export_selection_changed()
623 static void
624 sp_export_selection_modified ( Inkscape::Application *inkscape, 
625                                Inkscape::Selection *selection, 
626                                guint flags,
627                                GtkObject *base )
629     selection_type current_key;
630     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
632     switch (current_key) {
633         case SELECTION_DRAWING:
634             if ( SP_ACTIVE_DESKTOP ) {
635                 SPDocument *doc;
636                 NRRect bbox;
637                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
638                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
640                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
641                     sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
642                 }
643             }
644             break;
645         case SELECTION_SELECTION:
646             if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
647                 NRRect bbox;
648                 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
649                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
650             }
651             break;
652         default:
653             /* Do nothing for page or for custom */
654             break;
655     }
657     return;
660 /// Called when one of the selection buttons was toggled.
661 static void
662 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
664     if (gtk_object_get_data (base, "update"))
665         return;
667     selection_type key, old_key;
668     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
669     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
671     /* Ignore all "turned off" events unless we're the only active button */
672     if (!gtk_toggle_button_get_active (tb) ) {
674         /* Don't let the current selection be deactived - but rerun the
675            activate to allow the user to renew the values */
676         if (key == old_key) {
677             gtk_toggle_button_set_active ( tb, TRUE );
678         }
680         return;
681     }
683     /* Turn off the currently active button unless it's us */
684     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
686     if (old_key != key) {
687         gtk_toggle_button_set_active
688             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
689               FALSE );
690     }
692     if ( SP_ACTIVE_DESKTOP )
693     {
694         SPDocument *doc;
695         NRRect bbox;
696         doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
698         /* Notice how the switch is used to 'fall through' here to get
699            various backups.  If you modify this without noticing you'll
700            probabaly screw something up. */
701         switch (key) {
702             case SELECTION_SELECTION:
703                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
704                 {
705                     (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
706                     /* Only if there is a selection that we can set
707                        do we break, otherwise we fall through to the
708                        drawing */
709                     // std::cout << "Using selection: SELECTION" << std::endl;
710                     key = SELECTION_SELECTION;
711                     break;
712                 }
713             case SELECTION_DRAWING:
714                 /** \todo 
715                  * This returns wrong values if the document has a viewBox.
716                  */
717                 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
718                 
719                 /* If the drawing is valid, then we'll use it and break
720                    otherwise we drop through to the page settings */
721                 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { 
722                     // std::cout << "Using selection: DRAWING" << std::endl;
723                     key = SELECTION_DRAWING;
724                     break;
725                 }
726             case SELECTION_PAGE:
727                 bbox.x0 = 0.0;
728                 bbox.y0 = 0.0;
729                 bbox.x1 = sp_document_width (doc);
730                 bbox.y1 = sp_document_height (doc);
731                 // std::cout << "Using selection: PAGE" << std::endl;
732                 key = SELECTION_PAGE;
733                 break;
734             case SELECTION_CUSTOM:
735             default:
736                 break;
737         } // switch
738         
739         // remember area setting
740         prefs_set_string_attribute ( "dialogs.export.exportarea", 
741                                      "value", selection_names[key]);
743         if (key != SELECTION_CUSTOM) {
744             sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
745         }
746     
747     } // end of if ( SP_ACTIVE_DESKTOP )
750     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
751         GtkWidget * file_entry;
752         const gchar * filename = NULL;
753         float xdpi = 0.0, ydpi = 0.0;
755         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
757         switch (key) {
758             case SELECTION_PAGE:
759             case SELECTION_DRAWING: {
760                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
761                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
762                 const gchar * dpi_string;
764                 filename = repr->attribute("inkscape:export-filename");
766                 dpi_string = NULL;
767                 dpi_string = repr->attribute("inkscape:export-xdpi");
768                 if (dpi_string != NULL) {
769                     xdpi = atof(dpi_string);
770                 }
772                 dpi_string = NULL;
773                 dpi_string = repr->attribute("inkscape:export-ydpi");
774                 if (dpi_string != NULL) {
775                     ydpi = atof(dpi_string);
776                 }
778                 if (filename == NULL) {
779                     if (doc_export_name != NULL) {
780                         filename = g_strdup(doc_export_name);
781                     } else {
782                         filename = g_strdup("");
783                     }
784                 }
786                 break;
787             }
788             case SELECTION_SELECTION:
789                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790                     const GSList * reprlst;
791                     bool filename_search = TRUE;
792                     bool xdpi_search = TRUE;
793                     bool ydpi_search = TRUE;
795                     reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
796                     for(; reprlst != NULL &&
797                             filename_search &&
798                             xdpi_search &&
799                             ydpi_search;
800                             reprlst = reprlst->next) {
801                         const gchar * dpi_string;
802                         Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
804                         if (filename_search) {
805                             filename = repr->attribute("inkscape:export-filename");
806                             if (filename != NULL)
807                                 filename_search = FALSE;
808                         }
810                         if (xdpi_search) {
811                             dpi_string = NULL;
812                             dpi_string = repr->attribute("inkscape:export-xdpi");
813                             if (dpi_string != NULL) {
814                                 xdpi = atof(dpi_string);
815                                 xdpi_search = FALSE;
816                             }
817                         }
819                         if (ydpi_search) {
820                             dpi_string = NULL;
821                             dpi_string = repr->attribute("inkscape:export-ydpi");
822                             if (dpi_string != NULL) {
823                                 ydpi = atof(dpi_string);
824                                 ydpi_search = FALSE;
825                             }
826                         }
827                     }
829                     /* If we still don't have a filename -- let's build
830                        one that's nice */
831                     if (filename == NULL) {
832                         const gchar * id = NULL;
833                         reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
834                         for(; reprlst != NULL; reprlst = reprlst->next) {
835                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
836                             if (repr->attribute("id")) {
837                                 id = repr->attribute("id");
838                                 break;
839                             }
840                         }
841                         if (id == NULL) /* This should never happen */
842                             id = "bitmap";
844                         gchar * directory = NULL;
845                         const gchar * file_entry_text;
847                         file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
848                         if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
849                             // std::cout << "Directory from dialog" << std::endl;
850                             directory = g_dirname(file_entry_text);
851                         }
853                         if (directory == NULL) {
854                             /* Grab document directory */
855                             if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
856                                 // std::cout << "Directory from document" << std::endl;
857                                 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
858                             }
859                         }
861                         if (directory == NULL) {
862                             // std::cout << "Home Directory" << std::endl;
863                             directory = homedir_path(NULL);
864                         }
866                         gchar * id_ext = g_strconcat(id, ".png", NULL);
867                         filename = g_build_filename(directory, id_ext, NULL);
868                         g_free(directory);
869                         g_free(id_ext);
870                     }
871                 }
872                 break;
873             case SELECTION_CUSTOM:
874             default:
875                 break;
876         }
878         if (filename != NULL) {
879             g_free(original_name);
880             original_name = g_strdup(filename);
881             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
882         }
884         if (xdpi != 0.0) {
885             sp_export_value_set(base, "xdpi", xdpi);
886         }
888         /* These can't be seperate, and setting x sets y, so for
889            now setting this is disabled.  Hopefully it won't be in
890            the future */
891         if (FALSE && ydpi != 0.0) {
892             sp_export_value_set(base, "ydpi", ydpi);
893         }
894     }
896     return;
897 } // end of sp_export_area_toggled()
899 /// Called when dialog is deleted
900 static gint
901 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
903     g_object_set_data (base, "cancel", (gpointer) 1);
904     return TRUE;
905 } // end of sp_export_progress_delete()
907 /// Called when progress is cancelled
908 static void
909 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
911     g_object_set_data (base, "cancel", (gpointer) 1);
912 } // end of sp_export_progress_cancel()
914 /// Called for every progress iteration
915 static unsigned int
916 sp_export_progress_callback (float value, void *data)
918     GtkWidget *prg;
919     int evtcount;
921     if (g_object_get_data ((GObject *) data, "cancel"))
922         return FALSE;
924     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
925     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
927     evtcount = 0;
928     while ((evtcount < 16) && gdk_events_pending ()) {
929             gtk_main_iteration_do (FALSE);
930             evtcount += 1;
931     }
933     gtk_main_iteration_do (FALSE);
935     return TRUE;
937 } // end of sp_export_progress_callback()
939 /// Called when export button is clicked
940 static void
941 sp_export_export_clicked (GtkButton *button, GtkObject *base)
943     if (!SP_ACTIVE_DESKTOP) return;
945     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
946     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
948     float const x0 = sp_export_value_get_px(base, "x0");
949     float const y0 = sp_export_value_get_px(base, "y0");
950     float const x1 = sp_export_value_get_px(base, "x1");
951     float const y1 = sp_export_value_get_px(base, "y1");
952     float const xdpi = sp_export_value_get(base, "xdpi");
953     float const ydpi = sp_export_value_get(base, "ydpi");
954     int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
955     int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
957     if (filename == NULL || *filename == '\0') {
958         sp_ui_error_dialog(_("You have to enter a filename"));
959         return;
960     }
962     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
963         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
964         return;
965     }
967     gchar *dirname = g_dirname(filename);
968     if ( dirname == NULL
969          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
970     {
971         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
972         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
973                                        safeDir);
974         sp_ui_error_dialog(error);
975         g_free(safeDir);
976         g_free(error);
977         g_free(dirname);
978         return;
979     }
980     g_free(dirname);
982     SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
983     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
984     char *fn;
985     gchar *text;
987     dlg = gtk_dialog_new ();
988     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
989     prg = gtk_progress_bar_new ();
990     sp_transientize (dlg);
991     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
992     g_object_set_data ((GObject *) base, "progress", prg);
993     fn = g_path_get_basename (filename);
994     text = g_strdup_printf ( _("Exporting %s (%d x %d)"), 
995                              fn, width, height);
996     g_free (fn);
997     gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
998     g_free (text);
999     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, 
1000                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1001     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, 
1002                         prg, FALSE, FALSE, 4 );
1003     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), 
1004                                   GTK_STOCK_CANCEL, 
1005                                   GTK_RESPONSE_CANCEL );
1006                                   
1007     g_signal_connect ( (GObject *) dlg, "delete_event", 
1008                        (GCallback) sp_export_progress_delete, base);
1009     g_signal_connect ( (GObject *) btn, "clicked", 
1010                        (GCallback) sp_export_progress_cancel, base);
1011     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1012     gtk_widget_show_all (dlg);
1013     
1014     /* Do export */
1015     if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename, 
1016                              x0, y0, x1, y1, width, height, 
1017                              nv->pagecolor, 
1018                              sp_export_progress_callback, base)) {
1019         gchar * error;
1020         gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1021         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1022         sp_ui_error_dialog(error);
1023         g_free(safeFile);
1024         g_free(error);
1025     }
1027     /* Reset the filename so that it can be changed again by changing
1028        selections and all that */
1029     g_free(original_name);
1030     original_name = g_strdup(filename);
1031     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1033     gtk_widget_destroy (dlg);
1034     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1036     /* Setup the values in the document */
1037     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1038         case SELECTION_PAGE:
1039         case SELECTION_DRAWING: {
1040             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1041             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1042             bool modified = FALSE;
1043             const gchar * temp_string;
1045             bool saved = sp_document_get_undo_sensitive(doc);
1046             sp_document_set_undo_sensitive(doc, FALSE);
1048             temp_string = repr->attribute("inkscape:export-filename");
1049             if (temp_string == NULL || strcmp(temp_string, filename)) {
1050                 repr->setAttribute("inkscape:export-filename", filename);
1051                 modified = TRUE;
1052             }
1053             temp_string = repr->attribute("inkscape:export-xdpi");
1054             if (temp_string == NULL || xdpi != atof(temp_string)) {
1055                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1056                 modified = TRUE;
1057             }
1058             temp_string = repr->attribute("inkscape:export-ydpi");
1059             if (temp_string == NULL || xdpi != atof(temp_string)) {
1060                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1061                 modified = TRUE;
1062             }
1064             if (modified)
1065                 repr->setAttribute("sodipodi:modified", "TRUE");
1066             sp_document_set_undo_sensitive(doc, saved);
1067             break;
1068         }
1069         case SELECTION_SELECTION: {
1070             const GSList * reprlst;
1071             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1072             bool modified = FALSE;
1074             bool saved = sp_document_get_undo_sensitive(doc);
1075             sp_document_set_undo_sensitive(doc, FALSE);
1076             reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1078             for(; reprlst != NULL; reprlst = reprlst->next) {
1079                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1080                 const gchar * temp_string;
1082                 if (repr->attribute("id") == NULL ||
1083                         !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1084                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1085                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1086                     temp_string = repr->attribute("inkscape:export-filename");
1087                     if (temp_string == NULL || strcmp(temp_string, filename)) {
1088                         repr->setAttribute("inkscape:export-filename", filename);
1089                         modified = TRUE;
1090                     }
1091                 }
1092                 temp_string = repr->attribute("inkscape:export-xdpi");
1093                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1094                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1095                     modified = TRUE;
1096                 }
1097                 temp_string = repr->attribute("inkscape:export-ydpi");
1098                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1099                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1100                     modified = TRUE;
1101                 }
1102             }
1104             if (modified) {
1105                 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1106                 repr->setAttribute("sodipodi:modified", "TRUE");
1107             }
1109             sp_document_set_undo_sensitive(doc, saved);
1110             break;
1111         }
1112         default:
1113             break;
1114     }
1117     return;
1118 } // end of sp_export_export_clicked()
1120 /// Called when Browse button is clicked
1121 static void
1122 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1124     GtkWidget *fs, *fe;
1125     const gchar *filename;
1127     fs = gtk_file_selection_new (_("Select a filename for exporting"));
1128     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1130     sp_transientize (fs);
1132     gtk_window_set_modal(GTK_WINDOW (fs), true);
1134     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1136     if (*filename == '\0') {
1137         filename = homedir_path(NULL);
1138     }
1140     gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1142     g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1143                        "clicked",
1144                        G_CALLBACK (sp_export_browse_store),
1145                        (gpointer) fs );
1147     g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1148                                "clicked",
1149                                G_CALLBACK (gtk_widget_destroy),
1150                                (gpointer) fs );
1152     g_signal_connect_swapped ( GTK_OBJECT
1153                                    (GTK_FILE_SELECTION (fs)->cancel_button),
1154                                "clicked",
1155                                G_CALLBACK (gtk_widget_destroy),
1156                                (gpointer) fs );
1158     gtk_widget_show (fs);
1160     return;
1161 } // end of sp_export_browse_clicked()
1163 /// Called when OK clicked in file dialog
1164 static void
1165 sp_export_browse_store (GtkButton *button, gpointer userdata)
1167     GtkWidget *fs = (GtkWidget *)userdata, *fe;
1168     const gchar *file;
1170     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1172     file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1173     gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1174     gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1175     g_free(utf8file);
1177     g_object_set_data (G_OBJECT (dlg), "filename", fe);
1179     return;
1180 } // end of sp_export_browse_store()
1182 // TODO: Move this to nr-rect-fns.h.
1183 static bool
1184 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1185
1186     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1187     return (
1188         (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1189         (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1190         (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1191         (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1192         );
1195 /**
1196     \brief  This function is used to detect the current selection setting
1197             based on the values in the x0, y0, x1 and y0 fields.
1198     \param  base  The export dialog itself
1200     One of the most confusing parts of this function is why the array
1201     is built at the beginning.  What needs to happen here is that we
1202     should always check the current selection to see if it is the valid
1203     one.  While this is a performance improvement it is also a usability
1204     one during the cases where things like selections and drawings match
1205     size.  This way buttons change less 'randomly' (atleast in the eyes
1206     of the user).  To do this an array is built where the current selection
1207     type is placed first, and then the others in an order from smallest
1208     to largest (this can be configured by reshuffling \c test_order).
1210     All of the values in this function are rounded to two decimal places
1211     because that is what is shown to the user.  While everything is kept
1212     more accurate than that, the user can't control more acurrate than
1213     that, so for this to work for them - it needs to check on that level
1214     of accuracy.
1216     \todo finish writing this up
1217 */
1218 static void
1219 sp_export_detect_size(GtkObject * base) {
1220     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1221     selection_type this_test[SELECTION_NUMBER_OF + 1];
1222     selection_type key = SELECTION_NUMBER_OF;
1224     NR::Point x(sp_export_value_get_px (base, "x0"),
1225                 sp_export_value_get_px (base, "y0"));
1226     NR::Point y(sp_export_value_get_px (base, "x1"),
1227                 sp_export_value_get_px (base, "y1"));
1228     NR::Rect current_bbox(x, y);
1229     //std::cout << "Current " << current_bbox;
1231     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1232     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1233         this_test[i + 1] = test_order[i];
1234     }
1236     for (int i = 0;
1237             i < SELECTION_NUMBER_OF + 1 &&
1238                 key == SELECTION_NUMBER_OF &&
1239                 SP_ACTIVE_DESKTOP != NULL;
1240             i++) {
1241         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1242         switch (this_test[i]) {
1243             case SELECTION_SELECTION:
1244                 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1245                     NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1247                     //std::cout << "Selection " << bbox;
1248                     if (sp_export_bbox_equal(bbox,current_bbox)) {
1249                         key = SELECTION_SELECTION;
1250                     }
1251                 }
1252                 break;
1253             case SELECTION_DRAWING: {
1254                 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1256                 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1258                 // std::cout << "Drawing " << bbox2;
1259                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1260                     key = SELECTION_DRAWING;
1261                 }
1262                 break;
1263             }
1265             case SELECTION_PAGE: {
1266                 SPDocument *doc;
1268                 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1270                 NR::Point x(0.0, 0.0);
1271                 NR::Point y(sp_document_width(doc),
1272                             sp_document_height(doc));
1273                 NR::Rect bbox(x, y);
1275                 // std::cout << "Page " << bbox;
1276                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1277                     key = SELECTION_PAGE;
1278                 }
1280                 break;
1281            }
1282         default:
1283            break;
1284         }
1285     }
1286     // std::cout << std::endl;
1288     if (key == SELECTION_NUMBER_OF) {
1289         key = SELECTION_CUSTOM;
1290     }
1292     /* We're now using a custom size, not a fixed one */
1293     /* printf("Detecting state: %s\n", selection_names[key]); */
1294     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1295     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1296     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1297     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1299     return;
1300 } /* sp_export_detect_size */
1302 /// Called when area x0 value is changed
1303 static void
1304 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1306     float x0, x1, xdpi, width;
1308     if (gtk_object_get_data (base, "update"))
1309         return;
1311     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1312             (base, "units")))
1313     {
1314         return;
1315     }
1317     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1319     x0 = sp_export_value_get_px (base, "x0");
1320     x1 = sp_export_value_get_px (base, "x1");
1321     xdpi = sp_export_value_get (base, "xdpi");
1323     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1325     if (width < SP_EXPORT_MIN_SIZE) {
1326         const gchar *key;
1327         width = SP_EXPORT_MIN_SIZE;
1328         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1330         if (!strcmp (key, "x0")) {
1331             x1 = x0 + width * DPI_BASE / xdpi;
1332             sp_export_value_set_px (base, "x1", x1);
1333         } else {
1334             x0 = x1 - width * DPI_BASE / xdpi;
1335             sp_export_value_set_px (base, "x0", x0);
1336         }
1337     }
1339     sp_export_value_set_px (base, "width", x1 - x0);
1340     sp_export_value_set (base, "bmwidth", width);
1342     sp_export_detect_size(base);
1344     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1346     return;
1347 } // end of sp_export_area_x_value_changed()
1349 /// Called when area y0 value is changed.
1350 static void
1351 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1353     float y0, y1, ydpi, height;
1355     if (gtk_object_get_data (base, "update"))
1356         return;
1358     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1359            (base, "units")))
1360     {
1361         return;
1362     }
1364     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1366     y0 = sp_export_value_get_px (base, "y0");
1367     y1 = sp_export_value_get_px (base, "y1");
1368     ydpi = sp_export_value_get (base, "ydpi");
1370     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1372     if (height < SP_EXPORT_MIN_SIZE) {
1373         const gchar *key;
1374         height = SP_EXPORT_MIN_SIZE;
1375         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1376         if (!strcmp (key, "y0")) {
1377             y1 = y0 + height * DPI_BASE / ydpi;
1378             sp_export_value_set_px (base, "y1", y1);
1379         } else {
1380             y0 = y1 - height * DPI_BASE / ydpi;
1381             sp_export_value_set_px (base, "y0", y0);
1382         }
1383     }
1385     sp_export_value_set_px (base, "height", y1 - y0);
1386     sp_export_value_set (base, "bmheight", height);
1388     sp_export_detect_size(base);
1390     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1392     return;
1393 } // end of sp_export_area_y_value_changed()
1395 /// Called when x1-x0 or area width is changed
1396 static void
1397 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1399     float x0, x1, xdpi, width, bmwidth;
1401     if (gtk_object_get_data (base, "update"))
1402         return;
1404     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1405            (base, "units"))) {
1406         return;
1407     }
1409     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1411     x0 = sp_export_value_get_px (base, "x0");
1412     x1 = sp_export_value_get_px (base, "x1");
1413     xdpi = sp_export_value_get (base, "xdpi");
1414     width = sp_export_value_get_px (base, "width");
1415     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1417     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1419         bmwidth = SP_EXPORT_MIN_SIZE;
1420         width = bmwidth * DPI_BASE / xdpi;
1421         sp_export_value_set_px (base, "width", width);
1422     }
1424     sp_export_value_set_px (base, "x1", x0 + width);
1425     sp_export_value_set (base, "bmwidth", bmwidth);
1427     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1429     return;
1430 } // end of sp_export_area_width_value_changed()
1432 /// Called when y1-y0 or area height is changed.
1433 static void
1434 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1437     float y0, y1, ydpi, height, bmheight;
1439     if (gtk_object_get_data (base, "update"))
1440         return;
1442     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1443            (base, "units"))) {
1444         return;
1445     }
1447     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1449     y0 = sp_export_value_get_px (base, "y0");
1450     y1 = sp_export_value_get_px (base, "y1");
1451     ydpi = sp_export_value_get (base, "ydpi");
1452     height = sp_export_value_get_px (base, "height");
1453     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1455     if (bmheight < SP_EXPORT_MIN_SIZE) {
1456         bmheight = SP_EXPORT_MIN_SIZE;
1457         height = bmheight * DPI_BASE / ydpi;
1458         sp_export_value_set_px (base, "height", height);
1459     }
1461     sp_export_value_set_px (base, "y1", y0 + height);
1462     sp_export_value_set (base, "bmheight", bmheight);
1464     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1466     return;
1467 } // end of sp_export_area_height_value_changed()
1469 /**
1470     \brief  A function to set the ydpi
1471     \param  base  The export dialog
1473     This function grabs all of the y values and then figures out the
1474     new bitmap size based on the changing dpi value.  The dpi value is
1475     gotten from the xdpi setting as these can not currently be independent.
1476 */
1477 static void
1478 sp_export_set_image_y (GtkObject *base)
1480     float y0, y1, xdpi;
1482     y0 = sp_export_value_get_px (base, "y0");
1483     y1 = sp_export_value_get_px (base, "y1");
1484     xdpi = sp_export_value_get (base, "xdpi");
1486     sp_export_value_set (base, "ydpi", xdpi);
1487     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1489     return;
1490 } // end of sp_export_set_image_y()
1492 /// Called when pixel width is changed
1493 static void
1494 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1496     float x0, x1, bmwidth, xdpi;
1498     if (gtk_object_get_data (base, "update"))
1499         return;
1501     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1502            (base, "units"))) {
1503        return;
1504     }
1506     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1508     x0 = sp_export_value_get_px (base, "x0");
1509     x1 = sp_export_value_get_px (base, "x1");
1510     bmwidth = sp_export_value_get (base, "bmwidth");
1512     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1513         bmwidth = SP_EXPORT_MIN_SIZE;
1514         sp_export_value_set (base, "bmwidth", bmwidth);
1515     }
1517     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1518     sp_export_value_set (base, "xdpi", xdpi);
1520     sp_export_set_image_y (base);
1522     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1524     return;
1525 } // end of sp_export_bitmap_width_value_changed()
1527 /**
1528     \brief  A function to adjust the bitmap width when the xdpi value changes
1529     \param  adj  The adjustment that was changed
1530     \param  base The export dialog itself
1532     The first thing this function checks is to see if we are doing an
1533     update.  If we are, this function just returns because there is another
1534     instance of it that will handle everything for us.  If there is a
1535     units change, we also assume that everyone is being updated appropriately
1536     and there is nothing for us to do.
1538     If we're the highest level function, we set the update flag, and
1539     continue on our way.
1541     All of the values are grabbed using the \c sp_export_value_get functions
1542     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1543     xdpi value is saved in the preferences for the next time the dialog
1544     is opened.  (does the selection dpi need to be set here?)
1546     A check is done to to ensure that we aren't outputing an invalid width,
1547     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1548     changed to make it valid.
1550     After all of this the bitmap width is changed.
1552     We also change the ydpi.  This is a temporary hack as these can not
1553     currently be independent.  This is likely to change in the future.
1554 */
1555 void
1556 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1558     float x0, x1, xdpi, bmwidth;
1560     if (gtk_object_get_data (base, "update"))
1561         return;
1563     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1564            (base, "units"))) {
1565        return;
1566     }
1568     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1570     x0 = sp_export_value_get_px (base, "x0");
1571     x1 = sp_export_value_get_px (base, "x1");
1572     xdpi = sp_export_value_get (base, "xdpi");
1574     // remember xdpi setting
1575     prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1577     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1579     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1580         bmwidth = SP_EXPORT_MIN_SIZE;
1581         if (x1 != x0)
1582             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1583         else
1584             xdpi = DPI_BASE;
1585         sp_export_value_set (base, "xdpi", xdpi);
1586     }
1588     sp_export_value_set (base, "bmwidth", bmwidth);
1590     sp_export_set_image_y (base);
1592     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594     return;
1595 } // end of sp_export_xdpi_value_changed()
1598 /**
1599     \brief  A function to change the area that is used for the exported
1600             bitmap.
1601     \param  base  This is the export dialog
1602     \param  x0    Horizontal upper left hand corner of the picture in points
1603     \param  y0    Vertical upper left hand corner of the picture in points
1604     \param  x1    Horizontal lower right hand corner of the picture in points
1605     \param  y1    Vertical lower right hand corner of the picture in points
1607     This function just calls \c sp_export_value_set_px for each of the
1608     parameters that is passed in.  This allows for setting them all in
1609     one convient area.
1611     Update is set to suspend all of the other test running while all the
1612     values are being set up.  This allows for a performance increase, but
1613     it also means that the wrong type won't be detected with only some of
1614     the values set.  After all the values are set everyone is told that
1615     there has been an update.
1616 */
1617 static void
1618 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1620     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1621     sp_export_value_set_px (base, "x1", x1);
1622     sp_export_value_set_px (base, "y1", y1);
1623     sp_export_value_set_px (base, "x0", x0);
1624     sp_export_value_set_px (base, "y0", y0);
1625     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1627     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1628     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1630     return;
1633 /**
1634     \brief  Sets the value of an adjustment
1635     \param  base  The export dialog
1636     \param  key   Which adjustment to set
1637     \param  val   What value to set it to
1639     This function finds the adjustment using the data stored in the
1640     export dialog.  After finding the adjustment it then sets
1641     the value of it.
1642 */
1643 static void
1644 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1646     GtkAdjustment *adj;
1648     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1650     gtk_adjustment_set_value (adj, val);
1653 /**
1654     \brief  A function to set a value using the units points
1655     \param  base  The export dialog
1656     \param  key   Which value should be set
1657     \param  val   What the value should be in points
1659     This function first gets the adjustment for the key that is passed
1660     in.  It then figures out what units are currently being used in the
1661     dialog.  After doing all of that, it then converts the incoming
1662     value and sets the adjustment.
1663 */
1664 static void
1665 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1667     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1669     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1671     return;
1674 /**
1675     \brief  Get the value of an adjustment in the export dialog
1676     \param  base  The export dialog
1677     \param  key   Which adjustment is being looked for
1678     \return The value in the specified adjustment
1680     This function gets the adjustment from the data field in the export
1681     dialog.  It then grabs the value from the adjustment.
1682 */
1683 static float
1684 sp_export_value_get ( GtkObject *base, const gchar *key )
1686     GtkAdjustment *adj;
1688     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1690     return adj->value;
1691 } // end of sp_export_value_get()
1693 /**
1694     \brief  Grabs a value in the export dialog and converts the unit
1695             to points
1696     \param  base  The export dialog
1697     \param  key   Which value should be returned
1698     \return The value in the adjustment in points
1700     This function, at its most basic, is a call to \c sp_export_value_get
1701     to get the value of the adjustment.  It then finds the units that
1702     are being used by looking at the "units" attribute of the export
1703     dialog.  Using that it converts the returned value into points.
1704 */
1705 static float
1706 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1708     float value = sp_export_value_get(base, key);
1709     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1711     return sp_units_get_pixels (value, *unit);
1712 } // end of sp_export_value_get_px()
1714 /**
1715     \brief  This function is called when the filename is changed by
1716             anyone.  It resets the virgin bit.
1717     \param  object  Text entry box
1718     \param  data    The export dialog
1719     \return None
1721     This function gets called when the text area is modified.  It is
1722     looking for the case where the text area is modified from its
1723     original value.  In that case it sets the "filename-modified" bit
1724     to TRUE.  If the text dialog returns back to the original text, the
1725     bit gets reset.  This should stop simple mistakes.
1726 */
1727 static void
1728 sp_export_filename_modified (GtkObject * object, gpointer data)
1730     GtkWidget * text_entry = (GtkWidget *)object;
1731     GtkWidget * export_dialog = (GtkWidget *)data;
1733     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1734         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1735 //        printf("Modified: FALSE\n");
1736     } else {
1737         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1738 //        printf("Modified: TRUE\n");
1739     }
1741     return;
1742 } // end sp_export_filename_modified
1744 /*
1745   Local Variables:
1746   mode:c++
1747   c-file-style:"stroustrup"
1748   c-file-offsets:((innamespace . 0)(inline-open . 0))
1749   indent-tabs-mode:nil
1750   fill-column:99
1751   End:
1752 */
1753 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :