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"
46 #include "selection-chemistry.h"
48 #include "dialog-events.h"
49 #include "../prefs-utils.h"
50 #include "../verbs.h"
51 #include "../interface.h"
53 #include "extension/output.h"
54 #include "extension/db.h"
56 #include "io/sys.h"
58 #include "helper/png-write.h"
61 #define SP_EXPORT_MIN_SIZE 1.0
63 #define DPI_BASE PX_PER_IN
65 #define EXPORT_COORD_PRECISION 3
67 #define MIN_ONSCREEN_DISTANCE 50
69 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
70 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
71 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
73 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
74 GtkObject *base);
76 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
77 GtkObject *base);
79 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
80 GtkObject *base);
82 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
83 GtkObject *base);
85 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
86 GtkObject *base);
88 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
89 GtkObject *base);
91 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
92 GtkObject *base);
94 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
95 Inkscape::Selection *selection,
96 GtkObject *base);
97 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
98 Inkscape::Selection *selection,
99 guint flags,
100 GtkObject *base );
102 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
103 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
104 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
105 static float sp_export_value_get ( GtkObject *base, const gchar *key );
106 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
108 static void sp_export_filename_modified (GtkObject * object, gpointer data);
109 static inline void sp_export_find_default_selection(GtkWidget * dlg);
110 static void sp_export_detect_size(GtkObject * base);
112 static const gchar *prefs_path = "dialogs.export";
114 // these all need to be reinitialized to their defaults during dialog_destroy
115 static GtkWidget *dlg = NULL;
116 static win_data wd;
117 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
118 static gchar * original_name = NULL;
119 static gchar * doc_export_name = NULL;
120 static bool was_empty = TRUE;
122 /** What type of button is being pressed */
123 enum selection_type {
124 SELECTION_PAGE = 0, /**< Export the whole page */
125 SELECTION_DRAWING, /**< Export everything drawn on the page */
126 SELECTION_SELECTION, /**< Export everything that is selected */
127 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
128 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
129 };
131 /** A list of strings that is used both in the preferences, and in the
132 data fields to describe the various values of \c selection_type. */
133 static const char * selection_names[SELECTION_NUMBER_OF] = {
134 "page", "drawing", "selection", "custom"};
136 /** The names on the buttons for the various selection types. */
137 static const char * selection_labels[SELECTION_NUMBER_OF] = {
138 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
140 static void
141 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
142 {
143 sp_signal_disconnect_by_data (INKSCAPE, dlg);
145 wd.win = dlg = NULL;
146 wd.stop = 0;
147 x = -1000; y = -1000; w = 0; h = 0;
148 g_free(original_name);
149 original_name = NULL;
150 g_free(doc_export_name);
151 doc_export_name = NULL;
152 was_empty = TRUE;
154 return;
155 } // end of sp_export_dialog_destroy()
157 /// Called when dialog is closed or inkscape is shut down.
158 static bool
159 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
160 {
162 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
163 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
165 if (x<0) x=0;
166 if (y<0) y=0;
168 prefs_set_int_attribute (prefs_path, "x", x);
169 prefs_set_int_attribute (prefs_path, "y", y);
170 prefs_set_int_attribute (prefs_path, "w", w);
171 prefs_set_int_attribute (prefs_path, "h", h);
173 return FALSE; // which means, go ahead and destroy it
175 } // end of sp_export_dialog_delete()
177 /**
178 \brief Creates a new spin button for the export dialog
179 \param key The name of the spin button
180 \param val A default value for the spin button
181 \param min Minimum value for the spin button
182 \param max Maximum value for the spin button
183 \param step The step size for the spin button
184 \param page Size of the page increment
185 \param us Unit selector that effects this spin button
186 \param t Table to put the spin button in
187 \param x X location in the table \c t to start with
188 \param y Y location in the table \c t to start with
189 \param ll Text to put on the left side of the spin button (optional)
190 \param lr Text to put on the right side of the spin button (optional)
191 \param digits Number of digits to display after the decimal
192 \param sensitive Whether the spin button is sensitive or not
193 \param cb Callback for when this spin button is changed (optional)
194 \param dlg Export dialog the spin button is being placed in
196 */
197 static void
198 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
199 float step, float page, GtkWidget *us,
200 GtkWidget *t, int x, int y,
201 const gchar *ll, const gchar *lr,
202 int digits, unsigned int sensitive,
203 GCallback cb, GtkWidget *dlg )
204 {
205 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
206 gtk_object_set_data (a, "key", key);
207 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
209 if (us) {
210 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
211 GTK_ADJUSTMENT (a) );
212 }
214 int pos = 0;
216 GtkWidget *l = NULL;
218 if (ll) {
220 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
221 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
222 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
223 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
224 gtk_widget_set_sensitive (l, sensitive);
225 pos += 1;
227 }
229 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
230 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
231 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
232 gtk_widget_set_size_request (sb, 80, -1);
233 gtk_widget_set_sensitive (sb, sensitive);
234 pos += 1;
236 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
238 if (lr) {
240 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
241 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
242 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
243 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
244 gtk_widget_set_sensitive (l, sensitive);
245 pos += 1;
247 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
248 }
250 if (cb)
251 gtk_signal_connect (a, "value_changed", cb, dlg);
253 return;
254 } // end of sp_export_spinbutton_new()
257 static Gtk::VBox *
258 sp_export_dialog_area_box (GtkWidget * dlg)
259 {
260 Gtk::VBox* vb = new Gtk::VBox(false, 3);
262 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Export area</b></big>"), Gtk::ALIGN_LEFT);
263 lbl->set_use_markup(true);
264 vb->pack_start(*lbl);
266 /* Units box */
267 Gtk::HBox* unitbox = new Gtk::HBox(false, 0);
268 /* gets added to the vbox later, but the unit selector is needed
269 earlier than that */
271 Gtk::Widget* us = Glib::wrap(sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE));
272 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
273 if (desktop)
274 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us->gobj()), sp_desktop_namedview(desktop)->doc_units);
275 unitbox->pack_end(*us, false, false, 0);
276 Gtk::Label* l = new Gtk::Label(_("Units:"));
277 unitbox->pack_end(*l, false, false, 3);
278 gtk_object_set_data (GTK_OBJECT (dlg), "units", us->gobj());
280 Gtk::HBox* togglebox = new Gtk::HBox(true, 0);
282 Gtk::ToggleButton* b;
283 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
284 b = new Gtk::ToggleButton(_(selection_labels[i]), true);
285 b->set_data("key", GINT_TO_POINTER(i));
286 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b->gobj());
287 togglebox->pack_start(*b, false, true, 0);
288 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
289 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
290 }
292 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
293 G_CALLBACK (sp_export_selection_changed), dlg );
294 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
295 G_CALLBACK (sp_export_selection_modified), dlg );
296 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
297 G_CALLBACK (sp_export_selection_changed), dlg );
299 Gtk::Table* t = new Gtk::Table(2, 6, FALSE);
300 t->set_row_spacings (4);
301 t->set_col_spacings (4);
303 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
304 GTK_WIDGET(t->gobj()), 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
305 G_CALLBACK ( sp_export_area_x_value_changed),
306 dlg );
308 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
309 GTK_WIDGET(t->gobj()), 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
310 G_CALLBACK (sp_export_area_x_value_changed),
311 dlg );
313 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
314 us->gobj(), GTK_WIDGET(t->gobj()), 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
315 G_CALLBACK
316 (sp_export_area_width_value_changed),
317 dlg );
319 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
320 GTK_WIDGET(t->gobj()), 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
321 G_CALLBACK (sp_export_area_y_value_changed),
322 dlg );
324 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us->gobj(),
325 GTK_WIDGET(t->gobj()), 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
326 G_CALLBACK (sp_export_area_y_value_changed),
327 dlg );
329 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
330 us->gobj(), GTK_WIDGET(t->gobj()), 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
331 G_CALLBACK (sp_export_area_height_value_changed),
332 dlg );
334 vb->pack_start(*togglebox, false, false, 3);
335 vb->pack_start(*t, false, false, 0);
336 vb->pack_start(*unitbox, false, false, 0);
338 return vb;
339 } // end of sp_export_dialog_area_box
342 void
343 sp_export_dialog (void)
344 {
345 if (!dlg) {
346 Gtk::VBox* vb;
347 Gtk::HBox* hb;
349 gchar title[500];
350 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
352 dlg = sp_window_new (title, TRUE);
354 if (x == -1000 || y == -1000) {
355 x = prefs_get_int_attribute (prefs_path, "x", 0);
356 y = prefs_get_int_attribute (prefs_path, "y", 0);
357 }
359 if (w ==0 || h == 0) {
360 w = prefs_get_int_attribute (prefs_path, "w", 0);
361 h = prefs_get_int_attribute (prefs_path, "h", 0);
362 }
364 // if (x<0) x=0;
365 // if (y<0) y=0;
367 if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h);
368 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE)))
369 gtk_window_move ((GtkWindow *) dlg, x, y);
370 else
371 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
372 sp_transientize (dlg);
373 wd.win = dlg;
374 wd.stop = 0;
376 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
377 G_CALLBACK (sp_transientize_callback), &wd);
379 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
380 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
382 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
383 G_CALLBACK (sp_export_dialog_destroy), dlg);
385 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
386 G_CALLBACK (sp_export_dialog_delete), dlg);
388 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
389 G_CALLBACK (sp_export_dialog_delete), dlg);
391 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
392 G_CALLBACK (sp_dialog_hide), dlg);
394 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
395 G_CALLBACK (sp_dialog_unhide), dlg);
397 GtkTooltips *tt = gtk_tooltips_new();
399 vb = new Gtk::VBox(false, 3);
400 vb->set_border_width(3);
401 gtk_container_add (GTK_CONTAINER (dlg), GTK_WIDGET(vb->gobj()));
403 /* Export area frame */
404 {
405 Gtk::VBox *area_box = sp_export_dialog_area_box(dlg);
406 area_box->set_border_width(3);
407 vb->pack_start(*area_box, false, false, 0);
408 }
410 /* Bitmap size frame */
411 {
412 Gtk::VBox *size_box = new Gtk::VBox(false, 3);
413 size_box->set_border_width(3);
415 Gtk::Label* lbl = new Gtk::Label(_("<big><b>Bitmap size</b></big>"), Gtk::ALIGN_LEFT);
416 lbl->set_use_markup(true);
417 size_box->pack_start(*lbl, false, false, 0);
418 const int rows = 2;
419 const int cols = 5;
420 const bool homogeneous = false;
421 Gtk::Table *t = new Gtk::Table(rows, cols, homogeneous);
422 t->set_row_spacings (4);
423 t->set_col_spacings (4);
424 size_box->pack_start(*t);
426 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
427 NULL, GTK_WIDGET(t->gobj()), 0, 0,
428 _("_Width:"), _("pixels at"), 0, 1,
429 G_CALLBACK
430 (sp_export_bitmap_width_value_changed),
431 dlg );
433 sp_export_spinbutton_new ( "xdpi",
434 prefs_get_double_attribute
435 ( "dialogs.export.defaultxdpi",
436 "value", DPI_BASE),
437 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 0,
438 NULL, _("dp_i"), 2, 1,
439 G_CALLBACK (sp_export_xdpi_value_changed),
440 dlg );
442 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
443 NULL, GTK_WIDGET(t->gobj()), 0, 1,
444 _("Height:"), _("pixels at"), 0, 1,
445 G_CALLBACK
446 (sp_export_bitmap_height_value_changed),
447 dlg );
449 /** \todo
450 * Needs fixing: there's no way to set ydpi currently, so we use
451 * the defaultxdpi value here, too...
452 */
453 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
454 ( "dialogs.export.defaultxdpi",
455 "value", DPI_BASE),
456 0.01, 100000.0, 0.1, 1.0, NULL, GTK_WIDGET(t->gobj()), 3, 1,
457 NULL, _("dpi"), 2, 0, NULL, dlg );
459 vb->pack_start(*size_box);
460 }
462 /* File entry */
463 {
464 Gtk::VBox* file_box = new Gtk::VBox(false, 3);
465 file_box->set_border_width(3);
467 // true = has mnemonic
468 Gtk::Label *flabel = new Gtk::Label(_("<big><b>_Filename</b></big>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, true);
469 flabel->set_use_markup(true);
470 file_box->pack_start(*flabel, false, false, 0);
472 Gtk::Entry *fe = new Gtk::Entry();
474 /*
475 * set the default filename to be that of the current path + document
476 * with .png extension
477 *
478 * One thing to notice here is that this filename may get
479 * overwritten, but it won't happen here. The filename gets
480 * written into the text field, but then the button to select
481 * the area gets set. In that code the filename can be changed
482 * if there are some with presidence in the document. So, while
483 * this code sets the name first, it may not be the one users
484 * really see.
485 */
486 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
487 {
488 gchar *name;
489 SPDocument * doc = SP_ACTIVE_DOCUMENT;
490 const gchar *uri = SP_DOCUMENT_URI (doc);
491 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
492 const gchar * text_extension = repr->attribute("inkscape:output_extension");
493 Inkscape::Extension::Output * oextension = NULL;
495 if (text_extension != NULL) {
496 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
497 }
499 if (oextension != NULL) {
500 gchar * old_extension = oextension->get_extension();
501 if (g_str_has_suffix(uri, old_extension)) {
502 gchar * uri_copy;
503 gchar * extension_point;
504 gchar * final_name;
506 uri_copy = g_strdup(uri);
507 extension_point = g_strrstr(uri_copy, old_extension);
508 extension_point[0] = '\0';
510 final_name = g_strconcat(uri_copy, ".png", NULL);
511 fe->set_text(final_name);
513 g_free(final_name);
514 g_free(uri_copy);
515 }
516 } else {
517 name = g_strconcat(uri, ".png", NULL);
518 fe->set_text(name);
519 g_free(name);
520 }
522 doc_export_name = g_strdup(fe->get_text().c_str());
523 }
524 g_signal_connect ( G_OBJECT (fe->gobj()), "changed",
525 G_CALLBACK (sp_export_filename_modified), dlg);
527 hb = new Gtk::HBox(FALSE, 5);
529 {
530 // true = has mnemonic
531 Gtk::Button *b = new Gtk::Button();
533 Gtk::HBox* pixlabel = new Gtk::HBox(false, 3);
534 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX),
535 Gtk::ICON_SIZE_BUTTON);
536 pixlabel->pack_start(*im);
538 Gtk::Label *l = new Gtk::Label();
539 l->set_markup_with_mnemonic(_("_Browse..."));
540 pixlabel->pack_start(*l);
542 b->add(*pixlabel);
544 hb->pack_end (*b, false, false, 4);
545 g_signal_connect ( G_OBJECT (b->gobj()), "clicked",
546 G_CALLBACK (sp_export_browse_clicked), NULL );
547 }
549 hb->pack_start (*fe, true, true, 0);
550 file_box->add(*hb);
551 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe->gobj());
552 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
553 original_name = g_strdup(fe->get_text().c_str());
554 // pressing enter in the filename field is the same as clicking export:
555 g_signal_connect ( G_OBJECT (fe->gobj()), "activate",
556 G_CALLBACK (sp_export_export_clicked), dlg );
557 // focus is in the filename initially:
558 fe->grab_focus();
560 // mnemonic in frame label moves focus to filename:
561 flabel->set_mnemonic_widget(*fe);
563 vb->pack_start(*file_box);
564 }
566 /* Buttons */
567 Gtk::HButtonBox* bb = new Gtk::HButtonBox(Gtk::BUTTONBOX_END);
568 bb->set_border_width(3);
570 {
571 Gtk::Button *b = new Gtk::Button();
572 Gtk::HBox* image_label = new Gtk::HBox(false, 3);
573 Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::APPLY),
574 Gtk::ICON_SIZE_BUTTON);
575 image_label->pack_start(*im);
577 Gtk::Label *l = new Gtk::Label();
578 l->set_markup_with_mnemonic(_("_Export"));
579 image_label->pack_start(*l);
581 b->add(*image_label);
582 gtk_tooltips_set_tip (tt, GTK_WIDGET(b->gobj()), _("Export the bitmap file with these settings"), NULL);
583 gtk_signal_connect ( GTK_OBJECT (b->gobj()), "clicked",
584 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
585 bb->pack_end(*b, false, false, 0);
586 }
588 vb->pack_end(*bb, false, false, 0);
589 vb->show_all();
591 } // end of if (!dlg)
593 sp_export_find_default_selection(dlg);
595 gtk_window_present ((GtkWindow *) dlg);
597 return;
598 } // end of sp_export_dialog()
600 static inline void
601 sp_export_find_default_selection(GtkWidget * dlg)
602 {
603 selection_type key = SELECTION_NUMBER_OF;
605 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
606 key = SELECTION_SELECTION;
607 }
609 /* Try using the preferences */
610 if (key == SELECTION_NUMBER_OF) {
611 const gchar *what = NULL;
612 int i = SELECTION_NUMBER_OF;
614 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
616 if (what != NULL) {
617 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
618 if (!strcmp (what, selection_names[i])) {
619 break;
620 }
621 }
622 }
624 key = (selection_type)i;
625 }
627 if (key == SELECTION_NUMBER_OF) {
628 key = SELECTION_SELECTION;
629 }
631 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
632 selection_names[key]);
633 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
635 return;
636 }
639 /**
640 * \brief If selection changed or a different document activated, we must
641 * recalculate any chosen areas
642 *
643 */
644 static void
645 sp_export_selection_changed ( Inkscape::Application *inkscape,
646 Inkscape::Selection *selection,
647 GtkObject *base )
648 {
649 selection_type current_key;
650 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
652 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
653 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
654 was_empty) {
655 gtk_toggle_button_set_active
656 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
657 TRUE );
658 }
659 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
661 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
663 if (inkscape &&
664 SP_IS_INKSCAPE (inkscape) &&
665 selection &&
666 SELECTION_CUSTOM != current_key) {
667 GtkToggleButton * button;
668 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
669 sp_export_area_toggled(button, base);
670 } // end of if()
672 return;
673 } // end of sp_export_selection_changed()
675 static void
676 sp_export_selection_modified ( Inkscape::Application *inkscape,
677 Inkscape::Selection *selection,
678 guint flags,
679 GtkObject *base )
680 {
681 selection_type current_key;
682 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
684 switch (current_key) {
685 case SELECTION_DRAWING:
686 if ( SP_ACTIVE_DESKTOP ) {
687 SPDocument *doc;
688 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
689 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
691 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
692 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
693 sp_export_set_area (base, bbox.min()[NR::X],
694 bbox.min()[NR::Y],
695 bbox.max()[NR::X],
696 bbox.max()[NR::Y]);
697 }
698 }
699 break;
700 case SELECTION_SELECTION:
701 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
702 NRRect bbox;
703 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
704 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
705 }
706 break;
707 default:
708 /* Do nothing for page or for custom */
709 break;
710 }
712 return;
713 }
715 /// Called when one of the selection buttons was toggled.
716 static void
717 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
718 {
719 if (gtk_object_get_data (base, "update"))
720 return;
722 selection_type key, old_key;
723 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
724 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
726 /* Ignore all "turned off" events unless we're the only active button */
727 if (!gtk_toggle_button_get_active (tb) ) {
729 /* Don't let the current selection be deactived - but rerun the
730 activate to allow the user to renew the values */
731 if (key == old_key) {
732 gtk_toggle_button_set_active ( tb, TRUE );
733 }
735 return;
736 }
738 /* Turn off the currently active button unless it's us */
739 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
741 if (old_key != key) {
742 gtk_toggle_button_set_active
743 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
744 FALSE );
745 }
747 if ( SP_ACTIVE_DESKTOP )
748 {
749 SPDocument *doc;
750 NR::Rect bbox;
751 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
753 /* Notice how the switch is used to 'fall through' here to get
754 various backups. If you modify this without noticing you'll
755 probabaly screw something up. */
756 switch (key) {
757 case SELECTION_SELECTION:
758 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
759 {
760 bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
761 /* Only if there is a selection that we can set
762 do we break, otherwise we fall through to the
763 drawing */
764 // std::cout << "Using selection: SELECTION" << std::endl;
765 key = SELECTION_SELECTION;
766 break;
767 }
768 case SELECTION_DRAWING:
769 /** \todo
770 * This returns wrong values if the document has a viewBox.
771 */
772 bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
774 /* If the drawing is valid, then we'll use it and break
775 otherwise we drop through to the page settings */
776 if (!(bbox.min()[NR::X] > bbox.max()[NR::X] &&
777 bbox.min()[NR::Y] > bbox.max()[NR::Y])) {
778 // std::cout << "Using selection: DRAWING" << std::endl;
779 key = SELECTION_DRAWING;
780 break;
781 }
782 case SELECTION_PAGE:
783 bbox = NR::Rect(NR::Point(0.0, 0.0),
784 NR::Point(sp_document_width(doc), sp_document_height(doc))
785 );
787 // std::cout << "Using selection: PAGE" << std::endl;
788 key = SELECTION_PAGE;
789 break;
790 case SELECTION_CUSTOM:
791 default:
792 break;
793 } // switch
795 // remember area setting
796 prefs_set_string_attribute ( "dialogs.export.exportarea",
797 "value", selection_names[key]);
799 if (key != SELECTION_CUSTOM) {
800 sp_export_set_area (base, bbox.min()[NR::X],
801 bbox.min()[NR::Y],
802 bbox.max()[NR::X],
803 bbox.max()[NR::Y]);
804 }
806 } // end of if ( SP_ACTIVE_DESKTOP )
809 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
810 GtkWidget * file_entry;
811 const gchar * filename = NULL;
812 float xdpi = 0.0, ydpi = 0.0;
814 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
816 switch (key) {
817 case SELECTION_PAGE:
818 case SELECTION_DRAWING: {
819 SPDocument * doc = SP_ACTIVE_DOCUMENT;
820 sp_document_get_export_hints (doc, &filename, &xdpi, &ydpi);
822 if (filename == NULL) {
823 if (doc_export_name != NULL) {
824 filename = g_strdup(doc_export_name);
825 } else {
826 filename = g_strdup("");
827 }
828 }
829 break;
830 }
831 case SELECTION_SELECTION:
832 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
834 sp_selection_get_export_hints (sp_desktop_selection(SP_ACTIVE_DESKTOP), &filename, &xdpi, &ydpi);
836 /* If we still don't have a filename -- let's build
837 one that's nice */
838 if (filename == NULL) {
839 const gchar * id = NULL;
840 const GSList * reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
841 for(; reprlst != NULL; reprlst = reprlst->next) {
842 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
843 if (repr->attribute("id")) {
844 id = repr->attribute("id");
845 break;
846 }
847 }
848 if (id == NULL) /* This should never happen */
849 id = "bitmap";
851 gchar * directory = NULL;
852 const gchar * file_entry_text;
854 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
855 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
856 // std::cout << "Directory from dialog" << std::endl;
857 directory = g_dirname(file_entry_text);
858 }
860 if (directory == NULL) {
861 /* Grab document directory */
862 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
863 // std::cout << "Directory from document" << std::endl;
864 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
865 }
866 }
868 if (directory == NULL) {
869 // std::cout << "Home Directory" << std::endl;
870 directory = homedir_path(NULL);
871 }
873 gchar * id_ext = g_strconcat(id, ".png", NULL);
874 filename = g_build_filename(directory, id_ext, NULL);
875 g_free(directory);
876 g_free(id_ext);
877 }
878 }
879 break;
880 case SELECTION_CUSTOM:
881 default:
882 break;
883 }
885 if (filename != NULL) {
886 g_free(original_name);
887 original_name = g_strdup(filename);
888 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
889 }
891 if (xdpi != 0.0) {
892 sp_export_value_set(base, "xdpi", xdpi);
893 }
895 /* These can't be separate, and setting x sets y, so for
896 now setting this is disabled. Hopefully it won't be in
897 the future */
898 if (FALSE && ydpi != 0.0) {
899 sp_export_value_set(base, "ydpi", ydpi);
900 }
901 }
903 return;
904 } // end of sp_export_area_toggled()
906 /// Called when dialog is deleted
907 static gint
908 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
909 {
910 g_object_set_data (base, "cancel", (gpointer) 1);
911 return TRUE;
912 } // end of sp_export_progress_delete()
914 /// Called when progress is cancelled
915 static void
916 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
917 {
918 g_object_set_data (base, "cancel", (gpointer) 1);
919 } // end of sp_export_progress_cancel()
921 /// Called for every progress iteration
922 static unsigned int
923 sp_export_progress_callback (float value, void *data)
924 {
925 GtkWidget *prg;
926 int evtcount;
928 if (g_object_get_data ((GObject *) data, "cancel"))
929 return FALSE;
931 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
932 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
934 evtcount = 0;
935 while ((evtcount < 16) && gdk_events_pending ()) {
936 gtk_main_iteration_do (FALSE);
937 evtcount += 1;
938 }
940 gtk_main_iteration_do (FALSE);
942 return TRUE;
944 } // end of sp_export_progress_callback()
946 /// Called when export button is clicked
947 static void
948 sp_export_export_clicked (GtkButton *button, GtkObject *base)
949 {
950 if (!SP_ACTIVE_DESKTOP) return;
952 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
953 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
955 float const x0 = sp_export_value_get_px(base, "x0");
956 float const y0 = sp_export_value_get_px(base, "y0");
957 float const x1 = sp_export_value_get_px(base, "x1");
958 float const y1 = sp_export_value_get_px(base, "y1");
959 float const xdpi = sp_export_value_get(base, "xdpi");
960 float const ydpi = sp_export_value_get(base, "ydpi");
961 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
962 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
964 if (filename == NULL || *filename == '\0') {
965 sp_ui_error_dialog(_("You have to enter a filename"));
966 return;
967 }
969 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
970 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
971 return;
972 }
974 gchar *dirname = g_dirname(filename);
975 if ( dirname == NULL
976 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
977 {
978 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
979 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
980 safeDir);
981 sp_ui_error_dialog(error);
982 g_free(safeDir);
983 g_free(error);
984 g_free(dirname);
985 return;
986 }
987 g_free(dirname);
989 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
990 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
991 char *fn;
992 gchar *text;
994 dlg = gtk_dialog_new ();
995 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
996 prg = gtk_progress_bar_new ();
997 sp_transientize (dlg);
998 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
999 g_object_set_data ((GObject *) base, "progress", prg);
1000 fn = g_path_get_basename (filename);
1001 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1002 fn, width, height);
1003 g_free (fn);
1004 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1005 g_free (text);
1006 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1007 GTK_PROGRESS_LEFT_TO_RIGHT);
1008 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1009 prg, FALSE, FALSE, 4 );
1010 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1011 GTK_STOCK_CANCEL,
1012 GTK_RESPONSE_CANCEL );
1014 g_signal_connect ( (GObject *) dlg, "delete_event",
1015 (GCallback) sp_export_progress_delete, base);
1016 g_signal_connect ( (GObject *) btn, "clicked",
1017 (GCallback) sp_export_progress_cancel, base);
1018 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1019 gtk_widget_show_all (dlg);
1021 /* Do export */
1022 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1023 x0, y0, x1, y1, width, height, xdpi, ydpi,
1024 nv->pagecolor,
1025 sp_export_progress_callback, base)) {
1026 gchar * error;
1027 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1028 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1029 sp_ui_error_dialog(error);
1030 g_free(safeFile);
1031 g_free(error);
1032 }
1034 /* Reset the filename so that it can be changed again by changing
1035 selections and all that */
1036 g_free(original_name);
1037 original_name = g_strdup(filename);
1038 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1040 gtk_widget_destroy (dlg);
1041 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1043 /* Setup the values in the document */
1044 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1045 case SELECTION_PAGE:
1046 case SELECTION_DRAWING: {
1047 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1048 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1049 bool modified = false;
1050 const gchar * temp_string;
1052 bool saved = sp_document_get_undo_sensitive(doc);
1053 sp_document_set_undo_sensitive(doc, false);
1055 temp_string = repr->attribute("inkscape:export-filename");
1056 if (temp_string == NULL || strcmp(temp_string, filename)) {
1057 repr->setAttribute("inkscape:export-filename", filename);
1058 modified = true;
1059 }
1060 temp_string = repr->attribute("inkscape:export-xdpi");
1061 if (temp_string == NULL || xdpi != atof(temp_string)) {
1062 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1063 modified = true;
1064 }
1065 temp_string = repr->attribute("inkscape:export-ydpi");
1066 if (temp_string == NULL || xdpi != atof(temp_string)) {
1067 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1068 modified = true;
1069 }
1071 if (modified)
1072 repr->setAttribute("sodipodi:modified", "TRUE");
1073 sp_document_set_undo_sensitive(doc, saved);
1074 break;
1075 }
1076 case SELECTION_SELECTION: {
1077 const GSList * reprlst;
1078 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1079 bool modified = false;
1081 bool saved = sp_document_get_undo_sensitive(doc);
1082 sp_document_set_undo_sensitive(doc, false);
1083 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1085 for(; reprlst != NULL; reprlst = reprlst->next) {
1086 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1087 const gchar * temp_string;
1089 if (repr->attribute("id") == NULL ||
1090 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1091 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1092 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1093 temp_string = repr->attribute("inkscape:export-filename");
1094 if (temp_string == NULL || strcmp(temp_string, filename)) {
1095 repr->setAttribute("inkscape:export-filename", filename);
1096 modified = true;
1097 }
1098 }
1099 temp_string = repr->attribute("inkscape:export-xdpi");
1100 if (temp_string == NULL || xdpi != atof(temp_string)) {
1101 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1102 modified = true;
1103 }
1104 temp_string = repr->attribute("inkscape:export-ydpi");
1105 if (temp_string == NULL || xdpi != atof(temp_string)) {
1106 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1107 modified = true;
1108 }
1109 }
1111 if (modified) {
1112 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1113 repr->setAttribute("sodipodi:modified", "TRUE");
1114 }
1116 sp_document_set_undo_sensitive(doc, saved);
1117 break;
1118 }
1119 default:
1120 break;
1121 }
1124 return;
1125 } // end of sp_export_export_clicked()
1127 /// Called when Browse button is clicked
1128 static void
1129 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1130 {
1131 GtkWidget *fs, *fe;
1132 const gchar *filename;
1134 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1135 NULL,
1136 GTK_FILE_CHOOSER_ACTION_SAVE,
1137 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1138 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1139 NULL );
1141 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1143 sp_transientize (fs);
1145 gtk_window_set_modal(GTK_WINDOW (fs), true);
1147 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1149 if (*filename == '\0') {
1150 filename = homedir_path(NULL);
1151 }
1153 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1155 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1156 {
1157 gchar *file;
1159 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1160 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1161 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1162 g_free(utf8file);
1164 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1165 g_free(file);
1166 }
1168 gtk_widget_destroy (fs);
1170 return;
1171 } // end of sp_export_browse_clicked()
1173 // TODO: Move this to nr-rect-fns.h.
1174 static bool
1175 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1176 {
1177 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1178 return (
1179 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1180 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1181 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1182 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1183 );
1184 }
1186 /**
1187 \brief This function is used to detect the current selection setting
1188 based on the values in the x0, y0, x1 and y0 fields.
1189 \param base The export dialog itself
1191 One of the most confusing parts of this function is why the array
1192 is built at the beginning. What needs to happen here is that we
1193 should always check the current selection to see if it is the valid
1194 one. While this is a performance improvement it is also a usability
1195 one during the cases where things like selections and drawings match
1196 size. This way buttons change less 'randomly' (atleast in the eyes
1197 of the user). To do this an array is built where the current selection
1198 type is placed first, and then the others in an order from smallest
1199 to largest (this can be configured by reshuffling \c test_order).
1201 All of the values in this function are rounded to two decimal places
1202 because that is what is shown to the user. While everything is kept
1203 more accurate than that, the user can't control more acurrate than
1204 that, so for this to work for them - it needs to check on that level
1205 of accuracy.
1207 \todo finish writing this up
1208 */
1209 static void
1210 sp_export_detect_size(GtkObject * base) {
1211 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1212 selection_type this_test[SELECTION_NUMBER_OF + 1];
1213 selection_type key = SELECTION_NUMBER_OF;
1215 NR::Point x(sp_export_value_get_px (base, "x0"),
1216 sp_export_value_get_px (base, "y0"));
1217 NR::Point y(sp_export_value_get_px (base, "x1"),
1218 sp_export_value_get_px (base, "y1"));
1219 NR::Rect current_bbox(x, y);
1220 //std::cout << "Current " << current_bbox;
1222 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1223 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1224 this_test[i + 1] = test_order[i];
1225 }
1227 for (int i = 0;
1228 i < SELECTION_NUMBER_OF + 1 &&
1229 key == SELECTION_NUMBER_OF &&
1230 SP_ACTIVE_DESKTOP != NULL;
1231 i++) {
1232 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1233 switch (this_test[i]) {
1234 case SELECTION_SELECTION:
1235 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1236 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1238 //std::cout << "Selection " << bbox;
1239 if (sp_export_bbox_equal(bbox,current_bbox)) {
1240 key = SELECTION_SELECTION;
1241 }
1242 }
1243 break;
1244 case SELECTION_DRAWING: {
1245 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1247 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1249 // std::cout << "Drawing " << bbox2;
1250 if (sp_export_bbox_equal(bbox,current_bbox)) {
1251 key = SELECTION_DRAWING;
1252 }
1253 break;
1254 }
1256 case SELECTION_PAGE: {
1257 SPDocument *doc;
1259 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1261 NR::Point x(0.0, 0.0);
1262 NR::Point y(sp_document_width(doc),
1263 sp_document_height(doc));
1264 NR::Rect bbox(x, y);
1266 // std::cout << "Page " << bbox;
1267 if (sp_export_bbox_equal(bbox,current_bbox)) {
1268 key = SELECTION_PAGE;
1269 }
1271 break;
1272 }
1273 default:
1274 break;
1275 }
1276 }
1277 // std::cout << std::endl;
1279 if (key == SELECTION_NUMBER_OF) {
1280 key = SELECTION_CUSTOM;
1281 }
1283 /* We're now using a custom size, not a fixed one */
1284 /* printf("Detecting state: %s\n", selection_names[key]); */
1285 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1286 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1287 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1288 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1290 return;
1291 } /* sp_export_detect_size */
1293 /// Called when area x0 value is changed
1294 static void
1295 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1296 {
1297 float x0, x1, xdpi, width;
1299 if (gtk_object_get_data (base, "update"))
1300 return;
1302 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1303 (base, "units")))
1304 {
1305 return;
1306 }
1308 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1310 x0 = sp_export_value_get_px (base, "x0");
1311 x1 = sp_export_value_get_px (base, "x1");
1312 xdpi = sp_export_value_get (base, "xdpi");
1314 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1316 if (width < SP_EXPORT_MIN_SIZE) {
1317 const gchar *key;
1318 width = SP_EXPORT_MIN_SIZE;
1319 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1321 if (!strcmp (key, "x0")) {
1322 x1 = x0 + width * DPI_BASE / xdpi;
1323 sp_export_value_set_px (base, "x1", x1);
1324 } else {
1325 x0 = x1 - width * DPI_BASE / xdpi;
1326 sp_export_value_set_px (base, "x0", x0);
1327 }
1328 }
1330 sp_export_value_set_px (base, "width", x1 - x0);
1331 sp_export_value_set (base, "bmwidth", width);
1333 sp_export_detect_size(base);
1335 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1337 return;
1338 } // end of sp_export_area_x_value_changed()
1340 /// Called when area y0 value is changed.
1341 static void
1342 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1343 {
1344 float y0, y1, ydpi, height;
1346 if (gtk_object_get_data (base, "update"))
1347 return;
1349 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1350 (base, "units")))
1351 {
1352 return;
1353 }
1355 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1357 y0 = sp_export_value_get_px (base, "y0");
1358 y1 = sp_export_value_get_px (base, "y1");
1359 ydpi = sp_export_value_get (base, "ydpi");
1361 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1363 if (height < SP_EXPORT_MIN_SIZE) {
1364 const gchar *key;
1365 height = SP_EXPORT_MIN_SIZE;
1366 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1367 if (!strcmp (key, "y0")) {
1368 y1 = y0 + height * DPI_BASE / ydpi;
1369 sp_export_value_set_px (base, "y1", y1);
1370 } else {
1371 y0 = y1 - height * DPI_BASE / ydpi;
1372 sp_export_value_set_px (base, "y0", y0);
1373 }
1374 }
1376 sp_export_value_set_px (base, "height", y1 - y0);
1377 sp_export_value_set (base, "bmheight", height);
1379 sp_export_detect_size(base);
1381 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1383 return;
1384 } // end of sp_export_area_y_value_changed()
1386 /// Called when x1-x0 or area width is changed
1387 static void
1388 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1389 {
1390 float x0, x1, xdpi, width, bmwidth;
1392 if (gtk_object_get_data (base, "update"))
1393 return;
1395 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1396 (base, "units"))) {
1397 return;
1398 }
1400 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1402 x0 = sp_export_value_get_px (base, "x0");
1403 x1 = sp_export_value_get_px (base, "x1");
1404 xdpi = sp_export_value_get (base, "xdpi");
1405 width = sp_export_value_get_px (base, "width");
1406 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1408 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1410 bmwidth = SP_EXPORT_MIN_SIZE;
1411 width = bmwidth * DPI_BASE / xdpi;
1412 sp_export_value_set_px (base, "width", width);
1413 }
1415 sp_export_value_set_px (base, "x1", x0 + width);
1416 sp_export_value_set (base, "bmwidth", bmwidth);
1418 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1420 return;
1421 } // end of sp_export_area_width_value_changed()
1423 /// Called when y1-y0 or area height is changed.
1424 static void
1425 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1426 {
1428 float y0, y1, ydpi, height, bmheight;
1430 if (gtk_object_get_data (base, "update"))
1431 return;
1433 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1434 (base, "units"))) {
1435 return;
1436 }
1438 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1440 y0 = sp_export_value_get_px (base, "y0");
1441 y1 = sp_export_value_get_px (base, "y1");
1442 ydpi = sp_export_value_get (base, "ydpi");
1443 height = sp_export_value_get_px (base, "height");
1444 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1446 if (bmheight < SP_EXPORT_MIN_SIZE) {
1447 bmheight = SP_EXPORT_MIN_SIZE;
1448 height = bmheight * DPI_BASE / ydpi;
1449 sp_export_value_set_px (base, "height", height);
1450 }
1452 sp_export_value_set_px (base, "y1", y0 + height);
1453 sp_export_value_set (base, "bmheight", bmheight);
1455 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1457 return;
1458 } // end of sp_export_area_height_value_changed()
1460 /**
1461 \brief A function to set the ydpi
1462 \param base The export dialog
1464 This function grabs all of the y values and then figures out the
1465 new bitmap size based on the changing dpi value. The dpi value is
1466 gotten from the xdpi setting as these can not currently be independent.
1467 */
1468 static void
1469 sp_export_set_image_y (GtkObject *base)
1470 {
1471 float y0, y1, xdpi;
1473 y0 = sp_export_value_get_px (base, "y0");
1474 y1 = sp_export_value_get_px (base, "y1");
1475 xdpi = sp_export_value_get (base, "xdpi");
1477 sp_export_value_set (base, "ydpi", xdpi);
1478 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1480 return;
1481 } // end of sp_export_set_image_y()
1483 /**
1484 \brief A function to set the xdpi
1485 \param base The export dialog
1487 This function grabs all of the x values and then figures out the
1488 new bitmap size based on the changing dpi value. The dpi value is
1489 gotten from the xdpi setting as these can not currently be independent.
1490 */
1491 static void
1492 sp_export_set_image_x (GtkObject *base)
1493 {
1494 float x0, x1, xdpi;
1496 x0 = sp_export_value_get_px (base, "x0");
1497 x1 = sp_export_value_get_px (base, "x1");
1498 xdpi = sp_export_value_get (base, "xdpi");
1500 sp_export_value_set (base, "ydpi", xdpi);
1501 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1503 return;
1504 } // end of sp_export_set_image_x()
1506 /// Called when pixel width is changed
1507 static void
1508 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1509 {
1510 float x0, x1, bmwidth, xdpi;
1512 if (gtk_object_get_data (base, "update"))
1513 return;
1515 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1516 (base, "units"))) {
1517 return;
1518 }
1520 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1522 x0 = sp_export_value_get_px (base, "x0");
1523 x1 = sp_export_value_get_px (base, "x1");
1524 bmwidth = sp_export_value_get (base, "bmwidth");
1526 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1527 bmwidth = SP_EXPORT_MIN_SIZE;
1528 sp_export_value_set (base, "bmwidth", bmwidth);
1529 }
1531 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1532 sp_export_value_set (base, "xdpi", xdpi);
1534 sp_export_set_image_y (base);
1536 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1538 return;
1539 } // end of sp_export_bitmap_width_value_changed()
1541 /// Called when pixel height is changed
1542 static void
1543 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1544 {
1545 float y0, y1, bmheight, 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 y0 = sp_export_value_get_px (base, "y0");
1558 y1 = sp_export_value_get_px (base, "y1");
1559 bmheight = sp_export_value_get (base, "bmheight");
1561 if (bmheight < SP_EXPORT_MIN_SIZE) {
1562 bmheight = SP_EXPORT_MIN_SIZE;
1563 sp_export_value_set (base, "bmheight", bmheight);
1564 }
1566 xdpi = bmheight * DPI_BASE / (y1 - y0);
1567 sp_export_value_set (base, "xdpi", xdpi);
1569 sp_export_set_image_x (base);
1571 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1573 return;
1574 } // end of sp_export_bitmap_width_value_changed()
1576 /**
1577 \brief A function to adjust the bitmap width when the xdpi value changes
1578 \param adj The adjustment that was changed
1579 \param base The export dialog itself
1581 The first thing this function checks is to see if we are doing an
1582 update. If we are, this function just returns because there is another
1583 instance of it that will handle everything for us. If there is a
1584 units change, we also assume that everyone is being updated appropriately
1585 and there is nothing for us to do.
1587 If we're the highest level function, we set the update flag, and
1588 continue on our way.
1590 All of the values are grabbed using the \c sp_export_value_get functions
1591 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1592 xdpi value is saved in the preferences for the next time the dialog
1593 is opened. (does the selection dpi need to be set here?)
1595 A check is done to to ensure that we aren't outputing an invalid width,
1596 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1597 changed to make it valid.
1599 After all of this the bitmap width is changed.
1601 We also change the ydpi. This is a temporary hack as these can not
1602 currently be independent. This is likely to change in the future.
1603 */
1604 void
1605 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1606 {
1607 float x0, x1, xdpi, bmwidth;
1609 if (gtk_object_get_data (base, "update"))
1610 return;
1612 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1613 (base, "units"))) {
1614 return;
1615 }
1617 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1619 x0 = sp_export_value_get_px (base, "x0");
1620 x1 = sp_export_value_get_px (base, "x1");
1621 xdpi = sp_export_value_get (base, "xdpi");
1623 // remember xdpi setting
1624 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1626 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1628 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1629 bmwidth = SP_EXPORT_MIN_SIZE;
1630 if (x1 != x0)
1631 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1632 else
1633 xdpi = DPI_BASE;
1634 sp_export_value_set (base, "xdpi", xdpi);
1635 }
1637 sp_export_value_set (base, "bmwidth", bmwidth);
1639 sp_export_set_image_y (base);
1641 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1643 return;
1644 } // end of sp_export_xdpi_value_changed()
1647 /**
1648 \brief A function to change the area that is used for the exported
1649 bitmap.
1650 \param base This is the export dialog
1651 \param x0 Horizontal upper left hand corner of the picture in points
1652 \param y0 Vertical upper left hand corner of the picture in points
1653 \param x1 Horizontal lower right hand corner of the picture in points
1654 \param y1 Vertical lower right hand corner of the picture in points
1656 This function just calls \c sp_export_value_set_px for each of the
1657 parameters that is passed in. This allows for setting them all in
1658 one convient area.
1660 Update is set to suspend all of the other test running while all the
1661 values are being set up. This allows for a performance increase, but
1662 it also means that the wrong type won't be detected with only some of
1663 the values set. After all the values are set everyone is told that
1664 there has been an update.
1665 */
1666 static void
1667 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1668 {
1669 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1670 sp_export_value_set_px (base, "x1", x1);
1671 sp_export_value_set_px (base, "y1", y1);
1672 sp_export_value_set_px (base, "x0", x0);
1673 sp_export_value_set_px (base, "y0", y0);
1674 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1676 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1677 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1679 return;
1680 }
1682 /**
1683 \brief Sets the value of an adjustment
1684 \param base The export dialog
1685 \param key Which adjustment to set
1686 \param val What value to set it to
1688 This function finds the adjustment using the data stored in the
1689 export dialog. After finding the adjustment it then sets
1690 the value of it.
1691 */
1692 static void
1693 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1694 {
1695 GtkAdjustment *adj;
1697 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1699 gtk_adjustment_set_value (adj, val);
1700 }
1702 /**
1703 \brief A function to set a value using the units points
1704 \param base The export dialog
1705 \param key Which value should be set
1706 \param val What the value should be in points
1708 This function first gets the adjustment for the key that is passed
1709 in. It then figures out what units are currently being used in the
1710 dialog. After doing all of that, it then converts the incoming
1711 value and sets the adjustment.
1712 */
1713 static void
1714 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1715 {
1716 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1718 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1720 return;
1721 }
1723 /**
1724 \brief Get the value of an adjustment in the export dialog
1725 \param base The export dialog
1726 \param key Which adjustment is being looked for
1727 \return The value in the specified adjustment
1729 This function gets the adjustment from the data field in the export
1730 dialog. It then grabs the value from the adjustment.
1731 */
1732 static float
1733 sp_export_value_get ( GtkObject *base, const gchar *key )
1734 {
1735 GtkAdjustment *adj;
1737 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1739 return adj->value;
1740 }
1742 /**
1743 \brief Grabs a value in the export dialog and converts the unit
1744 to points
1745 \param base The export dialog
1746 \param key Which value should be returned
1747 \return The value in the adjustment in points
1749 This function, at its most basic, is a call to \c sp_export_value_get
1750 to get the value of the adjustment. It then finds the units that
1751 are being used by looking at the "units" attribute of the export
1752 dialog. Using that it converts the returned value into points.
1753 */
1754 static float
1755 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1756 {
1757 float value = sp_export_value_get(base, key);
1758 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1760 return sp_units_get_pixels (value, *unit);
1761 } // end of sp_export_value_get_px()
1763 /**
1764 \brief This function is called when the filename is changed by
1765 anyone. It resets the virgin bit.
1766 \param object Text entry box
1767 \param data The export dialog
1768 \return None
1770 This function gets called when the text area is modified. It is
1771 looking for the case where the text area is modified from its
1772 original value. In that case it sets the "filename-modified" bit
1773 to TRUE. If the text dialog returns back to the original text, the
1774 bit gets reset. This should stop simple mistakes.
1775 */
1776 static void
1777 sp_export_filename_modified (GtkObject * object, gpointer data)
1778 {
1779 GtkWidget * text_entry = (GtkWidget *)object;
1780 GtkWidget * export_dialog = (GtkWidget *)data;
1782 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1783 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1784 // printf("Modified: FALSE\n");
1785 } else {
1786 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1787 // printf("Modified: TRUE\n");
1788 }
1790 return;
1791 } // end sp_export_filename_modified
1793 /*
1794 Local Variables:
1795 mode:c++
1796 c-file-style:"stroustrup"
1797 c-file-offsets:((innamespace . 0)(inline-open . 0))
1798 indent-tabs-mode:nil
1799 fill-column:99
1800 End:
1801 */
1802 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :