1 #define __SP_EXPORT_C__
3 /** \file
4 * \brief PNG export dialog
5 */
7 /*
8 * Authors:
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * bulia byak <buliabyak@users.sf.net>
11 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
12 *
13 * Copyright (C) 1999-2007 Authors
14 * Copyright (C) 2001-2002 Ximian, Inc.
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/buttonbox.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/togglebutton.h>
29 #include <gtkmm/entry.h>
30 #include <gtkmm/image.h>
31 #include <gtkmm/stockid.h>
32 #include <gtkmm/stock.h>
33 #ifdef WITH_GNOME_VFS
34 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
35 #endif
37 #include <glibmm/i18n.h>
38 #include "helper/unit-menu.h"
39 #include "helper/units.h"
40 #include "unit-constants.h"
41 #include "helper/window.h"
42 #include "inkscape-private.h"
43 #include "document.h"
44 #include "desktop-handles.h"
45 #include "sp-item.h"
46 #include "selection.h"
47 #include "file.h"
48 #include "macros.h"
49 #include "sp-namedview.h"
50 #include "selection-chemistry.h"
52 #include "dialog-events.h"
53 #include "preferences.h"
54 #include "verbs.h"
55 #include "interface.h"
57 #include "extension/output.h"
58 #include "extension/db.h"
60 #include "io/sys.h"
62 #include "helper/png-write.h"
63 #include <png.h>
66 #define SP_EXPORT_MIN_SIZE 1.0
68 #define DPI_BASE PX_PER_IN
70 #define EXPORT_COORD_PRECISION 3
72 #define MIN_ONSCREEN_DISTANCE 50
74 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
75 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
76 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
78 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
79 GtkObject *base);
81 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
82 GtkObject *base);
84 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
85 GtkObject *base);
87 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
88 GtkObject *base);
90 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
91 GtkObject *base);
93 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
94 GtkObject *base);
96 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
97 GtkObject *base);
99 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
100 Inkscape::Selection *selection,
101 GtkObject *base);
102 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
103 Inkscape::Selection *selection,
104 guint flags,
105 GtkObject *base );
107 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
108 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
109 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
110 static float sp_export_value_get ( GtkObject *base, const gchar *key );
111 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
113 static void sp_export_filename_modified (GtkObject * object, gpointer data);
114 static inline void sp_export_find_default_selection(GtkWidget * dlg);
115 static void sp_export_detect_size(GtkObject * base);
117 static const gchar *prefs_path = "dialogs.export";
119 // these all need to be reinitialized to their defaults during dialog_destroy
120 static GtkWidget *dlg = NULL;
121 static win_data wd;
122 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
123 static gchar * original_name = NULL;
124 static gchar * doc_export_name = NULL;
125 static bool was_empty = TRUE;
127 /** What type of button is being pressed */
128 enum selection_type {
129 SELECTION_PAGE = 0, /**< Export the whole page */
130 SELECTION_DRAWING, /**< Export everything drawn on the page */
131 SELECTION_SELECTION, /**< Export everything that is selected */
132 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
133 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
134 };
136 /** A list of strings that is used both in the preferences, and in the
137 data fields to describe the various values of \c selection_type. */
138 static const char * selection_names[SELECTION_NUMBER_OF] = {
139 "page", "drawing", "selection", "custom"};
141 /** The names on the buttons for the various selection types. */
142 static const char * selection_labels[SELECTION_NUMBER_OF] = {
143 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
145 static void
146 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
147 {
148 sp_signal_disconnect_by_data (INKSCAPE, dlg);
150 wd.win = dlg = NULL;
151 wd.stop = 0;
152 x = -1000; y = -1000; w = 0; h = 0;
153 g_free(original_name);
154 original_name = NULL;
155 g_free(doc_export_name);
156 doc_export_name = NULL;
157 was_empty = TRUE;
159 return;
160 } // end of sp_export_dialog_destroy()
162 /// Called when dialog is closed or inkscape is shut down.
163 static bool
164 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
165 {
167 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
168 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
170 if (x<0) x=0;
171 if (y<0) y=0;
173 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174 prefs->setInt(prefs_path, "x", x);
175 prefs->setInt(prefs_path, "y", y);
176 prefs->setInt(prefs_path, "w", w);
177 prefs->setInt(prefs_path, "h", h);
179 return FALSE; // which means, go ahead and destroy it
181 } // end of sp_export_dialog_delete()
183 /**
184 \brief Creates a new spin button for the export dialog
185 \param key The name of the spin button
186 \param val A default value for the spin button
187 \param min Minimum value for the spin button
188 \param max Maximum value for the spin button
189 \param step The step size for the spin button
190 \param page Size of the page increment
191 \param us Unit selector that effects this spin button
192 \param t Table to put the spin button in
193 \param x X location in the table \c t to start with
194 \param y Y location in the table \c t to start with
195 \param ll Text to put on the left side of the spin button (optional)
196 \param lr Text to put on the right side of the spin button (optional)
197 \param digits Number of digits to display after the decimal
198 \param sensitive Whether the spin button is sensitive or not
199 \param cb Callback for when this spin button is changed (optional)
200 \param dlg Export dialog the spin button is being placed in
202 */
203 static void
204 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
205 float step, float page, GtkWidget *us,
206 GtkWidget *t, int x, int y,
207 const gchar *ll, const gchar *lr,
208 int digits, unsigned int sensitive,
209 GCallback cb, GtkWidget *dlg )
210 {
211 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
212 gtk_object_set_data (a, "key", const_cast<gchar *>(key));
213 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
215 if (us) {
216 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
217 GTK_ADJUSTMENT (a) );
218 }
220 int pos = 0;
222 GtkWidget *l = NULL;
224 if (ll) {
226 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
227 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
228 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
229 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
230 gtk_widget_set_sensitive (l, sensitive);
231 pos += 1;
233 }
235 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
236 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
237 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
238 gtk_widget_set_size_request (sb, 80, -1);
239 gtk_widget_set_sensitive (sb, sensitive);
240 pos += 1;
242 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
244 if (lr) {
246 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
247 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
248 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
249 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
250 gtk_widget_set_sensitive (l, sensitive);
251 pos += 1;
253 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
254 }
256 if (cb)
257 gtk_signal_connect (a, "value_changed", cb, dlg);
259 return;
260 } // end of sp_export_spinbutton_new()
263 static Gtk::VBox *
264 sp_export_dialog_area_box (GtkWidget * dlg)
265 {
266 Gtk::VBox* vb = new Gtk::VBox(false, 3);
268 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
269 lbl->set_use_markup(true);
270 vb->pack_start(*lbl);
272 /* Units box */
273 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
274 /* gets added to the vbox later, but the unit selector is needed
275 earlier than that */
277 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
278 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
279 if (desktop)
280 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
281 unitbox->pack_end(*us, false, false, 0);
282 Gtk::Label* l = new Gtk::Label(_("Units:"));
283 unitbox->pack_end(*l, false, false, 3);
284 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
286 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
288 Gtk::ToggleButton* b;
289 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
290 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
291 b->set_data("key", GINT_TO_POINTER(i));
292 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
293 togglebox->pack_start(*b, false, true, 0);
294 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
295 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
296 }
298 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
299 G_CALLBACK (sp_export_selection_changed), dlg );
300 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
301 G_CALLBACK (sp_export_selection_modified), dlg );
302 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
303 G_CALLBACK (sp_export_selection_changed), dlg );
305 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
306 t->set_row_spacings (4);
307 t->set_col_spacings (4);
309 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
310 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
311 G_CALLBACK ( sp_export_area_x_value_changed),
312 dlg );
314 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
315 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
316 G_CALLBACK (sp_export_area_x_value_changed),
317 dlg );
319 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
320 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
321 G_CALLBACK
322 (sp_export_area_width_value_changed),
323 dlg );
325 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
326 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
327 G_CALLBACK (sp_export_area_y_value_changed),
328 dlg );
330 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
331 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
332 G_CALLBACK (sp_export_area_y_value_changed),
333 dlg );
335 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
336 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
337 G_CALLBACK (sp_export_area_height_value_changed),
338 dlg );
340 vb->pack_start(*togglebox, false, false, 3);
341 vb->pack_start(*t, false, false, 0);
342 vb->pack_start(*unitbox, false, false, 0);
344 return vb;
345 } // end of sp_export_dialog_area_box
348 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
350 if (id == NULL) /* This should never happen */
351 id = "bitmap";
353 gchar * directory = NULL;
355 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
356 // std::cout << "Directory from dialog" << std::endl;
357 directory = g_dirname(file_entry_text);
358 }
360 if (directory == NULL) {
361 /* Grab document directory */
362 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
363 // std::cout << "Directory from document" << std::endl;
364 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
365 }
366 }
368 if (directory == NULL) {
369 // std::cout << "Home Directory" << std::endl;
370 directory = homedir_path(NULL);
371 }
373 gchar * id_ext = g_strconcat(id, ".png", NULL);
374 gchar *filename = g_build_filename(directory, id_ext, NULL);
375 g_free(directory);
376 g_free(id_ext);
377 return filename;
378 }
380 static void
381 batch_export_clicked (GtkWidget *widget, GtkObject *base)
382 {
383 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
384 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
385 vb_singleexport->set_sensitive(false);
386 } else {
387 vb_singleexport->set_sensitive(true);
388 }
389 }
391 void
392 sp_export_dialog (void)
393 {
394 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
395 if (!dlg) {
397 gchar title[500];
398 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
400 dlg = sp_window_new (title, TRUE);
402 if (x == -1000 || y == -1000) {
403 x = prefs->getInt(prefs_path, "x", 0);
404 y = prefs->getInt(prefs_path, "y", 0);
405 }
407 if (w ==0 || h == 0) {
408 w = prefs->getInt(prefs_path, "w", 0);
409 h = prefs->getInt(prefs_path, "h", 0);
410 }
412 // if (x<0) x=0;
413 // if (y<0) y=0;
415 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
416 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
417 gtk_window_move ((GtkWindow *) dlg, x, y);
418 else
419 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
420 sp_transientize (dlg);
421 wd.win = dlg;
422 wd.stop = 0;
424 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
425 G_CALLBACK (sp_transientize_callback), &wd);
427 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
428 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
430 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
431 G_CALLBACK (sp_export_dialog_destroy), dlg);
433 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
434 G_CALLBACK (sp_export_dialog_delete), dlg);
436 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
437 G_CALLBACK (sp_export_dialog_delete), dlg);
439 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
440 G_CALLBACK (sp_dialog_hide), dlg);
442 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
443 G_CALLBACK (sp_dialog_unhide), dlg);
445 GtkTooltips *tt = gtk_tooltips_new();
447 Gtk::VBox *vb = new Gtk::VBox(false, 3);
448 vb->set_border_width(3);
449 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
451 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
452 vb_singleexport->set_border_width(0);
453 vb->pack_start(*vb_singleexport);
454 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
456 /* Export area frame */
457 {
458 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
459 area_box->set_border_width(3);
460 vb_singleexport->pack_start(*area_box, false, false, 0);
461 }
463 /* Bitmap size frame */
464 {
465 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
466 size_box->set_border_width(3);
468 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
469 lbl->set_use_markup(true);
470 size_box->pack_start(*lbl, false, false, 0);
471 const int rows = 2;
472 const int cols = 5;
473 const bool homogeneous = false;
474 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
475 t->set_row_spacings (4);
476 t->set_col_spacings (4);
477 size_box->pack_start(*t);
479 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
480 NULL, GTK_WIDGET(t->gobj()), 0, 0,
481 _("_Width:"), _("pixels at"), 0, 1,
482 G_CALLBACK
483 (sp_export_bitmap_width_value_changed),
484 dlg );
486 sp_export_spinbutton_new ( "xdpi",
487 prefs->getDouble("dialogs.export.defaultxdpi", "value", DPI_BASE),
488 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
489 NULL, _("dp_i"), 2, 1,
490 G_CALLBACK (sp_export_xdpi_value_changed),
491 dlg );
493 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
494 NULL, GTK_WIDGET(t->gobj()), 0, 1,
495 _("Height:"), _("pixels at"), 0, 1,
496 G_CALLBACK
497 (sp_export_bitmap_height_value_changed),
498 dlg );
500 /** \todo
501 * Needs fixing: there's no way to set ydpi currently, so we use
502 * the defaultxdpi value here, too...
503 */
504 sp_export_spinbutton_new ( "ydpi", prefs->getDouble("dialogs.export.defaultxdpi", "value", DPI_BASE),
505 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
506 NULL, _("dpi"), 2, 0, NULL, dlg );
508 vb_singleexport->pack_start(*size_box);
509 }
511 /* File entry */
512 {
513 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
514 file_box->set_border_width(3);
516 // true = has mnemonic
517 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
518 flabel->set_use_markup(true);
519 file_box->pack_start(*flabel, false, false, 0);
521 Gtk::Entry *fe = new Gtk::Entry();
523 /*
524 * set the default filename to be that of the current path + document
525 * with .png extension
526 *
527 * One thing to notice here is that this filename may get
528 * overwritten, but it won't happen here. The filename gets
529 * written into the text field, but then the button to select
530 * the area gets set. In that code the filename can be changed
531 * if there are some with presidence in the document. So, while
532 * this code sets the name first, it may not be the one users
533 * really see.
534 */
535 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
536 {
537 gchar *name;
538 SPDocument * doc = SP_ACTIVE_DOCUMENT;
539 const gchar *uri = SP_DOCUMENT_URI (doc);
540 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
541 const gchar * text_extension = repr->attribute("inkscape:output_extension");
542 Inkscape::Extension::Output * oextension = NULL;
544 if (text_extension != NULL) {
545 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
546 }
548 if (oextension != NULL) {
549 gchar * old_extension = oextension->get_extension();
550 if (g_str_has_suffix(uri, old_extension)) {
551 gchar * uri_copy;
552 gchar * extension_point;
553 gchar * final_name;
555 uri_copy = g_strdup(uri);
556 extension_point = g_strrstr(uri_copy, old_extension);
557 extension_point[0] = '\0';
559 final_name = g_strconcat(uri_copy, ".png", NULL);
560 fe->set_text(final_name);
562 g_free(final_name);
563 g_free(uri_copy);
564 }
565 } else {
566 name = g_strconcat(uri, ".png", NULL);
567 fe->set_text(name);
568 g_free(name);
569 }
571 doc_export_name = g_strdup(fe->get_text().c_str());
572 }
573 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
574 G_CALLBACK (sp_export_filename_modified), dlg);
576 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
578 {
579 // true = has mnemonic
580 Gtk::Button *b = new Gtk::Button();
582 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
583 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
584 Gtk::ICON_SIZE_BUTTON);
585 pixlabel->pack_start(*im);
587 Gtk::Label *l = new Gtk::Label();
588 l->set_markup_with_mnemonic(_("_Browse..."));
589 pixlabel->pack_start(*l);
591 b->add(*pixlabel);
593 hb->pack_end (*b, false, false, 4);
594 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
595 G_CALLBACK (sp_export_browse_clicked), NULL );
596 }
598 hb->pack_start (*fe, true, true, 0);
599 file_box->add(*hb);
600 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
601 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
602 original_name = g_strdup(fe->get_text().c_str());
603 // pressing enter in the filename field is the same as clicking export:
604 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
605 G_CALLBACK (sp_export_export_clicked), dlg );
606 // focus is in the filename initially:
607 fe->grab_focus();
609 // mnemonic in frame label moves focus to filename:
610 flabel->set_mnemonic_widget(*fe);
612 vb_singleexport->pack_start(*file_box);
613 }
615 {
616 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
617 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
618 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
619 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
620 batch_box->pack_start(*Glib::wrap(be), false, false);
621 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);
622 batch_box->show_all();
623 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
624 vb->pack_start(*batch_box);
625 }
627 {
628 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
629 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
630 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
631 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
632 hide_box->pack_start(*Glib::wrap(he), false, false);
633 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
634 hide_box->show_all();
635 vb->pack_start(*hide_box);
636 }
638 /* Buttons */
639 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
640 bb->set_border_width(3);
642 {
643 Gtk::Button *b = new Gtk::Button();
644 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
645 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
646 Gtk::ICON_SIZE_BUTTON);
647 image_label->pack_start(*im);
649 Gtk::Label *l = new Gtk::Label();
650 l->set_markup_with_mnemonic(_("_Export"));
651 image_label->pack_start(*l);
653 b->add(*image_label);
654 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
655 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
656 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
657 bb->pack_end(*b, false, false, 0);
658 }
660 vb->pack_end(*bb, false, false, 0);
661 vb->show_all();
663 } // end of if (!dlg)
665 sp_export_find_default_selection(dlg);
667 gtk_window_present ((GtkWindow *) dlg);
669 return;
670 } // end of sp_export_dialog()
672 static void
673 sp_export_update_checkbuttons (GtkObject *base)
674 {
675 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
676 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
677 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
678 if (num >= 2) {
679 gtk_widget_set_sensitive (be, true);
680 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
681 } else {
682 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
683 gtk_widget_set_sensitive (be, FALSE);
684 }
685 if (num > 0) {
686 gtk_widget_set_sensitive (he, true);
687 } else {
688 gtk_widget_set_sensitive (he, false);
689 }
690 }
692 static inline void
693 sp_export_find_default_selection(GtkWidget * dlg)
694 {
695 selection_type key = SELECTION_NUMBER_OF;
697 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
698 key = SELECTION_SELECTION;
699 }
701 /* Try using the preferences */
702 if (key == SELECTION_NUMBER_OF) {
703 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
704 int i = SELECTION_NUMBER_OF;
706 Glib::ustring what = prefs->getString("dialogs.export.exportarea", "value");
708 if (!what.empty()) {
709 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
710 if (what == selection_names[i]) {
711 break;
712 }
713 }
714 }
716 key = (selection_type)i;
717 }
719 if (key == SELECTION_NUMBER_OF) {
720 key = SELECTION_SELECTION;
721 }
723 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
724 selection_names[key]);
725 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
727 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
728 }
731 /**
732 * \brief If selection changed or a different document activated, we must
733 * recalculate any chosen areas
734 *
735 */
736 static void
737 sp_export_selection_changed ( Inkscape::Application *inkscape,
738 Inkscape::Selection *selection,
739 GtkObject *base )
740 {
741 selection_type current_key;
742 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
744 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
745 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
746 was_empty) {
747 gtk_toggle_button_set_active
748 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
749 TRUE );
750 }
751 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
753 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
755 if (inkscape &&
756 SP_IS_INKSCAPE (inkscape) &&
757 selection &&
758 SELECTION_CUSTOM != current_key) {
759 GtkToggleButton * button;
760 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
761 sp_export_area_toggled(button, base);
762 }
764 sp_export_update_checkbuttons (base);
765 }
767 static void
768 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
769 Inkscape::Selection */*selection*/,
770 guint /*flags*/,
771 GtkObject *base )
772 {
773 selection_type current_key;
774 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
776 switch (current_key) {
777 case SELECTION_DRAWING:
778 if ( SP_ACTIVE_DESKTOP ) {
779 SPDocument *doc;
780 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
781 boost::optional<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
782 if (bbox) {
783 sp_export_set_area (base, bbox->min()[NR::X],
784 bbox->min()[NR::Y],
785 bbox->max()[NR::X],
786 bbox->max()[NR::Y]);
787 }
788 }
789 break;
790 case SELECTION_SELECTION:
791 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
792 NRRect bbox;
793 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
794 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
795 }
796 break;
797 default:
798 /* Do nothing for page or for custom */
799 break;
800 }
802 return;
803 }
805 /// Called when one of the selection buttons was toggled.
806 static void
807 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
808 {
809 if (gtk_object_get_data (base, "update"))
810 return;
812 selection_type key, old_key;
813 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
814 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
816 /* Ignore all "turned off" events unless we're the only active button */
817 if (!gtk_toggle_button_get_active (tb) ) {
819 /* Don't let the current selection be deactived - but rerun the
820 activate to allow the user to renew the values */
821 if (key == old_key) {
822 gtk_toggle_button_set_active ( tb, TRUE );
823 }
825 return;
826 }
828 /* Turn off the currently active button unless it's us */
829 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
831 if (old_key != key) {
832 gtk_toggle_button_set_active
833 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
834 FALSE );
835 }
837 if ( SP_ACTIVE_DESKTOP )
838 {
839 SPDocument *doc;
840 boost::optional<NR::Rect> bbox;
841 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
843 /* Notice how the switch is used to 'fall through' here to get
844 various backups. If you modify this without noticing you'll
845 probabaly screw something up. */
846 switch (key) {
847 case SELECTION_SELECTION:
848 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
849 {
850 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
851 /* Only if there is a selection that we can set
852 do we break, otherwise we fall through to the
853 drawing */
854 // std::cout << "Using selection: SELECTION" << std::endl;
855 key = SELECTION_SELECTION;
856 break;
857 }
858 case SELECTION_DRAWING:
859 /** \todo
860 * This returns wrong values if the document has a viewBox.
861 */
862 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
863 /* If the drawing is valid, then we'll use it and break
864 otherwise we drop through to the page settings */
865 if (bbox) {
866 // std::cout << "Using selection: DRAWING" << std::endl;
867 key = SELECTION_DRAWING;
868 break;
869 }
870 case SELECTION_PAGE:
871 bbox = NR::Rect(NR::Point(0.0, 0.0),
872 NR::Point(sp_document_width(doc), sp_document_height(doc))
873 );
875 // std::cout << "Using selection: PAGE" << std::endl;
876 key = SELECTION_PAGE;
877 break;
878 case SELECTION_CUSTOM:
879 default:
880 break;
881 } // switch
883 // remember area setting
884 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
885 prefs->setString("dialogs.export.exportarea", "value", selection_names[key]);
887 if ( key != SELECTION_CUSTOM && bbox ) {
888 sp_export_set_area (base, bbox->min()[NR::X],
889 bbox->min()[NR::Y],
890 bbox->max()[NR::X],
891 bbox->max()[NR::Y]);
892 }
894 } // end of if ( SP_ACTIVE_DESKTOP )
897 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
898 GtkWidget * file_entry;
899 const gchar * filename = NULL;
900 float xdpi = 0.0, ydpi = 0.0;
902 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
904 switch (key) {
905 case SELECTION_PAGE:
906 case SELECTION_DRAWING: {
907 SPDocument * doc = SP_ACTIVE_DOCUMENT;
908 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
910 if (filename == NULL) {
911 if (doc_export_name != NULL) {
912 filename = g_strdup(doc_export_name);
913 } else {
914 filename = g_strdup("");
915 }
916 }
917 break;
918 }
919 case SELECTION_SELECTION:
920 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
922 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
924 /* If we still don't have a filename -- let's build
925 one that's nice */
926 if (filename == NULL) {
927 const gchar * id = NULL;
928 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
929 for(; reprlst != NULL; reprlst = reprlst->next) {
930 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
931 if (repr->attribute("id")) {
932 id = repr->attribute("id");
933 break;
934 }
935 }
937 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
938 }
939 }
940 break;
941 case SELECTION_CUSTOM:
942 default:
943 break;
944 }
946 if (filename != NULL) {
947 g_free(original_name);
948 original_name = g_strdup(filename);
949 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
950 }
952 if (xdpi != 0.0) {
953 sp_export_value_set(base, "xdpi", xdpi);
954 }
956 /* These can't be separate, and setting x sets y, so for
957 now setting this is disabled. Hopefully it won't be in
958 the future */
959 if (FALSE && ydpi != 0.0) {
960 sp_export_value_set(base, "ydpi", ydpi);
961 }
962 }
964 return;
965 } // end of sp_export_area_toggled()
967 /// Called when dialog is deleted
968 static gint
969 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
970 {
971 g_object_set_data (base, "cancel", (gpointer) 1);
972 return TRUE;
973 } // end of sp_export_progress_delete()
975 /// Called when progress is cancelled
976 static void
977 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
978 {
979 g_object_set_data (base, "cancel", (gpointer) 1);
980 } // end of sp_export_progress_cancel()
982 /// Called for every progress iteration
983 static unsigned int
984 sp_export_progress_callback (float value, void *data)
985 {
986 GtkWidget *prg;
987 int evtcount;
989 if (g_object_get_data ((GObject *) data, "cancel"))
990 return FALSE;
992 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
993 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
995 evtcount = 0;
996 while ((evtcount < 16) && gdk_events_pending ()) {
997 gtk_main_iteration_do (FALSE);
998 evtcount += 1;
999 }
1001 gtk_main_iteration_do (FALSE);
1003 return TRUE;
1005 } // end of sp_export_progress_callback()
1007 GtkWidget *
1008 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1009 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1011 dlg = gtk_dialog_new ();
1012 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1013 prg = gtk_progress_bar_new ();
1014 sp_transientize (dlg);
1015 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1016 g_object_set_data ((GObject *) base, "progress", prg);
1018 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1020 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1021 GTK_PROGRESS_LEFT_TO_RIGHT);
1022 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1023 prg, FALSE, FALSE, 4 );
1024 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1025 GTK_STOCK_CANCEL,
1026 GTK_RESPONSE_CANCEL );
1028 g_signal_connect ( (GObject *) dlg, "delete_event",
1029 (GCallback) sp_export_progress_delete, base);
1030 g_signal_connect ( (GObject *) btn, "clicked",
1031 (GCallback) sp_export_progress_cancel, base);
1032 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1033 gtk_widget_show_all (dlg);
1035 return dlg;
1036 }
1038 // FIXME: Some lib function should be available to do this ...
1039 static gchar *
1040 filename_add_extension (const gchar *filename, const gchar *extension)
1041 {
1042 gchar *dot;
1044 dot = strrchr (filename, '.');
1045 if ( !dot )
1046 return g_strconcat (filename, ".", extension, NULL);
1047 {
1048 if (dot[1] == '\0')
1049 return g_strconcat (filename, extension, NULL);
1050 else
1051 {
1052 if (g_strcasecmp (dot + 1, extension) == 0)
1053 return g_strdup (filename);
1054 else
1055 {
1056 return g_strconcat (filename, ".", extension, NULL);
1057 }
1058 }
1059 }
1060 }
1062 /// Called when export button is clicked
1063 static void
1064 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1065 {
1066 if (!SP_ACTIVE_DESKTOP) return;
1068 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1070 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1071 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1072 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1073 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1074 // Batch export of selected objects
1076 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1077 gint n = 0;
1079 if (num < 1)
1080 return;
1082 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1083 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1084 g_free (progress_text);
1086 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1087 i != NULL;
1088 i = i->next) {
1089 SPItem *item = (SPItem *) i->data;
1090 // retrieve export filename hint
1091 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1092 if (!fn) {
1093 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1094 }
1096 // retrieve export dpi hints
1097 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1098 gdouble dpi = 0.0;
1099 if (dpi_hint) {
1100 dpi = atof(dpi_hint);
1101 }
1102 if (dpi == 0.0) {
1103 dpi = DPI_BASE;
1104 }
1106 NRRect area;
1107 sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1109 gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1110 gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1112 if (width > 1 && height > 1) {
1113 /* Do export */
1114 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1115 area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1116 nv->pagecolor,
1117 NULL, NULL, TRUE, // overwrite without asking
1118 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1119 )) {
1120 gchar * error;
1121 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1122 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1123 sp_ui_error_dialog(error);
1124 g_free(safeFile);
1125 g_free(error);
1126 }
1127 }
1128 n++;
1129 sp_export_progress_callback((float)n/num, base);
1130 }
1132 gtk_widget_destroy (prog_dlg);
1133 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1135 } else {
1137 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1138 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1140 float const x0 = sp_export_value_get_px(base, "x0");
1141 float const y0 = sp_export_value_get_px(base, "y0");
1142 float const x1 = sp_export_value_get_px(base, "x1");
1143 float const y1 = sp_export_value_get_px(base, "y1");
1144 float const xdpi = sp_export_value_get(base, "xdpi");
1145 float const ydpi = sp_export_value_get(base, "ydpi");
1146 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1147 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1149 if (filename == NULL || *filename == '\0') {
1150 sp_ui_error_dialog(_("You have to enter a filename"));
1151 return;
1152 }
1154 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1155 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1156 return;
1157 }
1159 gchar *dirname = g_path_get_dirname(filename);
1160 if ( dirname == NULL
1161 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1162 {
1163 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1164 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1165 safeDir);
1166 sp_ui_error_dialog(error);
1167 g_free(safeDir);
1168 g_free(error);
1169 g_free(dirname);
1170 return;
1171 }
1172 g_free(dirname);
1174 // make sure that .png is the extension of the file:
1175 gchar * filename_ext = filename_add_extension(filename, "png");
1176 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1178 gchar *fn = g_path_get_basename (filename_ext);
1180 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1181 g_free (fn);
1182 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1183 g_free (progress_text);
1185 /* Do export */
1186 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1187 x0, y0, x1, y1, width, height, xdpi, ydpi,
1188 nv->pagecolor,
1189 sp_export_progress_callback, base, FALSE,
1190 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1191 )) {
1192 gchar * error;
1193 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1194 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1195 sp_ui_error_dialog(error);
1196 g_free(safeFile);
1197 g_free(error);
1198 }
1200 /* Reset the filename so that it can be changed again by changing
1201 selections and all that */
1202 g_free(original_name);
1203 original_name = g_strdup(filename_ext);
1204 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1206 gtk_widget_destroy (prog_dlg);
1207 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1209 /* Setup the values in the document */
1210 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1211 case SELECTION_PAGE:
1212 case SELECTION_DRAWING: {
1213 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1214 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1215 bool modified = false;
1216 const gchar * temp_string;
1218 bool saved = sp_document_get_undo_sensitive(doc);
1219 sp_document_set_undo_sensitive(doc, false);
1221 temp_string = repr->attribute("inkscape:export-filename");
1222 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1223 repr->setAttribute("inkscape:export-filename", filename_ext);
1224 modified = true;
1225 }
1226 temp_string = repr->attribute("inkscape:export-xdpi");
1227 if (temp_string == NULL || xdpi != atof(temp_string)) {
1228 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1229 modified = true;
1230 }
1231 temp_string = repr->attribute("inkscape:export-ydpi");
1232 if (temp_string == NULL || xdpi != atof(temp_string)) {
1233 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1234 modified = true;
1235 }
1236 sp_document_set_undo_sensitive(doc, saved);
1238 if (modified) {
1239 doc->setModifiedSinceSave();
1240 }
1241 break;
1242 }
1243 case SELECTION_SELECTION: {
1244 const GSList * reprlst;
1245 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1246 bool modified = false;
1248 bool saved = sp_document_get_undo_sensitive(doc);
1249 sp_document_set_undo_sensitive(doc, false);
1250 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1252 for(; reprlst != NULL; reprlst = reprlst->next) {
1253 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1254 const gchar * temp_string;
1256 if (repr->attribute("id") == NULL ||
1257 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1258 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1259 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1260 temp_string = repr->attribute("inkscape:export-filename");
1261 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1262 repr->setAttribute("inkscape:export-filename", filename_ext);
1263 modified = true;
1264 }
1265 }
1266 temp_string = repr->attribute("inkscape:export-xdpi");
1267 if (temp_string == NULL || xdpi != atof(temp_string)) {
1268 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1269 modified = true;
1270 }
1271 temp_string = repr->attribute("inkscape:export-ydpi");
1272 if (temp_string == NULL || xdpi != atof(temp_string)) {
1273 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1274 modified = true;
1275 }
1276 }
1277 sp_document_set_undo_sensitive(doc, saved);
1279 if (modified) {
1280 doc->setModifiedSinceSave();
1281 }
1282 break;
1283 }
1284 default:
1285 break;
1286 }
1288 g_free (filename_ext);
1290 }
1292 } // end of sp_export_export_clicked()
1294 /// Called when Browse button is clicked
1295 static void
1296 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1297 {
1298 GtkWidget *fs, *fe;
1299 const gchar *filename;
1301 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1302 (GtkWindow*)dlg,
1303 GTK_FILE_CHOOSER_ACTION_SAVE,
1304 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1305 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1306 NULL );
1308 #ifdef WITH_GNOME_VFS
1309 if (gnome_vfs_initialized()) {
1310 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1311 }
1312 #endif
1314 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1316 sp_transientize (fs);
1318 gtk_window_set_modal(GTK_WINDOW (fs), true);
1320 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1322 if (*filename == '\0') {
1323 filename = homedir_path(NULL);
1324 }
1326 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1328 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1329 {
1330 gchar *file;
1332 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1334 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1335 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1337 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1339 g_free(utf8file);
1340 g_free(file);
1341 }
1343 gtk_widget_destroy (fs);
1345 return;
1346 } // end of sp_export_browse_clicked()
1348 // TODO: Move this to nr-rect-fns.h.
1349 static bool
1350 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1351 {
1352 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1353 return (
1354 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1355 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1356 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1357 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1358 );
1359 }
1361 /**
1362 \brief This function is used to detect the current selection setting
1363 based on the values in the x0, y0, x1 and y0 fields.
1364 \param base The export dialog itself
1366 One of the most confusing parts of this function is why the array
1367 is built at the beginning. What needs to happen here is that we
1368 should always check the current selection to see if it is the valid
1369 one. While this is a performance improvement it is also a usability
1370 one during the cases where things like selections and drawings match
1371 size. This way buttons change less 'randomly' (atleast in the eyes
1372 of the user). To do this an array is built where the current selection
1373 type is placed first, and then the others in an order from smallest
1374 to largest (this can be configured by reshuffling \c test_order).
1376 All of the values in this function are rounded to two decimal places
1377 because that is what is shown to the user. While everything is kept
1378 more accurate than that, the user can't control more acurrate than
1379 that, so for this to work for them - it needs to check on that level
1380 of accuracy.
1382 \todo finish writing this up
1383 */
1384 static void
1385 sp_export_detect_size(GtkObject * base) {
1386 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1387 selection_type this_test[SELECTION_NUMBER_OF + 1];
1388 selection_type key = SELECTION_NUMBER_OF;
1390 NR::Point x(sp_export_value_get_px (base, "x0"),
1391 sp_export_value_get_px (base, "y0"));
1392 NR::Point y(sp_export_value_get_px (base, "x1"),
1393 sp_export_value_get_px (base, "y1"));
1394 NR::Rect current_bbox(x, y);
1395 //std::cout << "Current " << current_bbox;
1397 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1398 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1399 this_test[i + 1] = test_order[i];
1400 }
1402 for (int i = 0;
1403 i < SELECTION_NUMBER_OF + 1 &&
1404 key == SELECTION_NUMBER_OF &&
1405 SP_ACTIVE_DESKTOP != NULL;
1406 i++) {
1407 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1408 switch (this_test[i]) {
1409 case SELECTION_SELECTION:
1410 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1411 boost::optional<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1413 //std::cout << "Selection " << bbox;
1414 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1415 key = SELECTION_SELECTION;
1416 }
1417 }
1418 break;
1419 case SELECTION_DRAWING: {
1420 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1422 boost::optional<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1424 // std::cout << "Drawing " << bbox2;
1425 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1426 key = SELECTION_DRAWING;
1427 }
1428 break;
1429 }
1431 case SELECTION_PAGE: {
1432 SPDocument *doc;
1434 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1436 NR::Point x(0.0, 0.0);
1437 NR::Point y(sp_document_width(doc),
1438 sp_document_height(doc));
1439 NR::Rect bbox(x, y);
1441 // std::cout << "Page " << bbox;
1442 if (sp_export_bbox_equal(bbox,current_bbox)) {
1443 key = SELECTION_PAGE;
1444 }
1446 break;
1447 }
1448 default:
1449 break;
1450 }
1451 }
1452 // std::cout << std::endl;
1454 if (key == SELECTION_NUMBER_OF) {
1455 key = SELECTION_CUSTOM;
1456 }
1458 /* We're now using a custom size, not a fixed one */
1459 /* printf("Detecting state: %s\n", selection_names[key]); */
1460 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1461 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1462 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1463 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1465 return;
1466 } /* sp_export_detect_size */
1468 /// Called when area x0 value is changed
1469 static void
1470 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1471 {
1472 float x0, x1, xdpi, width;
1474 if (gtk_object_get_data (base, "update"))
1475 return;
1477 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1478 (base, "units")))
1479 {
1480 return;
1481 }
1483 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1485 x0 = sp_export_value_get_px (base, "x0");
1486 x1 = sp_export_value_get_px (base, "x1");
1487 xdpi = sp_export_value_get (base, "xdpi");
1489 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1491 if (width < SP_EXPORT_MIN_SIZE) {
1492 const gchar *key;
1493 width = SP_EXPORT_MIN_SIZE;
1494 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1496 if (!strcmp (key, "x0")) {
1497 x1 = x0 + width * DPI_BASE / xdpi;
1498 sp_export_value_set_px (base, "x1", x1);
1499 } else {
1500 x0 = x1 - width * DPI_BASE / xdpi;
1501 sp_export_value_set_px (base, "x0", x0);
1502 }
1503 }
1505 sp_export_value_set_px (base, "width", x1 - x0);
1506 sp_export_value_set (base, "bmwidth", width);
1508 sp_export_detect_size(base);
1510 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1512 return;
1513 } // end of sp_export_area_x_value_changed()
1515 /// Called when area y0 value is changed.
1516 static void
1517 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1518 {
1519 float y0, y1, ydpi, height;
1521 if (gtk_object_get_data (base, "update"))
1522 return;
1524 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1525 (base, "units")))
1526 {
1527 return;
1528 }
1530 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1532 y0 = sp_export_value_get_px (base, "y0");
1533 y1 = sp_export_value_get_px (base, "y1");
1534 ydpi = sp_export_value_get (base, "ydpi");
1536 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1538 if (height < SP_EXPORT_MIN_SIZE) {
1539 const gchar *key;
1540 height = SP_EXPORT_MIN_SIZE;
1541 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1542 if (!strcmp (key, "y0")) {
1543 y1 = y0 + height * DPI_BASE / ydpi;
1544 sp_export_value_set_px (base, "y1", y1);
1545 } else {
1546 y0 = y1 - height * DPI_BASE / ydpi;
1547 sp_export_value_set_px (base, "y0", y0);
1548 }
1549 }
1551 sp_export_value_set_px (base, "height", y1 - y0);
1552 sp_export_value_set (base, "bmheight", height);
1554 sp_export_detect_size(base);
1556 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1558 return;
1559 } // end of sp_export_area_y_value_changed()
1561 /// Called when x1-x0 or area width is changed
1562 static void
1563 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1564 {
1565 float x0, x1, xdpi, width, bmwidth;
1567 if (gtk_object_get_data (base, "update"))
1568 return;
1570 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1571 (base, "units"))) {
1572 return;
1573 }
1575 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1577 x0 = sp_export_value_get_px (base, "x0");
1578 x1 = sp_export_value_get_px (base, "x1");
1579 xdpi = sp_export_value_get (base, "xdpi");
1580 width = sp_export_value_get_px (base, "width");
1581 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1583 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1585 bmwidth = SP_EXPORT_MIN_SIZE;
1586 width = bmwidth * DPI_BASE / xdpi;
1587 sp_export_value_set_px (base, "width", width);
1588 }
1590 sp_export_value_set_px (base, "x1", x0 + width);
1591 sp_export_value_set (base, "bmwidth", bmwidth);
1593 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1595 return;
1596 } // end of sp_export_area_width_value_changed()
1598 /// Called when y1-y0 or area height is changed.
1599 static void
1600 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1601 {
1603 float y0, y1, ydpi, height, bmheight;
1605 if (gtk_object_get_data (base, "update"))
1606 return;
1608 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1609 (base, "units"))) {
1610 return;
1611 }
1613 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1615 y0 = sp_export_value_get_px (base, "y0");
1616 y1 = sp_export_value_get_px (base, "y1");
1617 ydpi = sp_export_value_get (base, "ydpi");
1618 height = sp_export_value_get_px (base, "height");
1619 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1621 if (bmheight < SP_EXPORT_MIN_SIZE) {
1622 bmheight = SP_EXPORT_MIN_SIZE;
1623 height = bmheight * DPI_BASE / ydpi;
1624 sp_export_value_set_px (base, "height", height);
1625 }
1627 sp_export_value_set_px (base, "y1", y0 + height);
1628 sp_export_value_set (base, "bmheight", bmheight);
1630 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1632 return;
1633 } // end of sp_export_area_height_value_changed()
1635 /**
1636 \brief A function to set the ydpi
1637 \param base The export dialog
1639 This function grabs all of the y values and then figures out the
1640 new bitmap size based on the changing dpi value. The dpi value is
1641 gotten from the xdpi setting as these can not currently be independent.
1642 */
1643 static void
1644 sp_export_set_image_y (GtkObject *base)
1645 {
1646 float y0, y1, xdpi;
1648 y0 = sp_export_value_get_px (base, "y0");
1649 y1 = sp_export_value_get_px (base, "y1");
1650 xdpi = sp_export_value_get (base, "xdpi");
1652 sp_export_value_set (base, "ydpi", xdpi);
1653 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1655 return;
1656 } // end of sp_export_set_image_y()
1658 /**
1659 \brief A function to set the xdpi
1660 \param base The export dialog
1662 This function grabs all of the x values and then figures out the
1663 new bitmap size based on the changing dpi value. The dpi value is
1664 gotten from the xdpi setting as these can not currently be independent.
1665 */
1666 static void
1667 sp_export_set_image_x (GtkObject *base)
1668 {
1669 float x0, x1, xdpi;
1671 x0 = sp_export_value_get_px (base, "x0");
1672 x1 = sp_export_value_get_px (base, "x1");
1673 xdpi = sp_export_value_get (base, "xdpi");
1675 sp_export_value_set (base, "ydpi", xdpi);
1676 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1678 return;
1679 } // end of sp_export_set_image_x()
1681 /// Called when pixel width is changed
1682 static void
1683 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1684 {
1685 float x0, x1, bmwidth, xdpi;
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 x0 = sp_export_value_get_px (base, "x0");
1698 x1 = sp_export_value_get_px (base, "x1");
1699 bmwidth = sp_export_value_get (base, "bmwidth");
1701 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1702 bmwidth = SP_EXPORT_MIN_SIZE;
1703 sp_export_value_set (base, "bmwidth", bmwidth);
1704 }
1706 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1707 sp_export_value_set (base, "xdpi", xdpi);
1709 sp_export_set_image_y (base);
1711 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1713 return;
1714 } // end of sp_export_bitmap_width_value_changed()
1716 /// Called when pixel height is changed
1717 static void
1718 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1719 {
1720 float y0, y1, bmheight, xdpi;
1722 if (gtk_object_get_data (base, "update"))
1723 return;
1725 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1726 (base, "units"))) {
1727 return;
1728 }
1730 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1732 y0 = sp_export_value_get_px (base, "y0");
1733 y1 = sp_export_value_get_px (base, "y1");
1734 bmheight = sp_export_value_get (base, "bmheight");
1736 if (bmheight < SP_EXPORT_MIN_SIZE) {
1737 bmheight = SP_EXPORT_MIN_SIZE;
1738 sp_export_value_set (base, "bmheight", bmheight);
1739 }
1741 xdpi = bmheight * DPI_BASE / (y1 - y0);
1742 sp_export_value_set (base, "xdpi", xdpi);
1744 sp_export_set_image_x (base);
1746 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1748 return;
1749 } // end of sp_export_bitmap_width_value_changed()
1751 /**
1752 \brief A function to adjust the bitmap width when the xdpi value changes
1753 \param adj The adjustment that was changed
1754 \param base The export dialog itself
1756 The first thing this function checks is to see if we are doing an
1757 update. If we are, this function just returns because there is another
1758 instance of it that will handle everything for us. If there is a
1759 units change, we also assume that everyone is being updated appropriately
1760 and there is nothing for us to do.
1762 If we're the highest level function, we set the update flag, and
1763 continue on our way.
1765 All of the values are grabbed using the \c sp_export_value_get functions
1766 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1767 xdpi value is saved in the preferences for the next time the dialog
1768 is opened. (does the selection dpi need to be set here?)
1770 A check is done to to ensure that we aren't outputing an invalid width,
1771 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1772 changed to make it valid.
1774 After all of this the bitmap width is changed.
1776 We also change the ydpi. This is a temporary hack as these can not
1777 currently be independent. This is likely to change in the future.
1778 */
1779 void
1780 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1781 {
1782 float x0, x1, xdpi, bmwidth;
1784 if (gtk_object_get_data (base, "update"))
1785 return;
1787 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1788 (base, "units"))) {
1789 return;
1790 }
1792 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1794 x0 = sp_export_value_get_px (base, "x0");
1795 x1 = sp_export_value_get_px (base, "x1");
1796 xdpi = sp_export_value_get (base, "xdpi");
1798 // remember xdpi setting
1799 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1800 prefs->setDouble("dialogs.export.defaultxdpi", "value", xdpi);
1802 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1804 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1805 bmwidth = SP_EXPORT_MIN_SIZE;
1806 if (x1 != x0)
1807 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1808 else
1809 xdpi = DPI_BASE;
1810 sp_export_value_set (base, "xdpi", xdpi);
1811 }
1813 sp_export_value_set (base, "bmwidth", bmwidth);
1815 sp_export_set_image_y (base);
1817 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1819 return;
1820 } // end of sp_export_xdpi_value_changed()
1823 /**
1824 \brief A function to change the area that is used for the exported
1825 bitmap.
1826 \param base This is the export dialog
1827 \param x0 Horizontal upper left hand corner of the picture in points
1828 \param y0 Vertical upper left hand corner of the picture in points
1829 \param x1 Horizontal lower right hand corner of the picture in points
1830 \param y1 Vertical lower right hand corner of the picture in points
1832 This function just calls \c sp_export_value_set_px for each of the
1833 parameters that is passed in. This allows for setting them all in
1834 one convient area.
1836 Update is set to suspend all of the other test running while all the
1837 values are being set up. This allows for a performance increase, but
1838 it also means that the wrong type won't be detected with only some of
1839 the values set. After all the values are set everyone is told that
1840 there has been an update.
1841 */
1842 static void
1843 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1844 {
1845 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1846 sp_export_value_set_px (base, "x1", x1);
1847 sp_export_value_set_px (base, "y1", y1);
1848 sp_export_value_set_px (base, "x0", x0);
1849 sp_export_value_set_px (base, "y0", y0);
1850 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1852 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1853 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1855 return;
1856 }
1858 /**
1859 \brief Sets the value of an adjustment
1860 \param base The export dialog
1861 \param key Which adjustment to set
1862 \param val What value to set it to
1864 This function finds the adjustment using the data stored in the
1865 export dialog. After finding the adjustment it then sets
1866 the value of it.
1867 */
1868 static void
1869 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1870 {
1871 GtkAdjustment *adj;
1873 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1875 gtk_adjustment_set_value (adj, val);
1876 }
1878 /**
1879 \brief A function to set a value using the units points
1880 \param base The export dialog
1881 \param key Which value should be set
1882 \param val What the value should be in points
1884 This function first gets the adjustment for the key that is passed
1885 in. It then figures out what units are currently being used in the
1886 dialog. After doing all of that, it then converts the incoming
1887 value and sets the adjustment.
1888 */
1889 static void
1890 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1891 {
1892 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1894 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1896 return;
1897 }
1899 /**
1900 \brief Get the value of an adjustment in the export dialog
1901 \param base The export dialog
1902 \param key Which adjustment is being looked for
1903 \return The value in the specified adjustment
1905 This function gets the adjustment from the data field in the export
1906 dialog. It then grabs the value from the adjustment.
1907 */
1908 static float
1909 sp_export_value_get ( GtkObject *base, const gchar *key )
1910 {
1911 GtkAdjustment *adj;
1913 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1915 return adj->value;
1916 }
1918 /**
1919 \brief Grabs a value in the export dialog and converts the unit
1920 to points
1921 \param base The export dialog
1922 \param key Which value should be returned
1923 \return The value in the adjustment in points
1925 This function, at its most basic, is a call to \c sp_export_value_get
1926 to get the value of the adjustment. It then finds the units that
1927 are being used by looking at the "units" attribute of the export
1928 dialog. Using that it converts the returned value into points.
1929 */
1930 static float
1931 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1932 {
1933 float value = sp_export_value_get(base, key);
1934 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1936 return sp_units_get_pixels (value, *unit);
1937 } // end of sp_export_value_get_px()
1939 /**
1940 \brief This function is called when the filename is changed by
1941 anyone. It resets the virgin bit.
1942 \param object Text entry box
1943 \param data The export dialog
1944 \return None
1946 This function gets called when the text area is modified. It is
1947 looking for the case where the text area is modified from its
1948 original value. In that case it sets the "filename-modified" bit
1949 to TRUE. If the text dialog returns back to the original text, the
1950 bit gets reset. This should stop simple mistakes.
1951 */
1952 static void
1953 sp_export_filename_modified (GtkObject * object, gpointer data)
1954 {
1955 GtkWidget * text_entry = (GtkWidget *)object;
1956 GtkWidget * export_dialog = (GtkWidget *)data;
1958 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1959 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1960 // printf("Modified: FALSE\n");
1961 } else {
1962 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1963 // printf("Modified: TRUE\n");
1964 }
1966 return;
1967 } // end sp_export_filename_modified
1969 /*
1970 Local Variables:
1971 mode:c++
1972 c-file-style:"stroustrup"
1973 c-file-offsets:((innamespace . 0)(inline-open . 0))
1974 indent-tabs-mode:nil
1975 fill-column:99
1976 End:
1977 */
1978 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :