3b1943c4ff23234f0a02c42227a1b667f57e8f11
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"
58 #define SP_EXPORT_MIN_SIZE 1.0
60 #define DPI_BASE PX_PER_IN
62 #define EXPORT_COORD_PRECISION 3
64 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
65 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
66 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
68 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
69 GtkObject *base);
71 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
72 GtkObject *base);
74 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
75 GtkObject *base);
77 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
78 GtkObject *base);
80 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
81 GtkObject *base);
83 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
84 GtkObject *base);
86 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
87 GtkObject *base);
89 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
90 Inkscape::Selection *selection,
91 GtkObject *base);
92 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
93 Inkscape::Selection *selection,
94 guint flags,
95 GtkObject *base );
97 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
98 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
99 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
100 static float sp_export_value_get ( GtkObject *base, const gchar *key );
101 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
103 static void sp_export_filename_modified (GtkObject * object, gpointer data);
104 static inline void sp_export_find_default_selection(GtkWidget * dlg);
105 static void sp_export_detect_size(GtkObject * base);
107 static const gchar *prefs_path = "dialogs.export";
109 // these all need to be reinitialized to their defaults during dialog_destroy
110 static GtkWidget *dlg = NULL;
111 static win_data wd;
112 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
113 static gchar * original_name = NULL;
114 static gchar * doc_export_name = NULL;
115 static bool was_empty = TRUE;
117 /** What type of button is being pressed */
118 enum selection_type {
119 SELECTION_PAGE = 0, /**< Export the whole page */
120 SELECTION_DRAWING, /**< Export everything drawn on the page */
121 SELECTION_SELECTION, /**< Export everything that is selected */
122 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
123 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
124 };
126 /** A list of strings that is used both in the preferences, and in the
127 data fields to describe the various values of \c selection_type. */
128 static const char * selection_names[SELECTION_NUMBER_OF] = {
129 "page", "drawing", "selection", "custom"};
131 /** The names on the buttons for the various selection types. */
132 static const char * selection_labels[SELECTION_NUMBER_OF] = {
133 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
135 static void
136 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
137 {
138 sp_signal_disconnect_by_data (INKSCAPE, dlg);
140 wd.win = dlg = NULL;
141 wd.stop = 0;
142 x = -1000; y = -1000; w = 0; h = 0;
143 g_free(original_name);
144 original_name = NULL;
145 g_free(doc_export_name);
146 doc_export_name = NULL;
147 was_empty = TRUE;
149 return;
150 } // end of sp_export_dialog_destroy()
152 /// Called when dialog is closed or inkscape is shut down.
153 static bool
154 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
155 {
157 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
158 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
160 if (x<0) x=0;
161 if (y<0) y=0;
163 prefs_set_int_attribute (prefs_path, "x", x);
164 prefs_set_int_attribute (prefs_path, "y", y);
165 prefs_set_int_attribute (prefs_path, "w", w);
166 prefs_set_int_attribute (prefs_path, "h", h);
168 return FALSE; // which means, go ahead and destroy it
170 } // end of sp_export_dialog_delete()
172 /**
173 \brief Creates a new spin button for the export dialog
174 \param key The name of the spin button
175 \param val A default value for the spin button
176 \param min Minimum value for the spin button
177 \param max Maximum value for the spin button
178 \param step The step size for the spin button
179 \param page Size of the page increment
180 \param us Unit selector that effects this spin button
181 \param t Table to put the spin button in
182 \param x X location in the table \c t to start with
183 \param y Y location in the table \c t to start with
184 \param ll Text to put on the left side of the spin button (optional)
185 \param lr Text to put on the right side of the spin button (optional)
186 \param digits Number of digits to display after the decimal
187 \param sensitive Whether the spin button is sensitive or not
188 \param cb Callback for when this spin button is changed (optional)
189 \param dlg Export dialog the spin button is being placed in
191 */
192 static void
193 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
194 float step, float page, GtkWidget *us,
195 GtkWidget *t, int x, int y,
196 const gchar *ll, const gchar *lr,
197 int digits, unsigned int sensitive,
198 GCallback cb, GtkWidget *dlg )
199 {
200 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
201 gtk_object_set_data (a, "key", key);
202 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
204 if (us) {
205 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
206 GTK_ADJUSTMENT (a) );
207 }
209 int pos = 0;
211 GtkWidget *l = NULL;
213 if (ll) {
215 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
216 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
217 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
218 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
219 gtk_widget_set_sensitive (l, sensitive);
220 pos += 1;
222 }
224 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
225 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
226 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
227 gtk_widget_set_size_request (sb, 80, -1);
228 gtk_widget_set_sensitive (sb, sensitive);
229 pos += 1;
231 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
233 if (lr) {
235 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
236 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
237 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
238 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
239 gtk_widget_set_sensitive (l, sensitive);
240 pos += 1;
242 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
243 }
245 if (cb)
246 gtk_signal_connect (a, "value_changed", cb, dlg);
248 return;
249 } // end of sp_export_spinbutton_new()
252 static Gtk::VBox *
253 sp_export_dialog_area_box (GtkWidget * dlg)
254 {
255 Gtk::VBox* vb = new Gtk::VBox(false, 3);
257 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
258 lbl->set_use_markup(true);
259 vb->pack_start(*lbl);
261 /* Units box */
262 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
263 /* gets added to the vbox later, but the unit selector is needed
264 earlier than that */
266 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
267 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
268 if (desktop)
269 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
270 unitbox->pack_end(*us, false, false, 0);
271 Gtk::Label* l = new Gtk::Label(_("Units:"));
272 unitbox->pack_end(*l, false, false, 3);
273 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
275 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
277 Gtk::ToggleButton* b;
278 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
279 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
280 b->set_data("key", GINT_TO_POINTER(i));
281 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
282 togglebox->pack_start(*b, false, true, 0);
283 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
284 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
285 }
287 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
288 G_CALLBACK (sp_export_selection_changed), dlg );
289 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
290 G_CALLBACK (sp_export_selection_modified), dlg );
291 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
292 G_CALLBACK (sp_export_selection_changed), dlg );
294 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
295 t->set_row_spacings (4);
296 t->set_col_spacings (4);
298 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
299 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
300 G_CALLBACK ( sp_export_area_x_value_changed),
301 dlg );
303 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
304 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
305 G_CALLBACK (sp_export_area_x_value_changed),
306 dlg );
308 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
309 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
310 G_CALLBACK
311 (sp_export_area_width_value_changed),
312 dlg );
314 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
315 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
316 G_CALLBACK (sp_export_area_y_value_changed),
317 dlg );
319 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
320 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
321 G_CALLBACK (sp_export_area_y_value_changed),
322 dlg );
324 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
325 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
326 G_CALLBACK (sp_export_area_height_value_changed),
327 dlg );
329 vb->pack_start(*togglebox, false, false, 3);
330 vb->pack_start(*t, false, false, 0);
331 vb->pack_start(*unitbox, false, false, 0);
333 return vb;
334 } // end of sp_export_dialog_area_box
337 void
338 sp_export_dialog (void)
339 {
340 if (!dlg) {
341 Gtk::VBox* vb;
342 Gtk::HBox* hb;
344 gchar title[500];
345 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
347 dlg = sp_window_new (title, TRUE);
349 if (x == -1000 || y == -1000) {
350 x = prefs_get_int_attribute (prefs_path, "x", 0);
351 y = prefs_get_int_attribute (prefs_path, "y", 0);
352 }
354 if (w ==0 || h == 0) {
355 w = prefs_get_int_attribute (prefs_path, "w", 0);
356 h = prefs_get_int_attribute (prefs_path, "h", 0);
357 }
359 if (x<0) x=0;
360 if (y<0) y=0;
362 if (x != 0 || y != 0) {
363 gtk_window_move ((GtkWindow *) dlg, x, y);
364 } else {
365 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
366 }
368 if (w && h)
369 gtk_window_resize ((GtkWindow *) dlg, w, h);
371 sp_transientize (dlg);
372 wd.win = dlg;
373 wd.stop = 0;
375 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
376 G_CALLBACK (sp_transientize_callback), &wd);
378 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
379 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
381 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
382 G_CALLBACK (sp_export_dialog_destroy), dlg);
384 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
385 G_CALLBACK (sp_export_dialog_delete), dlg);
387 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
388 G_CALLBACK (sp_export_dialog_delete), dlg);
390 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
391 G_CALLBACK (sp_dialog_hide), dlg);
393 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
394 G_CALLBACK (sp_dialog_unhide), dlg);
396 GtkTooltips *tt = gtk_tooltips_new();
398 vb = new Gtk::VBox(false, 3);
399 vb->set_border_width(3);
400 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
402 /* Export area frame */
403 {
404 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
405 area_box->set_border_width(3);
406 vb->pack_start(*area_box, false, false, 0);
407 }
409 /* Bitmap size frame */
410 {
411 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
412 size_box->set_border_width(3);
414 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
415 lbl->set_use_markup(true);
416 size_box->pack_start(*lbl, false, false, 0);
417 const int rows = 2;
418 const int cols = 5;
419 const bool homogeneous = false;
420 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
421 t->set_row_spacings (4);
422 t->set_col_spacings (4);
423 size_box->pack_start(*t);
425 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
426 NULL, GTK_WIDGET(t->gobj()), 0, 0,
427 _("_Width:"), _("pixels at"), 0, 1,
428 G_CALLBACK
429 (sp_export_bitmap_width_value_changed),
430 dlg );
432 sp_export_spinbutton_new ( "xdpi",
433 prefs_get_double_attribute
434 ( "dialogs.export.defaultxdpi",
435 "value", DPI_BASE),
436 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
437 NULL, _("dp_i"), 2, 1,
438 G_CALLBACK (sp_export_xdpi_value_changed),
439 dlg );
441 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
442 NULL, GTK_WIDGET(t->gobj()), 0, 1,
443 _("Height:"), _("pixels at"), 0, 1,
444 G_CALLBACK
445 (sp_export_bitmap_height_value_changed),
446 dlg );
448 /** \todo
449 * Needs fixing: there's no way to set ydpi currently, so we use
450 * the defaultxdpi value here, too...
451 */
452 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
453 ( "dialogs.export.defaultxdpi",
454 "value", DPI_BASE),
455 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
456 NULL, _("dpi"), 2, 0, NULL, dlg );
458 vb->pack_start(*size_box);
459 }
461 /* File entry */
462 {
463 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
464 file_box->set_border_width(3);
466 // true = has mnemonic
467 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
468 flabel->set_use_markup(true);
469 file_box->pack_start(*flabel, false, false, 0);
471 Gtk::Entry *fe = new Gtk::Entry();
473 /*
474 * set the default filename to be that of the current path + document
475 * with .png extension
476 *
477 * One thing to notice here is that this filename may get
478 * overwritten, but it won't happen here. The filename gets
479 * written into the text field, but then the button to select
480 * the area gets set. In that code the filename can be changed
481 * if there are some with presidence in the document. So, while
482 * this code sets the name first, it may not be the one users
483 * really see.
484 */
485 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
486 {
487 gchar *name;
488 SPDocument * doc = SP_ACTIVE_DOCUMENT;
489 const gchar *uri = SP_DOCUMENT_URI (doc);
490 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
491 const gchar * text_extension = repr->attribute("inkscape:output_extension");
492 Inkscape::Extension::Output * oextension = NULL;
494 if (text_extension != NULL) {
495 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
496 }
498 if (oextension != NULL) {
499 gchar * old_extension = oextension->get_extension();
500 if (g_str_has_suffix(uri, old_extension)) {
501 gchar * uri_copy;
502 gchar * extension_point;
503 gchar * final_name;
505 uri_copy = g_strdup(uri);
506 extension_point = g_strrstr(uri_copy, old_extension);
507 extension_point[0] = '\0';
509 final_name = g_strconcat(uri_copy, ".png", NULL);
510 fe->set_text(final_name);
512 g_free(final_name);
513 g_free(uri_copy);
514 }
515 } else {
516 name = g_strconcat(uri, ".png", NULL);
517 fe->set_text(name);
518 g_free(name);
519 }
521 doc_export_name = g_strdup(fe->get_text().c_str());
522 }
523 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
524 G_CALLBACK (sp_export_filename_modified), dlg);
526 hb = new Gtk::HBox(FALSE, 5);
528 {
529 // true = has mnemonic
530 Gtk::Button *b = new Gtk::Button();
532 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
533 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
534 Gtk::ICON_SIZE_BUTTON);
535 pixlabel->pack_start(*im);
537 Gtk::Label *l = new Gtk::Label();
538 l->set_markup_with_mnemonic(_("_Browse..."));
539 pixlabel->pack_start(*l);
541 b->add(*pixlabel);
543 hb->pack_end (*b, false, false, 4);
544 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
545 G_CALLBACK (sp_export_browse_clicked), NULL );
546 }
548 hb->pack_start (*fe, true, true, 0);
549 file_box->add(*hb);
550 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
551 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
552 original_name = g_strdup(fe->get_text().c_str());
553 // pressing enter in the filename field is the same as clicking export:
554 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
555 G_CALLBACK (sp_export_export_clicked), dlg );
556 // focus is in the filename initially:
557 fe->grab_focus();
559 // mnemonic in frame label moves focus to filename:
560 flabel->set_mnemonic_widget(*fe);
562 vb->pack_start(*file_box);
563 }
565 /* Buttons */
566 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
567 bb->set_border_width(3);
569 {
570 Gtk::Button *b = new Gtk::Button();
571 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
572 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
573 Gtk::ICON_SIZE_BUTTON);
574 image_label->pack_start(*im);
576 Gtk::Label *l = new Gtk::Label();
577 l->set_markup_with_mnemonic(_("_Export"));
578 image_label->pack_start(*l);
580 b->add(*image_label);
581 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
582 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
583 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
584 bb->pack_end(*b, false, false, 0);
585 }
587 vb->pack_end(*bb, false, false, 0);
588 vb->show_all();
590 } // end of if (!dlg)
592 sp_export_find_default_selection(dlg);
594 gtk_window_present ((GtkWindow *) dlg);
596 return;
597 } // end of sp_export_dialog()
599 static inline void
600 sp_export_find_default_selection(GtkWidget * dlg)
601 {
602 selection_type key = SELECTION_NUMBER_OF;
604 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
605 key = SELECTION_SELECTION;
606 }
608 /* Try using the preferences */
609 if (key == SELECTION_NUMBER_OF) {
610 const gchar *what = NULL;
611 int i = SELECTION_NUMBER_OF;
613 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
615 if (what != NULL) {
616 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
617 if (!strcmp (what, selection_names[i])) {
618 break;
619 }
620 }
621 }
623 key = (selection_type)i;
624 }
626 if (key == SELECTION_NUMBER_OF) {
627 key = SELECTION_SELECTION;
628 }
630 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
631 selection_names[key]);
632 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
634 return;
635 }
638 /**
639 * \brief If selection changed or a different document activated, we must
640 * recalculate any chosen areas
641 *
642 */
643 static void
644 sp_export_selection_changed ( Inkscape::Application *inkscape,
645 Inkscape::Selection *selection,
646 GtkObject *base )
647 {
648 selection_type current_key;
649 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
651 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
652 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
653 was_empty) {
654 gtk_toggle_button_set_active
655 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
656 TRUE );
657 }
658 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
660 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
662 if (inkscape &&
663 SP_IS_INKSCAPE (inkscape) &&
664 selection &&
665 SELECTION_CUSTOM != current_key) {
666 GtkToggleButton * button;
667 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
668 sp_export_area_toggled(button, base);
669 } // end of if()
671 return;
672 } // end of sp_export_selection_changed()
674 static void
675 sp_export_selection_modified ( Inkscape::Application *inkscape,
676 Inkscape::Selection *selection,
677 guint flags,
678 GtkObject *base )
679 {
680 selection_type current_key;
681 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
683 switch (current_key) {
684 case SELECTION_DRAWING:
685 if ( SP_ACTIVE_DESKTOP ) {
686 SPDocument *doc;
687 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
688 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
690 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
691 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
692 sp_export_set_area (base, bbox.min()[NR::X],
693 bbox.min()[NR::Y],
694 bbox.max()[NR::X],
695 bbox.max()[NR::Y]);
696 }
697 }
698 break;
699 case SELECTION_SELECTION:
700 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
701 NRRect bbox;
702 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
703 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
704 }
705 break;
706 default:
707 /* Do nothing for page or for custom */
708 break;
709 }
711 return;
712 }
714 /// Called when one of the selection buttons was toggled.
715 static void
716 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
717 {
718 if (gtk_object_get_data (base, "update"))
719 return;
721 selection_type key, old_key;
722 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
723 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
725 /* Ignore all "turned off" events unless we're the only active button */
726 if (!gtk_toggle_button_get_active (tb) ) {
728 /* Don't let the current selection be deactived - but rerun the
729 activate to allow the user to renew the values */
730 if (key == old_key) {
731 gtk_toggle_button_set_active ( tb, TRUE );
732 }
734 return;
735 }
737 /* Turn off the currently active button unless it's us */
738 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
740 if (old_key != key) {
741 gtk_toggle_button_set_active
742 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
743 FALSE );
744 }
746 if ( SP_ACTIVE_DESKTOP )
747 {
748 SPDocument *doc;
749 NR::Rect bbox;
750 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
752 /* Notice how the switch is used to 'fall through' here to get
753 various backups. If you modify this without noticing you'll
754 probabaly screw something up. */
755 switch (key) {
756 case SELECTION_SELECTION:
757 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
758 {
759 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
760 /* Only if there is a selection that we can set
761 do we break, otherwise we fall through to the
762 drawing */
763 // std::cout << "Using selection: SELECTION" << std::endl;
764 key = SELECTION_SELECTION;
765 break;
766 }
767 case SELECTION_DRAWING:
768 /** \todo
769 * This returns wrong values if the document has a viewBox.
770 */
771 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
773 /* If the drawing is valid, then we'll use it and break
774 otherwise we drop through to the page settings */
775 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
776 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
777 // std::cout << "Using selection: DRAWING" << std::endl;
778 key = SELECTION_DRAWING;
779 break;
780 }
781 case SELECTION_PAGE:
782 bbox = NR::Rect(NR::Point(0.0, 0.0),
783 NR::Point(sp_document_width(doc), sp_document_height(doc))
784 );
786 // std::cout << "Using selection: PAGE" << std::endl;
787 key = SELECTION_PAGE;
788 break;
789 case SELECTION_CUSTOM:
790 default:
791 break;
792 } // switch
794 // remember area setting
795 prefs_set_string_attribute ( "dialogs.export.exportarea",
796 "value", selection_names[key]);
798 if (key != SELECTION_CUSTOM) {
799 sp_export_set_area (base, bbox.min()[NR::X],
800 bbox.min()[NR::Y],
801 bbox.max()[NR::X],
802 bbox.max()[NR::Y]);
803 }
805 } // end of if ( SP_ACTIVE_DESKTOP )
808 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
809 GtkWidget * file_entry;
810 const gchar * filename = NULL;
811 float xdpi = 0.0, ydpi = 0.0;
813 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
815 switch (key) {
816 case SELECTION_PAGE:
817 case SELECTION_DRAWING: {
818 SPDocument * doc = SP_ACTIVE_DOCUMENT;
819 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
820 const gchar * dpi_string;
822 filename = repr->attribute("inkscape:export-filename");
824 dpi_string = NULL;
825 dpi_string = repr->attribute("inkscape:export-xdpi");
826 if (dpi_string != NULL) {
827 xdpi = atof(dpi_string);
828 }
830 dpi_string = NULL;
831 dpi_string = repr->attribute("inkscape:export-ydpi");
832 if (dpi_string != NULL) {
833 ydpi = atof(dpi_string);
834 }
836 if (filename == NULL) {
837 if (doc_export_name != NULL) {
838 filename = g_strdup(doc_export_name);
839 } else {
840 filename = g_strdup("");
841 }
842 }
843 break;
844 }
845 case SELECTION_SELECTION:
846 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
847 const GSList * reprlst;
848 bool filename_search = TRUE;
849 bool xdpi_search = TRUE;
850 bool ydpi_search = TRUE;
852 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
853 for(; reprlst != NULL &&
854 filename_search &&
855 xdpi_search &&
856 ydpi_search;
857 reprlst = reprlst->next) {
858 const gchar * dpi_string;
859 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
861 if (filename_search) {
862 filename = repr->attribute("inkscape:export-filename");
863 if (filename != NULL)
864 filename_search = FALSE;
865 }
867 if (xdpi_search) {
868 dpi_string = NULL;
869 dpi_string = repr->attribute("inkscape:export-xdpi");
870 if (dpi_string != NULL) {
871 xdpi = atof(dpi_string);
872 xdpi_search = FALSE;
873 }
874 }
876 if (ydpi_search) {
877 dpi_string = NULL;
878 dpi_string = repr->attribute("inkscape:export-ydpi");
879 if (dpi_string != NULL) {
880 ydpi = atof(dpi_string);
881 ydpi_search = FALSE;
882 }
883 }
884 }
886 /* If we still don't have a filename -- let's build
887 one that's nice */
888 if (filename == NULL) {
889 const gchar * id = NULL;
890 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
891 for(; reprlst != NULL; reprlst = reprlst->next) {
892 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
893 if (repr->attribute("id")) {
894 id = repr->attribute("id");
895 break;
896 }
897 }
898 if (id == NULL) /* This should never happen */
899 id = "bitmap";
901 gchar * directory = NULL;
902 const gchar * file_entry_text;
904 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
905 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
906 // std::cout << "Directory from dialog" << std::endl;
907 directory = g_dirname(file_entry_text);
908 }
910 if (directory == NULL) {
911 /* Grab document directory */
912 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
913 // std::cout << "Directory from document" << std::endl;
914 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
915 }
916 }
918 if (directory == NULL) {
919 // std::cout << "Home Directory" << std::endl;
920 directory = homedir_path(NULL);
921 }
923 gchar * id_ext = g_strconcat(id, ".png", NULL);
924 filename = g_build_filename(directory, id_ext, NULL);
925 g_free(directory);
926 g_free(id_ext);
927 }
928 }
929 break;
930 case SELECTION_CUSTOM:
931 default:
932 break;
933 }
935 if (filename != NULL) {
936 g_free(original_name);
937 original_name = g_strdup(filename);
938 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
939 }
941 if (xdpi != 0.0) {
942 sp_export_value_set(base, "xdpi", xdpi);
943 }
945 /* These can't be seperate, and setting x sets y, so for
946 now setting this is disabled. Hopefully it won't be in
947 the future */
948 if (FALSE && ydpi != 0.0) {
949 sp_export_value_set(base, "ydpi", ydpi);
950 }
951 }
953 return;
954 } // end of sp_export_area_toggled()
956 /// Called when dialog is deleted
957 static gint
958 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
959 {
960 g_object_set_data (base, "cancel", (gpointer) 1);
961 return TRUE;
962 } // end of sp_export_progress_delete()
964 /// Called when progress is cancelled
965 static void
966 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
967 {
968 g_object_set_data (base, "cancel", (gpointer) 1);
969 } // end of sp_export_progress_cancel()
971 /// Called for every progress iteration
972 static unsigned int
973 sp_export_progress_callback (float value, void *data)
974 {
975 GtkWidget *prg;
976 int evtcount;
978 if (g_object_get_data ((GObject *) data, "cancel"))
979 return FALSE;
981 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
982 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
984 evtcount = 0;
985 while ((evtcount < 16) && gdk_events_pending ()) {
986 gtk_main_iteration_do (FALSE);
987 evtcount += 1;
988 }
990 gtk_main_iteration_do (FALSE);
992 return TRUE;
994 } // end of sp_export_progress_callback()
996 /// Called when export button is clicked
997 static void
998 sp_export_export_clicked (GtkButton *button, GtkObject *base)
999 {
1000 if (!SP_ACTIVE_DESKTOP) return;
1002 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
1003 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
1005 float const x0 = sp_export_value_get_px(base, "x0");
1006 float const y0 = sp_export_value_get_px(base, "y0");
1007 float const x1 = sp_export_value_get_px(base, "x1");
1008 float const y1 = sp_export_value_get_px(base, "y1");
1009 float const xdpi = sp_export_value_get(base, "xdpi");
1010 float const ydpi = sp_export_value_get(base, "ydpi");
1011 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1012 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1014 if (filename == NULL || *filename == '\0') {
1015 sp_ui_error_dialog(_("You have to enter a filename"));
1016 return;
1017 }
1019 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1020 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1021 return;
1022 }
1024 gchar *dirname = g_dirname(filename);
1025 if ( dirname == NULL
1026 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1027 {
1028 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1029 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1030 safeDir);
1031 sp_ui_error_dialog(error);
1032 g_free(safeDir);
1033 g_free(error);
1034 g_free(dirname);
1035 return;
1036 }
1037 g_free(dirname);
1039 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1040 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1041 char *fn;
1042 gchar *text;
1044 dlg = gtk_dialog_new ();
1045 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1046 prg = gtk_progress_bar_new ();
1047 sp_transientize (dlg);
1048 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1049 g_object_set_data ((GObject *) base, "progress", prg);
1050 fn = g_path_get_basename (filename);
1051 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1052 fn, width, height);
1053 g_free (fn);
1054 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1055 g_free (text);
1056 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1057 GTK_PROGRESS_LEFT_TO_RIGHT);
1058 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1059 prg, FALSE, FALSE, 4 );
1060 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1061 GTK_STOCK_CANCEL,
1062 GTK_RESPONSE_CANCEL );
1064 g_signal_connect ( (GObject *) dlg, "delete_event",
1065 (GCallback) sp_export_progress_delete, base);
1066 g_signal_connect ( (GObject *) btn, "clicked",
1067 (GCallback) sp_export_progress_cancel, base);
1068 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1069 gtk_widget_show_all (dlg);
1071 /* Do export */
1072 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1073 x0, y0, x1, y1, width, height,
1074 nv->pagecolor,
1075 sp_export_progress_callback, base)) {
1076 gchar * error;
1077 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1078 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1079 sp_ui_error_dialog(error);
1080 g_free(safeFile);
1081 g_free(error);
1082 }
1084 /* Reset the filename so that it can be changed again by changing
1085 selections and all that */
1086 g_free(original_name);
1087 original_name = g_strdup(filename);
1088 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1090 gtk_widget_destroy (dlg);
1091 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1093 /* Setup the values in the document */
1094 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1095 case SELECTION_PAGE:
1096 case SELECTION_DRAWING: {
1097 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1098 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1099 bool modified = FALSE;
1100 const gchar * temp_string;
1102 bool saved = sp_document_get_undo_sensitive(doc);
1103 sp_document_set_undo_sensitive(doc, FALSE);
1105 temp_string = repr->attribute("inkscape:export-filename");
1106 if (temp_string == NULL || strcmp(temp_string, filename)) {
1107 repr->setAttribute("inkscape:export-filename", filename);
1108 modified = TRUE;
1109 }
1110 temp_string = repr->attribute("inkscape:export-xdpi");
1111 if (temp_string == NULL || xdpi != atof(temp_string)) {
1112 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1113 modified = TRUE;
1114 }
1115 temp_string = repr->attribute("inkscape:export-ydpi");
1116 if (temp_string == NULL || xdpi != atof(temp_string)) {
1117 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1118 modified = TRUE;
1119 }
1121 if (modified)
1122 repr->setAttribute("sodipodi:modified", "TRUE");
1123 sp_document_set_undo_sensitive(doc, saved);
1124 break;
1125 }
1126 case SELECTION_SELECTION: {
1127 const GSList * reprlst;
1128 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1129 bool modified = FALSE;
1131 bool saved = sp_document_get_undo_sensitive(doc);
1132 sp_document_set_undo_sensitive(doc, FALSE);
1133 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1135 for(; reprlst != NULL; reprlst = reprlst->next) {
1136 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1137 const gchar * temp_string;
1139 if (repr->attribute("id") == NULL ||
1140 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1141 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1142 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1143 temp_string = repr->attribute("inkscape:export-filename");
1144 if (temp_string == NULL || strcmp(temp_string, filename)) {
1145 repr->setAttribute("inkscape:export-filename", filename);
1146 modified = TRUE;
1147 }
1148 }
1149 temp_string = repr->attribute("inkscape:export-xdpi");
1150 if (temp_string == NULL || xdpi != atof(temp_string)) {
1151 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1152 modified = TRUE;
1153 }
1154 temp_string = repr->attribute("inkscape:export-ydpi");
1155 if (temp_string == NULL || xdpi != atof(temp_string)) {
1156 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1157 modified = TRUE;
1158 }
1159 }
1161 if (modified) {
1162 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1163 repr->setAttribute("sodipodi:modified", "TRUE");
1164 }
1166 sp_document_set_undo_sensitive(doc, saved);
1167 break;
1168 }
1169 default:
1170 break;
1171 }
1174 return;
1175 } // end of sp_export_export_clicked()
1177 /// Called when Browse button is clicked
1178 static void
1179 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1180 {
1181 GtkWidget *fs, *fe;
1182 const gchar *filename;
1184 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1185 NULL,
1186 GTK_FILE_CHOOSER_ACTION_SAVE,
1187 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1188 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1189 NULL );
1191 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1193 sp_transientize (fs);
1195 gtk_window_set_modal(GTK_WINDOW (fs), true);
1197 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1199 if (*filename == '\0') {
1200 filename = homedir_path(NULL);
1201 }
1203 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1205 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1206 {
1207 gchar *file;
1209 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1210 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1211 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1212 g_free(utf8file);
1214 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1215 g_free(file);
1216 }
1218 gtk_widget_destroy (fs);
1220 return;
1221 } // end of sp_export_browse_clicked()
1223 // TODO: Move this to nr-rect-fns.h.
1224 static bool
1225 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1226 {
1227 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1228 return (
1229 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1230 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1231 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1232 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1233 );
1234 }
1236 /**
1237 \brief This function is used to detect the current selection setting
1238 based on the values in the x0, y0, x1 and y0 fields.
1239 \param base The export dialog itself
1241 One of the most confusing parts of this function is why the array
1242 is built at the beginning. What needs to happen here is that we
1243 should always check the current selection to see if it is the valid
1244 one. While this is a performance improvement it is also a usability
1245 one during the cases where things like selections and drawings match
1246 size. This way buttons change less 'randomly' (atleast in the eyes
1247 of the user). To do this an array is built where the current selection
1248 type is placed first, and then the others in an order from smallest
1249 to largest (this can be configured by reshuffling \c test_order).
1251 All of the values in this function are rounded to two decimal places
1252 because that is what is shown to the user. While everything is kept
1253 more accurate than that, the user can't control more acurrate than
1254 that, so for this to work for them - it needs to check on that level
1255 of accuracy.
1257 \todo finish writing this up
1258 */
1259 static void
1260 sp_export_detect_size(GtkObject * base) {
1261 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1262 selection_type this_test[SELECTION_NUMBER_OF + 1];
1263 selection_type key = SELECTION_NUMBER_OF;
1265 NR::Point x(sp_export_value_get_px (base, "x0"),
1266 sp_export_value_get_px (base, "y0"));
1267 NR::Point y(sp_export_value_get_px (base, "x1"),
1268 sp_export_value_get_px (base, "y1"));
1269 NR::Rect current_bbox(x, y);
1270 //std::cout << "Current " << current_bbox;
1272 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1273 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1274 this_test[i + 1] = test_order[i];
1275 }
1277 for (int i = 0;
1278 i < SELECTION_NUMBER_OF + 1 &&
1279 key == SELECTION_NUMBER_OF &&
1280 SP_ACTIVE_DESKTOP != NULL;
1281 i++) {
1282 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1283 switch (this_test[i]) {
1284 case SELECTION_SELECTION:
1285 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1286 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1288 //std::cout << "Selection " << bbox;
1289 if (sp_export_bbox_equal(bbox,current_bbox)) {
1290 key = SELECTION_SELECTION;
1291 }
1292 }
1293 break;
1294 case SELECTION_DRAWING: {
1295 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1297 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1299 // std::cout << "Drawing " << bbox2;
1300 if (sp_export_bbox_equal(bbox,current_bbox)) {
1301 key = SELECTION_DRAWING;
1302 }
1303 break;
1304 }
1306 case SELECTION_PAGE: {
1307 SPDocument *doc;
1309 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1311 NR::Point x(0.0, 0.0);
1312 NR::Point y(sp_document_width(doc),
1313 sp_document_height(doc));
1314 NR::Rect bbox(x, y);
1316 // std::cout << "Page " << bbox;
1317 if (sp_export_bbox_equal(bbox,current_bbox)) {
1318 key = SELECTION_PAGE;
1319 }
1321 break;
1322 }
1323 default:
1324 break;
1325 }
1326 }
1327 // std::cout << std::endl;
1329 if (key == SELECTION_NUMBER_OF) {
1330 key = SELECTION_CUSTOM;
1331 }
1333 /* We're now using a custom size, not a fixed one */
1334 /* printf("Detecting state: %s\n", selection_names[key]); */
1335 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1336 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1337 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1338 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1340 return;
1341 } /* sp_export_detect_size */
1343 /// Called when area x0 value is changed
1344 static void
1345 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1346 {
1347 float x0, x1, xdpi, width;
1349 if (gtk_object_get_data (base, "update"))
1350 return;
1352 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1353 (base, "units")))
1354 {
1355 return;
1356 }
1358 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1360 x0 = sp_export_value_get_px (base, "x0");
1361 x1 = sp_export_value_get_px (base, "x1");
1362 xdpi = sp_export_value_get (base, "xdpi");
1364 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1366 if (width < SP_EXPORT_MIN_SIZE) {
1367 const gchar *key;
1368 width = SP_EXPORT_MIN_SIZE;
1369 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1371 if (!strcmp (key, "x0")) {
1372 x1 = x0 + width * DPI_BASE / xdpi;
1373 sp_export_value_set_px (base, "x1", x1);
1374 } else {
1375 x0 = x1 - width * DPI_BASE / xdpi;
1376 sp_export_value_set_px (base, "x0", x0);
1377 }
1378 }
1380 sp_export_value_set_px (base, "width", x1 - x0);
1381 sp_export_value_set (base, "bmwidth", width);
1383 sp_export_detect_size(base);
1385 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1387 return;
1388 } // end of sp_export_area_x_value_changed()
1390 /// Called when area y0 value is changed.
1391 static void
1392 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1393 {
1394 float y0, y1, ydpi, height;
1396 if (gtk_object_get_data (base, "update"))
1397 return;
1399 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1400 (base, "units")))
1401 {
1402 return;
1403 }
1405 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1407 y0 = sp_export_value_get_px (base, "y0");
1408 y1 = sp_export_value_get_px (base, "y1");
1409 ydpi = sp_export_value_get (base, "ydpi");
1411 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1413 if (height < SP_EXPORT_MIN_SIZE) {
1414 const gchar *key;
1415 height = SP_EXPORT_MIN_SIZE;
1416 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1417 if (!strcmp (key, "y0")) {
1418 y1 = y0 + height * DPI_BASE / ydpi;
1419 sp_export_value_set_px (base, "y1", y1);
1420 } else {
1421 y0 = y1 - height * DPI_BASE / ydpi;
1422 sp_export_value_set_px (base, "y0", y0);
1423 }
1424 }
1426 sp_export_value_set_px (base, "height", y1 - y0);
1427 sp_export_value_set (base, "bmheight", height);
1429 sp_export_detect_size(base);
1431 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1433 return;
1434 } // end of sp_export_area_y_value_changed()
1436 /// Called when x1-x0 or area width is changed
1437 static void
1438 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1439 {
1440 float x0, x1, xdpi, width, bmwidth;
1442 if (gtk_object_get_data (base, "update"))
1443 return;
1445 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1446 (base, "units"))) {
1447 return;
1448 }
1450 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1452 x0 = sp_export_value_get_px (base, "x0");
1453 x1 = sp_export_value_get_px (base, "x1");
1454 xdpi = sp_export_value_get (base, "xdpi");
1455 width = sp_export_value_get_px (base, "width");
1456 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1458 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1460 bmwidth = SP_EXPORT_MIN_SIZE;
1461 width = bmwidth * DPI_BASE / xdpi;
1462 sp_export_value_set_px (base, "width", width);
1463 }
1465 sp_export_value_set_px (base, "x1", x0 + width);
1466 sp_export_value_set (base, "bmwidth", bmwidth);
1468 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1470 return;
1471 } // end of sp_export_area_width_value_changed()
1473 /// Called when y1-y0 or area height is changed.
1474 static void
1475 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1476 {
1478 float y0, y1, ydpi, height, bmheight;
1480 if (gtk_object_get_data (base, "update"))
1481 return;
1483 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1484 (base, "units"))) {
1485 return;
1486 }
1488 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1490 y0 = sp_export_value_get_px (base, "y0");
1491 y1 = sp_export_value_get_px (base, "y1");
1492 ydpi = sp_export_value_get (base, "ydpi");
1493 height = sp_export_value_get_px (base, "height");
1494 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1496 if (bmheight < SP_EXPORT_MIN_SIZE) {
1497 bmheight = SP_EXPORT_MIN_SIZE;
1498 height = bmheight * DPI_BASE / ydpi;
1499 sp_export_value_set_px (base, "height", height);
1500 }
1502 sp_export_value_set_px (base, "y1", y0 + height);
1503 sp_export_value_set (base, "bmheight", bmheight);
1505 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1507 return;
1508 } // end of sp_export_area_height_value_changed()
1510 /**
1511 \brief A function to set the ydpi
1512 \param base The export dialog
1514 This function grabs all of the y values and then figures out the
1515 new bitmap size based on the changing dpi value. The dpi value is
1516 gotten from the xdpi setting as these can not currently be independent.
1517 */
1518 static void
1519 sp_export_set_image_y (GtkObject *base)
1520 {
1521 float y0, y1, xdpi;
1523 y0 = sp_export_value_get_px (base, "y0");
1524 y1 = sp_export_value_get_px (base, "y1");
1525 xdpi = sp_export_value_get (base, "xdpi");
1527 sp_export_value_set (base, "ydpi", xdpi);
1528 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1530 return;
1531 } // end of sp_export_set_image_y()
1533 /**
1534 \brief A function to set the xdpi
1535 \param base The export dialog
1537 This function grabs all of the x values and then figures out the
1538 new bitmap size based on the changing dpi value. The dpi value is
1539 gotten from the xdpi setting as these can not currently be independent.
1540 */
1541 static void
1542 sp_export_set_image_x (GtkObject *base)
1543 {
1544 float x0, x1, xdpi;
1546 x0 = sp_export_value_get_px (base, "x0");
1547 x1 = sp_export_value_get_px (base, "x1");
1548 xdpi = sp_export_value_get (base, "xdpi");
1550 sp_export_value_set (base, "ydpi", xdpi);
1551 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1553 return;
1554 } // end of sp_export_set_image_x()
1556 /// Called when pixel width is changed
1557 static void
1558 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1559 {
1560 float x0, x1, bmwidth, xdpi;
1562 if (gtk_object_get_data (base, "update"))
1563 return;
1565 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1566 (base, "units"))) {
1567 return;
1568 }
1570 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1572 x0 = sp_export_value_get_px (base, "x0");
1573 x1 = sp_export_value_get_px (base, "x1");
1574 bmwidth = sp_export_value_get (base, "bmwidth");
1576 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1577 bmwidth = SP_EXPORT_MIN_SIZE;
1578 sp_export_value_set (base, "bmwidth", bmwidth);
1579 }
1581 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1582 sp_export_value_set (base, "xdpi", xdpi);
1584 sp_export_set_image_y (base);
1586 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1588 return;
1589 } // end of sp_export_bitmap_width_value_changed()
1591 /// Called when pixel height is changed
1592 static void
1593 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1594 {
1595 float y0, y1, bmheight, xdpi;
1597 if (gtk_object_get_data (base, "update"))
1598 return;
1600 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1601 (base, "units"))) {
1602 return;
1603 }
1605 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1607 y0 = sp_export_value_get_px (base, "y0");
1608 y1 = sp_export_value_get_px (base, "y1");
1609 bmheight = sp_export_value_get (base, "bmheight");
1611 if (bmheight < SP_EXPORT_MIN_SIZE) {
1612 bmheight = SP_EXPORT_MIN_SIZE;
1613 sp_export_value_set (base, "bmheight", bmheight);
1614 }
1616 xdpi = bmheight * DPI_BASE / (y1 - y0);
1617 sp_export_value_set (base, "xdpi", xdpi);
1619 sp_export_set_image_x (base);
1621 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1623 return;
1624 } // end of sp_export_bitmap_width_value_changed()
1626 /**
1627 \brief A function to adjust the bitmap width when the xdpi value changes
1628 \param adj The adjustment that was changed
1629 \param base The export dialog itself
1631 The first thing this function checks is to see if we are doing an
1632 update. If we are, this function just returns because there is another
1633 instance of it that will handle everything for us. If there is a
1634 units change, we also assume that everyone is being updated appropriately
1635 and there is nothing for us to do.
1637 If we're the highest level function, we set the update flag, and
1638 continue on our way.
1640 All of the values are grabbed using the \c sp_export_value_get functions
1641 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1642 xdpi value is saved in the preferences for the next time the dialog
1643 is opened. (does the selection dpi need to be set here?)
1645 A check is done to to ensure that we aren't outputing an invalid width,
1646 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1647 changed to make it valid.
1649 After all of this the bitmap width is changed.
1651 We also change the ydpi. This is a temporary hack as these can not
1652 currently be independent. This is likely to change in the future.
1653 */
1654 void
1655 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1656 {
1657 float x0, x1, xdpi, bmwidth;
1659 if (gtk_object_get_data (base, "update"))
1660 return;
1662 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1663 (base, "units"))) {
1664 return;
1665 }
1667 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1669 x0 = sp_export_value_get_px (base, "x0");
1670 x1 = sp_export_value_get_px (base, "x1");
1671 xdpi = sp_export_value_get (base, "xdpi");
1673 // remember xdpi setting
1674 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1676 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1678 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1679 bmwidth = SP_EXPORT_MIN_SIZE;
1680 if (x1 != x0)
1681 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1682 else
1683 xdpi = DPI_BASE;
1684 sp_export_value_set (base, "xdpi", xdpi);
1685 }
1687 sp_export_value_set (base, "bmwidth", bmwidth);
1689 sp_export_set_image_y (base);
1691 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1693 return;
1694 } // end of sp_export_xdpi_value_changed()
1697 /**
1698 \brief A function to change the area that is used for the exported
1699 bitmap.
1700 \param base This is the export dialog
1701 \param x0 Horizontal upper left hand corner of the picture in points
1702 \param y0 Vertical upper left hand corner of the picture in points
1703 \param x1 Horizontal lower right hand corner of the picture in points
1704 \param y1 Vertical lower right hand corner of the picture in points
1706 This function just calls \c sp_export_value_set_px for each of the
1707 parameters that is passed in. This allows for setting them all in
1708 one convient area.
1710 Update is set to suspend all of the other test running while all the
1711 values are being set up. This allows for a performance increase, but
1712 it also means that the wrong type won't be detected with only some of
1713 the values set. After all the values are set everyone is told that
1714 there has been an update.
1715 */
1716 static void
1717 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1718 {
1719 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1720 sp_export_value_set_px (base, "x1", x1);
1721 sp_export_value_set_px (base, "y1", y1);
1722 sp_export_value_set_px (base, "x0", x0);
1723 sp_export_value_set_px (base, "y0", y0);
1724 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1726 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1727 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1729 return;
1730 }
1732 /**
1733 \brief Sets the value of an adjustment
1734 \param base The export dialog
1735 \param key Which adjustment to set
1736 \param val What value to set it to
1738 This function finds the adjustment using the data stored in the
1739 export dialog. After finding the adjustment it then sets
1740 the value of it.
1741 */
1742 static void
1743 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1744 {
1745 GtkAdjustment *adj;
1747 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1749 gtk_adjustment_set_value (adj, val);
1750 }
1752 /**
1753 \brief A function to set a value using the units points
1754 \param base The export dialog
1755 \param key Which value should be set
1756 \param val What the value should be in points
1758 This function first gets the adjustment for the key that is passed
1759 in. It then figures out what units are currently being used in the
1760 dialog. After doing all of that, it then converts the incoming
1761 value and sets the adjustment.
1762 */
1763 static void
1764 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1765 {
1766 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1768 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1770 return;
1771 }
1773 /**
1774 \brief Get the value of an adjustment in the export dialog
1775 \param base The export dialog
1776 \param key Which adjustment is being looked for
1777 \return The value in the specified adjustment
1779 This function gets the adjustment from the data field in the export
1780 dialog. It then grabs the value from the adjustment.
1781 */
1782 static float
1783 sp_export_value_get ( GtkObject *base, const gchar *key )
1784 {
1785 GtkAdjustment *adj;
1787 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1789 return adj->value;
1790 }
1792 /**
1793 \brief Grabs a value in the export dialog and converts the unit
1794 to points
1795 \param base The export dialog
1796 \param key Which value should be returned
1797 \return The value in the adjustment in points
1799 This function, at its most basic, is a call to \c sp_export_value_get
1800 to get the value of the adjustment. It then finds the units that
1801 are being used by looking at the "units" attribute of the export
1802 dialog. Using that it converts the returned value into points.
1803 */
1804 static float
1805 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1806 {
1807 float value = sp_export_value_get(base, key);
1808 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1810 return sp_units_get_pixels (value, *unit);
1811 } // end of sp_export_value_get_px()
1813 /**
1814 \brief This function is called when the filename is changed by
1815 anyone. It resets the virgin bit.
1816 \param object Text entry box
1817 \param data The export dialog
1818 \return None
1820 This function gets called when the text area is modified. It is
1821 looking for the case where the text area is modified from its
1822 original value. In that case it sets the "filename-modified" bit
1823 to TRUE. If the text dialog returns back to the original text, the
1824 bit gets reset. This should stop simple mistakes.
1825 */
1826 static void
1827 sp_export_filename_modified (GtkObject * object, gpointer data)
1828 {
1829 GtkWidget * text_entry = (GtkWidget *)object;
1830 GtkWidget * export_dialog = (GtkWidget *)data;
1832 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1833 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1834 // printf("Modified: FALSE\n");
1835 } else {
1836 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1837 // printf("Modified: TRUE\n");
1838 }
1840 return;
1841 } // end sp_export_filename_modified
1843 /*
1844 Local Variables:
1845 mode:c++
1846 c-file-style:"stroustrup"
1847 c-file-offsets:((innamespace . 0)(inline-open . 0))
1848 indent-tabs-mode:nil
1849 fill-column:99
1850 End:
1851 */
1852 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :