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>
24 #include <glibmm/i18n.h>
25 #include "helper/unit-menu.h"
26 #include "helper/units.h"
27 #include "unit-constants.h"
28 #include "helper/window.h"
29 #include "inkscape-private.h"
30 #include "document.h"
31 #include "desktop-handles.h"
32 #include "sp-item.h"
33 #include "selection.h"
34 #include "file.h"
35 #include "macros.h"
36 #include "sp-namedview.h"
38 #include "dialog-events.h"
39 #include "../prefs-utils.h"
40 #include "../verbs.h"
41 #include "../interface.h"
43 #include "extension/output.h"
44 #include "extension/db.h"
46 #include "io/sys.h"
49 #define SP_EXPORT_MIN_SIZE 1.0
51 #define DPI_BASE PX_PER_IN
53 #define EXPORT_COORD_PRECISION 3
55 static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base );
56 static void sp_export_export_clicked ( GtkButton *button, GtkObject *base );
57 static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata );
59 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
60 GtkObject *base);
62 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
63 GtkObject *base);
65 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
66 GtkObject *base);
68 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
69 GtkObject *base);
71 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
72 GtkObject *base);
74 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
75 GtkObject *base);
77 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
78 GtkObject *base);
80 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
81 Inkscape::Selection *selection,
82 GtkObject *base);
83 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
84 Inkscape::Selection *selection,
85 guint flags,
86 GtkObject *base );
88 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
89 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
90 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
91 static float sp_export_value_get ( GtkObject *base, const gchar *key );
92 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
94 static void sp_export_filename_modified (GtkObject * object, gpointer data);
95 static inline void sp_export_find_default_selection(GtkWidget * dlg);
96 static void sp_export_detect_size(GtkObject * base);
98 static const gchar *prefs_path = "dialogs.export";
100 // these all need to be reinitialized to their defaults during dialog_destroy
101 static GtkWidget *dlg = NULL;
102 static win_data wd;
103 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
104 static gchar * original_name = NULL;
105 static gchar * doc_export_name = NULL;
106 static bool was_empty = TRUE;
108 /** What type of button is being pressed */
109 enum selection_type {
110 SELECTION_PAGE = 0, /**< Export the whole page */
111 SELECTION_DRAWING, /**< Export everything drawn on the page */
112 SELECTION_SELECTION, /**< Export everything that is selected */
113 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
114 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
115 };
117 /** A list of strings that is used both in the preferences, and in the
118 data fields to describe the various values of \c selection_type. */
119 static const char * selection_names[SELECTION_NUMBER_OF] = {
120 "page", "drawing", "selection", "custom"};
122 /** The names on the buttons for the various selection types. */
123 static const char * selection_labels[SELECTION_NUMBER_OF] = {
124 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
126 static void
127 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
128 {
129 sp_signal_disconnect_by_data (INKSCAPE, dlg);
131 wd.win = dlg = NULL;
132 wd.stop = 0;
133 x = -1000; y = -1000; w = 0; h = 0;
134 g_free(original_name);
135 original_name = NULL;
136 g_free(doc_export_name);
137 doc_export_name = NULL;
138 was_empty = TRUE;
140 return;
141 } // end of sp_export_dialog_destroy()
143 /// Called when dialog is closed or inkscape is shut down.
144 static bool
145 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
146 {
148 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
149 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
151 if (x<0) x=0;
152 if (y<0) y=0;
154 prefs_set_int_attribute (prefs_path, "x", x);
155 prefs_set_int_attribute (prefs_path, "y", y);
156 prefs_set_int_attribute (prefs_path, "w", w);
157 prefs_set_int_attribute (prefs_path, "h", h);
159 return FALSE; // which means, go ahead and destroy it
161 } // end of sp_export_dialog_delete()
163 /**
164 \brief Creates a new spin button for the export dialog
165 \param key The name of the spin button
166 \param val A default value for the spin button
167 \param min Minimum value for the spin button
168 \param max Maximum value for the spin button
169 \param step The step size for the spin button
170 \param page Size of the page increment
171 \param us Unit selector that effects this spin button
172 \param t Table to put the spin button in
173 \param x X location in the table \c t to start with
174 \param y Y location in the table \c t to start with
175 \param ll Text to put on the left side of the spin button (optional)
176 \param lr Text to put on the right side of the spin button (optional)
177 \param digits Number of digits to display after the decimal
178 \param sensitive Whether the spin button is sensitive or not
179 \param cb Callback for when this spin button is changed (optional)
180 \param dlg Export dialog the spin button is being placed in
182 */
183 static void
184 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
185 float step, float page, GtkWidget *us,
186 GtkWidget *t, int x, int y,
187 const gchar *ll, const gchar *lr,
188 int digits, unsigned int sensitive,
189 GCallback cb, GtkWidget *dlg )
190 {
191 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
192 gtk_object_set_data (a, "key", key);
193 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
195 if (us) {
196 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
197 GTK_ADJUSTMENT (a) );
198 }
200 int pos = 0;
202 GtkWidget *l = NULL;
204 if (ll) {
206 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
207 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
208 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
209 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
210 gtk_widget_set_sensitive (l, sensitive);
211 pos += 1;
213 }
215 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
216 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
217 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
218 gtk_widget_set_size_request (sb, 80, -1);
219 gtk_widget_set_sensitive (sb, sensitive);
220 pos += 1;
222 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
224 if (lr) {
226 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
227 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
228 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
229 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
230 gtk_widget_set_sensitive (l, sensitive);
231 pos += 1;
233 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
234 }
236 if (cb)
237 gtk_signal_connect (a, "value_changed", cb, dlg);
239 return;
240 } // end of sp_export_spinbutton_new()
243 static GtkWidget *
244 sp_export_dialog_area_frame (GtkWidget * dlg)
245 {
246 GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
248 f = gtk_frame_new (_("Export area"));
249 vb = gtk_vbox_new (FALSE, 2);
250 gtk_container_add (GTK_CONTAINER (f), vb);
252 /* Units box */
253 unitbox = gtk_hbox_new (FALSE, 0);
254 gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
255 /* gets added to the vbox later, but the unit selector is needed
256 earlier than that */
258 us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
259 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
260 if (desktop)
261 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
262 gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
263 l = gtk_label_new (_("Units:"));
264 gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
265 gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
267 hb = gtk_hbox_new (TRUE, 0);
268 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
269 gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
271 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
272 b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
273 gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
274 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
275 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
276 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
277 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
278 }
280 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
281 G_CALLBACK (sp_export_selection_changed), dlg );
282 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
283 G_CALLBACK (sp_export_selection_modified), dlg );
284 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
285 G_CALLBACK (sp_export_selection_changed), dlg );
287 t = gtk_table_new (2, 6, FALSE);
288 gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
289 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
290 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
291 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
293 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
294 t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
295 G_CALLBACK ( sp_export_area_x_value_changed),
296 dlg );
298 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
299 t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
300 G_CALLBACK (sp_export_area_x_value_changed),
301 dlg );
303 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
304 us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
305 G_CALLBACK
306 (sp_export_area_width_value_changed),
307 dlg );
309 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
310 t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
311 G_CALLBACK (sp_export_area_y_value_changed),
312 dlg );
314 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
315 t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
316 G_CALLBACK (sp_export_area_y_value_changed),
317 dlg );
319 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
320 us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
321 G_CALLBACK (sp_export_area_height_value_changed),
322 dlg );
324 /* Adding in the unit box */
325 gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
327 return f;
328 } // end of sp_export_dialog_area_frame
331 void
332 sp_export_dialog (void)
333 {
334 if (!dlg) {
335 GtkWidget *vb, *hb;
337 gchar title[500];
338 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
340 dlg = sp_window_new (title, TRUE);
342 if (x == -1000 || y == -1000) {
343 x = prefs_get_int_attribute (prefs_path, "x", 0);
344 y = prefs_get_int_attribute (prefs_path, "y", 0);
345 }
347 if (w ==0 || h == 0) {
348 w = prefs_get_int_attribute (prefs_path, "w", 0);
349 h = prefs_get_int_attribute (prefs_path, "h", 0);
350 }
352 if (x<0) x=0;
353 if (y<0) y=0;
355 if (x != 0 || y != 0) {
356 gtk_window_move ((GtkWindow *) dlg, x, y);
357 } else {
358 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
359 }
361 if (w && h)
362 gtk_window_resize ((GtkWindow *) dlg, w, h);
364 sp_transientize (dlg);
365 wd.win = dlg;
366 wd.stop = 0;
368 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
369 G_CALLBACK (sp_transientize_callback), &wd);
371 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
372 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
374 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
375 G_CALLBACK (sp_export_dialog_destroy), dlg);
377 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
378 G_CALLBACK (sp_export_dialog_delete), dlg);
380 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
381 G_CALLBACK (sp_export_dialog_delete), dlg);
383 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
384 G_CALLBACK (sp_dialog_hide), dlg);
386 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
387 G_CALLBACK (sp_dialog_unhide), dlg);
389 GtkTooltips *tt = gtk_tooltips_new();
391 vb = gtk_vbox_new (FALSE, 4);
392 gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
393 gtk_container_add (GTK_CONTAINER (dlg), vb);
395 /* Export area frame */
396 {
397 GtkWidget *f = sp_export_dialog_area_frame(dlg);
398 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
399 }
401 /* Bitmap size frame */
402 {
403 GtkWidget *f = gtk_frame_new (_("Bitmap size"));
404 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
405 GtkWidget *t = gtk_table_new (2, 5, FALSE);
406 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
407 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
408 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
409 gtk_container_add (GTK_CONTAINER (f), t);
411 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
412 NULL, t, 0, 0,
413 _("_Width:"), _("pixels at"), 0, 1,
414 G_CALLBACK
415 (sp_export_bitmap_width_value_changed),
416 dlg );
418 sp_export_spinbutton_new ( "xdpi",
419 prefs_get_double_attribute
420 ( "dialogs.export.defaultxdpi",
421 "value", DPI_BASE),
422 0.01, 100000.0, 0.1, 1.0, NULL, t, 3, 0,
423 NULL, _("dp_i"), 2, 1,
424 G_CALLBACK (sp_export_xdpi_value_changed),
425 dlg );
427 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
428 NULL, t, 0, 1,
429 _("Height:"), _("pixels at"), 0, 1,
430 G_CALLBACK
431 (sp_export_bitmap_height_value_changed),
432 dlg );
434 /** \todo
435 * Needs fixing: there's no way to set ydpi currently, so we use
436 * the defaultxdpi value here, too...
437 */
438 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
439 ( "dialogs.export.defaultxdpi",
440 "value", DPI_BASE),
441 0.01, 100000.0, 0.1, 1.0, NULL, t, 3, 1,
442 NULL, _("dpi"), 2, 0, NULL, dlg );
443 }
445 /* File entry */
446 {
447 GtkWidget *frame = gtk_frame_new ("");
448 GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
449 gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
450 gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
452 GtkWidget *fe = gtk_entry_new ();
454 /*
455 * set the default filename to be that of the current path + document
456 * with .png extension
457 *
458 * One thing to notice here is that this filename may get
459 * overwritten, but it won't happen here. The filename gets
460 * written into the text field, but then the button to select
461 * the area gets set. In that code the filename can be changed
462 * if there are some with presidence in the document. So, while
463 * this code sets the name first, it may not be the one users
464 * really see.
465 */
466 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
467 {
468 gchar *name;
469 SPDocument * doc = SP_ACTIVE_DOCUMENT;
470 const gchar *uri = SP_DOCUMENT_URI (doc);
471 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
472 const gchar * text_extension = repr->attribute("inkscape:output_extension");
473 Inkscape::Extension::Output * oextension = NULL;
475 if (text_extension != NULL) {
476 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
477 }
479 if (oextension != NULL) {
480 gchar * old_extension = oextension->get_extension();
481 if (g_str_has_suffix(uri, old_extension)) {
482 gchar * uri_copy;
483 gchar * extension_point;
484 gchar * final_name;
486 uri_copy = g_strdup(uri);
487 extension_point = g_strrstr(uri_copy, old_extension);
488 extension_point[0] = '\0';
490 final_name = g_strconcat(uri_copy, ".png", NULL);
491 gtk_entry_set_text (GTK_ENTRY (fe), final_name);
493 g_free(final_name);
494 g_free(uri_copy);
495 }
496 } else {
497 name = g_strconcat(uri, ".png", NULL);
498 gtk_entry_set_text (GTK_ENTRY (fe), name);
499 g_free(name);
500 }
502 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
503 }
504 g_signal_connect ( G_OBJECT (fe), "changed",
505 G_CALLBACK (sp_export_filename_modified), dlg);
507 hb = gtk_hbox_new (FALSE, 5);
508 gtk_container_add (GTK_CONTAINER (frame), hb);
509 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
511 {
512 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
513 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
514 g_signal_connect ( G_OBJECT (b), "clicked",
515 G_CALLBACK (sp_export_browse_clicked), NULL );
516 }
518 gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
519 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
520 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
521 original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
522 // pressing enter in the filename field is the same as clicking export:
523 g_signal_connect ( G_OBJECT (fe), "activate",
524 G_CALLBACK (sp_export_export_clicked), dlg );
525 // focus is in the filename initially:
526 gtk_widget_grab_focus (GTK_WIDGET (fe));
528 // mnemonic in frame label moves focus to filename:
529 gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
530 }
532 /* Buttons */
533 hb = gtk_hbox_new (FALSE, 0);
534 gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
536 {
537 GtkWidget *b = gtk_button_new ();
538 GtkWidget *l = gtk_label_new ("");
539 gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
540 gtk_container_add (GTK_CONTAINER(b), l);
541 gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
542 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
543 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
544 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
545 }
547 gtk_widget_show_all (vb);
549 } // end of if (!dlg)
551 sp_export_find_default_selection(dlg);
553 gtk_window_present ((GtkWindow *) dlg);
555 return;
556 } // end of sp_export_dialog()
558 static inline void
559 sp_export_find_default_selection(GtkWidget * dlg)
560 {
561 selection_type key = SELECTION_NUMBER_OF;
563 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
564 key = SELECTION_SELECTION;
565 }
567 /* Try using the preferences */
568 if (key == SELECTION_NUMBER_OF) {
569 const gchar *what = NULL;
570 int i = SELECTION_NUMBER_OF;
572 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
574 if (what != NULL) {
575 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
576 if (!strcmp (what, selection_names[i])) {
577 break;
578 }
579 }
580 }
582 key = (selection_type)i;
583 }
585 if (key == SELECTION_NUMBER_OF) {
586 key = SELECTION_SELECTION;
587 }
589 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
590 selection_names[key]);
591 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
593 return;
594 }
597 /**
598 * \brief If selection changed or a different document activated, we must
599 * recalculate any chosen areas
600 *
601 */
602 static void
603 sp_export_selection_changed ( Inkscape::Application *inkscape,
604 Inkscape::Selection *selection,
605 GtkObject *base )
606 {
607 selection_type current_key;
608 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
610 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
611 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
612 was_empty) {
613 gtk_toggle_button_set_active
614 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
615 TRUE );
616 }
617 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
619 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
621 if (inkscape &&
622 SP_IS_INKSCAPE (inkscape) &&
623 selection &&
624 SELECTION_CUSTOM != current_key) {
625 GtkToggleButton * button;
626 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
627 sp_export_area_toggled(button, base);
628 } // end of if()
630 return;
631 } // end of sp_export_selection_changed()
633 static void
634 sp_export_selection_modified ( Inkscape::Application *inkscape,
635 Inkscape::Selection *selection,
636 guint flags,
637 GtkObject *base )
638 {
639 selection_type current_key;
640 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
642 switch (current_key) {
643 case SELECTION_DRAWING:
644 if ( SP_ACTIVE_DESKTOP ) {
645 SPDocument *doc;
646 NRRect bbox;
647 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
648 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
650 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
651 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
652 }
653 }
654 break;
655 case SELECTION_SELECTION:
656 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
657 NRRect bbox;
658 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
659 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
660 }
661 break;
662 default:
663 /* Do nothing for page or for custom */
664 break;
665 }
667 return;
668 }
670 /// Called when one of the selection buttons was toggled.
671 static void
672 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
673 {
674 if (gtk_object_get_data (base, "update"))
675 return;
677 selection_type key, old_key;
678 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
679 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
681 /* Ignore all "turned off" events unless we're the only active button */
682 if (!gtk_toggle_button_get_active (tb) ) {
684 /* Don't let the current selection be deactived - but rerun the
685 activate to allow the user to renew the values */
686 if (key == old_key) {
687 gtk_toggle_button_set_active ( tb, TRUE );
688 }
690 return;
691 }
693 /* Turn off the currently active button unless it's us */
694 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
696 if (old_key != key) {
697 gtk_toggle_button_set_active
698 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
699 FALSE );
700 }
702 if ( SP_ACTIVE_DESKTOP )
703 {
704 SPDocument *doc;
705 NRRect bbox;
706 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
708 /* Notice how the switch is used to 'fall through' here to get
709 various backups. If you modify this without noticing you'll
710 probabaly screw something up. */
711 switch (key) {
712 case SELECTION_SELECTION:
713 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
714 {
715 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
716 /* Only if there is a selection that we can set
717 do we break, otherwise we fall through to the
718 drawing */
719 // std::cout << "Using selection: SELECTION" << std::endl;
720 key = SELECTION_SELECTION;
721 break;
722 }
723 case SELECTION_DRAWING:
724 /** \todo
725 * This returns wrong values if the document has a viewBox.
726 */
727 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
729 /* If the drawing is valid, then we'll use it and break
730 otherwise we drop through to the page settings */
731 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
732 // std::cout << "Using selection: DRAWING" << std::endl;
733 key = SELECTION_DRAWING;
734 break;
735 }
736 case SELECTION_PAGE:
737 bbox.x0 = 0.0;
738 bbox.y0 = 0.0;
739 bbox.x1 = sp_document_width (doc);
740 bbox.y1 = sp_document_height (doc);
741 // std::cout << "Using selection: PAGE" << std::endl;
742 key = SELECTION_PAGE;
743 break;
744 case SELECTION_CUSTOM:
745 default:
746 break;
747 } // switch
749 // remember area setting
750 prefs_set_string_attribute ( "dialogs.export.exportarea",
751 "value", selection_names[key]);
753 if (key != SELECTION_CUSTOM) {
754 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
755 }
757 } // end of if ( SP_ACTIVE_DESKTOP )
760 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
761 GtkWidget * file_entry;
762 const gchar * filename = NULL;
763 float xdpi = 0.0, ydpi = 0.0;
765 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
767 switch (key) {
768 case SELECTION_PAGE:
769 case SELECTION_DRAWING: {
770 SPDocument * doc = SP_ACTIVE_DOCUMENT;
771 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
772 const gchar * dpi_string;
774 filename = repr->attribute("inkscape:export-filename");
776 dpi_string = NULL;
777 dpi_string = repr->attribute("inkscape:export-xdpi");
778 if (dpi_string != NULL) {
779 xdpi = atof(dpi_string);
780 }
782 dpi_string = NULL;
783 dpi_string = repr->attribute("inkscape:export-ydpi");
784 if (dpi_string != NULL) {
785 ydpi = atof(dpi_string);
786 }
788 if (filename == NULL) {
789 if (doc_export_name != NULL) {
790 filename = g_strdup(doc_export_name);
791 } else {
792 filename = g_strdup("");
793 }
794 }
796 break;
797 }
798 case SELECTION_SELECTION:
799 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
800 const GSList * reprlst;
801 bool filename_search = TRUE;
802 bool xdpi_search = TRUE;
803 bool ydpi_search = TRUE;
805 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
806 for(; reprlst != NULL &&
807 filename_search &&
808 xdpi_search &&
809 ydpi_search;
810 reprlst = reprlst->next) {
811 const gchar * dpi_string;
812 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
814 if (filename_search) {
815 filename = repr->attribute("inkscape:export-filename");
816 if (filename != NULL)
817 filename_search = FALSE;
818 }
820 if (xdpi_search) {
821 dpi_string = NULL;
822 dpi_string = repr->attribute("inkscape:export-xdpi");
823 if (dpi_string != NULL) {
824 xdpi = atof(dpi_string);
825 xdpi_search = FALSE;
826 }
827 }
829 if (ydpi_search) {
830 dpi_string = NULL;
831 dpi_string = repr->attribute("inkscape:export-ydpi");
832 if (dpi_string != NULL) {
833 ydpi = atof(dpi_string);
834 ydpi_search = FALSE;
835 }
836 }
837 }
839 /* If we still don't have a filename -- let's build
840 one that's nice */
841 if (filename == NULL) {
842 const gchar * id = NULL;
843 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
844 for(; reprlst != NULL; reprlst = reprlst->next) {
845 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
846 if (repr->attribute("id")) {
847 id = repr->attribute("id");
848 break;
849 }
850 }
851 if (id == NULL) /* This should never happen */
852 id = "bitmap";
854 gchar * directory = NULL;
855 const gchar * file_entry_text;
857 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
858 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
859 // std::cout << "Directory from dialog" << std::endl;
860 directory = g_dirname(file_entry_text);
861 }
863 if (directory == NULL) {
864 /* Grab document directory */
865 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
866 // std::cout << "Directory from document" << std::endl;
867 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
868 }
869 }
871 if (directory == NULL) {
872 // std::cout << "Home Directory" << std::endl;
873 directory = homedir_path(NULL);
874 }
876 gchar * id_ext = g_strconcat(id, ".png", NULL);
877 filename = g_build_filename(directory, id_ext, NULL);
878 g_free(directory);
879 g_free(id_ext);
880 }
881 }
882 break;
883 case SELECTION_CUSTOM:
884 default:
885 break;
886 }
888 if (filename != NULL) {
889 g_free(original_name);
890 original_name = g_strdup(filename);
891 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
892 }
894 if (xdpi != 0.0) {
895 sp_export_value_set(base, "xdpi", xdpi);
896 }
898 /* These can't be seperate, and setting x sets y, so for
899 now setting this is disabled. Hopefully it won't be in
900 the future */
901 if (FALSE && ydpi != 0.0) {
902 sp_export_value_set(base, "ydpi", ydpi);
903 }
904 }
906 return;
907 } // end of sp_export_area_toggled()
909 /// Called when dialog is deleted
910 static gint
911 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
912 {
913 g_object_set_data (base, "cancel", (gpointer) 1);
914 return TRUE;
915 } // end of sp_export_progress_delete()
917 /// Called when progress is cancelled
918 static void
919 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
920 {
921 g_object_set_data (base, "cancel", (gpointer) 1);
922 } // end of sp_export_progress_cancel()
924 /// Called for every progress iteration
925 static unsigned int
926 sp_export_progress_callback (float value, void *data)
927 {
928 GtkWidget *prg;
929 int evtcount;
931 if (g_object_get_data ((GObject *) data, "cancel"))
932 return FALSE;
934 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
935 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
937 evtcount = 0;
938 while ((evtcount < 16) && gdk_events_pending ()) {
939 gtk_main_iteration_do (FALSE);
940 evtcount += 1;
941 }
943 gtk_main_iteration_do (FALSE);
945 return TRUE;
947 } // end of sp_export_progress_callback()
949 /// Called when export button is clicked
950 static void
951 sp_export_export_clicked (GtkButton *button, GtkObject *base)
952 {
953 if (!SP_ACTIVE_DESKTOP) return;
955 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
956 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
958 float const x0 = sp_export_value_get_px(base, "x0");
959 float const y0 = sp_export_value_get_px(base, "y0");
960 float const x1 = sp_export_value_get_px(base, "x1");
961 float const y1 = sp_export_value_get_px(base, "y1");
962 float const xdpi = sp_export_value_get(base, "xdpi");
963 float const ydpi = sp_export_value_get(base, "ydpi");
964 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
965 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
967 if (filename == NULL || *filename == '\0') {
968 sp_ui_error_dialog(_("You have to enter a filename"));
969 return;
970 }
972 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
973 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
974 return;
975 }
977 gchar *dirname = g_dirname(filename);
978 if ( dirname == NULL
979 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
980 {
981 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
982 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
983 safeDir);
984 sp_ui_error_dialog(error);
985 g_free(safeDir);
986 g_free(error);
987 g_free(dirname);
988 return;
989 }
990 g_free(dirname);
992 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
993 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
994 char *fn;
995 gchar *text;
997 dlg = gtk_dialog_new ();
998 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
999 prg = gtk_progress_bar_new ();
1000 sp_transientize (dlg);
1001 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1002 g_object_set_data ((GObject *) base, "progress", prg);
1003 fn = g_path_get_basename (filename);
1004 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1005 fn, width, height);
1006 g_free (fn);
1007 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1008 g_free (text);
1009 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1010 GTK_PROGRESS_LEFT_TO_RIGHT);
1011 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1012 prg, FALSE, FALSE, 4 );
1013 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1014 GTK_STOCK_CANCEL,
1015 GTK_RESPONSE_CANCEL );
1017 g_signal_connect ( (GObject *) dlg, "delete_event",
1018 (GCallback) sp_export_progress_delete, base);
1019 g_signal_connect ( (GObject *) btn, "clicked",
1020 (GCallback) sp_export_progress_cancel, base);
1021 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1022 gtk_widget_show_all (dlg);
1024 /* Do export */
1025 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1026 x0, y0, x1, y1, width, height,
1027 nv->pagecolor,
1028 sp_export_progress_callback, base)) {
1029 gchar * error;
1030 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1031 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1032 sp_ui_error_dialog(error);
1033 g_free(safeFile);
1034 g_free(error);
1035 }
1037 /* Reset the filename so that it can be changed again by changing
1038 selections and all that */
1039 g_free(original_name);
1040 original_name = g_strdup(filename);
1041 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1043 gtk_widget_destroy (dlg);
1044 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1046 /* Setup the values in the document */
1047 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1048 case SELECTION_PAGE:
1049 case SELECTION_DRAWING: {
1050 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1051 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1052 bool modified = FALSE;
1053 const gchar * temp_string;
1055 bool saved = sp_document_get_undo_sensitive(doc);
1056 sp_document_set_undo_sensitive(doc, FALSE);
1058 temp_string = repr->attribute("inkscape:export-filename");
1059 if (temp_string == NULL || strcmp(temp_string, filename)) {
1060 repr->setAttribute("inkscape:export-filename", filename);
1061 modified = TRUE;
1062 }
1063 temp_string = repr->attribute("inkscape:export-xdpi");
1064 if (temp_string == NULL || xdpi != atof(temp_string)) {
1065 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1066 modified = TRUE;
1067 }
1068 temp_string = repr->attribute("inkscape:export-ydpi");
1069 if (temp_string == NULL || xdpi != atof(temp_string)) {
1070 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1071 modified = TRUE;
1072 }
1074 if (modified)
1075 repr->setAttribute("sodipodi:modified", "TRUE");
1076 sp_document_set_undo_sensitive(doc, saved);
1077 break;
1078 }
1079 case SELECTION_SELECTION: {
1080 const GSList * reprlst;
1081 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1082 bool modified = FALSE;
1084 bool saved = sp_document_get_undo_sensitive(doc);
1085 sp_document_set_undo_sensitive(doc, FALSE);
1086 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1088 for(; reprlst != NULL; reprlst = reprlst->next) {
1089 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1090 const gchar * temp_string;
1092 if (repr->attribute("id") == NULL ||
1093 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1094 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1095 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1096 temp_string = repr->attribute("inkscape:export-filename");
1097 if (temp_string == NULL || strcmp(temp_string, filename)) {
1098 repr->setAttribute("inkscape:export-filename", filename);
1099 modified = TRUE;
1100 }
1101 }
1102 temp_string = repr->attribute("inkscape:export-xdpi");
1103 if (temp_string == NULL || xdpi != atof(temp_string)) {
1104 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1105 modified = TRUE;
1106 }
1107 temp_string = repr->attribute("inkscape:export-ydpi");
1108 if (temp_string == NULL || xdpi != atof(temp_string)) {
1109 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1110 modified = TRUE;
1111 }
1112 }
1114 if (modified) {
1115 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1116 repr->setAttribute("sodipodi:modified", "TRUE");
1117 }
1119 sp_document_set_undo_sensitive(doc, saved);
1120 break;
1121 }
1122 default:
1123 break;
1124 }
1127 return;
1128 } // end of sp_export_export_clicked()
1130 /// Called when Browse button is clicked
1131 static void
1132 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1133 {
1134 GtkWidget *fs, *fe;
1135 const gchar *filename;
1137 fs = gtk_file_chooser_dialog_new (_("Select a filename for exporting"),
1138 NULL,
1139 GTK_FILE_CHOOSER_ACTION_SAVE,
1140 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1141 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1142 NULL );
1144 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1146 sp_transientize (fs);
1148 gtk_window_set_modal(GTK_WINDOW (fs), true);
1150 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1152 if (*filename == '\0') {
1153 filename = homedir_path(NULL);
1154 }
1156 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename);
1158 if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT)
1159 {
1160 gchar *file;
1162 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
1163 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1164 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1165 g_free(utf8file);
1167 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1168 g_free(file);
1169 }
1171 gtk_widget_destroy (fs);
1173 return;
1174 } // end of sp_export_browse_clicked()
1176 // TODO: Move this to nr-rect-fns.h.
1177 static bool
1178 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1179 {
1180 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1181 return (
1182 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1183 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1184 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1185 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1186 );
1187 }
1189 /**
1190 \brief This function is used to detect the current selection setting
1191 based on the values in the x0, y0, x1 and y0 fields.
1192 \param base The export dialog itself
1194 One of the most confusing parts of this function is why the array
1195 is built at the beginning. What needs to happen here is that we
1196 should always check the current selection to see if it is the valid
1197 one. While this is a performance improvement it is also a usability
1198 one during the cases where things like selections and drawings match
1199 size. This way buttons change less 'randomly' (atleast in the eyes
1200 of the user). To do this an array is built where the current selection
1201 type is placed first, and then the others in an order from smallest
1202 to largest (this can be configured by reshuffling \c test_order).
1204 All of the values in this function are rounded to two decimal places
1205 because that is what is shown to the user. While everything is kept
1206 more accurate than that, the user can't control more acurrate than
1207 that, so for this to work for them - it needs to check on that level
1208 of accuracy.
1210 \todo finish writing this up
1211 */
1212 static void
1213 sp_export_detect_size(GtkObject * base) {
1214 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1215 selection_type this_test[SELECTION_NUMBER_OF + 1];
1216 selection_type key = SELECTION_NUMBER_OF;
1218 NR::Point x(sp_export_value_get_px (base, "x0"),
1219 sp_export_value_get_px (base, "y0"));
1220 NR::Point y(sp_export_value_get_px (base, "x1"),
1221 sp_export_value_get_px (base, "y1"));
1222 NR::Rect current_bbox(x, y);
1223 //std::cout << "Current " << current_bbox;
1225 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1226 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1227 this_test[i + 1] = test_order[i];
1228 }
1230 for (int i = 0;
1231 i < SELECTION_NUMBER_OF + 1 &&
1232 key == SELECTION_NUMBER_OF &&
1233 SP_ACTIVE_DESKTOP != NULL;
1234 i++) {
1235 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1236 switch (this_test[i]) {
1237 case SELECTION_SELECTION:
1238 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1239 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1241 //std::cout << "Selection " << bbox;
1242 if (sp_export_bbox_equal(bbox,current_bbox)) {
1243 key = SELECTION_SELECTION;
1244 }
1245 }
1246 break;
1247 case SELECTION_DRAWING: {
1248 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1250 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1252 // std::cout << "Drawing " << bbox2;
1253 if (sp_export_bbox_equal(bbox,current_bbox)) {
1254 key = SELECTION_DRAWING;
1255 }
1256 break;
1257 }
1259 case SELECTION_PAGE: {
1260 SPDocument *doc;
1262 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1264 NR::Point x(0.0, 0.0);
1265 NR::Point y(sp_document_width(doc),
1266 sp_document_height(doc));
1267 NR::Rect bbox(x, y);
1269 // std::cout << "Page " << bbox;
1270 if (sp_export_bbox_equal(bbox,current_bbox)) {
1271 key = SELECTION_PAGE;
1272 }
1274 break;
1275 }
1276 default:
1277 break;
1278 }
1279 }
1280 // std::cout << std::endl;
1282 if (key == SELECTION_NUMBER_OF) {
1283 key = SELECTION_CUSTOM;
1284 }
1286 /* We're now using a custom size, not a fixed one */
1287 /* printf("Detecting state: %s\n", selection_names[key]); */
1288 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1289 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1290 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1291 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1293 return;
1294 } /* sp_export_detect_size */
1296 /// Called when area x0 value is changed
1297 static void
1298 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1299 {
1300 float x0, x1, xdpi, width;
1302 if (gtk_object_get_data (base, "update"))
1303 return;
1305 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1306 (base, "units")))
1307 {
1308 return;
1309 }
1311 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1313 x0 = sp_export_value_get_px (base, "x0");
1314 x1 = sp_export_value_get_px (base, "x1");
1315 xdpi = sp_export_value_get (base, "xdpi");
1317 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1319 if (width < SP_EXPORT_MIN_SIZE) {
1320 const gchar *key;
1321 width = SP_EXPORT_MIN_SIZE;
1322 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1324 if (!strcmp (key, "x0")) {
1325 x1 = x0 + width * DPI_BASE / xdpi;
1326 sp_export_value_set_px (base, "x1", x1);
1327 } else {
1328 x0 = x1 - width * DPI_BASE / xdpi;
1329 sp_export_value_set_px (base, "x0", x0);
1330 }
1331 }
1333 sp_export_value_set_px (base, "width", x1 - x0);
1334 sp_export_value_set (base, "bmwidth", width);
1336 sp_export_detect_size(base);
1338 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1340 return;
1341 } // end of sp_export_area_x_value_changed()
1343 /// Called when area y0 value is changed.
1344 static void
1345 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1346 {
1347 float y0, y1, ydpi, height;
1349 if (gtk_object_get_data (base, "update"))
1350 return;
1352 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1353 (base, "units")))
1354 {
1355 return;
1356 }
1358 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1360 y0 = sp_export_value_get_px (base, "y0");
1361 y1 = sp_export_value_get_px (base, "y1");
1362 ydpi = sp_export_value_get (base, "ydpi");
1364 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1366 if (height < SP_EXPORT_MIN_SIZE) {
1367 const gchar *key;
1368 height = SP_EXPORT_MIN_SIZE;
1369 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1370 if (!strcmp (key, "y0")) {
1371 y1 = y0 + height * DPI_BASE / ydpi;
1372 sp_export_value_set_px (base, "y1", y1);
1373 } else {
1374 y0 = y1 - height * DPI_BASE / ydpi;
1375 sp_export_value_set_px (base, "y0", y0);
1376 }
1377 }
1379 sp_export_value_set_px (base, "height", y1 - y0);
1380 sp_export_value_set (base, "bmheight", height);
1382 sp_export_detect_size(base);
1384 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1386 return;
1387 } // end of sp_export_area_y_value_changed()
1389 /// Called when x1-x0 or area width is changed
1390 static void
1391 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1392 {
1393 float x0, x1, xdpi, width, bmwidth;
1395 if (gtk_object_get_data (base, "update"))
1396 return;
1398 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1399 (base, "units"))) {
1400 return;
1401 }
1403 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1405 x0 = sp_export_value_get_px (base, "x0");
1406 x1 = sp_export_value_get_px (base, "x1");
1407 xdpi = sp_export_value_get (base, "xdpi");
1408 width = sp_export_value_get_px (base, "width");
1409 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1411 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1413 bmwidth = SP_EXPORT_MIN_SIZE;
1414 width = bmwidth * DPI_BASE / xdpi;
1415 sp_export_value_set_px (base, "width", width);
1416 }
1418 sp_export_value_set_px (base, "x1", x0 + width);
1419 sp_export_value_set (base, "bmwidth", bmwidth);
1421 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1423 return;
1424 } // end of sp_export_area_width_value_changed()
1426 /// Called when y1-y0 or area height is changed.
1427 static void
1428 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1429 {
1431 float y0, y1, ydpi, height, bmheight;
1433 if (gtk_object_get_data (base, "update"))
1434 return;
1436 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1437 (base, "units"))) {
1438 return;
1439 }
1441 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1443 y0 = sp_export_value_get_px (base, "y0");
1444 y1 = sp_export_value_get_px (base, "y1");
1445 ydpi = sp_export_value_get (base, "ydpi");
1446 height = sp_export_value_get_px (base, "height");
1447 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1449 if (bmheight < SP_EXPORT_MIN_SIZE) {
1450 bmheight = SP_EXPORT_MIN_SIZE;
1451 height = bmheight * DPI_BASE / ydpi;
1452 sp_export_value_set_px (base, "height", height);
1453 }
1455 sp_export_value_set_px (base, "y1", y0 + height);
1456 sp_export_value_set (base, "bmheight", bmheight);
1458 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1460 return;
1461 } // end of sp_export_area_height_value_changed()
1463 /**
1464 \brief A function to set the ydpi
1465 \param base The export dialog
1467 This function grabs all of the y values and then figures out the
1468 new bitmap size based on the changing dpi value. The dpi value is
1469 gotten from the xdpi setting as these can not currently be independent.
1470 */
1471 static void
1472 sp_export_set_image_y (GtkObject *base)
1473 {
1474 float y0, y1, xdpi;
1476 y0 = sp_export_value_get_px (base, "y0");
1477 y1 = sp_export_value_get_px (base, "y1");
1478 xdpi = sp_export_value_get (base, "xdpi");
1480 sp_export_value_set (base, "ydpi", xdpi);
1481 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1483 return;
1484 } // end of sp_export_set_image_y()
1486 /**
1487 \brief A function to set the xdpi
1488 \param base The export dialog
1490 This function grabs all of the x values and then figures out the
1491 new bitmap size based on the changing dpi value. The dpi value is
1492 gotten from the xdpi setting as these can not currently be independent.
1493 */
1494 static void
1495 sp_export_set_image_x (GtkObject *base)
1496 {
1497 float x0, x1, xdpi;
1499 x0 = sp_export_value_get_px (base, "x0");
1500 x1 = sp_export_value_get_px (base, "x1");
1501 xdpi = sp_export_value_get (base, "xdpi");
1503 sp_export_value_set (base, "ydpi", xdpi);
1504 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1506 return;
1507 } // end of sp_export_set_image_x()
1509 /// Called when pixel width is changed
1510 static void
1511 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1512 {
1513 float x0, x1, bmwidth, xdpi;
1515 if (gtk_object_get_data (base, "update"))
1516 return;
1518 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1519 (base, "units"))) {
1520 return;
1521 }
1523 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1525 x0 = sp_export_value_get_px (base, "x0");
1526 x1 = sp_export_value_get_px (base, "x1");
1527 bmwidth = sp_export_value_get (base, "bmwidth");
1529 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1530 bmwidth = SP_EXPORT_MIN_SIZE;
1531 sp_export_value_set (base, "bmwidth", bmwidth);
1532 }
1534 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1535 sp_export_value_set (base, "xdpi", xdpi);
1537 sp_export_set_image_y (base);
1539 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1541 return;
1542 } // end of sp_export_bitmap_width_value_changed()
1544 /// Called when pixel height is changed
1545 static void
1546 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1547 {
1548 float y0, y1, bmheight, xdpi;
1550 if (gtk_object_get_data (base, "update"))
1551 return;
1553 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1554 (base, "units"))) {
1555 return;
1556 }
1558 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1560 y0 = sp_export_value_get_px (base, "y0");
1561 y1 = sp_export_value_get_px (base, "y1");
1562 bmheight = sp_export_value_get (base, "bmheight");
1564 if (bmheight < SP_EXPORT_MIN_SIZE) {
1565 bmheight = SP_EXPORT_MIN_SIZE;
1566 sp_export_value_set (base, "bmheight", bmheight);
1567 }
1569 xdpi = bmheight * DPI_BASE / (y1 - y0);
1570 sp_export_value_set (base, "xdpi", xdpi);
1572 sp_export_set_image_x (base);
1574 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1576 return;
1577 } // end of sp_export_bitmap_width_value_changed()
1579 /**
1580 \brief A function to adjust the bitmap width when the xdpi value changes
1581 \param adj The adjustment that was changed
1582 \param base The export dialog itself
1584 The first thing this function checks is to see if we are doing an
1585 update. If we are, this function just returns because there is another
1586 instance of it that will handle everything for us. If there is a
1587 units change, we also assume that everyone is being updated appropriately
1588 and there is nothing for us to do.
1590 If we're the highest level function, we set the update flag, and
1591 continue on our way.
1593 All of the values are grabbed using the \c sp_export_value_get functions
1594 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1595 xdpi value is saved in the preferences for the next time the dialog
1596 is opened. (does the selection dpi need to be set here?)
1598 A check is done to to ensure that we aren't outputing an invalid width,
1599 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1600 changed to make it valid.
1602 After all of this the bitmap width is changed.
1604 We also change the ydpi. This is a temporary hack as these can not
1605 currently be independent. This is likely to change in the future.
1606 */
1607 void
1608 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1609 {
1610 float x0, x1, xdpi, bmwidth;
1612 if (gtk_object_get_data (base, "update"))
1613 return;
1615 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1616 (base, "units"))) {
1617 return;
1618 }
1620 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1622 x0 = sp_export_value_get_px (base, "x0");
1623 x1 = sp_export_value_get_px (base, "x1");
1624 xdpi = sp_export_value_get (base, "xdpi");
1626 // remember xdpi setting
1627 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1629 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1631 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1632 bmwidth = SP_EXPORT_MIN_SIZE;
1633 if (x1 != x0)
1634 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1635 else
1636 xdpi = DPI_BASE;
1637 sp_export_value_set (base, "xdpi", xdpi);
1638 }
1640 sp_export_value_set (base, "bmwidth", bmwidth);
1642 sp_export_set_image_y (base);
1644 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1646 return;
1647 } // end of sp_export_xdpi_value_changed()
1650 /**
1651 \brief A function to change the area that is used for the exported
1652 bitmap.
1653 \param base This is the export dialog
1654 \param x0 Horizontal upper left hand corner of the picture in points
1655 \param y0 Vertical upper left hand corner of the picture in points
1656 \param x1 Horizontal lower right hand corner of the picture in points
1657 \param y1 Vertical lower right hand corner of the picture in points
1659 This function just calls \c sp_export_value_set_px for each of the
1660 parameters that is passed in. This allows for setting them all in
1661 one convient area.
1663 Update is set to suspend all of the other test running while all the
1664 values are being set up. This allows for a performance increase, but
1665 it also means that the wrong type won't be detected with only some of
1666 the values set. After all the values are set everyone is told that
1667 there has been an update.
1668 */
1669 static void
1670 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1671 {
1672 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1673 sp_export_value_set_px (base, "x1", x1);
1674 sp_export_value_set_px (base, "y1", y1);
1675 sp_export_value_set_px (base, "x0", x0);
1676 sp_export_value_set_px (base, "y0", y0);
1677 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1679 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1680 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1682 return;
1683 }
1685 /**
1686 \brief Sets the value of an adjustment
1687 \param base The export dialog
1688 \param key Which adjustment to set
1689 \param val What value to set it to
1691 This function finds the adjustment using the data stored in the
1692 export dialog. After finding the adjustment it then sets
1693 the value of it.
1694 */
1695 static void
1696 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1697 {
1698 GtkAdjustment *adj;
1700 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1702 gtk_adjustment_set_value (adj, val);
1703 }
1705 /**
1706 \brief A function to set a value using the units points
1707 \param base The export dialog
1708 \param key Which value should be set
1709 \param val What the value should be in points
1711 This function first gets the adjustment for the key that is passed
1712 in. It then figures out what units are currently being used in the
1713 dialog. After doing all of that, it then converts the incoming
1714 value and sets the adjustment.
1715 */
1716 static void
1717 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1718 {
1719 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1721 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1723 return;
1724 }
1726 /**
1727 \brief Get the value of an adjustment in the export dialog
1728 \param base The export dialog
1729 \param key Which adjustment is being looked for
1730 \return The value in the specified adjustment
1732 This function gets the adjustment from the data field in the export
1733 dialog. It then grabs the value from the adjustment.
1734 */
1735 static float
1736 sp_export_value_get ( GtkObject *base, const gchar *key )
1737 {
1738 GtkAdjustment *adj;
1740 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1742 return adj->value;
1743 }
1745 /**
1746 \brief Grabs a value in the export dialog and converts the unit
1747 to points
1748 \param base The export dialog
1749 \param key Which value should be returned
1750 \return The value in the adjustment in points
1752 This function, at its most basic, is a call to \c sp_export_value_get
1753 to get the value of the adjustment. It then finds the units that
1754 are being used by looking at the "units" attribute of the export
1755 dialog. Using that it converts the returned value into points.
1756 */
1757 static float
1758 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1759 {
1760 float value = sp_export_value_get(base, key);
1761 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1763 return sp_units_get_pixels (value, *unit);
1764 } // end of sp_export_value_get_px()
1766 /**
1767 \brief This function is called when the filename is changed by
1768 anyone. It resets the virgin bit.
1769 \param object Text entry box
1770 \param data The export dialog
1771 \return None
1773 This function gets called when the text area is modified. It is
1774 looking for the case where the text area is modified from its
1775 original value. In that case it sets the "filename-modified" bit
1776 to TRUE. If the text dialog returns back to the original text, the
1777 bit gets reset. This should stop simple mistakes.
1778 */
1779 static void
1780 sp_export_filename_modified (GtkObject * object, gpointer data)
1781 {
1782 GtkWidget * text_entry = (GtkWidget *)object;
1783 GtkWidget * export_dialog = (GtkWidget *)data;
1785 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1786 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1787 // printf("Modified: FALSE\n");
1788 } else {
1789 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1790 // printf("Modified: TRUE\n");
1791 }
1793 return;
1794 } // end sp_export_filename_modified
1796 /*
1797 Local Variables:
1798 mode:c++
1799 c-file-style:"stroustrup"
1800 c-file-offsets:((innamespace . 0)(inline-open . 0))
1801 indent-tabs-mode:nil
1802 fill-column:99
1803 End:
1804 */
1805 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :