Code

Pot and Dutch translation update
[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  *
9  * Copyright (C) 1999-2007 Authors
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
20 #include <png.h>
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
32 #ifdef WITH_GNOME_VFS
33 # include <libgnomevfs/gnome-vfs-init.h>  // gnome_vfs_initialized
34 #endif
36 #include <glibmm/i18n.h>
37 #include "helper/unit-menu.h"
38 #include "helper/units.h"
39 #include "unit-constants.h"
40 #include "helper/window.h"
41 #include "inkscape-private.h"
42 #include "document.h"
43 #include "desktop-handles.h"
44 #include "sp-item.h"
45 #include "selection.h"
46 #include "file.h"
47 #include "macros.h"
48 #include "sp-namedview.h"
49 #include "selection-chemistry.h"
51 #include "dialog-events.h"
52 #include "preferences.h"
53 #include "verbs.h"
54 #include "interface.h"
56 #include "extension/output.h"
57 #include "extension/db.h"
59 #include "io/sys.h"
61 #include "helper/png-write.h"
63 #ifdef WIN32
64 #include <windows.h>
65 #include <commdlg.h>
66 #include <gdk/gdkwin32.h>
67 #endif
69 #define SP_EXPORT_MIN_SIZE 1.0
71 #define DPI_BASE PX_PER_IN
73 #define EXPORT_COORD_PRECISION 3
75 #define MIN_ONSCREEN_DISTANCE 50
77 static void sp_export_area_toggled   ( GtkToggleButton *tb, GtkObject *base );
78 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
79 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
81 static void sp_export_area_x_value_changed       ( GtkAdjustment *adj,
82                                                    GtkObject *base);
84 static void sp_export_area_y_value_changed       ( GtkAdjustment *adj,
85                                                    GtkObject *base);
87 static void sp_export_area_width_value_changed   ( GtkAdjustment *adj,
88                                                    GtkObject *base);
90 static void sp_export_area_height_value_changed  ( GtkAdjustment *adj,
91                                                    GtkObject *base);
93 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
94                                                    GtkObject *base);
96 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
97                                                    GtkObject *base);
99 static void sp_export_xdpi_value_changed         ( GtkAdjustment *adj,
100                                                    GtkObject *base);
102 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
103                                           Inkscape::Selection *selection,
104                                           GtkObject *base);
105 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
106                                            Inkscape::Selection *selection,
107                                            guint flags,
108                                            GtkObject *base );
110 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
111 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
112 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
113 static float sp_export_value_get    ( GtkObject *base, const gchar *key );
114 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
116 static void sp_export_filename_modified (GtkObject * object, gpointer data);
117 static inline void sp_export_find_default_selection(GtkWidget * dlg);
118 static void sp_export_detect_size(GtkObject * base);
120 static Glib::ustring const prefs_path = "/dialogs/export/";
122 // these all need to be reinitialized to their defaults during dialog_destroy
123 static GtkWidget *dlg = NULL;
124 static win_data wd;
125 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
126 static gchar * original_name = NULL;
127 static gchar * doc_export_name = NULL;
128 static bool was_empty = TRUE;
130 /** What type of button is being pressed */
131 enum selection_type {
132     SELECTION_PAGE = 0,  /**< Export the whole page */
133     SELECTION_DRAWING,   /**< Export everything drawn on the page */
134     SELECTION_SELECTION, /**< Export everything that is selected */
135     SELECTION_CUSTOM,    /**< Allows the user to set the region exported */
136     SELECTION_NUMBER_OF  /**< A counter for the number of these guys */
137 };
139 /** A list of strings that is used both in the preferences, and in the
140     data fields to describe the various values of \c selection_type. */
141 static const char * selection_names[SELECTION_NUMBER_OF] = {
142     "page", "drawing", "selection", "custom"};
144 /** The names on the buttons for the various selection types. */
145 static const char * selection_labels[SELECTION_NUMBER_OF] = {
146     N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
148 static void
149 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
151     sp_signal_disconnect_by_data (INKSCAPE, dlg);
153     wd.win = dlg = NULL;
154     wd.stop = 0;
155     x = -1000; y = -1000; w = 0; h = 0;
156     g_free(original_name);
157     original_name = NULL;
158     g_free(doc_export_name);
159     doc_export_name = NULL;
160     was_empty = TRUE;
162     return;
163 } // end of sp_export_dialog_destroy()
165 /// Called when dialog is closed or inkscape is shut down.
166 static bool
167 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
170     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
171     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
173     if (x<0) x=0;
174     if (y<0) y=0;
176     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
177     prefs->setInt(prefs_path + "x", x);
178     prefs->setInt(prefs_path + "y", y);
179     prefs->setInt(prefs_path + "w", w);
180     prefs->setInt(prefs_path + "h", h);
182     return FALSE; // which means, go ahead and destroy it
184 } // end of sp_export_dialog_delete()
186 /**
187     \brief  Creates a new spin button for the export dialog
188     \param  key  The name of the spin button
189     \param  val  A default value for the spin button
190     \param  min  Minimum value for the spin button
191     \param  max  Maximum value for the spin button
192     \param  step The step size for the spin button
193     \param  page Size of the page increment
194     \param  us   Unit selector that effects this spin button
195     \param  t    Table to put the spin button in
196     \param  x    X location in the table \c t to start with
197     \param  y    Y location in the table \c t to start with
198     \param  ll   Text to put on the left side of the spin button (optional)
199     \param  lr   Text to put on the right side of the spin button (optional)
200     \param  digits  Number of digits to display after the decimal
201     \param  sensitive  Whether the spin button is sensitive or not
202     \param  cb   Callback for when this spin button is changed (optional)
203     \param  dlg  Export dialog the spin button is being placed in
205 */
206 static void
207 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
208                            float step, float page, GtkWidget *us,
209                            GtkWidget *t, int x, int y,
210                            const gchar *ll, const gchar *lr,
211                            int digits, unsigned int sensitive,
212                            GCallback cb, GtkWidget *dlg )
214     GtkObject *adj = gtk_adjustment_new( val, min, max, step, page, 0 );
215     gtk_object_set_data( adj, "key", const_cast<gchar *>(key) );
216     gtk_object_set_data( GTK_OBJECT (dlg), (const gchar *)key, adj );
218     if (us) {
219         sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
220                                           GTK_ADJUSTMENT (adj) );
221     }
223     int pos = 0;
225     GtkWidget *l = NULL;
227     if (ll) {
229         l = gtk_label_new_with_mnemonic ((const gchar *)ll);
230         gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
231         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
232                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
233         gtk_widget_set_sensitive (l, sensitive);
234         pos += 1;
236     }
238     GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, digits);
239     gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
240                        (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
241     gtk_widget_set_size_request (sb, 80, -1);
242     gtk_widget_set_sensitive (sb, sensitive);
243     pos += 1;
245     if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
247     if (lr) {
249         l = gtk_label_new_with_mnemonic ((const gchar *)lr);
250         gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
251         gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
252                            (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
253         gtk_widget_set_sensitive (l, sensitive);
254         pos += 1;
256         gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
257     }
259     if (cb)
260         gtk_signal_connect (adj, "value_changed", cb, dlg);
262     return;
263 } // end of sp_export_spinbutton_new()
266 static Gtk::VBox *
267 sp_export_dialog_area_box (GtkWidget * dlg)
269     Gtk::VBox* vb = new Gtk::VBox(false, 3);
271     Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
272     lbl->set_use_markup(true);
273     vb->pack_start(*lbl);
275     /* Units box */
276     Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
277     /* gets added to the vbox later, but the unit selector is needed
278        earlier than that */
280     Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
281     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
282     if (desktop)
283         sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
284     unitbox->pack_end(*us, false, false, 0);
285     Gtk::Label* l = new Gtk::Label(_("Units:"));
286     unitbox->pack_end(*l, false, false, 3);
287     gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
289     Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
291     Gtk::ToggleButton* b;
292     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
293         b = new Gtk::ToggleButton(_(selection_labels[i]), true);
294         b->set_data("key", GINT_TO_POINTER(i));
295         gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
296         togglebox->pack_start(*b, false, true, 0);
297         gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
298                              GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
299     }
301     g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
302                        G_CALLBACK (sp_export_selection_changed), dlg );
303     g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
304                        G_CALLBACK (sp_export_selection_modified), dlg );
305     g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
306                        G_CALLBACK (sp_export_selection_changed), dlg );
308     Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
309     t->set_row_spacings (4);
310     t->set_col_spacings (4);
312     sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
313                                GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
314                                G_CALLBACK ( sp_export_area_x_value_changed),
315                                dlg );
317     sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
318                                GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
319                                G_CALLBACK (sp_export_area_x_value_changed),
320                                dlg );
322     sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
323                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
324                                G_CALLBACK
325                                    (sp_export_area_width_value_changed),
326                                dlg );
328     sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
329                                GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
330                                G_CALLBACK (sp_export_area_y_value_changed),
331                                dlg );
333     sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
334                                GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
335                                G_CALLBACK (sp_export_area_y_value_changed),
336                                dlg );
338     sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
339                                us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
340                                G_CALLBACK (sp_export_area_height_value_changed),
341                                dlg );
343     vb->pack_start(*togglebox, false, false, 3);
344     vb->pack_start(*t, false, false, 0);
345     vb->pack_start(*unitbox, false, false, 0);
347     return vb;
348 } // end of sp_export_dialog_area_box
351 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
353     if (id == NULL) /* This should never happen */
354         id = "bitmap";
356     gchar * directory = NULL;
358     if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
359         // std::cout << "Directory from dialog" << std::endl;
360         directory = g_dirname(file_entry_text);
361     }
363     if (directory == NULL) {
364         /* Grab document directory */
365         if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
366             // std::cout << "Directory from document" << std::endl;
367             directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
368         }
369     }
371     if (directory == NULL) {
372         // std::cout << "Home Directory" << std::endl;
373         directory = homedir_path(NULL);
374     }
376     gchar * id_ext = g_strconcat(id, ".png", NULL);
377     gchar *filename = g_build_filename(directory, id_ext, NULL);
378     g_free(directory);
379     g_free(id_ext);
380     return filename;
383 static void
384 batch_export_clicked (GtkWidget *widget, GtkObject *base)
386     Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
387     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
388         vb_singleexport->set_sensitive(false);
389     } else {
390         vb_singleexport->set_sensitive(true);
391     }
394 void
395 sp_export_dialog (void)
397     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
398     if (!dlg) {
400         gchar title[500];
401         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
403         dlg = sp_window_new (title, TRUE);
405         if (x == -1000 || y == -1000) {
406             x = prefs->getInt(prefs_path + "x", 0);
407             y = prefs->getInt(prefs_path + "y", 0);
408         }
410         if (w ==0 || h == 0) {
411             w = prefs->getInt(prefs_path + "w", 0);
412             h = prefs->getInt(prefs_path + "h", 0);
413         }
415 //        if (x<0) x=0;
416 //        if (y<0) y=0;
418         if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
419         if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
420             gtk_window_move ((GtkWindow *) dlg, x, y);
421         else
422             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
423         sp_transientize (dlg);
424         wd.win = dlg;
425         wd.stop = 0;
427         g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop",
428                              G_CALLBACK (sp_transientize_callback), &wd);
430         gtk_signal_connect ( GTK_OBJECT (dlg), "event",
431                              GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
433         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
434                              G_CALLBACK (sp_export_dialog_destroy), dlg);
436         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
437                              G_CALLBACK (sp_export_dialog_delete), dlg);
439         g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down",
440                              G_CALLBACK (sp_export_dialog_delete), dlg);
442         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide",
443                              G_CALLBACK (sp_dialog_hide), dlg);
445         g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide",
446                              G_CALLBACK (sp_dialog_unhide), dlg);
448         GtkTooltips *tt = gtk_tooltips_new();
450         Gtk::VBox *vb = new Gtk::VBox(false, 3);
451         vb->set_border_width(3);
452         gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
454         Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
455         vb_singleexport->set_border_width(0);
456         vb->pack_start(*vb_singleexport);
457         gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
459         /* Export area frame */
460         {
461             Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
462             area_box->set_border_width(3);
463             vb_singleexport->pack_start(*area_box, false, false, 0);
464         }
466         /* Bitmap size frame */
467         {
468             Gtk::VBox *size_box = new Gtk::VBox(false, 3);
469             size_box->set_border_width(3);
471             Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
472             lbl->set_use_markup(true);
473             size_box->pack_start(*lbl, false, false, 0);
474             const int rows = 2;
475             const int cols = 5;
476             const bool homogeneous = false;
477             Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
478             t->set_row_spacings (4);
479             t->set_col_spacings (4);
480             size_box->pack_start(*t);
482             sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
483                                        NULL, GTK_WIDGET(t->gobj()), 0, 0,
484                                        _("_Width:"), _("pixels at"), 0, 1,
485                                        G_CALLBACK
486                                        (sp_export_bitmap_width_value_changed),
487                                        dlg );
489             sp_export_spinbutton_new ( "xdpi",
490                                        prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
491                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
492                                        NULL, _("dp_i"), 2, 1,
493                                        G_CALLBACK (sp_export_xdpi_value_changed),
494                                        dlg );
496             sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
497                                        NULL, GTK_WIDGET(t->gobj()), 0, 1,
498                                        _("_Height:"), _("pixels at"), 0, 1,
499                                        G_CALLBACK
500                                        (sp_export_bitmap_height_value_changed),
501                                        dlg );
503             /** \todo
504              * Needs fixing: there's no way to set ydpi currently, so we use
505              *       the defaultxdpi value here, too...
506              */
507             sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
508                                        0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
509                                        NULL, _("dpi"), 2, 0, NULL, dlg );
511             vb_singleexport->pack_start(*size_box);
512         }
514         /* File entry */
515         {
516             Gtk::VBox* file_box = new Gtk::VBox(false, 3);
517             file_box->set_border_width(3);
519             // true = has mnemonic
520             Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
521             flabel->set_use_markup(true);
522             file_box->pack_start(*flabel, false, false, 0);
524             Gtk::Entry *fe = new Gtk::Entry();
526             /*
527              * set the default filename to be that of the current path + document
528              * with .png extension
529              *
530              * One thing to notice here is that this filename may get
531              * overwritten, but it won't happen here.  The filename gets
532              * written into the text field, but then the button to select
533              * the area gets set.  In that code the filename can be changed
534              * if there are some with presidence in the document.  So, while
535              * this code sets the name first, it may not be the one users
536              * really see.
537              */
538             if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
539             {
540                 gchar *name;
541                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
542                 const gchar *uri = SP_DOCUMENT_URI (doc);
543                 const gchar *text_extension = get_file_save_extension (Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS).c_str();
544                 Inkscape::Extension::Output * oextension = NULL;
546                 if (text_extension != NULL) {
547                     oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
548                 }
550                 if (oextension != NULL) {
551                     gchar * old_extension = oextension->get_extension();
552                     if (g_str_has_suffix(uri, old_extension)) {
553                         gchar * uri_copy;
554                         gchar * extension_point;
555                         gchar * final_name;
557                         uri_copy = g_strdup(uri);
558                         extension_point = g_strrstr(uri_copy, old_extension);
559                         extension_point[0] = '\0';
561                         final_name = g_strconcat(uri_copy, ".png", NULL);
562                         fe->set_text(final_name);
564                         g_free(final_name);
565                         g_free(uri_copy);
566                     }
567                 } else {
568                     name = g_strconcat(uri, ".png", NULL);
569                     fe->set_text(name);
570                     g_free(name);
571                 }
573                 doc_export_name = g_strdup(fe->get_text().c_str());
574             }
575             g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
576                                G_CALLBACK (sp_export_filename_modified), dlg);
578             Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
580             {
581                 // true = has mnemonic
582                 Gtk::Button *b = new Gtk::Button();
584                 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
585                 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
586                         Gtk::ICON_SIZE_BUTTON);
587                 pixlabel->pack_start(*im);
589                 Gtk::Label *l = new Gtk::Label();
590                 l->set_markup_with_mnemonic(_("_Browse..."));
591                 pixlabel->pack_start(*l);
593                 b->add(*pixlabel);
595                 hb->pack_end (*b, false, false, 4);
596                 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
597                                    G_CALLBACK (sp_export_browse_clicked), NULL );
598             }
600             hb->pack_start (*fe, true, true, 0);
601             file_box->add(*hb);
602             gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
603             gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
604             original_name = g_strdup(fe->get_text().c_str());
605             // pressing enter in the filename field is the same as clicking export:
606             g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
607                                G_CALLBACK (sp_export_export_clicked), dlg );
608             // focus is in the filename initially:
609             fe->grab_focus();
611             // mnemonic in frame label moves focus to filename:
612             flabel->set_mnemonic_widget(*fe);
614             vb_singleexport->pack_start(*file_box);
615         }
617         {
618             Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
619             GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
620             gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
621             gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
622             batch_box->pack_start(*Glib::wrap(be), false, false);
623             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);
624             batch_box->show_all();
625             g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
626             vb->pack_start(*batch_box);
627         }
629         {
630             Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
631             GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
632             gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
633             gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
634             hide_box->pack_start(*Glib::wrap(he), false, false);
635             gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
636             hide_box->show_all();
637             vb->pack_start(*hide_box);
638         }
640         /* Buttons */
641         Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
642         bb->set_border_width(3);
644         {
645             Gtk::Button *b = new Gtk::Button();
646             Gtk::HBox* image_label = new Gtk::HBox(false, 3);
647             Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
648                     Gtk::ICON_SIZE_BUTTON);
649             image_label->pack_start(*im);
651             Gtk::Label *l = new Gtk::Label();
652             l->set_markup_with_mnemonic(_("_Export"));
653             image_label->pack_start(*l);
655             b->add(*image_label);
656             gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
657             gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
658                                  GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
659             bb->pack_end(*b, false, false, 0);
660         }
662         vb->pack_end(*bb, false, false, 0);
663         vb->show_all();
665     } // end of if (!dlg)
667     sp_export_find_default_selection(dlg);
669     gtk_window_present ((GtkWindow *) dlg);
671     return;
672 } // end of sp_export_dialog()
674 static void
675 sp_export_update_checkbuttons (GtkObject *base)
677     gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
678     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
679     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
680     if (num >= 2) {
681         gtk_widget_set_sensitive (be, true);
682         gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
683     } else {
684         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
685         gtk_widget_set_sensitive (be, FALSE);
686     }
687     if (num > 0) {
688         gtk_widget_set_sensitive (he, true);
689     } else {
690         gtk_widget_set_sensitive (he, false);
691     }
694 static inline void
695 sp_export_find_default_selection(GtkWidget * dlg)
697     selection_type key = SELECTION_NUMBER_OF;
699     if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
700         key = SELECTION_SELECTION;
701     }
703     /* Try using the preferences */
704     if (key == SELECTION_NUMBER_OF) {
705         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
706         int i = SELECTION_NUMBER_OF;
708         Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
710         if (!what.empty()) {
711             for (i = 0; i < SELECTION_NUMBER_OF; i++) {
712                 if (what == selection_names[i]) {
713                     break;
714                 }
715             }
716         }
718         key = (selection_type)i;
719     }
721     if (key == SELECTION_NUMBER_OF) {
722         key = SELECTION_SELECTION;
723     }
725     GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
726                                                        selection_names[key]);
727     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
729     sp_export_update_checkbuttons (GTK_OBJECT(dlg));
733 /**
734  * \brief  If selection changed or a different document activated, we must
735  * recalculate any chosen areas
736  *
737  */
738 static void
739 sp_export_selection_changed ( Inkscape::Application *inkscape,
740                               Inkscape::Selection *selection,
741                               GtkObject *base )
743     selection_type current_key;
744     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
746     if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
747             (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
748             was_empty) {
749         gtk_toggle_button_set_active
750             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
751               TRUE );
752     }
753     was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
755     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
757     if (inkscape &&
758             SP_IS_INKSCAPE (inkscape) &&
759             selection &&
760             SELECTION_CUSTOM != current_key) {
761         GtkToggleButton * button;
762         button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
763         sp_export_area_toggled(button, base);
764     }
766     sp_export_update_checkbuttons (base);
769 static void
770 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
771                                Inkscape::Selection */*selection*/,
772                                guint /*flags*/,
773                                GtkObject *base )
775     selection_type current_key;
776     current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
778     switch (current_key) {
779         case SELECTION_DRAWING:
780             if ( SP_ACTIVE_DESKTOP ) {
781                 SPDocument *doc;
782                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
783                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), SPItem::RENDERING_BBOX);
784                 if (bbox) {
785                     sp_export_set_area (base, bbox->min()[Geom::X],
786                                               bbox->min()[Geom::Y],
787                                               bbox->max()[Geom::X],
788                                               bbox->max()[Geom::Y]);
789                 }
790             }
791             break;
792         case SELECTION_SELECTION:
793             if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
794                 NRRect bbox;
795                 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox, SPItem::RENDERING_BBOX);
796                 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
797             }
798             break;
799         default:
800             /* Do nothing for page or for custom */
801             break;
802     }
804     return;
807 /// Called when one of the selection buttons was toggled.
808 static void
809 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
811     if (gtk_object_get_data (base, "update"))
812         return;
814     selection_type key, old_key;
815     key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
816     old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
818     /* Ignore all "turned off" events unless we're the only active button */
819     if (!gtk_toggle_button_get_active (tb) ) {
821         /* Don't let the current selection be deactived - but rerun the
822            activate to allow the user to renew the values */
823         if (key == old_key) {
824             gtk_toggle_button_set_active ( tb, TRUE );
825         }
827         return;
828     }
830     /* Turn off the currently active button unless it's us */
831     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
833     if (old_key != key) {
834         gtk_toggle_button_set_active
835             ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
836               FALSE );
837     }
839     if ( SP_ACTIVE_DESKTOP )
840     {
841         SPDocument *doc;
842         Geom::OptRect bbox;
843         doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
845         /* Notice how the switch is used to 'fall through' here to get
846            various backups.  If you modify this without noticing you'll
847            probabaly screw something up. */
848         switch (key) {
849             case SELECTION_SELECTION:
850                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
851                 {
852                     bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds(SPItem::RENDERING_BBOX);
853                     /* Only if there is a selection that we can set
854                        do we break, otherwise we fall through to the
855                        drawing */
856                     // std::cout << "Using selection: SELECTION" << std::endl;
857                     key = SELECTION_SELECTION;
858                     break;
859                 }
860             case SELECTION_DRAWING:
861                 /** \todo
862                  * This returns wrong values if the document has a viewBox.
863                  */
864                 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), SPItem::RENDERING_BBOX);
865                 /* If the drawing is valid, then we'll use it and break
866                    otherwise we drop through to the page settings */
867                 if (bbox) {
868                     // std::cout << "Using selection: DRAWING" << std::endl;
869                     key = SELECTION_DRAWING;
870                     break;
871                 }
872             case SELECTION_PAGE:
873                 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
874                                   Geom::Point(sp_document_width(doc), sp_document_height(doc)));
876                 // std::cout << "Using selection: PAGE" << std::endl;
877                 key = SELECTION_PAGE;
878                 break;
879             case SELECTION_CUSTOM:
880             default:
881                 break;
882         } // switch
884         // remember area setting
885         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
886         prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
888         if ( key != SELECTION_CUSTOM && bbox ) {
889             sp_export_set_area (base, bbox->min()[Geom::X],
890                                       bbox->min()[Geom::Y],
891                                       bbox->max()[Geom::X],
892                                       bbox->max()[Geom::Y]);
893         }
895     } // end of if ( SP_ACTIVE_DESKTOP )
898     if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
899         GtkWidget * file_entry;
900         const gchar * filename = NULL;
901         float xdpi = 0.0, ydpi = 0.0;
903         file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
905         switch (key) {
906             case SELECTION_PAGE:
907             case SELECTION_DRAWING: {
908                 SPDocument * doc = SP_ACTIVE_DOCUMENT;
909                 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
911                 if (filename == NULL) {
912                     if (doc_export_name != NULL) {
913                         filename = g_strdup(doc_export_name);
914                     } else {
915                         filename = g_strdup("");
916                     }
917                 }
918                 break;
919             }
920             case SELECTION_SELECTION:
921                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
923                     sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
925                     /* If we still don't have a filename -- let's build
926                        one that's nice */
927                     if (filename == NULL) {
928                         const gchar * id = NULL;
929                         const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
930                         for(; reprlst != NULL; reprlst = reprlst->next) {
931                             Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
932                             if (repr->attribute("id")) {
933                                 id = repr->attribute("id");
934                                 break;
935                             }
936                         }
938                         filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
939                     }
940                 }
941                 break;
942             case SELECTION_CUSTOM:
943             default:
944                 break;
945         }
947         if (filename != NULL) {
948             g_free(original_name);
949             original_name = g_strdup(filename);
950             gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
951         }
953         if (xdpi != 0.0) {
954             sp_export_value_set(base, "xdpi", xdpi);
955         }
957         /* These can't be separate, and setting x sets y, so for
958            now setting this is disabled.  Hopefully it won't be in
959            the future */
960         if (FALSE && ydpi != 0.0) {
961             sp_export_value_set(base, "ydpi", ydpi);
962         }
963     }
965     return;
966 } // end of sp_export_area_toggled()
968 /// Called when dialog is deleted
969 static gint
970 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
972     g_object_set_data (base, "cancel", (gpointer) 1);
973     return TRUE;
974 } // end of sp_export_progress_delete()
976 /// Called when progress is cancelled
977 static void
978 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
980     g_object_set_data (base, "cancel", (gpointer) 1);
981 } // end of sp_export_progress_cancel()
983 /// Called for every progress iteration
984 static unsigned int
985 sp_export_progress_callback (float value, void *data)
987     GtkWidget *prg;
988     int evtcount;
990     if (g_object_get_data ((GObject *) data, "cancel"))
991         return FALSE;
993     prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
994     gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
996     evtcount = 0;
997     while ((evtcount < 16) && gdk_events_pending ()) {
998             gtk_main_iteration_do (FALSE);
999             evtcount += 1;
1000     }
1002     gtk_main_iteration_do (FALSE);
1004     return TRUE;
1006 } // end of sp_export_progress_callback()
1008 GtkWidget *
1009 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1010     GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1012     dlg = gtk_dialog_new ();
1013     gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1014     prg = gtk_progress_bar_new ();
1015     sp_transientize (dlg);
1016     gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1017     g_object_set_data ((GObject *) base, "progress", prg);
1019     gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1021     gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1022                                        GTK_PROGRESS_LEFT_TO_RIGHT);
1023     gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1024                         prg, FALSE, FALSE, 4 );
1025     btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1026                                   GTK_STOCK_CANCEL,
1027                                   GTK_RESPONSE_CANCEL );
1029     g_signal_connect ( (GObject *) dlg, "delete_event",
1030                        (GCallback) sp_export_progress_delete, base);
1031     g_signal_connect ( (GObject *) btn, "clicked",
1032                        (GCallback) sp_export_progress_cancel, base);
1033     gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1034     gtk_widget_show_all (dlg);
1036     return dlg;
1039 // FIXME: Some lib function should be available to do this ...
1040 static gchar *
1041 filename_add_extension (const gchar *filename, const gchar *extension)
1043   const gchar *dot;
1045   dot = strrchr (filename, '.');
1046   if ( !dot )
1047     return g_strconcat (filename, ".", extension, NULL);
1048   {
1049     if (dot[1] == '\0')
1050       return g_strconcat (filename, extension, NULL);
1051     else
1052     {
1053       if (g_strcasecmp (dot + 1, extension) == 0)
1054         return g_strdup (filename);
1055       else
1056       {
1057         return g_strconcat (filename, ".", extension, NULL);
1058       }
1059     }
1060   }
1063 gchar *absolutize_path_from_document_location (SPDocument *doc, const gchar *filename)
1065     gchar *path = 0;
1066     //Make relative paths go from the document location, if possible:
1067     if (!g_path_is_absolute(filename) && doc->uri) {
1068         gchar *dirname = g_path_get_dirname(doc->uri);
1069         if (dirname) {
1070             path = g_build_filename(dirname, filename, NULL);
1071             g_free(dirname);
1072         }
1073     }
1074     if (!path) {
1075         path = g_strdup(filename);
1076     }
1077     return path;
1080 /// Called when export button is clicked
1081 static void
1082 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1084     if (!SP_ACTIVE_DESKTOP) return;
1086     SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1087     SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1089     GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1090     GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1091     bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1092     if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1093         // Batch export of selected objects
1095         gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1096         gint n = 0;
1098         if (num < 1)
1099             return;
1101         gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1102         GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1103         g_free (progress_text);
1105         for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1106              i != NULL;
1107              i = i->next) {
1108             SPItem *item = (SPItem *) i->data;
1110             // retrieve export filename hint
1111             const gchar *filename = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1112             gchar *path = 0;
1113             if (!filename) {
1114                 path = create_filepath_from_id(item->getId(), NULL);
1115             } else {
1116                 path = absolutize_path_from_document_location(doc, filename);
1117             }
1119             // retrieve export dpi hints
1120             const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1121             gdouble dpi = 0.0;
1122             if (dpi_hint) {
1123                 dpi = atof(dpi_hint);
1124             }
1125             if (dpi == 0.0) {
1126                 dpi = DPI_BASE;
1127             }
1129             Geom::OptRect area;
1130             sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1131             if (area) {
1132                 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1133                 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1135                 if (width > 1 && height > 1) {
1136                     /* Do export */
1137                     if (!sp_export_png_file (doc, path,
1138                                              *area, width, height, dpi, dpi,
1139                                              nv->pagecolor,
1140                                              NULL, NULL, TRUE,  // overwrite without asking
1141                                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1142                             )) {
1143                         gchar * error;
1144                         gchar * safeFile = Inkscape::IO::sanitizeString(path);
1145                         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1146                         sp_ui_error_dialog(error);
1147                         g_free(safeFile);
1148                         g_free(error);
1149                     }
1150                 }
1151             }
1152             n++;
1153             g_free(path);
1154             sp_export_progress_callback((float)n/num, base);
1155         }
1157         gtk_widget_destroy (prog_dlg);
1158         g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1160     } else {
1162     GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1163     gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1165     float const x0 = sp_export_value_get_px(base, "x0");
1166     float const y0 = sp_export_value_get_px(base, "y0");
1167     float const x1 = sp_export_value_get_px(base, "x1");
1168     float const y1 = sp_export_value_get_px(base, "y1");
1169     float const xdpi = sp_export_value_get(base, "xdpi");
1170     float const ydpi = sp_export_value_get(base, "ydpi");
1171     unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1172     unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1174     if (filename == NULL || *filename == '\0') {
1175         sp_ui_error_dialog(_("You have to enter a filename"));
1176         return;
1177     }
1179     if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1180         sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1181         return;
1182     }
1184     // make sure that .png is the extension of the file:
1185     gchar * filename_ext = filename_add_extension(filename, "png");
1186     gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1188     gchar *path = absolutize_path_from_document_location(doc, filename_ext);
1190     gchar *dirname = g_path_get_dirname(path);
1191     if ( dirname == NULL
1192          || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1193     {
1194         gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1195         gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1196                                        safeDir);
1197         sp_ui_error_dialog(error);
1198         g_free(safeDir);
1199         g_free(error);
1200         g_free(dirname);
1201         g_free(path);
1202         return;
1203     }
1204     g_free(dirname);
1206     gchar *fn = g_path_get_basename (path);
1207     gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1208     g_free (fn);
1210     GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1211     g_free (progress_text);
1213     /* Do export */
1214     if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), path,
1215                              Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1216                              nv->pagecolor,
1217                              sp_export_progress_callback, base, FALSE,
1218                              hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1219             )) {
1220         gchar * error;
1221         gchar * safeFile = Inkscape::IO::sanitizeString(path);
1222         error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1223         sp_ui_error_dialog(error);
1224         g_free(safeFile);
1225         g_free(error);
1226     }
1228     /* Reset the filename so that it can be changed again by changing
1229        selections and all that */
1230     g_free(original_name);
1231     original_name = g_strdup(filename_ext);
1232     gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1234     gtk_widget_destroy (prog_dlg);
1235     g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1237     /* Setup the values in the document */
1238     switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1239         case SELECTION_PAGE:
1240         case SELECTION_DRAWING: {
1241             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1242             Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1243             bool modified = false;
1244             const gchar * temp_string;
1246             bool saved = sp_document_get_undo_sensitive(doc);
1247             sp_document_set_undo_sensitive(doc, false);
1249             temp_string = repr->attribute("inkscape:export-filename");
1250             if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1251                 repr->setAttribute("inkscape:export-filename", filename_ext);
1252                 modified = true;
1253             }
1254             temp_string = repr->attribute("inkscape:export-xdpi");
1255             if (temp_string == NULL || xdpi != atof(temp_string)) {
1256                 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1257                 modified = true;
1258             }
1259             temp_string = repr->attribute("inkscape:export-ydpi");
1260             if (temp_string == NULL || xdpi != atof(temp_string)) {
1261                 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1262                 modified = true;
1263             }
1264             sp_document_set_undo_sensitive(doc, saved);
1266             if (modified) {
1267                 doc->setModifiedSinceSave();
1268             }
1269             break;
1270         }
1271         case SELECTION_SELECTION: {
1272             const GSList * reprlst;
1273             SPDocument * doc = SP_ACTIVE_DOCUMENT;
1274             bool modified = false;
1276             bool saved = sp_document_get_undo_sensitive(doc);
1277             sp_document_set_undo_sensitive(doc, false);
1278             reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1280             for(; reprlst != NULL; reprlst = reprlst->next) {
1281                 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1282                 const gchar * temp_string;
1284                 if (repr->attribute("id") == NULL ||
1285                         !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1286                           (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1287                             strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1288                     temp_string = repr->attribute("inkscape:export-filename");
1289                     if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1290                         repr->setAttribute("inkscape:export-filename", filename_ext);
1291                         modified = true;
1292                     }
1293                 }
1294                 temp_string = repr->attribute("inkscape:export-xdpi");
1295                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1296                     sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1297                     modified = true;
1298                 }
1299                 temp_string = repr->attribute("inkscape:export-ydpi");
1300                 if (temp_string == NULL || xdpi != atof(temp_string)) {
1301                     sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1302                     modified = true;
1303                 }
1304             }
1305             sp_document_set_undo_sensitive(doc, saved);
1307             if (modified) {
1308                 doc->setModifiedSinceSave();
1309             }
1310             break;
1311         }
1312         default:
1313             break;
1314     }
1316     g_free (filename_ext);
1317     g_free (path);
1319     }
1321 } // end of sp_export_export_clicked()
1323 /// Called when Browse button is clicked
1324 /// @todo refactor this code to use ui/dialogs/filedialog.cpp
1325 static void
1326 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1328     GtkWidget *fs, *fe;
1329     const gchar *filename;
1331     fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1332                                       (GtkWindow*)dlg,
1333                                       GTK_FILE_CHOOSER_ACTION_SAVE,
1334                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1335                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1336                                       NULL );
1338 #ifdef WITH_GNOME_VFS
1339     if (gnome_vfs_initialized()) {
1340         gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1341     }
1342 #endif
1344     fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1346     sp_transientize (fs);
1348     gtk_window_set_modal(GTK_WINDOW (fs), true);
1350     filename = gtk_entry_get_text (GTK_ENTRY (fe));
1352     if (*filename == '\0') {
1353         filename = create_filepath_from_id(NULL, NULL);
1354     }
1356     gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1358 #ifdef WIN32
1359     // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
1360     OPENFILENAMEW opf;
1361     WCHAR filter_string[20];
1362     wcsncpy(filter_string, L"PNG#*.png##", 11);
1363     filter_string[3] = L'\0';
1364     filter_string[9] = L'\0';
1365     filter_string[10] = L'\0';
1366     WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
1367     WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL);
1368     // Copy the selected file name, converting from UTF-8 to UTF-16
1369     WCHAR _filename[_MAX_PATH + 1];
1370     memset(_filename, 0, sizeof(_filename));
1371     gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
1372     wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH);
1373     g_free(utf16_path_string);
1375     opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window));
1376     opf.lpstrFilter = filter_string;
1377     opf.lpstrCustomFilter = 0;
1378     opf.nMaxCustFilter = 0L;
1379     opf.nFilterIndex = 1L;
1380     opf.lpstrFile = _filename;
1381     opf.nMaxFile = _MAX_PATH;
1382     opf.lpstrFileTitle = NULL;
1383     opf.nMaxFileTitle=0;
1384     opf.lpstrInitialDir = 0;
1385     opf.lpstrTitle = title_string;
1386     opf.nFileOffset = 0;
1387     opf.nFileExtension = 2;
1388     opf.lpstrDefExt = extension_string;
1389     opf.lpfnHook = NULL;
1390     opf.lCustData = 0;
1391     opf.Flags = OFN_PATHMUSTEXIST;
1392     opf.lStructSize = sizeof(OPENFILENAMEW);
1393     if (GetSaveFileNameW(&opf) != 0)
1394     {
1395         // Copy the selected file name, converting from UTF-16 to UTF-8
1396         gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL);
1397         gtk_entry_set_text (GTK_ENTRY (fe), utf8string);
1398         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1399         g_free(utf8string);
1401     }
1402     g_free(extension_string);
1403     g_free(title_string);
1405 #else
1406     if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1407     {
1408         gchar *file;
1410         file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1412         gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1413         gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1415         g_object_set_data (G_OBJECT (dlg), "filename", fe);
1417         g_free(utf8file);
1418         g_free(file);
1419     }
1420 #endif
1422     gtk_widget_destroy (fs);
1424     return;
1425 } // end of sp_export_browse_clicked()
1427 // TODO: Move this to nr-rect-fns.h.
1428 static bool
1429 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1431     double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1432     return (
1433         (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1434         (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1435         (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1436         (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1437         );
1440 /**
1441     \brief  This function is used to detect the current selection setting
1442             based on the values in the x0, y0, x1 and y0 fields.
1443     \param  base  The export dialog itself
1445     One of the most confusing parts of this function is why the array
1446     is built at the beginning.  What needs to happen here is that we
1447     should always check the current selection to see if it is the valid
1448     one.  While this is a performance improvement it is also a usability
1449     one during the cases where things like selections and drawings match
1450     size.  This way buttons change less 'randomly' (atleast in the eyes
1451     of the user).  To do this an array is built where the current selection
1452     type is placed first, and then the others in an order from smallest
1453     to largest (this can be configured by reshuffling \c test_order).
1455     All of the values in this function are rounded to two decimal places
1456     because that is what is shown to the user.  While everything is kept
1457     more accurate than that, the user can't control more acurrate than
1458     that, so for this to work for them - it needs to check on that level
1459     of accuracy.
1461     \todo finish writing this up
1462 */
1463 static void
1464 sp_export_detect_size(GtkObject * base) {
1465     static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1466     selection_type this_test[SELECTION_NUMBER_OF + 1];
1467     selection_type key = SELECTION_NUMBER_OF;
1469     Geom::Point x(sp_export_value_get_px (base, "x0"),
1470                   sp_export_value_get_px (base, "y0"));
1471     Geom::Point y(sp_export_value_get_px (base, "x1"),
1472                   sp_export_value_get_px (base, "y1"));
1473     Geom::Rect current_bbox(x, y);
1474     //std::cout << "Current " << current_bbox;
1476     this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1477     for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1478         this_test[i + 1] = test_order[i];
1479     }
1481     for (int i = 0;
1482             i < SELECTION_NUMBER_OF + 1 &&
1483                 key == SELECTION_NUMBER_OF &&
1484                 SP_ACTIVE_DESKTOP != NULL;
1485             i++) {
1486         // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1487         switch (this_test[i]) {
1488             case SELECTION_SELECTION:
1489                 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1490                     Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(SPItem::RENDERING_BBOX);
1492                     //std::cout << "Selection " << bbox;
1493                     if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1494                         key = SELECTION_SELECTION;
1495                     }
1496                 }
1497                 break;
1498             case SELECTION_DRAWING: {
1499                 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1501                 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), SPItem::RENDERING_BBOX);
1503                 // std::cout << "Drawing " << bbox2;
1504                 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1505                     key = SELECTION_DRAWING;
1506                 }
1507                 break;
1508             }
1510             case SELECTION_PAGE: {
1511                 SPDocument *doc;
1513                 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1515                 Geom::Point x(0.0, 0.0);
1516                 Geom::Point y(sp_document_width(doc),
1517                               sp_document_height(doc));
1518                 Geom::Rect bbox(x, y);
1520                 // std::cout << "Page " << bbox;
1521                 if (sp_export_bbox_equal(bbox,current_bbox)) {
1522                     key = SELECTION_PAGE;
1523                 }
1525                 break;
1526            }
1527         default:
1528            break;
1529         }
1530     }
1531     // std::cout << std::endl;
1533     if (key == SELECTION_NUMBER_OF) {
1534         key = SELECTION_CUSTOM;
1535     }
1537     /* We're now using a custom size, not a fixed one */
1538     /* printf("Detecting state: %s\n", selection_names[key]); */
1539     selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1540     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1541     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1542     gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1544     return;
1545 } /* sp_export_detect_size */
1547 /// Called when area x0 value is changed
1548 static void
1549 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1551     float x0, x1, xdpi, width;
1553     if (gtk_object_get_data (base, "update"))
1554         return;
1556     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1557             (base, "units")))
1558     {
1559         return;
1560     }
1562     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1564     x0 = sp_export_value_get_px (base, "x0");
1565     x1 = sp_export_value_get_px (base, "x1");
1566     xdpi = sp_export_value_get (base, "xdpi");
1568     width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1570     if (width < SP_EXPORT_MIN_SIZE) {
1571         const gchar *key;
1572         width = SP_EXPORT_MIN_SIZE;
1573         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1575         if (!strcmp (key, "x0")) {
1576             x1 = x0 + width * DPI_BASE / xdpi;
1577             sp_export_value_set_px (base, "x1", x1);
1578         } else {
1579             x0 = x1 - width * DPI_BASE / xdpi;
1580             sp_export_value_set_px (base, "x0", x0);
1581         }
1582     }
1584     sp_export_value_set_px (base, "width", x1 - x0);
1585     sp_export_value_set (base, "bmwidth", width);
1587     sp_export_detect_size(base);
1589     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1591     return;
1592 } // end of sp_export_area_x_value_changed()
1594 /// Called when area y0 value is changed.
1595 static void
1596 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1598     float y0, y1, ydpi, height;
1600     if (gtk_object_get_data (base, "update"))
1601         return;
1603     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1604            (base, "units")))
1605     {
1606         return;
1607     }
1609     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1611     y0 = sp_export_value_get_px (base, "y0");
1612     y1 = sp_export_value_get_px (base, "y1");
1613     ydpi = sp_export_value_get (base, "ydpi");
1615     height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1617     if (height < SP_EXPORT_MIN_SIZE) {
1618         const gchar *key;
1619         height = SP_EXPORT_MIN_SIZE;
1620         key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1621         if (!strcmp (key, "y0")) {
1622             y1 = y0 + height * DPI_BASE / ydpi;
1623             sp_export_value_set_px (base, "y1", y1);
1624         } else {
1625             y0 = y1 - height * DPI_BASE / ydpi;
1626             sp_export_value_set_px (base, "y0", y0);
1627         }
1628     }
1630     sp_export_value_set_px (base, "height", y1 - y0);
1631     sp_export_value_set (base, "bmheight", height);
1633     sp_export_detect_size(base);
1635     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1637     return;
1638 } // end of sp_export_area_y_value_changed()
1640 /// Called when x1-x0 or area width is changed
1641 static void
1642 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1644     float x0, x1, xdpi, width, bmwidth;
1646     if (gtk_object_get_data (base, "update"))
1647         return;
1649     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1650            (base, "units"))) {
1651         return;
1652     }
1654     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1656     x0 = sp_export_value_get_px (base, "x0");
1657     x1 = sp_export_value_get_px (base, "x1");
1658     xdpi = sp_export_value_get (base, "xdpi");
1659     width = sp_export_value_get_px (base, "width");
1660     bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1662     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1664         bmwidth = SP_EXPORT_MIN_SIZE;
1665         width = bmwidth * DPI_BASE / xdpi;
1666         sp_export_value_set_px (base, "width", width);
1667     }
1669     sp_export_value_set_px (base, "x1", x0 + width);
1670     sp_export_value_set (base, "bmwidth", bmwidth);
1672     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1674     return;
1675 } // end of sp_export_area_width_value_changed()
1677 /// Called when y1-y0 or area height is changed.
1678 static void
1679 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1682     float y0, y1, ydpi, height, bmheight;
1684     if (gtk_object_get_data (base, "update"))
1685         return;
1687     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1688            (base, "units"))) {
1689         return;
1690     }
1692     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1694     y0 = sp_export_value_get_px (base, "y0");
1695     y1 = sp_export_value_get_px (base, "y1");
1696     ydpi = sp_export_value_get (base, "ydpi");
1697     height = sp_export_value_get_px (base, "height");
1698     bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1700     if (bmheight < SP_EXPORT_MIN_SIZE) {
1701         bmheight = SP_EXPORT_MIN_SIZE;
1702         height = bmheight * DPI_BASE / ydpi;
1703         sp_export_value_set_px (base, "height", height);
1704     }
1706     sp_export_value_set_px (base, "y1", y0 + height);
1707     sp_export_value_set (base, "bmheight", bmheight);
1709     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1711     return;
1712 } // end of sp_export_area_height_value_changed()
1714 /**
1715     \brief  A function to set the ydpi
1716     \param  base  The export dialog
1718     This function grabs all of the y values and then figures out the
1719     new bitmap size based on the changing dpi value.  The dpi value is
1720     gotten from the xdpi setting as these can not currently be independent.
1721 */
1722 static void
1723 sp_export_set_image_y (GtkObject *base)
1725     float y0, y1, xdpi;
1727     y0 = sp_export_value_get_px (base, "y0");
1728     y1 = sp_export_value_get_px (base, "y1");
1729     xdpi = sp_export_value_get (base, "xdpi");
1731     sp_export_value_set (base, "ydpi", xdpi);
1732     sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1734     return;
1735 } // end of sp_export_set_image_y()
1737 /**
1738     \brief  A function to set the xdpi
1739     \param  base  The export dialog
1741     This function grabs all of the x values and then figures out the
1742     new bitmap size based on the changing dpi value.  The dpi value is
1743     gotten from the xdpi setting as these can not currently be independent.
1744 */
1745 static void
1746 sp_export_set_image_x (GtkObject *base)
1748     float x0, x1, xdpi;
1750     x0 = sp_export_value_get_px (base, "x0");
1751     x1 = sp_export_value_get_px (base, "x1");
1752     xdpi = sp_export_value_get (base, "xdpi");
1754     sp_export_value_set (base, "ydpi", xdpi);
1755     sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1757     return;
1758 } // end of sp_export_set_image_x()
1760 /// Called when pixel width is changed
1761 static void
1762 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1764     float x0, x1, bmwidth, xdpi;
1766     if (gtk_object_get_data (base, "update"))
1767         return;
1769     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1770            (base, "units"))) {
1771        return;
1772     }
1774     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1776     x0 = sp_export_value_get_px (base, "x0");
1777     x1 = sp_export_value_get_px (base, "x1");
1778     bmwidth = sp_export_value_get (base, "bmwidth");
1780     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1781         bmwidth = SP_EXPORT_MIN_SIZE;
1782         sp_export_value_set (base, "bmwidth", bmwidth);
1783     }
1785     xdpi = bmwidth * DPI_BASE / (x1 - x0);
1786     sp_export_value_set (base, "xdpi", xdpi);
1788     sp_export_set_image_y (base);
1790     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1792     return;
1793 } // end of sp_export_bitmap_width_value_changed()
1795 /// Called when pixel height is changed
1796 static void
1797 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1799     float y0, y1, bmheight, xdpi;
1801     if (gtk_object_get_data (base, "update"))
1802         return;
1804     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1805            (base, "units"))) {
1806        return;
1807     }
1809     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1811     y0 = sp_export_value_get_px (base, "y0");
1812     y1 = sp_export_value_get_px (base, "y1");
1813     bmheight = sp_export_value_get (base, "bmheight");
1815     if (bmheight < SP_EXPORT_MIN_SIZE) {
1816         bmheight = SP_EXPORT_MIN_SIZE;
1817         sp_export_value_set (base, "bmheight", bmheight);
1818     }
1820     xdpi = bmheight * DPI_BASE / (y1 - y0);
1821     sp_export_value_set (base, "xdpi", xdpi);
1823     sp_export_set_image_x (base);
1825     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1827     return;
1828 } // end of sp_export_bitmap_width_value_changed()
1830 /**
1831     \brief  A function to adjust the bitmap width when the xdpi value changes
1832     \param  adj  The adjustment that was changed
1833     \param  base The export dialog itself
1835     The first thing this function checks is to see if we are doing an
1836     update.  If we are, this function just returns because there is another
1837     instance of it that will handle everything for us.  If there is a
1838     units change, we also assume that everyone is being updated appropriately
1839     and there is nothing for us to do.
1841     If we're the highest level function, we set the update flag, and
1842     continue on our way.
1844     All of the values are grabbed using the \c sp_export_value_get functions
1845     (call to the _pt ones for x0 and x1 but just standard for xdpi).  The
1846     xdpi value is saved in the preferences for the next time the dialog
1847     is opened.  (does the selection dpi need to be set here?)
1849     A check is done to to ensure that we aren't outputing an invalid width,
1850     this is set by SP_EXPORT_MIN_SIZE.  If that is the case the dpi is
1851     changed to make it valid.
1853     After all of this the bitmap width is changed.
1855     We also change the ydpi.  This is a temporary hack as these can not
1856     currently be independent.  This is likely to change in the future.
1857 */
1858 void
1859 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1861     float x0, x1, xdpi, bmwidth;
1863     if (gtk_object_get_data (base, "update"))
1864         return;
1866     if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1867            (base, "units"))) {
1868        return;
1869     }
1871     gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1873     x0 = sp_export_value_get_px (base, "x0");
1874     x1 = sp_export_value_get_px (base, "x1");
1875     xdpi = sp_export_value_get (base, "xdpi");
1877     // remember xdpi setting
1878     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1879     prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1881     bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1883     if (bmwidth < SP_EXPORT_MIN_SIZE) {
1884         bmwidth = SP_EXPORT_MIN_SIZE;
1885         if (x1 != x0)
1886             xdpi = bmwidth * DPI_BASE / (x1 - x0);
1887         else
1888             xdpi = DPI_BASE;
1889         sp_export_value_set (base, "xdpi", xdpi);
1890     }
1892     sp_export_value_set (base, "bmwidth", bmwidth);
1894     sp_export_set_image_y (base);
1896     gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1898     return;
1899 } // end of sp_export_xdpi_value_changed()
1902 /**
1903     \brief  A function to change the area that is used for the exported
1904             bitmap.
1905     \param  base  This is the export dialog
1906     \param  x0    Horizontal upper left hand corner of the picture in points
1907     \param  y0    Vertical upper left hand corner of the picture in points
1908     \param  x1    Horizontal lower right hand corner of the picture in points
1909     \param  y1    Vertical lower right hand corner of the picture in points
1911     This function just calls \c sp_export_value_set_px for each of the
1912     parameters that is passed in.  This allows for setting them all in
1913     one convient area.
1915     Update is set to suspend all of the other test running while all the
1916     values are being set up.  This allows for a performance increase, but
1917     it also means that the wrong type won't be detected with only some of
1918     the values set.  After all the values are set everyone is told that
1919     there has been an update.
1920 */
1921 static void
1922 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1924     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1925     sp_export_value_set_px (base, "x1", x1);
1926     sp_export_value_set_px (base, "y1", y1);
1927     sp_export_value_set_px (base, "x0", x0);
1928     sp_export_value_set_px (base, "y0", y0);
1929     gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1931     sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1932     sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1934     return;
1937 /**
1938     \brief  Sets the value of an adjustment
1939     \param  base  The export dialog
1940     \param  key   Which adjustment to set
1941     \param  val   What value to set it to
1943     This function finds the adjustment using the data stored in the
1944     export dialog.  After finding the adjustment it then sets
1945     the value of it.
1946 */
1947 static void
1948 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1950     GtkAdjustment *adj;
1952     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1954     gtk_adjustment_set_value (adj, val);
1957 /**
1958     \brief  A function to set a value using the units points
1959     \param  base  The export dialog
1960     \param  key   Which value should be set
1961     \param  val   What the value should be in points
1963     This function first gets the adjustment for the key that is passed
1964     in.  It then figures out what units are currently being used in the
1965     dialog.  After doing all of that, it then converts the incoming
1966     value and sets the adjustment.
1967 */
1968 static void
1969 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1971     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1973     sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1975     return;
1978 /**
1979     \brief  Get the value of an adjustment in the export dialog
1980     \param  base  The export dialog
1981     \param  key   Which adjustment is being looked for
1982     \return The value in the specified adjustment
1984     This function gets the adjustment from the data field in the export
1985     dialog.  It then grabs the value from the adjustment.
1986 */
1987 static float
1988 sp_export_value_get ( GtkObject *base, const gchar *key )
1990     GtkAdjustment *adj;
1992     adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1994     return adj->value;
1997 /**
1998     \brief  Grabs a value in the export dialog and converts the unit
1999             to points
2000     \param  base  The export dialog
2001     \param  key   Which value should be returned
2002     \return The value in the adjustment in points
2004     This function, at its most basic, is a call to \c sp_export_value_get
2005     to get the value of the adjustment.  It then finds the units that
2006     are being used by looking at the "units" attribute of the export
2007     dialog.  Using that it converts the returned value into points.
2008 */
2009 static float
2010 sp_export_value_get_px ( GtkObject *base, const gchar *key )
2012     float value = sp_export_value_get(base, key);
2013     const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
2015     return sp_units_get_pixels (value, *unit);
2016 } // end of sp_export_value_get_px()
2018 /**
2019     \brief  This function is called when the filename is changed by
2020             anyone.  It resets the virgin bit.
2021     \param  object  Text entry box
2022     \param  data    The export dialog
2023     \return None
2025     This function gets called when the text area is modified.  It is
2026     looking for the case where the text area is modified from its
2027     original value.  In that case it sets the "filename-modified" bit
2028     to TRUE.  If the text dialog returns back to the original text, the
2029     bit gets reset.  This should stop simple mistakes.
2030 */
2031 static void
2032 sp_export_filename_modified (GtkObject * object, gpointer data)
2034     GtkWidget * text_entry = (GtkWidget *)object;
2035     GtkWidget * export_dialog = (GtkWidget *)data;
2037     if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
2038         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
2039 //        printf("Modified: FALSE\n");
2040     } else {
2041         gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
2042 //        printf("Modified: TRUE\n");
2043     }
2045     return;
2046 } // end sp_export_filename_modified
2048 /*
2049   Local Variables:
2050   mode:c++
2051   c-file-style:"stroustrup"
2052   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2053   indent-tabs-mode:nil
2054   fill-column:99
2055   End:
2056 */
2057 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :