47f2b351d11e6bd5a5ca6b47b14c07ea33590d7f
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 NRRect bbox;
688 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
689 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
691 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
692 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
693 }
694 }
695 break;
696 case SELECTION_SELECTION:
697 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
698 NRRect bbox;
699 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
700 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
701 }
702 break;
703 default:
704 /* Do nothing for page or for custom */
705 break;
706 }
708 return;
709 }
711 /// Called when one of the selection buttons was toggled.
712 static void
713 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
714 {
715 if (gtk_object_get_data (base, "update"))
716 return;
718 selection_type key, old_key;
719 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
720 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
722 /* Ignore all "turned off" events unless we're the only active button */
723 if (!gtk_toggle_button_get_active (tb) ) {
725 /* Don't let the current selection be deactived - but rerun the
726 activate to allow the user to renew the values */
727 if (key == old_key) {
728 gtk_toggle_button_set_active ( tb, TRUE );
729 }
731 return;
732 }
734 /* Turn off the currently active button unless it's us */
735 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
737 if (old_key != key) {
738 gtk_toggle_button_set_active
739 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
740 FALSE );
741 }
743 if ( SP_ACTIVE_DESKTOP )
744 {
745 SPDocument *doc;
746 NRRect bbox;
747 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
749 /* Notice how the switch is used to 'fall through' here to get
750 various backups. If you modify this without noticing you'll
751 probabaly screw something up. */
752 switch (key) {
753 case SELECTION_SELECTION:
754 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
755 {
756 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
757 /* Only if there is a selection that we can set
758 do we break, otherwise we fall through to the
759 drawing */
760 // std::cout << "Using selection: SELECTION" << std::endl;
761 key = SELECTION_SELECTION;
762 break;
763 }
764 case SELECTION_DRAWING:
765 /** \todo
766 * This returns wrong values if the document has a viewBox.
767 */
768 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
770 /* If the drawing is valid, then we'll use it and break
771 otherwise we drop through to the page settings */
772 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
773 // std::cout << "Using selection: DRAWING" << std::endl;
774 key = SELECTION_DRAWING;
775 break;
776 }
777 case SELECTION_PAGE:
778 bbox.x0 = 0.0;
779 bbox.y0 = 0.0;
780 bbox.x1 = sp_document_width (doc);
781 bbox.y1 = sp_document_height (doc);
782 // std::cout << "Using selection: PAGE" << std::endl;
783 key = SELECTION_PAGE;
784 break;
785 case SELECTION_CUSTOM:
786 default:
787 break;
788 } // switch
790 // remember area setting
791 prefs_set_string_attribute ( "dialogs.export.exportarea",
792 "value", selection_names[key]);
794 if (key != SELECTION_CUSTOM) {
795 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
796 }
798 } // end of if ( SP_ACTIVE_DESKTOP )
801 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
802 GtkWidget * file_entry;
803 const gchar * filename = NULL;
804 float xdpi = 0.0, ydpi = 0.0;
806 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
808 switch (key) {
809 case SELECTION_PAGE:
810 case SELECTION_DRAWING: {
811 SPDocument * doc = SP_ACTIVE_DOCUMENT;
812 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
813 const gchar * dpi_string;
815 filename = repr->attribute("inkscape:export-filename");
817 dpi_string = NULL;
818 dpi_string = repr->attribute("inkscape:export-xdpi");
819 if (dpi_string != NULL) {
820 xdpi = atof(dpi_string);
821 }
823 dpi_string = NULL;
824 dpi_string = repr->attribute("inkscape:export-ydpi");
825 if (dpi_string != NULL) {
826 ydpi = atof(dpi_string);
827 }
829 if (filename == NULL) {
830 if (doc_export_name != NULL) {
831 filename = g_strdup(doc_export_name);
832 } else {
833 filename = g_strdup("");
834 }
835 }
836 break;
837 }
838 case SELECTION_SELECTION:
839 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
840 const GSList * reprlst;
841 bool filename_search = TRUE;
842 bool xdpi_search = TRUE;
843 bool ydpi_search = TRUE;
845 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
846 for(; reprlst != NULL &&
847 filename_search &&
848 xdpi_search &&
849 ydpi_search;
850 reprlst = reprlst->next) {
851 const gchar * dpi_string;
852 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
854 if (filename_search) {
855 filename = repr->attribute("inkscape:export-filename");
856 if (filename != NULL)
857 filename_search = FALSE;
858 }
860 if (xdpi_search) {
861 dpi_string = NULL;
862 dpi_string = repr->attribute("inkscape:export-xdpi");
863 if (dpi_string != NULL) {
864 xdpi = atof(dpi_string);
865 xdpi_search = FALSE;
866 }
867 }
869 if (ydpi_search) {
870 dpi_string = NULL;
871 dpi_string = repr->attribute("inkscape:export-ydpi");
872 if (dpi_string != NULL) {
873 ydpi = atof(dpi_string);
874 ydpi_search = FALSE;
875 }
876 }
877 }
879 /* If we still don't have a filename -- let's build
880 one that's nice */
881 if (filename == NULL) {
882 const gchar * id = NULL;
883 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
884 for(; reprlst != NULL; reprlst = reprlst->next) {
885 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
886 if (repr->attribute("id")) {
887 id = repr->attribute("id");
888 break;
889 }
890 }
891 if (id == NULL) /* This should never happen */
892 id = "bitmap";
894 gchar * directory = NULL;
895 const gchar * file_entry_text;
897 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
898 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
899 // std::cout << "Directory from dialog" << std::endl;
900 directory = g_dirname(file_entry_text);
901 }
903 if (directory == NULL) {
904 /* Grab document directory */
905 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
906 // std::cout << "Directory from document" << std::endl;
907 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
908 }
909 }
911 if (directory == NULL) {
912 // std::cout << "Home Directory" << std::endl;
913 directory = homedir_path(NULL);
914 }
916 gchar * id_ext = g_strconcat(id, ".png", NULL);
917 filename = g_build_filename(directory, id_ext, NULL);
918 g_free(directory);
919 g_free(id_ext);
920 }
921 }
922 break;
923 case SELECTION_CUSTOM:
924 default:
925 break;
926 }
928 if (filename != NULL) {
929 g_free(original_name);
930 original_name = g_strdup(filename);
931 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
932 }
934 if (xdpi != 0.0) {
935 sp_export_value_set(base, "xdpi", xdpi);
936 }
938 /* These can't be seperate, and setting x sets y, so for
939 now setting this is disabled. Hopefully it won't be in
940 the future */
941 if (FALSE && ydpi != 0.0) {
942 sp_export_value_set(base, "ydpi", ydpi);
943 }
944 }
946 return;
947 } // end of sp_export_area_toggled()
949 /// Called when dialog is deleted
950 static gint
951 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
952 {
953 g_object_set_data (base, "cancel", (gpointer) 1);
954 return TRUE;
955 } // end of sp_export_progress_delete()
957 /// Called when progress is cancelled
958 static void
959 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
960 {
961 g_object_set_data (base, "cancel", (gpointer) 1);
962 } // end of sp_export_progress_cancel()
964 /// Called for every progress iteration
965 static unsigned int
966 sp_export_progress_callback (float value, void *data)
967 {
968 GtkWidget *prg;
969 int evtcount;
971 if (g_object_get_data ((GObject *) data, "cancel"))
972 return FALSE;
974 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
975 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
977 evtcount = 0;
978 while ((evtcount < 16) && gdk_events_pending ()) {
979 gtk_main_iteration_do (FALSE);
980 evtcount += 1;
981 }
983 gtk_main_iteration_do (FALSE);
985 return TRUE;
987 } // end of sp_export_progress_callback()
989 /// Called when export button is clicked
990 static void
991 sp_export_export_clicked (GtkButton *button, GtkObject *base)
992 {
993 if (!SP_ACTIVE_DESKTOP) return;
995 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
996 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
998 float const x0 = sp_export_value_get_px(base, "x0");
999 float const y0 = sp_export_value_get_px(base, "y0");
1000 float const x1 = sp_export_value_get_px(base, "x1");
1001 float const y1 = sp_export_value_get_px(base, "y1");
1002 float const xdpi = sp_export_value_get(base, "xdpi");
1003 float const ydpi = sp_export_value_get(base, "ydpi");
1004 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
1005 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
1007 if (filename == NULL || *filename == '\0') {
1008 sp_ui_error_dialog(_("You have to enter a filename"));
1009 return;
1010 }
1012 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
1013 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
1014 return;
1015 }
1017 gchar *dirname = g_dirname(filename);
1018 if ( dirname == NULL
1019 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
1020 {
1021 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
1022 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
1023 safeDir);
1024 sp_ui_error_dialog(error);
1025 g_free(safeDir);
1026 g_free(error);
1027 g_free(dirname);
1028 return;
1029 }
1030 g_free(dirname);
1032 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
1033 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
1034 char *fn;
1035 gchar *text;
1037 dlg = gtk_dialog_new ();
1038 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1039 prg = gtk_progress_bar_new ();
1040 sp_transientize (dlg);
1041 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1042 g_object_set_data ((GObject *) base, "progress", prg);
1043 fn = g_path_get_basename (filename);
1044 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1045 fn, width, height);
1046 g_free (fn);
1047 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1048 g_free (text);
1049 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1050 GTK_PROGRESS_LEFT_TO_RIGHT);
1051 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1052 prg, FALSE, FALSE, 4 );
1053 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1054 GTK_STOCK_CANCEL,
1055 GTK_RESPONSE_CANCEL );
1057 g_signal_connect ( (GObject *) dlg, "delete_event",
1058 (GCallback) sp_export_progress_delete, base);
1059 g_signal_connect ( (GObject *) btn, "clicked",
1060 (GCallback) sp_export_progress_cancel, base);
1061 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1062 gtk_widget_show_all (dlg);
1064 /* Do export */
1065 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1066 x0, y0, x1, y1, width, height,
1067 nv->pagecolor,
1068 sp_export_progress_callback, base)) {
1069 gchar * error;
1070 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1071 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1072 sp_ui_error_dialog(error);
1073 g_free(safeFile);
1074 g_free(error);
1075 }
1077 /* Reset the filename so that it can be changed again by changing
1078 selections and all that */
1079 g_free(original_name);
1080 original_name = g_strdup(filename);
1081 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1083 gtk_widget_destroy (dlg);
1084 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1086 /* Setup the values in the document */
1087 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1088 case SELECTION_PAGE:
1089 case SELECTION_DRAWING: {
1090 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1091 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1092 bool modified = FALSE;
1093 const gchar * temp_string;
1095 bool saved = sp_document_get_undo_sensitive(doc);
1096 sp_document_set_undo_sensitive(doc, FALSE);
1098 temp_string = repr->attribute("inkscape:export-filename");
1099 if (temp_string == NULL || strcmp(temp_string, filename)) {
1100 repr->setAttribute("inkscape:export-filename", filename);
1101 modified = TRUE;
1102 }
1103 temp_string = repr->attribute("inkscape:export-xdpi");
1104 if (temp_string == NULL || xdpi != atof(temp_string)) {
1105 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1106 modified = TRUE;
1107 }
1108 temp_string = repr->attribute("inkscape:export-ydpi");
1109 if (temp_string == NULL || xdpi != atof(temp_string)) {
1110 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1111 modified = TRUE;
1112 }
1114 if (modified)
1115 repr->setAttribute("sodipodi:modified", "TRUE");
1116 sp_document_set_undo_sensitive(doc, saved);
1117 break;
1118 }
1119 case SELECTION_SELECTION: {
1120 const GSList * reprlst;
1121 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1122 bool modified = FALSE;
1124 bool saved = sp_document_get_undo_sensitive(doc);
1125 sp_document_set_undo_sensitive(doc, FALSE);
1126 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1128 for(; reprlst != NULL; reprlst = reprlst->next) {
1129 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1130 const gchar * temp_string;
1132 if (repr->attribute("id") == NULL ||
1133 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1134 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1135 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1136 temp_string = repr->attribute("inkscape:export-filename");
1137 if (temp_string == NULL || strcmp(temp_string, filename)) {
1138 repr->setAttribute("inkscape:export-filename", filename);
1139 modified = TRUE;
1140 }
1141 }
1142 temp_string = repr->attribute("inkscape:export-xdpi");
1143 if (temp_string == NULL || xdpi != atof(temp_string)) {
1144 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1145 modified = TRUE;
1146 }
1147 temp_string = repr->attribute("inkscape:export-ydpi");
1148 if (temp_string == NULL || xdpi != atof(temp_string)) {
1149 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1150 modified = TRUE;
1151 }
1152 }
1154 if (modified) {
1155 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1156 repr->setAttribute("sodipodi:modified", "TRUE");
1157 }
1159 sp_document_set_undo_sensitive(doc, saved);
1160 break;
1161 }
1162 default:
1163 break;
1164 }
1167 return;
1168 } // end of sp_export_export_clicked()
1170 /// Called when Browse button is clicked
1171 static void
1172 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1173 {
1174 GtkWidget *fs, *fe;
1175 const gchar *filename;
1177 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1178 NULL,
1179 GTK_FILE_CHOOSER_ACTION_SAVE,
1180 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1181 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1182 NULL );
1184 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1186 sp_transientize (fs);
1188 gtk_window_set_modal(GTK_WINDOW (fs), true);
1190 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1192 if (*filename == '\0') {
1193 filename = homedir_path(NULL);
1194 }
1196 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1198 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1199 {
1200 gchar *file;
1202 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1203 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1204 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1205 g_free(utf8file);
1207 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1208 g_free(file);
1209 }
1211 gtk_widget_destroy (fs);
1213 return;
1214 } // end of sp_export_browse_clicked()
1216 // TODO: Move this to nr-rect-fns.h.
1217 static bool
1218 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1219 {
1220 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1221 return (
1222 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1223 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1224 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1225 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1226 );
1227 }
1229 /**
1230 \brief This function is used to detect the current selection setting
1231 based on the values in the x0, y0, x1 and y0 fields.
1232 \param base The export dialog itself
1234 One of the most confusing parts of this function is why the array
1235 is built at the beginning. What needs to happen here is that we
1236 should always check the current selection to see if it is the valid
1237 one. While this is a performance improvement it is also a usability
1238 one during the cases where things like selections and drawings match
1239 size. This way buttons change less 'randomly' (atleast in the eyes
1240 of the user). To do this an array is built where the current selection
1241 type is placed first, and then the others in an order from smallest
1242 to largest (this can be configured by reshuffling \c test_order).
1244 All of the values in this function are rounded to two decimal places
1245 because that is what is shown to the user. While everything is kept
1246 more accurate than that, the user can't control more acurrate than
1247 that, so for this to work for them - it needs to check on that level
1248 of accuracy.
1250 \todo finish writing this up
1251 */
1252 static void
1253 sp_export_detect_size(GtkObject * base) {
1254 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1255 selection_type this_test[SELECTION_NUMBER_OF + 1];
1256 selection_type key = SELECTION_NUMBER_OF;
1258 NR::Point x(sp_export_value_get_px (base, "x0"),
1259 sp_export_value_get_px (base, "y0"));
1260 NR::Point y(sp_export_value_get_px (base, "x1"),
1261 sp_export_value_get_px (base, "y1"));
1262 NR::Rect current_bbox(x, y);
1263 //std::cout << "Current " << current_bbox;
1265 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1266 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1267 this_test[i + 1] = test_order[i];
1268 }
1270 for (int i = 0;
1271 i < SELECTION_NUMBER_OF + 1 &&
1272 key == SELECTION_NUMBER_OF &&
1273 SP_ACTIVE_DESKTOP != NULL;
1274 i++) {
1275 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1276 switch (this_test[i]) {
1277 case SELECTION_SELECTION:
1278 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1279 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1281 //std::cout << "Selection " << bbox;
1282 if (sp_export_bbox_equal(bbox,current_bbox)) {
1283 key = SELECTION_SELECTION;
1284 }
1285 }
1286 break;
1287 case SELECTION_DRAWING: {
1288 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1290 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1292 // std::cout << "Drawing " << bbox2;
1293 if (sp_export_bbox_equal(bbox,current_bbox)) {
1294 key = SELECTION_DRAWING;
1295 }
1296 break;
1297 }
1299 case SELECTION_PAGE: {
1300 SPDocument *doc;
1302 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1304 NR::Point x(0.0, 0.0);
1305 NR::Point y(sp_document_width(doc),
1306 sp_document_height(doc));
1307 NR::Rect bbox(x, y);
1309 // std::cout << "Page " << bbox;
1310 if (sp_export_bbox_equal(bbox,current_bbox)) {
1311 key = SELECTION_PAGE;
1312 }
1314 break;
1315 }
1316 default:
1317 break;
1318 }
1319 }
1320 // std::cout << std::endl;
1322 if (key == SELECTION_NUMBER_OF) {
1323 key = SELECTION_CUSTOM;
1324 }
1326 /* We're now using a custom size, not a fixed one */
1327 /* printf("Detecting state: %s\n", selection_names[key]); */
1328 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1329 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1330 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1331 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1333 return;
1334 } /* sp_export_detect_size */
1336 /// Called when area x0 value is changed
1337 static void
1338 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1339 {
1340 float x0, x1, xdpi, width;
1342 if (gtk_object_get_data (base, "update"))
1343 return;
1345 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1346 (base, "units")))
1347 {
1348 return;
1349 }
1351 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1353 x0 = sp_export_value_get_px (base, "x0");
1354 x1 = sp_export_value_get_px (base, "x1");
1355 xdpi = sp_export_value_get (base, "xdpi");
1357 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1359 if (width < SP_EXPORT_MIN_SIZE) {
1360 const gchar *key;
1361 width = SP_EXPORT_MIN_SIZE;
1362 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1364 if (!strcmp (key, "x0")) {
1365 x1 = x0 + width * DPI_BASE / xdpi;
1366 sp_export_value_set_px (base, "x1", x1);
1367 } else {
1368 x0 = x1 - width * DPI_BASE / xdpi;
1369 sp_export_value_set_px (base, "x0", x0);
1370 }
1371 }
1373 sp_export_value_set_px (base, "width", x1 - x0);
1374 sp_export_value_set (base, "bmwidth", width);
1376 sp_export_detect_size(base);
1378 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1380 return;
1381 } // end of sp_export_area_x_value_changed()
1383 /// Called when area y0 value is changed.
1384 static void
1385 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1386 {
1387 float y0, y1, ydpi, height;
1389 if (gtk_object_get_data (base, "update"))
1390 return;
1392 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1393 (base, "units")))
1394 {
1395 return;
1396 }
1398 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1400 y0 = sp_export_value_get_px (base, "y0");
1401 y1 = sp_export_value_get_px (base, "y1");
1402 ydpi = sp_export_value_get (base, "ydpi");
1404 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1406 if (height < SP_EXPORT_MIN_SIZE) {
1407 const gchar *key;
1408 height = SP_EXPORT_MIN_SIZE;
1409 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1410 if (!strcmp (key, "y0")) {
1411 y1 = y0 + height * DPI_BASE / ydpi;
1412 sp_export_value_set_px (base, "y1", y1);
1413 } else {
1414 y0 = y1 - height * DPI_BASE / ydpi;
1415 sp_export_value_set_px (base, "y0", y0);
1416 }
1417 }
1419 sp_export_value_set_px (base, "height", y1 - y0);
1420 sp_export_value_set (base, "bmheight", height);
1422 sp_export_detect_size(base);
1424 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1426 return;
1427 } // end of sp_export_area_y_value_changed()
1429 /// Called when x1-x0 or area width is changed
1430 static void
1431 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1432 {
1433 float x0, x1, xdpi, width, bmwidth;
1435 if (gtk_object_get_data (base, "update"))
1436 return;
1438 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1439 (base, "units"))) {
1440 return;
1441 }
1443 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1445 x0 = sp_export_value_get_px (base, "x0");
1446 x1 = sp_export_value_get_px (base, "x1");
1447 xdpi = sp_export_value_get (base, "xdpi");
1448 width = sp_export_value_get_px (base, "width");
1449 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1451 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1453 bmwidth = SP_EXPORT_MIN_SIZE;
1454 width = bmwidth * DPI_BASE / xdpi;
1455 sp_export_value_set_px (base, "width", width);
1456 }
1458 sp_export_value_set_px (base, "x1", x0 + width);
1459 sp_export_value_set (base, "bmwidth", bmwidth);
1461 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1463 return;
1464 } // end of sp_export_area_width_value_changed()
1466 /// Called when y1-y0 or area height is changed.
1467 static void
1468 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1469 {
1471 float y0, y1, ydpi, height, bmheight;
1473 if (gtk_object_get_data (base, "update"))
1474 return;
1476 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1477 (base, "units"))) {
1478 return;
1479 }
1481 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1483 y0 = sp_export_value_get_px (base, "y0");
1484 y1 = sp_export_value_get_px (base, "y1");
1485 ydpi = sp_export_value_get (base, "ydpi");
1486 height = sp_export_value_get_px (base, "height");
1487 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1489 if (bmheight < SP_EXPORT_MIN_SIZE) {
1490 bmheight = SP_EXPORT_MIN_SIZE;
1491 height = bmheight * DPI_BASE / ydpi;
1492 sp_export_value_set_px (base, "height", height);
1493 }
1495 sp_export_value_set_px (base, "y1", y0 + height);
1496 sp_export_value_set (base, "bmheight", bmheight);
1498 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1500 return;
1501 } // end of sp_export_area_height_value_changed()
1503 /**
1504 \brief A function to set the ydpi
1505 \param base The export dialog
1507 This function grabs all of the y values and then figures out the
1508 new bitmap size based on the changing dpi value. The dpi value is
1509 gotten from the xdpi setting as these can not currently be independent.
1510 */
1511 static void
1512 sp_export_set_image_y (GtkObject *base)
1513 {
1514 float y0, y1, xdpi;
1516 y0 = sp_export_value_get_px (base, "y0");
1517 y1 = sp_export_value_get_px (base, "y1");
1518 xdpi = sp_export_value_get (base, "xdpi");
1520 sp_export_value_set (base, "ydpi", xdpi);
1521 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1523 return;
1524 } // end of sp_export_set_image_y()
1526 /**
1527 \brief A function to set the xdpi
1528 \param base The export dialog
1530 This function grabs all of the x values and then figures out the
1531 new bitmap size based on the changing dpi value. The dpi value is
1532 gotten from the xdpi setting as these can not currently be independent.
1533 */
1534 static void
1535 sp_export_set_image_x (GtkObject *base)
1536 {
1537 float x0, x1, xdpi;
1539 x0 = sp_export_value_get_px (base, "x0");
1540 x1 = sp_export_value_get_px (base, "x1");
1541 xdpi = sp_export_value_get (base, "xdpi");
1543 sp_export_value_set (base, "ydpi", xdpi);
1544 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1546 return;
1547 } // end of sp_export_set_image_x()
1549 /// Called when pixel width is changed
1550 static void
1551 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1552 {
1553 float x0, x1, bmwidth, xdpi;
1555 if (gtk_object_get_data (base, "update"))
1556 return;
1558 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1559 (base, "units"))) {
1560 return;
1561 }
1563 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1565 x0 = sp_export_value_get_px (base, "x0");
1566 x1 = sp_export_value_get_px (base, "x1");
1567 bmwidth = sp_export_value_get (base, "bmwidth");
1569 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1570 bmwidth = SP_EXPORT_MIN_SIZE;
1571 sp_export_value_set (base, "bmwidth", bmwidth);
1572 }
1574 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1575 sp_export_value_set (base, "xdpi", xdpi);
1577 sp_export_set_image_y (base);
1579 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1581 return;
1582 } // end of sp_export_bitmap_width_value_changed()
1584 /// Called when pixel height is changed
1585 static void
1586 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1587 {
1588 float y0, y1, bmheight, xdpi;
1590 if (gtk_object_get_data (base, "update"))
1591 return;
1593 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1594 (base, "units"))) {
1595 return;
1596 }
1598 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1600 y0 = sp_export_value_get_px (base, "y0");
1601 y1 = sp_export_value_get_px (base, "y1");
1602 bmheight = sp_export_value_get (base, "bmheight");
1604 if (bmheight < SP_EXPORT_MIN_SIZE) {
1605 bmheight = SP_EXPORT_MIN_SIZE;
1606 sp_export_value_set (base, "bmheight", bmheight);
1607 }
1609 xdpi = bmheight * DPI_BASE / (y1 - y0);
1610 sp_export_value_set (base, "xdpi", xdpi);
1612 sp_export_set_image_x (base);
1614 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1616 return;
1617 } // end of sp_export_bitmap_width_value_changed()
1619 /**
1620 \brief A function to adjust the bitmap width when the xdpi value changes
1621 \param adj The adjustment that was changed
1622 \param base The export dialog itself
1624 The first thing this function checks is to see if we are doing an
1625 update. If we are, this function just returns because there is another
1626 instance of it that will handle everything for us. If there is a
1627 units change, we also assume that everyone is being updated appropriately
1628 and there is nothing for us to do.
1630 If we're the highest level function, we set the update flag, and
1631 continue on our way.
1633 All of the values are grabbed using the \c sp_export_value_get functions
1634 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1635 xdpi value is saved in the preferences for the next time the dialog
1636 is opened. (does the selection dpi need to be set here?)
1638 A check is done to to ensure that we aren't outputing an invalid width,
1639 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1640 changed to make it valid.
1642 After all of this the bitmap width is changed.
1644 We also change the ydpi. This is a temporary hack as these can not
1645 currently be independent. This is likely to change in the future.
1646 */
1647 void
1648 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1649 {
1650 float x0, x1, xdpi, bmwidth;
1652 if (gtk_object_get_data (base, "update"))
1653 return;
1655 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1656 (base, "units"))) {
1657 return;
1658 }
1660 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1662 x0 = sp_export_value_get_px (base, "x0");
1663 x1 = sp_export_value_get_px (base, "x1");
1664 xdpi = sp_export_value_get (base, "xdpi");
1666 // remember xdpi setting
1667 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1669 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1671 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1672 bmwidth = SP_EXPORT_MIN_SIZE;
1673 if (x1 != x0)
1674 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1675 else
1676 xdpi = DPI_BASE;
1677 sp_export_value_set (base, "xdpi", xdpi);
1678 }
1680 sp_export_value_set (base, "bmwidth", bmwidth);
1682 sp_export_set_image_y (base);
1684 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1686 return;
1687 } // end of sp_export_xdpi_value_changed()
1690 /**
1691 \brief A function to change the area that is used for the exported
1692 bitmap.
1693 \param base This is the export dialog
1694 \param x0 Horizontal upper left hand corner of the picture in points
1695 \param y0 Vertical upper left hand corner of the picture in points
1696 \param x1 Horizontal lower right hand corner of the picture in points
1697 \param y1 Vertical lower right hand corner of the picture in points
1699 This function just calls \c sp_export_value_set_px for each of the
1700 parameters that is passed in. This allows for setting them all in
1701 one convient area.
1703 Update is set to suspend all of the other test running while all the
1704 values are being set up. This allows for a performance increase, but
1705 it also means that the wrong type won't be detected with only some of
1706 the values set. After all the values are set everyone is told that
1707 there has been an update.
1708 */
1709 static void
1710 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1711 {
1712 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1713 sp_export_value_set_px (base, "x1", x1);
1714 sp_export_value_set_px (base, "y1", y1);
1715 sp_export_value_set_px (base, "x0", x0);
1716 sp_export_value_set_px (base, "y0", y0);
1717 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1719 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1720 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1722 return;
1723 }
1725 /**
1726 \brief Sets the value of an adjustment
1727 \param base The export dialog
1728 \param key Which adjustment to set
1729 \param val What value to set it to
1731 This function finds the adjustment using the data stored in the
1732 export dialog. After finding the adjustment it then sets
1733 the value of it.
1734 */
1735 static void
1736 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1737 {
1738 GtkAdjustment *adj;
1740 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1742 gtk_adjustment_set_value (adj, val);
1743 }
1745 /**
1746 \brief A function to set a value using the units points
1747 \param base The export dialog
1748 \param key Which value should be set
1749 \param val What the value should be in points
1751 This function first gets the adjustment for the key that is passed
1752 in. It then figures out what units are currently being used in the
1753 dialog. After doing all of that, it then converts the incoming
1754 value and sets the adjustment.
1755 */
1756 static void
1757 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1758 {
1759 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1761 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1763 return;
1764 }
1766 /**
1767 \brief Get the value of an adjustment in the export dialog
1768 \param base The export dialog
1769 \param key Which adjustment is being looked for
1770 \return The value in the specified adjustment
1772 This function gets the adjustment from the data field in the export
1773 dialog. It then grabs the value from the adjustment.
1774 */
1775 static float
1776 sp_export_value_get ( GtkObject *base, const gchar *key )
1777 {
1778 GtkAdjustment *adj;
1780 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1782 return adj->value;
1783 }
1785 /**
1786 \brief Grabs a value in the export dialog and converts the unit
1787 to points
1788 \param base The export dialog
1789 \param key Which value should be returned
1790 \return The value in the adjustment in points
1792 This function, at its most basic, is a call to \c sp_export_value_get
1793 to get the value of the adjustment. It then finds the units that
1794 are being used by looking at the "units" attribute of the export
1795 dialog. Using that it converts the returned value into points.
1796 */
1797 static float
1798 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1799 {
1800 float value = sp_export_value_get(base, key);
1801 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1803 return sp_units_get_pixels (value, *unit);
1804 } // end of sp_export_value_get_px()
1806 /**
1807 \brief This function is called when the filename is changed by
1808 anyone. It resets the virgin bit.
1809 \param object Text entry box
1810 \param data The export dialog
1811 \return None
1813 This function gets called when the text area is modified. It is
1814 looking for the case where the text area is modified from its
1815 original value. In that case it sets the "filename-modified" bit
1816 to TRUE. If the text dialog returns back to the original text, the
1817 bit gets reset. This should stop simple mistakes.
1818 */
1819 static void
1820 sp_export_filename_modified (GtkObject * object, gpointer data)
1821 {
1822 GtkWidget * text_entry = (GtkWidget *)object;
1823 GtkWidget * export_dialog = (GtkWidget *)data;
1825 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1826 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1827 // printf("Modified: FALSE\n");
1828 } else {
1829 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1830 // printf("Modified: TRUE\n");
1831 }
1833 return;
1834 } // end sp_export_filename_modified
1836 /*
1837 Local Variables:
1838 mode:c++
1839 c-file-style:"stroustrup"
1840 c-file-offsets:((innamespace . 0)(inline-open . 0))
1841 indent-tabs-mode:nil
1842 fill-column:99
1843 End:
1844 */
1845 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :