Code

Mnemonics in "Export bitmap", and "Rows and Columns" dialogs (Bug 170765)
[inkscape.git] / src / dialogs / export.cpp
1 /** @file
2  * @brief  PNG export dialog
3  */
4 /* Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   bulia byak <buliabyak@users.sf.net>
7  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 1999-2007 Authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
22 #include <png.h>
24 #include <gtk/gtk.h>
25 #include <gtkmm/box.h>
26 #include <gtkmm/buttonbox.h>
27 #include <gtkmm/label.h>
28 #include <gtkmm/widget.h>
29 #include <gtkmm/togglebutton.h>
30 #include <gtkmm/entry.h>
31 #include <gtkmm/image.h>
32 #include <gtkmm/stockid.h>
33 #include <gtkmm/stock.h>
34 #ifdef WITH_GNOME_VFS
35 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
36 #endif
38 #include <glibmm/i18n.h>
39 #include "helper/unit-menu.h"
40 #include "helper/units.h"
41 #include "unit-constants.h"
42 #include "helper/window.h"
43 #include "inkscape-private.h"
44 #include "document.h"
45 #include "desktop-handles.h"
46 #include "sp-item.h"
47 #include "selection.h"
48 #include "file.h"
49 #include "macros.h"
50 #include "sp-namedview.h"
51 #include "selection-chemistry.h"
53 #include "dialog-events.h"
54 #include "preferences.h"
55 #include "verbs.h"
56 #include "interface.h"
58 #include "extension/output.h"
59 #include "extension/db.h"
61 #include "io/sys.h"
63 #include "helper/png-write.h"
65 #ifdef WIN32
66 #include <windows.h>
67 #include <commdlg.h>
68 #include <gdk/gdkwin32.h>
69 #endif
71 using Inkscape::DocumentUndo;
73 #define SP_EXPORT_MIN_SIZE 1.0
75 #define DPI_BASE PX_PER_IN
77 #define EXPORT_COORD_PRECISION 3
79 #define MIN_ONSCREEN_DISTANCE 50
81 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
82 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
83 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
85 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj,
86                                                    GtkObject *base);
88 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj,
89                                                    GtkObject *base);
91 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj,
92                                                    GtkObject *base);
94 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj,
95                                                    GtkObject *base);
97 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
98                                                    GtkObject *base);
100 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
101                                                    GtkObject *base);
103 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj,
104                                                    GtkObject *base);
106 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
107                                           Inkscape::Selection *selection,
108                                           GtkObject *base);
109 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
110                                            Inkscape::Selection *selection,
111                                            guint flags,
112                                            GtkObject *base );
114 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
115 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
116 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
117 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
118 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
120 static void sp_export_filename_modified (GtkObject * object, gpointer data);
121 static inline void sp_export_find_default_selection(GtkWidget * dlg);
122 static void sp_export_detect_size(GtkObject * base);
124 static Glib::ustring const prefs_path = "/dialogs/export/";
126 // these all need to be reinitialized to their defaults during dialog_destroy
127 static GtkWidget *dlg = NULL;
128 static win_data wd;
129 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
130 static gchar * original_name = NULL;
131 static gchar * doc_export_name = NULL;
132 static bool was_empty = TRUE;
134 /** What type of button is being pressed */
135 enum selection_type {
136     SELECTION_PAGE = 0,  /**< Export the whole page */
137     SELECTION_DRAWING,   /**< Export everything drawn on the page */
138     SELECTION_SELECTION, /**< Export everything that is selected */
139     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
140     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
141 };
143 /** A list of strings that is used both in the preferences, and in the
144     data fields to describe the various values of \c selection_type. */
145 static const char * selection_names[SELECTION_NUMBER_OF] = {
146     "page", "drawing", "selection", "custom"};
148 /** The names on the buttons for the various selection types. */
149 static const char * selection_labels[SELECTION_NUMBER_OF] = {
150     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
152 static void
153 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
155     sp_signal_disconnect_by_data (INKSCAPE, dlg);
157     wd.win = dlg = NULL;
158     wd.stop = 0;
159     x = -1000; y = -1000; w = 0; h = 0;
160     g_free(original_name);
161     original_name = NULL;
162     g_free(doc_export_name);
163     doc_export_name = NULL;
164     was_empty = TRUE;
166     return;
167 } // end of sp_export_dialog_destroy()
169 /// Called when dialog is closed or inkscape is shut down.
170 static bool
171 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
174     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
175     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
177     if (x<0) x=0;
178     if (y<0) y=0;
180     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
181     prefs->setInt(prefs_path + "x", x);
182     prefs->setInt(prefs_path + "y", y);
183     prefs->setInt(prefs_path + "w", w);
184     prefs->setInt(prefs_path + "h", h);
186     return FALSE; // which means, go ahead and destroy it
188 } // end of sp_export_dialog_delete()
190 /**
191     \brief  Creates a new spin button for the export dialog
192     \param  key  The name of the spin button
193     \param  val  A default value for the spin button
194     \param  min  Minimum value for the spin button
195     \param  max  Maximum value for the spin button
196     \param  step The step size for the spin button
197     \param  page Size of the page increment
198     \param  us   Unit selector that effects this spin button
199     \param  t    Table to put the spin button in
200     \param  x    X location in the table \c t to start with
201     \param  y    Y location in the table \c t to start with
202     \param  ll   Text to put on the left side of the spin button (optional)
203     \param  lr   Text to put on the right side of the spin button (optional)
204     \param  digits  Number of digits to display after the decimal
205     \param  sensitive  Whether the spin button is sensitive or not
206     \param  cb   Callback for when this spin button is changed (optional)
207     \param  dlg  Export dialog the spin button is being placed in
209 */
210 static void
211 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
212                            float step, float page, GtkWidget *us,
213                            GtkWidget *t, int x, int y,
214                            const gchar *ll, const gchar *lr,
215                            int digits, unsigned int sensitive,
216                            GCallback cb, GtkWidget *dlg )
218     GtkObject *adj = gtk_adjustment_new( val, min, max, step, page, 0 );
219     gtk_object_set_data( adj, "key", const_cast<gchar *>(key) );
220     gtk_object_set_data( GTK_OBJECT (dlg), (const gchar *)key, adj );
222     if (us) {
223         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
224                                           GTK_ADJUSTMENT (adj) );
225     }
227     int pos = 0;
229     GtkWidget *l = NULL;
231     if (ll) {
233         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
234         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
235         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
236                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
237         gtk_widget_set_sensitive (l, sensitive);
238         pos += 1;
240     }
242     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, digits);
243     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
244                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
245     gtk_widget_set_size_request (sb, 80, -1);
246     gtk_widget_set_sensitive (sb, sensitive);
247     pos += 1;
249     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
251     if (lr) {
253         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
254         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
255         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
256                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
257         gtk_widget_set_sensitive (l, sensitive);
258         pos += 1;
260         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
261     }
263     if (cb)
264         gtk_signal_connect (adj, "value_changed", cb, dlg);
266     return;
267 } // end of sp_export_spinbutton_new()
270 static Gtk::VBox *
271 sp_export_dialog_area_box (GtkWidget * dlg)
273     Gtk::VBox* vb = new Gtk::VBox(false, 3);
275     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
276     lbl->set_use_markup(true);
277     vb->pack_start(*lbl);
279     /* Units box */
280     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
281     /* gets added to the vbox later, but the unit selector is needed
282        earlier than that */
284     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
285     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
286     if (desktop)
287         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
288     unitbox->pack_end(*us, false, false, 0);
289     Gtk::Label* l = new Gtk::Label(_("Units:"));
290     unitbox->pack_end(*l, false, false, 3);
291     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
293     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
295     Gtk::ToggleButton* b;
296     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
297         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
298         b->set_data("key", GINT_TO_POINTER(i));
299         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
300         togglebox->pack_start(*b, false, true, 0);
301         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
302                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
303     }
305     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
306                        G_CALLBACK (sp_export_selection_changed), dlg );
307     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
308                        G_CALLBACK (sp_export_selection_modified), dlg );
309     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
310                        G_CALLBACK (sp_export_selection_changed), dlg );
312     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
313     t->set_row_spacings (4);
314     t->set_col_spacings (4);
316     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
317                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
318                                G_CALLBACK ( sp_export_area_x_value_changed),
319                                dlg );
321     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
322                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
323                                G_CALLBACK (sp_export_area_x_value_changed),
324                                dlg );
326     sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
327                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
328                                G_CALLBACK
329                                    (sp_export_area_width_value_changed),
330                                dlg );
332     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
333                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
334                                G_CALLBACK (sp_export_area_y_value_changed),
335                                dlg );
337     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
338                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
339                                G_CALLBACK (sp_export_area_y_value_changed),
340                                dlg );
342     sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
343                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
344                                G_CALLBACK (sp_export_area_height_value_changed),
345                                dlg );
347     vb->pack_start(*togglebox, false, false, 3);
348     vb->pack_start(*t, false, false, 0);
349     vb->pack_start(*unitbox, false, false, 0);
351     return vb;
352 } // end of sp_export_dialog_area_box
355 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
357     if (id == NULL) /* This should never happen */
358         id = "bitmap";
360     gchar * directory = NULL;
362     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
363         // std::cout << "Directory from dialog" << std::endl;
364         directory = g_dirname(file_entry_text);
365     }
367     if (directory == NULL) {
368         /* Grab document directory */
369         if ( SP_ACTIVE_DOCUMENT->getURI() ) {
370             // std::cout << "Directory from document" << std::endl;
371             directory = g_dirname( SP_ACTIVE_DOCUMENT->getURI() );
372         }
373     }
375     if (directory == NULL) {
376         // std::cout << "Home Directory" << std::endl;
377         directory = homedir_path(NULL);
378     }
380     gchar * id_ext = g_strconcat(id, ".png", NULL);
381     gchar *filename = g_build_filename(directory, id_ext, NULL);
382     g_free(directory);
383     g_free(id_ext);
384     return filename;
387 static void
388 batch_export_clicked (GtkWidget *widget, GtkObject *base)
390     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
391     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
392         vb_singleexport->set_sensitive(false);
393     } else {
394         vb_singleexport->set_sensitive(true);
395     }
398 void
399 sp_export_dialog (void)
401     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
402     if (!dlg) {
404         gchar title[500];
405         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
407         dlg = sp_window_new (title, TRUE);
409         if (x == -1000 || y == -1000) {
410             x = prefs->getInt(prefs_path + "x", 0);
411             y = prefs->getInt(prefs_path + "y", 0);
412         }
414         if (w ==0 || h == 0) {
415             w = prefs->getInt(prefs_path + "w", 0);
416             h = prefs->getInt(prefs_path + "h", 0);
417         }
419 //        if (x<0) x=0;
420 //        if (y<0) y=0;
422         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
423         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
424             gtk_window_move ((GtkWindow *) dlg, x, y);
425         else
426             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
427         sp_transientize (dlg);
428         wd.win = dlg;
429         wd.stop = 0;
431         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
432                              G_CALLBACK (sp_transientize_callback), &wd);
434         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
435                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
437         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
438                              G_CALLBACK (sp_export_dialog_destroy), dlg);
440         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
441                              G_CALLBACK (sp_export_dialog_delete), dlg);
443         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
444                              G_CALLBACK (sp_export_dialog_delete), dlg);
446         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
447                              G_CALLBACK (sp_dialog_hide), dlg);
449         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
450                              G_CALLBACK (sp_dialog_unhide), dlg);
452         GtkTooltips *tt = gtk_tooltips_new();
454         Gtk::VBox *vb = new Gtk::VBox(false, 3);
455         vb->set_border_width(3);
456         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
458         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
459         vb_singleexport->set_border_width(0);
460         vb->pack_start(*vb_singleexport);
461         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
463         /* Export area frame */
464         {
465             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
466             area_box->set_border_width(3);
467             vb_singleexport->pack_start(*area_box, false, false, 0);
468         }
470         /* Bitmap size frame */
471         {
472             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
473             size_box->set_border_width(3);
475             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
476             lbl->set_use_markup(true);
477             size_box->pack_start(*lbl, false, false, 0);
478             const int rows = 2;
479             const int cols = 5;
480             const bool homogeneous = false;
481             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
482             t->set_row_spacings (4);
483             t->set_col_spacings (4);
484             size_box->pack_start(*t);
486             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
487                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
488                                        _("_Width:"), _("pixels at"), 0, 1,
489                                        G_CALLBACK
490                                        (sp_export_bitmap_width_value_changed),
491                                        dlg );
493             sp_export_spinbutton_new ( "xdpi",
494                                        prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
495                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
496                                        NULL, _("dp_i"), 2, 1,
497                                        G_CALLBACK (sp_export_xdpi_value_changed),
498                                        dlg );
500             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
501                                        NULL, GTK_WIDGET(t->gobj()), 0, 1,
502                                        _("_Height:"), _("pixels at"), 0, 1,
503                                        G_CALLBACK
504                                        (sp_export_bitmap_height_value_changed),
505                                        dlg );
507             /** \todo
508              * Needs fixing: there's no way to set ydpi currently, so we use
509              *       the defaultxdpi value here, too...
510              */
511             sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
512                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
513                                        NULL, _("dpi"), 2, 0, NULL, dlg );
515             vb_singleexport->pack_start(*size_box);
516         }
518         /* File entry */
519         {
520             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
521             file_box->set_border_width(3);
523             // true = has mnemonic
524             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
525             flabel->set_use_markup(true);
526             file_box->pack_start(*flabel, false, false, 0);
528             Gtk::Entry *fe = new Gtk::Entry();
530             /*
531              * set the default filename to be that of the current path + document
532              * with .png extension
533              *
534              * One thing to notice here is that this filename may get
535              * overwritten, but it won't happen here.  The filename gets
536              * written into the text field, but then the button to select
537              * the area gets set.  In that code the filename can be changed
538              * if there are some with presidence in the document.  So, while
539              * this code sets the name first, it may not be the one users
540              * really see.
541              */
542             if ( SP_ACTIVE_DOCUMENT && SP_ACTIVE_DOCUMENT->getURI() )
543             {
544                 gchar *name;
545                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
546                 const gchar *uri = doc->getURI();
547                 const gchar *text_extension = get_file_save_extension (Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS).c_str();
548                 Inkscape::Extension::Output * oextension = NULL;
550                 if (text_extension != NULL) {
551                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
552                 }
554                 if (oextension != NULL) {
555                     gchar * old_extension = oextension->get_extension();
556                     if (g_str_has_suffix(uri, old_extension)) {
557                         gchar * uri_copy;
558                         gchar * extension_point;
559                         gchar * final_name;
561                         uri_copy = g_strdup(uri);
562                         extension_point = g_strrstr(uri_copy, old_extension);
563                         extension_point[0] = '\0';
565                         final_name = g_strconcat(uri_copy, ".png", NULL);
566                         fe->set_text(final_name);
568                         g_free(final_name);
569                         g_free(uri_copy);
570                     }
571                 } else {
572                     name = g_strconcat(uri, ".png", NULL);
573                     fe->set_text(name);
574                     g_free(name);
575                 }
577                 doc_export_name = g_strdup(fe->get_text().c_str());
578             }
579             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
580                                G_CALLBACK (sp_export_filename_modified), dlg);
582             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
584             {
585                 // true = has mnemonic
586                 Gtk::Button *b = new Gtk::Button();
588                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
589                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
590                         Gtk::ICON_SIZE_BUTTON);
591                 pixlabel->pack_start(*im);
593                 Gtk::Label *l = new Gtk::Label();
594                 l->set_markup_with_mnemonic(_("_Browse..."));
595                 pixlabel->pack_start(*l);
597                 b->add(*pixlabel);
599                 hb->pack_end (*b, false, false, 4);
600                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
601                                    G_CALLBACK (sp_export_browse_clicked), NULL );
602             }
604             hb->pack_start (*fe, true, true, 0);
605             file_box->add(*hb);
606             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
607             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
608             original_name = g_strdup(fe->get_text().c_str());
609             // pressing enter in the filename field is the same as clicking export:
610             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
611                                G_CALLBACK (sp_export_export_clicked), dlg );
612             // focus is in the filename initially:
613             fe->grab_focus();
615             // mnemonic in frame label moves focus to filename:
616             flabel->set_mnemonic_widget(*fe);
618             vb_singleexport->pack_start(*file_box);
619         }
621         {
622             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
623             GtkWidget *be = gtk_check_button_new_with_mnemonic(_("B_atch export all selected objects"));
624             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
625             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
626             batch_box->pack_start(*Glib::wrap(be), false, false);
627             gtk_tooltips_set_tip(tt, be, _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"), NULL);
628             batch_box->show_all();
629             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
630             vb->pack_start(*batch_box);
631         }
633         {
634             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
635             GtkWidget *he = gtk_check_button_new_with_mnemonic(_("Hide a_ll except selected"));
636             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
637             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
638             hide_box->pack_start(*Glib::wrap(he), false, false);
639             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
640             hide_box->show_all();
641             vb->pack_start(*hide_box);
642         }
644         /* Buttons */
645         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
646         bb->set_border_width(3);
648         {
649             Gtk::Button *b = new Gtk::Button();
650             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
651             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
652                     Gtk::ICON_SIZE_BUTTON);
653             image_label->pack_start(*im);
655             Gtk::Label *l = new Gtk::Label();
656             l->set_markup_with_mnemonic(_("_Export"));
657             image_label->pack_start(*l);
659             b->add(*image_label);
660             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
661             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
662                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
663             bb->pack_end(*b, false, false, 0);
664         }
666         vb->pack_end(*bb, false, false, 0);
667         vb->show_all();
669     } // end of if (!dlg)
671     sp_export_find_default_selection(dlg);
673     gtk_window_present ((GtkWindow *) dlg);
675     return;
676 } // end of sp_export_dialog()
678 static void
679 sp_export_update_checkbuttons (GtkObject *base)
681     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
682     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
683     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
684     if (num >= 2) {
685         gtk_widget_set_sensitive (be, true);
686         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("B_atch export %d selected object","B_atch export %d selected objects",num), num));
687     } else {
688         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
689         gtk_widget_set_sensitive (be, FALSE);
690     }
691     if (num > 0) {
692         gtk_widget_set_sensitive (he, true);
693     } else {
694         gtk_widget_set_sensitive (he, false);
695     }
698 static inline void
699 sp_export_find_default_selection(GtkWidget * dlg)
701     selection_type key = SELECTION_NUMBER_OF;
703     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
704         key = SELECTION_SELECTION;
705     }
707     /* Try using the preferences */
708     if (key == SELECTION_NUMBER_OF) {
709         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
710         int i = SELECTION_NUMBER_OF;
712         Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
714         if (!what.empty()) {
715             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
716                 if (what == selection_names[i]) {
717                     break;
718                 }
719             }
720         }
722         key = (selection_type)i;
723     }
725     if (key == SELECTION_NUMBER_OF) {
726         key = SELECTION_SELECTION;
727     }
729     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
730                                                        selection_names[key]);
731     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
733     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
737 /**
738  * \brief  If selection changed or a different document activated, we must
739  * recalculate any chosen areas
740  *
741  */
742 static void
743 sp_export_selection_changed ( Inkscape::Application *inkscape,
744                               Inkscape::Selection *selection,
745                               GtkObject *base )
747     selection_type current_key;
748     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
750     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
751             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
752             was_empty) {
753         gtk_toggle_button_set_active
754             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
755               TRUE );
756     }
757     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
759     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
761     if (inkscape &&
762             SP_IS_INKSCAPE (inkscape) &&
763             selection &&
764             SELECTION_CUSTOM != current_key) {
765         GtkToggleButton * button;
766         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
767         sp_export_area_toggled(button, base);
768     }
770     sp_export_update_checkbuttons (base);
773 static void
774 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
775                                Inkscape::Selection */*selection*/,
776                                guint /*flags*/,
777                                GtkObject *base )
779     selection_type current_key;
780     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
782     switch (current_key) {
783         case SELECTION_DRAWING:
784             if ( SP_ACTIVE_DESKTOP ) {
785                 SPDocument *doc;
786                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
787                 Geom::OptRect bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
788                 if (bbox) {
789                     sp_export_set_area (base, bbox->min()[Geom::X],
790                                               bbox->min()[Geom::Y],
791                                               bbox->max()[Geom::X],
792                                               bbox->max()[Geom::Y]);
793                 }
794             }
795             break;
796         case SELECTION_SELECTION:
797             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
798                 NRRect bbox;
799                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox, SPItem::RENDERING_BBOX);
800                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
801             }
802             break;
803         default:
804             /* Do nothing for page or for custom */
805             break;
806     }
808     return;
811 /// Called when one of the selection buttons was toggled.
812 static void
813 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
815     if (gtk_object_get_data (base, "update"))
816         return;
818     selection_type key, old_key;
819     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
820     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
822     /* Ignore all "turned off" events unless we're the only active button */
823     if (!gtk_toggle_button_get_active (tb) ) {
825         /* Don't let the current selection be deactived - but rerun the
826            activate to allow the user to renew the values */
827         if (key == old_key) {
828             gtk_toggle_button_set_active ( tb, TRUE );
829         }
831         return;
832     }
834     /* Turn off the currently active button unless it's us */
835     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
837     if (old_key != key) {
838         gtk_toggle_button_set_active
839             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
840               FALSE );
841     }
843     if ( SP_ACTIVE_DESKTOP )
844     {
845         SPDocument *doc;
846         Geom::OptRect bbox;
847         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
849         /* Notice how the switch is used to 'fall through' here to get
850            various backups.  If you modify this without noticing you'll
851            probabaly screw something up. */
852         switch (key) {
853             case SELECTION_SELECTION:
854                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
855                 {
856                     bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds(SPItem::RENDERING_BBOX);
857                     /* Only if there is a selection that we can set
858                        do we break, otherwise we fall through to the
859                        drawing */
860                     // std::cout << "Using selection: SELECTION" << std::endl;
861                     key = SELECTION_SELECTION;
862                     break;
863                 }
864             case SELECTION_DRAWING:
865                 /** \todo
866                  * This returns wrong values if the document has a viewBox.
867                  */
868                 bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
869                 /* If the drawing is valid, then we'll use it and break
870                    otherwise we drop through to the page settings */
871                 if (bbox) {
872                     // std::cout << "Using selection: DRAWING" << std::endl;
873                     key = SELECTION_DRAWING;
874                     break;
875                 }
876             case SELECTION_PAGE:
877                 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
878                                   Geom::Point(doc->getWidth(), doc->getHeight()));
880                 // std::cout << "Using selection: PAGE" << std::endl;
881                 key = SELECTION_PAGE;
882                 break;
883             case SELECTION_CUSTOM:
884             default:
885                 break;
886         } // switch
888         // remember area setting
889         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
890         prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
892         if ( key != SELECTION_CUSTOM && bbox ) {
893             sp_export_set_area (base, bbox->min()[Geom::X],
894                                       bbox->min()[Geom::Y],
895                                       bbox->max()[Geom::X],
896                                       bbox->max()[Geom::Y]);
897         }
899     } // end of if ( SP_ACTIVE_DESKTOP )
902     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
903         GtkWidget * file_entry;
904         const gchar * filename = NULL;
905         float xdpi = 0.0, ydpi = 0.0;
907         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
909         switch (key) {
910             case SELECTION_PAGE:
911             case SELECTION_DRAWING: {
912                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
913                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
915                 if (filename == NULL) {
916                     if (doc_export_name != NULL) {
917                         filename = g_strdup(doc_export_name);
918                     } else {
919                         filename = g_strdup("");
920                     }
921                 }
922                 break;
923             }
924             case SELECTION_SELECTION:
925                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
927                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
929                     /* If we still don't have a filename -- let's build
930                        one that's nice */
931                     if (filename == NULL) {
932                         const gchar * id = NULL;
933                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
934                         for(; reprlst != NULL; reprlst = reprlst->next) {
935                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
936                             if (repr->attribute("id")) {
937                                 id = repr->attribute("id");
938                                 break;
939                             }
940                         }
942                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
943                     }
944                 }
945                 break;
946             case SELECTION_CUSTOM:
947             default:
948                 break;
949         }
951         if (filename != NULL) {
952             g_free(original_name);
953             original_name = g_strdup(filename);
954             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
955         }
957         if (xdpi != 0.0) {
958             sp_export_value_set(base, "xdpi", xdpi);
959         }
961         /* These can't be separate, and setting x sets y, so for
962            now setting this is disabled.  Hopefully it won't be in
963            the future */
964         if (FALSE && ydpi != 0.0) {
965             sp_export_value_set(base, "ydpi", ydpi);
966         }
967     }
969     return;
970 } // end of sp_export_area_toggled()
972 /// Called when dialog is deleted
973 static gint
974 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
976     g_object_set_data (base, "cancel", (gpointer) 1);
977     return TRUE;
978 } // end of sp_export_progress_delete()
980 /// Called when progress is cancelled
981 static void
982 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
984     g_object_set_data (base, "cancel", (gpointer) 1);
985 } // end of sp_export_progress_cancel()
987 /// Called for every progress iteration
988 static unsigned int
989 sp_export_progress_callback (float value, void *data)
991     GtkWidget *prg;
992     int evtcount;
994     if (g_object_get_data ((GObject *) data, "cancel"))
995         return FALSE;
997     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
998     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
1000     evtcount = 0;
1001     while ((evtcount < 16) && gdk_events_pending ()) {
1002             gtk_main_iteration_do (FALSE);
1003             evtcount += 1;
1004     }
1006     gtk_main_iteration_do (FALSE);
1008     return TRUE;
1010 } // end of sp_export_progress_callback()
1012 GtkWidget *
1013 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1014     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1016     dlg = gtk_dialog_new ();
1017     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1018     prg = gtk_progress_bar_new ();
1019     sp_transientize (dlg);
1020     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1021     g_object_set_data ((GObject *) base, "progress", prg);
1023     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1025     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1026                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1027     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1028                         prg, FALSE, FALSE, 4 );
1029     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1030                                   GTK_STOCK_CANCEL,
1031                                   GTK_RESPONSE_CANCEL );
1033     g_signal_connect ( (GObject *) dlg, "delete_event",
1034                        (GCallback) sp_export_progress_delete, base);
1035     g_signal_connect ( (GObject *) btn, "clicked",
1036                        (GCallback) sp_export_progress_cancel, base);
1037     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1038     gtk_widget_show_all (dlg);
1040     return dlg;
1043 // FIXME: Some lib function should be available to do this ...
1044 static gchar *
1045 filename_add_extension (const gchar *filename, const gchar *extension)
1047   const gchar *dot;
1049   dot = strrchr (filename, '.');
1050   if ( !dot )
1051     return g_strconcat (filename, ".", extension, NULL);
1052   {
1053     if (dot[1] == '\0')
1054       return g_strconcat (filename, extension, NULL);
1055     else
1056     {
1057       if (g_strcasecmp (dot + 1, extension) == 0)
1058         return g_strdup (filename);
1059       else
1060       {
1061         return g_strconcat (filename, ".", extension, NULL);
1062       }
1063     }
1064   }
1067 gchar *absolutize_path_from_document_location (SPDocument *doc, const gchar *filename)
1069     gchar *path = 0;
1070     //Make relative paths go from the document location, if possible:
1071     if (!g_path_is_absolute(filename) && doc->getURI()) {
1072         gchar *dirname = g_path_get_dirname(doc->getURI());
1073         if (dirname) {
1074             path = g_build_filename(dirname, filename, NULL);
1075             g_free(dirname);
1076         }
1077     }
1078     if (!path) {
1079         path = g_strdup(filename);
1080     }
1081     return path;
1084 /// Called when export button is clicked
1085 static void
1086 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1088     if (!SP_ACTIVE_DESKTOP) return;
1090     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1091     SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1093     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1094     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1095     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1096     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1097         // Batch export of selected objects
1099         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1100         gint n = 0;
1102         if (num < 1)
1103             return;
1105         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1106         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1107         g_free (progress_text);
1109         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1110              i != NULL;
1111              i = i->next) {
1112             SPItem *item = reinterpret_cast<SPItem *>(i->data);
1114             // retrieve export filename hint
1115             const gchar *filename = item->getRepr()->attribute("inkscape:export-filename");
1116             gchar *path = 0;
1117             if (!filename) {
1118                 path = create_filepath_from_id(item->getId(), NULL);
1119             } else {
1120                 path = absolutize_path_from_document_location(doc, filename);
1121             }
1123             // retrieve export dpi hints
1124             const gchar *dpi_hint = item->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1125             gdouble dpi = 0.0;
1126             if (dpi_hint) {
1127                 dpi = atof(dpi_hint);
1128             }
1129             if (dpi == 0.0) {
1130                 dpi = DPI_BASE;
1131             }
1133             Geom::OptRect area;
1134             item->invoke_bbox( area, item->i2d_affine(), TRUE );
1135             if (area) {
1136                 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1137                 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1139                 if (width > 1 && height > 1) {
1140                     /* Do export */
1141                     if (!sp_export_png_file (doc, path,
1142                                              *area, width, height, dpi, dpi,
1143                                              nv->pagecolor,
1144                                              NULL, NULL, TRUE,  // overwrite without asking
1145                                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1146                             )) {
1147                         gchar * error;
1148                         gchar * safeFile = Inkscape::IO::sanitizeString(path);
1149                         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1150                         sp_ui_error_dialog(error);
1151                         g_free(safeFile);
1152                         g_free(error);
1153                     }
1154                 }
1155             }
1156             n++;
1157             g_free(path);
1158             sp_export_progress_callback((float)n/num, base);
1159         }
1161         gtk_widget_destroy (prog_dlg);
1162         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1164     } else {
1166     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1167     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1169     float const x0 = sp_export_value_get_px(base, "x0");
1170     float const y0 = sp_export_value_get_px(base, "y0");
1171     float const x1 = sp_export_value_get_px(base, "x1");
1172     float const y1 = sp_export_value_get_px(base, "y1");
1173     float const xdpi = sp_export_value_get(base, "xdpi");
1174     float const ydpi = sp_export_value_get(base, "ydpi");
1175     unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1176     unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1178     if (filename == NULL || *filename == '\0') {
1179         sp_ui_error_dialog(_("You have to enter a filename"));
1180         return;
1181     }
1183     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1184         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1185         return;
1186     }
1188     // make sure that .png is the extension of the file:
1189     gchar * filename_ext = filename_add_extension(filename, "png");
1190     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1192     gchar *path = absolutize_path_from_document_location(doc, filename_ext);
1194     gchar *dirname = g_path_get_dirname(path);
1195     if ( dirname == NULL
1196          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1197     {
1198         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1199         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1200                                        safeDir);
1201         sp_ui_error_dialog(error);
1202         g_free(safeDir);
1203         g_free(error);
1204         g_free(dirname);
1205         g_free(path);
1206         return;
1207     }
1208     g_free(dirname);
1210     gchar *fn = g_path_get_basename (path);
1211     gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1212     g_free (fn);
1214     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1215     g_free (progress_text);
1217     /* Do export */
1218     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), path,
1219                              Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1220                              nv->pagecolor,
1221                              sp_export_progress_callback, base, FALSE,
1222                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1223             )) {
1224         gchar * error;
1225         gchar * safeFile = Inkscape::IO::sanitizeString(path);
1226         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1227         sp_ui_error_dialog(error);
1228         g_free(safeFile);
1229         g_free(error);
1230     }
1232     /* Reset the filename so that it can be changed again by changing
1233        selections and all that */
1234     g_free(original_name);
1235     original_name = g_strdup(filename_ext);
1236     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1238     gtk_widget_destroy (prog_dlg);
1239     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1241     /* Setup the values in the document */
1242     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1243         case SELECTION_PAGE:
1244         case SELECTION_DRAWING: {
1245             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1246             Inkscape::XML::Node * repr = doc->getReprRoot();
1247             bool modified = false;
1249             bool saved = DocumentUndo::getUndoSensitive(doc);
1250             DocumentUndo::setUndoSensitive(doc, false);
1252             gchar const *temp_string = repr->attribute("inkscape:export-filename");
1253             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1254                 repr->setAttribute("inkscape:export-filename", filename_ext);
1255                 modified = true;
1256             }
1257             temp_string = repr->attribute("inkscape:export-xdpi");
1258             if (temp_string == NULL || xdpi != atof(temp_string)) {
1259                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1260                 modified = true;
1261             }
1262             temp_string = repr->attribute("inkscape:export-ydpi");
1263             if (temp_string == NULL || xdpi != atof(temp_string)) {
1264                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1265                 modified = true;
1266             }
1267             DocumentUndo::setUndoSensitive(doc, saved);
1269             if (modified) {
1270                 doc->setModifiedSinceSave();
1271             }
1272             break;
1273         }
1274         case SELECTION_SELECTION: {
1275             const GSList * reprlst;
1276             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1277             bool modified = false;
1279             bool saved = DocumentUndo::getUndoSensitive(doc);
1280             DocumentUndo::setUndoSensitive(doc, false);
1281             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1283             for(; reprlst != NULL; reprlst = reprlst->next) {
1284                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1285                 const gchar * temp_string;
1287                 if (repr->attribute("id") == NULL ||
1288                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1289                           ( !SP_ACTIVE_DOCUMENT->getURI() ||
1290                             strcmp(g_dirname(filename), g_dirname(SP_ACTIVE_DOCUMENT->getURI())) == 0))) {
1291                     temp_string = repr->attribute("inkscape:export-filename");
1292                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1293                         repr->setAttribute("inkscape:export-filename", filename_ext);
1294                         modified = true;
1295                     }
1296                 }
1297                 temp_string = repr->attribute("inkscape:export-xdpi");
1298                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1299                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1300                     modified = true;
1301                 }
1302                 temp_string = repr->attribute("inkscape:export-ydpi");
1303                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1304                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1305                     modified = true;
1306                 }
1307             }
1308             DocumentUndo::setUndoSensitive(doc, saved);
1310             if (modified) {
1311                 doc->setModifiedSinceSave();
1312             }
1313             break;
1314         }
1315         default:
1316             break;
1317     }
1319     g_free (filename_ext);
1320     g_free (path);
1322     }
1324 } // end of sp_export_export_clicked()
1326 /// Called when Browse button is clicked
1327 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1328 static void
1329 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1331     GtkWidget *fs, *fe;
1332     const gchar *filename;
1334     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1335                                       (GtkWindow*)dlg,
1336                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1337                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1338                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1339                                       NULL );
1341 #ifdef WITH_GNOME_VFS
1342     if (gnome_vfs_initialized()) {
1343         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1344     }
1345 #endif
1347     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1349     sp_transientize (fs);
1351     gtk_window_set_modal(GTK_WINDOW (fs), true);
1353     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1355     if (*filename == '\0') {
1356         filename = create_filepath_from_id(NULL, NULL);
1357     }
1359     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1361 #ifdef WIN32
1362     // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1363     OPENFILENAMEW opf;
1364     WCHAR filter_string[20];
1365     wcsncpy(filter_string, L"PNG#*.png##", 11);
1366     filter_string[3] = L'\0';
1367     filter_string[9] = L'\0';
1368     filter_string[10] = L'\0';
1369     WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1370     WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1371     // Copy the selected file name, converting from UTF-8 to UTF-16
1372     WCHAR _filename[_MAX_PATH + 1];
1373     memset(_filename, 0, sizeof(_filename));
1374     gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1375     wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1376     g_free(utf16_path_string);
1378     opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1379     opf.lpstrFilter = filter_string;
1380     opf.lpstrCustomFilter = 0;
1381     opf.nMaxCustFilter = 0L;
1382     opf.nFilterIndex = 1L;
1383     opf.lpstrFile = _filename;
1384     opf.nMaxFile = _MAX_PATH;
1385     opf.lpstrFileTitle = NULL;
1386     opf.nMaxFileTitle=0;
1387     opf.lpstrInitialDir = 0;
1388     opf.lpstrTitle = title_string;
1389     opf.nFileOffset = 0;
1390     opf.nFileExtension = 2;
1391     opf.lpstrDefExt = extension_string;
1392     opf.lpfnHook = NULL;
1393     opf.lCustData = 0;
1394     opf.Flags = OFN_PATHMUSTEXIST;
1395     opf.lStructSize = sizeof(OPENFILENAMEW);
1396     if (GetSaveFileNameW(&opf) != 0)
1397     {
1398         // Copy the selected file name, converting from UTF-16 to UTF-8
1399         gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1400         gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1401         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1402         g_free(utf8string);
1404     }
1405     g_free(extension_string);
1406     g_free(title_string);
1408 #else
1409     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1410     {
1411         gchar *file;
1413         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1415         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1416         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1418         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1420         g_free(utf8file);
1421         g_free(file);
1422     }
1423 #endif
1425     gtk_widget_destroy (fs);
1427     return;
1428 } // end of sp_export_browse_clicked()
1430 // TODO: Move this to nr-rect-fns.h.
1431 static bool
1432 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1434     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1435     return (
1436         (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1437         (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1438         (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1439         (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1440         );
1443 /**
1444     \brief  This function is used to detect the current selection setting
1445             based on the values in the x0, y0, x1 and y0 fields.
1446     \param  base  The export dialog itself
1448     One of the most confusing parts of this function is why the array
1449     is built at the beginning.  What needs to happen here is that we
1450     should always check the current selection to see if it is the valid
1451     one.  While this is a performance improvement it is also a usability
1452     one during the cases where things like selections and drawings match
1453     size.  This way buttons change less 'randomly' (atleast in the eyes
1454     of the user).  To do this an array is built where the current selection
1455     type is placed first, and then the others in an order from smallest
1456     to largest (this can be configured by reshuffling \c test_order).
1458     All of the values in this function are rounded to two decimal places
1459     because that is what is shown to the user.  While everything is kept
1460     more accurate than that, the user can't control more acurrate than
1461     that, so for this to work for them - it needs to check on that level
1462     of accuracy.
1464     \todo finish writing this up
1465 */
1466 static void
1467 sp_export_detect_size(GtkObject * base) {
1468     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1469     selection_type this_test[SELECTION_NUMBER_OF + 1];
1470     selection_type key = SELECTION_NUMBER_OF;
1472     Geom::Point x(sp_export_value_get_px (base, "x0"),
1473                   sp_export_value_get_px (base, "y0"));
1474     Geom::Point y(sp_export_value_get_px (base, "x1"),
1475                   sp_export_value_get_px (base, "y1"));
1476     Geom::Rect current_bbox(x, y);
1477     //std::cout << "Current " << current_bbox;
1479     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1480     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1481         this_test[i + 1] = test_order[i];
1482     }
1484     for (int i = 0;
1485             i < SELECTION_NUMBER_OF + 1 &&
1486                 key == SELECTION_NUMBER_OF &&
1487                 SP_ACTIVE_DESKTOP != NULL;
1488             i++) {
1489         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1490         switch (this_test[i]) {
1491             case SELECTION_SELECTION:
1492                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1493                     Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(SPItem::RENDERING_BBOX);
1495                     //std::cout << "Selection " << bbox;
1496                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1497                         key = SELECTION_SELECTION;
1498                     }
1499                 }
1500                 break;
1501             case SELECTION_DRAWING: {
1502                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1504                 Geom::OptRect bbox = SP_ITEM(doc->root)->getBboxDesktop(SPItem::RENDERING_BBOX);
1506                 // std::cout << "Drawing " << bbox2;
1507                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1508                     key = SELECTION_DRAWING;
1509                 }
1510                 break;
1511             }
1513             case SELECTION_PAGE: {
1514                 SPDocument *doc;
1516                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1518                 Geom::Point x(0.0, 0.0);
1519                 Geom::Point y(doc->getWidth(),
1520                               doc->getHeight());
1521                 Geom::Rect bbox(x, y);
1523                 // std::cout << "Page " << bbox;
1524                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1525                     key = SELECTION_PAGE;
1526                 }
1528                 break;
1529            }
1530         default:
1531            break;
1532         }
1533     }
1534     // std::cout << std::endl;
1536     if (key == SELECTION_NUMBER_OF) {
1537         key = SELECTION_CUSTOM;
1538     }
1540     /* We're now using a custom size, not a fixed one */
1541     /* printf("Detecting state: %s\n", selection_names[key]); */
1542     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1543     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1544     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1545     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1547     return;
1548 } /* sp_export_detect_size */
1550 /// Called when area x0 value is changed
1551 static void
1552 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1554     float x0, x1, xdpi, width;
1556     if (gtk_object_get_data (base, "update"))
1557         return;
1559     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1560             (base, "units")))
1561     {
1562         return;
1563     }
1565     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1567     x0 = sp_export_value_get_px (base, "x0");
1568     x1 = sp_export_value_get_px (base, "x1");
1569     xdpi = sp_export_value_get (base, "xdpi");
1571     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1573     if (width < SP_EXPORT_MIN_SIZE) {
1574         const gchar *key;
1575         width = SP_EXPORT_MIN_SIZE;
1576         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1578         if (!strcmp (key, "x0")) {
1579             x1 = x0 + width * DPI_BASE / xdpi;
1580             sp_export_value_set_px (base, "x1", x1);
1581         } else {
1582             x0 = x1 - width * DPI_BASE / xdpi;
1583             sp_export_value_set_px (base, "x0", x0);
1584         }
1585     }
1587     sp_export_value_set_px (base, "width", x1 - x0);
1588     sp_export_value_set (base, "bmwidth", width);
1590     sp_export_detect_size(base);
1592     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594     return;
1595 } // end of sp_export_area_x_value_changed()
1597 /// Called when area y0 value is changed.
1598 static void
1599 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1601     float y0, y1, ydpi, height;
1603     if (gtk_object_get_data (base, "update"))
1604         return;
1606     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1607            (base, "units")))
1608     {
1609         return;
1610     }
1612     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1614     y0 = sp_export_value_get_px (base, "y0");
1615     y1 = sp_export_value_get_px (base, "y1");
1616     ydpi = sp_export_value_get (base, "ydpi");
1618     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1620     if (height < SP_EXPORT_MIN_SIZE) {
1621         const gchar *key;
1622         height = SP_EXPORT_MIN_SIZE;
1623         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1624         if (!strcmp (key, "y0")) {
1625             y1 = y0 + height * DPI_BASE / ydpi;
1626             sp_export_value_set_px (base, "y1", y1);
1627         } else {
1628             y0 = y1 - height * DPI_BASE / ydpi;
1629             sp_export_value_set_px (base, "y0", y0);
1630         }
1631     }
1633     sp_export_value_set_px (base, "height", y1 - y0);
1634     sp_export_value_set (base, "bmheight", height);
1636     sp_export_detect_size(base);
1638     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1640     return;
1641 } // end of sp_export_area_y_value_changed()
1643 /// Called when x1-x0 or area width is changed
1644 static void
1645 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1647     float x0, x1, xdpi, width, bmwidth;
1649     if (gtk_object_get_data (base, "update"))
1650         return;
1652     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1653            (base, "units"))) {
1654         return;
1655     }
1657     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1659     x0 = sp_export_value_get_px (base, "x0");
1660     x1 = sp_export_value_get_px (base, "x1");
1661     xdpi = sp_export_value_get (base, "xdpi");
1662     width = sp_export_value_get_px (base, "width");
1663     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1665     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1667         bmwidth = SP_EXPORT_MIN_SIZE;
1668         width = bmwidth * DPI_BASE / xdpi;
1669         sp_export_value_set_px (base, "width", width);
1670     }
1672     sp_export_value_set_px (base, "x1", x0 + width);
1673     sp_export_value_set (base, "bmwidth", bmwidth);
1675     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1677     return;
1678 } // end of sp_export_area_width_value_changed()
1680 /// Called when y1-y0 or area height is changed.
1681 static void
1682 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1685     float y0, y1, ydpi, height, bmheight;
1687     if (gtk_object_get_data (base, "update"))
1688         return;
1690     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1691            (base, "units"))) {
1692         return;
1693     }
1695     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1697     y0 = sp_export_value_get_px (base, "y0");
1698     y1 = sp_export_value_get_px (base, "y1");
1699     ydpi = sp_export_value_get (base, "ydpi");
1700     height = sp_export_value_get_px (base, "height");
1701     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1703     if (bmheight < SP_EXPORT_MIN_SIZE) {
1704         bmheight = SP_EXPORT_MIN_SIZE;
1705         height = bmheight * DPI_BASE / ydpi;
1706         sp_export_value_set_px (base, "height", height);
1707     }
1709     sp_export_value_set_px (base, "y1", y0 + height);
1710     sp_export_value_set (base, "bmheight", bmheight);
1712     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1714     return;
1715 } // end of sp_export_area_height_value_changed()
1717 /**
1718     \brief  A function to set the ydpi
1719     \param  base  The export dialog
1721     This function grabs all of the y values and then figures out the
1722     new bitmap size based on the changing dpi value.  The dpi value is
1723     gotten from the xdpi setting as these can not currently be independent.
1724 */
1725 static void
1726 sp_export_set_image_y (GtkObject *base)
1728     float y0, y1, xdpi;
1730     y0 = sp_export_value_get_px (base, "y0");
1731     y1 = sp_export_value_get_px (base, "y1");
1732     xdpi = sp_export_value_get (base, "xdpi");
1734     sp_export_value_set (base, "ydpi", xdpi);
1735     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1737     return;
1738 } // end of sp_export_set_image_y()
1740 /**
1741     \brief  A function to set the xdpi
1742     \param  base  The export dialog
1744     This function grabs all of the x values and then figures out the
1745     new bitmap size based on the changing dpi value.  The dpi value is
1746     gotten from the xdpi setting as these can not currently be independent.
1747 */
1748 static void
1749 sp_export_set_image_x (GtkObject *base)
1751     float x0, x1, xdpi;
1753     x0 = sp_export_value_get_px (base, "x0");
1754     x1 = sp_export_value_get_px (base, "x1");
1755     xdpi = sp_export_value_get (base, "xdpi");
1757     sp_export_value_set (base, "ydpi", xdpi);
1758     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1760     return;
1761 } // end of sp_export_set_image_x()
1763 /// Called when pixel width is changed
1764 static void
1765 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1767     float x0, x1, bmwidth, xdpi;
1769     if (gtk_object_get_data (base, "update"))
1770         return;
1772     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1773            (base, "units"))) {
1774        return;
1775     }
1777     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1779     x0 = sp_export_value_get_px (base, "x0");
1780     x1 = sp_export_value_get_px (base, "x1");
1781     bmwidth = sp_export_value_get (base, "bmwidth");
1783     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1784         bmwidth = SP_EXPORT_MIN_SIZE;
1785         sp_export_value_set (base, "bmwidth", bmwidth);
1786     }
1788     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1789     sp_export_value_set (base, "xdpi", xdpi);
1791     sp_export_set_image_y (base);
1793     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1795     return;
1796 } // end of sp_export_bitmap_width_value_changed()
1798 /// Called when pixel height is changed
1799 static void
1800 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1802     float y0, y1, bmheight, xdpi;
1804     if (gtk_object_get_data (base, "update"))
1805         return;
1807     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1808            (base, "units"))) {
1809        return;
1810     }
1812     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1814     y0 = sp_export_value_get_px (base, "y0");
1815     y1 = sp_export_value_get_px (base, "y1");
1816     bmheight = sp_export_value_get (base, "bmheight");
1818     if (bmheight < SP_EXPORT_MIN_SIZE) {
1819         bmheight = SP_EXPORT_MIN_SIZE;
1820         sp_export_value_set (base, "bmheight", bmheight);
1821     }
1823     xdpi = bmheight * DPI_BASE / (y1 - y0);
1824     sp_export_value_set (base, "xdpi", xdpi);
1826     sp_export_set_image_x (base);
1828     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1830     return;
1831 } // end of sp_export_bitmap_width_value_changed()
1833 /**
1834     \brief  A function to adjust the bitmap width when the xdpi value changes
1835     \param  adj  The adjustment that was changed
1836     \param  base The export dialog itself
1838     The first thing this function checks is to see if we are doing an
1839     update.  If we are, this function just returns because there is another
1840     instance of it that will handle everything for us.  If there is a
1841     units change, we also assume that everyone is being updated appropriately
1842     and there is nothing for us to do.
1844     If we're the highest level function, we set the update flag, and
1845     continue on our way.
1847     All of the values are grabbed using the \c sp_export_value_get functions
1848     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1849     xdpi value is saved in the preferences for the next time the dialog
1850     is opened.  (does the selection dpi need to be set here?)
1852     A check is done to to ensure that we aren't outputing an invalid width,
1853     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1854     changed to make it valid.
1856     After all of this the bitmap width is changed.
1858     We also change the ydpi.  This is a temporary hack as these can not
1859     currently be independent.  This is likely to change in the future.
1860 */
1861 void
1862 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1864     float x0, x1, xdpi, bmwidth;
1866     if (gtk_object_get_data (base, "update"))
1867         return;
1869     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1870            (base, "units"))) {
1871        return;
1872     }
1874     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1876     x0 = sp_export_value_get_px (base, "x0");
1877     x1 = sp_export_value_get_px (base, "x1");
1878     xdpi = sp_export_value_get (base, "xdpi");
1880     // remember xdpi setting
1881     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1882     prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1884     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1886     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1887         bmwidth = SP_EXPORT_MIN_SIZE;
1888         if (x1 != x0)
1889             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1890         else
1891             xdpi = DPI_BASE;
1892         sp_export_value_set (base, "xdpi", xdpi);
1893     }
1895     sp_export_value_set (base, "bmwidth", bmwidth);
1897     sp_export_set_image_y (base);
1899     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1901     return;
1902 } // end of sp_export_xdpi_value_changed()
1905 /**
1906     \brief  A function to change the area that is used for the exported
1907             bitmap.
1908     \param  base  This is the export dialog
1909     \param  x0    Horizontal upper left hand corner of the picture in points
1910     \param  y0    Vertical upper left hand corner of the picture in points
1911     \param  x1    Horizontal lower right hand corner of the picture in points
1912     \param  y1    Vertical lower right hand corner of the picture in points
1914     This function just calls \c sp_export_value_set_px for each of the
1915     parameters that is passed in.  This allows for setting them all in
1916     one convient area.
1918     Update is set to suspend all of the other test running while all the
1919     values are being set up.  This allows for a performance increase, but
1920     it also means that the wrong type won't be detected with only some of
1921     the values set.  After all the values are set everyone is told that
1922     there has been an update.
1923 */
1924 static void
1925 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1927     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1928     sp_export_value_set_px (base, "x1", x1);
1929     sp_export_value_set_px (base, "y1", y1);
1930     sp_export_value_set_px (base, "x0", x0);
1931     sp_export_value_set_px (base, "y0", y0);
1932     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1934     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1935     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1937     return;
1940 /**
1941     \brief  Sets the value of an adjustment
1942     \param  base  The export dialog
1943     \param  key   Which adjustment to set
1944     \param  val   What value to set it to
1946     This function finds the adjustment using the data stored in the
1947     export dialog.  After finding the adjustment it then sets
1948     the value of it.
1949 */
1950 static void
1951 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1953     GtkAdjustment *adj;
1955     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1957     gtk_adjustment_set_value (adj, val);
1960 /**
1961     \brief  A function to set a value using the units points
1962     \param  base  The export dialog
1963     \param  key   Which value should be set
1964     \param  val   What the value should be in points
1966     This function first gets the adjustment for the key that is passed
1967     in.  It then figures out what units are currently being used in the
1968     dialog.  After doing all of that, it then converts the incoming
1969     value and sets the adjustment.
1970 */
1971 static void
1972 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1974     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1976     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1978     return;
1981 /**
1982     \brief  Get the value of an adjustment in the export dialog
1983     \param  base  The export dialog
1984     \param  key   Which adjustment is being looked for
1985     \return The value in the specified adjustment
1987     This function gets the adjustment from the data field in the export
1988     dialog.  It then grabs the value from the adjustment.
1989 */
1990 static float
1991 sp_export_value_get ( GtkObject *base, const gchar *key )
1993     GtkAdjustment *adj;
1995     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1997     return adj->value;
2000 /**
2001     \brief  Grabs a value in the export dialog and converts the unit
2002             to points
2003     \param  base  The export dialog
2004     \param  key   Which value should be returned
2005     \return The value in the adjustment in points
2007     This function, at its most basic, is a call to \c sp_export_value_get
2008     to get the value of the adjustment.  It then finds the units that
2009     are being used by looking at the "units" attribute of the export
2010     dialog.  Using that it converts the returned value into points.
2011 */
2012 static float
2013 sp_export_value_get_px ( GtkObject *base, const gchar *key )
2015     float value = sp_export_value_get(base, key);
2016     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
2018     return sp_units_get_pixels (value, *unit);
2019 } // end of sp_export_value_get_px()
2021 /**
2022     \brief  This function is called when the filename is changed by
2023             anyone.  It resets the virgin bit.
2024     \param  object  Text entry box
2025     \param  data    The export dialog
2026     \return None
2028     This function gets called when the text area is modified.  It is
2029     looking for the case where the text area is modified from its
2030     original value.  In that case it sets the "filename-modified" bit
2031     to TRUE.  If the text dialog returns back to the original text, the
2032     bit gets reset.  This should stop simple mistakes.
2033 */
2034 static void
2035 sp_export_filename_modified (GtkObject * object, gpointer data)
2037     GtkWidget * text_entry = (GtkWidget *)object;
2038     GtkWidget * export_dialog = (GtkWidget *)data;
2040     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2041         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2042 //        printf("Modified: FALSE\n");
2043     } else {
2044         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2045 //        printf("Modified: TRUE\n");
2046     }
2048     return;
2049 } // end sp_export_filename_modified
2051 /*
2052   Local Variables:
2053   mode:c++
2054   c-file-style:"stroustrup"
2055   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2056   indent-tabs-mode:nil
2057   fill-column:99
2058   End:
2059 */
2060 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :