dc337ef8e841ea006d025334258262825ee0ffbd
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 Glib::ustring const 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<Geom::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
782 if (bbox) {
783 sp_export_set_area (base, bbox->min()[Geom::X],
784 bbox->min()[Geom::Y],
785 bbox->max()[Geom::X],
786 bbox->max()[Geom::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<Geom::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 = Geom::Rect(Geom::Point(0.0, 0.0),
872 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
874 // std::cout << "Using selection: PAGE" << std::endl;
875 key = SELECTION_PAGE;
876 break;
877 case SELECTION_CUSTOM:
878 default:
879 break;
880 } // switch
882 // remember area setting
883 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
884 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
886 if ( key != SELECTION_CUSTOM && bbox ) {
887 sp_export_set_area (base, bbox->min()[Geom::X],
888 bbox->min()[Geom::Y],
889 bbox->max()[Geom::X],
890 bbox->max()[Geom::Y]);
891 }
893 } // end of if ( SP_ACTIVE_DESKTOP )
896 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
897 GtkWidget * file_entry;
898 const gchar * filename = NULL;
899 float xdpi = 0.0, ydpi = 0.0;
901 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
903 switch (key) {
904 case SELECTION_PAGE:
905 case SELECTION_DRAWING: {
906 SPDocument * doc = SP_ACTIVE_DOCUMENT;
907 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
909 if (filename == NULL) {
910 if (doc_export_name != NULL) {
911 filename = g_strdup(doc_export_name);
912 } else {
913 filename = g_strdup("");
914 }
915 }
916 break;
917 }
918 case SELECTION_SELECTION:
919 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
921 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
923 /* If we still don't have a filename -- let's build
924 one that's nice */
925 if (filename == NULL) {
926 const gchar * id = NULL;
927 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
928 for(; reprlst != NULL; reprlst = reprlst->next) {
929 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
930 if (repr->attribute("id")) {
931 id = repr->attribute("id");
932 break;
933 }
934 }
936 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
937 }
938 }
939 break;
940 case SELECTION_CUSTOM:
941 default:
942 break;
943 }
945 if (filename != NULL) {
946 g_free(original_name);
947 original_name = g_strdup(filename);
948 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
949 }
951 if (xdpi != 0.0) {
952 sp_export_value_set(base, "xdpi", xdpi);
953 }
955 /* These can't be separate, and setting x sets y, so for
956 now setting this is disabled. Hopefully it won't be in
957 the future */
958 if (FALSE && ydpi != 0.0) {
959 sp_export_value_set(base, "ydpi", ydpi);
960 }
961 }
963 return;
964 } // end of sp_export_area_toggled()
966 /// Called when dialog is deleted
967 static gint
968 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
969 {
970 g_object_set_data (base, "cancel", (gpointer) 1);
971 return TRUE;
972 } // end of sp_export_progress_delete()
974 /// Called when progress is cancelled
975 static void
976 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
977 {
978 g_object_set_data (base, "cancel", (gpointer) 1);
979 } // end of sp_export_progress_cancel()
981 /// Called for every progress iteration
982 static unsigned int
983 sp_export_progress_callback (float value, void *data)
984 {
985 GtkWidget *prg;
986 int evtcount;
988 if (g_object_get_data ((GObject *) data, "cancel"))
989 return FALSE;
991 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
992 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
994 evtcount = 0;
995 while ((evtcount < 16) && gdk_events_pending ()) {
996 gtk_main_iteration_do (FALSE);
997 evtcount += 1;
998 }
1000 gtk_main_iteration_do (FALSE);
1002 return TRUE;
1004 } // end of sp_export_progress_callback()
1006 GtkWidget *
1007 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1008 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1010 dlg = gtk_dialog_new ();
1011 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1012 prg = gtk_progress_bar_new ();
1013 sp_transientize (dlg);
1014 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1015 g_object_set_data ((GObject *) base, "progress", prg);
1017 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1019 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1020 GTK_PROGRESS_LEFT_TO_RIGHT);
1021 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1022 prg, FALSE, FALSE, 4 );
1023 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1024 GTK_STOCK_CANCEL,
1025 GTK_RESPONSE_CANCEL );
1027 g_signal_connect ( (GObject *) dlg, "delete_event",
1028 (GCallback) sp_export_progress_delete, base);
1029 g_signal_connect ( (GObject *) btn, "clicked",
1030 (GCallback) sp_export_progress_cancel, base);
1031 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1032 gtk_widget_show_all (dlg);
1034 return dlg;
1035 }
1037 // FIXME: Some lib function should be available to do this ...
1038 static gchar *
1039 filename_add_extension (const gchar *filename, const gchar *extension)
1040 {
1041 gchar *dot;
1043 dot = strrchr (filename, '.');
1044 if ( !dot )
1045 return g_strconcat (filename, ".", extension, NULL);
1046 {
1047 if (dot[1] == '\0')
1048 return g_strconcat (filename, extension, NULL);
1049 else
1050 {
1051 if (g_strcasecmp (dot + 1, extension) == 0)
1052 return g_strdup (filename);
1053 else
1054 {
1055 return g_strconcat (filename, ".", extension, NULL);
1056 }
1057 }
1058 }
1059 }
1061 /// Called when export button is clicked
1062 static void
1063 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1064 {
1065 if (!SP_ACTIVE_DESKTOP) return;
1067 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1069 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1070 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1071 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1072 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1073 // Batch export of selected objects
1075 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1076 gint n = 0;
1078 if (num < 1)
1079 return;
1081 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1082 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1083 g_free (progress_text);
1085 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1086 i != NULL;
1087 i = i->next) {
1088 SPItem *item = (SPItem *) i->data;
1089 // retrieve export filename hint
1090 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1091 if (!fn) {
1092 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1093 }
1095 // retrieve export dpi hints
1096 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1097 gdouble dpi = 0.0;
1098 if (dpi_hint) {
1099 dpi = atof(dpi_hint);
1100 }
1101 if (dpi == 0.0) {
1102 dpi = DPI_BASE;
1103 }
1105 NRRect area;
1106 sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1108 gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1109 gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1111 if (width > 1 && height > 1) {
1112 /* Do export */
1113 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1114 area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1115 nv->pagecolor,
1116 NULL, NULL, TRUE, // overwrite without asking
1117 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1118 )) {
1119 gchar * error;
1120 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1121 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1122 sp_ui_error_dialog(error);
1123 g_free(safeFile);
1124 g_free(error);
1125 }
1126 }
1127 n++;
1128 sp_export_progress_callback((float)n/num, base);
1129 }
1131 gtk_widget_destroy (prog_dlg);
1132 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1134 } else {
1136 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1137 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1139 float const x0 = sp_export_value_get_px(base, "x0");
1140 float const y0 = sp_export_value_get_px(base, "y0");
1141 float const x1 = sp_export_value_get_px(base, "x1");
1142 float const y1 = sp_export_value_get_px(base, "y1");
1143 float const xdpi = sp_export_value_get(base, "xdpi");
1144 float const ydpi = sp_export_value_get(base, "ydpi");
1145 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1146 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1148 if (filename == NULL || *filename == '\0') {
1149 sp_ui_error_dialog(_("You have to enter a filename"));
1150 return;
1151 }
1153 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1154 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1155 return;
1156 }
1158 gchar *dirname = g_path_get_dirname(filename);
1159 if ( dirname == NULL
1160 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1161 {
1162 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1163 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1164 safeDir);
1165 sp_ui_error_dialog(error);
1166 g_free(safeDir);
1167 g_free(error);
1168 g_free(dirname);
1169 return;
1170 }
1171 g_free(dirname);
1173 // make sure that .png is the extension of the file:
1174 gchar * filename_ext = filename_add_extension(filename, "png");
1175 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1177 gchar *fn = g_path_get_basename (filename_ext);
1179 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1180 g_free (fn);
1181 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1182 g_free (progress_text);
1184 /* Do export */
1185 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1186 x0, y0, x1, y1, width, height, xdpi, ydpi,
1187 nv->pagecolor,
1188 sp_export_progress_callback, base, FALSE,
1189 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1190 )) {
1191 gchar * error;
1192 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1193 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1194 sp_ui_error_dialog(error);
1195 g_free(safeFile);
1196 g_free(error);
1197 }
1199 /* Reset the filename so that it can be changed again by changing
1200 selections and all that */
1201 g_free(original_name);
1202 original_name = g_strdup(filename_ext);
1203 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1205 gtk_widget_destroy (prog_dlg);
1206 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1208 /* Setup the values in the document */
1209 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1210 case SELECTION_PAGE:
1211 case SELECTION_DRAWING: {
1212 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1213 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1214 bool modified = false;
1215 const gchar * temp_string;
1217 bool saved = sp_document_get_undo_sensitive(doc);
1218 sp_document_set_undo_sensitive(doc, false);
1220 temp_string = repr->attribute("inkscape:export-filename");
1221 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1222 repr->setAttribute("inkscape:export-filename", filename_ext);
1223 modified = true;
1224 }
1225 temp_string = repr->attribute("inkscape:export-xdpi");
1226 if (temp_string == NULL || xdpi != atof(temp_string)) {
1227 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1228 modified = true;
1229 }
1230 temp_string = repr->attribute("inkscape:export-ydpi");
1231 if (temp_string == NULL || xdpi != atof(temp_string)) {
1232 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1233 modified = true;
1234 }
1235 sp_document_set_undo_sensitive(doc, saved);
1237 if (modified) {
1238 doc->setModifiedSinceSave();
1239 }
1240 break;
1241 }
1242 case SELECTION_SELECTION: {
1243 const GSList * reprlst;
1244 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1245 bool modified = false;
1247 bool saved = sp_document_get_undo_sensitive(doc);
1248 sp_document_set_undo_sensitive(doc, false);
1249 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1251 for(; reprlst != NULL; reprlst = reprlst->next) {
1252 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1253 const gchar * temp_string;
1255 if (repr->attribute("id") == NULL ||
1256 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1257 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1258 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1259 temp_string = repr->attribute("inkscape:export-filename");
1260 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1261 repr->setAttribute("inkscape:export-filename", filename_ext);
1262 modified = true;
1263 }
1264 }
1265 temp_string = repr->attribute("inkscape:export-xdpi");
1266 if (temp_string == NULL || xdpi != atof(temp_string)) {
1267 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1268 modified = true;
1269 }
1270 temp_string = repr->attribute("inkscape:export-ydpi");
1271 if (temp_string == NULL || xdpi != atof(temp_string)) {
1272 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1273 modified = true;
1274 }
1275 }
1276 sp_document_set_undo_sensitive(doc, saved);
1278 if (modified) {
1279 doc->setModifiedSinceSave();
1280 }
1281 break;
1282 }
1283 default:
1284 break;
1285 }
1287 g_free (filename_ext);
1289 }
1291 } // end of sp_export_export_clicked()
1293 /// Called when Browse button is clicked
1294 static void
1295 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1296 {
1297 GtkWidget *fs, *fe;
1298 const gchar *filename;
1300 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1301 (GtkWindow*)dlg,
1302 GTK_FILE_CHOOSER_ACTION_SAVE,
1303 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1304 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1305 NULL );
1307 #ifdef WITH_GNOME_VFS
1308 if (gnome_vfs_initialized()) {
1309 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1310 }
1311 #endif
1313 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1315 sp_transientize (fs);
1317 gtk_window_set_modal(GTK_WINDOW (fs), true);
1319 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1321 if (*filename == '\0') {
1322 filename = homedir_path(NULL);
1323 }
1325 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1327 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1328 {
1329 gchar *file;
1331 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1333 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1334 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1336 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1338 g_free(utf8file);
1339 g_free(file);
1340 }
1342 gtk_widget_destroy (fs);
1344 return;
1345 } // end of sp_export_browse_clicked()
1347 // TODO: Move this to nr-rect-fns.h.
1348 static bool
1349 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1350 {
1351 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1352 return (
1353 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1354 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1355 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1356 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1357 );
1358 }
1360 /**
1361 \brief This function is used to detect the current selection setting
1362 based on the values in the x0, y0, x1 and y0 fields.
1363 \param base The export dialog itself
1365 One of the most confusing parts of this function is why the array
1366 is built at the beginning. What needs to happen here is that we
1367 should always check the current selection to see if it is the valid
1368 one. While this is a performance improvement it is also a usability
1369 one during the cases where things like selections and drawings match
1370 size. This way buttons change less 'randomly' (atleast in the eyes
1371 of the user). To do this an array is built where the current selection
1372 type is placed first, and then the others in an order from smallest
1373 to largest (this can be configured by reshuffling \c test_order).
1375 All of the values in this function are rounded to two decimal places
1376 because that is what is shown to the user. While everything is kept
1377 more accurate than that, the user can't control more acurrate than
1378 that, so for this to work for them - it needs to check on that level
1379 of accuracy.
1381 \todo finish writing this up
1382 */
1383 static void
1384 sp_export_detect_size(GtkObject * base) {
1385 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1386 selection_type this_test[SELECTION_NUMBER_OF + 1];
1387 selection_type key = SELECTION_NUMBER_OF;
1389 Geom::Point x(sp_export_value_get_px (base, "x0"),
1390 sp_export_value_get_px (base, "y0"));
1391 Geom::Point y(sp_export_value_get_px (base, "x1"),
1392 sp_export_value_get_px (base, "y1"));
1393 Geom::Rect current_bbox(x, y);
1394 //std::cout << "Current " << current_bbox;
1396 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1397 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1398 this_test[i + 1] = test_order[i];
1399 }
1401 for (int i = 0;
1402 i < SELECTION_NUMBER_OF + 1 &&
1403 key == SELECTION_NUMBER_OF &&
1404 SP_ACTIVE_DESKTOP != NULL;
1405 i++) {
1406 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1407 switch (this_test[i]) {
1408 case SELECTION_SELECTION:
1409 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1410 boost::optional<Geom::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1412 //std::cout << "Selection " << bbox;
1413 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1414 key = SELECTION_SELECTION;
1415 }
1416 }
1417 break;
1418 case SELECTION_DRAWING: {
1419 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1421 boost::optional<Geom::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1423 // std::cout << "Drawing " << bbox2;
1424 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1425 key = SELECTION_DRAWING;
1426 }
1427 break;
1428 }
1430 case SELECTION_PAGE: {
1431 SPDocument *doc;
1433 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1435 Geom::Point x(0.0, 0.0);
1436 Geom::Point y(sp_document_width(doc),
1437 sp_document_height(doc));
1438 Geom::Rect bbox(x, y);
1440 // std::cout << "Page " << bbox;
1441 if (sp_export_bbox_equal(bbox,current_bbox)) {
1442 key = SELECTION_PAGE;
1443 }
1445 break;
1446 }
1447 default:
1448 break;
1449 }
1450 }
1451 // std::cout << std::endl;
1453 if (key == SELECTION_NUMBER_OF) {
1454 key = SELECTION_CUSTOM;
1455 }
1457 /* We're now using a custom size, not a fixed one */
1458 /* printf("Detecting state: %s\n", selection_names[key]); */
1459 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1460 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1461 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1462 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1464 return;
1465 } /* sp_export_detect_size */
1467 /// Called when area x0 value is changed
1468 static void
1469 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1470 {
1471 float x0, x1, xdpi, width;
1473 if (gtk_object_get_data (base, "update"))
1474 return;
1476 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1477 (base, "units")))
1478 {
1479 return;
1480 }
1482 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1484 x0 = sp_export_value_get_px (base, "x0");
1485 x1 = sp_export_value_get_px (base, "x1");
1486 xdpi = sp_export_value_get (base, "xdpi");
1488 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1490 if (width < SP_EXPORT_MIN_SIZE) {
1491 const gchar *key;
1492 width = SP_EXPORT_MIN_SIZE;
1493 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1495 if (!strcmp (key, "x0")) {
1496 x1 = x0 + width * DPI_BASE / xdpi;
1497 sp_export_value_set_px (base, "x1", x1);
1498 } else {
1499 x0 = x1 - width * DPI_BASE / xdpi;
1500 sp_export_value_set_px (base, "x0", x0);
1501 }
1502 }
1504 sp_export_value_set_px (base, "width", x1 - x0);
1505 sp_export_value_set (base, "bmwidth", width);
1507 sp_export_detect_size(base);
1509 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1511 return;
1512 } // end of sp_export_area_x_value_changed()
1514 /// Called when area y0 value is changed.
1515 static void
1516 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1517 {
1518 float y0, y1, ydpi, height;
1520 if (gtk_object_get_data (base, "update"))
1521 return;
1523 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1524 (base, "units")))
1525 {
1526 return;
1527 }
1529 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1531 y0 = sp_export_value_get_px (base, "y0");
1532 y1 = sp_export_value_get_px (base, "y1");
1533 ydpi = sp_export_value_get (base, "ydpi");
1535 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1537 if (height < SP_EXPORT_MIN_SIZE) {
1538 const gchar *key;
1539 height = SP_EXPORT_MIN_SIZE;
1540 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1541 if (!strcmp (key, "y0")) {
1542 y1 = y0 + height * DPI_BASE / ydpi;
1543 sp_export_value_set_px (base, "y1", y1);
1544 } else {
1545 y0 = y1 - height * DPI_BASE / ydpi;
1546 sp_export_value_set_px (base, "y0", y0);
1547 }
1548 }
1550 sp_export_value_set_px (base, "height", y1 - y0);
1551 sp_export_value_set (base, "bmheight", height);
1553 sp_export_detect_size(base);
1555 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1557 return;
1558 } // end of sp_export_area_y_value_changed()
1560 /// Called when x1-x0 or area width is changed
1561 static void
1562 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1563 {
1564 float x0, x1, xdpi, width, bmwidth;
1566 if (gtk_object_get_data (base, "update"))
1567 return;
1569 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1570 (base, "units"))) {
1571 return;
1572 }
1574 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1576 x0 = sp_export_value_get_px (base, "x0");
1577 x1 = sp_export_value_get_px (base, "x1");
1578 xdpi = sp_export_value_get (base, "xdpi");
1579 width = sp_export_value_get_px (base, "width");
1580 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1582 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1584 bmwidth = SP_EXPORT_MIN_SIZE;
1585 width = bmwidth * DPI_BASE / xdpi;
1586 sp_export_value_set_px (base, "width", width);
1587 }
1589 sp_export_value_set_px (base, "x1", x0 + width);
1590 sp_export_value_set (base, "bmwidth", bmwidth);
1592 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594 return;
1595 } // end of sp_export_area_width_value_changed()
1597 /// Called when y1-y0 or area height is changed.
1598 static void
1599 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1600 {
1602 float y0, y1, ydpi, height, bmheight;
1604 if (gtk_object_get_data (base, "update"))
1605 return;
1607 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1608 (base, "units"))) {
1609 return;
1610 }
1612 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1614 y0 = sp_export_value_get_px (base, "y0");
1615 y1 = sp_export_value_get_px (base, "y1");
1616 ydpi = sp_export_value_get (base, "ydpi");
1617 height = sp_export_value_get_px (base, "height");
1618 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1620 if (bmheight < SP_EXPORT_MIN_SIZE) {
1621 bmheight = SP_EXPORT_MIN_SIZE;
1622 height = bmheight * DPI_BASE / ydpi;
1623 sp_export_value_set_px (base, "height", height);
1624 }
1626 sp_export_value_set_px (base, "y1", y0 + height);
1627 sp_export_value_set (base, "bmheight", bmheight);
1629 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1631 return;
1632 } // end of sp_export_area_height_value_changed()
1634 /**
1635 \brief A function to set the ydpi
1636 \param base The export dialog
1638 This function grabs all of the y values and then figures out the
1639 new bitmap size based on the changing dpi value. The dpi value is
1640 gotten from the xdpi setting as these can not currently be independent.
1641 */
1642 static void
1643 sp_export_set_image_y (GtkObject *base)
1644 {
1645 float y0, y1, xdpi;
1647 y0 = sp_export_value_get_px (base, "y0");
1648 y1 = sp_export_value_get_px (base, "y1");
1649 xdpi = sp_export_value_get (base, "xdpi");
1651 sp_export_value_set (base, "ydpi", xdpi);
1652 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1654 return;
1655 } // end of sp_export_set_image_y()
1657 /**
1658 \brief A function to set the xdpi
1659 \param base The export dialog
1661 This function grabs all of the x values and then figures out the
1662 new bitmap size based on the changing dpi value. The dpi value is
1663 gotten from the xdpi setting as these can not currently be independent.
1664 */
1665 static void
1666 sp_export_set_image_x (GtkObject *base)
1667 {
1668 float x0, x1, xdpi;
1670 x0 = sp_export_value_get_px (base, "x0");
1671 x1 = sp_export_value_get_px (base, "x1");
1672 xdpi = sp_export_value_get (base, "xdpi");
1674 sp_export_value_set (base, "ydpi", xdpi);
1675 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1677 return;
1678 } // end of sp_export_set_image_x()
1680 /// Called when pixel width is changed
1681 static void
1682 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1683 {
1684 float x0, x1, bmwidth, xdpi;
1686 if (gtk_object_get_data (base, "update"))
1687 return;
1689 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1690 (base, "units"))) {
1691 return;
1692 }
1694 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1696 x0 = sp_export_value_get_px (base, "x0");
1697 x1 = sp_export_value_get_px (base, "x1");
1698 bmwidth = sp_export_value_get (base, "bmwidth");
1700 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1701 bmwidth = SP_EXPORT_MIN_SIZE;
1702 sp_export_value_set (base, "bmwidth", bmwidth);
1703 }
1705 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1706 sp_export_value_set (base, "xdpi", xdpi);
1708 sp_export_set_image_y (base);
1710 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1712 return;
1713 } // end of sp_export_bitmap_width_value_changed()
1715 /// Called when pixel height is changed
1716 static void
1717 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1718 {
1719 float y0, y1, bmheight, xdpi;
1721 if (gtk_object_get_data (base, "update"))
1722 return;
1724 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1725 (base, "units"))) {
1726 return;
1727 }
1729 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1731 y0 = sp_export_value_get_px (base, "y0");
1732 y1 = sp_export_value_get_px (base, "y1");
1733 bmheight = sp_export_value_get (base, "bmheight");
1735 if (bmheight < SP_EXPORT_MIN_SIZE) {
1736 bmheight = SP_EXPORT_MIN_SIZE;
1737 sp_export_value_set (base, "bmheight", bmheight);
1738 }
1740 xdpi = bmheight * DPI_BASE / (y1 - y0);
1741 sp_export_value_set (base, "xdpi", xdpi);
1743 sp_export_set_image_x (base);
1745 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1747 return;
1748 } // end of sp_export_bitmap_width_value_changed()
1750 /**
1751 \brief A function to adjust the bitmap width when the xdpi value changes
1752 \param adj The adjustment that was changed
1753 \param base The export dialog itself
1755 The first thing this function checks is to see if we are doing an
1756 update. If we are, this function just returns because there is another
1757 instance of it that will handle everything for us. If there is a
1758 units change, we also assume that everyone is being updated appropriately
1759 and there is nothing for us to do.
1761 If we're the highest level function, we set the update flag, and
1762 continue on our way.
1764 All of the values are grabbed using the \c sp_export_value_get functions
1765 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1766 xdpi value is saved in the preferences for the next time the dialog
1767 is opened. (does the selection dpi need to be set here?)
1769 A check is done to to ensure that we aren't outputing an invalid width,
1770 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1771 changed to make it valid.
1773 After all of this the bitmap width is changed.
1775 We also change the ydpi. This is a temporary hack as these can not
1776 currently be independent. This is likely to change in the future.
1777 */
1778 void
1779 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1780 {
1781 float x0, x1, xdpi, bmwidth;
1783 if (gtk_object_get_data (base, "update"))
1784 return;
1786 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1787 (base, "units"))) {
1788 return;
1789 }
1791 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1793 x0 = sp_export_value_get_px (base, "x0");
1794 x1 = sp_export_value_get_px (base, "x1");
1795 xdpi = sp_export_value_get (base, "xdpi");
1797 // remember xdpi setting
1798 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1799 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1801 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1803 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1804 bmwidth = SP_EXPORT_MIN_SIZE;
1805 if (x1 != x0)
1806 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1807 else
1808 xdpi = DPI_BASE;
1809 sp_export_value_set (base, "xdpi", xdpi);
1810 }
1812 sp_export_value_set (base, "bmwidth", bmwidth);
1814 sp_export_set_image_y (base);
1816 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1818 return;
1819 } // end of sp_export_xdpi_value_changed()
1822 /**
1823 \brief A function to change the area that is used for the exported
1824 bitmap.
1825 \param base This is the export dialog
1826 \param x0 Horizontal upper left hand corner of the picture in points
1827 \param y0 Vertical upper left hand corner of the picture in points
1828 \param x1 Horizontal lower right hand corner of the picture in points
1829 \param y1 Vertical lower right hand corner of the picture in points
1831 This function just calls \c sp_export_value_set_px for each of the
1832 parameters that is passed in. This allows for setting them all in
1833 one convient area.
1835 Update is set to suspend all of the other test running while all the
1836 values are being set up. This allows for a performance increase, but
1837 it also means that the wrong type won't be detected with only some of
1838 the values set. After all the values are set everyone is told that
1839 there has been an update.
1840 */
1841 static void
1842 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1843 {
1844 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1845 sp_export_value_set_px (base, "x1", x1);
1846 sp_export_value_set_px (base, "y1", y1);
1847 sp_export_value_set_px (base, "x0", x0);
1848 sp_export_value_set_px (base, "y0", y0);
1849 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1851 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1852 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1854 return;
1855 }
1857 /**
1858 \brief Sets the value of an adjustment
1859 \param base The export dialog
1860 \param key Which adjustment to set
1861 \param val What value to set it to
1863 This function finds the adjustment using the data stored in the
1864 export dialog. After finding the adjustment it then sets
1865 the value of it.
1866 */
1867 static void
1868 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1869 {
1870 GtkAdjustment *adj;
1872 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1874 gtk_adjustment_set_value (adj, val);
1875 }
1877 /**
1878 \brief A function to set a value using the units points
1879 \param base The export dialog
1880 \param key Which value should be set
1881 \param val What the value should be in points
1883 This function first gets the adjustment for the key that is passed
1884 in. It then figures out what units are currently being used in the
1885 dialog. After doing all of that, it then converts the incoming
1886 value and sets the adjustment.
1887 */
1888 static void
1889 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1890 {
1891 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1893 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1895 return;
1896 }
1898 /**
1899 \brief Get the value of an adjustment in the export dialog
1900 \param base The export dialog
1901 \param key Which adjustment is being looked for
1902 \return The value in the specified adjustment
1904 This function gets the adjustment from the data field in the export
1905 dialog. It then grabs the value from the adjustment.
1906 */
1907 static float
1908 sp_export_value_get ( GtkObject *base, const gchar *key )
1909 {
1910 GtkAdjustment *adj;
1912 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1914 return adj->value;
1915 }
1917 /**
1918 \brief Grabs a value in the export dialog and converts the unit
1919 to points
1920 \param base The export dialog
1921 \param key Which value should be returned
1922 \return The value in the adjustment in points
1924 This function, at its most basic, is a call to \c sp_export_value_get
1925 to get the value of the adjustment. It then finds the units that
1926 are being used by looking at the "units" attribute of the export
1927 dialog. Using that it converts the returned value into points.
1928 */
1929 static float
1930 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1931 {
1932 float value = sp_export_value_get(base, key);
1933 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1935 return sp_units_get_pixels (value, *unit);
1936 } // end of sp_export_value_get_px()
1938 /**
1939 \brief This function is called when the filename is changed by
1940 anyone. It resets the virgin bit.
1941 \param object Text entry box
1942 \param data The export dialog
1943 \return None
1945 This function gets called when the text area is modified. It is
1946 looking for the case where the text area is modified from its
1947 original value. In that case it sets the "filename-modified" bit
1948 to TRUE. If the text dialog returns back to the original text, the
1949 bit gets reset. This should stop simple mistakes.
1950 */
1951 static void
1952 sp_export_filename_modified (GtkObject * object, gpointer data)
1953 {
1954 GtkWidget * text_entry = (GtkWidget *)object;
1955 GtkWidget * export_dialog = (GtkWidget *)data;
1957 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1958 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1959 // printf("Modified: FALSE\n");
1960 } else {
1961 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1962 // printf("Modified: TRUE\n");
1963 }
1965 return;
1966 } // end sp_export_filename_modified
1968 /*
1969 Local Variables:
1970 mode:c++
1971 c-file-style:"stroustrup"
1972 c-file-offsets:((innamespace . 0)(inline-open . 0))
1973 indent-tabs-mode:nil
1974 fill-column:99
1975 End:
1976 */
1977 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :