0e2a08eb4c4442f3ae08ab55c1fedc54489d1e0d
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 "../prefs-utils.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 prefs_set_int_attribute (prefs_path, "x", x);
174 prefs_set_int_attribute (prefs_path, "y", y);
175 prefs_set_int_attribute (prefs_path, "w", w);
176 prefs_set_int_attribute (prefs_path, "h", h);
178 return FALSE; // which means, go ahead and destroy it
180 } // end of sp_export_dialog_delete()
182 /**
183 \brief Creates a new spin button for the export dialog
184 \param key The name of the spin button
185 \param val A default value for the spin button
186 \param min Minimum value for the spin button
187 \param max Maximum value for the spin button
188 \param step The step size for the spin button
189 \param page Size of the page increment
190 \param us Unit selector that effects this spin button
191 \param t Table to put the spin button in
192 \param x X location in the table \c t to start with
193 \param y Y location in the table \c t to start with
194 \param ll Text to put on the left side of the spin button (optional)
195 \param lr Text to put on the right side of the spin button (optional)
196 \param digits Number of digits to display after the decimal
197 \param sensitive Whether the spin button is sensitive or not
198 \param cb Callback for when this spin button is changed (optional)
199 \param dlg Export dialog the spin button is being placed in
201 */
202 static void
203 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
204 float step, float page, GtkWidget *us,
205 GtkWidget *t, int x, int y,
206 const gchar *ll, const gchar *lr,
207 int digits, unsigned int sensitive,
208 GCallback cb, GtkWidget *dlg )
209 {
210 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
211 gtk_object_set_data (a, "key", const_cast<gchar *>(key));
212 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
214 if (us) {
215 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
216 GTK_ADJUSTMENT (a) );
217 }
219 int pos = 0;
221 GtkWidget *l = NULL;
223 if (ll) {
225 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
226 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
227 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
228 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229 gtk_widget_set_sensitive (l, sensitive);
230 pos += 1;
232 }
234 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
235 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
236 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
237 gtk_widget_set_size_request (sb, 80, -1);
238 gtk_widget_set_sensitive (sb, sensitive);
239 pos += 1;
241 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
243 if (lr) {
245 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
246 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
247 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
248 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
249 gtk_widget_set_sensitive (l, sensitive);
250 pos += 1;
252 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
253 }
255 if (cb)
256 gtk_signal_connect (a, "value_changed", cb, dlg);
258 return;
259 } // end of sp_export_spinbutton_new()
262 static Gtk::VBox *
263 sp_export_dialog_area_box (GtkWidget * dlg)
264 {
265 Gtk::VBox* vb = new Gtk::VBox(false, 3);
267 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
268 lbl->set_use_markup(true);
269 vb->pack_start(*lbl);
271 /* Units box */
272 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
273 /* gets added to the vbox later, but the unit selector is needed
274 earlier than that */
276 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
277 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
278 if (desktop)
279 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
280 unitbox->pack_end(*us, false, false, 0);
281 Gtk::Label* l = new Gtk::Label(_("Units:"));
282 unitbox->pack_end(*l, false, false, 3);
283 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
285 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
287 Gtk::ToggleButton* b;
288 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
289 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
290 b->set_data("key", GINT_TO_POINTER(i));
291 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
292 togglebox->pack_start(*b, false, true, 0);
293 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
294 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
295 }
297 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
298 G_CALLBACK (sp_export_selection_changed), dlg );
299 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
300 G_CALLBACK (sp_export_selection_modified), dlg );
301 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
302 G_CALLBACK (sp_export_selection_changed), dlg );
304 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
305 t->set_row_spacings (4);
306 t->set_col_spacings (4);
308 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
309 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
310 G_CALLBACK ( sp_export_area_x_value_changed),
311 dlg );
313 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
314 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
315 G_CALLBACK (sp_export_area_x_value_changed),
316 dlg );
318 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
319 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
320 G_CALLBACK
321 (sp_export_area_width_value_changed),
322 dlg );
324 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
325 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
326 G_CALLBACK (sp_export_area_y_value_changed),
327 dlg );
329 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
330 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
331 G_CALLBACK (sp_export_area_y_value_changed),
332 dlg );
334 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
335 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
336 G_CALLBACK (sp_export_area_height_value_changed),
337 dlg );
339 vb->pack_start(*togglebox, false, false, 3);
340 vb->pack_start(*t, false, false, 0);
341 vb->pack_start(*unitbox, false, false, 0);
343 return vb;
344 } // end of sp_export_dialog_area_box
347 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
349 if (id == NULL) /* This should never happen */
350 id = "bitmap";
352 gchar * directory = NULL;
354 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
355 // std::cout << "Directory from dialog" << std::endl;
356 directory = g_dirname(file_entry_text);
357 }
359 if (directory == NULL) {
360 /* Grab document directory */
361 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
362 // std::cout << "Directory from document" << std::endl;
363 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
364 }
365 }
367 if (directory == NULL) {
368 // std::cout << "Home Directory" << std::endl;
369 directory = homedir_path(NULL);
370 }
372 gchar * id_ext = g_strconcat(id, ".png", NULL);
373 gchar *filename = g_build_filename(directory, id_ext, NULL);
374 g_free(directory);
375 g_free(id_ext);
376 return filename;
377 }
379 static void
380 batch_export_clicked (GtkWidget *widget, GtkObject *base)
381 {
382 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
383 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
384 vb_singleexport->set_sensitive(false);
385 } else {
386 vb_singleexport->set_sensitive(true);
387 }
388 }
390 void
391 sp_export_dialog (void)
392 {
393 if (!dlg) {
395 gchar title[500];
396 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
398 dlg = sp_window_new (title, TRUE);
400 if (x == -1000 || y == -1000) {
401 x = prefs_get_int_attribute (prefs_path, "x", 0);
402 y = prefs_get_int_attribute (prefs_path, "y", 0);
403 }
405 if (w ==0 || h == 0) {
406 w = prefs_get_int_attribute (prefs_path, "w", 0);
407 h = prefs_get_int_attribute (prefs_path, "h", 0);
408 }
410 // if (x<0) x=0;
411 // if (y<0) y=0;
413 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
414 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
415 gtk_window_move ((GtkWindow *) dlg, x, y);
416 else
417 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
418 sp_transientize (dlg);
419 wd.win = dlg;
420 wd.stop = 0;
422 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
423 G_CALLBACK (sp_transientize_callback), &wd);
425 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
426 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
428 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
429 G_CALLBACK (sp_export_dialog_destroy), dlg);
431 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
432 G_CALLBACK (sp_export_dialog_delete), dlg);
434 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
435 G_CALLBACK (sp_export_dialog_delete), dlg);
437 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
438 G_CALLBACK (sp_dialog_hide), dlg);
440 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
441 G_CALLBACK (sp_dialog_unhide), dlg);
443 GtkTooltips *tt = gtk_tooltips_new();
445 Gtk::VBox *vb = new Gtk::VBox(false, 3);
446 vb->set_border_width(3);
447 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
449 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
450 vb_singleexport->set_border_width(0);
451 vb->pack_start(*vb_singleexport);
452 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
454 /* Export area frame */
455 {
456 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
457 area_box->set_border_width(3);
458 vb_singleexport->pack_start(*area_box, false, false, 0);
459 }
461 /* Bitmap size frame */
462 {
463 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
464 size_box->set_border_width(3);
466 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
467 lbl->set_use_markup(true);
468 size_box->pack_start(*lbl, false, false, 0);
469 const int rows = 2;
470 const int cols = 5;
471 const bool homogeneous = false;
472 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
473 t->set_row_spacings (4);
474 t->set_col_spacings (4);
475 size_box->pack_start(*t);
477 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
478 NULL, GTK_WIDGET(t->gobj()), 0, 0,
479 _("_Width:"), _("pixels at"), 0, 1,
480 G_CALLBACK
481 (sp_export_bitmap_width_value_changed),
482 dlg );
484 sp_export_spinbutton_new ( "xdpi",
485 prefs_get_double_attribute
486 ( "dialogs.export.defaultxdpi",
487 "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_get_double_attribute
505 ( "dialogs.export.defaultxdpi",
506 "value", DPI_BASE),
507 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
508 NULL, _("dpi"), 2, 0, NULL, dlg );
510 vb_singleexport->pack_start(*size_box);
511 }
513 /* File entry */
514 {
515 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
516 file_box->set_border_width(3);
518 // true = has mnemonic
519 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
520 flabel->set_use_markup(true);
521 file_box->pack_start(*flabel, false, false, 0);
523 Gtk::Entry *fe = new Gtk::Entry();
525 /*
526 * set the default filename to be that of the current path + document
527 * with .png extension
528 *
529 * One thing to notice here is that this filename may get
530 * overwritten, but it won't happen here. The filename gets
531 * written into the text field, but then the button to select
532 * the area gets set. In that code the filename can be changed
533 * if there are some with presidence in the document. So, while
534 * this code sets the name first, it may not be the one users
535 * really see.
536 */
537 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
538 {
539 gchar *name;
540 SPDocument * doc = SP_ACTIVE_DOCUMENT;
541 const gchar *uri = SP_DOCUMENT_URI (doc);
542 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
543 const gchar * text_extension = repr->attribute("inkscape:output_extension");
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)
676 {
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 }
692 }
694 static inline void
695 sp_export_find_default_selection(GtkWidget * dlg)
696 {
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 const gchar *what = NULL;
706 int i = SELECTION_NUMBER_OF;
708 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
710 if (what != NULL) {
711 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
712 if (!strcmp (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));
730 }
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 )
742 {
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);
767 }
769 static void
770 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
771 Inkscape::Selection */*selection*/,
772 guint /*flags*/,
773 GtkObject *base )
774 {
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 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
784 if (bbox) {
785 sp_export_set_area (base, bbox->min()[NR::X],
786 bbox->min()[NR::Y],
787 bbox->max()[NR::X],
788 bbox->max()[NR::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);
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;
805 }
807 /// Called when one of the selection buttons was toggled.
808 static void
809 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
810 {
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 NR::Maybe<NR::Rect> 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();
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)));
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 = NR::Rect(NR::Point(0.0, 0.0),
874 NR::Point(sp_document_width(doc), sp_document_height(doc))
875 );
877 // std::cout << "Using selection: PAGE" << std::endl;
878 key = SELECTION_PAGE;
879 break;
880 case SELECTION_CUSTOM:
881 default:
882 break;
883 } // switch
885 // remember area setting
886 prefs_set_string_attribute ( "dialogs.export.exportarea",
887 "value", selection_names[key]);
889 if ( key != SELECTION_CUSTOM && bbox ) {
890 sp_export_set_area (base, bbox->min()[NR::X],
891 bbox->min()[NR::Y],
892 bbox->max()[NR::X],
893 bbox->max()[NR::Y]);
894 }
896 } // end of if ( SP_ACTIVE_DESKTOP )
899 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
900 GtkWidget * file_entry;
901 const gchar * filename = NULL;
902 float xdpi = 0.0, ydpi = 0.0;
904 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
906 switch (key) {
907 case SELECTION_PAGE:
908 case SELECTION_DRAWING: {
909 SPDocument * doc = SP_ACTIVE_DOCUMENT;
910 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
912 if (filename == NULL) {
913 if (doc_export_name != NULL) {
914 filename = g_strdup(doc_export_name);
915 } else {
916 filename = g_strdup("");
917 }
918 }
919 break;
920 }
921 case SELECTION_SELECTION:
922 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
924 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
926 /* If we still don't have a filename -- let's build
927 one that's nice */
928 if (filename == NULL) {
929 const gchar * id = NULL;
930 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
931 for(; reprlst != NULL; reprlst = reprlst->next) {
932 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
933 if (repr->attribute("id")) {
934 id = repr->attribute("id");
935 break;
936 }
937 }
939 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
940 }
941 }
942 break;
943 case SELECTION_CUSTOM:
944 default:
945 break;
946 }
948 if (filename != NULL) {
949 g_free(original_name);
950 original_name = g_strdup(filename);
951 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
952 }
954 if (xdpi != 0.0) {
955 sp_export_value_set(base, "xdpi", xdpi);
956 }
958 /* These can't be separate, and setting x sets y, so for
959 now setting this is disabled. Hopefully it won't be in
960 the future */
961 if (FALSE && ydpi != 0.0) {
962 sp_export_value_set(base, "ydpi", ydpi);
963 }
964 }
966 return;
967 } // end of sp_export_area_toggled()
969 /// Called when dialog is deleted
970 static gint
971 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
972 {
973 g_object_set_data (base, "cancel", (gpointer) 1);
974 return TRUE;
975 } // end of sp_export_progress_delete()
977 /// Called when progress is cancelled
978 static void
979 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
980 {
981 g_object_set_data (base, "cancel", (gpointer) 1);
982 } // end of sp_export_progress_cancel()
984 /// Called for every progress iteration
985 static unsigned int
986 sp_export_progress_callback (float value, void *data)
987 {
988 GtkWidget *prg;
989 int evtcount;
991 if (g_object_get_data ((GObject *) data, "cancel"))
992 return FALSE;
994 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
995 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
997 evtcount = 0;
998 while ((evtcount < 16) && gdk_events_pending ()) {
999 gtk_main_iteration_do (FALSE);
1000 evtcount += 1;
1001 }
1003 gtk_main_iteration_do (FALSE);
1005 return TRUE;
1007 } // end of sp_export_progress_callback()
1009 GtkWidget *
1010 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1011 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1013 dlg = gtk_dialog_new ();
1014 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1015 prg = gtk_progress_bar_new ();
1016 sp_transientize (dlg);
1017 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1018 g_object_set_data ((GObject *) base, "progress", prg);
1020 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1022 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1023 GTK_PROGRESS_LEFT_TO_RIGHT);
1024 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1025 prg, FALSE, FALSE, 4 );
1026 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1027 GTK_STOCK_CANCEL,
1028 GTK_RESPONSE_CANCEL );
1030 g_signal_connect ( (GObject *) dlg, "delete_event",
1031 (GCallback) sp_export_progress_delete, base);
1032 g_signal_connect ( (GObject *) btn, "clicked",
1033 (GCallback) sp_export_progress_cancel, base);
1034 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1035 gtk_widget_show_all (dlg);
1037 return dlg;
1038 }
1040 // FIXME: Some lib function should be available to do this ...
1041 static gchar *
1042 filename_add_extension (const gchar *filename, const gchar *extension)
1043 {
1044 gchar *dot;
1046 dot = strrchr (filename, '.');
1047 if ( !dot )
1048 return g_strconcat (filename, ".", extension, NULL);
1049 {
1050 if (dot[1] == '\0')
1051 return g_strconcat (filename, extension, NULL);
1052 else
1053 {
1054 if (g_strcasecmp (dot + 1, extension) == 0)
1055 return g_strdup (filename);
1056 else
1057 {
1058 return g_strconcat (filename, ".", extension, NULL);
1059 }
1060 }
1061 }
1062 }
1064 /// Called when export button is clicked
1065 static void
1066 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1067 {
1068 if (!SP_ACTIVE_DESKTOP) return;
1070 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1072 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1073 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1074 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1075 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1076 // Batch export of selected objects
1078 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1079 gint n = 0;
1081 if (num < 1)
1082 return;
1084 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1085 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1086 g_free (progress_text);
1088 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1089 i != NULL;
1090 i = i->next) {
1091 SPItem *item = (SPItem *) i->data;
1092 // retrieve export filename hint
1093 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1094 if (!fn) {
1095 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1096 }
1098 // retrieve export dpi hints
1099 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1100 gdouble dpi = 0.0;
1101 if (dpi_hint) {
1102 dpi = atof(dpi_hint);
1103 }
1104 if (dpi == 0.0) {
1105 dpi = DPI_BASE;
1106 }
1108 NRRect area;
1109 sp_item_invoke_bbox(item, &area, from_2geom(sp_item_i2r_affine((SPItem *) item)), TRUE);
1111 gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1112 gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1114 if (width > 1 && height > 1) {
1115 /* Do export */
1116 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1117 area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1118 nv->pagecolor,
1119 NULL, NULL, TRUE, // overwrite without asking
1120 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1121 )) {
1122 gchar * error;
1123 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1124 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1125 sp_ui_error_dialog(error);
1126 g_free(safeFile);
1127 g_free(error);
1128 }
1129 }
1130 n++;
1131 sp_export_progress_callback((float)n/num, base);
1132 }
1134 gtk_widget_destroy (prog_dlg);
1135 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1137 } else {
1139 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1140 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1142 float const x0 = sp_export_value_get_px(base, "x0");
1143 float const y0 = sp_export_value_get_px(base, "y0");
1144 float const x1 = sp_export_value_get_px(base, "x1");
1145 float const y1 = sp_export_value_get_px(base, "y1");
1146 float const xdpi = sp_export_value_get(base, "xdpi");
1147 float const ydpi = sp_export_value_get(base, "ydpi");
1148 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1149 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1151 if (filename == NULL || *filename == '\0') {
1152 sp_ui_error_dialog(_("You have to enter a filename"));
1153 return;
1154 }
1156 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1157 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1158 return;
1159 }
1161 gchar *dirname = g_path_get_dirname(filename);
1162 if ( dirname == NULL
1163 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1164 {
1165 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1166 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1167 safeDir);
1168 sp_ui_error_dialog(error);
1169 g_free(safeDir);
1170 g_free(error);
1171 g_free(dirname);
1172 return;
1173 }
1174 g_free(dirname);
1176 // make sure that .png is the extension of the file:
1177 gchar * filename_ext = filename_add_extension(filename, "png");
1178 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1180 gchar *fn = g_path_get_basename (filename_ext);
1182 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1183 g_free (fn);
1184 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1185 g_free (progress_text);
1187 /* Do export */
1188 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1189 x0, y0, x1, y1, width, height, xdpi, ydpi,
1190 nv->pagecolor,
1191 sp_export_progress_callback, base, FALSE,
1192 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1193 )) {
1194 gchar * error;
1195 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1196 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1197 sp_ui_error_dialog(error);
1198 g_free(safeFile);
1199 g_free(error);
1200 }
1202 /* Reset the filename so that it can be changed again by changing
1203 selections and all that */
1204 g_free(original_name);
1205 original_name = g_strdup(filename_ext);
1206 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1208 gtk_widget_destroy (prog_dlg);
1209 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1211 /* Setup the values in the document */
1212 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1213 case SELECTION_PAGE:
1214 case SELECTION_DRAWING: {
1215 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1216 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1217 bool modified = false;
1218 const gchar * temp_string;
1220 bool saved = sp_document_get_undo_sensitive(doc);
1221 sp_document_set_undo_sensitive(doc, false);
1223 temp_string = repr->attribute("inkscape:export-filename");
1224 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1225 repr->setAttribute("inkscape:export-filename", filename_ext);
1226 modified = true;
1227 }
1228 temp_string = repr->attribute("inkscape:export-xdpi");
1229 if (temp_string == NULL || xdpi != atof(temp_string)) {
1230 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1231 modified = true;
1232 }
1233 temp_string = repr->attribute("inkscape:export-ydpi");
1234 if (temp_string == NULL || xdpi != atof(temp_string)) {
1235 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1236 modified = true;
1237 }
1238 sp_document_set_undo_sensitive(doc, saved);
1240 if (modified) {
1241 doc->setModifiedSinceSave();
1242 }
1243 break;
1244 }
1245 case SELECTION_SELECTION: {
1246 const GSList * reprlst;
1247 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1248 bool modified = false;
1250 bool saved = sp_document_get_undo_sensitive(doc);
1251 sp_document_set_undo_sensitive(doc, false);
1252 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1254 for(; reprlst != NULL; reprlst = reprlst->next) {
1255 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1256 const gchar * temp_string;
1258 if (repr->attribute("id") == NULL ||
1259 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1260 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1261 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1262 temp_string = repr->attribute("inkscape:export-filename");
1263 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1264 repr->setAttribute("inkscape:export-filename", filename_ext);
1265 modified = true;
1266 }
1267 }
1268 temp_string = repr->attribute("inkscape:export-xdpi");
1269 if (temp_string == NULL || xdpi != atof(temp_string)) {
1270 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1271 modified = true;
1272 }
1273 temp_string = repr->attribute("inkscape:export-ydpi");
1274 if (temp_string == NULL || xdpi != atof(temp_string)) {
1275 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1276 modified = true;
1277 }
1278 }
1279 sp_document_set_undo_sensitive(doc, saved);
1281 if (modified) {
1282 doc->setModifiedSinceSave();
1283 }
1284 break;
1285 }
1286 default:
1287 break;
1288 }
1290 g_free (filename_ext);
1292 }
1294 } // end of sp_export_export_clicked()
1296 /// Called when Browse button is clicked
1297 static void
1298 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1299 {
1300 GtkWidget *fs, *fe;
1301 const gchar *filename;
1303 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1304 (GtkWindow*)dlg,
1305 GTK_FILE_CHOOSER_ACTION_SAVE,
1306 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1307 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1308 NULL );
1310 #ifdef WITH_GNOME_VFS
1311 if (gnome_vfs_initialized()) {
1312 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1313 }
1314 #endif
1316 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1318 sp_transientize (fs);
1320 gtk_window_set_modal(GTK_WINDOW (fs), true);
1322 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1324 if (*filename == '\0') {
1325 filename = homedir_path(NULL);
1326 }
1328 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1330 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1331 {
1332 gchar *file;
1334 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1336 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1337 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1339 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1341 g_free(utf8file);
1342 g_free(file);
1343 }
1345 gtk_widget_destroy (fs);
1347 return;
1348 } // end of sp_export_browse_clicked()
1350 // TODO: Move this to nr-rect-fns.h.
1351 static bool
1352 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1353 {
1354 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1355 return (
1356 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1357 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1358 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1359 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1360 );
1361 }
1363 /**
1364 \brief This function is used to detect the current selection setting
1365 based on the values in the x0, y0, x1 and y0 fields.
1366 \param base The export dialog itself
1368 One of the most confusing parts of this function is why the array
1369 is built at the beginning. What needs to happen here is that we
1370 should always check the current selection to see if it is the valid
1371 one. While this is a performance improvement it is also a usability
1372 one during the cases where things like selections and drawings match
1373 size. This way buttons change less 'randomly' (atleast in the eyes
1374 of the user). To do this an array is built where the current selection
1375 type is placed first, and then the others in an order from smallest
1376 to largest (this can be configured by reshuffling \c test_order).
1378 All of the values in this function are rounded to two decimal places
1379 because that is what is shown to the user. While everything is kept
1380 more accurate than that, the user can't control more acurrate than
1381 that, so for this to work for them - it needs to check on that level
1382 of accuracy.
1384 \todo finish writing this up
1385 */
1386 static void
1387 sp_export_detect_size(GtkObject * base) {
1388 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1389 selection_type this_test[SELECTION_NUMBER_OF + 1];
1390 selection_type key = SELECTION_NUMBER_OF;
1392 NR::Point x(sp_export_value_get_px (base, "x0"),
1393 sp_export_value_get_px (base, "y0"));
1394 NR::Point y(sp_export_value_get_px (base, "x1"),
1395 sp_export_value_get_px (base, "y1"));
1396 NR::Rect current_bbox(x, y);
1397 //std::cout << "Current " << current_bbox;
1399 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1400 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1401 this_test[i + 1] = test_order[i];
1402 }
1404 for (int i = 0;
1405 i < SELECTION_NUMBER_OF + 1 &&
1406 key == SELECTION_NUMBER_OF &&
1407 SP_ACTIVE_DESKTOP != NULL;
1408 i++) {
1409 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1410 switch (this_test[i]) {
1411 case SELECTION_SELECTION:
1412 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1413 NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1415 //std::cout << "Selection " << bbox;
1416 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1417 key = SELECTION_SELECTION;
1418 }
1419 }
1420 break;
1421 case SELECTION_DRAWING: {
1422 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1424 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1426 // std::cout << "Drawing " << bbox2;
1427 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1428 key = SELECTION_DRAWING;
1429 }
1430 break;
1431 }
1433 case SELECTION_PAGE: {
1434 SPDocument *doc;
1436 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1438 NR::Point x(0.0, 0.0);
1439 NR::Point y(sp_document_width(doc),
1440 sp_document_height(doc));
1441 NR::Rect bbox(x, y);
1443 // std::cout << "Page " << bbox;
1444 if (sp_export_bbox_equal(bbox,current_bbox)) {
1445 key = SELECTION_PAGE;
1446 }
1448 break;
1449 }
1450 default:
1451 break;
1452 }
1453 }
1454 // std::cout << std::endl;
1456 if (key == SELECTION_NUMBER_OF) {
1457 key = SELECTION_CUSTOM;
1458 }
1460 /* We're now using a custom size, not a fixed one */
1461 /* printf("Detecting state: %s\n", selection_names[key]); */
1462 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1463 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1464 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1465 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1467 return;
1468 } /* sp_export_detect_size */
1470 /// Called when area x0 value is changed
1471 static void
1472 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1473 {
1474 float x0, x1, xdpi, width;
1476 if (gtk_object_get_data (base, "update"))
1477 return;
1479 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1480 (base, "units")))
1481 {
1482 return;
1483 }
1485 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1487 x0 = sp_export_value_get_px (base, "x0");
1488 x1 = sp_export_value_get_px (base, "x1");
1489 xdpi = sp_export_value_get (base, "xdpi");
1491 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1493 if (width < SP_EXPORT_MIN_SIZE) {
1494 const gchar *key;
1495 width = SP_EXPORT_MIN_SIZE;
1496 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1498 if (!strcmp (key, "x0")) {
1499 x1 = x0 + width * DPI_BASE / xdpi;
1500 sp_export_value_set_px (base, "x1", x1);
1501 } else {
1502 x0 = x1 - width * DPI_BASE / xdpi;
1503 sp_export_value_set_px (base, "x0", x0);
1504 }
1505 }
1507 sp_export_value_set_px (base, "width", x1 - x0);
1508 sp_export_value_set (base, "bmwidth", width);
1510 sp_export_detect_size(base);
1512 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1514 return;
1515 } // end of sp_export_area_x_value_changed()
1517 /// Called when area y0 value is changed.
1518 static void
1519 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1520 {
1521 float y0, y1, ydpi, height;
1523 if (gtk_object_get_data (base, "update"))
1524 return;
1526 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1527 (base, "units")))
1528 {
1529 return;
1530 }
1532 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1534 y0 = sp_export_value_get_px (base, "y0");
1535 y1 = sp_export_value_get_px (base, "y1");
1536 ydpi = sp_export_value_get (base, "ydpi");
1538 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1540 if (height < SP_EXPORT_MIN_SIZE) {
1541 const gchar *key;
1542 height = SP_EXPORT_MIN_SIZE;
1543 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1544 if (!strcmp (key, "y0")) {
1545 y1 = y0 + height * DPI_BASE / ydpi;
1546 sp_export_value_set_px (base, "y1", y1);
1547 } else {
1548 y0 = y1 - height * DPI_BASE / ydpi;
1549 sp_export_value_set_px (base, "y0", y0);
1550 }
1551 }
1553 sp_export_value_set_px (base, "height", y1 - y0);
1554 sp_export_value_set (base, "bmheight", height);
1556 sp_export_detect_size(base);
1558 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1560 return;
1561 } // end of sp_export_area_y_value_changed()
1563 /// Called when x1-x0 or area width is changed
1564 static void
1565 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1566 {
1567 float x0, x1, xdpi, width, bmwidth;
1569 if (gtk_object_get_data (base, "update"))
1570 return;
1572 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1573 (base, "units"))) {
1574 return;
1575 }
1577 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1579 x0 = sp_export_value_get_px (base, "x0");
1580 x1 = sp_export_value_get_px (base, "x1");
1581 xdpi = sp_export_value_get (base, "xdpi");
1582 width = sp_export_value_get_px (base, "width");
1583 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1585 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1587 bmwidth = SP_EXPORT_MIN_SIZE;
1588 width = bmwidth * DPI_BASE / xdpi;
1589 sp_export_value_set_px (base, "width", width);
1590 }
1592 sp_export_value_set_px (base, "x1", x0 + width);
1593 sp_export_value_set (base, "bmwidth", bmwidth);
1595 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1597 return;
1598 } // end of sp_export_area_width_value_changed()
1600 /// Called when y1-y0 or area height is changed.
1601 static void
1602 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1603 {
1605 float y0, y1, ydpi, height, bmheight;
1607 if (gtk_object_get_data (base, "update"))
1608 return;
1610 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1611 (base, "units"))) {
1612 return;
1613 }
1615 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1617 y0 = sp_export_value_get_px (base, "y0");
1618 y1 = sp_export_value_get_px (base, "y1");
1619 ydpi = sp_export_value_get (base, "ydpi");
1620 height = sp_export_value_get_px (base, "height");
1621 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1623 if (bmheight < SP_EXPORT_MIN_SIZE) {
1624 bmheight = SP_EXPORT_MIN_SIZE;
1625 height = bmheight * DPI_BASE / ydpi;
1626 sp_export_value_set_px (base, "height", height);
1627 }
1629 sp_export_value_set_px (base, "y1", y0 + height);
1630 sp_export_value_set (base, "bmheight", bmheight);
1632 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1634 return;
1635 } // end of sp_export_area_height_value_changed()
1637 /**
1638 \brief A function to set the ydpi
1639 \param base The export dialog
1641 This function grabs all of the y values and then figures out the
1642 new bitmap size based on the changing dpi value. The dpi value is
1643 gotten from the xdpi setting as these can not currently be independent.
1644 */
1645 static void
1646 sp_export_set_image_y (GtkObject *base)
1647 {
1648 float y0, y1, xdpi;
1650 y0 = sp_export_value_get_px (base, "y0");
1651 y1 = sp_export_value_get_px (base, "y1");
1652 xdpi = sp_export_value_get (base, "xdpi");
1654 sp_export_value_set (base, "ydpi", xdpi);
1655 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1657 return;
1658 } // end of sp_export_set_image_y()
1660 /**
1661 \brief A function to set the xdpi
1662 \param base The export dialog
1664 This function grabs all of the x values and then figures out the
1665 new bitmap size based on the changing dpi value. The dpi value is
1666 gotten from the xdpi setting as these can not currently be independent.
1667 */
1668 static void
1669 sp_export_set_image_x (GtkObject *base)
1670 {
1671 float x0, x1, xdpi;
1673 x0 = sp_export_value_get_px (base, "x0");
1674 x1 = sp_export_value_get_px (base, "x1");
1675 xdpi = sp_export_value_get (base, "xdpi");
1677 sp_export_value_set (base, "ydpi", xdpi);
1678 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1680 return;
1681 } // end of sp_export_set_image_x()
1683 /// Called when pixel width is changed
1684 static void
1685 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1686 {
1687 float x0, x1, bmwidth, xdpi;
1689 if (gtk_object_get_data (base, "update"))
1690 return;
1692 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1693 (base, "units"))) {
1694 return;
1695 }
1697 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1699 x0 = sp_export_value_get_px (base, "x0");
1700 x1 = sp_export_value_get_px (base, "x1");
1701 bmwidth = sp_export_value_get (base, "bmwidth");
1703 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1704 bmwidth = SP_EXPORT_MIN_SIZE;
1705 sp_export_value_set (base, "bmwidth", bmwidth);
1706 }
1708 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1709 sp_export_value_set (base, "xdpi", xdpi);
1711 sp_export_set_image_y (base);
1713 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1715 return;
1716 } // end of sp_export_bitmap_width_value_changed()
1718 /// Called when pixel height is changed
1719 static void
1720 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1721 {
1722 float y0, y1, bmheight, xdpi;
1724 if (gtk_object_get_data (base, "update"))
1725 return;
1727 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1728 (base, "units"))) {
1729 return;
1730 }
1732 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1734 y0 = sp_export_value_get_px (base, "y0");
1735 y1 = sp_export_value_get_px (base, "y1");
1736 bmheight = sp_export_value_get (base, "bmheight");
1738 if (bmheight < SP_EXPORT_MIN_SIZE) {
1739 bmheight = SP_EXPORT_MIN_SIZE;
1740 sp_export_value_set (base, "bmheight", bmheight);
1741 }
1743 xdpi = bmheight * DPI_BASE / (y1 - y0);
1744 sp_export_value_set (base, "xdpi", xdpi);
1746 sp_export_set_image_x (base);
1748 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1750 return;
1751 } // end of sp_export_bitmap_width_value_changed()
1753 /**
1754 \brief A function to adjust the bitmap width when the xdpi value changes
1755 \param adj The adjustment that was changed
1756 \param base The export dialog itself
1758 The first thing this function checks is to see if we are doing an
1759 update. If we are, this function just returns because there is another
1760 instance of it that will handle everything for us. If there is a
1761 units change, we also assume that everyone is being updated appropriately
1762 and there is nothing for us to do.
1764 If we're the highest level function, we set the update flag, and
1765 continue on our way.
1767 All of the values are grabbed using the \c sp_export_value_get functions
1768 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1769 xdpi value is saved in the preferences for the next time the dialog
1770 is opened. (does the selection dpi need to be set here?)
1772 A check is done to to ensure that we aren't outputing an invalid width,
1773 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1774 changed to make it valid.
1776 After all of this the bitmap width is changed.
1778 We also change the ydpi. This is a temporary hack as these can not
1779 currently be independent. This is likely to change in the future.
1780 */
1781 void
1782 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1783 {
1784 float x0, x1, xdpi, bmwidth;
1786 if (gtk_object_get_data (base, "update"))
1787 return;
1789 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1790 (base, "units"))) {
1791 return;
1792 }
1794 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1796 x0 = sp_export_value_get_px (base, "x0");
1797 x1 = sp_export_value_get_px (base, "x1");
1798 xdpi = sp_export_value_get (base, "xdpi");
1800 // remember xdpi setting
1801 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1803 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1805 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1806 bmwidth = SP_EXPORT_MIN_SIZE;
1807 if (x1 != x0)
1808 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1809 else
1810 xdpi = DPI_BASE;
1811 sp_export_value_set (base, "xdpi", xdpi);
1812 }
1814 sp_export_value_set (base, "bmwidth", bmwidth);
1816 sp_export_set_image_y (base);
1818 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1820 return;
1821 } // end of sp_export_xdpi_value_changed()
1824 /**
1825 \brief A function to change the area that is used for the exported
1826 bitmap.
1827 \param base This is the export dialog
1828 \param x0 Horizontal upper left hand corner of the picture in points
1829 \param y0 Vertical upper left hand corner of the picture in points
1830 \param x1 Horizontal lower right hand corner of the picture in points
1831 \param y1 Vertical lower right hand corner of the picture in points
1833 This function just calls \c sp_export_value_set_px for each of the
1834 parameters that is passed in. This allows for setting them all in
1835 one convient area.
1837 Update is set to suspend all of the other test running while all the
1838 values are being set up. This allows for a performance increase, but
1839 it also means that the wrong type won't be detected with only some of
1840 the values set. After all the values are set everyone is told that
1841 there has been an update.
1842 */
1843 static void
1844 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1845 {
1846 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1847 sp_export_value_set_px (base, "x1", x1);
1848 sp_export_value_set_px (base, "y1", y1);
1849 sp_export_value_set_px (base, "x0", x0);
1850 sp_export_value_set_px (base, "y0", y0);
1851 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1853 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1854 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1856 return;
1857 }
1859 /**
1860 \brief Sets the value of an adjustment
1861 \param base The export dialog
1862 \param key Which adjustment to set
1863 \param val What value to set it to
1865 This function finds the adjustment using the data stored in the
1866 export dialog. After finding the adjustment it then sets
1867 the value of it.
1868 */
1869 static void
1870 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1871 {
1872 GtkAdjustment *adj;
1874 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1876 gtk_adjustment_set_value (adj, val);
1877 }
1879 /**
1880 \brief A function to set a value using the units points
1881 \param base The export dialog
1882 \param key Which value should be set
1883 \param val What the value should be in points
1885 This function first gets the adjustment for the key that is passed
1886 in. It then figures out what units are currently being used in the
1887 dialog. After doing all of that, it then converts the incoming
1888 value and sets the adjustment.
1889 */
1890 static void
1891 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1892 {
1893 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1895 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1897 return;
1898 }
1900 /**
1901 \brief Get the value of an adjustment in the export dialog
1902 \param base The export dialog
1903 \param key Which adjustment is being looked for
1904 \return The value in the specified adjustment
1906 This function gets the adjustment from the data field in the export
1907 dialog. It then grabs the value from the adjustment.
1908 */
1909 static float
1910 sp_export_value_get ( GtkObject *base, const gchar *key )
1911 {
1912 GtkAdjustment *adj;
1914 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1916 return adj->value;
1917 }
1919 /**
1920 \brief Grabs a value in the export dialog and converts the unit
1921 to points
1922 \param base The export dialog
1923 \param key Which value should be returned
1924 \return The value in the adjustment in points
1926 This function, at its most basic, is a call to \c sp_export_value_get
1927 to get the value of the adjustment. It then finds the units that
1928 are being used by looking at the "units" attribute of the export
1929 dialog. Using that it converts the returned value into points.
1930 */
1931 static float
1932 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1933 {
1934 float value = sp_export_value_get(base, key);
1935 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1937 return sp_units_get_pixels (value, *unit);
1938 } // end of sp_export_value_get_px()
1940 /**
1941 \brief This function is called when the filename is changed by
1942 anyone. It resets the virgin bit.
1943 \param object Text entry box
1944 \param data The export dialog
1945 \return None
1947 This function gets called when the text area is modified. It is
1948 looking for the case where the text area is modified from its
1949 original value. In that case it sets the "filename-modified" bit
1950 to TRUE. If the text dialog returns back to the original text, the
1951 bit gets reset. This should stop simple mistakes.
1952 */
1953 static void
1954 sp_export_filename_modified (GtkObject * object, gpointer data)
1955 {
1956 GtkWidget * text_entry = (GtkWidget *)object;
1957 GtkWidget * export_dialog = (GtkWidget *)data;
1959 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1960 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1961 // printf("Modified: FALSE\n");
1962 } else {
1963 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1964 // printf("Modified: TRUE\n");
1965 }
1967 return;
1968 } // end sp_export_filename_modified
1970 /*
1971 Local Variables:
1972 mode:c++
1973 c-file-style:"stroustrup"
1974 c-file-offsets:((innamespace . 0)(inline-open . 0))
1975 indent-tabs-mode:nil
1976 fill-column:99
1977 End:
1978 */
1979 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :