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 *
12 * Copyright (C) 1999-2005 Authors
13 * Copyright (C) 2001-2002 Ximian, Inc.
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include <gtkmm/box.h>
24 #include <gtkmm/buttonbox.h>
25 #include <gtkmm/label.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/togglebutton.h>
28 #include <gtkmm/entry.h>
29 #include <gtkmm/image.h>
30 #include <gtkmm/stockid.h>
31 #include <gtkmm/stock.h>
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"
47 #include "dialog-events.h"
48 #include "../prefs-utils.h"
49 #include "../verbs.h"
50 #include "../interface.h"
52 #include "extension/output.h"
53 #include "extension/db.h"
55 #include "io/sys.h"
57 #include "helper/png-write.h"
60 #define SP_EXPORT_MIN_SIZE 1.0
62 #define DPI_BASE PX_PER_IN
64 #define EXPORT_COORD_PRECISION 3
66 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
67 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
68 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
70 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
71 GtkObject *base);
73 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
74 GtkObject *base);
76 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
77 GtkObject *base);
79 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
80 GtkObject *base);
82 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
83 GtkObject *base);
85 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
86 GtkObject *base);
88 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
89 GtkObject *base);
91 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
92 Inkscape::Selection *selection,
93 GtkObject *base);
94 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
95 Inkscape::Selection *selection,
96 guint flags,
97 GtkObject *base );
99 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
100 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
101 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
102 static float sp_export_value_get ( GtkObject *base, const gchar *key );
103 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
105 static void sp_export_filename_modified (GtkObject * object, gpointer data);
106 static inline void sp_export_find_default_selection(GtkWidget * dlg);
107 static void sp_export_detect_size(GtkObject * base);
109 static const gchar *prefs_path = "dialogs.export";
111 // these all need to be reinitialized to their defaults during dialog_destroy
112 static GtkWidget *dlg = NULL;
113 static win_data wd;
114 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
115 static gchar * original_name = NULL;
116 static gchar * doc_export_name = NULL;
117 static bool was_empty = TRUE;
119 /** What type of button is being pressed */
120 enum selection_type {
121 SELECTION_PAGE = 0, /**< Export the whole page */
122 SELECTION_DRAWING, /**< Export everything drawn on the page */
123 SELECTION_SELECTION, /**< Export everything that is selected */
124 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
125 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
126 };
128 /** A list of strings that is used both in the preferences, and in the
129 data fields to describe the various values of \c selection_type. */
130 static const char * selection_names[SELECTION_NUMBER_OF] = {
131 "page", "drawing", "selection", "custom"};
133 /** The names on the buttons for the various selection types. */
134 static const char * selection_labels[SELECTION_NUMBER_OF] = {
135 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
137 static void
138 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
139 {
140 sp_signal_disconnect_by_data (INKSCAPE, dlg);
142 wd.win = dlg = NULL;
143 wd.stop = 0;
144 x = -1000; y = -1000; w = 0; h = 0;
145 g_free(original_name);
146 original_name = NULL;
147 g_free(doc_export_name);
148 doc_export_name = NULL;
149 was_empty = TRUE;
151 return;
152 } // end of sp_export_dialog_destroy()
154 /// Called when dialog is closed or inkscape is shut down.
155 static bool
156 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
157 {
159 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
160 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
162 if (x<0) x=0;
163 if (y<0) y=0;
165 prefs_set_int_attribute (prefs_path, "x", x);
166 prefs_set_int_attribute (prefs_path, "y", y);
167 prefs_set_int_attribute (prefs_path, "w", w);
168 prefs_set_int_attribute (prefs_path, "h", h);
170 return FALSE; // which means, go ahead and destroy it
172 } // end of sp_export_dialog_delete()
174 /**
175 \brief Creates a new spin button for the export dialog
176 \param key The name of the spin button
177 \param val A default value for the spin button
178 \param min Minimum value for the spin button
179 \param max Maximum value for the spin button
180 \param step The step size for the spin button
181 \param page Size of the page increment
182 \param us Unit selector that effects this spin button
183 \param t Table to put the spin button in
184 \param x X location in the table \c t to start with
185 \param y Y location in the table \c t to start with
186 \param ll Text to put on the left side of the spin button (optional)
187 \param lr Text to put on the right side of the spin button (optional)
188 \param digits Number of digits to display after the decimal
189 \param sensitive Whether the spin button is sensitive or not
190 \param cb Callback for when this spin button is changed (optional)
191 \param dlg Export dialog the spin button is being placed in
193 */
194 static void
195 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
196 float step, float page, GtkWidget *us,
197 GtkWidget *t, int x, int y,
198 const gchar *ll, const gchar *lr,
199 int digits, unsigned int sensitive,
200 GCallback cb, GtkWidget *dlg )
201 {
202 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
203 gtk_object_set_data (a, "key", key);
204 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
206 if (us) {
207 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
208 GTK_ADJUSTMENT (a) );
209 }
211 int pos = 0;
213 GtkWidget *l = NULL;
215 if (ll) {
217 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
218 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
219 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
220 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
221 gtk_widget_set_sensitive (l, sensitive);
222 pos += 1;
224 }
226 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
227 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
228 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229 gtk_widget_set_size_request (sb, 80, -1);
230 gtk_widget_set_sensitive (sb, sensitive);
231 pos += 1;
233 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
235 if (lr) {
237 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
238 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
239 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
240 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
241 gtk_widget_set_sensitive (l, sensitive);
242 pos += 1;
244 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
245 }
247 if (cb)
248 gtk_signal_connect (a, "value_changed", cb, dlg);
250 return;
251 } // end of sp_export_spinbutton_new()
254 static Gtk::VBox *
255 sp_export_dialog_area_box (GtkWidget * dlg)
256 {
257 Gtk::VBox* vb = new Gtk::VBox(false, 3);
259 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
260 lbl->set_use_markup(true);
261 vb->pack_start(*lbl);
263 /* Units box */
264 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
265 /* gets added to the vbox later, but the unit selector is needed
266 earlier than that */
268 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
269 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
270 if (desktop)
271 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
272 unitbox->pack_end(*us, false, false, 0);
273 Gtk::Label* l = new Gtk::Label(_("Units:"));
274 unitbox->pack_end(*l, false, false, 3);
275 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
277 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
279 Gtk::ToggleButton* b;
280 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
281 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
282 b->set_data("key", GINT_TO_POINTER(i));
283 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
284 togglebox->pack_start(*b, false, true, 0);
285 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
286 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
287 }
289 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
290 G_CALLBACK (sp_export_selection_changed), dlg );
291 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
292 G_CALLBACK (sp_export_selection_modified), dlg );
293 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
294 G_CALLBACK (sp_export_selection_changed), dlg );
296 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
297 t->set_row_spacings (4);
298 t->set_col_spacings (4);
300 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
301 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
302 G_CALLBACK ( sp_export_area_x_value_changed),
303 dlg );
305 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
306 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
307 G_CALLBACK (sp_export_area_x_value_changed),
308 dlg );
310 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
311 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
312 G_CALLBACK
313 (sp_export_area_width_value_changed),
314 dlg );
316 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
317 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
318 G_CALLBACK (sp_export_area_y_value_changed),
319 dlg );
321 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
322 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
323 G_CALLBACK (sp_export_area_y_value_changed),
324 dlg );
326 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
327 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
328 G_CALLBACK (sp_export_area_height_value_changed),
329 dlg );
331 vb->pack_start(*togglebox, false, false, 3);
332 vb->pack_start(*t, false, false, 0);
333 vb->pack_start(*unitbox, false, false, 0);
335 return vb;
336 } // end of sp_export_dialog_area_box
339 void
340 sp_export_dialog (void)
341 {
342 if (!dlg) {
343 Gtk::VBox* vb;
344 Gtk::HBox* hb;
346 gchar title[500];
347 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
349 dlg = sp_window_new (title, TRUE);
351 if (x == -1000 || y == -1000) {
352 x = prefs_get_int_attribute (prefs_path, "x", 0);
353 y = prefs_get_int_attribute (prefs_path, "y", 0);
354 }
356 if (w ==0 || h == 0) {
357 w = prefs_get_int_attribute (prefs_path, "w", 0);
358 h = prefs_get_int_attribute (prefs_path, "h", 0);
359 }
361 if (x<0) x=0;
362 if (y<0) y=0;
364 if (x != 0 || y != 0) {
365 gtk_window_move ((GtkWindow *) dlg, x, y);
366 } else {
367 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
368 }
370 if (w && h)
371 gtk_window_resize ((GtkWindow *) dlg, w, h);
373 sp_transientize (dlg);
374 wd.win = dlg;
375 wd.stop = 0;
377 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
378 G_CALLBACK (sp_transientize_callback), &wd);
380 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
381 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
383 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
384 G_CALLBACK (sp_export_dialog_destroy), dlg);
386 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
387 G_CALLBACK (sp_export_dialog_delete), dlg);
389 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
390 G_CALLBACK (sp_export_dialog_delete), dlg);
392 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
393 G_CALLBACK (sp_dialog_hide), dlg);
395 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
396 G_CALLBACK (sp_dialog_unhide), dlg);
398 GtkTooltips *tt = gtk_tooltips_new();
400 vb = new Gtk::VBox(false, 3);
401 vb->set_border_width(3);
402 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
404 /* Export area frame */
405 {
406 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
407 area_box->set_border_width(3);
408 vb->pack_start(*area_box, false, false, 0);
409 }
411 /* Bitmap size frame */
412 {
413 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
414 size_box->set_border_width(3);
416 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
417 lbl->set_use_markup(true);
418 size_box->pack_start(*lbl, false, false, 0);
419 const int rows = 2;
420 const int cols = 5;
421 const bool homogeneous = false;
422 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
423 t->set_row_spacings (4);
424 t->set_col_spacings (4);
425 size_box->pack_start(*t);
427 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
428 NULL, GTK_WIDGET(t->gobj()), 0, 0,
429 _("_Width:"), _("pixels at"), 0, 1,
430 G_CALLBACK
431 (sp_export_bitmap_width_value_changed),
432 dlg );
434 sp_export_spinbutton_new ( "xdpi",
435 prefs_get_double_attribute
436 ( "dialogs.export.defaultxdpi",
437 "value", DPI_BASE),
438 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
439 NULL, _("dp_i"), 2, 1,
440 G_CALLBACK (sp_export_xdpi_value_changed),
441 dlg );
443 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
444 NULL, GTK_WIDGET(t->gobj()), 0, 1,
445 _("Height:"), _("pixels at"), 0, 1,
446 G_CALLBACK
447 (sp_export_bitmap_height_value_changed),
448 dlg );
450 /** \todo
451 * Needs fixing: there's no way to set ydpi currently, so we use
452 * the defaultxdpi value here, too...
453 */
454 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
455 ( "dialogs.export.defaultxdpi",
456 "value", DPI_BASE),
457 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
458 NULL, _("dpi"), 2, 0, NULL, dlg );
460 vb->pack_start(*size_box);
461 }
463 /* File entry */
464 {
465 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
466 file_box->set_border_width(3);
468 // true = has mnemonic
469 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
470 flabel->set_use_markup(true);
471 file_box->pack_start(*flabel, false, false, 0);
473 Gtk::Entry *fe = new Gtk::Entry();
475 /*
476 * set the default filename to be that of the current path + document
477 * with .png extension
478 *
479 * One thing to notice here is that this filename may get
480 * overwritten, but it won't happen here. The filename gets
481 * written into the text field, but then the button to select
482 * the area gets set. In that code the filename can be changed
483 * if there are some with presidence in the document. So, while
484 * this code sets the name first, it may not be the one users
485 * really see.
486 */
487 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
488 {
489 gchar *name;
490 SPDocument * doc = SP_ACTIVE_DOCUMENT;
491 const gchar *uri = SP_DOCUMENT_URI (doc);
492 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
493 const gchar * text_extension = repr->attribute("inkscape:output_extension");
494 Inkscape::Extension::Output * oextension = NULL;
496 if (text_extension != NULL) {
497 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
498 }
500 if (oextension != NULL) {
501 gchar * old_extension = oextension->get_extension();
502 if (g_str_has_suffix(uri, old_extension)) {
503 gchar * uri_copy;
504 gchar * extension_point;
505 gchar * final_name;
507 uri_copy = g_strdup(uri);
508 extension_point = g_strrstr(uri_copy, old_extension);
509 extension_point[0] = '\0';
511 final_name = g_strconcat(uri_copy, ".png", NULL);
512 fe->set_text(final_name);
514 g_free(final_name);
515 g_free(uri_copy);
516 }
517 } else {
518 name = g_strconcat(uri, ".png", NULL);
519 fe->set_text(name);
520 g_free(name);
521 }
523 doc_export_name = g_strdup(fe->get_text().c_str());
524 }
525 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
526 G_CALLBACK (sp_export_filename_modified), dlg);
528 hb = new Gtk::HBox(FALSE, 5);
530 {
531 // true = has mnemonic
532 Gtk::Button *b = new Gtk::Button();
534 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
535 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
536 Gtk::ICON_SIZE_BUTTON);
537 pixlabel->pack_start(*im);
539 Gtk::Label *l = new Gtk::Label();
540 l->set_markup_with_mnemonic(_("_Browse..."));
541 pixlabel->pack_start(*l);
543 b->add(*pixlabel);
545 hb->pack_end (*b, false, false, 4);
546 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
547 G_CALLBACK (sp_export_browse_clicked), NULL );
548 }
550 hb->pack_start (*fe, true, true, 0);
551 file_box->add(*hb);
552 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
553 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
554 original_name = g_strdup(fe->get_text().c_str());
555 // pressing enter in the filename field is the same as clicking export:
556 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
557 G_CALLBACK (sp_export_export_clicked), dlg );
558 // focus is in the filename initially:
559 fe->grab_focus();
561 // mnemonic in frame label moves focus to filename:
562 flabel->set_mnemonic_widget(*fe);
564 vb->pack_start(*file_box);
565 }
567 /* Buttons */
568 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
569 bb->set_border_width(3);
571 {
572 Gtk::Button *b = new Gtk::Button();
573 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
574 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
575 Gtk::ICON_SIZE_BUTTON);
576 image_label->pack_start(*im);
578 Gtk::Label *l = new Gtk::Label();
579 l->set_markup_with_mnemonic(_("_Export"));
580 image_label->pack_start(*l);
582 b->add(*image_label);
583 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
584 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
585 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
586 bb->pack_end(*b, false, false, 0);
587 }
589 vb->pack_end(*bb, false, false, 0);
590 vb->show_all();
592 } // end of if (!dlg)
594 sp_export_find_default_selection(dlg);
596 gtk_window_present ((GtkWindow *) dlg);
598 return;
599 } // end of sp_export_dialog()
601 static inline void
602 sp_export_find_default_selection(GtkWidget * dlg)
603 {
604 selection_type key = SELECTION_NUMBER_OF;
606 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
607 key = SELECTION_SELECTION;
608 }
610 /* Try using the preferences */
611 if (key == SELECTION_NUMBER_OF) {
612 const gchar *what = NULL;
613 int i = SELECTION_NUMBER_OF;
615 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
617 if (what != NULL) {
618 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
619 if (!strcmp (what, selection_names[i])) {
620 break;
621 }
622 }
623 }
625 key = (selection_type)i;
626 }
628 if (key == SELECTION_NUMBER_OF) {
629 key = SELECTION_SELECTION;
630 }
632 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
633 selection_names[key]);
634 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
636 return;
637 }
640 /**
641 * \brief If selection changed or a different document activated, we must
642 * recalculate any chosen areas
643 *
644 */
645 static void
646 sp_export_selection_changed ( Inkscape::Application *inkscape,
647 Inkscape::Selection *selection,
648 GtkObject *base )
649 {
650 selection_type current_key;
651 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
653 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
654 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
655 was_empty) {
656 gtk_toggle_button_set_active
657 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
658 TRUE );
659 }
660 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
662 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
664 if (inkscape &&
665 SP_IS_INKSCAPE (inkscape) &&
666 selection &&
667 SELECTION_CUSTOM != current_key) {
668 GtkToggleButton * button;
669 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
670 sp_export_area_toggled(button, base);
671 } // end of if()
673 return;
674 } // end of sp_export_selection_changed()
676 static void
677 sp_export_selection_modified ( Inkscape::Application *inkscape,
678 Inkscape::Selection *selection,
679 guint flags,
680 GtkObject *base )
681 {
682 selection_type current_key;
683 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
685 switch (current_key) {
686 case SELECTION_DRAWING:
687 if ( SP_ACTIVE_DESKTOP ) {
688 SPDocument *doc;
689 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
690 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
692 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
693 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
694 sp_export_set_area (base, bbox.min()[NR::X],
695 bbox.min()[NR::Y],
696 bbox.max()[NR::X],
697 bbox.max()[NR::Y]);
698 }
699 }
700 break;
701 case SELECTION_SELECTION:
702 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
703 NRRect bbox;
704 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
705 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
706 }
707 break;
708 default:
709 /* Do nothing for page or for custom */
710 break;
711 }
713 return;
714 }
716 /// Called when one of the selection buttons was toggled.
717 static void
718 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
719 {
720 if (gtk_object_get_data (base, "update"))
721 return;
723 selection_type key, old_key;
724 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
725 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
727 /* Ignore all "turned off" events unless we're the only active button */
728 if (!gtk_toggle_button_get_active (tb) ) {
730 /* Don't let the current selection be deactived - but rerun the
731 activate to allow the user to renew the values */
732 if (key == old_key) {
733 gtk_toggle_button_set_active ( tb, TRUE );
734 }
736 return;
737 }
739 /* Turn off the currently active button unless it's us */
740 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
742 if (old_key != key) {
743 gtk_toggle_button_set_active
744 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
745 FALSE );
746 }
748 if ( SP_ACTIVE_DESKTOP )
749 {
750 SPDocument *doc;
751 NR::Rect bbox;
752 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
754 /* Notice how the switch is used to 'fall through' here to get
755 various backups. If you modify this without noticing you'll
756 probabaly screw something up. */
757 switch (key) {
758 case SELECTION_SELECTION:
759 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
760 {
761 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
762 /* Only if there is a selection that we can set
763 do we break, otherwise we fall through to the
764 drawing */
765 // std::cout << "Using selection: SELECTION" << std::endl;
766 key = SELECTION_SELECTION;
767 break;
768 }
769 case SELECTION_DRAWING:
770 /** \todo
771 * This returns wrong values if the document has a viewBox.
772 */
773 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
775 /* If the drawing is valid, then we'll use it and break
776 otherwise we drop through to the page settings */
777 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
778 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
779 // std::cout << "Using selection: DRAWING" << std::endl;
780 key = SELECTION_DRAWING;
781 break;
782 }
783 case SELECTION_PAGE:
784 bbox = NR::Rect(NR::Point(0.0, 0.0),
785 NR::Point(sp_document_width(doc), sp_document_height(doc))
786 );
788 // std::cout << "Using selection: PAGE" << std::endl;
789 key = SELECTION_PAGE;
790 break;
791 case SELECTION_CUSTOM:
792 default:
793 break;
794 } // switch
796 // remember area setting
797 prefs_set_string_attribute ( "dialogs.export.exportarea",
798 "value", selection_names[key]);
800 if (key != SELECTION_CUSTOM) {
801 sp_export_set_area (base, bbox.min()[NR::X],
802 bbox.min()[NR::Y],
803 bbox.max()[NR::X],
804 bbox.max()[NR::Y]);
805 }
807 } // end of if ( SP_ACTIVE_DESKTOP )
810 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
811 GtkWidget * file_entry;
812 const gchar * filename = NULL;
813 float xdpi = 0.0, ydpi = 0.0;
815 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
817 switch (key) {
818 case SELECTION_PAGE:
819 case SELECTION_DRAWING: {
820 SPDocument * doc = SP_ACTIVE_DOCUMENT;
821 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
822 const gchar * dpi_string;
824 filename = repr->attribute("inkscape:export-filename");
826 dpi_string = NULL;
827 dpi_string = repr->attribute("inkscape:export-xdpi");
828 if (dpi_string != NULL) {
829 xdpi = atof(dpi_string);
830 }
832 dpi_string = NULL;
833 dpi_string = repr->attribute("inkscape:export-ydpi");
834 if (dpi_string != NULL) {
835 ydpi = atof(dpi_string);
836 }
838 if (filename == NULL) {
839 if (doc_export_name != NULL) {
840 filename = g_strdup(doc_export_name);
841 } else {
842 filename = g_strdup("");
843 }
844 }
845 break;
846 }
847 case SELECTION_SELECTION:
848 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
849 const GSList * reprlst;
850 bool filename_search = TRUE;
851 bool xdpi_search = TRUE;
852 bool ydpi_search = TRUE;
854 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
855 for(; reprlst != NULL &&
856 filename_search &&
857 xdpi_search &&
858 ydpi_search;
859 reprlst = reprlst->next) {
860 const gchar * dpi_string;
861 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
863 if (filename_search) {
864 filename = repr->attribute("inkscape:export-filename");
865 if (filename != NULL)
866 filename_search = FALSE;
867 }
869 if (xdpi_search) {
870 dpi_string = NULL;
871 dpi_string = repr->attribute("inkscape:export-xdpi");
872 if (dpi_string != NULL) {
873 xdpi = atof(dpi_string);
874 xdpi_search = FALSE;
875 }
876 }
878 if (ydpi_search) {
879 dpi_string = NULL;
880 dpi_string = repr->attribute("inkscape:export-ydpi");
881 if (dpi_string != NULL) {
882 ydpi = atof(dpi_string);
883 ydpi_search = FALSE;
884 }
885 }
886 }
888 /* If we still don't have a filename -- let's build
889 one that's nice */
890 if (filename == NULL) {
891 const gchar * id = NULL;
892 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
893 for(; reprlst != NULL; reprlst = reprlst->next) {
894 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
895 if (repr->attribute("id")) {
896 id = repr->attribute("id");
897 break;
898 }
899 }
900 if (id == NULL) /* This should never happen */
901 id = "bitmap";
903 gchar * directory = NULL;
904 const gchar * file_entry_text;
906 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
907 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
908 // std::cout << "Directory from dialog" << std::endl;
909 directory = g_dirname(file_entry_text);
910 }
912 if (directory == NULL) {
913 /* Grab document directory */
914 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
915 // std::cout << "Directory from document" << std::endl;
916 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
917 }
918 }
920 if (directory == NULL) {
921 // std::cout << "Home Directory" << std::endl;
922 directory = homedir_path(NULL);
923 }
925 gchar * id_ext = g_strconcat(id, ".png", NULL);
926 filename = g_build_filename(directory, id_ext, NULL);
927 g_free(directory);
928 g_free(id_ext);
929 }
930 }
931 break;
932 case SELECTION_CUSTOM:
933 default:
934 break;
935 }
937 if (filename != NULL) {
938 g_free(original_name);
939 original_name = g_strdup(filename);
940 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
941 }
943 if (xdpi != 0.0) {
944 sp_export_value_set(base, "xdpi", xdpi);
945 }
947 /* These can't be seperate, and setting x sets y, so for
948 now setting this is disabled. Hopefully it won't be in
949 the future */
950 if (FALSE && ydpi != 0.0) {
951 sp_export_value_set(base, "ydpi", ydpi);
952 }
953 }
955 return;
956 } // end of sp_export_area_toggled()
958 /// Called when dialog is deleted
959 static gint
960 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
961 {
962 g_object_set_data (base, "cancel", (gpointer) 1);
963 return TRUE;
964 } // end of sp_export_progress_delete()
966 /// Called when progress is cancelled
967 static void
968 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
969 {
970 g_object_set_data (base, "cancel", (gpointer) 1);
971 } // end of sp_export_progress_cancel()
973 /// Called for every progress iteration
974 static unsigned int
975 sp_export_progress_callback (float value, void *data)
976 {
977 GtkWidget *prg;
978 int evtcount;
980 if (g_object_get_data ((GObject *) data, "cancel"))
981 return FALSE;
983 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
984 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
986 evtcount = 0;
987 while ((evtcount < 16) && gdk_events_pending ()) {
988 gtk_main_iteration_do (FALSE);
989 evtcount += 1;
990 }
992 gtk_main_iteration_do (FALSE);
994 return TRUE;
996 } // end of sp_export_progress_callback()
998 /// Called when export button is clicked
999 static void
1000 sp_export_export_clicked (GtkButton *button, GtkObject *base)
1001 {
1002 if (!SP_ACTIVE_DESKTOP) return;
1004 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1005 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1007 float const x0 = sp_export_value_get_px(base, "x0");
1008 float const y0 = sp_export_value_get_px(base, "y0");
1009 float const x1 = sp_export_value_get_px(base, "x1");
1010 float const y1 = sp_export_value_get_px(base, "y1");
1011 float const xdpi = sp_export_value_get(base, "xdpi");
1012 float const ydpi = sp_export_value_get(base, "ydpi");
1013 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1014 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1016 if (filename == NULL || *filename == '\0') {
1017 sp_ui_error_dialog(_("You have to enter a filename"));
1018 return;
1019 }
1021 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1022 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1023 return;
1024 }
1026 gchar *dirname = g_dirname(filename);
1027 if ( dirname == NULL
1028 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1029 {
1030 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1031 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1032 safeDir);
1033 sp_ui_error_dialog(error);
1034 g_free(safeDir);
1035 g_free(error);
1036 g_free(dirname);
1037 return;
1038 }
1039 g_free(dirname);
1041 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1042 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1043 char *fn;
1044 gchar *text;
1046 dlg = gtk_dialog_new ();
1047 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1048 prg = gtk_progress_bar_new ();
1049 sp_transientize (dlg);
1050 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1051 g_object_set_data ((GObject *) base, "progress", prg);
1052 fn = g_path_get_basename (filename);
1053 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1054 fn, width, height);
1055 g_free (fn);
1056 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1057 g_free (text);
1058 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1059 GTK_PROGRESS_LEFT_TO_RIGHT);
1060 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1061 prg, FALSE, FALSE, 4 );
1062 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1063 GTK_STOCK_CANCEL,
1064 GTK_RESPONSE_CANCEL );
1066 g_signal_connect ( (GObject *) dlg, "delete_event",
1067 (GCallback) sp_export_progress_delete, base);
1068 g_signal_connect ( (GObject *) btn, "clicked",
1069 (GCallback) sp_export_progress_cancel, base);
1070 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1071 gtk_widget_show_all (dlg);
1073 /* Do export */
1074 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1075 x0, y0, x1, y1, width, height, xdpi, ydpi,
1076 nv->pagecolor,
1077 sp_export_progress_callback, base)) {
1078 gchar * error;
1079 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1080 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1081 sp_ui_error_dialog(error);
1082 g_free(safeFile);
1083 g_free(error);
1084 }
1086 /* Reset the filename so that it can be changed again by changing
1087 selections and all that */
1088 g_free(original_name);
1089 original_name = g_strdup(filename);
1090 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1092 gtk_widget_destroy (dlg);
1093 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1095 /* Setup the values in the document */
1096 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1097 case SELECTION_PAGE:
1098 case SELECTION_DRAWING: {
1099 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1100 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1101 bool modified = false;
1102 const gchar * temp_string;
1104 bool saved = sp_document_get_undo_sensitive(doc);
1105 sp_document_set_undo_sensitive(doc, false);
1107 temp_string = repr->attribute("inkscape:export-filename");
1108 if (temp_string == NULL || strcmp(temp_string, filename)) {
1109 repr->setAttribute("inkscape:export-filename", filename);
1110 modified = true;
1111 }
1112 temp_string = repr->attribute("inkscape:export-xdpi");
1113 if (temp_string == NULL || xdpi != atof(temp_string)) {
1114 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1115 modified = true;
1116 }
1117 temp_string = repr->attribute("inkscape:export-ydpi");
1118 if (temp_string == NULL || xdpi != atof(temp_string)) {
1119 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1120 modified = true;
1121 }
1123 if (modified)
1124 repr->setAttribute("sodipodi:modified", "TRUE");
1125 sp_document_set_undo_sensitive(doc, saved);
1126 break;
1127 }
1128 case SELECTION_SELECTION: {
1129 const GSList * reprlst;
1130 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1131 bool modified = false;
1133 bool saved = sp_document_get_undo_sensitive(doc);
1134 sp_document_set_undo_sensitive(doc, false);
1135 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1137 for(; reprlst != NULL; reprlst = reprlst->next) {
1138 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1139 const gchar * temp_string;
1141 if (repr->attribute("id") == NULL ||
1142 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1143 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1144 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1145 temp_string = repr->attribute("inkscape:export-filename");
1146 if (temp_string == NULL || strcmp(temp_string, filename)) {
1147 repr->setAttribute("inkscape:export-filename", filename);
1148 modified = true;
1149 }
1150 }
1151 temp_string = repr->attribute("inkscape:export-xdpi");
1152 if (temp_string == NULL || xdpi != atof(temp_string)) {
1153 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1154 modified = true;
1155 }
1156 temp_string = repr->attribute("inkscape:export-ydpi");
1157 if (temp_string == NULL || xdpi != atof(temp_string)) {
1158 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1159 modified = true;
1160 }
1161 }
1163 if (modified) {
1164 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1165 repr->setAttribute("sodipodi:modified", "TRUE");
1166 }
1168 sp_document_set_undo_sensitive(doc, saved);
1169 break;
1170 }
1171 default:
1172 break;
1173 }
1176 return;
1177 } // end of sp_export_export_clicked()
1179 /// Called when Browse button is clicked
1180 static void
1181 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1182 {
1183 GtkWidget *fs, *fe;
1184 const gchar *filename;
1186 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1187 NULL,
1188 GTK_FILE_CHOOSER_ACTION_SAVE,
1189 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1190 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1191 NULL );
1193 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1195 sp_transientize (fs);
1197 gtk_window_set_modal(GTK_WINDOW (fs), true);
1199 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1201 if (*filename == '\0') {
1202 filename = homedir_path(NULL);
1203 }
1205 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1207 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1208 {
1209 gchar *file;
1211 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1212 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1213 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1214 g_free(utf8file);
1216 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1217 g_free(file);
1218 }
1220 gtk_widget_destroy (fs);
1222 return;
1223 } // end of sp_export_browse_clicked()
1225 // TODO: Move this to nr-rect-fns.h.
1226 static bool
1227 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1228 {
1229 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1230 return (
1231 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1232 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1233 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1234 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1235 );
1236 }
1238 /**
1239 \brief This function is used to detect the current selection setting
1240 based on the values in the x0, y0, x1 and y0 fields.
1241 \param base The export dialog itself
1243 One of the most confusing parts of this function is why the array
1244 is built at the beginning. What needs to happen here is that we
1245 should always check the current selection to see if it is the valid
1246 one. While this is a performance improvement it is also a usability
1247 one during the cases where things like selections and drawings match
1248 size. This way buttons change less 'randomly' (atleast in the eyes
1249 of the user). To do this an array is built where the current selection
1250 type is placed first, and then the others in an order from smallest
1251 to largest (this can be configured by reshuffling \c test_order).
1253 All of the values in this function are rounded to two decimal places
1254 because that is what is shown to the user. While everything is kept
1255 more accurate than that, the user can't control more acurrate than
1256 that, so for this to work for them - it needs to check on that level
1257 of accuracy.
1259 \todo finish writing this up
1260 */
1261 static void
1262 sp_export_detect_size(GtkObject * base) {
1263 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1264 selection_type this_test[SELECTION_NUMBER_OF + 1];
1265 selection_type key = SELECTION_NUMBER_OF;
1267 NR::Point x(sp_export_value_get_px (base, "x0"),
1268 sp_export_value_get_px (base, "y0"));
1269 NR::Point y(sp_export_value_get_px (base, "x1"),
1270 sp_export_value_get_px (base, "y1"));
1271 NR::Rect current_bbox(x, y);
1272 //std::cout << "Current " << current_bbox;
1274 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1275 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1276 this_test[i + 1] = test_order[i];
1277 }
1279 for (int i = 0;
1280 i < SELECTION_NUMBER_OF + 1 &&
1281 key == SELECTION_NUMBER_OF &&
1282 SP_ACTIVE_DESKTOP != NULL;
1283 i++) {
1284 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1285 switch (this_test[i]) {
1286 case SELECTION_SELECTION:
1287 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1288 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1290 //std::cout << "Selection " << bbox;
1291 if (sp_export_bbox_equal(bbox,current_bbox)) {
1292 key = SELECTION_SELECTION;
1293 }
1294 }
1295 break;
1296 case SELECTION_DRAWING: {
1297 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1299 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1301 // std::cout << "Drawing " << bbox2;
1302 if (sp_export_bbox_equal(bbox,current_bbox)) {
1303 key = SELECTION_DRAWING;
1304 }
1305 break;
1306 }
1308 case SELECTION_PAGE: {
1309 SPDocument *doc;
1311 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1313 NR::Point x(0.0, 0.0);
1314 NR::Point y(sp_document_width(doc),
1315 sp_document_height(doc));
1316 NR::Rect bbox(x, y);
1318 // std::cout << "Page " << bbox;
1319 if (sp_export_bbox_equal(bbox,current_bbox)) {
1320 key = SELECTION_PAGE;
1321 }
1323 break;
1324 }
1325 default:
1326 break;
1327 }
1328 }
1329 // std::cout << std::endl;
1331 if (key == SELECTION_NUMBER_OF) {
1332 key = SELECTION_CUSTOM;
1333 }
1335 /* We're now using a custom size, not a fixed one */
1336 /* printf("Detecting state: %s\n", selection_names[key]); */
1337 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1338 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1339 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1340 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1342 return;
1343 } /* sp_export_detect_size */
1345 /// Called when area x0 value is changed
1346 static void
1347 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1348 {
1349 float x0, x1, xdpi, width;
1351 if (gtk_object_get_data (base, "update"))
1352 return;
1354 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1355 (base, "units")))
1356 {
1357 return;
1358 }
1360 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1362 x0 = sp_export_value_get_px (base, "x0");
1363 x1 = sp_export_value_get_px (base, "x1");
1364 xdpi = sp_export_value_get (base, "xdpi");
1366 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1368 if (width < SP_EXPORT_MIN_SIZE) {
1369 const gchar *key;
1370 width = SP_EXPORT_MIN_SIZE;
1371 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1373 if (!strcmp (key, "x0")) {
1374 x1 = x0 + width * DPI_BASE / xdpi;
1375 sp_export_value_set_px (base, "x1", x1);
1376 } else {
1377 x0 = x1 - width * DPI_BASE / xdpi;
1378 sp_export_value_set_px (base, "x0", x0);
1379 }
1380 }
1382 sp_export_value_set_px (base, "width", x1 - x0);
1383 sp_export_value_set (base, "bmwidth", width);
1385 sp_export_detect_size(base);
1387 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1389 return;
1390 } // end of sp_export_area_x_value_changed()
1392 /// Called when area y0 value is changed.
1393 static void
1394 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1395 {
1396 float y0, y1, ydpi, height;
1398 if (gtk_object_get_data (base, "update"))
1399 return;
1401 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1402 (base, "units")))
1403 {
1404 return;
1405 }
1407 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1409 y0 = sp_export_value_get_px (base, "y0");
1410 y1 = sp_export_value_get_px (base, "y1");
1411 ydpi = sp_export_value_get (base, "ydpi");
1413 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1415 if (height < SP_EXPORT_MIN_SIZE) {
1416 const gchar *key;
1417 height = SP_EXPORT_MIN_SIZE;
1418 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1419 if (!strcmp (key, "y0")) {
1420 y1 = y0 + height * DPI_BASE / ydpi;
1421 sp_export_value_set_px (base, "y1", y1);
1422 } else {
1423 y0 = y1 - height * DPI_BASE / ydpi;
1424 sp_export_value_set_px (base, "y0", y0);
1425 }
1426 }
1428 sp_export_value_set_px (base, "height", y1 - y0);
1429 sp_export_value_set (base, "bmheight", height);
1431 sp_export_detect_size(base);
1433 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1435 return;
1436 } // end of sp_export_area_y_value_changed()
1438 /// Called when x1-x0 or area width is changed
1439 static void
1440 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1441 {
1442 float x0, x1, xdpi, width, bmwidth;
1444 if (gtk_object_get_data (base, "update"))
1445 return;
1447 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1448 (base, "units"))) {
1449 return;
1450 }
1452 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1454 x0 = sp_export_value_get_px (base, "x0");
1455 x1 = sp_export_value_get_px (base, "x1");
1456 xdpi = sp_export_value_get (base, "xdpi");
1457 width = sp_export_value_get_px (base, "width");
1458 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1460 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1462 bmwidth = SP_EXPORT_MIN_SIZE;
1463 width = bmwidth * DPI_BASE / xdpi;
1464 sp_export_value_set_px (base, "width", width);
1465 }
1467 sp_export_value_set_px (base, "x1", x0 + width);
1468 sp_export_value_set (base, "bmwidth", bmwidth);
1470 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1472 return;
1473 } // end of sp_export_area_width_value_changed()
1475 /// Called when y1-y0 or area height is changed.
1476 static void
1477 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1478 {
1480 float y0, y1, ydpi, height, bmheight;
1482 if (gtk_object_get_data (base, "update"))
1483 return;
1485 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1486 (base, "units"))) {
1487 return;
1488 }
1490 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1492 y0 = sp_export_value_get_px (base, "y0");
1493 y1 = sp_export_value_get_px (base, "y1");
1494 ydpi = sp_export_value_get (base, "ydpi");
1495 height = sp_export_value_get_px (base, "height");
1496 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1498 if (bmheight < SP_EXPORT_MIN_SIZE) {
1499 bmheight = SP_EXPORT_MIN_SIZE;
1500 height = bmheight * DPI_BASE / ydpi;
1501 sp_export_value_set_px (base, "height", height);
1502 }
1504 sp_export_value_set_px (base, "y1", y0 + height);
1505 sp_export_value_set (base, "bmheight", bmheight);
1507 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1509 return;
1510 } // end of sp_export_area_height_value_changed()
1512 /**
1513 \brief A function to set the ydpi
1514 \param base The export dialog
1516 This function grabs all of the y values and then figures out the
1517 new bitmap size based on the changing dpi value. The dpi value is
1518 gotten from the xdpi setting as these can not currently be independent.
1519 */
1520 static void
1521 sp_export_set_image_y (GtkObject *base)
1522 {
1523 float y0, y1, xdpi;
1525 y0 = sp_export_value_get_px (base, "y0");
1526 y1 = sp_export_value_get_px (base, "y1");
1527 xdpi = sp_export_value_get (base, "xdpi");
1529 sp_export_value_set (base, "ydpi", xdpi);
1530 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1532 return;
1533 } // end of sp_export_set_image_y()
1535 /**
1536 \brief A function to set the xdpi
1537 \param base The export dialog
1539 This function grabs all of the x values and then figures out the
1540 new bitmap size based on the changing dpi value. The dpi value is
1541 gotten from the xdpi setting as these can not currently be independent.
1542 */
1543 static void
1544 sp_export_set_image_x (GtkObject *base)
1545 {
1546 float x0, x1, xdpi;
1548 x0 = sp_export_value_get_px (base, "x0");
1549 x1 = sp_export_value_get_px (base, "x1");
1550 xdpi = sp_export_value_get (base, "xdpi");
1552 sp_export_value_set (base, "ydpi", xdpi);
1553 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1555 return;
1556 } // end of sp_export_set_image_x()
1558 /// Called when pixel width is changed
1559 static void
1560 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1561 {
1562 float x0, x1, bmwidth, xdpi;
1564 if (gtk_object_get_data (base, "update"))
1565 return;
1567 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1568 (base, "units"))) {
1569 return;
1570 }
1572 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1574 x0 = sp_export_value_get_px (base, "x0");
1575 x1 = sp_export_value_get_px (base, "x1");
1576 bmwidth = sp_export_value_get (base, "bmwidth");
1578 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1579 bmwidth = SP_EXPORT_MIN_SIZE;
1580 sp_export_value_set (base, "bmwidth", bmwidth);
1581 }
1583 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1584 sp_export_value_set (base, "xdpi", xdpi);
1586 sp_export_set_image_y (base);
1588 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1590 return;
1591 } // end of sp_export_bitmap_width_value_changed()
1593 /// Called when pixel height is changed
1594 static void
1595 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1596 {
1597 float y0, y1, bmheight, xdpi;
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 bmheight = sp_export_value_get (base, "bmheight");
1613 if (bmheight < SP_EXPORT_MIN_SIZE) {
1614 bmheight = SP_EXPORT_MIN_SIZE;
1615 sp_export_value_set (base, "bmheight", bmheight);
1616 }
1618 xdpi = bmheight * DPI_BASE / (y1 - y0);
1619 sp_export_value_set (base, "xdpi", xdpi);
1621 sp_export_set_image_x (base);
1623 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1625 return;
1626 } // end of sp_export_bitmap_width_value_changed()
1628 /**
1629 \brief A function to adjust the bitmap width when the xdpi value changes
1630 \param adj The adjustment that was changed
1631 \param base The export dialog itself
1633 The first thing this function checks is to see if we are doing an
1634 update. If we are, this function just returns because there is another
1635 instance of it that will handle everything for us. If there is a
1636 units change, we also assume that everyone is being updated appropriately
1637 and there is nothing for us to do.
1639 If we're the highest level function, we set the update flag, and
1640 continue on our way.
1642 All of the values are grabbed using the \c sp_export_value_get functions
1643 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1644 xdpi value is saved in the preferences for the next time the dialog
1645 is opened. (does the selection dpi need to be set here?)
1647 A check is done to to ensure that we aren't outputing an invalid width,
1648 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1649 changed to make it valid.
1651 After all of this the bitmap width is changed.
1653 We also change the ydpi. This is a temporary hack as these can not
1654 currently be independent. This is likely to change in the future.
1655 */
1656 void
1657 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1658 {
1659 float x0, x1, xdpi, bmwidth;
1661 if (gtk_object_get_data (base, "update"))
1662 return;
1664 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1665 (base, "units"))) {
1666 return;
1667 }
1669 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1671 x0 = sp_export_value_get_px (base, "x0");
1672 x1 = sp_export_value_get_px (base, "x1");
1673 xdpi = sp_export_value_get (base, "xdpi");
1675 // remember xdpi setting
1676 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1678 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1680 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1681 bmwidth = SP_EXPORT_MIN_SIZE;
1682 if (x1 != x0)
1683 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1684 else
1685 xdpi = DPI_BASE;
1686 sp_export_value_set (base, "xdpi", xdpi);
1687 }
1689 sp_export_value_set (base, "bmwidth", bmwidth);
1691 sp_export_set_image_y (base);
1693 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1695 return;
1696 } // end of sp_export_xdpi_value_changed()
1699 /**
1700 \brief A function to change the area that is used for the exported
1701 bitmap.
1702 \param base This is the export dialog
1703 \param x0 Horizontal upper left hand corner of the picture in points
1704 \param y0 Vertical upper left hand corner of the picture in points
1705 \param x1 Horizontal lower right hand corner of the picture in points
1706 \param y1 Vertical lower right hand corner of the picture in points
1708 This function just calls \c sp_export_value_set_px for each of the
1709 parameters that is passed in. This allows for setting them all in
1710 one convient area.
1712 Update is set to suspend all of the other test running while all the
1713 values are being set up. This allows for a performance increase, but
1714 it also means that the wrong type won't be detected with only some of
1715 the values set. After all the values are set everyone is told that
1716 there has been an update.
1717 */
1718 static void
1719 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1720 {
1721 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1722 sp_export_value_set_px (base, "x1", x1);
1723 sp_export_value_set_px (base, "y1", y1);
1724 sp_export_value_set_px (base, "x0", x0);
1725 sp_export_value_set_px (base, "y0", y0);
1726 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1728 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1729 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1731 return;
1732 }
1734 /**
1735 \brief Sets the value of an adjustment
1736 \param base The export dialog
1737 \param key Which adjustment to set
1738 \param val What value to set it to
1740 This function finds the adjustment using the data stored in the
1741 export dialog. After finding the adjustment it then sets
1742 the value of it.
1743 */
1744 static void
1745 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1746 {
1747 GtkAdjustment *adj;
1749 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1751 gtk_adjustment_set_value (adj, val);
1752 }
1754 /**
1755 \brief A function to set a value using the units points
1756 \param base The export dialog
1757 \param key Which value should be set
1758 \param val What the value should be in points
1760 This function first gets the adjustment for the key that is passed
1761 in. It then figures out what units are currently being used in the
1762 dialog. After doing all of that, it then converts the incoming
1763 value and sets the adjustment.
1764 */
1765 static void
1766 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1767 {
1768 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1770 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1772 return;
1773 }
1775 /**
1776 \brief Get the value of an adjustment in the export dialog
1777 \param base The export dialog
1778 \param key Which adjustment is being looked for
1779 \return The value in the specified adjustment
1781 This function gets the adjustment from the data field in the export
1782 dialog. It then grabs the value from the adjustment.
1783 */
1784 static float
1785 sp_export_value_get ( GtkObject *base, const gchar *key )
1786 {
1787 GtkAdjustment *adj;
1789 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1791 return adj->value;
1792 }
1794 /**
1795 \brief Grabs a value in the export dialog and converts the unit
1796 to points
1797 \param base The export dialog
1798 \param key Which value should be returned
1799 \return The value in the adjustment in points
1801 This function, at its most basic, is a call to \c sp_export_value_get
1802 to get the value of the adjustment. It then finds the units that
1803 are being used by looking at the "units" attribute of the export
1804 dialog. Using that it converts the returned value into points.
1805 */
1806 static float
1807 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1808 {
1809 float value = sp_export_value_get(base, key);
1810 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1812 return sp_units_get_pixels (value, *unit);
1813 } // end of sp_export_value_get_px()
1815 /**
1816 \brief This function is called when the filename is changed by
1817 anyone. It resets the virgin bit.
1818 \param object Text entry box
1819 \param data The export dialog
1820 \return None
1822 This function gets called when the text area is modified. It is
1823 looking for the case where the text area is modified from its
1824 original value. In that case it sets the "filename-modified" bit
1825 to TRUE. If the text dialog returns back to the original text, the
1826 bit gets reset. This should stop simple mistakes.
1827 */
1828 static void
1829 sp_export_filename_modified (GtkObject * object, gpointer data)
1830 {
1831 GtkWidget * text_entry = (GtkWidget *)object;
1832 GtkWidget * export_dialog = (GtkWidget *)data;
1834 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1835 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1836 // printf("Modified: FALSE\n");
1837 } else {
1838 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1839 // printf("Modified: TRUE\n");
1840 }
1842 return;
1843 } // end sp_export_filename_modified
1845 /*
1846 Local Variables:
1847 mode:c++
1848 c-file-style:"stroustrup"
1849 c-file-offsets:((innamespace . 0)(inline-open . 0))
1850 indent-tabs-mode:nil
1851 fill-column:99
1852 End:
1853 */
1854 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :