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