1 #define __SP_EXPORT_C__
3 /** \file
4 * \brief PNG export dialog
5 */
7 /*
8 * Authors:
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * bulia byak <buliabyak@users.sf.net>
11 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
12 *
13 * Copyright (C) 1999-2007 Authors
14 * Copyright (C) 2001-2002 Ximian, Inc.
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <gtkmm/box.h>
25 #include <gtkmm/buttonbox.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/togglebutton.h>
29 #include <gtkmm/entry.h>
30 #include <gtkmm/image.h>
31 #include <gtkmm/stockid.h>
32 #include <gtkmm/stock.h>
33 #ifdef WITH_GNOME_VFS
34 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
35 #endif
37 #include <glibmm/i18n.h>
38 #include "helper/unit-menu.h"
39 #include "helper/units.h"
40 #include "unit-constants.h"
41 #include "helper/window.h"
42 #include "inkscape-private.h"
43 #include "document.h"
44 #include "desktop-handles.h"
45 #include "sp-item.h"
46 #include "selection.h"
47 #include "file.h"
48 #include "macros.h"
49 #include "sp-namedview.h"
50 #include "selection-chemistry.h"
52 #include "dialog-events.h"
53 #include "../prefs-utils.h"
54 #include "../verbs.h"
55 #include "../interface.h"
57 #include "extension/output.h"
58 #include "extension/db.h"
60 #include "io/sys.h"
62 #include "helper/png-write.h"
63 #include <png.h>
66 #define SP_EXPORT_MIN_SIZE 1.0
68 #define DPI_BASE PX_PER_IN
70 #define EXPORT_COORD_PRECISION 3
72 #define MIN_ONSCREEN_DISTANCE 50
74 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
75 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
76 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
78 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
79 GtkObject *base);
81 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
82 GtkObject *base);
84 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
85 GtkObject *base);
87 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
88 GtkObject *base);
90 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
91 GtkObject *base);
93 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
94 GtkObject *base);
96 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
97 GtkObject *base);
99 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
100 Inkscape::Selection *selection,
101 GtkObject *base);
102 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
103 Inkscape::Selection *selection,
104 guint flags,
105 GtkObject *base );
107 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
108 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
109 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
110 static float sp_export_value_get ( GtkObject *base, const gchar *key );
111 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
113 static void sp_export_filename_modified (GtkObject * object, gpointer data);
114 static inline void sp_export_find_default_selection(GtkWidget * dlg);
115 static void sp_export_detect_size(GtkObject * base);
117 static const gchar *prefs_path = "dialogs.export";
119 // these all need to be reinitialized to their defaults during dialog_destroy
120 static GtkWidget *dlg = NULL;
121 static win_data wd;
122 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
123 static gchar * original_name = NULL;
124 static gchar * doc_export_name = NULL;
125 static bool was_empty = TRUE;
127 /** What type of button is being pressed */
128 enum selection_type {
129 SELECTION_PAGE = 0, /**< Export the whole page */
130 SELECTION_DRAWING, /**< Export everything drawn on the page */
131 SELECTION_SELECTION, /**< Export everything that is selected */
132 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
133 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
134 };
136 /** A list of strings that is used both in the preferences, and in the
137 data fields to describe the various values of \c selection_type. */
138 static const char * selection_names[SELECTION_NUMBER_OF] = {
139 "page", "drawing", "selection", "custom"};
141 /** The names on the buttons for the various selection types. */
142 static const char * selection_labels[SELECTION_NUMBER_OF] = {
143 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
145 static void
146 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
147 {
148 sp_signal_disconnect_by_data (INKSCAPE, dlg);
150 wd.win = dlg = NULL;
151 wd.stop = 0;
152 x = -1000; y = -1000; w = 0; h = 0;
153 g_free(original_name);
154 original_name = NULL;
155 g_free(doc_export_name);
156 doc_export_name = NULL;
157 was_empty = TRUE;
159 return;
160 } // end of sp_export_dialog_destroy()
162 /// Called when dialog is closed or inkscape is shut down.
163 static bool
164 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
165 {
167 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
168 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
170 if (x<0) x=0;
171 if (y<0) y=0;
173 prefs_set_int_attribute (prefs_path, "x", x);
174 prefs_set_int_attribute (prefs_path, "y", y);
175 prefs_set_int_attribute (prefs_path, "w", w);
176 prefs_set_int_attribute (prefs_path, "h", h);
178 return FALSE; // which means, go ahead and destroy it
180 } // end of sp_export_dialog_delete()
182 /**
183 \brief Creates a new spin button for the export dialog
184 \param key The name of the spin button
185 \param val A default value for the spin button
186 \param min Minimum value for the spin button
187 \param max Maximum value for the spin button
188 \param step The step size for the spin button
189 \param page Size of the page increment
190 \param us Unit selector that effects this spin button
191 \param t Table to put the spin button in
192 \param x X location in the table \c t to start with
193 \param y Y location in the table \c t to start with
194 \param ll Text to put on the left side of the spin button (optional)
195 \param lr Text to put on the right side of the spin button (optional)
196 \param digits Number of digits to display after the decimal
197 \param sensitive Whether the spin button is sensitive or not
198 \param cb Callback for when this spin button is changed (optional)
199 \param dlg Export dialog the spin button is being placed in
201 */
202 static void
203 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
204 float step, float page, GtkWidget *us,
205 GtkWidget *t, int x, int y,
206 const gchar *ll, const gchar *lr,
207 int digits, unsigned int sensitive,
208 GCallback cb, GtkWidget *dlg )
209 {
210 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
211 gtk_object_set_data (a, "key", key);
212 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
214 if (us) {
215 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
216 GTK_ADJUSTMENT (a) );
217 }
219 int pos = 0;
221 GtkWidget *l = NULL;
223 if (ll) {
225 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
226 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
227 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
228 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229 gtk_widget_set_sensitive (l, sensitive);
230 pos += 1;
232 }
234 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
235 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
236 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
237 gtk_widget_set_size_request (sb, 80, -1);
238 gtk_widget_set_sensitive (sb, sensitive);
239 pos += 1;
241 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
243 if (lr) {
245 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
246 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
247 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
248 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
249 gtk_widget_set_sensitive (l, sensitive);
250 pos += 1;
252 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
253 }
255 if (cb)
256 gtk_signal_connect (a, "value_changed", cb, dlg);
258 return;
259 } // end of sp_export_spinbutton_new()
262 static Gtk::VBox *
263 sp_export_dialog_area_box (GtkWidget * dlg)
264 {
265 Gtk::VBox* vb = new Gtk::VBox(false, 3);
267 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
268 lbl->set_use_markup(true);
269 vb->pack_start(*lbl);
271 /* Units box */
272 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
273 /* gets added to the vbox later, but the unit selector is needed
274 earlier than that */
276 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
277 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
278 if (desktop)
279 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
280 unitbox->pack_end(*us, false, false, 0);
281 Gtk::Label* l = new Gtk::Label(_("Units:"));
282 unitbox->pack_end(*l, false, false, 3);
283 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
285 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
287 Gtk::ToggleButton* b;
288 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
289 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
290 b->set_data("key", GINT_TO_POINTER(i));
291 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
292 togglebox->pack_start(*b, false, true, 0);
293 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
294 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
295 }
297 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
298 G_CALLBACK (sp_export_selection_changed), dlg );
299 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
300 G_CALLBACK (sp_export_selection_modified), dlg );
301 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
302 G_CALLBACK (sp_export_selection_changed), dlg );
304 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
305 t->set_row_spacings (4);
306 t->set_col_spacings (4);
308 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
309 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
310 G_CALLBACK ( sp_export_area_x_value_changed),
311 dlg );
313 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
314 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
315 G_CALLBACK (sp_export_area_x_value_changed),
316 dlg );
318 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
319 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
320 G_CALLBACK
321 (sp_export_area_width_value_changed),
322 dlg );
324 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
325 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
326 G_CALLBACK (sp_export_area_y_value_changed),
327 dlg );
329 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
330 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
331 G_CALLBACK (sp_export_area_y_value_changed),
332 dlg );
334 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
335 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
336 G_CALLBACK (sp_export_area_height_value_changed),
337 dlg );
339 vb->pack_start(*togglebox, false, false, 3);
340 vb->pack_start(*t, false, false, 0);
341 vb->pack_start(*unitbox, false, false, 0);
343 return vb;
344 } // end of sp_export_dialog_area_box
347 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
349 if (id == NULL) /* This should never happen */
350 id = "bitmap";
352 gchar * directory = NULL;
354 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
355 // std::cout << "Directory from dialog" << std::endl;
356 directory = g_dirname(file_entry_text);
357 }
359 if (directory == NULL) {
360 /* Grab document directory */
361 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
362 // std::cout << "Directory from document" << std::endl;
363 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
364 }
365 }
367 if (directory == NULL) {
368 // std::cout << "Home Directory" << std::endl;
369 directory = homedir_path(NULL);
370 }
372 gchar * id_ext = g_strconcat(id, ".png", NULL);
373 gchar *filename = g_build_filename(directory, id_ext, NULL);
374 g_free(directory);
375 g_free(id_ext);
376 return filename;
377 }
379 static void
380 batch_export_clicked (GtkWidget *widget, GtkObject *base)
381 {
382 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
383 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
384 vb_singleexport->set_sensitive(false);
385 } else {
386 vb_singleexport->set_sensitive(true);
387 }
388 }
390 void
391 sp_export_dialog (void)
392 {
393 if (!dlg) {
395 gchar title[500];
396 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
398 dlg = sp_window_new (title, TRUE);
400 if (x == -1000 || y == -1000) {
401 x = prefs_get_int_attribute (prefs_path, "x", 0);
402 y = prefs_get_int_attribute (prefs_path, "y", 0);
403 }
405 if (w ==0 || h == 0) {
406 w = prefs_get_int_attribute (prefs_path, "w", 0);
407 h = prefs_get_int_attribute (prefs_path, "h", 0);
408 }
410 // if (x<0) x=0;
411 // if (y<0) y=0;
413 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
414 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
415 gtk_window_move ((GtkWindow *) dlg, x, y);
416 else
417 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
418 sp_transientize (dlg);
419 wd.win = dlg;
420 wd.stop = 0;
422 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
423 G_CALLBACK (sp_transientize_callback), &wd);
425 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
426 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
428 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
429 G_CALLBACK (sp_export_dialog_destroy), dlg);
431 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
432 G_CALLBACK (sp_export_dialog_delete), dlg);
434 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
435 G_CALLBACK (sp_export_dialog_delete), dlg);
437 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
438 G_CALLBACK (sp_dialog_hide), dlg);
440 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
441 G_CALLBACK (sp_dialog_unhide), dlg);
443 GtkTooltips *tt = gtk_tooltips_new();
445 Gtk::VBox *vb = new Gtk::VBox(false, 3);
446 vb->set_border_width(3);
447 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
449 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
450 vb_singleexport->set_border_width(0);
451 vb->pack_start(*vb_singleexport);
452 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
454 /* Export area frame */
455 {
456 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
457 area_box->set_border_width(3);
458 vb_singleexport->pack_start(*area_box, false, false, 0);
459 }
461 /* Bitmap size frame */
462 {
463 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
464 size_box->set_border_width(3);
466 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
467 lbl->set_use_markup(true);
468 size_box->pack_start(*lbl, false, false, 0);
469 const int rows = 2;
470 const int cols = 5;
471 const bool homogeneous = false;
472 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
473 t->set_row_spacings (4);
474 t->set_col_spacings (4);
475 size_box->pack_start(*t);
477 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
478 NULL, GTK_WIDGET(t->gobj()), 0, 0,
479 _("_Width:"), _("pixels at"), 0, 1,
480 G_CALLBACK
481 (sp_export_bitmap_width_value_changed),
482 dlg );
484 sp_export_spinbutton_new ( "xdpi",
485 prefs_get_double_attribute
486 ( "dialogs.export.defaultxdpi",
487 "value", DPI_BASE),
488 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
489 NULL, _("dp_i"), 2, 1,
490 G_CALLBACK (sp_export_xdpi_value_changed),
491 dlg );
493 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
494 NULL, GTK_WIDGET(t->gobj()), 0, 1,
495 _("Height:"), _("pixels at"), 0, 1,
496 G_CALLBACK
497 (sp_export_bitmap_height_value_changed),
498 dlg );
500 /** \todo
501 * Needs fixing: there's no way to set ydpi currently, so we use
502 * the defaultxdpi value here, too...
503 */
504 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
505 ( "dialogs.export.defaultxdpi",
506 "value", DPI_BASE),
507 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
508 NULL, _("dpi"), 2, 0, NULL, dlg );
510 vb_singleexport->pack_start(*size_box);
511 }
513 /* File entry */
514 {
515 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
516 file_box->set_border_width(3);
518 // true = has mnemonic
519 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
520 flabel->set_use_markup(true);
521 file_box->pack_start(*flabel, false, false, 0);
523 Gtk::Entry *fe = new Gtk::Entry();
525 /*
526 * set the default filename to be that of the current path + document
527 * with .png extension
528 *
529 * One thing to notice here is that this filename may get
530 * overwritten, but it won't happen here. The filename gets
531 * written into the text field, but then the button to select
532 * the area gets set. In that code the filename can be changed
533 * if there are some with presidence in the document. So, while
534 * this code sets the name first, it may not be the one users
535 * really see.
536 */
537 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
538 {
539 gchar *name;
540 SPDocument * doc = SP_ACTIVE_DOCUMENT;
541 const gchar *uri = SP_DOCUMENT_URI (doc);
542 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
543 const gchar * text_extension = repr->attribute("inkscape:output_extension");
544 Inkscape::Extension::Output * oextension = NULL;
546 if (text_extension != NULL) {
547 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
548 }
550 if (oextension != NULL) {
551 gchar * old_extension = oextension->get_extension();
552 if (g_str_has_suffix(uri, old_extension)) {
553 gchar * uri_copy;
554 gchar * extension_point;
555 gchar * final_name;
557 uri_copy = g_strdup(uri);
558 extension_point = g_strrstr(uri_copy, old_extension);
559 extension_point[0] = '\0';
561 final_name = g_strconcat(uri_copy, ".png", NULL);
562 fe->set_text(final_name);
564 g_free(final_name);
565 g_free(uri_copy);
566 }
567 } else {
568 name = g_strconcat(uri, ".png", NULL);
569 fe->set_text(name);
570 g_free(name);
571 }
573 doc_export_name = g_strdup(fe->get_text().c_str());
574 }
575 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
576 G_CALLBACK (sp_export_filename_modified), dlg);
578 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
580 {
581 // true = has mnemonic
582 Gtk::Button *b = new Gtk::Button();
584 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
585 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
586 Gtk::ICON_SIZE_BUTTON);
587 pixlabel->pack_start(*im);
589 Gtk::Label *l = new Gtk::Label();
590 l->set_markup_with_mnemonic(_("_Browse..."));
591 pixlabel->pack_start(*l);
593 b->add(*pixlabel);
595 hb->pack_end (*b, false, false, 4);
596 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
597 G_CALLBACK (sp_export_browse_clicked), NULL );
598 }
600 hb->pack_start (*fe, true, true, 0);
601 file_box->add(*hb);
602 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
603 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
604 original_name = g_strdup(fe->get_text().c_str());
605 // pressing enter in the filename field is the same as clicking export:
606 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
607 G_CALLBACK (sp_export_export_clicked), dlg );
608 // focus is in the filename initially:
609 fe->grab_focus();
611 // mnemonic in frame label moves focus to filename:
612 flabel->set_mnemonic_widget(*fe);
614 vb_singleexport->pack_start(*file_box);
615 }
617 {
618 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
619 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
620 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
621 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
622 batch_box->pack_start(*Glib::wrap(be), false, false);
623 gtk_tooltips_set_tip(tt, be, _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"), NULL);
624 batch_box->show_all();
625 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
626 vb->pack_start(*batch_box);
627 }
629 {
630 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
631 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
632 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
633 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
634 hide_box->pack_start(*Glib::wrap(he), false, false);
635 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
636 hide_box->show_all();
637 vb->pack_start(*hide_box);
638 }
640 /* Buttons */
641 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
642 bb->set_border_width(3);
644 {
645 Gtk::Button *b = new Gtk::Button();
646 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
647 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
648 Gtk::ICON_SIZE_BUTTON);
649 image_label->pack_start(*im);
651 Gtk::Label *l = new Gtk::Label();
652 l->set_markup_with_mnemonic(_("_Export"));
653 image_label->pack_start(*l);
655 b->add(*image_label);
656 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
657 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
658 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
659 bb->pack_end(*b, false, false, 0);
660 }
662 vb->pack_end(*bb, false, false, 0);
663 vb->show_all();
665 } // end of if (!dlg)
667 sp_export_find_default_selection(dlg);
669 gtk_window_present ((GtkWindow *) dlg);
671 return;
672 } // end of sp_export_dialog()
674 static void
675 sp_export_update_checkbuttons (GtkObject *base)
676 {
677 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
678 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
679 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
680 if (num >= 2) {
681 gtk_widget_set_sensitive (be, true);
682 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (_("Batch export %d selected objects"), num));
683 } else {
684 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
685 gtk_widget_set_sensitive (be, FALSE);
686 }
687 if (num > 0) {
688 gtk_widget_set_sensitive (he, true);
689 } else {
690 gtk_widget_set_sensitive (he, false);
691 }
692 }
694 static inline void
695 sp_export_find_default_selection(GtkWidget * dlg)
696 {
697 selection_type key = SELECTION_NUMBER_OF;
699 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
700 key = SELECTION_SELECTION;
701 }
703 /* Try using the preferences */
704 if (key == SELECTION_NUMBER_OF) {
705 const gchar *what = NULL;
706 int i = SELECTION_NUMBER_OF;
708 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
710 if (what != NULL) {
711 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
712 if (!strcmp (what, selection_names[i])) {
713 break;
714 }
715 }
716 }
718 key = (selection_type)i;
719 }
721 if (key == SELECTION_NUMBER_OF) {
722 key = SELECTION_SELECTION;
723 }
725 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
726 selection_names[key]);
727 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
729 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
730 }
733 /**
734 * \brief If selection changed or a different document activated, we must
735 * recalculate any chosen areas
736 *
737 */
738 static void
739 sp_export_selection_changed ( Inkscape::Application *inkscape,
740 Inkscape::Selection *selection,
741 GtkObject *base )
742 {
743 selection_type current_key;
744 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
746 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
747 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
748 was_empty) {
749 gtk_toggle_button_set_active
750 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
751 TRUE );
752 }
753 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
755 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
757 if (inkscape &&
758 SP_IS_INKSCAPE (inkscape) &&
759 selection &&
760 SELECTION_CUSTOM != current_key) {
761 GtkToggleButton * button;
762 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
763 sp_export_area_toggled(button, base);
764 }
766 sp_export_update_checkbuttons (base);
767 }
769 static void
770 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
771 Inkscape::Selection */*selection*/,
772 guint /*flags*/,
773 GtkObject *base )
774 {
775 selection_type current_key;
776 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
778 switch (current_key) {
779 case SELECTION_DRAWING:
780 if ( SP_ACTIVE_DESKTOP ) {
781 SPDocument *doc;
782 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
783 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
784 if (bbox) {
785 sp_export_set_area (base, bbox->min()[NR::X],
786 bbox->min()[NR::Y],
787 bbox->max()[NR::X],
788 bbox->max()[NR::Y]);
789 }
790 }
791 break;
792 case SELECTION_SELECTION:
793 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
794 NRRect bbox;
795 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
796 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
797 }
798 break;
799 default:
800 /* Do nothing for page or for custom */
801 break;
802 }
804 return;
805 }
807 /// Called when one of the selection buttons was toggled.
808 static void
809 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
810 {
811 if (gtk_object_get_data (base, "update"))
812 return;
814 selection_type key, old_key;
815 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
816 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
818 /* Ignore all "turned off" events unless we're the only active button */
819 if (!gtk_toggle_button_get_active (tb) ) {
821 /* Don't let the current selection be deactived - but rerun the
822 activate to allow the user to renew the values */
823 if (key == old_key) {
824 gtk_toggle_button_set_active ( tb, TRUE );
825 }
827 return;
828 }
830 /* Turn off the currently active button unless it's us */
831 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
833 if (old_key != key) {
834 gtk_toggle_button_set_active
835 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
836 FALSE );
837 }
839 if ( SP_ACTIVE_DESKTOP )
840 {
841 SPDocument *doc;
842 NR::Maybe<NR::Rect> bbox;
843 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
845 /* Notice how the switch is used to 'fall through' here to get
846 various backups. If you modify this without noticing you'll
847 probabaly screw something up. */
848 switch (key) {
849 case SELECTION_SELECTION:
850 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
851 {
852 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
853 /* Only if there is a selection that we can set
854 do we break, otherwise we fall through to the
855 drawing */
856 // std::cout << "Using selection: SELECTION" << std::endl;
857 key = SELECTION_SELECTION;
858 break;
859 }
860 case SELECTION_DRAWING:
861 /** \todo
862 * This returns wrong values if the document has a viewBox.
863 */
864 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
865 /* If the drawing is valid, then we'll use it and break
866 otherwise we drop through to the page settings */
867 if (bbox) {
868 // std::cout << "Using selection: DRAWING" << std::endl;
869 key = SELECTION_DRAWING;
870 break;
871 }
872 case SELECTION_PAGE:
873 bbox = NR::Rect(NR::Point(0.0, 0.0),
874 NR::Point(sp_document_width(doc), sp_document_height(doc))
875 );
877 // std::cout << "Using selection: PAGE" << std::endl;
878 key = SELECTION_PAGE;
879 break;
880 case SELECTION_CUSTOM:
881 default:
882 break;
883 } // switch
885 // remember area setting
886 prefs_set_string_attribute ( "dialogs.export.exportarea",
887 "value", selection_names[key]);
889 if ( key != SELECTION_CUSTOM && bbox ) {
890 sp_export_set_area (base, bbox->min()[NR::X],
891 bbox->min()[NR::Y],
892 bbox->max()[NR::X],
893 bbox->max()[NR::Y]);
894 }
896 } // end of if ( SP_ACTIVE_DESKTOP )
899 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
900 GtkWidget * file_entry;
901 const gchar * filename = NULL;
902 float xdpi = 0.0, ydpi = 0.0;
904 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
906 switch (key) {
907 case SELECTION_PAGE:
908 case SELECTION_DRAWING: {
909 SPDocument * doc = SP_ACTIVE_DOCUMENT;
910 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
912 if (filename == NULL) {
913 if (doc_export_name != NULL) {
914 filename = g_strdup(doc_export_name);
915 } else {
916 filename = g_strdup("");
917 }
918 }
919 break;
920 }
921 case SELECTION_SELECTION:
922 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
924 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
926 /* If we still don't have a filename -- let's build
927 one that's nice */
928 if (filename == NULL) {
929 const gchar * id = NULL;
930 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
931 for(; reprlst != NULL; reprlst = reprlst->next) {
932 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
933 if (repr->attribute("id")) {
934 id = repr->attribute("id");
935 break;
936 }
937 }
939 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
940 }
941 }
942 break;
943 case SELECTION_CUSTOM:
944 default:
945 break;
946 }
948 if (filename != NULL) {
949 g_free(original_name);
950 original_name = g_strdup(filename);
951 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
952 }
954 if (xdpi != 0.0) {
955 sp_export_value_set(base, "xdpi", xdpi);
956 }
958 /* These can't be separate, and setting x sets y, so for
959 now setting this is disabled. Hopefully it won't be in
960 the future */
961 if (FALSE && ydpi != 0.0) {
962 sp_export_value_set(base, "ydpi", ydpi);
963 }
964 }
966 return;
967 } // end of sp_export_area_toggled()
969 /// Called when dialog is deleted
970 static gint
971 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
972 {
973 g_object_set_data (base, "cancel", (gpointer) 1);
974 return TRUE;
975 } // end of sp_export_progress_delete()
977 /// Called when progress is cancelled
978 static void
979 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
980 {
981 g_object_set_data (base, "cancel", (gpointer) 1);
982 } // end of sp_export_progress_cancel()
984 /// Called for every progress iteration
985 static unsigned int
986 sp_export_progress_callback (float value, void *data)
987 {
988 GtkWidget *prg;
989 int evtcount;
991 if (g_object_get_data ((GObject *) data, "cancel"))
992 return FALSE;
994 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
995 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
997 evtcount = 0;
998 while ((evtcount < 16) && gdk_events_pending ()) {
999 gtk_main_iteration_do (FALSE);
1000 evtcount += 1;
1001 }
1003 gtk_main_iteration_do (FALSE);
1005 return TRUE;
1007 } // end of sp_export_progress_callback()
1009 GtkWidget *
1010 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1011 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1013 dlg = gtk_dialog_new ();
1014 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1015 prg = gtk_progress_bar_new ();
1016 sp_transientize (dlg);
1017 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1018 g_object_set_data ((GObject *) base, "progress", prg);
1020 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1022 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1023 GTK_PROGRESS_LEFT_TO_RIGHT);
1024 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1025 prg, FALSE, FALSE, 4 );
1026 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1027 GTK_STOCK_CANCEL,
1028 GTK_RESPONSE_CANCEL );
1030 g_signal_connect ( (GObject *) dlg, "delete_event",
1031 (GCallback) sp_export_progress_delete, base);
1032 g_signal_connect ( (GObject *) btn, "clicked",
1033 (GCallback) sp_export_progress_cancel, base);
1034 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1035 gtk_widget_show_all (dlg);
1037 return dlg;
1038 }
1040 // FIXME: Some lib function should be available to do this ...
1041 static gchar *
1042 filename_add_extension (const gchar *filename, const gchar *extension)
1043 {
1044 gchar *dot;
1046 dot = strrchr (filename, '.');
1047 if ( !dot )
1048 return g_strconcat (filename, ".", extension, NULL);
1049 {
1050 if (dot[1] == '\0')
1051 return g_strconcat (filename, extension, NULL);
1052 else
1053 {
1054 if (g_strcasecmp (dot + 1, extension) == 0)
1055 return g_strdup (filename);
1056 else
1057 {
1058 return g_strconcat (filename, ".", extension, NULL);
1059 }
1060 }
1061 }
1062 }
1064 /// Called when export button is clicked
1065 static void
1066 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1067 {
1068 if (!SP_ACTIVE_DESKTOP) return;
1070 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1072 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1073 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1074 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1075 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1076 // Batch export of selected objects
1078 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1079 gint n = 0;
1081 if (num < 1)
1082 return;
1084 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1085 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1086 g_free (progress_text);
1088 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1089 i != NULL;
1090 i = i->next) {
1091 SPItem *item = (SPItem *) i->data;
1092 // retrieve export filename hint
1093 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1094 if (!fn) {
1095 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1096 }
1098 // retrieve export dpi hints
1099 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1100 gdouble dpi = 0.0;
1101 if (dpi_hint) {
1102 dpi = atof(dpi_hint);
1103 }
1104 if (dpi == 0.0) {
1105 dpi = DPI_BASE;
1106 }
1108 NRRect area;
1109 sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1111 gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1112 gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1114 if (width > 1 && height > 1) {
1115 /* Do export */
1116 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1117 area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1118 nv->pagecolor,
1119 NULL, NULL, TRUE, // overwrite without asking
1120 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1121 )) {
1122 gchar * error;
1123 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1124 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1125 sp_ui_error_dialog(error);
1126 g_free(safeFile);
1127 g_free(error);
1128 }
1129 }
1130 n++;
1131 sp_export_progress_callback((float)n/num, base);
1132 }
1134 gtk_widget_destroy (prog_dlg);
1135 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1137 } else {
1139 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1140 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1142 float const x0 = sp_export_value_get_px(base, "x0");
1143 float const y0 = sp_export_value_get_px(base, "y0");
1144 float const x1 = sp_export_value_get_px(base, "x1");
1145 float const y1 = sp_export_value_get_px(base, "y1");
1146 float const xdpi = sp_export_value_get(base, "xdpi");
1147 float const ydpi = sp_export_value_get(base, "ydpi");
1148 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1149 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1151 if (filename == NULL || *filename == '\0') {
1152 sp_ui_error_dialog(_("You have to enter a filename"));
1153 return;
1154 }
1156 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1157 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1158 return;
1159 }
1161 gchar *dirname = g_path_get_dirname(filename);
1162 if ( dirname == NULL
1163 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1164 {
1165 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1166 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1167 safeDir);
1168 sp_ui_error_dialog(error);
1169 g_free(safeDir);
1170 g_free(error);
1171 g_free(dirname);
1172 return;
1173 }
1174 g_free(dirname);
1176 // make sure that .png is the extension of the file:
1177 gchar * filename_ext = filename_add_extension(filename, "png");
1178 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1180 gchar *fn = g_path_get_basename (filename_ext);
1182 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1183 g_free (fn);
1184 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1185 g_free (progress_text);
1187 /* Do export */
1188 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1189 x0, y0, x1, y1, width, height, xdpi, ydpi,
1190 nv->pagecolor,
1191 sp_export_progress_callback, base, FALSE,
1192 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1193 )) {
1194 gchar * error;
1195 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1196 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1197 sp_ui_error_dialog(error);
1198 g_free(safeFile);
1199 g_free(error);
1200 }
1202 /* Reset the filename so that it can be changed again by changing
1203 selections and all that */
1204 g_free(original_name);
1205 original_name = g_strdup(filename_ext);
1206 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1208 gtk_widget_destroy (prog_dlg);
1209 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1211 /* Setup the values in the document */
1212 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1213 case SELECTION_PAGE:
1214 case SELECTION_DRAWING: {
1215 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1216 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1217 bool modified = false;
1218 const gchar * temp_string;
1220 bool saved = sp_document_get_undo_sensitive(doc);
1221 sp_document_set_undo_sensitive(doc, false);
1223 temp_string = repr->attribute("inkscape:export-filename");
1224 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1225 repr->setAttribute("inkscape:export-filename", filename_ext);
1226 modified = true;
1227 }
1228 temp_string = repr->attribute("inkscape:export-xdpi");
1229 if (temp_string == NULL || xdpi != atof(temp_string)) {
1230 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1231 modified = true;
1232 }
1233 temp_string = repr->attribute("inkscape:export-ydpi");
1234 if (temp_string == NULL || xdpi != atof(temp_string)) {
1235 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1236 modified = true;
1237 }
1239 if (modified)
1240 repr->setAttribute("sodipodi:modified", "TRUE");
1241 sp_document_set_undo_sensitive(doc, saved);
1242 break;
1243 }
1244 case SELECTION_SELECTION: {
1245 const GSList * reprlst;
1246 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1247 bool modified = false;
1249 bool saved = sp_document_get_undo_sensitive(doc);
1250 sp_document_set_undo_sensitive(doc, false);
1251 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1253 for(; reprlst != NULL; reprlst = reprlst->next) {
1254 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1255 const gchar * temp_string;
1257 if (repr->attribute("id") == NULL ||
1258 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1259 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1260 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1261 temp_string = repr->attribute("inkscape:export-filename");
1262 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1263 repr->setAttribute("inkscape:export-filename", filename_ext);
1264 modified = true;
1265 }
1266 }
1267 temp_string = repr->attribute("inkscape:export-xdpi");
1268 if (temp_string == NULL || xdpi != atof(temp_string)) {
1269 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1270 modified = true;
1271 }
1272 temp_string = repr->attribute("inkscape:export-ydpi");
1273 if (temp_string == NULL || xdpi != atof(temp_string)) {
1274 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1275 modified = true;
1276 }
1277 }
1279 if (modified) {
1280 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1281 repr->setAttribute("sodipodi:modified", "TRUE");
1282 }
1284 sp_document_set_undo_sensitive(doc, saved);
1285 break;
1286 }
1287 default:
1288 break;
1289 }
1291 g_free (filename_ext);
1293 }
1295 } // end of sp_export_export_clicked()
1297 /// Called when Browse button is clicked
1298 static void
1299 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1300 {
1301 GtkWidget *fs, *fe;
1302 const gchar *filename;
1304 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1305 NULL,
1306 GTK_FILE_CHOOSER_ACTION_SAVE,
1307 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1308 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1309 NULL );
1311 #ifdef WITH_GNOME_VFS
1312 if (gnome_vfs_initialized()) {
1313 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1314 }
1315 #endif
1317 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1319 sp_transientize (fs);
1321 gtk_window_set_modal(GTK_WINDOW (fs), true);
1323 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1325 if (*filename == '\0') {
1326 filename = homedir_path(NULL);
1327 }
1329 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1331 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1332 {
1333 gchar *file;
1335 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1337 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1338 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1340 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1342 g_free(utf8file);
1343 g_free(file);
1344 }
1346 gtk_widget_destroy (fs);
1348 return;
1349 } // end of sp_export_browse_clicked()
1351 // TODO: Move this to nr-rect-fns.h.
1352 static bool
1353 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1354 {
1355 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1356 return (
1357 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1358 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1359 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1360 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1361 );
1362 }
1364 /**
1365 \brief This function is used to detect the current selection setting
1366 based on the values in the x0, y0, x1 and y0 fields.
1367 \param base The export dialog itself
1369 One of the most confusing parts of this function is why the array
1370 is built at the beginning. What needs to happen here is that we
1371 should always check the current selection to see if it is the valid
1372 one. While this is a performance improvement it is also a usability
1373 one during the cases where things like selections and drawings match
1374 size. This way buttons change less 'randomly' (atleast in the eyes
1375 of the user). To do this an array is built where the current selection
1376 type is placed first, and then the others in an order from smallest
1377 to largest (this can be configured by reshuffling \c test_order).
1379 All of the values in this function are rounded to two decimal places
1380 because that is what is shown to the user. While everything is kept
1381 more accurate than that, the user can't control more acurrate than
1382 that, so for this to work for them - it needs to check on that level
1383 of accuracy.
1385 \todo finish writing this up
1386 */
1387 static void
1388 sp_export_detect_size(GtkObject * base) {
1389 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1390 selection_type this_test[SELECTION_NUMBER_OF + 1];
1391 selection_type key = SELECTION_NUMBER_OF;
1393 NR::Point x(sp_export_value_get_px (base, "x0"),
1394 sp_export_value_get_px (base, "y0"));
1395 NR::Point y(sp_export_value_get_px (base, "x1"),
1396 sp_export_value_get_px (base, "y1"));
1397 NR::Rect current_bbox(x, y);
1398 //std::cout << "Current " << current_bbox;
1400 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1401 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1402 this_test[i + 1] = test_order[i];
1403 }
1405 for (int i = 0;
1406 i < SELECTION_NUMBER_OF + 1 &&
1407 key == SELECTION_NUMBER_OF &&
1408 SP_ACTIVE_DESKTOP != NULL;
1409 i++) {
1410 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1411 switch (this_test[i]) {
1412 case SELECTION_SELECTION:
1413 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1414 NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1416 //std::cout << "Selection " << bbox;
1417 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1418 key = SELECTION_SELECTION;
1419 }
1420 }
1421 break;
1422 case SELECTION_DRAWING: {
1423 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1425 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1427 // std::cout << "Drawing " << bbox2;
1428 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1429 key = SELECTION_DRAWING;
1430 }
1431 break;
1432 }
1434 case SELECTION_PAGE: {
1435 SPDocument *doc;
1437 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1439 NR::Point x(0.0, 0.0);
1440 NR::Point y(sp_document_width(doc),
1441 sp_document_height(doc));
1442 NR::Rect bbox(x, y);
1444 // std::cout << "Page " << bbox;
1445 if (sp_export_bbox_equal(bbox,current_bbox)) {
1446 key = SELECTION_PAGE;
1447 }
1449 break;
1450 }
1451 default:
1452 break;
1453 }
1454 }
1455 // std::cout << std::endl;
1457 if (key == SELECTION_NUMBER_OF) {
1458 key = SELECTION_CUSTOM;
1459 }
1461 /* We're now using a custom size, not a fixed one */
1462 /* printf("Detecting state: %s\n", selection_names[key]); */
1463 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1464 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1465 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1466 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1468 return;
1469 } /* sp_export_detect_size */
1471 /// Called when area x0 value is changed
1472 static void
1473 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1474 {
1475 float x0, x1, xdpi, width;
1477 if (gtk_object_get_data (base, "update"))
1478 return;
1480 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1481 (base, "units")))
1482 {
1483 return;
1484 }
1486 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1488 x0 = sp_export_value_get_px (base, "x0");
1489 x1 = sp_export_value_get_px (base, "x1");
1490 xdpi = sp_export_value_get (base, "xdpi");
1492 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1494 if (width < SP_EXPORT_MIN_SIZE) {
1495 const gchar *key;
1496 width = SP_EXPORT_MIN_SIZE;
1497 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1499 if (!strcmp (key, "x0")) {
1500 x1 = x0 + width * DPI_BASE / xdpi;
1501 sp_export_value_set_px (base, "x1", x1);
1502 } else {
1503 x0 = x1 - width * DPI_BASE / xdpi;
1504 sp_export_value_set_px (base, "x0", x0);
1505 }
1506 }
1508 sp_export_value_set_px (base, "width", x1 - x0);
1509 sp_export_value_set (base, "bmwidth", width);
1511 sp_export_detect_size(base);
1513 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1515 return;
1516 } // end of sp_export_area_x_value_changed()
1518 /// Called when area y0 value is changed.
1519 static void
1520 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1521 {
1522 float y0, y1, ydpi, height;
1524 if (gtk_object_get_data (base, "update"))
1525 return;
1527 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1528 (base, "units")))
1529 {
1530 return;
1531 }
1533 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1535 y0 = sp_export_value_get_px (base, "y0");
1536 y1 = sp_export_value_get_px (base, "y1");
1537 ydpi = sp_export_value_get (base, "ydpi");
1539 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1541 if (height < SP_EXPORT_MIN_SIZE) {
1542 const gchar *key;
1543 height = SP_EXPORT_MIN_SIZE;
1544 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1545 if (!strcmp (key, "y0")) {
1546 y1 = y0 + height * DPI_BASE / ydpi;
1547 sp_export_value_set_px (base, "y1", y1);
1548 } else {
1549 y0 = y1 - height * DPI_BASE / ydpi;
1550 sp_export_value_set_px (base, "y0", y0);
1551 }
1552 }
1554 sp_export_value_set_px (base, "height", y1 - y0);
1555 sp_export_value_set (base, "bmheight", height);
1557 sp_export_detect_size(base);
1559 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1561 return;
1562 } // end of sp_export_area_y_value_changed()
1564 /// Called when x1-x0 or area width is changed
1565 static void
1566 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1567 {
1568 float x0, x1, xdpi, width, bmwidth;
1570 if (gtk_object_get_data (base, "update"))
1571 return;
1573 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1574 (base, "units"))) {
1575 return;
1576 }
1578 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1580 x0 = sp_export_value_get_px (base, "x0");
1581 x1 = sp_export_value_get_px (base, "x1");
1582 xdpi = sp_export_value_get (base, "xdpi");
1583 width = sp_export_value_get_px (base, "width");
1584 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1586 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1588 bmwidth = SP_EXPORT_MIN_SIZE;
1589 width = bmwidth * DPI_BASE / xdpi;
1590 sp_export_value_set_px (base, "width", width);
1591 }
1593 sp_export_value_set_px (base, "x1", x0 + width);
1594 sp_export_value_set (base, "bmwidth", bmwidth);
1596 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1598 return;
1599 } // end of sp_export_area_width_value_changed()
1601 /// Called when y1-y0 or area height is changed.
1602 static void
1603 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1604 {
1606 float y0, y1, ydpi, height, bmheight;
1608 if (gtk_object_get_data (base, "update"))
1609 return;
1611 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1612 (base, "units"))) {
1613 return;
1614 }
1616 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1618 y0 = sp_export_value_get_px (base, "y0");
1619 y1 = sp_export_value_get_px (base, "y1");
1620 ydpi = sp_export_value_get (base, "ydpi");
1621 height = sp_export_value_get_px (base, "height");
1622 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1624 if (bmheight < SP_EXPORT_MIN_SIZE) {
1625 bmheight = SP_EXPORT_MIN_SIZE;
1626 height = bmheight * DPI_BASE / ydpi;
1627 sp_export_value_set_px (base, "height", height);
1628 }
1630 sp_export_value_set_px (base, "y1", y0 + height);
1631 sp_export_value_set (base, "bmheight", bmheight);
1633 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1635 return;
1636 } // end of sp_export_area_height_value_changed()
1638 /**
1639 \brief A function to set the ydpi
1640 \param base The export dialog
1642 This function grabs all of the y values and then figures out the
1643 new bitmap size based on the changing dpi value. The dpi value is
1644 gotten from the xdpi setting as these can not currently be independent.
1645 */
1646 static void
1647 sp_export_set_image_y (GtkObject *base)
1648 {
1649 float y0, y1, xdpi;
1651 y0 = sp_export_value_get_px (base, "y0");
1652 y1 = sp_export_value_get_px (base, "y1");
1653 xdpi = sp_export_value_get (base, "xdpi");
1655 sp_export_value_set (base, "ydpi", xdpi);
1656 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1658 return;
1659 } // end of sp_export_set_image_y()
1661 /**
1662 \brief A function to set the xdpi
1663 \param base The export dialog
1665 This function grabs all of the x values and then figures out the
1666 new bitmap size based on the changing dpi value. The dpi value is
1667 gotten from the xdpi setting as these can not currently be independent.
1668 */
1669 static void
1670 sp_export_set_image_x (GtkObject *base)
1671 {
1672 float x0, x1, xdpi;
1674 x0 = sp_export_value_get_px (base, "x0");
1675 x1 = sp_export_value_get_px (base, "x1");
1676 xdpi = sp_export_value_get (base, "xdpi");
1678 sp_export_value_set (base, "ydpi", xdpi);
1679 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1681 return;
1682 } // end of sp_export_set_image_x()
1684 /// Called when pixel width is changed
1685 static void
1686 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1687 {
1688 float x0, x1, bmwidth, xdpi;
1690 if (gtk_object_get_data (base, "update"))
1691 return;
1693 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1694 (base, "units"))) {
1695 return;
1696 }
1698 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1700 x0 = sp_export_value_get_px (base, "x0");
1701 x1 = sp_export_value_get_px (base, "x1");
1702 bmwidth = sp_export_value_get (base, "bmwidth");
1704 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1705 bmwidth = SP_EXPORT_MIN_SIZE;
1706 sp_export_value_set (base, "bmwidth", bmwidth);
1707 }
1709 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1710 sp_export_value_set (base, "xdpi", xdpi);
1712 sp_export_set_image_y (base);
1714 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1716 return;
1717 } // end of sp_export_bitmap_width_value_changed()
1719 /// Called when pixel height is changed
1720 static void
1721 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1722 {
1723 float y0, y1, bmheight, xdpi;
1725 if (gtk_object_get_data (base, "update"))
1726 return;
1728 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1729 (base, "units"))) {
1730 return;
1731 }
1733 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1735 y0 = sp_export_value_get_px (base, "y0");
1736 y1 = sp_export_value_get_px (base, "y1");
1737 bmheight = sp_export_value_get (base, "bmheight");
1739 if (bmheight < SP_EXPORT_MIN_SIZE) {
1740 bmheight = SP_EXPORT_MIN_SIZE;
1741 sp_export_value_set (base, "bmheight", bmheight);
1742 }
1744 xdpi = bmheight * DPI_BASE / (y1 - y0);
1745 sp_export_value_set (base, "xdpi", xdpi);
1747 sp_export_set_image_x (base);
1749 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1751 return;
1752 } // end of sp_export_bitmap_width_value_changed()
1754 /**
1755 \brief A function to adjust the bitmap width when the xdpi value changes
1756 \param adj The adjustment that was changed
1757 \param base The export dialog itself
1759 The first thing this function checks is to see if we are doing an
1760 update. If we are, this function just returns because there is another
1761 instance of it that will handle everything for us. If there is a
1762 units change, we also assume that everyone is being updated appropriately
1763 and there is nothing for us to do.
1765 If we're the highest level function, we set the update flag, and
1766 continue on our way.
1768 All of the values are grabbed using the \c sp_export_value_get functions
1769 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1770 xdpi value is saved in the preferences for the next time the dialog
1771 is opened. (does the selection dpi need to be set here?)
1773 A check is done to to ensure that we aren't outputing an invalid width,
1774 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1775 changed to make it valid.
1777 After all of this the bitmap width is changed.
1779 We also change the ydpi. This is a temporary hack as these can not
1780 currently be independent. This is likely to change in the future.
1781 */
1782 void
1783 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1784 {
1785 float x0, x1, xdpi, bmwidth;
1787 if (gtk_object_get_data (base, "update"))
1788 return;
1790 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1791 (base, "units"))) {
1792 return;
1793 }
1795 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1797 x0 = sp_export_value_get_px (base, "x0");
1798 x1 = sp_export_value_get_px (base, "x1");
1799 xdpi = sp_export_value_get (base, "xdpi");
1801 // remember xdpi setting
1802 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1804 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1806 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1807 bmwidth = SP_EXPORT_MIN_SIZE;
1808 if (x1 != x0)
1809 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1810 else
1811 xdpi = DPI_BASE;
1812 sp_export_value_set (base, "xdpi", xdpi);
1813 }
1815 sp_export_value_set (base, "bmwidth", bmwidth);
1817 sp_export_set_image_y (base);
1819 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1821 return;
1822 } // end of sp_export_xdpi_value_changed()
1825 /**
1826 \brief A function to change the area that is used for the exported
1827 bitmap.
1828 \param base This is the export dialog
1829 \param x0 Horizontal upper left hand corner of the picture in points
1830 \param y0 Vertical upper left hand corner of the picture in points
1831 \param x1 Horizontal lower right hand corner of the picture in points
1832 \param y1 Vertical lower right hand corner of the picture in points
1834 This function just calls \c sp_export_value_set_px for each of the
1835 parameters that is passed in. This allows for setting them all in
1836 one convient area.
1838 Update is set to suspend all of the other test running while all the
1839 values are being set up. This allows for a performance increase, but
1840 it also means that the wrong type won't be detected with only some of
1841 the values set. After all the values are set everyone is told that
1842 there has been an update.
1843 */
1844 static void
1845 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1846 {
1847 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1848 sp_export_value_set_px (base, "x1", x1);
1849 sp_export_value_set_px (base, "y1", y1);
1850 sp_export_value_set_px (base, "x0", x0);
1851 sp_export_value_set_px (base, "y0", y0);
1852 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1854 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1855 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1857 return;
1858 }
1860 /**
1861 \brief Sets the value of an adjustment
1862 \param base The export dialog
1863 \param key Which adjustment to set
1864 \param val What value to set it to
1866 This function finds the adjustment using the data stored in the
1867 export dialog. After finding the adjustment it then sets
1868 the value of it.
1869 */
1870 static void
1871 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1872 {
1873 GtkAdjustment *adj;
1875 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1877 gtk_adjustment_set_value (adj, val);
1878 }
1880 /**
1881 \brief A function to set a value using the units points
1882 \param base The export dialog
1883 \param key Which value should be set
1884 \param val What the value should be in points
1886 This function first gets the adjustment for the key that is passed
1887 in. It then figures out what units are currently being used in the
1888 dialog. After doing all of that, it then converts the incoming
1889 value and sets the adjustment.
1890 */
1891 static void
1892 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1893 {
1894 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1896 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1898 return;
1899 }
1901 /**
1902 \brief Get the value of an adjustment in the export dialog
1903 \param base The export dialog
1904 \param key Which adjustment is being looked for
1905 \return The value in the specified adjustment
1907 This function gets the adjustment from the data field in the export
1908 dialog. It then grabs the value from the adjustment.
1909 */
1910 static float
1911 sp_export_value_get ( GtkObject *base, const gchar *key )
1912 {
1913 GtkAdjustment *adj;
1915 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1917 return adj->value;
1918 }
1920 /**
1921 \brief Grabs a value in the export dialog and converts the unit
1922 to points
1923 \param base The export dialog
1924 \param key Which value should be returned
1925 \return The value in the adjustment in points
1927 This function, at its most basic, is a call to \c sp_export_value_get
1928 to get the value of the adjustment. It then finds the units that
1929 are being used by looking at the "units" attribute of the export
1930 dialog. Using that it converts the returned value into points.
1931 */
1932 static float
1933 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1934 {
1935 float value = sp_export_value_get(base, key);
1936 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1938 return sp_units_get_pixels (value, *unit);
1939 } // end of sp_export_value_get_px()
1941 /**
1942 \brief This function is called when the filename is changed by
1943 anyone. It resets the virgin bit.
1944 \param object Text entry box
1945 \param data The export dialog
1946 \return None
1948 This function gets called when the text area is modified. It is
1949 looking for the case where the text area is modified from its
1950 original value. In that case it sets the "filename-modified" bit
1951 to TRUE. If the text dialog returns back to the original text, the
1952 bit gets reset. This should stop simple mistakes.
1953 */
1954 static void
1955 sp_export_filename_modified (GtkObject * object, gpointer data)
1956 {
1957 GtkWidget * text_entry = (GtkWidget *)object;
1958 GtkWidget * export_dialog = (GtkWidget *)data;
1960 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1961 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1962 // printf("Modified: FALSE\n");
1963 } else {
1964 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1965 // printf("Modified: TRUE\n");
1966 }
1968 return;
1969 } // end sp_export_filename_modified
1971 /*
1972 Local Variables:
1973 mode:c++
1974 c-file-style:"stroustrup"
1975 c-file-offsets:((innamespace . 0)(inline-open . 0))
1976 indent-tabs-mode:nil
1977 fill-column:99
1978 End:
1979 */
1980 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :