5438dfdc55306dd645185e036f8c099ca3a598cd
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 );
58 static void sp_export_browse_store ( GtkButton *button, gpointer userdata );
61 static void sp_export_area_x_value_changed ( GtkAdjustment *adj,
62 GtkObject *base);
64 static void sp_export_area_y_value_changed ( GtkAdjustment *adj,
65 GtkObject *base);
67 static void sp_export_area_width_value_changed ( GtkAdjustment *adj,
68 GtkObject *base);
70 static void sp_export_area_height_value_changed ( GtkAdjustment *adj,
71 GtkObject *base);
73 static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj,
74 GtkObject *base);
76 static void sp_export_bitmap_height_value_changed ( GtkAdjustment *adj,
77 GtkObject *base);
79 static void sp_export_xdpi_value_changed ( GtkAdjustment *adj,
80 GtkObject *base);
82 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
83 Inkscape::Selection *selection,
84 GtkObject *base);
85 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
86 Inkscape::Selection *selection,
87 guint flags,
88 GtkObject *base );
90 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
91 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
92 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
93 static float sp_export_value_get ( GtkObject *base, const gchar *key );
94 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
96 static void sp_export_filename_modified (GtkObject * object, gpointer data);
97 static inline void sp_export_find_default_selection(GtkWidget * dlg);
98 static void sp_export_detect_size(GtkObject * base);
100 static const gchar *prefs_path = "dialogs.export";
102 // these all need to be reinitialized to their defaults during dialog_destroy
103 static GtkWidget *dlg = NULL;
104 static win_data wd;
105 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
106 static gchar * original_name = NULL;
107 static gchar * doc_export_name = NULL;
108 static bool was_empty = TRUE;
110 /** What type of button is being pressed */
111 enum selection_type {
112 SELECTION_PAGE = 0, /**< Export the whole page */
113 SELECTION_DRAWING, /**< Export everything drawn on the page */
114 SELECTION_SELECTION, /**< Export everything that is selected */
115 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
116 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
117 };
119 /** A list of strings that is used both in the preferences, and in the
120 data fields to describe the various values of \c selection_type. */
121 static const char * selection_names[SELECTION_NUMBER_OF] = {
122 "page", "drawing", "selection", "custom"};
124 /** The names on the buttons for the various selection types. */
125 static const char * selection_labels[SELECTION_NUMBER_OF] = {
126 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
128 static void
129 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
130 {
131 sp_signal_disconnect_by_data (INKSCAPE, dlg);
133 wd.win = dlg = NULL;
134 wd.stop = 0;
135 x = -1000; y = -1000; w = 0; h = 0;
136 g_free(original_name);
137 original_name = NULL;
138 g_free(doc_export_name);
139 doc_export_name = NULL;
140 was_empty = TRUE;
142 return;
143 } // end of sp_export_dialog_destroy()
145 /// Called when dialog is closed or inkscape is shut down.
146 static bool
147 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
148 {
150 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
151 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
153 if (x<0) x=0;
154 if (y<0) y=0;
156 prefs_set_int_attribute (prefs_path, "x", x);
157 prefs_set_int_attribute (prefs_path, "y", y);
158 prefs_set_int_attribute (prefs_path, "w", w);
159 prefs_set_int_attribute (prefs_path, "h", h);
161 return FALSE; // which means, go ahead and destroy it
163 } // end of sp_export_dialog_delete()
165 /**
166 \brief Creates a new spin button for the export dialog
167 \param key The name of the spin button
168 \param val A default value for the spin button
169 \param min Minimum value for the spin button
170 \param max Maximum value for the spin button
171 \param step The step size for the spin button
172 \param page Size of the page increment
173 \param us Unit selector that effects this spin button
174 \param t Table to put the spin button in
175 \param x X location in the table \c t to start with
176 \param y Y location in the table \c t to start with
177 \param ll Text to put on the left side of the spin button (optional)
178 \param lr Text to put on the right side of the spin button (optional)
179 \param digits Number of digits to display after the decimal
180 \param sensitive Whether the spin button is sensitive or not
181 \param cb Callback for when this spin button is changed (optional)
182 \param dlg Export dialog the spin button is being placed in
184 */
185 static void
186 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
187 float step, float page, GtkWidget *us,
188 GtkWidget *t, int x, int y,
189 const gchar *ll, const gchar *lr,
190 int digits, unsigned int sensitive,
191 GCallback cb, GtkWidget *dlg )
192 {
193 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
194 gtk_object_set_data (a, "key", key);
195 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
197 if (us) {
198 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
199 GTK_ADJUSTMENT (a) );
200 }
202 int pos = 0;
204 GtkWidget *l = NULL;
206 if (ll) {
208 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
209 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
210 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
211 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
212 gtk_widget_set_sensitive (l, sensitive);
213 pos += 1;
215 }
217 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
218 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
219 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
220 gtk_widget_set_size_request (sb, 80, -1);
221 gtk_widget_set_sensitive (sb, sensitive);
222 pos += 1;
224 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
226 if (lr) {
228 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
229 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
230 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
231 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
232 gtk_widget_set_sensitive (l, sensitive);
233 pos += 1;
235 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
236 }
238 if (cb)
239 gtk_signal_connect (a, "value_changed", cb, dlg);
241 return;
242 } // end of sp_export_spinbutton_new()
245 static GtkWidget *
246 sp_export_dialog_area_frame (GtkWidget * dlg)
247 {
248 GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
250 f = gtk_frame_new (_("Export area"));
251 vb = gtk_vbox_new (FALSE, 2);
252 gtk_container_add (GTK_CONTAINER (f), vb);
254 /* Units box */
255 unitbox = gtk_hbox_new (FALSE, 0);
256 gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
257 /* gets added to the vbox later, but the unit selector is needed
258 earlier than that */
260 us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
261 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
262 if (desktop)
263 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
264 gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
265 l = gtk_label_new (_("Units:"));
266 gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
267 gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
269 hb = gtk_hbox_new (TRUE, 0);
270 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
271 gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
273 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
274 b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
275 gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
276 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
277 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
278 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
279 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
280 }
282 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
283 G_CALLBACK (sp_export_selection_changed), dlg );
284 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
285 G_CALLBACK (sp_export_selection_modified), dlg );
286 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
287 G_CALLBACK (sp_export_selection_changed), dlg );
289 t = gtk_table_new (2, 6, FALSE);
290 gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
291 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
292 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
293 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
295 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
296 t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
297 G_CALLBACK ( sp_export_area_x_value_changed),
298 dlg );
300 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
301 t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
302 G_CALLBACK (sp_export_area_x_value_changed),
303 dlg );
305 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
306 us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
307 G_CALLBACK
308 (sp_export_area_width_value_changed),
309 dlg );
311 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
312 t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
313 G_CALLBACK (sp_export_area_y_value_changed),
314 dlg );
316 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
317 t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
318 G_CALLBACK (sp_export_area_y_value_changed),
319 dlg );
321 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
322 us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
323 G_CALLBACK (sp_export_area_height_value_changed),
324 dlg );
326 /* Adding in the unit box */
327 gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
329 return f;
330 } // end of sp_export_dialog_area_frame
333 void
334 sp_export_dialog (void)
335 {
336 if (!dlg) {
337 GtkWidget *vb, *hb;
339 gchar title[500];
340 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
342 dlg = sp_window_new (title, TRUE);
344 if (x == -1000 || y == -1000) {
345 x = prefs_get_int_attribute (prefs_path, "x", 0);
346 y = prefs_get_int_attribute (prefs_path, "y", 0);
347 }
349 if (w ==0 || h == 0) {
350 w = prefs_get_int_attribute (prefs_path, "w", 0);
351 h = prefs_get_int_attribute (prefs_path, "h", 0);
352 }
354 if (x<0) x=0;
355 if (y<0) y=0;
357 if (x != 0 || y != 0) {
358 gtk_window_move ((GtkWindow *) dlg, x, y);
359 } else {
360 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
361 }
363 if (w && h)
364 gtk_window_resize ((GtkWindow *) dlg, w, h);
366 sp_transientize (dlg);
367 wd.win = dlg;
368 wd.stop = 0;
370 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
371 G_CALLBACK (sp_transientize_callback), &wd);
373 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
374 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
376 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
377 G_CALLBACK (sp_export_dialog_destroy), dlg);
379 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
380 G_CALLBACK (sp_export_dialog_delete), dlg);
382 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
383 G_CALLBACK (sp_export_dialog_delete), dlg);
385 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
386 G_CALLBACK (sp_dialog_hide), dlg);
388 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
389 G_CALLBACK (sp_dialog_unhide), dlg);
391 GtkTooltips *tt = gtk_tooltips_new();
393 vb = gtk_vbox_new (FALSE, 4);
394 gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
395 gtk_container_add (GTK_CONTAINER (dlg), vb);
397 /* Export area frame */
398 {
399 GtkWidget *f = sp_export_dialog_area_frame(dlg);
400 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
401 }
403 /* Bitmap size frame */
404 {
405 GtkWidget *f = gtk_frame_new (_("Bitmap size"));
406 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
407 GtkWidget *t = gtk_table_new (2, 5, FALSE);
408 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
409 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
410 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
411 gtk_container_add (GTK_CONTAINER (f), t);
413 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
414 NULL, t, 0, 0,
415 _("_Width:"), _("pixels at"), 0, 1,
416 G_CALLBACK
417 (sp_export_bitmap_width_value_changed),
418 dlg );
420 sp_export_spinbutton_new ( "xdpi",
421 prefs_get_double_attribute
422 ( "dialogs.export.defaultxdpi",
423 "value", DPI_BASE),
424 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
425 NULL, _("dp_i"), 2, 1,
426 G_CALLBACK (sp_export_xdpi_value_changed),
427 dlg );
429 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1.0, 10.0,
430 NULL, t, 0, 1,
431 _("Height:"), _("pixels at"), 0, 1,
432 G_CALLBACK
433 (sp_export_bitmap_height_value_changed),
434 dlg );
436 /** \todo
437 * Needs fixing: there's no way to set ydpi currently, so we use
438 * the defaultxdpi value here, too...
439 */
440 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
441 ( "dialogs.export.defaultxdpi",
442 "value", DPI_BASE),
443 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
444 NULL, _("dpi"), 2, 0, NULL, dlg );
445 }
447 /* File entry */
448 {
449 GtkWidget *frame = gtk_frame_new ("");
450 GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
451 gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
452 gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
454 GtkWidget *fe = gtk_entry_new ();
456 /*
457 * set the default filename to be that of the current path + document
458 * with .png extension
459 *
460 * One thing to notice here is that this filename may get
461 * overwritten, but it won't happen here. The filename gets
462 * written into the text field, but then the button to select
463 * the area gets set. In that code the filename can be changed
464 * if there are some with presidence in the document. So, while
465 * this code sets the name first, it may not be the one users
466 * really see.
467 */
468 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
469 {
470 gchar *name;
471 SPDocument * doc = SP_ACTIVE_DOCUMENT;
472 const gchar *uri = SP_DOCUMENT_URI (doc);
473 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
474 const gchar * text_extension = repr->attribute("inkscape:output_extension");
475 Inkscape::Extension::Output * oextension = NULL;
477 if (text_extension != NULL) {
478 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
479 }
481 if (oextension != NULL) {
482 gchar * old_extension = oextension->get_extension();
483 if (g_str_has_suffix(uri, old_extension)) {
484 gchar * uri_copy;
485 gchar * extension_point;
486 gchar * final_name;
488 uri_copy = g_strdup(uri);
489 extension_point = g_strrstr(uri_copy, old_extension);
490 extension_point[0] = '\0';
492 final_name = g_strconcat(uri_copy, ".png", NULL);
493 gtk_entry_set_text (GTK_ENTRY (fe), final_name);
495 g_free(final_name);
496 g_free(uri_copy);
497 }
498 } else {
499 name = g_strconcat(uri, ".png", NULL);
500 gtk_entry_set_text (GTK_ENTRY (fe), name);
501 g_free(name);
502 }
504 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
505 }
506 g_signal_connect ( G_OBJECT (fe), "changed",
507 G_CALLBACK (sp_export_filename_modified), dlg);
509 hb = gtk_hbox_new (FALSE, 5);
510 gtk_container_add (GTK_CONTAINER (frame), hb);
511 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
513 {
514 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
515 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
516 g_signal_connect ( G_OBJECT (b), "clicked",
517 G_CALLBACK (sp_export_browse_clicked), NULL );
518 }
520 gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
521 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
522 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
523 original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
524 // pressing enter in the filename field is the same as clicking export:
525 g_signal_connect ( G_OBJECT (fe), "activate",
526 G_CALLBACK (sp_export_export_clicked), dlg );
527 // focus is in the filename initially:
528 gtk_widget_grab_focus (GTK_WIDGET (fe));
530 // mnemonic in frame label moves focus to filename:
531 gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
532 }
534 /* Buttons */
535 hb = gtk_hbox_new (FALSE, 0);
536 gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
538 {
539 GtkWidget *b = gtk_button_new ();
540 GtkWidget *l = gtk_label_new ("");
541 gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
542 gtk_container_add (GTK_CONTAINER(b), l);
543 gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
544 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
545 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
546 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
547 }
549 gtk_widget_show_all (vb);
551 } // end of if (!dlg)
553 sp_export_find_default_selection(dlg);
555 gtk_window_present ((GtkWindow *) dlg);
557 return;
558 } // end of sp_export_dialog()
560 static inline void
561 sp_export_find_default_selection(GtkWidget * dlg)
562 {
563 selection_type key = SELECTION_NUMBER_OF;
565 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
566 key = SELECTION_SELECTION;
567 }
569 /* Try using the preferences */
570 if (key == SELECTION_NUMBER_OF) {
571 const gchar *what = NULL;
572 int i = SELECTION_NUMBER_OF;
574 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
576 if (what != NULL) {
577 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
578 if (!strcmp (what, selection_names[i])) {
579 break;
580 }
581 }
582 }
584 key = (selection_type)i;
585 }
587 if (key == SELECTION_NUMBER_OF) {
588 key = SELECTION_SELECTION;
589 }
591 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
592 selection_names[key]);
593 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
595 return;
596 }
599 /**
600 * \brief If selection changed or a different document activated, we must
601 * recalculate any chosen areas
602 *
603 */
604 static void
605 sp_export_selection_changed ( Inkscape::Application *inkscape,
606 Inkscape::Selection *selection,
607 GtkObject *base )
608 {
609 selection_type current_key;
610 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
612 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
613 (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
614 was_empty) {
615 gtk_toggle_button_set_active
616 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
617 TRUE );
618 }
619 was_empty = (sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty();
621 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
623 if (inkscape &&
624 SP_IS_INKSCAPE (inkscape) &&
625 selection &&
626 SELECTION_CUSTOM != current_key) {
627 GtkToggleButton * button;
628 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
629 sp_export_area_toggled(button, base);
630 } // end of if()
632 return;
633 } // end of sp_export_selection_changed()
635 static void
636 sp_export_selection_modified ( Inkscape::Application *inkscape,
637 Inkscape::Selection *selection,
638 guint flags,
639 GtkObject *base )
640 {
641 selection_type current_key;
642 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
644 switch (current_key) {
645 case SELECTION_DRAWING:
646 if ( SP_ACTIVE_DESKTOP ) {
647 SPDocument *doc;
648 NRRect bbox;
649 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
650 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
652 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
653 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
654 }
655 }
656 break;
657 case SELECTION_SELECTION:
658 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
659 NRRect bbox;
660 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
661 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
662 }
663 break;
664 default:
665 /* Do nothing for page or for custom */
666 break;
667 }
669 return;
670 }
672 /// Called when one of the selection buttons was toggled.
673 static void
674 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
675 {
676 if (gtk_object_get_data (base, "update"))
677 return;
679 selection_type key, old_key;
680 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
681 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
683 /* Ignore all "turned off" events unless we're the only active button */
684 if (!gtk_toggle_button_get_active (tb) ) {
686 /* Don't let the current selection be deactived - but rerun the
687 activate to allow the user to renew the values */
688 if (key == old_key) {
689 gtk_toggle_button_set_active ( tb, TRUE );
690 }
692 return;
693 }
695 /* Turn off the currently active button unless it's us */
696 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
698 if (old_key != key) {
699 gtk_toggle_button_set_active
700 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
701 FALSE );
702 }
704 if ( SP_ACTIVE_DESKTOP )
705 {
706 SPDocument *doc;
707 NRRect bbox;
708 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
710 /* Notice how the switch is used to 'fall through' here to get
711 various backups. If you modify this without noticing you'll
712 probabaly screw something up. */
713 switch (key) {
714 case SELECTION_SELECTION:
715 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false)
716 {
717 (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds(&bbox);
718 /* Only if there is a selection that we can set
719 do we break, otherwise we fall through to the
720 drawing */
721 // std::cout << "Using selection: SELECTION" << std::endl;
722 key = SELECTION_SELECTION;
723 break;
724 }
725 case SELECTION_DRAWING:
726 /** \todo
727 * This returns wrong values if the document has a viewBox.
728 */
729 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
731 /* If the drawing is valid, then we'll use it and break
732 otherwise we drop through to the page settings */
733 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
734 // std::cout << "Using selection: DRAWING" << std::endl;
735 key = SELECTION_DRAWING;
736 break;
737 }
738 case SELECTION_PAGE:
739 bbox.x0 = 0.0;
740 bbox.y0 = 0.0;
741 bbox.x1 = sp_document_width (doc);
742 bbox.y1 = sp_document_height (doc);
743 // std::cout << "Using selection: PAGE" << std::endl;
744 key = SELECTION_PAGE;
745 break;
746 case SELECTION_CUSTOM:
747 default:
748 break;
749 } // switch
751 // remember area setting
752 prefs_set_string_attribute ( "dialogs.export.exportarea",
753 "value", selection_names[key]);
755 if (key != SELECTION_CUSTOM) {
756 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
757 }
759 } // end of if ( SP_ACTIVE_DESKTOP )
762 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
763 GtkWidget * file_entry;
764 const gchar * filename = NULL;
765 float xdpi = 0.0, ydpi = 0.0;
767 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
769 switch (key) {
770 case SELECTION_PAGE:
771 case SELECTION_DRAWING: {
772 SPDocument * doc = SP_ACTIVE_DOCUMENT;
773 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
774 const gchar * dpi_string;
776 filename = repr->attribute("inkscape:export-filename");
778 dpi_string = NULL;
779 dpi_string = repr->attribute("inkscape:export-xdpi");
780 if (dpi_string != NULL) {
781 xdpi = atof(dpi_string);
782 }
784 dpi_string = NULL;
785 dpi_string = repr->attribute("inkscape:export-ydpi");
786 if (dpi_string != NULL) {
787 ydpi = atof(dpi_string);
788 }
790 if (filename == NULL) {
791 if (doc_export_name != NULL) {
792 filename = g_strdup(doc_export_name);
793 } else {
794 filename = g_strdup("");
795 }
796 }
798 break;
799 }
800 case SELECTION_SELECTION:
801 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
802 const GSList * reprlst;
803 bool filename_search = TRUE;
804 bool xdpi_search = TRUE;
805 bool ydpi_search = TRUE;
807 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
808 for(; reprlst != NULL &&
809 filename_search &&
810 xdpi_search &&
811 ydpi_search;
812 reprlst = reprlst->next) {
813 const gchar * dpi_string;
814 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
816 if (filename_search) {
817 filename = repr->attribute("inkscape:export-filename");
818 if (filename != NULL)
819 filename_search = FALSE;
820 }
822 if (xdpi_search) {
823 dpi_string = NULL;
824 dpi_string = repr->attribute("inkscape:export-xdpi");
825 if (dpi_string != NULL) {
826 xdpi = atof(dpi_string);
827 xdpi_search = FALSE;
828 }
829 }
831 if (ydpi_search) {
832 dpi_string = NULL;
833 dpi_string = repr->attribute("inkscape:export-ydpi");
834 if (dpi_string != NULL) {
835 ydpi = atof(dpi_string);
836 ydpi_search = FALSE;
837 }
838 }
839 }
841 /* If we still don't have a filename -- let's build
842 one that's nice */
843 if (filename == NULL) {
844 const gchar * id = NULL;
845 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
846 for(; reprlst != NULL; reprlst = reprlst->next) {
847 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
848 if (repr->attribute("id")) {
849 id = repr->attribute("id");
850 break;
851 }
852 }
853 if (id == NULL) /* This should never happen */
854 id = "bitmap";
856 gchar * directory = NULL;
857 const gchar * file_entry_text;
859 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
860 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
861 // std::cout << "Directory from dialog" << std::endl;
862 directory = g_dirname(file_entry_text);
863 }
865 if (directory == NULL) {
866 /* Grab document directory */
867 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
868 // std::cout << "Directory from document" << std::endl;
869 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
870 }
871 }
873 if (directory == NULL) {
874 // std::cout << "Home Directory" << std::endl;
875 directory = homedir_path(NULL);
876 }
878 gchar * id_ext = g_strconcat(id, ".png", NULL);
879 filename = g_build_filename(directory, id_ext, NULL);
880 g_free(directory);
881 g_free(id_ext);
882 }
883 }
884 break;
885 case SELECTION_CUSTOM:
886 default:
887 break;
888 }
890 if (filename != NULL) {
891 g_free(original_name);
892 original_name = g_strdup(filename);
893 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
894 }
896 if (xdpi != 0.0) {
897 sp_export_value_set(base, "xdpi", xdpi);
898 }
900 /* These can't be seperate, and setting x sets y, so for
901 now setting this is disabled. Hopefully it won't be in
902 the future */
903 if (FALSE && ydpi != 0.0) {
904 sp_export_value_set(base, "ydpi", ydpi);
905 }
906 }
908 return;
909 } // end of sp_export_area_toggled()
911 /// Called when dialog is deleted
912 static gint
913 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
914 {
915 g_object_set_data (base, "cancel", (gpointer) 1);
916 return TRUE;
917 } // end of sp_export_progress_delete()
919 /// Called when progress is cancelled
920 static void
921 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
922 {
923 g_object_set_data (base, "cancel", (gpointer) 1);
924 } // end of sp_export_progress_cancel()
926 /// Called for every progress iteration
927 static unsigned int
928 sp_export_progress_callback (float value, void *data)
929 {
930 GtkWidget *prg;
931 int evtcount;
933 if (g_object_get_data ((GObject *) data, "cancel"))
934 return FALSE;
936 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
937 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
939 evtcount = 0;
940 while ((evtcount < 16) && gdk_events_pending ()) {
941 gtk_main_iteration_do (FALSE);
942 evtcount += 1;
943 }
945 gtk_main_iteration_do (FALSE);
947 return TRUE;
949 } // end of sp_export_progress_callback()
951 /// Called when export button is clicked
952 static void
953 sp_export_export_clicked (GtkButton *button, GtkObject *base)
954 {
955 if (!SP_ACTIVE_DESKTOP) return;
957 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
958 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
960 float const x0 = sp_export_value_get_px(base, "x0");
961 float const y0 = sp_export_value_get_px(base, "y0");
962 float const x1 = sp_export_value_get_px(base, "x1");
963 float const y1 = sp_export_value_get_px(base, "y1");
964 float const xdpi = sp_export_value_get(base, "xdpi");
965 float const ydpi = sp_export_value_get(base, "ydpi");
966 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
967 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
969 if (filename == NULL || *filename == '\0') {
970 sp_ui_error_dialog(_("You have to enter a filename"));
971 return;
972 }
974 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
975 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
976 return;
977 }
979 gchar *dirname = g_dirname(filename);
980 if ( dirname == NULL
981 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
982 {
983 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
984 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
985 safeDir);
986 sp_ui_error_dialog(error);
987 g_free(safeDir);
988 g_free(error);
989 g_free(dirname);
990 return;
991 }
992 g_free(dirname);
994 SPNamedView *nv = sp_desktop_namedview(SP_ACTIVE_DESKTOP);
995 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
996 char *fn;
997 gchar *text;
999 dlg = gtk_dialog_new ();
1000 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
1001 prg = gtk_progress_bar_new ();
1002 sp_transientize (dlg);
1003 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
1004 g_object_set_data ((GObject *) base, "progress", prg);
1005 fn = g_path_get_basename (filename);
1006 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1007 fn, width, height);
1008 g_free (fn);
1009 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1010 g_free (text);
1011 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1012 GTK_PROGRESS_LEFT_TO_RIGHT);
1013 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1014 prg, FALSE, FALSE, 4 );
1015 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1016 GTK_STOCK_CANCEL,
1017 GTK_RESPONSE_CANCEL );
1019 g_signal_connect ( (GObject *) dlg, "delete_event",
1020 (GCallback) sp_export_progress_delete, base);
1021 g_signal_connect ( (GObject *) btn, "clicked",
1022 (GCallback) sp_export_progress_cancel, base);
1023 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1024 gtk_widget_show_all (dlg);
1026 /* Do export */
1027 if (!sp_export_png_file (sp_desktop_document (SP_ACTIVE_DESKTOP), filename,
1028 x0, y0, x1, y1, width, height,
1029 nv->pagecolor,
1030 sp_export_progress_callback, base)) {
1031 gchar * error;
1032 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1033 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1034 sp_ui_error_dialog(error);
1035 g_free(safeFile);
1036 g_free(error);
1037 }
1039 /* Reset the filename so that it can be changed again by changing
1040 selections and all that */
1041 g_free(original_name);
1042 original_name = g_strdup(filename);
1043 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1045 gtk_widget_destroy (dlg);
1046 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1048 /* Setup the values in the document */
1049 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1050 case SELECTION_PAGE:
1051 case SELECTION_DRAWING: {
1052 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1053 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1054 bool modified = FALSE;
1055 const gchar * temp_string;
1057 bool saved = sp_document_get_undo_sensitive(doc);
1058 sp_document_set_undo_sensitive(doc, FALSE);
1060 temp_string = repr->attribute("inkscape:export-filename");
1061 if (temp_string == NULL || strcmp(temp_string, filename)) {
1062 repr->setAttribute("inkscape:export-filename", filename);
1063 modified = TRUE;
1064 }
1065 temp_string = repr->attribute("inkscape:export-xdpi");
1066 if (temp_string == NULL || xdpi != atof(temp_string)) {
1067 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1068 modified = TRUE;
1069 }
1070 temp_string = repr->attribute("inkscape:export-ydpi");
1071 if (temp_string == NULL || xdpi != atof(temp_string)) {
1072 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1073 modified = TRUE;
1074 }
1076 if (modified)
1077 repr->setAttribute("sodipodi:modified", "TRUE");
1078 sp_document_set_undo_sensitive(doc, saved);
1079 break;
1080 }
1081 case SELECTION_SELECTION: {
1082 const GSList * reprlst;
1083 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1084 bool modified = FALSE;
1086 bool saved = sp_document_get_undo_sensitive(doc);
1087 sp_document_set_undo_sensitive(doc, FALSE);
1088 reprlst = sp_desktop_selection(SP_ACTIVE_DESKTOP)->reprList();
1090 for(; reprlst != NULL; reprlst = reprlst->next) {
1091 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1092 const gchar * temp_string;
1094 if (repr->attribute("id") == NULL ||
1095 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1096 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1097 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1098 temp_string = repr->attribute("inkscape:export-filename");
1099 if (temp_string == NULL || strcmp(temp_string, filename)) {
1100 repr->setAttribute("inkscape:export-filename", filename);
1101 modified = TRUE;
1102 }
1103 }
1104 temp_string = repr->attribute("inkscape:export-xdpi");
1105 if (temp_string == NULL || xdpi != atof(temp_string)) {
1106 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1107 modified = TRUE;
1108 }
1109 temp_string = repr->attribute("inkscape:export-ydpi");
1110 if (temp_string == NULL || xdpi != atof(temp_string)) {
1111 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1112 modified = TRUE;
1113 }
1114 }
1116 if (modified) {
1117 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1118 repr->setAttribute("sodipodi:modified", "TRUE");
1119 }
1121 sp_document_set_undo_sensitive(doc, saved);
1122 break;
1123 }
1124 default:
1125 break;
1126 }
1129 return;
1130 } // end of sp_export_export_clicked()
1132 /// Called when Browse button is clicked
1133 static void
1134 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1135 {
1136 GtkWidget *fs, *fe;
1137 const gchar *filename;
1139 fs = gtk_file_selection_new (_("Select a filename for exporting"));
1140 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1142 sp_transientize (fs);
1144 gtk_window_set_modal(GTK_WINDOW (fs), true);
1146 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1148 if (*filename == '\0') {
1149 filename = homedir_path(NULL);
1150 }
1152 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1154 g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1155 "clicked",
1156 G_CALLBACK (sp_export_browse_store),
1157 (gpointer) fs );
1159 g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1160 "clicked",
1161 G_CALLBACK (gtk_widget_destroy),
1162 (gpointer) fs );
1164 g_signal_connect_swapped ( GTK_OBJECT
1165 (GTK_FILE_SELECTION (fs)->cancel_button),
1166 "clicked",
1167 G_CALLBACK (gtk_widget_destroy),
1168 (gpointer) fs );
1170 gtk_widget_show (fs);
1172 return;
1173 } // end of sp_export_browse_clicked()
1175 /// Called when OK clicked in file dialog
1176 static void
1177 sp_export_browse_store (GtkButton *button, gpointer userdata)
1178 {
1179 GtkWidget *fs = (GtkWidget *)userdata, *fe;
1180 const gchar *file;
1182 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1184 file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1185 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1186 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1187 g_free(utf8file);
1189 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1191 return;
1192 } // end of sp_export_browse_store()
1194 // TODO: Move this to nr-rect-fns.h.
1195 static bool
1196 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1197 {
1198 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1199 return (
1200 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1201 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1202 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1203 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1204 );
1205 }
1207 /**
1208 \brief This function is used to detect the current selection setting
1209 based on the values in the x0, y0, x1 and y0 fields.
1210 \param base The export dialog itself
1212 One of the most confusing parts of this function is why the array
1213 is built at the beginning. What needs to happen here is that we
1214 should always check the current selection to see if it is the valid
1215 one. While this is a performance improvement it is also a usability
1216 one during the cases where things like selections and drawings match
1217 size. This way buttons change less 'randomly' (atleast in the eyes
1218 of the user). To do this an array is built where the current selection
1219 type is placed first, and then the others in an order from smallest
1220 to largest (this can be configured by reshuffling \c test_order).
1222 All of the values in this function are rounded to two decimal places
1223 because that is what is shown to the user. While everything is kept
1224 more accurate than that, the user can't control more acurrate than
1225 that, so for this to work for them - it needs to check on that level
1226 of accuracy.
1228 \todo finish writing this up
1229 */
1230 static void
1231 sp_export_detect_size(GtkObject * base) {
1232 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1233 selection_type this_test[SELECTION_NUMBER_OF + 1];
1234 selection_type key = SELECTION_NUMBER_OF;
1236 NR::Point x(sp_export_value_get_px (base, "x0"),
1237 sp_export_value_get_px (base, "y0"));
1238 NR::Point y(sp_export_value_get_px (base, "x1"),
1239 sp_export_value_get_px (base, "y1"));
1240 NR::Rect current_bbox(x, y);
1241 //std::cout << "Current " << current_bbox;
1243 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1244 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1245 this_test[i + 1] = test_order[i];
1246 }
1248 for (int i = 0;
1249 i < SELECTION_NUMBER_OF + 1 &&
1250 key == SELECTION_NUMBER_OF &&
1251 SP_ACTIVE_DESKTOP != NULL;
1252 i++) {
1253 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1254 switch (this_test[i]) {
1255 case SELECTION_SELECTION:
1256 if ((sp_desktop_selection(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1257 NR::Rect bbox = (sp_desktop_selection (SP_ACTIVE_DESKTOP))->bounds();
1259 //std::cout << "Selection " << bbox;
1260 if (sp_export_bbox_equal(bbox,current_bbox)) {
1261 key = SELECTION_SELECTION;
1262 }
1263 }
1264 break;
1265 case SELECTION_DRAWING: {
1266 SPDocument *doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1268 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1270 // std::cout << "Drawing " << bbox2;
1271 if (sp_export_bbox_equal(bbox,current_bbox)) {
1272 key = SELECTION_DRAWING;
1273 }
1274 break;
1275 }
1277 case SELECTION_PAGE: {
1278 SPDocument *doc;
1280 doc = sp_desktop_document (SP_ACTIVE_DESKTOP);
1282 NR::Point x(0.0, 0.0);
1283 NR::Point y(sp_document_width(doc),
1284 sp_document_height(doc));
1285 NR::Rect bbox(x, y);
1287 // std::cout << "Page " << bbox;
1288 if (sp_export_bbox_equal(bbox,current_bbox)) {
1289 key = SELECTION_PAGE;
1290 }
1292 break;
1293 }
1294 default:
1295 break;
1296 }
1297 }
1298 // std::cout << std::endl;
1300 if (key == SELECTION_NUMBER_OF) {
1301 key = SELECTION_CUSTOM;
1302 }
1304 /* We're now using a custom size, not a fixed one */
1305 /* printf("Detecting state: %s\n", selection_names[key]); */
1306 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1307 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1308 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1309 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1311 return;
1312 } /* sp_export_detect_size */
1314 /// Called when area x0 value is changed
1315 static void
1316 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1317 {
1318 float x0, x1, xdpi, width;
1320 if (gtk_object_get_data (base, "update"))
1321 return;
1323 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1324 (base, "units")))
1325 {
1326 return;
1327 }
1329 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1331 x0 = sp_export_value_get_px (base, "x0");
1332 x1 = sp_export_value_get_px (base, "x1");
1333 xdpi = sp_export_value_get (base, "xdpi");
1335 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1337 if (width < SP_EXPORT_MIN_SIZE) {
1338 const gchar *key;
1339 width = SP_EXPORT_MIN_SIZE;
1340 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1342 if (!strcmp (key, "x0")) {
1343 x1 = x0 + width * DPI_BASE / xdpi;
1344 sp_export_value_set_px (base, "x1", x1);
1345 } else {
1346 x0 = x1 - width * DPI_BASE / xdpi;
1347 sp_export_value_set_px (base, "x0", x0);
1348 }
1349 }
1351 sp_export_value_set_px (base, "width", x1 - x0);
1352 sp_export_value_set (base, "bmwidth", width);
1354 sp_export_detect_size(base);
1356 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1358 return;
1359 } // end of sp_export_area_x_value_changed()
1361 /// Called when area y0 value is changed.
1362 static void
1363 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1364 {
1365 float y0, y1, ydpi, height;
1367 if (gtk_object_get_data (base, "update"))
1368 return;
1370 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1371 (base, "units")))
1372 {
1373 return;
1374 }
1376 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1378 y0 = sp_export_value_get_px (base, "y0");
1379 y1 = sp_export_value_get_px (base, "y1");
1380 ydpi = sp_export_value_get (base, "ydpi");
1382 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1384 if (height < SP_EXPORT_MIN_SIZE) {
1385 const gchar *key;
1386 height = SP_EXPORT_MIN_SIZE;
1387 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1388 if (!strcmp (key, "y0")) {
1389 y1 = y0 + height * DPI_BASE / ydpi;
1390 sp_export_value_set_px (base, "y1", y1);
1391 } else {
1392 y0 = y1 - height * DPI_BASE / ydpi;
1393 sp_export_value_set_px (base, "y0", y0);
1394 }
1395 }
1397 sp_export_value_set_px (base, "height", y1 - y0);
1398 sp_export_value_set (base, "bmheight", height);
1400 sp_export_detect_size(base);
1402 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1404 return;
1405 } // end of sp_export_area_y_value_changed()
1407 /// Called when x1-x0 or area width is changed
1408 static void
1409 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1410 {
1411 float x0, x1, xdpi, width, bmwidth;
1413 if (gtk_object_get_data (base, "update"))
1414 return;
1416 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1417 (base, "units"))) {
1418 return;
1419 }
1421 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1423 x0 = sp_export_value_get_px (base, "x0");
1424 x1 = sp_export_value_get_px (base, "x1");
1425 xdpi = sp_export_value_get (base, "xdpi");
1426 width = sp_export_value_get_px (base, "width");
1427 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1429 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1431 bmwidth = SP_EXPORT_MIN_SIZE;
1432 width = bmwidth * DPI_BASE / xdpi;
1433 sp_export_value_set_px (base, "width", width);
1434 }
1436 sp_export_value_set_px (base, "x1", x0 + width);
1437 sp_export_value_set (base, "bmwidth", bmwidth);
1439 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1441 return;
1442 } // end of sp_export_area_width_value_changed()
1444 /// Called when y1-y0 or area height is changed.
1445 static void
1446 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1447 {
1449 float y0, y1, ydpi, height, bmheight;
1451 if (gtk_object_get_data (base, "update"))
1452 return;
1454 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1455 (base, "units"))) {
1456 return;
1457 }
1459 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1461 y0 = sp_export_value_get_px (base, "y0");
1462 y1 = sp_export_value_get_px (base, "y1");
1463 ydpi = sp_export_value_get (base, "ydpi");
1464 height = sp_export_value_get_px (base, "height");
1465 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1467 if (bmheight < SP_EXPORT_MIN_SIZE) {
1468 bmheight = SP_EXPORT_MIN_SIZE;
1469 height = bmheight * DPI_BASE / ydpi;
1470 sp_export_value_set_px (base, "height", height);
1471 }
1473 sp_export_value_set_px (base, "y1", y0 + height);
1474 sp_export_value_set (base, "bmheight", bmheight);
1476 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1478 return;
1479 } // end of sp_export_area_height_value_changed()
1481 /**
1482 \brief A function to set the ydpi
1483 \param base The export dialog
1485 This function grabs all of the y values and then figures out the
1486 new bitmap size based on the changing dpi value. The dpi value is
1487 gotten from the xdpi setting as these can not currently be independent.
1488 */
1489 static void
1490 sp_export_set_image_y (GtkObject *base)
1491 {
1492 float y0, y1, xdpi;
1494 y0 = sp_export_value_get_px (base, "y0");
1495 y1 = sp_export_value_get_px (base, "y1");
1496 xdpi = sp_export_value_get (base, "xdpi");
1498 sp_export_value_set (base, "ydpi", xdpi);
1499 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1501 return;
1502 } // end of sp_export_set_image_y()
1504 /**
1505 \brief A function to set the xdpi
1506 \param base The export dialog
1508 This function grabs all of the x values and then figures out the
1509 new bitmap size based on the changing dpi value. The dpi value is
1510 gotten from the xdpi setting as these can not currently be independent.
1511 */
1512 static void
1513 sp_export_set_image_x (GtkObject *base)
1514 {
1515 float x0, x1, xdpi;
1517 x0 = sp_export_value_get_px (base, "x0");
1518 x1 = sp_export_value_get_px (base, "x1");
1519 xdpi = sp_export_value_get (base, "xdpi");
1521 sp_export_value_set (base, "ydpi", xdpi);
1522 sp_export_value_set (base, "bmwidth", (x1 - x0) * xdpi / DPI_BASE);
1524 return;
1525 } // end of sp_export_set_image_x()
1527 /// Called when pixel width is changed
1528 static void
1529 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1530 {
1531 float x0, x1, bmwidth, xdpi;
1533 if (gtk_object_get_data (base, "update"))
1534 return;
1536 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1537 (base, "units"))) {
1538 return;
1539 }
1541 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1543 x0 = sp_export_value_get_px (base, "x0");
1544 x1 = sp_export_value_get_px (base, "x1");
1545 bmwidth = sp_export_value_get (base, "bmwidth");
1547 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1548 bmwidth = SP_EXPORT_MIN_SIZE;
1549 sp_export_value_set (base, "bmwidth", bmwidth);
1550 }
1552 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1553 sp_export_value_set (base, "xdpi", xdpi);
1555 sp_export_set_image_y (base);
1557 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1559 return;
1560 } // end of sp_export_bitmap_width_value_changed()
1562 /// Called when pixel height is changed
1563 static void
1564 sp_export_bitmap_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1565 {
1566 float y0, y1, bmheight, xdpi;
1568 if (gtk_object_get_data (base, "update"))
1569 return;
1571 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1572 (base, "units"))) {
1573 return;
1574 }
1576 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1578 y0 = sp_export_value_get_px (base, "y0");
1579 y1 = sp_export_value_get_px (base, "y1");
1580 bmheight = sp_export_value_get (base, "bmheight");
1582 if (bmheight < SP_EXPORT_MIN_SIZE) {
1583 bmheight = SP_EXPORT_MIN_SIZE;
1584 sp_export_value_set (base, "bmheight", bmheight);
1585 }
1587 xdpi = bmheight * DPI_BASE / (y1 - y0);
1588 sp_export_value_set (base, "xdpi", xdpi);
1590 sp_export_set_image_x (base);
1592 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594 return;
1595 } // end of sp_export_bitmap_width_value_changed()
1597 /**
1598 \brief A function to adjust the bitmap width when the xdpi value changes
1599 \param adj The adjustment that was changed
1600 \param base The export dialog itself
1602 The first thing this function checks is to see if we are doing an
1603 update. If we are, this function just returns because there is another
1604 instance of it that will handle everything for us. If there is a
1605 units change, we also assume that everyone is being updated appropriately
1606 and there is nothing for us to do.
1608 If we're the highest level function, we set the update flag, and
1609 continue on our way.
1611 All of the values are grabbed using the \c sp_export_value_get functions
1612 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1613 xdpi value is saved in the preferences for the next time the dialog
1614 is opened. (does the selection dpi need to be set here?)
1616 A check is done to to ensure that we aren't outputing an invalid width,
1617 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1618 changed to make it valid.
1620 After all of this the bitmap width is changed.
1622 We also change the ydpi. This is a temporary hack as these can not
1623 currently be independent. This is likely to change in the future.
1624 */
1625 void
1626 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1627 {
1628 float x0, x1, xdpi, bmwidth;
1630 if (gtk_object_get_data (base, "update"))
1631 return;
1633 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1634 (base, "units"))) {
1635 return;
1636 }
1638 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1640 x0 = sp_export_value_get_px (base, "x0");
1641 x1 = sp_export_value_get_px (base, "x1");
1642 xdpi = sp_export_value_get (base, "xdpi");
1644 // remember xdpi setting
1645 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1647 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1649 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1650 bmwidth = SP_EXPORT_MIN_SIZE;
1651 if (x1 != x0)
1652 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1653 else
1654 xdpi = DPI_BASE;
1655 sp_export_value_set (base, "xdpi", xdpi);
1656 }
1658 sp_export_value_set (base, "bmwidth", bmwidth);
1660 sp_export_set_image_y (base);
1662 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1664 return;
1665 } // end of sp_export_xdpi_value_changed()
1668 /**
1669 \brief A function to change the area that is used for the exported
1670 bitmap.
1671 \param base This is the export dialog
1672 \param x0 Horizontal upper left hand corner of the picture in points
1673 \param y0 Vertical upper left hand corner of the picture in points
1674 \param x1 Horizontal lower right hand corner of the picture in points
1675 \param y1 Vertical lower right hand corner of the picture in points
1677 This function just calls \c sp_export_value_set_px for each of the
1678 parameters that is passed in. This allows for setting them all in
1679 one convient area.
1681 Update is set to suspend all of the other test running while all the
1682 values are being set up. This allows for a performance increase, but
1683 it also means that the wrong type won't be detected with only some of
1684 the values set. After all the values are set everyone is told that
1685 there has been an update.
1686 */
1687 static void
1688 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1689 {
1690 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1691 sp_export_value_set_px (base, "x1", x1);
1692 sp_export_value_set_px (base, "y1", y1);
1693 sp_export_value_set_px (base, "x0", x0);
1694 sp_export_value_set_px (base, "y0", y0);
1695 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1697 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1698 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1700 return;
1701 }
1703 /**
1704 \brief Sets the value of an adjustment
1705 \param base The export dialog
1706 \param key Which adjustment to set
1707 \param val What value to set it to
1709 This function finds the adjustment using the data stored in the
1710 export dialog. After finding the adjustment it then sets
1711 the value of it.
1712 */
1713 static void
1714 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1715 {
1716 GtkAdjustment *adj;
1718 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1720 gtk_adjustment_set_value (adj, val);
1721 }
1723 /**
1724 \brief A function to set a value using the units points
1725 \param base The export dialog
1726 \param key Which value should be set
1727 \param val What the value should be in points
1729 This function first gets the adjustment for the key that is passed
1730 in. It then figures out what units are currently being used in the
1731 dialog. After doing all of that, it then converts the incoming
1732 value and sets the adjustment.
1733 */
1734 static void
1735 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1736 {
1737 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1739 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1741 return;
1742 }
1744 /**
1745 \brief Get the value of an adjustment in the export dialog
1746 \param base The export dialog
1747 \param key Which adjustment is being looked for
1748 \return The value in the specified adjustment
1750 This function gets the adjustment from the data field in the export
1751 dialog. It then grabs the value from the adjustment.
1752 */
1753 static float
1754 sp_export_value_get ( GtkObject *base, const gchar *key )
1755 {
1756 GtkAdjustment *adj;
1758 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1760 return adj->value;
1761 } // end of sp_export_value_get()
1763 /**
1764 \brief Grabs a value in the export dialog and converts the unit
1765 to points
1766 \param base The export dialog
1767 \param key Which value should be returned
1768 \return The value in the adjustment in points
1770 This function, at its most basic, is a call to \c sp_export_value_get
1771 to get the value of the adjustment. It then finds the units that
1772 are being used by looking at the "units" attribute of the export
1773 dialog. Using that it converts the returned value into points.
1774 */
1775 static float
1776 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1777 {
1778 float value = sp_export_value_get(base, key);
1779 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1781 return sp_units_get_pixels (value, *unit);
1782 } // end of sp_export_value_get_px()
1784 /**
1785 \brief This function is called when the filename is changed by
1786 anyone. It resets the virgin bit.
1787 \param object Text entry box
1788 \param data The export dialog
1789 \return None
1791 This function gets called when the text area is modified. It is
1792 looking for the case where the text area is modified from its
1793 original value. In that case it sets the "filename-modified" bit
1794 to TRUE. If the text dialog returns back to the original text, the
1795 bit gets reset. This should stop simple mistakes.
1796 */
1797 static void
1798 sp_export_filename_modified (GtkObject * object, gpointer data)
1799 {
1800 GtkWidget * text_entry = (GtkWidget *)object;
1801 GtkWidget * export_dialog = (GtkWidget *)data;
1803 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1804 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1805 // printf("Modified: FALSE\n");
1806 } else {
1807 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1808 // printf("Modified: TRUE\n");
1809 }
1811 return;
1812 } // end sp_export_filename_modified
1814 /*
1815 Local Variables:
1816 mode:c++
1817 c-file-style:"stroustrup"
1818 c-file-offsets:((innamespace . 0)(inline-open . 0))
1819 indent-tabs-mode:nil
1820 fill-column:99
1821 End:
1822 */
1823 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :