dd96431f07782df199740596ff73963bdb0e03db
1 /** @file
2 * @brief PNG export dialog
3 */
4 /* Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * bulia byak <buliabyak@users.sf.net>
7 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
8 *
9 * Copyright (C) 1999-2007 Authors
10 * Copyright (C) 2001-2002 Ximian, Inc.
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
20 #include <png.h>
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
32 #ifdef WITH_GNOME_VFS
33 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
34 #endif
36 #include <glibmm/i18n.h>
37 #include "helper/unit-menu.h"
38 #include "helper/units.h"
39 #include "unit-constants.h"
40 #include "helper/window.h"
41 #include "inkscape-private.h"
42 #include "document.h"
43 #include "desktop-handles.h"
44 #include "sp-item.h"
45 #include "selection.h"
46 #include "file.h"
47 #include "macros.h"
48 #include "sp-namedview.h"
49 #include "selection-chemistry.h"
51 #include "dialog-events.h"
52 #include "preferences.h"
53 #include "verbs.h"
54 #include "interface.h"
56 #include "extension/output.h"
57 #include "extension/db.h"
59 #include "io/sys.h"
61 #include "helper/png-write.h"
64 #define SP_EXPORT_MIN_SIZE 1.0
66 #define DPI_BASE PX_PER_IN
68 #define EXPORT_COORD_PRECISION 3
70 #define MIN_ONSCREEN_DISTANCE 50
72 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
73 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
74 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
76 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
77 GtkObject *base);
79 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
80 GtkObject *base);
82 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
83 GtkObject *base);
85 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
86 GtkObject *base);
88 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
89 GtkObject *base);
91 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
92 GtkObject *base);
94 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
95 GtkObject *base);
97 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
98 Inkscape::Selection *selection,
99 GtkObject *base);
100 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
101 Inkscape::Selection *selection,
102 guint flags,
103 GtkObject *base );
105 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
106 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
107 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
108 static float sp_export_value_get ( GtkObject *base, const gchar *key );
109 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
111 static void sp_export_filename_modified (GtkObject * object, gpointer data);
112 static inline void sp_export_find_default_selection(GtkWidget * dlg);
113 static void sp_export_detect_size(GtkObject * base);
115 static Glib::ustring const prefs_path = "/dialogs/export/";
117 // these all need to be reinitialized to their defaults during dialog_destroy
118 static GtkWidget *dlg = NULL;
119 static win_data wd;
120 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
121 static gchar * original_name = NULL;
122 static gchar * doc_export_name = NULL;
123 static bool was_empty = TRUE;
125 /** What type of button is being pressed */
126 enum selection_type {
127 SELECTION_PAGE = 0, /**< Export the whole page */
128 SELECTION_DRAWING, /**< Export everything drawn on the page */
129 SELECTION_SELECTION, /**< Export everything that is selected */
130 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
131 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
132 };
134 /** A list of strings that is used both in the preferences, and in the
135 data fields to describe the various values of \c selection_type. */
136 static const char * selection_names[SELECTION_NUMBER_OF] = {
137 "page", "drawing", "selection", "custom"};
139 /** The names on the buttons for the various selection types. */
140 static const char * selection_labels[SELECTION_NUMBER_OF] = {
141 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
143 static void
144 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
145 {
146 sp_signal_disconnect_by_data (INKSCAPE, dlg);
148 wd.win = dlg = NULL;
149 wd.stop = 0;
150 x = -1000; y = -1000; w = 0; h = 0;
151 g_free(original_name);
152 original_name = NULL;
153 g_free(doc_export_name);
154 doc_export_name = NULL;
155 was_empty = TRUE;
157 return;
158 } // end of sp_export_dialog_destroy()
160 /// Called when dialog is closed or inkscape is shut down.
161 static bool
162 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
163 {
165 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
166 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
168 if (x<0) x=0;
169 if (y<0) y=0;
171 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
172 prefs->setInt(prefs_path + "x", x);
173 prefs->setInt(prefs_path + "y", y);
174 prefs->setInt(prefs_path + "w", w);
175 prefs->setInt(prefs_path + "h", h);
177 return FALSE; // which means, go ahead and destroy it
179 } // end of sp_export_dialog_delete()
181 /**
182 \brief Creates a new spin button for the export dialog
183 \param key The name of the spin button
184 \param val A default value for the spin button
185 \param min Minimum value for the spin button
186 \param max Maximum value for the spin button
187 \param step The step size for the spin button
188 \param page Size of the page increment
189 \param us Unit selector that effects this spin button
190 \param t Table to put the spin button in
191 \param x X location in the table \c t to start with
192 \param y Y location in the table \c t to start with
193 \param ll Text to put on the left side of the spin button (optional)
194 \param lr Text to put on the right side of the spin button (optional)
195 \param digits Number of digits to display after the decimal
196 \param sensitive Whether the spin button is sensitive or not
197 \param cb Callback for when this spin button is changed (optional)
198 \param dlg Export dialog the spin button is being placed in
200 */
201 static void
202 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
203 float step, float page, GtkWidget *us,
204 GtkWidget *t, int x, int y,
205 const gchar *ll, const gchar *lr,
206 int digits, unsigned int sensitive,
207 GCallback cb, GtkWidget *dlg )
208 {
209 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
210 gtk_object_set_data (a, "key", const_cast<gchar *>(key));
211 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
213 if (us) {
214 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
215 GTK_ADJUSTMENT (a) );
216 }
218 int pos = 0;
220 GtkWidget *l = NULL;
222 if (ll) {
224 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
225 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
226 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
227 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
228 gtk_widget_set_sensitive (l, sensitive);
229 pos += 1;
231 }
233 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
234 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
235 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
236 gtk_widget_set_size_request (sb, 80, -1);
237 gtk_widget_set_sensitive (sb, sensitive);
238 pos += 1;
240 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
242 if (lr) {
244 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
245 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
246 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
247 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
248 gtk_widget_set_sensitive (l, sensitive);
249 pos += 1;
251 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
252 }
254 if (cb)
255 gtk_signal_connect (a, "value_changed", cb, dlg);
257 return;
258 } // end of sp_export_spinbutton_new()
261 static Gtk::VBox *
262 sp_export_dialog_area_box (GtkWidget * dlg)
263 {
264 Gtk::VBox* vb = new Gtk::VBox(false, 3);
266 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
267 lbl->set_use_markup(true);
268 vb->pack_start(*lbl);
270 /* Units box */
271 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
272 /* gets added to the vbox later, but the unit selector is needed
273 earlier than that */
275 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
276 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
277 if (desktop)
278 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
279 unitbox->pack_end(*us, false, false, 0);
280 Gtk::Label* l = new Gtk::Label(_("Units:"));
281 unitbox->pack_end(*l, false, false, 3);
282 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
284 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
286 Gtk::ToggleButton* b;
287 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
288 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
289 b->set_data("key", GINT_TO_POINTER(i));
290 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
291 togglebox->pack_start(*b, false, true, 0);
292 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
293 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
294 }
296 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
297 G_CALLBACK (sp_export_selection_changed), dlg );
298 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
299 G_CALLBACK (sp_export_selection_modified), dlg );
300 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
301 G_CALLBACK (sp_export_selection_changed), dlg );
303 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
304 t->set_row_spacings (4);
305 t->set_col_spacings (4);
307 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
308 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
309 G_CALLBACK ( sp_export_area_x_value_changed),
310 dlg );
312 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
313 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
314 G_CALLBACK (sp_export_area_x_value_changed),
315 dlg );
317 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
318 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
319 G_CALLBACK
320 (sp_export_area_width_value_changed),
321 dlg );
323 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
324 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
325 G_CALLBACK (sp_export_area_y_value_changed),
326 dlg );
328 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
329 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
330 G_CALLBACK (sp_export_area_y_value_changed),
331 dlg );
333 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
334 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
335 G_CALLBACK (sp_export_area_height_value_changed),
336 dlg );
338 vb->pack_start(*togglebox, false, false, 3);
339 vb->pack_start(*t, false, false, 0);
340 vb->pack_start(*unitbox, false, false, 0);
342 return vb;
343 } // end of sp_export_dialog_area_box
346 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
348 if (id == NULL) /* This should never happen */
349 id = "bitmap";
351 gchar * directory = NULL;
353 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
354 // std::cout << "Directory from dialog" << std::endl;
355 directory = g_dirname(file_entry_text);
356 }
358 if (directory == NULL) {
359 /* Grab document directory */
360 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
361 // std::cout << "Directory from document" << std::endl;
362 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
363 }
364 }
366 if (directory == NULL) {
367 // std::cout << "Home Directory" << std::endl;
368 directory = homedir_path(NULL);
369 }
371 gchar * id_ext = g_strconcat(id, ".png", NULL);
372 gchar *filename = g_build_filename(directory, id_ext, NULL);
373 g_free(directory);
374 g_free(id_ext);
375 return filename;
376 }
378 static void
379 batch_export_clicked (GtkWidget *widget, GtkObject *base)
380 {
381 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
382 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
383 vb_singleexport->set_sensitive(false);
384 } else {
385 vb_singleexport->set_sensitive(true);
386 }
387 }
389 void
390 sp_export_dialog (void)
391 {
392 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
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->getInt(prefs_path + "x", 0);
402 y = prefs->getInt(prefs_path + "y", 0);
403 }
405 if (w ==0 || h == 0) {
406 w = prefs->getInt(prefs_path + "w", 0);
407 h = prefs->getInt(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->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
486 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
487 NULL, _("dp_i"), 2, 1,
488 G_CALLBACK (sp_export_xdpi_value_changed),
489 dlg );
491 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
492 NULL, GTK_WIDGET(t->gobj()), 0, 1,
493 _("_Height:"), _("pixels at"), 0, 1,
494 G_CALLBACK
495 (sp_export_bitmap_height_value_changed),
496 dlg );
498 /** \todo
499 * Needs fixing: there's no way to set ydpi currently, so we use
500 * the defaultxdpi value here, too...
501 */
502 sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
503 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
504 NULL, _("dpi"), 2, 0, NULL, dlg );
506 vb_singleexport->pack_start(*size_box);
507 }
509 /* File entry */
510 {
511 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
512 file_box->set_border_width(3);
514 // true = has mnemonic
515 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
516 flabel->set_use_markup(true);
517 file_box->pack_start(*flabel, false, false, 0);
519 Gtk::Entry *fe = new Gtk::Entry();
521 /*
522 * set the default filename to be that of the current path + document
523 * with .png extension
524 *
525 * One thing to notice here is that this filename may get
526 * overwritten, but it won't happen here. The filename gets
527 * written into the text field, but then the button to select
528 * the area gets set. In that code the filename can be changed
529 * if there are some with presidence in the document. So, while
530 * this code sets the name first, it may not be the one users
531 * really see.
532 */
533 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
534 {
535 gchar *name;
536 SPDocument * doc = SP_ACTIVE_DOCUMENT;
537 const gchar *uri = SP_DOCUMENT_URI (doc);
538 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
539 const gchar * text_extension = repr->attribute("inkscape:output_extension");
540 Inkscape::Extension::Output * oextension = NULL;
542 if (text_extension != NULL) {
543 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
544 }
546 if (oextension != NULL) {
547 gchar * old_extension = oextension->get_extension();
548 if (g_str_has_suffix(uri, old_extension)) {
549 gchar * uri_copy;
550 gchar * extension_point;
551 gchar * final_name;
553 uri_copy = g_strdup(uri);
554 extension_point = g_strrstr(uri_copy, old_extension);
555 extension_point[0] = '\0';
557 final_name = g_strconcat(uri_copy, ".png", NULL);
558 fe->set_text(final_name);
560 g_free(final_name);
561 g_free(uri_copy);
562 }
563 } else {
564 name = g_strconcat(uri, ".png", NULL);
565 fe->set_text(name);
566 g_free(name);
567 }
569 doc_export_name = g_strdup(fe->get_text().c_str());
570 }
571 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
572 G_CALLBACK (sp_export_filename_modified), dlg);
574 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
576 {
577 // true = has mnemonic
578 Gtk::Button *b = new Gtk::Button();
580 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
581 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
582 Gtk::ICON_SIZE_BUTTON);
583 pixlabel->pack_start(*im);
585 Gtk::Label *l = new Gtk::Label();
586 l->set_markup_with_mnemonic(_("_Browse..."));
587 pixlabel->pack_start(*l);
589 b->add(*pixlabel);
591 hb->pack_end (*b, false, false, 4);
592 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
593 G_CALLBACK (sp_export_browse_clicked), NULL );
594 }
596 hb->pack_start (*fe, true, true, 0);
597 file_box->add(*hb);
598 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
599 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
600 original_name = g_strdup(fe->get_text().c_str());
601 // pressing enter in the filename field is the same as clicking export:
602 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
603 G_CALLBACK (sp_export_export_clicked), dlg );
604 // focus is in the filename initially:
605 fe->grab_focus();
607 // mnemonic in frame label moves focus to filename:
608 flabel->set_mnemonic_widget(*fe);
610 vb_singleexport->pack_start(*file_box);
611 }
613 {
614 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
615 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
616 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
617 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
618 batch_box->pack_start(*Glib::wrap(be), false, false);
619 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);
620 batch_box->show_all();
621 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
622 vb->pack_start(*batch_box);
623 }
625 {
626 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
627 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
628 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
629 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
630 hide_box->pack_start(*Glib::wrap(he), false, false);
631 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
632 hide_box->show_all();
633 vb->pack_start(*hide_box);
634 }
636 /* Buttons */
637 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
638 bb->set_border_width(3);
640 {
641 Gtk::Button *b = new Gtk::Button();
642 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
643 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
644 Gtk::ICON_SIZE_BUTTON);
645 image_label->pack_start(*im);
647 Gtk::Label *l = new Gtk::Label();
648 l->set_markup_with_mnemonic(_("_Export"));
649 image_label->pack_start(*l);
651 b->add(*image_label);
652 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
653 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
654 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
655 bb->pack_end(*b, false, false, 0);
656 }
658 vb->pack_end(*bb, false, false, 0);
659 vb->show_all();
661 } // end of if (!dlg)
663 sp_export_find_default_selection(dlg);
665 gtk_window_present ((GtkWindow *) dlg);
667 return;
668 } // end of sp_export_dialog()
670 static void
671 sp_export_update_checkbuttons (GtkObject *base)
672 {
673 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
674 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
675 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
676 if (num >= 2) {
677 gtk_widget_set_sensitive (be, true);
678 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
679 } else {
680 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
681 gtk_widget_set_sensitive (be, FALSE);
682 }
683 if (num > 0) {
684 gtk_widget_set_sensitive (he, true);
685 } else {
686 gtk_widget_set_sensitive (he, false);
687 }
688 }
690 static inline void
691 sp_export_find_default_selection(GtkWidget * dlg)
692 {
693 selection_type key = SELECTION_NUMBER_OF;
695 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
696 key = SELECTION_SELECTION;
697 }
699 /* Try using the preferences */
700 if (key == SELECTION_NUMBER_OF) {
701 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
702 int i = SELECTION_NUMBER_OF;
704 Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
706 if (!what.empty()) {
707 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
708 if (what == selection_names[i]) {
709 break;
710 }
711 }
712 }
714 key = (selection_type)i;
715 }
717 if (key == SELECTION_NUMBER_OF) {
718 key = SELECTION_SELECTION;
719 }
721 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
722 selection_names[key]);
723 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
725 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
726 }
729 /**
730 * \brief If selection changed or a different document activated, we must
731 * recalculate any chosen areas
732 *
733 */
734 static void
735 sp_export_selection_changed ( Inkscape::Application *inkscape,
736 Inkscape::Selection *selection,
737 GtkObject *base )
738 {
739 selection_type current_key;
740 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
742 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
743 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
744 was_empty) {
745 gtk_toggle_button_set_active
746 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
747 TRUE );
748 }
749 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
751 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
753 if (inkscape &&
754 SP_IS_INKSCAPE (inkscape) &&
755 selection &&
756 SELECTION_CUSTOM != current_key) {
757 GtkToggleButton * button;
758 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
759 sp_export_area_toggled(button, base);
760 }
762 sp_export_update_checkbuttons (base);
763 }
765 static void
766 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
767 Inkscape::Selection */*selection*/,
768 guint /*flags*/,
769 GtkObject *base )
770 {
771 selection_type current_key;
772 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
774 switch (current_key) {
775 case SELECTION_DRAWING:
776 if ( SP_ACTIVE_DESKTOP ) {
777 SPDocument *doc;
778 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
779 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
780 if (bbox) {
781 sp_export_set_area (base, bbox->min()[Geom::X],
782 bbox->min()[Geom::Y],
783 bbox->max()[Geom::X],
784 bbox->max()[Geom::Y]);
785 }
786 }
787 break;
788 case SELECTION_SELECTION:
789 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790 NRRect bbox;
791 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
792 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
793 }
794 break;
795 default:
796 /* Do nothing for page or for custom */
797 break;
798 }
800 return;
801 }
803 /// Called when one of the selection buttons was toggled.
804 static void
805 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
806 {
807 if (gtk_object_get_data (base, "update"))
808 return;
810 selection_type key, old_key;
811 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
812 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
814 /* Ignore all "turned off" events unless we're the only active button */
815 if (!gtk_toggle_button_get_active (tb) ) {
817 /* Don't let the current selection be deactived - but rerun the
818 activate to allow the user to renew the values */
819 if (key == old_key) {
820 gtk_toggle_button_set_active ( tb, TRUE );
821 }
823 return;
824 }
826 /* Turn off the currently active button unless it's us */
827 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
829 if (old_key != key) {
830 gtk_toggle_button_set_active
831 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
832 FALSE );
833 }
835 if ( SP_ACTIVE_DESKTOP )
836 {
837 SPDocument *doc;
838 Geom::OptRect bbox;
839 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
841 /* Notice how the switch is used to 'fall through' here to get
842 various backups. If you modify this without noticing you'll
843 probabaly screw something up. */
844 switch (key) {
845 case SELECTION_SELECTION:
846 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
847 {
848 bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds();
849 /* Only if there is a selection that we can set
850 do we break, otherwise we fall through to the
851 drawing */
852 // std::cout << "Using selection: SELECTION" << std::endl;
853 key = SELECTION_SELECTION;
854 break;
855 }
856 case SELECTION_DRAWING:
857 /** \todo
858 * This returns wrong values if the document has a viewBox.
859 */
860 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
861 /* If the drawing is valid, then we'll use it and break
862 otherwise we drop through to the page settings */
863 if (bbox) {
864 // std::cout << "Using selection: DRAWING" << std::endl;
865 key = SELECTION_DRAWING;
866 break;
867 }
868 case SELECTION_PAGE:
869 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
870 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
872 // std::cout << "Using selection: PAGE" << std::endl;
873 key = SELECTION_PAGE;
874 break;
875 case SELECTION_CUSTOM:
876 default:
877 break;
878 } // switch
880 // remember area setting
881 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
882 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
884 if ( key != SELECTION_CUSTOM && bbox ) {
885 sp_export_set_area (base, bbox->min()[Geom::X],
886 bbox->min()[Geom::Y],
887 bbox->max()[Geom::X],
888 bbox->max()[Geom::Y]);
889 }
891 } // end of if ( SP_ACTIVE_DESKTOP )
894 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
895 GtkWidget * file_entry;
896 const gchar * filename = NULL;
897 float xdpi = 0.0, ydpi = 0.0;
899 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
901 switch (key) {
902 case SELECTION_PAGE:
903 case SELECTION_DRAWING: {
904 SPDocument * doc = SP_ACTIVE_DOCUMENT;
905 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
907 if (filename == NULL) {
908 if (doc_export_name != NULL) {
909 filename = g_strdup(doc_export_name);
910 } else {
911 filename = g_strdup("");
912 }
913 }
914 break;
915 }
916 case SELECTION_SELECTION:
917 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
919 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
921 /* If we still don't have a filename -- let's build
922 one that's nice */
923 if (filename == NULL) {
924 const gchar * id = NULL;
925 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
926 for(; reprlst != NULL; reprlst = reprlst->next) {
927 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
928 if (repr->attribute("id")) {
929 id = repr->attribute("id");
930 break;
931 }
932 }
934 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
935 }
936 }
937 break;
938 case SELECTION_CUSTOM:
939 default:
940 break;
941 }
943 if (filename != NULL) {
944 g_free(original_name);
945 original_name = g_strdup(filename);
946 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
947 }
949 if (xdpi != 0.0) {
950 sp_export_value_set(base, "xdpi", xdpi);
951 }
953 /* These can't be separate, and setting x sets y, so for
954 now setting this is disabled. Hopefully it won't be in
955 the future */
956 if (FALSE && ydpi != 0.0) {
957 sp_export_value_set(base, "ydpi", ydpi);
958 }
959 }
961 return;
962 } // end of sp_export_area_toggled()
964 /// Called when dialog is deleted
965 static gint
966 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
967 {
968 g_object_set_data (base, "cancel", (gpointer) 1);
969 return TRUE;
970 } // end of sp_export_progress_delete()
972 /// Called when progress is cancelled
973 static void
974 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
975 {
976 g_object_set_data (base, "cancel", (gpointer) 1);
977 } // end of sp_export_progress_cancel()
979 /// Called for every progress iteration
980 static unsigned int
981 sp_export_progress_callback (float value, void *data)
982 {
983 GtkWidget *prg;
984 int evtcount;
986 if (g_object_get_data ((GObject *) data, "cancel"))
987 return FALSE;
989 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
990 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
992 evtcount = 0;
993 while ((evtcount < 16) && gdk_events_pending ()) {
994 gtk_main_iteration_do (FALSE);
995 evtcount += 1;
996 }
998 gtk_main_iteration_do (FALSE);
1000 return TRUE;
1002 } // end of sp_export_progress_callback()
1004 GtkWidget *
1005 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1006 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1008 dlg = gtk_dialog_new ();
1009 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1010 prg = gtk_progress_bar_new ();
1011 sp_transientize (dlg);
1012 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1013 g_object_set_data ((GObject *) base, "progress", prg);
1015 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1017 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1018 GTK_PROGRESS_LEFT_TO_RIGHT);
1019 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1020 prg, FALSE, FALSE, 4 );
1021 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1022 GTK_STOCK_CANCEL,
1023 GTK_RESPONSE_CANCEL );
1025 g_signal_connect ( (GObject *) dlg, "delete_event",
1026 (GCallback) sp_export_progress_delete, base);
1027 g_signal_connect ( (GObject *) btn, "clicked",
1028 (GCallback) sp_export_progress_cancel, base);
1029 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1030 gtk_widget_show_all (dlg);
1032 return dlg;
1033 }
1035 // FIXME: Some lib function should be available to do this ...
1036 static gchar *
1037 filename_add_extension (const gchar *filename, const gchar *extension)
1038 {
1039 const gchar *dot;
1041 dot = strrchr (filename, '.');
1042 if ( !dot )
1043 return g_strconcat (filename, ".", extension, NULL);
1044 {
1045 if (dot[1] == '\0')
1046 return g_strconcat (filename, extension, NULL);
1047 else
1048 {
1049 if (g_strcasecmp (dot + 1, extension) == 0)
1050 return g_strdup (filename);
1051 else
1052 {
1053 return g_strconcat (filename, ".", extension, NULL);
1054 }
1055 }
1056 }
1057 }
1059 /// Called when export button is clicked
1060 static void
1061 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1062 {
1063 if (!SP_ACTIVE_DESKTOP) return;
1065 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1067 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1068 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1069 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1070 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1071 // Batch export of selected objects
1073 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1074 gint n = 0;
1076 if (num < 1)
1077 return;
1079 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1080 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1081 g_free (progress_text);
1083 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1084 i != NULL;
1085 i = i->next) {
1086 SPItem *item = (SPItem *) i->data;
1087 // retrieve export filename hint
1088 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1089 if (!fn) {
1090 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1091 }
1093 // retrieve export dpi hints
1094 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1095 gdouble dpi = 0.0;
1096 if (dpi_hint) {
1097 dpi = atof(dpi_hint);
1098 }
1099 if (dpi == 0.0) {
1100 dpi = DPI_BASE;
1101 }
1103 Geom::OptRect area;
1104 sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1105 if (area) {
1106 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1107 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1109 if (width > 1 && height > 1) {
1110 /* Do export */
1111 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1112 *area, width, height, dpi, dpi,
1113 nv->pagecolor,
1114 NULL, NULL, TRUE, // overwrite without asking
1115 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1116 )) {
1117 gchar * error;
1118 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1119 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1120 sp_ui_error_dialog(error);
1121 g_free(safeFile);
1122 g_free(error);
1123 }
1124 }
1125 }
1126 n++;
1127 sp_export_progress_callback((float)n/num, base);
1128 }
1130 gtk_widget_destroy (prog_dlg);
1131 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1133 } else {
1135 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1136 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1138 float const x0 = sp_export_value_get_px(base, "x0");
1139 float const y0 = sp_export_value_get_px(base, "y0");
1140 float const x1 = sp_export_value_get_px(base, "x1");
1141 float const y1 = sp_export_value_get_px(base, "y1");
1142 float const xdpi = sp_export_value_get(base, "xdpi");
1143 float const ydpi = sp_export_value_get(base, "ydpi");
1144 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1145 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1147 if (filename == NULL || *filename == '\0') {
1148 sp_ui_error_dialog(_("You have to enter a filename"));
1149 return;
1150 }
1152 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1153 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1154 return;
1155 }
1157 gchar *dirname = g_path_get_dirname(filename);
1158 if ( dirname == NULL
1159 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1160 {
1161 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1162 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1163 safeDir);
1164 sp_ui_error_dialog(error);
1165 g_free(safeDir);
1166 g_free(error);
1167 g_free(dirname);
1168 return;
1169 }
1170 g_free(dirname);
1172 // make sure that .png is the extension of the file:
1173 gchar * filename_ext = filename_add_extension(filename, "png");
1174 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1176 gchar *fn = g_path_get_basename (filename_ext);
1178 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1179 g_free (fn);
1180 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1181 g_free (progress_text);
1183 /* Do export */
1184 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1185 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1186 nv->pagecolor,
1187 sp_export_progress_callback, base, FALSE,
1188 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1189 )) {
1190 gchar * error;
1191 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1192 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1193 sp_ui_error_dialog(error);
1194 g_free(safeFile);
1195 g_free(error);
1196 }
1198 /* Reset the filename so that it can be changed again by changing
1199 selections and all that */
1200 g_free(original_name);
1201 original_name = g_strdup(filename_ext);
1202 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1204 gtk_widget_destroy (prog_dlg);
1205 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1207 /* Setup the values in the document */
1208 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1209 case SELECTION_PAGE:
1210 case SELECTION_DRAWING: {
1211 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1212 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1213 bool modified = false;
1214 const gchar * temp_string;
1216 bool saved = sp_document_get_undo_sensitive(doc);
1217 sp_document_set_undo_sensitive(doc, false);
1219 temp_string = repr->attribute("inkscape:export-filename");
1220 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1221 repr->setAttribute("inkscape:export-filename", filename_ext);
1222 modified = true;
1223 }
1224 temp_string = repr->attribute("inkscape:export-xdpi");
1225 if (temp_string == NULL || xdpi != atof(temp_string)) {
1226 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1227 modified = true;
1228 }
1229 temp_string = repr->attribute("inkscape:export-ydpi");
1230 if (temp_string == NULL || xdpi != atof(temp_string)) {
1231 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1232 modified = true;
1233 }
1234 sp_document_set_undo_sensitive(doc, saved);
1236 if (modified) {
1237 doc->setModifiedSinceSave();
1238 }
1239 break;
1240 }
1241 case SELECTION_SELECTION: {
1242 const GSList * reprlst;
1243 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1244 bool modified = false;
1246 bool saved = sp_document_get_undo_sensitive(doc);
1247 sp_document_set_undo_sensitive(doc, false);
1248 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1250 for(; reprlst != NULL; reprlst = reprlst->next) {
1251 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1252 const gchar * temp_string;
1254 if (repr->attribute("id") == NULL ||
1255 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1256 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1257 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1258 temp_string = repr->attribute("inkscape:export-filename");
1259 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1260 repr->setAttribute("inkscape:export-filename", filename_ext);
1261 modified = true;
1262 }
1263 }
1264 temp_string = repr->attribute("inkscape:export-xdpi");
1265 if (temp_string == NULL || xdpi != atof(temp_string)) {
1266 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1267 modified = true;
1268 }
1269 temp_string = repr->attribute("inkscape:export-ydpi");
1270 if (temp_string == NULL || xdpi != atof(temp_string)) {
1271 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1272 modified = true;
1273 }
1274 }
1275 sp_document_set_undo_sensitive(doc, saved);
1277 if (modified) {
1278 doc->setModifiedSinceSave();
1279 }
1280 break;
1281 }
1282 default:
1283 break;
1284 }
1286 g_free (filename_ext);
1288 }
1290 } // end of sp_export_export_clicked()
1292 /// Called when Browse button is clicked
1293 static void
1294 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1295 {
1296 GtkWidget *fs, *fe;
1297 const gchar *filename;
1299 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1300 (GtkWindow*)dlg,
1301 GTK_FILE_CHOOSER_ACTION_SAVE,
1302 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1303 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1304 NULL );
1306 #ifdef WITH_GNOME_VFS
1307 if (gnome_vfs_initialized()) {
1308 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1309 }
1310 #endif
1312 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1314 sp_transientize (fs);
1316 gtk_window_set_modal(GTK_WINDOW (fs), true);
1318 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1320 if (*filename == '\0') {
1321 filename = homedir_path(NULL);
1322 }
1324 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1326 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1327 {
1328 gchar *file;
1330 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1332 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1333 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1335 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1337 g_free(utf8file);
1338 g_free(file);
1339 }
1341 gtk_widget_destroy (fs);
1343 return;
1344 } // end of sp_export_browse_clicked()
1346 // TODO: Move this to nr-rect-fns.h.
1347 static bool
1348 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1349 {
1350 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1351 return (
1352 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1353 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1354 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1355 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1356 );
1357 }
1359 /**
1360 \brief This function is used to detect the current selection setting
1361 based on the values in the x0, y0, x1 and y0 fields.
1362 \param base The export dialog itself
1364 One of the most confusing parts of this function is why the array
1365 is built at the beginning. What needs to happen here is that we
1366 should always check the current selection to see if it is the valid
1367 one. While this is a performance improvement it is also a usability
1368 one during the cases where things like selections and drawings match
1369 size. This way buttons change less 'randomly' (atleast in the eyes
1370 of the user). To do this an array is built where the current selection
1371 type is placed first, and then the others in an order from smallest
1372 to largest (this can be configured by reshuffling \c test_order).
1374 All of the values in this function are rounded to two decimal places
1375 because that is what is shown to the user. While everything is kept
1376 more accurate than that, the user can't control more acurrate than
1377 that, so for this to work for them - it needs to check on that level
1378 of accuracy.
1380 \todo finish writing this up
1381 */
1382 static void
1383 sp_export_detect_size(GtkObject * base) {
1384 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1385 selection_type this_test[SELECTION_NUMBER_OF + 1];
1386 selection_type key = SELECTION_NUMBER_OF;
1388 Geom::Point x(sp_export_value_get_px (base, "x0"),
1389 sp_export_value_get_px (base, "y0"));
1390 Geom::Point y(sp_export_value_get_px (base, "x1"),
1391 sp_export_value_get_px (base, "y1"));
1392 Geom::Rect current_bbox(x, y);
1393 //std::cout << "Current " << current_bbox;
1395 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1396 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1397 this_test[i + 1] = test_order[i];
1398 }
1400 for (int i = 0;
1401 i < SELECTION_NUMBER_OF + 1 &&
1402 key == SELECTION_NUMBER_OF &&
1403 SP_ACTIVE_DESKTOP != NULL;
1404 i++) {
1405 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1406 switch (this_test[i]) {
1407 case SELECTION_SELECTION:
1408 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1409 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1411 //std::cout << "Selection " << bbox;
1412 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1413 key = SELECTION_SELECTION;
1414 }
1415 }
1416 break;
1417 case SELECTION_DRAWING: {
1418 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1420 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1422 // std::cout << "Drawing " << bbox2;
1423 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1424 key = SELECTION_DRAWING;
1425 }
1426 break;
1427 }
1429 case SELECTION_PAGE: {
1430 SPDocument *doc;
1432 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1434 Geom::Point x(0.0, 0.0);
1435 Geom::Point y(sp_document_width(doc),
1436 sp_document_height(doc));
1437 Geom::Rect bbox(x, y);
1439 // std::cout << "Page " << bbox;
1440 if (sp_export_bbox_equal(bbox,current_bbox)) {
1441 key = SELECTION_PAGE;
1442 }
1444 break;
1445 }
1446 default:
1447 break;
1448 }
1449 }
1450 // std::cout << std::endl;
1452 if (key == SELECTION_NUMBER_OF) {
1453 key = SELECTION_CUSTOM;
1454 }
1456 /* We're now using a custom size, not a fixed one */
1457 /* printf("Detecting state: %s\n", selection_names[key]); */
1458 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1459 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1460 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1461 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1463 return;
1464 } /* sp_export_detect_size */
1466 /// Called when area x0 value is changed
1467 static void
1468 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1469 {
1470 float x0, x1, xdpi, width;
1472 if (gtk_object_get_data (base, "update"))
1473 return;
1475 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1476 (base, "units")))
1477 {
1478 return;
1479 }
1481 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1483 x0 = sp_export_value_get_px (base, "x0");
1484 x1 = sp_export_value_get_px (base, "x1");
1485 xdpi = sp_export_value_get (base, "xdpi");
1487 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1489 if (width < SP_EXPORT_MIN_SIZE) {
1490 const gchar *key;
1491 width = SP_EXPORT_MIN_SIZE;
1492 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1494 if (!strcmp (key, "x0")) {
1495 x1 = x0 + width * DPI_BASE / xdpi;
1496 sp_export_value_set_px (base, "x1", x1);
1497 } else {
1498 x0 = x1 - width * DPI_BASE / xdpi;
1499 sp_export_value_set_px (base, "x0", x0);
1500 }
1501 }
1503 sp_export_value_set_px (base, "width", x1 - x0);
1504 sp_export_value_set (base, "bmwidth", width);
1506 sp_export_detect_size(base);
1508 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1510 return;
1511 } // end of sp_export_area_x_value_changed()
1513 /// Called when area y0 value is changed.
1514 static void
1515 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1516 {
1517 float y0, y1, ydpi, height;
1519 if (gtk_object_get_data (base, "update"))
1520 return;
1522 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1523 (base, "units")))
1524 {
1525 return;
1526 }
1528 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1530 y0 = sp_export_value_get_px (base, "y0");
1531 y1 = sp_export_value_get_px (base, "y1");
1532 ydpi = sp_export_value_get (base, "ydpi");
1534 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1536 if (height < SP_EXPORT_MIN_SIZE) {
1537 const gchar *key;
1538 height = SP_EXPORT_MIN_SIZE;
1539 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1540 if (!strcmp (key, "y0")) {
1541 y1 = y0 + height * DPI_BASE / ydpi;
1542 sp_export_value_set_px (base, "y1", y1);
1543 } else {
1544 y0 = y1 - height * DPI_BASE / ydpi;
1545 sp_export_value_set_px (base, "y0", y0);
1546 }
1547 }
1549 sp_export_value_set_px (base, "height", y1 - y0);
1550 sp_export_value_set (base, "bmheight", height);
1552 sp_export_detect_size(base);
1554 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1556 return;
1557 } // end of sp_export_area_y_value_changed()
1559 /// Called when x1-x0 or area width is changed
1560 static void
1561 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1562 {
1563 float x0, x1, xdpi, width, bmwidth;
1565 if (gtk_object_get_data (base, "update"))
1566 return;
1568 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1569 (base, "units"))) {
1570 return;
1571 }
1573 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1575 x0 = sp_export_value_get_px (base, "x0");
1576 x1 = sp_export_value_get_px (base, "x1");
1577 xdpi = sp_export_value_get (base, "xdpi");
1578 width = sp_export_value_get_px (base, "width");
1579 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1581 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1583 bmwidth = SP_EXPORT_MIN_SIZE;
1584 width = bmwidth * DPI_BASE / xdpi;
1585 sp_export_value_set_px (base, "width", width);
1586 }
1588 sp_export_value_set_px (base, "x1", x0 + width);
1589 sp_export_value_set (base, "bmwidth", bmwidth);
1591 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1593 return;
1594 } // end of sp_export_area_width_value_changed()
1596 /// Called when y1-y0 or area height is changed.
1597 static void
1598 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1599 {
1601 float y0, y1, ydpi, height, bmheight;
1603 if (gtk_object_get_data (base, "update"))
1604 return;
1606 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1607 (base, "units"))) {
1608 return;
1609 }
1611 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1613 y0 = sp_export_value_get_px (base, "y0");
1614 y1 = sp_export_value_get_px (base, "y1");
1615 ydpi = sp_export_value_get (base, "ydpi");
1616 height = sp_export_value_get_px (base, "height");
1617 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1619 if (bmheight < SP_EXPORT_MIN_SIZE) {
1620 bmheight = SP_EXPORT_MIN_SIZE;
1621 height = bmheight * DPI_BASE / ydpi;
1622 sp_export_value_set_px (base, "height", height);
1623 }
1625 sp_export_value_set_px (base, "y1", y0 + height);
1626 sp_export_value_set (base, "bmheight", bmheight);
1628 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1630 return;
1631 } // end of sp_export_area_height_value_changed()
1633 /**
1634 \brief A function to set the ydpi
1635 \param base The export dialog
1637 This function grabs all of the y values and then figures out the
1638 new bitmap size based on the changing dpi value. The dpi value is
1639 gotten from the xdpi setting as these can not currently be independent.
1640 */
1641 static void
1642 sp_export_set_image_y (GtkObject *base)
1643 {
1644 float y0, y1, xdpi;
1646 y0 = sp_export_value_get_px (base, "y0");
1647 y1 = sp_export_value_get_px (base, "y1");
1648 xdpi = sp_export_value_get (base, "xdpi");
1650 sp_export_value_set (base, "ydpi", xdpi);
1651 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1653 return;
1654 } // end of sp_export_set_image_y()
1656 /**
1657 \brief A function to set the xdpi
1658 \param base The export dialog
1660 This function grabs all of the x values and then figures out the
1661 new bitmap size based on the changing dpi value. The dpi value is
1662 gotten from the xdpi setting as these can not currently be independent.
1663 */
1664 static void
1665 sp_export_set_image_x (GtkObject *base)
1666 {
1667 float x0, x1, xdpi;
1669 x0 = sp_export_value_get_px (base, "x0");
1670 x1 = sp_export_value_get_px (base, "x1");
1671 xdpi = sp_export_value_get (base, "xdpi");
1673 sp_export_value_set (base, "ydpi", xdpi);
1674 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1676 return;
1677 } // end of sp_export_set_image_x()
1679 /// Called when pixel width is changed
1680 static void
1681 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1682 {
1683 float x0, x1, bmwidth, xdpi;
1685 if (gtk_object_get_data (base, "update"))
1686 return;
1688 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1689 (base, "units"))) {
1690 return;
1691 }
1693 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1695 x0 = sp_export_value_get_px (base, "x0");
1696 x1 = sp_export_value_get_px (base, "x1");
1697 bmwidth = sp_export_value_get (base, "bmwidth");
1699 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1700 bmwidth = SP_EXPORT_MIN_SIZE;
1701 sp_export_value_set (base, "bmwidth", bmwidth);
1702 }
1704 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1705 sp_export_value_set (base, "xdpi", xdpi);
1707 sp_export_set_image_y (base);
1709 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1711 return;
1712 } // end of sp_export_bitmap_width_value_changed()
1714 /// Called when pixel height is changed
1715 static void
1716 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1717 {
1718 float y0, y1, bmheight, xdpi;
1720 if (gtk_object_get_data (base, "update"))
1721 return;
1723 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1724 (base, "units"))) {
1725 return;
1726 }
1728 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1730 y0 = sp_export_value_get_px (base, "y0");
1731 y1 = sp_export_value_get_px (base, "y1");
1732 bmheight = sp_export_value_get (base, "bmheight");
1734 if (bmheight < SP_EXPORT_MIN_SIZE) {
1735 bmheight = SP_EXPORT_MIN_SIZE;
1736 sp_export_value_set (base, "bmheight", bmheight);
1737 }
1739 xdpi = bmheight * DPI_BASE / (y1 - y0);
1740 sp_export_value_set (base, "xdpi", xdpi);
1742 sp_export_set_image_x (base);
1744 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1746 return;
1747 } // end of sp_export_bitmap_width_value_changed()
1749 /**
1750 \brief A function to adjust the bitmap width when the xdpi value changes
1751 \param adj The adjustment that was changed
1752 \param base The export dialog itself
1754 The first thing this function checks is to see if we are doing an
1755 update. If we are, this function just returns because there is another
1756 instance of it that will handle everything for us. If there is a
1757 units change, we also assume that everyone is being updated appropriately
1758 and there is nothing for us to do.
1760 If we're the highest level function, we set the update flag, and
1761 continue on our way.
1763 All of the values are grabbed using the \c sp_export_value_get functions
1764 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1765 xdpi value is saved in the preferences for the next time the dialog
1766 is opened. (does the selection dpi need to be set here?)
1768 A check is done to to ensure that we aren't outputing an invalid width,
1769 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1770 changed to make it valid.
1772 After all of this the bitmap width is changed.
1774 We also change the ydpi. This is a temporary hack as these can not
1775 currently be independent. This is likely to change in the future.
1776 */
1777 void
1778 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1779 {
1780 float x0, x1, xdpi, bmwidth;
1782 if (gtk_object_get_data (base, "update"))
1783 return;
1785 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1786 (base, "units"))) {
1787 return;
1788 }
1790 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1792 x0 = sp_export_value_get_px (base, "x0");
1793 x1 = sp_export_value_get_px (base, "x1");
1794 xdpi = sp_export_value_get (base, "xdpi");
1796 // remember xdpi setting
1797 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1798 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1800 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1802 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1803 bmwidth = SP_EXPORT_MIN_SIZE;
1804 if (x1 != x0)
1805 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1806 else
1807 xdpi = DPI_BASE;
1808 sp_export_value_set (base, "xdpi", xdpi);
1809 }
1811 sp_export_value_set (base, "bmwidth", bmwidth);
1813 sp_export_set_image_y (base);
1815 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1817 return;
1818 } // end of sp_export_xdpi_value_changed()
1821 /**
1822 \brief A function to change the area that is used for the exported
1823 bitmap.
1824 \param base This is the export dialog
1825 \param x0 Horizontal upper left hand corner of the picture in points
1826 \param y0 Vertical upper left hand corner of the picture in points
1827 \param x1 Horizontal lower right hand corner of the picture in points
1828 \param y1 Vertical lower right hand corner of the picture in points
1830 This function just calls \c sp_export_value_set_px for each of the
1831 parameters that is passed in. This allows for setting them all in
1832 one convient area.
1834 Update is set to suspend all of the other test running while all the
1835 values are being set up. This allows for a performance increase, but
1836 it also means that the wrong type won't be detected with only some of
1837 the values set. After all the values are set everyone is told that
1838 there has been an update.
1839 */
1840 static void
1841 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1842 {
1843 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1844 sp_export_value_set_px (base, "x1", x1);
1845 sp_export_value_set_px (base, "y1", y1);
1846 sp_export_value_set_px (base, "x0", x0);
1847 sp_export_value_set_px (base, "y0", y0);
1848 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1850 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1851 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1853 return;
1854 }
1856 /**
1857 \brief Sets the value of an adjustment
1858 \param base The export dialog
1859 \param key Which adjustment to set
1860 \param val What value to set it to
1862 This function finds the adjustment using the data stored in the
1863 export dialog. After finding the adjustment it then sets
1864 the value of it.
1865 */
1866 static void
1867 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1868 {
1869 GtkAdjustment *adj;
1871 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1873 gtk_adjustment_set_value (adj, val);
1874 }
1876 /**
1877 \brief A function to set a value using the units points
1878 \param base The export dialog
1879 \param key Which value should be set
1880 \param val What the value should be in points
1882 This function first gets the adjustment for the key that is passed
1883 in. It then figures out what units are currently being used in the
1884 dialog. After doing all of that, it then converts the incoming
1885 value and sets the adjustment.
1886 */
1887 static void
1888 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1889 {
1890 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1892 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1894 return;
1895 }
1897 /**
1898 \brief Get the value of an adjustment in the export dialog
1899 \param base The export dialog
1900 \param key Which adjustment is being looked for
1901 \return The value in the specified adjustment
1903 This function gets the adjustment from the data field in the export
1904 dialog. It then grabs the value from the adjustment.
1905 */
1906 static float
1907 sp_export_value_get ( GtkObject *base, const gchar *key )
1908 {
1909 GtkAdjustment *adj;
1911 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1913 return adj->value;
1914 }
1916 /**
1917 \brief Grabs a value in the export dialog and converts the unit
1918 to points
1919 \param base The export dialog
1920 \param key Which value should be returned
1921 \return The value in the adjustment in points
1923 This function, at its most basic, is a call to \c sp_export_value_get
1924 to get the value of the adjustment. It then finds the units that
1925 are being used by looking at the "units" attribute of the export
1926 dialog. Using that it converts the returned value into points.
1927 */
1928 static float
1929 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1930 {
1931 float value = sp_export_value_get(base, key);
1932 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1934 return sp_units_get_pixels (value, *unit);
1935 } // end of sp_export_value_get_px()
1937 /**
1938 \brief This function is called when the filename is changed by
1939 anyone. It resets the virgin bit.
1940 \param object Text entry box
1941 \param data The export dialog
1942 \return None
1944 This function gets called when the text area is modified. It is
1945 looking for the case where the text area is modified from its
1946 original value. In that case it sets the "filename-modified" bit
1947 to TRUE. If the text dialog returns back to the original text, the
1948 bit gets reset. This should stop simple mistakes.
1949 */
1950 static void
1951 sp_export_filename_modified (GtkObject * object, gpointer data)
1952 {
1953 GtkWidget * text_entry = (GtkWidget *)object;
1954 GtkWidget * export_dialog = (GtkWidget *)data;
1956 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1957 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1958 // printf("Modified: FALSE\n");
1959 } else {
1960 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1961 // printf("Modified: TRUE\n");
1962 }
1964 return;
1965 } // end sp_export_filename_modified
1967 /*
1968 Local Variables:
1969 mode:c++
1970 c-file-style:"stroustrup"
1971 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1972 indent-tabs-mode:nil
1973 fill-column:99
1974 End:
1975 */
1976 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :