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 #include <gtk/gtk.h>
20 #include <gtkmm/box.h>
21 #include <gtkmm/buttonbox.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/widget.h>
24 #include <gtkmm/togglebutton.h>
25 #include <gtkmm/entry.h>
26 #include <gtkmm/image.h>
27 #include <gtkmm/stockid.h>
28 #include <gtkmm/stock.h>
29 #ifdef WITH_GNOME_VFS
30 # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized
31 #endif
33 #include <glibmm/i18n.h>
34 #include "helper/unit-menu.h"
35 #include "helper/units.h"
36 #include "unit-constants.h"
37 #include "helper/window.h"
38 #include "inkscape-private.h"
39 #include "document.h"
40 #include "desktop-handles.h"
41 #include "sp-item.h"
42 #include "selection.h"
43 #include "file.h"
44 #include "macros.h"
45 #include "sp-namedview.h"
46 #include "selection-chemistry.h"
48 #include "dialog-events.h"
49 #include "preferences.h"
50 #include "verbs.h"
51 #include "interface.h"
53 #include "extension/output.h"
54 #include "extension/db.h"
56 #include "io/sys.h"
58 #include "helper/png-write.h"
59 #include <png.h>
62 #define SP_EXPORT_MIN_SIZE 1.0
64 #define DPI_BASE PX_PER_IN
66 #define EXPORT_COORD_PRECISION 3
68 #define MIN_ONSCREEN_DISTANCE 50
70 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
71 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
72 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
74 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
75 GtkObject *base);
77 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
78 GtkObject *base);
80 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
81 GtkObject *base);
83 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
84 GtkObject *base);
86 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
87 GtkObject *base);
89 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
90 GtkObject *base);
92 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
93 GtkObject *base);
95 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
96 Inkscape::Selection *selection,
97 GtkObject *base);
98 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
99 Inkscape::Selection *selection,
100 guint flags,
101 GtkObject *base );
103 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
104 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
105 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
106 static float sp_export_value_get ( GtkObject *base, const gchar *key );
107 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
109 static void sp_export_filename_modified (GtkObject * object, gpointer data);
110 static inline void sp_export_find_default_selection(GtkWidget * dlg);
111 static void sp_export_detect_size(GtkObject * base);
113 static Glib::ustring const prefs_path = "/dialogs/export/";
115 // these all need to be reinitialized to their defaults during dialog_destroy
116 static GtkWidget *dlg = NULL;
117 static win_data wd;
118 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
119 static gchar * original_name = NULL;
120 static gchar * doc_export_name = NULL;
121 static bool was_empty = TRUE;
123 /** What type of button is being pressed */
124 enum selection_type {
125 SELECTION_PAGE = 0, /**< Export the whole page */
126 SELECTION_DRAWING, /**< Export everything drawn on the page */
127 SELECTION_SELECTION, /**< Export everything that is selected */
128 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
129 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
130 };
132 /** A list of strings that is used both in the preferences, and in the
133 data fields to describe the various values of \c selection_type. */
134 static const char * selection_names[SELECTION_NUMBER_OF] = {
135 "page", "drawing", "selection", "custom"};
137 /** The names on the buttons for the various selection types. */
138 static const char * selection_labels[SELECTION_NUMBER_OF] = {
139 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
141 static void
142 sp_export_dialog_destroy ( GtkObject */*object*/, gpointer /*data*/ )
143 {
144 sp_signal_disconnect_by_data (INKSCAPE, dlg);
146 wd.win = dlg = NULL;
147 wd.stop = 0;
148 x = -1000; y = -1000; w = 0; h = 0;
149 g_free(original_name);
150 original_name = NULL;
151 g_free(doc_export_name);
152 doc_export_name = NULL;
153 was_empty = TRUE;
155 return;
156 } // end of sp_export_dialog_destroy()
158 /// Called when dialog is closed or inkscape is shut down.
159 static bool
160 sp_export_dialog_delete ( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
161 {
163 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
164 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
166 if (x<0) x=0;
167 if (y<0) y=0;
169 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
170 prefs->setInt(prefs_path + "x", x);
171 prefs->setInt(prefs_path + "y", y);
172 prefs->setInt(prefs_path + "w", w);
173 prefs->setInt(prefs_path + "h", h);
175 return FALSE; // which means, go ahead and destroy it
177 } // end of sp_export_dialog_delete()
179 /**
180 \brief Creates a new spin button for the export dialog
181 \param key The name of the spin button
182 \param val A default value for the spin button
183 \param min Minimum value for the spin button
184 \param max Maximum value for the spin button
185 \param step The step size for the spin button
186 \param page Size of the page increment
187 \param us Unit selector that effects this spin button
188 \param t Table to put the spin button in
189 \param x X location in the table \c t to start with
190 \param y Y location in the table \c t to start with
191 \param ll Text to put on the left side of the spin button (optional)
192 \param lr Text to put on the right side of the spin button (optional)
193 \param digits Number of digits to display after the decimal
194 \param sensitive Whether the spin button is sensitive or not
195 \param cb Callback for when this spin button is changed (optional)
196 \param dlg Export dialog the spin button is being placed in
198 */
199 static void
200 sp_export_spinbutton_new ( gchar const *key, float val, float min, float max,
201 float step, float page, GtkWidget *us,
202 GtkWidget *t, int x, int y,
203 const gchar *ll, const gchar *lr,
204 int digits, unsigned int sensitive,
205 GCallback cb, GtkWidget *dlg )
206 {
207 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
208 gtk_object_set_data (a, "key", const_cast<gchar *>(key));
209 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
211 if (us) {
212 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
213 GTK_ADJUSTMENT (a) );
214 }
216 int pos = 0;
218 GtkWidget *l = NULL;
220 if (ll) {
222 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
223 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
224 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
225 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
226 gtk_widget_set_sensitive (l, sensitive);
227 pos += 1;
229 }
231 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
232 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
233 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
234 gtk_widget_set_size_request (sb, 80, -1);
235 gtk_widget_set_sensitive (sb, sensitive);
236 pos += 1;
238 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
240 if (lr) {
242 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
243 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
244 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
245 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
246 gtk_widget_set_sensitive (l, sensitive);
247 pos += 1;
249 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
250 }
252 if (cb)
253 gtk_signal_connect (a, "value_changed", cb, dlg);
255 return;
256 } // end of sp_export_spinbutton_new()
259 static Gtk::VBox *
260 sp_export_dialog_area_box (GtkWidget * dlg)
261 {
262 Gtk::VBox* vb = new Gtk::VBox(false, 3);
264 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
265 lbl->set_use_markup(true);
266 vb->pack_start(*lbl);
268 /* Units box */
269 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
270 /* gets added to the vbox later, but the unit selector is needed
271 earlier than that */
273 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
274 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
275 if (desktop)
276 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
277 unitbox->pack_end(*us, false, false, 0);
278 Gtk::Label* l = new Gtk::Label(_("Units:"));
279 unitbox->pack_end(*l, false, false, 3);
280 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
282 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
284 Gtk::ToggleButton* b;
285 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
286 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
287 b->set_data("key", GINT_TO_POINTER(i));
288 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
289 togglebox->pack_start(*b, false, true, 0);
290 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
291 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
292 }
294 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
295 G_CALLBACK (sp_export_selection_changed), dlg );
296 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
297 G_CALLBACK (sp_export_selection_modified), dlg );
298 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
299 G_CALLBACK (sp_export_selection_changed), dlg );
301 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
302 t->set_row_spacings (4);
303 t->set_col_spacings (4);
305 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
306 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
307 G_CALLBACK ( sp_export_area_x_value_changed),
308 dlg );
310 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
311 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
312 G_CALLBACK (sp_export_area_x_value_changed),
313 dlg );
315 sp_export_spinbutton_new ( "width", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
316 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Wid_th:"), NULL, EXPORT_COORD_PRECISION, 1,
317 G_CALLBACK
318 (sp_export_area_width_value_changed),
319 dlg );
321 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
322 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
323 G_CALLBACK (sp_export_area_y_value_changed),
324 dlg );
326 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
327 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
328 G_CALLBACK (sp_export_area_y_value_changed),
329 dlg );
331 sp_export_spinbutton_new ( "height", 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0,
332 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Hei_ght:"), NULL, EXPORT_COORD_PRECISION, 1,
333 G_CALLBACK (sp_export_area_height_value_changed),
334 dlg );
336 vb->pack_start(*togglebox, false, false, 3);
337 vb->pack_start(*t, false, false, 0);
338 vb->pack_start(*unitbox, false, false, 0);
340 return vb;
341 } // end of sp_export_dialog_area_box
344 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
346 if (id == NULL) /* This should never happen */
347 id = "bitmap";
349 gchar * directory = NULL;
351 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
352 // std::cout << "Directory from dialog" << std::endl;
353 directory = g_dirname(file_entry_text);
354 }
356 if (directory == NULL) {
357 /* Grab document directory */
358 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
359 // std::cout << "Directory from document" << std::endl;
360 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
361 }
362 }
364 if (directory == NULL) {
365 // std::cout << "Home Directory" << std::endl;
366 directory = homedir_path(NULL);
367 }
369 gchar * id_ext = g_strconcat(id, ".png", NULL);
370 gchar *filename = g_build_filename(directory, id_ext, NULL);
371 g_free(directory);
372 g_free(id_ext);
373 return filename;
374 }
376 static void
377 batch_export_clicked (GtkWidget *widget, GtkObject *base)
378 {
379 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
380 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
381 vb_singleexport->set_sensitive(false);
382 } else {
383 vb_singleexport->set_sensitive(true);
384 }
385 }
387 void
388 sp_export_dialog (void)
389 {
390 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
391 if (!dlg) {
393 gchar title[500];
394 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
396 dlg = sp_window_new (title, TRUE);
398 if (x == -1000 || y == -1000) {
399 x = prefs->getInt(prefs_path + "x", 0);
400 y = prefs->getInt(prefs_path + "y", 0);
401 }
403 if (w ==0 || h == 0) {
404 w = prefs->getInt(prefs_path + "w", 0);
405 h = prefs->getInt(prefs_path + "h", 0);
406 }
408 // if (x<0) x=0;
409 // if (y<0) y=0;
411 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
412 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
413 gtk_window_move ((GtkWindow *) dlg, x, y);
414 else
415 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
416 sp_transientize (dlg);
417 wd.win = dlg;
418 wd.stop = 0;
420 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
421 G_CALLBACK (sp_transientize_callback), &wd);
423 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
424 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
426 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
427 G_CALLBACK (sp_export_dialog_destroy), dlg);
429 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
430 G_CALLBACK (sp_export_dialog_delete), dlg);
432 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
433 G_CALLBACK (sp_export_dialog_delete), dlg);
435 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
436 G_CALLBACK (sp_dialog_hide), dlg);
438 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
439 G_CALLBACK (sp_dialog_unhide), dlg);
441 GtkTooltips *tt = gtk_tooltips_new();
443 Gtk::VBox *vb = new Gtk::VBox(false, 3);
444 vb->set_border_width(3);
445 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
447 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
448 vb_singleexport->set_border_width(0);
449 vb->pack_start(*vb_singleexport);
450 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
452 /* Export area frame */
453 {
454 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
455 area_box->set_border_width(3);
456 vb_singleexport->pack_start(*area_box, false, false, 0);
457 }
459 /* Bitmap size frame */
460 {
461 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
462 size_box->set_border_width(3);
464 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
465 lbl->set_use_markup(true);
466 size_box->pack_start(*lbl, false, false, 0);
467 const int rows = 2;
468 const int cols = 5;
469 const bool homogeneous = false;
470 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
471 t->set_row_spacings (4);
472 t->set_col_spacings (4);
473 size_box->pack_start(*t);
475 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
476 NULL, GTK_WIDGET(t->gobj()), 0, 0,
477 _("_Width:"), _("pixels at"), 0, 1,
478 G_CALLBACK
479 (sp_export_bitmap_width_value_changed),
480 dlg );
482 sp_export_spinbutton_new ( "xdpi",
483 prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
484 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
485 NULL, _("dp_i"), 2, 1,
486 G_CALLBACK (sp_export_xdpi_value_changed),
487 dlg );
489 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
490 NULL, GTK_WIDGET(t->gobj()), 0, 1,
491 _("_Height:"), _("pixels at"), 0, 1,
492 G_CALLBACK
493 (sp_export_bitmap_height_value_changed),
494 dlg );
496 /** \todo
497 * Needs fixing: there's no way to set ydpi currently, so we use
498 * the defaultxdpi value here, too...
499 */
500 sp_export_spinbutton_new ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
501 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
502 NULL, _("dpi"), 2, 0, NULL, dlg );
504 vb_singleexport->pack_start(*size_box);
505 }
507 /* File entry */
508 {
509 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
510 file_box->set_border_width(3);
512 // true = has mnemonic
513 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
514 flabel->set_use_markup(true);
515 file_box->pack_start(*flabel, false, false, 0);
517 Gtk::Entry *fe = new Gtk::Entry();
519 /*
520 * set the default filename to be that of the current path + document
521 * with .png extension
522 *
523 * One thing to notice here is that this filename may get
524 * overwritten, but it won't happen here. The filename gets
525 * written into the text field, but then the button to select
526 * the area gets set. In that code the filename can be changed
527 * if there are some with presidence in the document. So, while
528 * this code sets the name first, it may not be the one users
529 * really see.
530 */
531 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
532 {
533 gchar *name;
534 SPDocument * doc = SP_ACTIVE_DOCUMENT;
535 const gchar *uri = SP_DOCUMENT_URI (doc);
536 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
537 const gchar * text_extension = repr->attribute("inkscape:output_extension");
538 Inkscape::Extension::Output * oextension = NULL;
540 if (text_extension != NULL) {
541 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
542 }
544 if (oextension != NULL) {
545 gchar * old_extension = oextension->get_extension();
546 if (g_str_has_suffix(uri, old_extension)) {
547 gchar * uri_copy;
548 gchar * extension_point;
549 gchar * final_name;
551 uri_copy = g_strdup(uri);
552 extension_point = g_strrstr(uri_copy, old_extension);
553 extension_point[0] = '\0';
555 final_name = g_strconcat(uri_copy, ".png", NULL);
556 fe->set_text(final_name);
558 g_free(final_name);
559 g_free(uri_copy);
560 }
561 } else {
562 name = g_strconcat(uri, ".png", NULL);
563 fe->set_text(name);
564 g_free(name);
565 }
567 doc_export_name = g_strdup(fe->get_text().c_str());
568 }
569 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
570 G_CALLBACK (sp_export_filename_modified), dlg);
572 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
574 {
575 // true = has mnemonic
576 Gtk::Button *b = new Gtk::Button();
578 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
579 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
580 Gtk::ICON_SIZE_BUTTON);
581 pixlabel->pack_start(*im);
583 Gtk::Label *l = new Gtk::Label();
584 l->set_markup_with_mnemonic(_("_Browse..."));
585 pixlabel->pack_start(*l);
587 b->add(*pixlabel);
589 hb->pack_end (*b, false, false, 4);
590 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
591 G_CALLBACK (sp_export_browse_clicked), NULL );
592 }
594 hb->pack_start (*fe, true, true, 0);
595 file_box->add(*hb);
596 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
597 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
598 original_name = g_strdup(fe->get_text().c_str());
599 // pressing enter in the filename field is the same as clicking export:
600 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
601 G_CALLBACK (sp_export_export_clicked), dlg );
602 // focus is in the filename initially:
603 fe->grab_focus();
605 // mnemonic in frame label moves focus to filename:
606 flabel->set_mnemonic_widget(*fe);
608 vb_singleexport->pack_start(*file_box);
609 }
611 {
612 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
613 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
614 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
615 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
616 batch_box->pack_start(*Glib::wrap(be), false, false);
617 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);
618 batch_box->show_all();
619 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
620 vb->pack_start(*batch_box);
621 }
623 {
624 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
625 GtkWidget *he = gtk_check_button_new_with_label(_("Hide _all except selected"));
626 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
627 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
628 hide_box->pack_start(*Glib::wrap(he), false, false);
629 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
630 hide_box->show_all();
631 vb->pack_start(*hide_box);
632 }
634 /* Buttons */
635 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
636 bb->set_border_width(3);
638 {
639 Gtk::Button *b = new Gtk::Button();
640 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
641 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
642 Gtk::ICON_SIZE_BUTTON);
643 image_label->pack_start(*im);
645 Gtk::Label *l = new Gtk::Label();
646 l->set_markup_with_mnemonic(_("_Export"));
647 image_label->pack_start(*l);
649 b->add(*image_label);
650 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
651 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
652 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
653 bb->pack_end(*b, false, false, 0);
654 }
656 vb->pack_end(*bb, false, false, 0);
657 vb->show_all();
659 } // end of if (!dlg)
661 sp_export_find_default_selection(dlg);
663 gtk_window_present ((GtkWindow *) dlg);
665 return;
666 } // end of sp_export_dialog()
668 static void
669 sp_export_update_checkbuttons (GtkObject *base)
670 {
671 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
672 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
673 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
674 if (num >= 2) {
675 gtk_widget_set_sensitive (be, true);
676 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (ngettext("Batch export %d selected object","Batch export %d selected objects",num), num));
677 } else {
678 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
679 gtk_widget_set_sensitive (be, FALSE);
680 }
681 if (num > 0) {
682 gtk_widget_set_sensitive (he, true);
683 } else {
684 gtk_widget_set_sensitive (he, false);
685 }
686 }
688 static inline void
689 sp_export_find_default_selection(GtkWidget * dlg)
690 {
691 selection_type key = SELECTION_NUMBER_OF;
693 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
694 key = SELECTION_SELECTION;
695 }
697 /* Try using the preferences */
698 if (key == SELECTION_NUMBER_OF) {
699 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
700 int i = SELECTION_NUMBER_OF;
702 Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
704 if (!what.empty()) {
705 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
706 if (what == selection_names[i]) {
707 break;
708 }
709 }
710 }
712 key = (selection_type)i;
713 }
715 if (key == SELECTION_NUMBER_OF) {
716 key = SELECTION_SELECTION;
717 }
719 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
720 selection_names[key]);
721 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
723 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
724 }
727 /**
728 * \brief If selection changed or a different document activated, we must
729 * recalculate any chosen areas
730 *
731 */
732 static void
733 sp_export_selection_changed ( Inkscape::Application *inkscape,
734 Inkscape::Selection *selection,
735 GtkObject *base )
736 {
737 selection_type current_key;
738 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
740 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
741 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
742 was_empty) {
743 gtk_toggle_button_set_active
744 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
745 TRUE );
746 }
747 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
749 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
751 if (inkscape &&
752 SP_IS_INKSCAPE (inkscape) &&
753 selection &&
754 SELECTION_CUSTOM != current_key) {
755 GtkToggleButton * button;
756 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
757 sp_export_area_toggled(button, base);
758 }
760 sp_export_update_checkbuttons (base);
761 }
763 static void
764 sp_export_selection_modified ( Inkscape::Application */*inkscape*/,
765 Inkscape::Selection */*selection*/,
766 guint /*flags*/,
767 GtkObject *base )
768 {
769 selection_type current_key;
770 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
772 switch (current_key) {
773 case SELECTION_DRAWING:
774 if ( SP_ACTIVE_DESKTOP ) {
775 SPDocument *doc;
776 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
777 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
778 if (bbox) {
779 sp_export_set_area (base, bbox->min()[Geom::X],
780 bbox->min()[Geom::Y],
781 bbox->max()[Geom::X],
782 bbox->max()[Geom::Y]);
783 }
784 }
785 break;
786 case SELECTION_SELECTION:
787 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
788 NRRect bbox;
789 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
790 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
791 }
792 break;
793 default:
794 /* Do nothing for page or for custom */
795 break;
796 }
798 return;
799 }
801 /// Called when one of the selection buttons was toggled.
802 static void
803 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
804 {
805 if (gtk_object_get_data (base, "update"))
806 return;
808 selection_type key, old_key;
809 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
810 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
812 /* Ignore all "turned off" events unless we're the only active button */
813 if (!gtk_toggle_button_get_active (tb) ) {
815 /* Don't let the current selection be deactived - but rerun the
816 activate to allow the user to renew the values */
817 if (key == old_key) {
818 gtk_toggle_button_set_active ( tb, TRUE );
819 }
821 return;
822 }
824 /* Turn off the currently active button unless it's us */
825 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
827 if (old_key != key) {
828 gtk_toggle_button_set_active
829 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
830 FALSE );
831 }
833 if ( SP_ACTIVE_DESKTOP )
834 {
835 SPDocument *doc;
836 Geom::OptRect bbox;
837 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
839 /* Notice how the switch is used to 'fall through' here to get
840 various backups. If you modify this without noticing you'll
841 probabaly screw something up. */
842 switch (key) {
843 case SELECTION_SELECTION:
844 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
845 {
846 bbox = sp_desktop_selection (SP_ACTIVE_DESKTOP)->bounds();
847 /* Only if there is a selection that we can set
848 do we break, otherwise we fall through to the
849 drawing */
850 // std::cout << "Using selection: SELECTION" << std::endl;
851 key = SELECTION_SELECTION;
852 break;
853 }
854 case SELECTION_DRAWING:
855 /** \todo
856 * This returns wrong values if the document has a viewBox.
857 */
858 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
859 /* If the drawing is valid, then we'll use it and break
860 otherwise we drop through to the page settings */
861 if (bbox) {
862 // std::cout << "Using selection: DRAWING" << std::endl;
863 key = SELECTION_DRAWING;
864 break;
865 }
866 case SELECTION_PAGE:
867 bbox = Geom::Rect(Geom::Point(0.0, 0.0),
868 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
870 // std::cout << "Using selection: PAGE" << std::endl;
871 key = SELECTION_PAGE;
872 break;
873 case SELECTION_CUSTOM:
874 default:
875 break;
876 } // switch
878 // remember area setting
879 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
880 prefs->setString("/dialogs/export/exportarea/value", selection_names[key]);
882 if ( key != SELECTION_CUSTOM && bbox ) {
883 sp_export_set_area (base, bbox->min()[Geom::X],
884 bbox->min()[Geom::Y],
885 bbox->max()[Geom::X],
886 bbox->max()[Geom::Y]);
887 }
889 } // end of if ( SP_ACTIVE_DESKTOP )
892 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
893 GtkWidget * file_entry;
894 const gchar * filename = NULL;
895 float xdpi = 0.0, ydpi = 0.0;
897 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
899 switch (key) {
900 case SELECTION_PAGE:
901 case SELECTION_DRAWING: {
902 SPDocument * doc = SP_ACTIVE_DOCUMENT;
903 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
905 if (filename == NULL) {
906 if (doc_export_name != NULL) {
907 filename = g_strdup(doc_export_name);
908 } else {
909 filename = g_strdup("");
910 }
911 }
912 break;
913 }
914 case SELECTION_SELECTION:
915 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
917 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
919 /* If we still don't have a filename -- let's build
920 one that's nice */
921 if (filename == NULL) {
922 const gchar * id = NULL;
923 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
924 for(; reprlst != NULL; reprlst = reprlst->next) {
925 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
926 if (repr->attribute("id")) {
927 id = repr->attribute("id");
928 break;
929 }
930 }
932 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
933 }
934 }
935 break;
936 case SELECTION_CUSTOM:
937 default:
938 break;
939 }
941 if (filename != NULL) {
942 g_free(original_name);
943 original_name = g_strdup(filename);
944 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
945 }
947 if (xdpi != 0.0) {
948 sp_export_value_set(base, "xdpi", xdpi);
949 }
951 /* These can't be separate, and setting x sets y, so for
952 now setting this is disabled. Hopefully it won't be in
953 the future */
954 if (FALSE && ydpi != 0.0) {
955 sp_export_value_set(base, "ydpi", ydpi);
956 }
957 }
959 return;
960 } // end of sp_export_area_toggled()
962 /// Called when dialog is deleted
963 static gint
964 sp_export_progress_delete ( GtkWidget */*widget*/, GdkEvent */*event*/, GObject *base )
965 {
966 g_object_set_data (base, "cancel", (gpointer) 1);
967 return TRUE;
968 } // end of sp_export_progress_delete()
970 /// Called when progress is cancelled
971 static void
972 sp_export_progress_cancel ( GtkWidget */*widget*/, GObject *base )
973 {
974 g_object_set_data (base, "cancel", (gpointer) 1);
975 } // end of sp_export_progress_cancel()
977 /// Called for every progress iteration
978 static unsigned int
979 sp_export_progress_callback (float value, void *data)
980 {
981 GtkWidget *prg;
982 int evtcount;
984 if (g_object_get_data ((GObject *) data, "cancel"))
985 return FALSE;
987 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
988 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
990 evtcount = 0;
991 while ((evtcount < 16) && gdk_events_pending ()) {
992 gtk_main_iteration_do (FALSE);
993 evtcount += 1;
994 }
996 gtk_main_iteration_do (FALSE);
998 return TRUE;
1000 } // end of sp_export_progress_callback()
1002 GtkWidget *
1003 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1004 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1006 dlg = gtk_dialog_new ();
1007 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1008 prg = gtk_progress_bar_new ();
1009 sp_transientize (dlg);
1010 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1011 g_object_set_data ((GObject *) base, "progress", prg);
1013 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1015 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1016 GTK_PROGRESS_LEFT_TO_RIGHT);
1017 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1018 prg, FALSE, FALSE, 4 );
1019 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1020 GTK_STOCK_CANCEL,
1021 GTK_RESPONSE_CANCEL );
1023 g_signal_connect ( (GObject *) dlg, "delete_event",
1024 (GCallback) sp_export_progress_delete, base);
1025 g_signal_connect ( (GObject *) btn, "clicked",
1026 (GCallback) sp_export_progress_cancel, base);
1027 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1028 gtk_widget_show_all (dlg);
1030 return dlg;
1031 }
1033 // FIXME: Some lib function should be available to do this ...
1034 static gchar *
1035 filename_add_extension (const gchar *filename, const gchar *extension)
1036 {
1037 gchar *dot;
1039 dot = strrchr (filename, '.');
1040 if ( !dot )
1041 return g_strconcat (filename, ".", extension, NULL);
1042 {
1043 if (dot[1] == '\0')
1044 return g_strconcat (filename, extension, NULL);
1045 else
1046 {
1047 if (g_strcasecmp (dot + 1, extension) == 0)
1048 return g_strdup (filename);
1049 else
1050 {
1051 return g_strconcat (filename, ".", extension, NULL);
1052 }
1053 }
1054 }
1055 }
1057 /// Called when export button is clicked
1058 static void
1059 sp_export_export_clicked (GtkButton */*button*/, GtkObject *base)
1060 {
1061 if (!SP_ACTIVE_DESKTOP) return;
1063 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1065 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1066 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1067 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1068 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1069 // Batch export of selected objects
1071 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1072 gint n = 0;
1074 if (num < 1)
1075 return;
1077 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1078 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1079 g_free (progress_text);
1081 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1082 i != NULL;
1083 i = i->next) {
1084 SPItem *item = (SPItem *) i->data;
1085 // retrieve export filename hint
1086 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1087 if (!fn) {
1088 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1089 }
1091 // retrieve export dpi hints
1092 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1093 gdouble dpi = 0.0;
1094 if (dpi_hint) {
1095 dpi = atof(dpi_hint);
1096 }
1097 if (dpi == 0.0) {
1098 dpi = DPI_BASE;
1099 }
1101 Geom::OptRect area;
1102 sp_item_invoke_bbox(item, area, sp_item_i2d_affine((SPItem *) item), TRUE);
1103 if (area) {
1104 gint width = (gint) (area->width() * dpi / PX_PER_IN + 0.5);
1105 gint height = (gint) (area->height() * dpi / PX_PER_IN + 0.5);
1107 if (width > 1 && height > 1) {
1108 /* Do export */
1109 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1110 *area, width, height, dpi, dpi,
1111 nv->pagecolor,
1112 NULL, NULL, TRUE, // overwrite without asking
1113 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1114 )) {
1115 gchar * error;
1116 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1117 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1118 sp_ui_error_dialog(error);
1119 g_free(safeFile);
1120 g_free(error);
1121 }
1122 }
1123 }
1124 n++;
1125 sp_export_progress_callback((float)n/num, base);
1126 }
1128 gtk_widget_destroy (prog_dlg);
1129 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1131 } else {
1133 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1134 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1136 float const x0 = sp_export_value_get_px(base, "x0");
1137 float const y0 = sp_export_value_get_px(base, "y0");
1138 float const x1 = sp_export_value_get_px(base, "x1");
1139 float const y1 = sp_export_value_get_px(base, "y1");
1140 float const xdpi = sp_export_value_get(base, "xdpi");
1141 float const ydpi = sp_export_value_get(base, "ydpi");
1142 unsigned long int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1143 unsigned long int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1145 if (filename == NULL || *filename == '\0') {
1146 sp_ui_error_dialog(_("You have to enter a filename"));
1147 return;
1148 }
1150 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1151 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1152 return;
1153 }
1155 gchar *dirname = g_path_get_dirname(filename);
1156 if ( dirname == NULL
1157 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1158 {
1159 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1160 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1161 safeDir);
1162 sp_ui_error_dialog(error);
1163 g_free(safeDir);
1164 g_free(error);
1165 g_free(dirname);
1166 return;
1167 }
1168 g_free(dirname);
1170 // make sure that .png is the extension of the file:
1171 gchar * filename_ext = filename_add_extension(filename, "png");
1172 gtk_entry_set_text(GTK_ENTRY(fe), filename_ext);
1174 gchar *fn = g_path_get_basename (filename_ext);
1176 gchar *progress_text = g_strdup_printf (_("Exporting %s (%lu x %lu)"), fn, width, height);
1177 g_free (fn);
1178 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1179 g_free (progress_text);
1181 /* Do export */
1182 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename_ext,
1183 Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
1184 nv->pagecolor,
1185 sp_export_progress_callback, base, FALSE,
1186 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1187 )) {
1188 gchar * error;
1189 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1190 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1191 sp_ui_error_dialog(error);
1192 g_free(safeFile);
1193 g_free(error);
1194 }
1196 /* Reset the filename so that it can be changed again by changing
1197 selections and all that */
1198 g_free(original_name);
1199 original_name = g_strdup(filename_ext);
1200 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1202 gtk_widget_destroy (prog_dlg);
1203 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1205 /* Setup the values in the document */
1206 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1207 case SELECTION_PAGE:
1208 case SELECTION_DRAWING: {
1209 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1210 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1211 bool modified = false;
1212 const gchar * temp_string;
1214 bool saved = sp_document_get_undo_sensitive(doc);
1215 sp_document_set_undo_sensitive(doc, false);
1217 temp_string = repr->attribute("inkscape:export-filename");
1218 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1219 repr->setAttribute("inkscape:export-filename", filename_ext);
1220 modified = true;
1221 }
1222 temp_string = repr->attribute("inkscape:export-xdpi");
1223 if (temp_string == NULL || xdpi != atof(temp_string)) {
1224 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1225 modified = true;
1226 }
1227 temp_string = repr->attribute("inkscape:export-ydpi");
1228 if (temp_string == NULL || xdpi != atof(temp_string)) {
1229 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1230 modified = true;
1231 }
1232 sp_document_set_undo_sensitive(doc, saved);
1234 if (modified) {
1235 doc->setModifiedSinceSave();
1236 }
1237 break;
1238 }
1239 case SELECTION_SELECTION: {
1240 const GSList * reprlst;
1241 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1242 bool modified = false;
1244 bool saved = sp_document_get_undo_sensitive(doc);
1245 sp_document_set_undo_sensitive(doc, false);
1246 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1248 for(; reprlst != NULL; reprlst = reprlst->next) {
1249 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1250 const gchar * temp_string;
1252 if (repr->attribute("id") == NULL ||
1253 !(g_strrstr(filename_ext, repr->attribute("id")) != NULL &&
1254 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1255 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1256 temp_string = repr->attribute("inkscape:export-filename");
1257 if (temp_string == NULL || strcmp(temp_string, filename_ext)) {
1258 repr->setAttribute("inkscape:export-filename", filename_ext);
1259 modified = true;
1260 }
1261 }
1262 temp_string = repr->attribute("inkscape:export-xdpi");
1263 if (temp_string == NULL || xdpi != atof(temp_string)) {
1264 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1265 modified = true;
1266 }
1267 temp_string = repr->attribute("inkscape:export-ydpi");
1268 if (temp_string == NULL || xdpi != atof(temp_string)) {
1269 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1270 modified = true;
1271 }
1272 }
1273 sp_document_set_undo_sensitive(doc, saved);
1275 if (modified) {
1276 doc->setModifiedSinceSave();
1277 }
1278 break;
1279 }
1280 default:
1281 break;
1282 }
1284 g_free (filename_ext);
1286 }
1288 } // end of sp_export_export_clicked()
1290 /// Called when Browse button is clicked
1291 static void
1292 sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/)
1293 {
1294 GtkWidget *fs, *fe;
1295 const gchar *filename;
1297 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1298 (GtkWindow*)dlg,
1299 GTK_FILE_CHOOSER_ACTION_SAVE,
1300 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1301 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1302 NULL );
1304 #ifdef WITH_GNOME_VFS
1305 if (gnome_vfs_initialized()) {
1306 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), false);
1307 }
1308 #endif
1310 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1312 sp_transientize (fs);
1314 gtk_window_set_modal(GTK_WINDOW (fs), true);
1316 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1318 if (*filename == '\0') {
1319 filename = homedir_path(NULL);
1320 }
1322 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1324 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1325 {
1326 gchar *file;
1328 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1330 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1331 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1333 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1335 g_free(utf8file);
1336 g_free(file);
1337 }
1339 gtk_widget_destroy (fs);
1341 return;
1342 } // end of sp_export_browse_clicked()
1344 // TODO: Move this to nr-rect-fns.h.
1345 static bool
1346 sp_export_bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
1347 {
1348 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1349 return (
1350 (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
1351 (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
1352 (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
1353 (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
1354 );
1355 }
1357 /**
1358 \brief This function is used to detect the current selection setting
1359 based on the values in the x0, y0, x1 and y0 fields.
1360 \param base The export dialog itself
1362 One of the most confusing parts of this function is why the array
1363 is built at the beginning. What needs to happen here is that we
1364 should always check the current selection to see if it is the valid
1365 one. While this is a performance improvement it is also a usability
1366 one during the cases where things like selections and drawings match
1367 size. This way buttons change less 'randomly' (atleast in the eyes
1368 of the user). To do this an array is built where the current selection
1369 type is placed first, and then the others in an order from smallest
1370 to largest (this can be configured by reshuffling \c test_order).
1372 All of the values in this function are rounded to two decimal places
1373 because that is what is shown to the user. While everything is kept
1374 more accurate than that, the user can't control more acurrate than
1375 that, so for this to work for them - it needs to check on that level
1376 of accuracy.
1378 \todo finish writing this up
1379 */
1380 static void
1381 sp_export_detect_size(GtkObject * base) {
1382 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1383 selection_type this_test[SELECTION_NUMBER_OF + 1];
1384 selection_type key = SELECTION_NUMBER_OF;
1386 Geom::Point x(sp_export_value_get_px (base, "x0"),
1387 sp_export_value_get_px (base, "y0"));
1388 Geom::Point y(sp_export_value_get_px (base, "x1"),
1389 sp_export_value_get_px (base, "y1"));
1390 Geom::Rect current_bbox(x, y);
1391 //std::cout << "Current " << current_bbox;
1393 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1394 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1395 this_test[i + 1] = test_order[i];
1396 }
1398 for (int i = 0;
1399 i < SELECTION_NUMBER_OF + 1 &&
1400 key == SELECTION_NUMBER_OF &&
1401 SP_ACTIVE_DESKTOP != NULL;
1402 i++) {
1403 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1404 switch (this_test[i]) {
1405 case SELECTION_SELECTION:
1406 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1407 Geom::OptRect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1409 //std::cout << "Selection " << bbox;
1410 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1411 key = SELECTION_SELECTION;
1412 }
1413 }
1414 break;
1415 case SELECTION_DRAWING: {
1416 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1418 Geom::OptRect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1420 // std::cout << "Drawing " << bbox2;
1421 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1422 key = SELECTION_DRAWING;
1423 }
1424 break;
1425 }
1427 case SELECTION_PAGE: {
1428 SPDocument *doc;
1430 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1432 Geom::Point x(0.0, 0.0);
1433 Geom::Point y(sp_document_width(doc),
1434 sp_document_height(doc));
1435 Geom::Rect bbox(x, y);
1437 // std::cout << "Page " << bbox;
1438 if (sp_export_bbox_equal(bbox,current_bbox)) {
1439 key = SELECTION_PAGE;
1440 }
1442 break;
1443 }
1444 default:
1445 break;
1446 }
1447 }
1448 // std::cout << std::endl;
1450 if (key == SELECTION_NUMBER_OF) {
1451 key = SELECTION_CUSTOM;
1452 }
1454 /* We're now using a custom size, not a fixed one */
1455 /* printf("Detecting state: %s\n", selection_names[key]); */
1456 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1457 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1458 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1459 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1461 return;
1462 } /* sp_export_detect_size */
1464 /// Called when area x0 value is changed
1465 static void
1466 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1467 {
1468 float x0, x1, xdpi, width;
1470 if (gtk_object_get_data (base, "update"))
1471 return;
1473 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1474 (base, "units")))
1475 {
1476 return;
1477 }
1479 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1481 x0 = sp_export_value_get_px (base, "x0");
1482 x1 = sp_export_value_get_px (base, "x1");
1483 xdpi = sp_export_value_get (base, "xdpi");
1485 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1487 if (width < SP_EXPORT_MIN_SIZE) {
1488 const gchar *key;
1489 width = SP_EXPORT_MIN_SIZE;
1490 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1492 if (!strcmp (key, "x0")) {
1493 x1 = x0 + width * DPI_BASE / xdpi;
1494 sp_export_value_set_px (base, "x1", x1);
1495 } else {
1496 x0 = x1 - width * DPI_BASE / xdpi;
1497 sp_export_value_set_px (base, "x0", x0);
1498 }
1499 }
1501 sp_export_value_set_px (base, "width", x1 - x0);
1502 sp_export_value_set (base, "bmwidth", width);
1504 sp_export_detect_size(base);
1506 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1508 return;
1509 } // end of sp_export_area_x_value_changed()
1511 /// Called when area y0 value is changed.
1512 static void
1513 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1514 {
1515 float y0, y1, ydpi, height;
1517 if (gtk_object_get_data (base, "update"))
1518 return;
1520 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1521 (base, "units")))
1522 {
1523 return;
1524 }
1526 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1528 y0 = sp_export_value_get_px (base, "y0");
1529 y1 = sp_export_value_get_px (base, "y1");
1530 ydpi = sp_export_value_get (base, "ydpi");
1532 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1534 if (height < SP_EXPORT_MIN_SIZE) {
1535 const gchar *key;
1536 height = SP_EXPORT_MIN_SIZE;
1537 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1538 if (!strcmp (key, "y0")) {
1539 y1 = y0 + height * DPI_BASE / ydpi;
1540 sp_export_value_set_px (base, "y1", y1);
1541 } else {
1542 y0 = y1 - height * DPI_BASE / ydpi;
1543 sp_export_value_set_px (base, "y0", y0);
1544 }
1545 }
1547 sp_export_value_set_px (base, "height", y1 - y0);
1548 sp_export_value_set (base, "bmheight", height);
1550 sp_export_detect_size(base);
1552 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1554 return;
1555 } // end of sp_export_area_y_value_changed()
1557 /// Called when x1-x0 or area width is changed
1558 static void
1559 sp_export_area_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1560 {
1561 float x0, x1, xdpi, width, bmwidth;
1563 if (gtk_object_get_data (base, "update"))
1564 return;
1566 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1567 (base, "units"))) {
1568 return;
1569 }
1571 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1573 x0 = sp_export_value_get_px (base, "x0");
1574 x1 = sp_export_value_get_px (base, "x1");
1575 xdpi = sp_export_value_get (base, "xdpi");
1576 width = sp_export_value_get_px (base, "width");
1577 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1579 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1581 bmwidth = SP_EXPORT_MIN_SIZE;
1582 width = bmwidth * DPI_BASE / xdpi;
1583 sp_export_value_set_px (base, "width", width);
1584 }
1586 sp_export_value_set_px (base, "x1", x0 + width);
1587 sp_export_value_set (base, "bmwidth", bmwidth);
1589 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1591 return;
1592 } // end of sp_export_area_width_value_changed()
1594 /// Called when y1-y0 or area height is changed.
1595 static void
1596 sp_export_area_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1597 {
1599 float y0, y1, ydpi, height, bmheight;
1601 if (gtk_object_get_data (base, "update"))
1602 return;
1604 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1605 (base, "units"))) {
1606 return;
1607 }
1609 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1611 y0 = sp_export_value_get_px (base, "y0");
1612 y1 = sp_export_value_get_px (base, "y1");
1613 ydpi = sp_export_value_get (base, "ydpi");
1614 height = sp_export_value_get_px (base, "height");
1615 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1617 if (bmheight < SP_EXPORT_MIN_SIZE) {
1618 bmheight = SP_EXPORT_MIN_SIZE;
1619 height = bmheight * DPI_BASE / ydpi;
1620 sp_export_value_set_px (base, "height", height);
1621 }
1623 sp_export_value_set_px (base, "y1", y0 + height);
1624 sp_export_value_set (base, "bmheight", bmheight);
1626 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1628 return;
1629 } // end of sp_export_area_height_value_changed()
1631 /**
1632 \brief A function to set the ydpi
1633 \param base The export dialog
1635 This function grabs all of the y values and then figures out the
1636 new bitmap size based on the changing dpi value. The dpi value is
1637 gotten from the xdpi setting as these can not currently be independent.
1638 */
1639 static void
1640 sp_export_set_image_y (GtkObject *base)
1641 {
1642 float y0, y1, xdpi;
1644 y0 = sp_export_value_get_px (base, "y0");
1645 y1 = sp_export_value_get_px (base, "y1");
1646 xdpi = sp_export_value_get (base, "xdpi");
1648 sp_export_value_set (base, "ydpi", xdpi);
1649 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1651 return;
1652 } // end of sp_export_set_image_y()
1654 /**
1655 \brief A function to set the xdpi
1656 \param base The export dialog
1658 This function grabs all of the x values and then figures out the
1659 new bitmap size based on the changing dpi value. The dpi value is
1660 gotten from the xdpi setting as these can not currently be independent.
1661 */
1662 static void
1663 sp_export_set_image_x (GtkObject *base)
1664 {
1665 float x0, x1, xdpi;
1667 x0 = sp_export_value_get_px (base, "x0");
1668 x1 = sp_export_value_get_px (base, "x1");
1669 xdpi = sp_export_value_get (base, "xdpi");
1671 sp_export_value_set (base, "ydpi", xdpi);
1672 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1674 return;
1675 } // end of sp_export_set_image_x()
1677 /// Called when pixel width is changed
1678 static void
1679 sp_export_bitmap_width_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1680 {
1681 float x0, x1, bmwidth, xdpi;
1683 if (gtk_object_get_data (base, "update"))
1684 return;
1686 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1687 (base, "units"))) {
1688 return;
1689 }
1691 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1693 x0 = sp_export_value_get_px (base, "x0");
1694 x1 = sp_export_value_get_px (base, "x1");
1695 bmwidth = sp_export_value_get (base, "bmwidth");
1697 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1698 bmwidth = SP_EXPORT_MIN_SIZE;
1699 sp_export_value_set (base, "bmwidth", bmwidth);
1700 }
1702 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1703 sp_export_value_set (base, "xdpi", xdpi);
1705 sp_export_set_image_y (base);
1707 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1709 return;
1710 } // end of sp_export_bitmap_width_value_changed()
1712 /// Called when pixel height is changed
1713 static void
1714 sp_export_bitmap_height_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1715 {
1716 float y0, y1, bmheight, xdpi;
1718 if (gtk_object_get_data (base, "update"))
1719 return;
1721 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1722 (base, "units"))) {
1723 return;
1724 }
1726 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1728 y0 = sp_export_value_get_px (base, "y0");
1729 y1 = sp_export_value_get_px (base, "y1");
1730 bmheight = sp_export_value_get (base, "bmheight");
1732 if (bmheight < SP_EXPORT_MIN_SIZE) {
1733 bmheight = SP_EXPORT_MIN_SIZE;
1734 sp_export_value_set (base, "bmheight", bmheight);
1735 }
1737 xdpi = bmheight * DPI_BASE / (y1 - y0);
1738 sp_export_value_set (base, "xdpi", xdpi);
1740 sp_export_set_image_x (base);
1742 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1744 return;
1745 } // end of sp_export_bitmap_width_value_changed()
1747 /**
1748 \brief A function to adjust the bitmap width when the xdpi value changes
1749 \param adj The adjustment that was changed
1750 \param base The export dialog itself
1752 The first thing this function checks is to see if we are doing an
1753 update. If we are, this function just returns because there is another
1754 instance of it that will handle everything for us. If there is a
1755 units change, we also assume that everyone is being updated appropriately
1756 and there is nothing for us to do.
1758 If we're the highest level function, we set the update flag, and
1759 continue on our way.
1761 All of the values are grabbed using the \c sp_export_value_get functions
1762 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1763 xdpi value is saved in the preferences for the next time the dialog
1764 is opened. (does the selection dpi need to be set here?)
1766 A check is done to to ensure that we aren't outputing an invalid width,
1767 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1768 changed to make it valid.
1770 After all of this the bitmap width is changed.
1772 We also change the ydpi. This is a temporary hack as these can not
1773 currently be independent. This is likely to change in the future.
1774 */
1775 void
1776 sp_export_xdpi_value_changed (GtkAdjustment */*adj*/, GtkObject *base)
1777 {
1778 float x0, x1, xdpi, bmwidth;
1780 if (gtk_object_get_data (base, "update"))
1781 return;
1783 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1784 (base, "units"))) {
1785 return;
1786 }
1788 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1790 x0 = sp_export_value_get_px (base, "x0");
1791 x1 = sp_export_value_get_px (base, "x1");
1792 xdpi = sp_export_value_get (base, "xdpi");
1794 // remember xdpi setting
1795 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1796 prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
1798 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1800 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1801 bmwidth = SP_EXPORT_MIN_SIZE;
1802 if (x1 != x0)
1803 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1804 else
1805 xdpi = DPI_BASE;
1806 sp_export_value_set (base, "xdpi", xdpi);
1807 }
1809 sp_export_value_set (base, "bmwidth", bmwidth);
1811 sp_export_set_image_y (base);
1813 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1815 return;
1816 } // end of sp_export_xdpi_value_changed()
1819 /**
1820 \brief A function to change the area that is used for the exported
1821 bitmap.
1822 \param base This is the export dialog
1823 \param x0 Horizontal upper left hand corner of the picture in points
1824 \param y0 Vertical upper left hand corner of the picture in points
1825 \param x1 Horizontal lower right hand corner of the picture in points
1826 \param y1 Vertical lower right hand corner of the picture in points
1828 This function just calls \c sp_export_value_set_px for each of the
1829 parameters that is passed in. This allows for setting them all in
1830 one convient area.
1832 Update is set to suspend all of the other test running while all the
1833 values are being set up. This allows for a performance increase, but
1834 it also means that the wrong type won't be detected with only some of
1835 the values set. After all the values are set everyone is told that
1836 there has been an update.
1837 */
1838 static void
1839 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1840 {
1841 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1842 sp_export_value_set_px (base, "x1", x1);
1843 sp_export_value_set_px (base, "y1", y1);
1844 sp_export_value_set_px (base, "x0", x0);
1845 sp_export_value_set_px (base, "y0", y0);
1846 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1848 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1849 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1851 return;
1852 }
1854 /**
1855 \brief Sets the value of an adjustment
1856 \param base The export dialog
1857 \param key Which adjustment to set
1858 \param val What value to set it to
1860 This function finds the adjustment using the data stored in the
1861 export dialog. After finding the adjustment it then sets
1862 the value of it.
1863 */
1864 static void
1865 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1866 {
1867 GtkAdjustment *adj;
1869 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1871 gtk_adjustment_set_value (adj, val);
1872 }
1874 /**
1875 \brief A function to set a value using the units points
1876 \param base The export dialog
1877 \param key Which value should be set
1878 \param val What the value should be in points
1880 This function first gets the adjustment for the key that is passed
1881 in. It then figures out what units are currently being used in the
1882 dialog. After doing all of that, it then converts the incoming
1883 value and sets the adjustment.
1884 */
1885 static void
1886 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1887 {
1888 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1890 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1892 return;
1893 }
1895 /**
1896 \brief Get the value of an adjustment in the export dialog
1897 \param base The export dialog
1898 \param key Which adjustment is being looked for
1899 \return The value in the specified adjustment
1901 This function gets the adjustment from the data field in the export
1902 dialog. It then grabs the value from the adjustment.
1903 */
1904 static float
1905 sp_export_value_get ( GtkObject *base, const gchar *key )
1906 {
1907 GtkAdjustment *adj;
1909 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1911 return adj->value;
1912 }
1914 /**
1915 \brief Grabs a value in the export dialog and converts the unit
1916 to points
1917 \param base The export dialog
1918 \param key Which value should be returned
1919 \return The value in the adjustment in points
1921 This function, at its most basic, is a call to \c sp_export_value_get
1922 to get the value of the adjustment. It then finds the units that
1923 are being used by looking at the "units" attribute of the export
1924 dialog. Using that it converts the returned value into points.
1925 */
1926 static float
1927 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1928 {
1929 float value = sp_export_value_get(base, key);
1930 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1932 return sp_units_get_pixels (value, *unit);
1933 } // end of sp_export_value_get_px()
1935 /**
1936 \brief This function is called when the filename is changed by
1937 anyone. It resets the virgin bit.
1938 \param object Text entry box
1939 \param data The export dialog
1940 \return None
1942 This function gets called when the text area is modified. It is
1943 looking for the case where the text area is modified from its
1944 original value. In that case it sets the "filename-modified" bit
1945 to TRUE. If the text dialog returns back to the original text, the
1946 bit gets reset. This should stop simple mistakes.
1947 */
1948 static void
1949 sp_export_filename_modified (GtkObject * object, gpointer data)
1950 {
1951 GtkWidget * text_entry = (GtkWidget *)object;
1952 GtkWidget * export_dialog = (GtkWidget *)data;
1954 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1955 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1956 // printf("Modified: FALSE\n");
1957 } else {
1958 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1959 // printf("Modified: TRUE\n");
1960 }
1962 return;
1963 } // end sp_export_filename_modified
1965 /*
1966 Local Variables:
1967 mode:c++
1968 c-file-style:"stroustrup"
1969 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1970 indent-tabs-mode:nil
1971 fill-column:99
1972 End:
1973 */
1974 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :