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>
34 #include <glibmm/i18n.h>
35 #include "helper/unit-menu.h"
36 #include "helper/units.h"
37 #include "unit-constants.h"
38 #include "helper/window.h"
39 #include "inkscape-private.h"
40 #include "document.h"
41 #include "desktop-handles.h"
42 #include "sp-item.h"
43 #include "selection.h"
44 #include "file.h"
45 #include "macros.h"
46 #include "sp-namedview.h"
47 #include "selection-chemistry.h"
49 #include "dialog-events.h"
50 #include "../prefs-utils.h"
51 #include "../verbs.h"
52 #include "../interface.h"
54 #include "extension/output.h"
55 #include "extension/db.h"
57 #include "io/sys.h"
59 #include "helper/png-write.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 const gchar *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 prefs_set_int_attribute (prefs_path, "x", x);
170 prefs_set_int_attribute (prefs_path, "y", y);
171 prefs_set_int_attribute (prefs_path, "w", w);
172 prefs_set_int_attribute (prefs_path, "h", h);
174 return FALSE; // which means, go ahead and destroy it
176 } // end of sp_export_dialog_delete()
178 /**
179 \brief Creates a new spin button for the export dialog
180 \param key The name of the spin button
181 \param val A default value for the spin button
182 \param min Minimum value for the spin button
183 \param max Maximum value for the spin button
184 \param step The step size for the spin button
185 \param page Size of the page increment
186 \param us Unit selector that effects this spin button
187 \param t Table to put the spin button in
188 \param x X location in the table \c t to start with
189 \param y Y location in the table \c t to start with
190 \param ll Text to put on the left side of the spin button (optional)
191 \param lr Text to put on the right side of the spin button (optional)
192 \param digits Number of digits to display after the decimal
193 \param sensitive Whether the spin button is sensitive or not
194 \param cb Callback for when this spin button is changed (optional)
195 \param dlg Export dialog the spin button is being placed in
197 */
198 static void
199 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
200 float step, float page, GtkWidget *us,
201 GtkWidget *t, int x, int y,
202 const gchar *ll, const gchar *lr,
203 int digits, unsigned int sensitive,
204 GCallback cb, GtkWidget *dlg )
205 {
206 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
207 gtk_object_set_data (a, "key", key);
208 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
210 if (us) {
211 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
212 GTK_ADJUSTMENT (a) );
213 }
215 int pos = 0;
217 GtkWidget *l = NULL;
219 if (ll) {
221 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
222 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
223 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
224 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
225 gtk_widget_set_sensitive (l, sensitive);
226 pos += 1;
228 }
230 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
231 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
232 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
233 gtk_widget_set_size_request (sb, 80, -1);
234 gtk_widget_set_sensitive (sb, sensitive);
235 pos += 1;
237 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
239 if (lr) {
241 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
242 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
243 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
244 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
245 gtk_widget_set_sensitive (l, sensitive);
246 pos += 1;
248 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
249 }
251 if (cb)
252 gtk_signal_connect (a, "value_changed", cb, dlg);
254 return;
255 } // end of sp_export_spinbutton_new()
258 static Gtk::VBox *
259 sp_export_dialog_area_box (GtkWidget * dlg)
260 {
261 Gtk::VBox* vb = new Gtk::VBox(false, 3);
263 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
264 lbl->set_use_markup(true);
265 vb->pack_start(*lbl);
267 /* Units box */
268 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
269 /* gets added to the vbox later, but the unit selector is needed
270 earlier than that */
272 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
273 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
274 if (desktop)
275 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
276 unitbox->pack_end(*us, false, false, 0);
277 Gtk::Label* l = new Gtk::Label(_("Units:"));
278 unitbox->pack_end(*l, false, false, 3);
279 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
281 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
283 Gtk::ToggleButton* b;
284 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
285 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
286 b->set_data("key", GINT_TO_POINTER(i));
287 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
288 togglebox->pack_start(*b, false, true, 0);
289 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
290 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
291 }
293 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
294 G_CALLBACK (sp_export_selection_changed), dlg );
295 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
296 G_CALLBACK (sp_export_selection_modified), dlg );
297 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
298 G_CALLBACK (sp_export_selection_changed), dlg );
300 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
301 t->set_row_spacings (4);
302 t->set_col_spacings (4);
304 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
305 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
306 G_CALLBACK ( sp_export_area_x_value_changed),
307 dlg );
309 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
310 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
311 G_CALLBACK (sp_export_area_x_value_changed),
312 dlg );
314 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
315 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
316 G_CALLBACK
317 (sp_export_area_width_value_changed),
318 dlg );
320 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
321 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
322 G_CALLBACK (sp_export_area_y_value_changed),
323 dlg );
325 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
326 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
327 G_CALLBACK (sp_export_area_y_value_changed),
328 dlg );
330 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
331 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
332 G_CALLBACK (sp_export_area_height_value_changed),
333 dlg );
335 vb->pack_start(*togglebox, false, false, 3);
336 vb->pack_start(*t, false, false, 0);
337 vb->pack_start(*unitbox, false, false, 0);
339 return vb;
340 } // end of sp_export_dialog_area_box
343 gchar* create_filepath_from_id (const gchar *id, const gchar *file_entry_text) {
345 if (id == NULL) /* This should never happen */
346 id = "bitmap";
348 gchar * directory = NULL;
350 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
351 // std::cout << "Directory from dialog" << std::endl;
352 directory = g_dirname(file_entry_text);
353 }
355 if (directory == NULL) {
356 /* Grab document directory */
357 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
358 // std::cout << "Directory from document" << std::endl;
359 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
360 }
361 }
363 if (directory == NULL) {
364 // std::cout << "Home Directory" << std::endl;
365 directory = homedir_path(NULL);
366 }
368 gchar * id_ext = g_strconcat(id, ".png", NULL);
369 gchar *filename = g_build_filename(directory, id_ext, NULL);
370 g_free(directory);
371 g_free(id_ext);
372 return filename;
373 }
375 static void
376 batch_export_clicked (GtkWidget *widget, GtkObject *base)
377 {
378 Gtk::Widget *vb_singleexport = (Gtk::Widget *)gtk_object_get_data(base, "vb_singleexport");
379 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
380 vb_singleexport->set_sensitive(false);
381 } else {
382 vb_singleexport->set_sensitive(true);
383 }
384 }
386 void
387 sp_export_dialog (void)
388 {
389 if (!dlg) {
391 gchar title[500];
392 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
394 dlg = sp_window_new (title, TRUE);
396 if (x == -1000 || y == -1000) {
397 x = prefs_get_int_attribute (prefs_path, "x", 0);
398 y = prefs_get_int_attribute (prefs_path, "y", 0);
399 }
401 if (w ==0 || h == 0) {
402 w = prefs_get_int_attribute (prefs_path, "w", 0);
403 h = prefs_get_int_attribute (prefs_path, "h", 0);
404 }
406 // if (x<0) x=0;
407 // if (y<0) y=0;
409 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
410 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
411 gtk_window_move ((GtkWindow *) dlg, x, y);
412 else
413 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
414 sp_transientize (dlg);
415 wd.win = dlg;
416 wd.stop = 0;
418 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
419 G_CALLBACK (sp_transientize_callback), &wd);
421 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
422 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
424 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
425 G_CALLBACK (sp_export_dialog_destroy), dlg);
427 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
428 G_CALLBACK (sp_export_dialog_delete), dlg);
430 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
431 G_CALLBACK (sp_export_dialog_delete), dlg);
433 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
434 G_CALLBACK (sp_dialog_hide), dlg);
436 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
437 G_CALLBACK (sp_dialog_unhide), dlg);
439 GtkTooltips *tt = gtk_tooltips_new();
441 Gtk::VBox *vb = new Gtk::VBox(false, 3);
442 vb->set_border_width(3);
443 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
445 Gtk::VBox *vb_singleexport = new Gtk::VBox(false, 0);
446 vb_singleexport->set_border_width(0);
447 vb->pack_start(*vb_singleexport);
448 gtk_object_set_data(GTK_OBJECT(dlg), "vb_singleexport", vb_singleexport);
450 /* Export area frame */
451 {
452 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
453 area_box->set_border_width(3);
454 vb_singleexport->pack_start(*area_box, false, false, 0);
455 }
457 /* Bitmap size frame */
458 {
459 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
460 size_box->set_border_width(3);
462 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
463 lbl->set_use_markup(true);
464 size_box->pack_start(*lbl, false, false, 0);
465 const int rows = 2;
466 const int cols = 5;
467 const bool homogeneous = false;
468 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
469 t->set_row_spacings (4);
470 t->set_col_spacings (4);
471 size_box->pack_start(*t);
473 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
474 NULL, GTK_WIDGET(t->gobj()), 0, 0,
475 _("_Width:"), _("pixels at"), 0, 1,
476 G_CALLBACK
477 (sp_export_bitmap_width_value_changed),
478 dlg );
480 sp_export_spinbutton_new ( "xdpi",
481 prefs_get_double_attribute
482 ( "dialogs.export.defaultxdpi",
483 "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_get_double_attribute
501 ( "dialogs.export.defaultxdpi",
502 "value", DPI_BASE),
503 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
504 NULL, _("dpi"), 2, 0, NULL, dlg );
506 vb_singleexport->pack_start(*size_box);
507 }
509 /* File entry */
510 {
511 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
512 file_box->set_border_width(3);
514 // true = has mnemonic
515 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
516 flabel->set_use_markup(true);
517 file_box->pack_start(*flabel, false, false, 0);
519 Gtk::Entry *fe = new Gtk::Entry();
521 /*
522 * set the default filename to be that of the current path + document
523 * with .png extension
524 *
525 * One thing to notice here is that this filename may get
526 * overwritten, but it won't happen here. The filename gets
527 * written into the text field, but then the button to select
528 * the area gets set. In that code the filename can be changed
529 * if there are some with presidence in the document. So, while
530 * this code sets the name first, it may not be the one users
531 * really see.
532 */
533 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
534 {
535 gchar *name;
536 SPDocument * doc = SP_ACTIVE_DOCUMENT;
537 const gchar *uri = SP_DOCUMENT_URI (doc);
538 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
539 const gchar * text_extension = repr->attribute("inkscape:output_extension");
540 Inkscape::Extension::Output * oextension = NULL;
542 if (text_extension != NULL) {
543 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
544 }
546 if (oextension != NULL) {
547 gchar * old_extension = oextension->get_extension();
548 if (g_str_has_suffix(uri, old_extension)) {
549 gchar * uri_copy;
550 gchar * extension_point;
551 gchar * final_name;
553 uri_copy = g_strdup(uri);
554 extension_point = g_strrstr(uri_copy, old_extension);
555 extension_point[0] = '\0';
557 final_name = g_strconcat(uri_copy, ".png", NULL);
558 fe->set_text(final_name);
560 g_free(final_name);
561 g_free(uri_copy);
562 }
563 } else {
564 name = g_strconcat(uri, ".png", NULL);
565 fe->set_text(name);
566 g_free(name);
567 }
569 doc_export_name = g_strdup(fe->get_text().c_str());
570 }
571 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
572 G_CALLBACK (sp_export_filename_modified), dlg);
574 Gtk::HBox *hb = new Gtk::HBox(FALSE, 5);
576 {
577 // true = has mnemonic
578 Gtk::Button *b = new Gtk::Button();
580 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
581 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
582 Gtk::ICON_SIZE_BUTTON);
583 pixlabel->pack_start(*im);
585 Gtk::Label *l = new Gtk::Label();
586 l->set_markup_with_mnemonic(_("_Browse..."));
587 pixlabel->pack_start(*l);
589 b->add(*pixlabel);
591 hb->pack_end (*b, false, false, 4);
592 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
593 G_CALLBACK (sp_export_browse_clicked), NULL );
594 }
596 hb->pack_start (*fe, true, true, 0);
597 file_box->add(*hb);
598 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
599 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
600 original_name = g_strdup(fe->get_text().c_str());
601 // pressing enter in the filename field is the same as clicking export:
602 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
603 G_CALLBACK (sp_export_export_clicked), dlg );
604 // focus is in the filename initially:
605 fe->grab_focus();
607 // mnemonic in frame label moves focus to filename:
608 flabel->set_mnemonic_widget(*fe);
610 vb_singleexport->pack_start(*file_box);
611 }
613 {
614 Gtk::HBox* batch_box = new Gtk::HBox(FALSE, 5);
615 GtkWidget *be = gtk_check_button_new_with_label(_("Batch export all selected objects"));
616 gtk_widget_set_sensitive(GTK_WIDGET(be), TRUE);
617 gtk_object_set_data(GTK_OBJECT(dlg), "batch_checkbox", be);
618 batch_box->pack_start(*Glib::wrap(be), false, false);
619 gtk_tooltips_set_tip(tt, be, _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"), NULL);
620 batch_box->show_all();
621 g_signal_connect(G_OBJECT(be), "toggled", GTK_SIGNAL_FUNC(batch_export_clicked), dlg);
622 vb->pack_start(*batch_box);
623 }
625 {
626 Gtk::HBox* hide_box = new Gtk::HBox(FALSE, 5);
627 GtkWidget *he = gtk_check_button_new_with_label(_("Hide all except selected"));
628 gtk_widget_set_sensitive(GTK_WIDGET(he), TRUE);
629 gtk_object_set_data(GTK_OBJECT(dlg), "hide_checkbox", he);
630 hide_box->pack_start(*Glib::wrap(he), false, false);
631 gtk_tooltips_set_tip(tt, he, _("In the exported image, hide all objects except those that are selected"), NULL);
632 hide_box->show_all();
633 vb->pack_start(*hide_box);
634 }
636 /* Buttons */
637 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
638 bb->set_border_width(3);
640 {
641 Gtk::Button *b = new Gtk::Button();
642 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
643 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
644 Gtk::ICON_SIZE_BUTTON);
645 image_label->pack_start(*im);
647 Gtk::Label *l = new Gtk::Label();
648 l->set_markup_with_mnemonic(_("_Export"));
649 image_label->pack_start(*l);
651 b->add(*image_label);
652 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
653 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
654 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
655 bb->pack_end(*b, false, false, 0);
656 }
658 vb->pack_end(*bb, false, false, 0);
659 vb->show_all();
661 } // end of if (!dlg)
663 sp_export_find_default_selection(dlg);
665 gtk_window_present ((GtkWindow *) dlg);
667 return;
668 } // end of sp_export_dialog()
670 static void
671 sp_export_update_checkbuttons (GtkObject *base)
672 {
673 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
674 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
675 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
676 if (num >= 2) {
677 gtk_widget_set_sensitive (be, true);
678 gtk_button_set_label (GTK_BUTTON(be), g_strdup_printf (_("Batch export %d selected objects"), num));
679 } else {
680 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(be), FALSE);
681 gtk_widget_set_sensitive (be, FALSE);
682 }
683 if (num > 0) {
684 gtk_widget_set_sensitive (he, true);
685 } else {
686 gtk_widget_set_sensitive (he, false);
687 }
688 }
690 static inline void
691 sp_export_find_default_selection(GtkWidget * dlg)
692 {
693 selection_type key = SELECTION_NUMBER_OF;
695 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
696 key = SELECTION_SELECTION;
697 }
699 /* Try using the preferences */
700 if (key == SELECTION_NUMBER_OF) {
701 const gchar *what = NULL;
702 int i = SELECTION_NUMBER_OF;
704 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
706 if (what != NULL) {
707 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
708 if (!strcmp (what, selection_names[i])) {
709 break;
710 }
711 }
712 }
714 key = (selection_type)i;
715 }
717 if (key == SELECTION_NUMBER_OF) {
718 key = SELECTION_SELECTION;
719 }
721 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
722 selection_names[key]);
723 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
725 sp_export_update_checkbuttons (GTK_OBJECT(dlg));
726 }
729 /**
730 * \brief If selection changed or a different document activated, we must
731 * recalculate any chosen areas
732 *
733 */
734 static void
735 sp_export_selection_changed ( Inkscape::Application *inkscape,
736 Inkscape::Selection *selection,
737 GtkObject *base )
738 {
739 selection_type current_key;
740 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
742 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
743 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
744 was_empty) {
745 gtk_toggle_button_set_active
746 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
747 TRUE );
748 }
749 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
751 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
753 if (inkscape &&
754 SP_IS_INKSCAPE (inkscape) &&
755 selection &&
756 SELECTION_CUSTOM != current_key) {
757 GtkToggleButton * button;
758 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
759 sp_export_area_toggled(button, base);
760 }
762 sp_export_update_checkbuttons (base);
763 }
765 static void
766 sp_export_selection_modified ( Inkscape::Application *inkscape,
767 Inkscape::Selection *selection,
768 guint flags,
769 GtkObject *base )
770 {
771 selection_type current_key;
772 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
774 switch (current_key) {
775 case SELECTION_DRAWING:
776 if ( SP_ACTIVE_DESKTOP ) {
777 SPDocument *doc;
778 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
779 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
780 if (bbox) {
781 sp_export_set_area (base, bbox->min()[NR::X],
782 bbox->min()[NR::Y],
783 bbox->max()[NR::X],
784 bbox->max()[NR::Y]);
785 }
786 }
787 break;
788 case SELECTION_SELECTION:
789 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790 NRRect bbox;
791 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
792 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
793 }
794 break;
795 default:
796 /* Do nothing for page or for custom */
797 break;
798 }
800 return;
801 }
803 /// Called when one of the selection buttons was toggled.
804 static void
805 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
806 {
807 if (gtk_object_get_data (base, "update"))
808 return;
810 selection_type key, old_key;
811 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
812 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
814 /* Ignore all "turned off" events unless we're the only active button */
815 if (!gtk_toggle_button_get_active (tb) ) {
817 /* Don't let the current selection be deactived - but rerun the
818 activate to allow the user to renew the values */
819 if (key == old_key) {
820 gtk_toggle_button_set_active ( tb, TRUE );
821 }
823 return;
824 }
826 /* Turn off the currently active button unless it's us */
827 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
829 if (old_key != key) {
830 gtk_toggle_button_set_active
831 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
832 FALSE );
833 }
835 if ( SP_ACTIVE_DESKTOP )
836 {
837 SPDocument *doc;
838 NR::Maybe<NR::Rect> bbox;
839 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
841 /* Notice how the switch is used to 'fall through' here to get
842 various backups. If you modify this without noticing you'll
843 probabaly screw something up. */
844 switch (key) {
845 case SELECTION_SELECTION:
846 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
847 {
848 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
849 /* Only if there is a selection that we can set
850 do we break, otherwise we fall through to the
851 drawing */
852 // std::cout << "Using selection: SELECTION" << std::endl;
853 key = SELECTION_SELECTION;
854 break;
855 }
856 case SELECTION_DRAWING:
857 /** \todo
858 * This returns wrong values if the document has a viewBox.
859 */
860 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
861 /* If the drawing is valid, then we'll use it and break
862 otherwise we drop through to the page settings */
863 if (bbox) {
864 // std::cout << "Using selection: DRAWING" << std::endl;
865 key = SELECTION_DRAWING;
866 break;
867 }
868 case SELECTION_PAGE:
869 bbox = NR::Rect(NR::Point(0.0, 0.0),
870 NR::Point(sp_document_width(doc), sp_document_height(doc))
871 );
873 // std::cout << "Using selection: PAGE" << std::endl;
874 key = SELECTION_PAGE;
875 break;
876 case SELECTION_CUSTOM:
877 default:
878 break;
879 } // switch
881 // remember area setting
882 prefs_set_string_attribute ( "dialogs.export.exportarea",
883 "value", selection_names[key]);
885 if ( key != SELECTION_CUSTOM && bbox ) {
886 sp_export_set_area (base, bbox->min()[NR::X],
887 bbox->min()[NR::Y],
888 bbox->max()[NR::X],
889 bbox->max()[NR::Y]);
890 }
892 } // end of if ( SP_ACTIVE_DESKTOP )
895 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
896 GtkWidget * file_entry;
897 const gchar * filename = NULL;
898 float xdpi = 0.0, ydpi = 0.0;
900 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
902 switch (key) {
903 case SELECTION_PAGE:
904 case SELECTION_DRAWING: {
905 SPDocument * doc = SP_ACTIVE_DOCUMENT;
906 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
908 if (filename == NULL) {
909 if (doc_export_name != NULL) {
910 filename = g_strdup(doc_export_name);
911 } else {
912 filename = g_strdup("");
913 }
914 }
915 break;
916 }
917 case SELECTION_SELECTION:
918 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
920 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
922 /* If we still don't have a filename -- let's build
923 one that's nice */
924 if (filename == NULL) {
925 const gchar * id = NULL;
926 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
927 for(; reprlst != NULL; reprlst = reprlst->next) {
928 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
929 if (repr->attribute("id")) {
930 id = repr->attribute("id");
931 break;
932 }
933 }
935 filename = create_filepath_from_id (id, gtk_entry_get_text(GTK_ENTRY(file_entry)));
936 }
937 }
938 break;
939 case SELECTION_CUSTOM:
940 default:
941 break;
942 }
944 if (filename != NULL) {
945 g_free(original_name);
946 original_name = g_strdup(filename);
947 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
948 }
950 if (xdpi != 0.0) {
951 sp_export_value_set(base, "xdpi", xdpi);
952 }
954 /* These can't be separate, and setting x sets y, so for
955 now setting this is disabled. Hopefully it won't be in
956 the future */
957 if (FALSE && ydpi != 0.0) {
958 sp_export_value_set(base, "ydpi", ydpi);
959 }
960 }
962 return;
963 } // end of sp_export_area_toggled()
965 /// Called when dialog is deleted
966 static gint
967 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
968 {
969 g_object_set_data (base, "cancel", (gpointer) 1);
970 return TRUE;
971 } // end of sp_export_progress_delete()
973 /// Called when progress is cancelled
974 static void
975 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
976 {
977 g_object_set_data (base, "cancel", (gpointer) 1);
978 } // end of sp_export_progress_cancel()
980 /// Called for every progress iteration
981 static unsigned int
982 sp_export_progress_callback (float value, void *data)
983 {
984 GtkWidget *prg;
985 int evtcount;
987 if (g_object_get_data ((GObject *) data, "cancel"))
988 return FALSE;
990 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
991 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
993 evtcount = 0;
994 while ((evtcount < 16) && gdk_events_pending ()) {
995 gtk_main_iteration_do (FALSE);
996 evtcount += 1;
997 }
999 gtk_main_iteration_do (FALSE);
1001 return TRUE;
1003 } // end of sp_export_progress_callback()
1005 GtkWidget *
1006 create_progress_dialog (GtkObject *base, gchar *progress_text) {
1007 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1009 dlg = gtk_dialog_new ();
1010 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1011 prg = gtk_progress_bar_new ();
1012 sp_transientize (dlg);
1013 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1014 g_object_set_data ((GObject *) base, "progress", prg);
1016 gtk_progress_bar_set_text ((GtkProgressBar *) prg, progress_text);
1018 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1019 GTK_PROGRESS_LEFT_TO_RIGHT);
1020 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1021 prg, FALSE, FALSE, 4 );
1022 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1023 GTK_STOCK_CANCEL,
1024 GTK_RESPONSE_CANCEL );
1026 g_signal_connect ( (GObject *) dlg, "delete_event",
1027 (GCallback) sp_export_progress_delete, base);
1028 g_signal_connect ( (GObject *) btn, "clicked",
1029 (GCallback) sp_export_progress_cancel, base);
1030 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1031 gtk_widget_show_all (dlg);
1033 return dlg;
1034 }
1036 /// Called when export button is clicked
1037 static void
1038 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1039 {
1040 if (!SP_ACTIVE_DESKTOP) return;
1042 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1044 GtkWidget *be = (GtkWidget *)gtk_object_get_data(base, "batch_checkbox");
1045 GtkWidget *he = (GtkWidget *)gtk_object_get_data(base, "hide_checkbox");
1046 bool hide = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (he));
1047 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (be))) {
1048 // Batch export of selected objects
1050 gint num = g_slist_length((GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
1051 gint n = 0;
1053 if (num < 1)
1054 return;
1056 gchar *progress_text = g_strdup_printf (_("Exporting %d files"), num);
1057 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1058 g_free (progress_text);
1060 for (GSList *i = (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
1061 i != NULL;
1062 i = i->next) {
1063 SPItem *item = (SPItem *) i->data;
1064 // retrieve export filename hint
1065 const gchar *fn = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename");
1066 if (!fn) {
1067 fn = create_filepath_from_id (SP_OBJECT_ID(item), NULL);
1068 }
1070 // retrieve export dpi hints
1071 const gchar *dpi_hint = SP_OBJECT_REPR(item)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1072 gdouble dpi = 0.0;
1073 if (dpi_hint) {
1074 dpi = atof(dpi_hint);
1075 }
1076 if (dpi == 0.0) {
1077 dpi = DPI_BASE;
1078 }
1080 NRRect area;
1081 sp_item_invoke_bbox(item, &area, sp_item_i2r_affine((SPItem *) item), TRUE);
1083 gint width = (gint) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1084 gint height = (gint) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1086 if (width > 1 && height > 1) {
1087 /* Do export */
1088 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), fn,
1089 area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi,
1090 nv->pagecolor,
1091 NULL, NULL, TRUE, // overwrite without asking
1092 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1093 )) {
1094 gchar * error;
1095 gchar * safeFile = Inkscape::IO::sanitizeString(fn);
1096 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1097 sp_ui_error_dialog(error);
1098 g_free(safeFile);
1099 g_free(error);
1100 }
1101 }
1102 n++;
1103 sp_export_progress_callback((float)n/num, base);
1104 }
1106 gtk_widget_destroy (prog_dlg);
1107 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1109 } else {
1111 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1112 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1114 float const x0 = sp_export_value_get_px(base, "x0");
1115 float const y0 = sp_export_value_get_px(base, "y0");
1116 float const x1 = sp_export_value_get_px(base, "x1");
1117 float const y1 = sp_export_value_get_px(base, "y1");
1118 float const xdpi = sp_export_value_get(base, "xdpi");
1119 float const ydpi = sp_export_value_get(base, "ydpi");
1120 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1121 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1123 if (filename == NULL || *filename == '\0') {
1124 sp_ui_error_dialog(_("You have to enter a filename"));
1125 return;
1126 }
1128 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1129 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1130 return;
1131 }
1133 gchar *dirname = g_dirname(filename);
1134 if ( dirname == NULL
1135 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1136 {
1137 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1138 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1139 safeDir);
1140 sp_ui_error_dialog(error);
1141 g_free(safeDir);
1142 g_free(error);
1143 g_free(dirname);
1144 return;
1145 }
1146 g_free(dirname);
1148 gchar *fn = g_path_get_basename (filename);
1149 gchar *progress_text = g_strdup_printf (_("Exporting %s (%d x %d)"), fn, width, height);
1150 g_free (fn);
1151 GtkWidget *prog_dlg = create_progress_dialog (base, progress_text);
1152 g_free (progress_text);
1154 /* Do export */
1155 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1156 x0, y0, x1, y1, width, height, xdpi, ydpi,
1157 nv->pagecolor,
1158 sp_export_progress_callback, base, FALSE,
1159 hide ? (GSList *) sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList() : NULL
1160 )) {
1161 gchar * error;
1162 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1163 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1164 sp_ui_error_dialog(error);
1165 g_free(safeFile);
1166 g_free(error);
1167 }
1169 /* Reset the filename so that it can be changed again by changing
1170 selections and all that */
1171 g_free(original_name);
1172 original_name = g_strdup(filename);
1173 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1175 gtk_widget_destroy (prog_dlg);
1176 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1178 /* Setup the values in the document */
1179 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1180 case SELECTION_PAGE:
1181 case SELECTION_DRAWING: {
1182 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1183 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1184 bool modified = false;
1185 const gchar * temp_string;
1187 bool saved = sp_document_get_undo_sensitive(doc);
1188 sp_document_set_undo_sensitive(doc, false);
1190 temp_string = repr->attribute("inkscape:export-filename");
1191 if (temp_string == NULL || strcmp(temp_string, filename)) {
1192 repr->setAttribute("inkscape:export-filename", filename);
1193 modified = true;
1194 }
1195 temp_string = repr->attribute("inkscape:export-xdpi");
1196 if (temp_string == NULL || xdpi != atof(temp_string)) {
1197 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1198 modified = true;
1199 }
1200 temp_string = repr->attribute("inkscape:export-ydpi");
1201 if (temp_string == NULL || xdpi != atof(temp_string)) {
1202 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1203 modified = true;
1204 }
1206 if (modified)
1207 repr->setAttribute("sodipodi:modified", "TRUE");
1208 sp_document_set_undo_sensitive(doc, saved);
1209 break;
1210 }
1211 case SELECTION_SELECTION: {
1212 const GSList * reprlst;
1213 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1214 bool modified = false;
1216 bool saved = sp_document_get_undo_sensitive(doc);
1217 sp_document_set_undo_sensitive(doc, false);
1218 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1220 for(; reprlst != NULL; reprlst = reprlst->next) {
1221 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1222 const gchar * temp_string;
1224 if (repr->attribute("id") == NULL ||
1225 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1226 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1227 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1228 temp_string = repr->attribute("inkscape:export-filename");
1229 if (temp_string == NULL || strcmp(temp_string, filename)) {
1230 repr->setAttribute("inkscape:export-filename", filename);
1231 modified = true;
1232 }
1233 }
1234 temp_string = repr->attribute("inkscape:export-xdpi");
1235 if (temp_string == NULL || xdpi != atof(temp_string)) {
1236 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1237 modified = true;
1238 }
1239 temp_string = repr->attribute("inkscape:export-ydpi");
1240 if (temp_string == NULL || xdpi != atof(temp_string)) {
1241 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1242 modified = true;
1243 }
1244 }
1246 if (modified) {
1247 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1248 repr->setAttribute("sodipodi:modified", "TRUE");
1249 }
1251 sp_document_set_undo_sensitive(doc, saved);
1252 break;
1253 }
1254 default:
1255 break;
1256 }
1258 }
1260 } // end of sp_export_export_clicked()
1263 // FIXME: Some lib function should be available to do this ...
1264 static gchar *
1265 filename_add_extension (const gchar *filename, const gchar *extension)
1266 {
1267 gchar *dot;
1269 dot = strrchr (filename, '.');
1270 if ( !dot )
1271 return g_strconcat (filename, ".", extension, NULL);
1272 {
1273 if (dot[1] == '\0')
1274 return g_strconcat (filename, extension, NULL);
1275 else
1276 {
1277 if (g_strcasecmp (dot + 1, extension) == 0)
1278 return g_strdup (filename);
1279 else
1280 {
1281 return g_strconcat (filename, ".", extension, NULL);
1282 }
1283 }
1284 }
1285 }
1287 /// Called when Browse button is clicked
1288 static void
1289 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1290 {
1291 GtkWidget *fs, *fe;
1292 const gchar *filename;
1294 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1295 NULL,
1296 GTK_FILE_CHOOSER_ACTION_SAVE,
1297 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1298 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1299 NULL );
1301 #ifdef WITH_GNOME_VFS
1302 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER (fs), false);
1303 #endif
1305 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1307 sp_transientize (fs);
1309 gtk_window_set_modal(GTK_WINDOW (fs), true);
1311 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1313 if (*filename == '\0') {
1314 filename = homedir_path(NULL);
1315 }
1317 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1319 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1320 {
1321 gchar *file;
1323 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1324 // make sure that .png is the extension of the file:
1325 gchar * file_ext = filename_add_extension(file, "png");
1327 gchar * utf8file = g_filename_to_utf8( file_ext, -1, NULL, NULL, NULL );
1328 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1330 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1332 g_free(file_ext);
1333 g_free(utf8file);
1334 g_free(file);
1335 }
1337 gtk_widget_destroy (fs);
1339 return;
1340 } // end of sp_export_browse_clicked()
1342 // TODO: Move this to nr-rect-fns.h.
1343 static bool
1344 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1345 {
1346 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1347 return (
1348 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1349 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1350 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1351 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1352 );
1353 }
1355 /**
1356 \brief This function is used to detect the current selection setting
1357 based on the values in the x0, y0, x1 and y0 fields.
1358 \param base The export dialog itself
1360 One of the most confusing parts of this function is why the array
1361 is built at the beginning. What needs to happen here is that we
1362 should always check the current selection to see if it is the valid
1363 one. While this is a performance improvement it is also a usability
1364 one during the cases where things like selections and drawings match
1365 size. This way buttons change less 'randomly' (atleast in the eyes
1366 of the user). To do this an array is built where the current selection
1367 type is placed first, and then the others in an order from smallest
1368 to largest (this can be configured by reshuffling \c test_order).
1370 All of the values in this function are rounded to two decimal places
1371 because that is what is shown to the user. While everything is kept
1372 more accurate than that, the user can't control more acurrate than
1373 that, so for this to work for them - it needs to check on that level
1374 of accuracy.
1376 \todo finish writing this up
1377 */
1378 static void
1379 sp_export_detect_size(GtkObject * base) {
1380 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1381 selection_type this_test[SELECTION_NUMBER_OF + 1];
1382 selection_type key = SELECTION_NUMBER_OF;
1384 NR::Point x(sp_export_value_get_px (base, "x0"),
1385 sp_export_value_get_px (base, "y0"));
1386 NR::Point y(sp_export_value_get_px (base, "x1"),
1387 sp_export_value_get_px (base, "y1"));
1388 NR::Rect current_bbox(x, y);
1389 //std::cout << "Current " << current_bbox;
1391 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1392 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1393 this_test[i + 1] = test_order[i];
1394 }
1396 for (int i = 0;
1397 i < SELECTION_NUMBER_OF + 1 &&
1398 key == SELECTION_NUMBER_OF &&
1399 SP_ACTIVE_DESKTOP != NULL;
1400 i++) {
1401 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1402 switch (this_test[i]) {
1403 case SELECTION_SELECTION:
1404 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1405 NR::Maybe<NR::Rect> bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1407 //std::cout << "Selection " << bbox;
1408 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox)) {
1409 key = SELECTION_SELECTION;
1410 }
1411 }
1412 break;
1413 case SELECTION_DRAWING: {
1414 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1416 NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1418 // std::cout << "Drawing " << bbox2;
1419 if ( bbox && sp_export_bbox_equal(*bbox,current_bbox) ) {
1420 key = SELECTION_DRAWING;
1421 }
1422 break;
1423 }
1425 case SELECTION_PAGE: {
1426 SPDocument *doc;
1428 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1430 NR::Point x(0.0, 0.0);
1431 NR::Point y(sp_document_width(doc),
1432 sp_document_height(doc));
1433 NR::Rect bbox(x, y);
1435 // std::cout << "Page " << bbox;
1436 if (sp_export_bbox_equal(bbox,current_bbox)) {
1437 key = SELECTION_PAGE;
1438 }
1440 break;
1441 }
1442 default:
1443 break;
1444 }
1445 }
1446 // std::cout << std::endl;
1448 if (key == SELECTION_NUMBER_OF) {
1449 key = SELECTION_CUSTOM;
1450 }
1452 /* We're now using a custom size, not a fixed one */
1453 /* printf("Detecting state: %s\n", selection_names[key]); */
1454 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1455 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1456 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1457 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1459 return;
1460 } /* sp_export_detect_size */
1462 /// Called when area x0 value is changed
1463 static void
1464 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1465 {
1466 float x0, x1, xdpi, width;
1468 if (gtk_object_get_data (base, "update"))
1469 return;
1471 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1472 (base, "units")))
1473 {
1474 return;
1475 }
1477 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1479 x0 = sp_export_value_get_px (base, "x0");
1480 x1 = sp_export_value_get_px (base, "x1");
1481 xdpi = sp_export_value_get (base, "xdpi");
1483 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1485 if (width < SP_EXPORT_MIN_SIZE) {
1486 const gchar *key;
1487 width = SP_EXPORT_MIN_SIZE;
1488 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1490 if (!strcmp (key, "x0")) {
1491 x1 = x0 + width * DPI_BASE / xdpi;
1492 sp_export_value_set_px (base, "x1", x1);
1493 } else {
1494 x0 = x1 - width * DPI_BASE / xdpi;
1495 sp_export_value_set_px (base, "x0", x0);
1496 }
1497 }
1499 sp_export_value_set_px (base, "width", x1 - x0);
1500 sp_export_value_set (base, "bmwidth", width);
1502 sp_export_detect_size(base);
1504 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1506 return;
1507 } // end of sp_export_area_x_value_changed()
1509 /// Called when area y0 value is changed.
1510 static void
1511 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1512 {
1513 float y0, y1, ydpi, height;
1515 if (gtk_object_get_data (base, "update"))
1516 return;
1518 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1519 (base, "units")))
1520 {
1521 return;
1522 }
1524 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1526 y0 = sp_export_value_get_px (base, "y0");
1527 y1 = sp_export_value_get_px (base, "y1");
1528 ydpi = sp_export_value_get (base, "ydpi");
1530 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1532 if (height < SP_EXPORT_MIN_SIZE) {
1533 const gchar *key;
1534 height = SP_EXPORT_MIN_SIZE;
1535 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1536 if (!strcmp (key, "y0")) {
1537 y1 = y0 + height * DPI_BASE / ydpi;
1538 sp_export_value_set_px (base, "y1", y1);
1539 } else {
1540 y0 = y1 - height * DPI_BASE / ydpi;
1541 sp_export_value_set_px (base, "y0", y0);
1542 }
1543 }
1545 sp_export_value_set_px (base, "height", y1 - y0);
1546 sp_export_value_set (base, "bmheight", height);
1548 sp_export_detect_size(base);
1550 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1552 return;
1553 } // end of sp_export_area_y_value_changed()
1555 /// Called when x1-x0 or area width is changed
1556 static void
1557 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1558 {
1559 float x0, x1, xdpi, width, bmwidth;
1561 if (gtk_object_get_data (base, "update"))
1562 return;
1564 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1565 (base, "units"))) {
1566 return;
1567 }
1569 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1571 x0 = sp_export_value_get_px (base, "x0");
1572 x1 = sp_export_value_get_px (base, "x1");
1573 xdpi = sp_export_value_get (base, "xdpi");
1574 width = sp_export_value_get_px (base, "width");
1575 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1577 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1579 bmwidth = SP_EXPORT_MIN_SIZE;
1580 width = bmwidth * DPI_BASE / xdpi;
1581 sp_export_value_set_px (base, "width", width);
1582 }
1584 sp_export_value_set_px (base, "x1", x0 + width);
1585 sp_export_value_set (base, "bmwidth", bmwidth);
1587 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1589 return;
1590 } // end of sp_export_area_width_value_changed()
1592 /// Called when y1-y0 or area height is changed.
1593 static void
1594 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1595 {
1597 float y0, y1, ydpi, height, bmheight;
1599 if (gtk_object_get_data (base, "update"))
1600 return;
1602 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1603 (base, "units"))) {
1604 return;
1605 }
1607 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1609 y0 = sp_export_value_get_px (base, "y0");
1610 y1 = sp_export_value_get_px (base, "y1");
1611 ydpi = sp_export_value_get (base, "ydpi");
1612 height = sp_export_value_get_px (base, "height");
1613 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1615 if (bmheight < SP_EXPORT_MIN_SIZE) {
1616 bmheight = SP_EXPORT_MIN_SIZE;
1617 height = bmheight * DPI_BASE / ydpi;
1618 sp_export_value_set_px (base, "height", height);
1619 }
1621 sp_export_value_set_px (base, "y1", y0 + height);
1622 sp_export_value_set (base, "bmheight", bmheight);
1624 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1626 return;
1627 } // end of sp_export_area_height_value_changed()
1629 /**
1630 \brief A function to set the ydpi
1631 \param base The export dialog
1633 This function grabs all of the y values and then figures out the
1634 new bitmap size based on the changing dpi value. The dpi value is
1635 gotten from the xdpi setting as these can not currently be independent.
1636 */
1637 static void
1638 sp_export_set_image_y (GtkObject *base)
1639 {
1640 float y0, y1, xdpi;
1642 y0 = sp_export_value_get_px (base, "y0");
1643 y1 = sp_export_value_get_px (base, "y1");
1644 xdpi = sp_export_value_get (base, "xdpi");
1646 sp_export_value_set (base, "ydpi", xdpi);
1647 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1649 return;
1650 } // end of sp_export_set_image_y()
1652 /**
1653 \brief A function to set the xdpi
1654 \param base The export dialog
1656 This function grabs all of the x values and then figures out the
1657 new bitmap size based on the changing dpi value. The dpi value is
1658 gotten from the xdpi setting as these can not currently be independent.
1659 */
1660 static void
1661 sp_export_set_image_x (GtkObject *base)
1662 {
1663 float x0, x1, xdpi;
1665 x0 = sp_export_value_get_px (base, "x0");
1666 x1 = sp_export_value_get_px (base, "x1");
1667 xdpi = sp_export_value_get (base, "xdpi");
1669 sp_export_value_set (base, "ydpi", xdpi);
1670 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1672 return;
1673 } // end of sp_export_set_image_x()
1675 /// Called when pixel width is changed
1676 static void
1677 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1678 {
1679 float x0, x1, bmwidth, xdpi;
1681 if (gtk_object_get_data (base, "update"))
1682 return;
1684 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1685 (base, "units"))) {
1686 return;
1687 }
1689 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1691 x0 = sp_export_value_get_px (base, "x0");
1692 x1 = sp_export_value_get_px (base, "x1");
1693 bmwidth = sp_export_value_get (base, "bmwidth");
1695 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1696 bmwidth = SP_EXPORT_MIN_SIZE;
1697 sp_export_value_set (base, "bmwidth", bmwidth);
1698 }
1700 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1701 sp_export_value_set (base, "xdpi", xdpi);
1703 sp_export_set_image_y (base);
1705 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1707 return;
1708 } // end of sp_export_bitmap_width_value_changed()
1710 /// Called when pixel height is changed
1711 static void
1712 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1713 {
1714 float y0, y1, bmheight, xdpi;
1716 if (gtk_object_get_data (base, "update"))
1717 return;
1719 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1720 (base, "units"))) {
1721 return;
1722 }
1724 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1726 y0 = sp_export_value_get_px (base, "y0");
1727 y1 = sp_export_value_get_px (base, "y1");
1728 bmheight = sp_export_value_get (base, "bmheight");
1730 if (bmheight < SP_EXPORT_MIN_SIZE) {
1731 bmheight = SP_EXPORT_MIN_SIZE;
1732 sp_export_value_set (base, "bmheight", bmheight);
1733 }
1735 xdpi = bmheight * DPI_BASE / (y1 - y0);
1736 sp_export_value_set (base, "xdpi", xdpi);
1738 sp_export_set_image_x (base);
1740 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1742 return;
1743 } // end of sp_export_bitmap_width_value_changed()
1745 /**
1746 \brief A function to adjust the bitmap width when the xdpi value changes
1747 \param adj The adjustment that was changed
1748 \param base The export dialog itself
1750 The first thing this function checks is to see if we are doing an
1751 update. If we are, this function just returns because there is another
1752 instance of it that will handle everything for us. If there is a
1753 units change, we also assume that everyone is being updated appropriately
1754 and there is nothing for us to do.
1756 If we're the highest level function, we set the update flag, and
1757 continue on our way.
1759 All of the values are grabbed using the \c sp_export_value_get functions
1760 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1761 xdpi value is saved in the preferences for the next time the dialog
1762 is opened. (does the selection dpi need to be set here?)
1764 A check is done to to ensure that we aren't outputing an invalid width,
1765 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1766 changed to make it valid.
1768 After all of this the bitmap width is changed.
1770 We also change the ydpi. This is a temporary hack as these can not
1771 currently be independent. This is likely to change in the future.
1772 */
1773 void
1774 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1775 {
1776 float x0, x1, xdpi, bmwidth;
1778 if (gtk_object_get_data (base, "update"))
1779 return;
1781 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1782 (base, "units"))) {
1783 return;
1784 }
1786 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1788 x0 = sp_export_value_get_px (base, "x0");
1789 x1 = sp_export_value_get_px (base, "x1");
1790 xdpi = sp_export_value_get (base, "xdpi");
1792 // remember xdpi setting
1793 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1795 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1797 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1798 bmwidth = SP_EXPORT_MIN_SIZE;
1799 if (x1 != x0)
1800 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1801 else
1802 xdpi = DPI_BASE;
1803 sp_export_value_set (base, "xdpi", xdpi);
1804 }
1806 sp_export_value_set (base, "bmwidth", bmwidth);
1808 sp_export_set_image_y (base);
1810 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1812 return;
1813 } // end of sp_export_xdpi_value_changed()
1816 /**
1817 \brief A function to change the area that is used for the exported
1818 bitmap.
1819 \param base This is the export dialog
1820 \param x0 Horizontal upper left hand corner of the picture in points
1821 \param y0 Vertical upper left hand corner of the picture in points
1822 \param x1 Horizontal lower right hand corner of the picture in points
1823 \param y1 Vertical lower right hand corner of the picture in points
1825 This function just calls \c sp_export_value_set_px for each of the
1826 parameters that is passed in. This allows for setting them all in
1827 one convient area.
1829 Update is set to suspend all of the other test running while all the
1830 values are being set up. This allows for a performance increase, but
1831 it also means that the wrong type won't be detected with only some of
1832 the values set. After all the values are set everyone is told that
1833 there has been an update.
1834 */
1835 static void
1836 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1837 {
1838 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1839 sp_export_value_set_px (base, "x1", x1);
1840 sp_export_value_set_px (base, "y1", y1);
1841 sp_export_value_set_px (base, "x0", x0);
1842 sp_export_value_set_px (base, "y0", y0);
1843 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1845 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1846 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1848 return;
1849 }
1851 /**
1852 \brief Sets the value of an adjustment
1853 \param base The export dialog
1854 \param key Which adjustment to set
1855 \param val What value to set it to
1857 This function finds the adjustment using the data stored in the
1858 export dialog. After finding the adjustment it then sets
1859 the value of it.
1860 */
1861 static void
1862 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1863 {
1864 GtkAdjustment *adj;
1866 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1868 gtk_adjustment_set_value (adj, val);
1869 }
1871 /**
1872 \brief A function to set a value using the units points
1873 \param base The export dialog
1874 \param key Which value should be set
1875 \param val What the value should be in points
1877 This function first gets the adjustment for the key that is passed
1878 in. It then figures out what units are currently being used in the
1879 dialog. After doing all of that, it then converts the incoming
1880 value and sets the adjustment.
1881 */
1882 static void
1883 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1884 {
1885 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1887 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1889 return;
1890 }
1892 /**
1893 \brief Get the value of an adjustment in the export dialog
1894 \param base The export dialog
1895 \param key Which adjustment is being looked for
1896 \return The value in the specified adjustment
1898 This function gets the adjustment from the data field in the export
1899 dialog. It then grabs the value from the adjustment.
1900 */
1901 static float
1902 sp_export_value_get ( GtkObject *base, const gchar *key )
1903 {
1904 GtkAdjustment *adj;
1906 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1908 return adj->value;
1909 }
1911 /**
1912 \brief Grabs a value in the export dialog and converts the unit
1913 to points
1914 \param base The export dialog
1915 \param key Which value should be returned
1916 \return The value in the adjustment in points
1918 This function, at its most basic, is a call to \c sp_export_value_get
1919 to get the value of the adjustment. It then finds the units that
1920 are being used by looking at the "units" attribute of the export
1921 dialog. Using that it converts the returned value into points.
1922 */
1923 static float
1924 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1925 {
1926 float value = sp_export_value_get(base, key);
1927 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1929 return sp_units_get_pixels (value, *unit);
1930 } // end of sp_export_value_get_px()
1932 /**
1933 \brief This function is called when the filename is changed by
1934 anyone. It resets the virgin bit.
1935 \param object Text entry box
1936 \param data The export dialog
1937 \return None
1939 This function gets called when the text area is modified. It is
1940 looking for the case where the text area is modified from its
1941 original value. In that case it sets the "filename-modified" bit
1942 to TRUE. If the text dialog returns back to the original text, the
1943 bit gets reset. This should stop simple mistakes.
1944 */
1945 static void
1946 sp_export_filename_modified (GtkObject * object, gpointer data)
1947 {
1948 GtkWidget * text_entry = (GtkWidget *)object;
1949 GtkWidget * export_dialog = (GtkWidget *)data;
1951 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1952 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1953 // printf("Modified: FALSE\n");
1954 } else {
1955 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1956 // printf("Modified: TRUE\n");
1957 }
1959 return;
1960 } // end sp_export_filename_modified
1962 /*
1963 Local Variables:
1964 mode:c++
1965 c-file-style:"stroustrup"
1966 c-file-offsets:((innamespace . 0)(inline-open . 0))
1967 indent-tabs-mode:nil
1968 fill-column:99
1969 End:
1970 */
1971 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :