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_xdpi_value_changed ( GtkAdjustment *adj,
77 GtkObject *base);
79 static void sp_export_selection_changed ( Inkscape::Application *inkscape,
80 Inkscape::Selection *selection,
81 GtkObject *base);
82 static void sp_export_selection_modified ( Inkscape::Application *inkscape,
83 Inkscape::Selection *selection,
84 guint flags,
85 GtkObject *base );
87 static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1);
88 static void sp_export_value_set (GtkObject *base, const gchar *key, double val);
89 static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val);
90 static float sp_export_value_get ( GtkObject *base, const gchar *key );
91 static float sp_export_value_get_px ( GtkObject *base, const gchar *key );
93 static void sp_export_filename_modified (GtkObject * object, gpointer data);
94 static inline void sp_export_find_default_selection(GtkWidget * dlg);
95 static void sp_export_detect_size(GtkObject * base);
97 static const gchar *prefs_path = "dialogs.export";
99 // these all need to be reinitialized to their defaults during dialog_destroy
100 static GtkWidget *dlg = NULL;
101 static win_data wd;
102 static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs
103 static gchar * original_name = NULL;
104 static gchar * doc_export_name = NULL;
105 static bool was_empty = TRUE;
107 /** What type of button is being pressed */
108 enum selection_type {
109 SELECTION_PAGE = 0, /**< Export the whole page */
110 SELECTION_DRAWING, /**< Export everything drawn on the page */
111 SELECTION_SELECTION, /**< Export everything that is selected */
112 SELECTION_CUSTOM, /**< Allows the user to set the region exported */
113 SELECTION_NUMBER_OF /**< A counter for the number of these guys */
114 };
116 /** A list of strings that is used both in the preferences, and in the
117 data fields to describe the various values of \c selection_type. */
118 static const char * selection_names[SELECTION_NUMBER_OF] = {
119 "page", "drawing", "selection", "custom"};
121 /** The names on the buttons for the various selection types. */
122 static const char * selection_labels[SELECTION_NUMBER_OF] = {
123 N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")};
125 static void
126 sp_export_dialog_destroy ( GtkObject *object, gpointer data )
127 {
128 sp_signal_disconnect_by_data (INKSCAPE, dlg);
130 wd.win = dlg = NULL;
131 wd.stop = 0;
132 x = -1000; y = -1000; w = 0; h = 0;
133 g_free(original_name);
134 original_name = NULL;
135 g_free(doc_export_name);
136 doc_export_name = NULL;
137 was_empty = TRUE;
139 return;
140 } // end of sp_export_dialog_destroy()
142 /// Called when dialog is closed or inkscape is shut down.
143 static bool
144 sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data )
145 {
147 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
148 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
150 if (x<0) x=0;
151 if (y<0) y=0;
153 prefs_set_int_attribute (prefs_path, "x", x);
154 prefs_set_int_attribute (prefs_path, "y", y);
155 prefs_set_int_attribute (prefs_path, "w", w);
156 prefs_set_int_attribute (prefs_path, "h", h);
158 return FALSE; // which means, go ahead and destroy it
160 } // end of sp_export_dialog_delete()
162 /**
163 \brief Creates a new spin button for the export dialog
164 \param key The name of the spin button
165 \param val A default value for the spin button
166 \param min Minimum value for the spin button
167 \param max Maximum value for the spin button
168 \param step The step size for the spin button
169 \param page Size of the page increment
170 \param us Unit selector that effects this spin button
171 \param t Table to put the spin button in
172 \param x X location in the table \c t to start with
173 \param y Y location in the table \c t to start with
174 \param ll Text to put on the left side of the spin button (optional)
175 \param lr Text to put on the right side of the spin button (optional)
176 \param digits Number of digits to display after the decimal
177 \param sensitive Whether the spin button is sensitive or not
178 \param cb Callback for when this spin button is changed (optional)
179 \param dlg Export dialog the spin button is being placed in
181 */
182 static void
183 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
184 float step, float page, GtkWidget *us,
185 GtkWidget *t, int x, int y,
186 const gchar *ll, const gchar *lr,
187 int digits, unsigned int sensitive,
188 GCallback cb, GtkWidget *dlg )
189 {
190 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
191 gtk_object_set_data (a, "key", key);
192 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
194 if (us) {
195 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
196 GTK_ADJUSTMENT (a) );
197 }
199 int pos = 0;
201 GtkWidget *l = NULL;
203 if (ll) {
205 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
206 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
207 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
208 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
209 gtk_widget_set_sensitive (l, sensitive);
210 pos += 1;
212 }
214 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
215 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
216 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
217 gtk_widget_set_size_request (sb, 80, -1);
218 gtk_widget_set_sensitive (sb, sensitive);
219 pos += 1;
221 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
223 if (lr) {
225 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
226 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
227 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
228 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
229 gtk_widget_set_sensitive (l, sensitive);
230 pos += 1;
232 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
233 }
235 if (cb)
236 gtk_signal_connect (a, "value_changed", cb, dlg);
238 return;
239 } // end of sp_export_spinbutton_new()
242 static GtkWidget *
243 sp_export_dialog_area_frame (GtkWidget * dlg)
244 {
245 GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
247 f = gtk_frame_new (_("Export area"));
248 vb = gtk_vbox_new (FALSE, 2);
249 gtk_container_add (GTK_CONTAINER (f), vb);
251 /* Units box */
252 unitbox = gtk_hbox_new (FALSE, 0);
253 gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
254 /* gets added to the vbox later, but the unit selector is needed
255 earlier than that */
257 us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
258 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
259 if (desktop)
260 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
261 gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
262 l = gtk_label_new (_("Units:"));
263 gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
264 gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
266 hb = gtk_hbox_new (TRUE, 0);
267 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
268 gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
270 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
271 b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
272 gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
273 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
274 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
275 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
276 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
277 }
279 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
280 G_CALLBACK (sp_export_selection_changed), dlg );
281 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
282 G_CALLBACK (sp_export_selection_modified), dlg );
283 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
284 G_CALLBACK (sp_export_selection_changed), dlg );
286 t = gtk_table_new (2, 6, FALSE);
287 gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
288 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
289 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
290 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
292 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
293 t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
294 G_CALLBACK ( sp_export_area_x_value_changed),
295 dlg );
297 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
298 t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
299 G_CALLBACK (sp_export_area_x_value_changed),
300 dlg );
302 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
303 us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
304 G_CALLBACK
305 (sp_export_area_width_value_changed),
306 dlg );
308 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
309 t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
310 G_CALLBACK (sp_export_area_y_value_changed),
311 dlg );
313 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
314 t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
315 G_CALLBACK (sp_export_area_y_value_changed),
316 dlg );
318 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
319 us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
320 G_CALLBACK (sp_export_area_height_value_changed),
321 dlg );
323 /* Adding in the unit box */
324 gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
326 return f;
327 } // end of sp_export_dialog_area_frame
330 void
331 sp_export_dialog (void)
332 {
333 if (!dlg) {
334 GtkWidget *vb, *hb;
336 gchar title[500];
337 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
339 dlg = sp_window_new (title, TRUE);
341 if (x == -1000 || y == -1000) {
342 x = prefs_get_int_attribute (prefs_path, "x", 0);
343 y = prefs_get_int_attribute (prefs_path, "y", 0);
344 }
346 if (w ==0 || h == 0) {
347 w = prefs_get_int_attribute (prefs_path, "w", 0);
348 h = prefs_get_int_attribute (prefs_path, "h", 0);
349 }
351 if (x<0) x=0;
352 if (y<0) y=0;
354 if (x != 0 || y != 0) {
355 gtk_window_move ((GtkWindow *) dlg, x, y);
356 } else {
357 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
358 }
360 if (w && h)
361 gtk_window_resize ((GtkWindow *) dlg, w, h);
363 sp_transientize (dlg);
364 wd.win = dlg;
365 wd.stop = 0;
367 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
368 G_CALLBACK (sp_transientize_callback), &wd);
370 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
371 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
373 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
374 G_CALLBACK (sp_export_dialog_destroy), dlg);
376 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
377 G_CALLBACK (sp_export_dialog_delete), dlg);
379 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
380 G_CALLBACK (sp_export_dialog_delete), dlg);
382 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
383 G_CALLBACK (sp_dialog_hide), dlg);
385 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
386 G_CALLBACK (sp_dialog_unhide), dlg);
388 GtkTooltips *tt = gtk_tooltips_new();
390 vb = gtk_vbox_new (FALSE, 4);
391 gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
392 gtk_container_add (GTK_CONTAINER (dlg), vb);
394 /* Export area frame */
395 {
396 GtkWidget *f = sp_export_dialog_area_frame(dlg);
397 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
398 }
400 /* Bitmap size frame */
401 {
402 GtkWidget *f = gtk_frame_new (_("Bitmap size"));
403 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
404 GtkWidget *t = gtk_table_new (2, 5, FALSE);
405 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
406 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
407 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
408 gtk_container_add (GTK_CONTAINER (f), t);
410 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
411 NULL, t, 0, 0,
412 _("_Width:"), _("pixels at"), 0, 1,
413 G_CALLBACK
414 (sp_export_bitmap_width_value_changed),
415 dlg );
417 sp_export_spinbutton_new ( "xdpi",
418 prefs_get_double_attribute
419 ( "dialogs.export.defaultxdpi",
420 "value", DPI_BASE),
421 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
422 NULL, _("dp_i"), 2, 1,
423 G_CALLBACK (sp_export_xdpi_value_changed),
424 dlg );
426 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0,
427 NULL, t, 0, 1, _("Height:"), _("pixels at"),
428 0, 0, NULL, dlg );
430 /** \todo
431 * Needs fixing: there's no way to set ydpi currently, so we use
432 * the defaultxdpi value here, too...
433 */
434 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
435 ( "dialogs.export.defaultxdpi",
436 "value", DPI_BASE),
437 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
438 NULL, _("dpi"), 2, 0, NULL, dlg );
439 }
441 /* File entry */
442 {
443 GtkWidget *frame = gtk_frame_new ("");
444 GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
445 gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
446 gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
448 GtkWidget *fe = gtk_entry_new ();
450 /*
451 * set the default filename to be that of the current path + document
452 * with .png extension
453 *
454 * One thing to notice here is that this filename may get
455 * overwritten, but it won't happen here. The filename gets
456 * written into the text field, but then the button to select
457 * the area gets set. In that code the filename can be changed
458 * if there are some with presidence in the document. So, while
459 * this code sets the name first, it may not be the one users
460 * really see.
461 */
462 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
463 {
464 gchar *name;
465 SPDocument * doc = SP_ACTIVE_DOCUMENT;
466 const gchar *uri = SP_DOCUMENT_URI (doc);
467 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
468 const gchar * text_extension = repr->attribute("inkscape:output_extension");
469 Inkscape::Extension::Output * oextension = NULL;
471 if (text_extension != NULL) {
472 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
473 }
475 if (oextension != NULL) {
476 gchar * old_extension = oextension->get_extension();
477 if (g_str_has_suffix(uri, old_extension)) {
478 gchar * uri_copy;
479 gchar * extension_point;
480 gchar * final_name;
482 uri_copy = g_strdup(uri);
483 extension_point = g_strrstr(uri_copy, old_extension);
484 extension_point[0] = '\0';
486 final_name = g_strconcat(uri_copy, ".png", NULL);
487 gtk_entry_set_text (GTK_ENTRY (fe), final_name);
489 g_free(final_name);
490 g_free(uri_copy);
491 }
492 } else {
493 name = g_strconcat(uri, ".png", NULL);
494 gtk_entry_set_text (GTK_ENTRY (fe), name);
495 g_free(name);
496 }
498 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
499 }
500 g_signal_connect ( G_OBJECT (fe), "changed",
501 G_CALLBACK (sp_export_filename_modified), dlg);
503 hb = gtk_hbox_new (FALSE, 5);
504 gtk_container_add (GTK_CONTAINER (frame), hb);
505 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
507 {
508 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
509 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
510 g_signal_connect ( G_OBJECT (b), "clicked",
511 G_CALLBACK (sp_export_browse_clicked), NULL );
512 }
514 gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
515 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
516 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
517 original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
518 // pressing enter in the filename field is the same as clicking export:
519 g_signal_connect ( G_OBJECT (fe), "activate",
520 G_CALLBACK (sp_export_export_clicked), dlg );
521 // focus is in the filename initially:
522 gtk_widget_grab_focus (GTK_WIDGET (fe));
524 // mnemonic in frame label moves focus to filename:
525 gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
526 }
528 /* Buttons */
529 hb = gtk_hbox_new (FALSE, 0);
530 gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
532 {
533 GtkWidget *b = gtk_button_new ();
534 GtkWidget *l = gtk_label_new ("");
535 gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
536 gtk_container_add (GTK_CONTAINER(b), l);
537 gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
538 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
539 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
540 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
541 }
543 gtk_widget_show_all (vb);
545 } // end of if (!dlg)
547 sp_export_find_default_selection(dlg);
549 gtk_window_present ((GtkWindow *) dlg);
551 return;
552 } // end of sp_export_dialog()
554 static inline void
555 sp_export_find_default_selection(GtkWidget * dlg)
556 {
557 selection_type key = SELECTION_NUMBER_OF;
559 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
560 key = SELECTION_SELECTION;
561 }
563 /* Try using the preferences */
564 if (key == SELECTION_NUMBER_OF) {
565 const gchar *what = NULL;
566 int i = SELECTION_NUMBER_OF;
568 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
570 if (what != NULL) {
571 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
572 if (!strcmp (what, selection_names[i])) {
573 break;
574 }
575 }
576 }
578 key = (selection_type)i;
579 }
581 if (key == SELECTION_NUMBER_OF) {
582 key = SELECTION_SELECTION;
583 }
585 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
586 selection_names[key]);
587 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
589 return;
590 }
593 /**
594 * \brief If selection changed or a different document activated, we must
595 * recalculate any chosen areas
596 *
597 */
598 static void
599 sp_export_selection_changed ( Inkscape::Application *inkscape,
600 Inkscape::Selection *selection,
601 GtkObject *base )
602 {
603 selection_type current_key;
604 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
606 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
607 (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
608 was_empty) {
609 gtk_toggle_button_set_active
610 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
611 TRUE );
612 }
613 was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
615 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
617 if (inkscape &&
618 SP_IS_INKSCAPE (inkscape) &&
619 selection &&
620 SELECTION_CUSTOM != current_key) {
621 GtkToggleButton * button;
622 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
623 sp_export_area_toggled(button, base);
624 } // end of if()
626 return;
627 } // end of sp_export_selection_changed()
629 static void
630 sp_export_selection_modified ( Inkscape::Application *inkscape,
631 Inkscape::Selection *selection,
632 guint flags,
633 GtkObject *base )
634 {
635 selection_type current_key;
636 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
638 switch (current_key) {
639 case SELECTION_DRAWING:
640 if ( SP_ACTIVE_DESKTOP ) {
641 SPDocument *doc;
642 NRRect bbox;
643 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
644 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
646 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
647 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
648 }
649 }
650 break;
651 case SELECTION_SELECTION:
652 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
653 NRRect bbox;
654 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
655 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
656 }
657 break;
658 default:
659 /* Do nothing for page or for custom */
660 break;
661 }
663 return;
664 }
666 /// Called when one of the selection buttons was toggled.
667 static void
668 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
669 {
670 if (gtk_object_get_data (base, "update"))
671 return;
673 selection_type key, old_key;
674 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
675 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
677 /* Ignore all "turned off" events unless we're the only active button */
678 if (!gtk_toggle_button_get_active (tb) ) {
680 /* Don't let the current selection be deactived - but rerun the
681 activate to allow the user to renew the values */
682 if (key == old_key) {
683 gtk_toggle_button_set_active ( tb, TRUE );
684 }
686 return;
687 }
689 /* Turn off the currently active button unless it's us */
690 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
692 if (old_key != key) {
693 gtk_toggle_button_set_active
694 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
695 FALSE );
696 }
698 if ( SP_ACTIVE_DESKTOP )
699 {
700 SPDocument *doc;
701 NRRect bbox;
702 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
704 /* Notice how the switch is used to 'fall through' here to get
705 various backups. If you modify this without noticing you'll
706 probabaly screw something up. */
707 switch (key) {
708 case SELECTION_SELECTION:
709 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
710 {
711 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
712 /* Only if there is a selection that we can set
713 do we break, otherwise we fall through to the
714 drawing */
715 // std::cout << "Using selection: SELECTION" << std::endl;
716 key = SELECTION_SELECTION;
717 break;
718 }
719 case SELECTION_DRAWING:
720 /** \todo
721 * This returns wrong values if the document has a viewBox.
722 */
723 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
725 /* If the drawing is valid, then we'll use it and break
726 otherwise we drop through to the page settings */
727 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
728 // std::cout << "Using selection: DRAWING" << std::endl;
729 key = SELECTION_DRAWING;
730 break;
731 }
732 case SELECTION_PAGE:
733 bbox.x0 = 0.0;
734 bbox.y0 = 0.0;
735 bbox.x1 = sp_document_width (doc);
736 bbox.y1 = sp_document_height (doc);
737 // std::cout << "Using selection: PAGE" << std::endl;
738 key = SELECTION_PAGE;
739 break;
740 case SELECTION_CUSTOM:
741 default:
742 break;
743 } // switch
745 // remember area setting
746 prefs_set_string_attribute ( "dialogs.export.exportarea",
747 "value", selection_names[key]);
749 if (key != SELECTION_CUSTOM) {
750 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
751 }
753 } // end of if ( SP_ACTIVE_DESKTOP )
756 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
757 GtkWidget * file_entry;
758 const gchar * filename = NULL;
759 float xdpi = 0.0, ydpi = 0.0;
761 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
763 switch (key) {
764 case SELECTION_PAGE:
765 case SELECTION_DRAWING: {
766 SPDocument * doc = SP_ACTIVE_DOCUMENT;
767 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
768 const gchar * dpi_string;
770 filename = repr->attribute("inkscape:export-filename");
772 dpi_string = NULL;
773 dpi_string = repr->attribute("inkscape:export-xdpi");
774 if (dpi_string != NULL) {
775 xdpi = atof(dpi_string);
776 }
778 dpi_string = NULL;
779 dpi_string = repr->attribute("inkscape:export-ydpi");
780 if (dpi_string != NULL) {
781 ydpi = atof(dpi_string);
782 }
784 if (filename == NULL) {
785 if (doc_export_name != NULL) {
786 filename = g_strdup(doc_export_name);
787 } else {
788 filename = g_strdup("");
789 }
790 }
792 break;
793 }
794 case SELECTION_SELECTION:
795 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
796 const GSList * reprlst;
797 bool filename_search = TRUE;
798 bool xdpi_search = TRUE;
799 bool ydpi_search = TRUE;
801 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
802 for(; reprlst != NULL &&
803 filename_search &&
804 xdpi_search &&
805 ydpi_search;
806 reprlst = reprlst->next) {
807 const gchar * dpi_string;
808 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
810 if (filename_search) {
811 filename = repr->attribute("inkscape:export-filename");
812 if (filename != NULL)
813 filename_search = FALSE;
814 }
816 if (xdpi_search) {
817 dpi_string = NULL;
818 dpi_string = repr->attribute("inkscape:export-xdpi");
819 if (dpi_string != NULL) {
820 xdpi = atof(dpi_string);
821 xdpi_search = FALSE;
822 }
823 }
825 if (ydpi_search) {
826 dpi_string = NULL;
827 dpi_string = repr->attribute("inkscape:export-ydpi");
828 if (dpi_string != NULL) {
829 ydpi = atof(dpi_string);
830 ydpi_search = FALSE;
831 }
832 }
833 }
835 /* If we still don't have a filename -- let's build
836 one that's nice */
837 if (filename == NULL) {
838 const gchar * id = NULL;
839 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
840 for(; reprlst != NULL; reprlst = reprlst->next) {
841 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
842 if (repr->attribute("id")) {
843 id = repr->attribute("id");
844 break;
845 }
846 }
847 if (id == NULL) /* This should never happen */
848 id = "bitmap";
850 gchar * directory = NULL;
851 const gchar * file_entry_text;
853 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
854 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
855 // std::cout << "Directory from dialog" << std::endl;
856 directory = g_dirname(file_entry_text);
857 }
859 if (directory == NULL) {
860 /* Grab document directory */
861 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
862 // std::cout << "Directory from document" << std::endl;
863 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
864 }
865 }
867 if (directory == NULL) {
868 // std::cout << "Home Directory" << std::endl;
869 directory = homedir_path(NULL);
870 }
872 gchar * id_ext = g_strconcat(id, ".png", NULL);
873 filename = g_build_filename(directory, id_ext, NULL);
874 g_free(directory);
875 g_free(id_ext);
876 }
877 }
878 break;
879 case SELECTION_CUSTOM:
880 default:
881 break;
882 }
884 if (filename != NULL) {
885 g_free(original_name);
886 original_name = g_strdup(filename);
887 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
888 }
890 if (xdpi != 0.0) {
891 sp_export_value_set(base, "xdpi", xdpi);
892 }
894 /* These can't be seperate, and setting x sets y, so for
895 now setting this is disabled. Hopefully it won't be in
896 the future */
897 if (FALSE && ydpi != 0.0) {
898 sp_export_value_set(base, "ydpi", ydpi);
899 }
900 }
902 return;
903 } // end of sp_export_area_toggled()
905 /// Called when dialog is deleted
906 static gint
907 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
908 {
909 g_object_set_data (base, "cancel", (gpointer) 1);
910 return TRUE;
911 } // end of sp_export_progress_delete()
913 /// Called when progress is cancelled
914 static void
915 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
916 {
917 g_object_set_data (base, "cancel", (gpointer) 1);
918 } // end of sp_export_progress_cancel()
920 /// Called for every progress iteration
921 static unsigned int
922 sp_export_progress_callback (float value, void *data)
923 {
924 GtkWidget *prg;
925 int evtcount;
927 if (g_object_get_data ((GObject *) data, "cancel"))
928 return FALSE;
930 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
931 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
933 evtcount = 0;
934 while ((evtcount < 16) && gdk_events_pending ()) {
935 gtk_main_iteration_do (FALSE);
936 evtcount += 1;
937 }
939 gtk_main_iteration_do (FALSE);
941 return TRUE;
943 } // end of sp_export_progress_callback()
945 /// Called when export button is clicked
946 static void
947 sp_export_export_clicked (GtkButton *button, GtkObject *base)
948 {
949 if (!SP_ACTIVE_DESKTOP) return;
951 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
952 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
954 float const x0 = sp_export_value_get_px(base, "x0");
955 float const y0 = sp_export_value_get_px(base, "y0");
956 float const x1 = sp_export_value_get_px(base, "x1");
957 float const y1 = sp_export_value_get_px(base, "y1");
958 float const xdpi = sp_export_value_get(base, "xdpi");
959 float const ydpi = sp_export_value_get(base, "ydpi");
960 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
961 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
963 if (filename == NULL || *filename == '\0') {
964 sp_ui_error_dialog(_("You have to enter a filename"));
965 return;
966 }
968 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
969 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
970 return;
971 }
973 gchar *dirname = g_dirname(filename);
974 if ( dirname == NULL
975 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
976 {
977 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
978 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
979 safeDir);
980 sp_ui_error_dialog(error);
981 g_free(safeDir);
982 g_free(error);
983 g_free(dirname);
984 return;
985 }
986 g_free(dirname);
988 SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
989 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
990 char *fn;
991 gchar *text;
993 dlg = gtk_dialog_new ();
994 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
995 prg = gtk_progress_bar_new ();
996 sp_transientize (dlg);
997 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
998 g_object_set_data ((GObject *) base, "progress", prg);
999 fn = g_path_get_basename (filename);
1000 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
1001 fn, width, height);
1002 g_free (fn);
1003 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1004 g_free (text);
1005 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1006 GTK_PROGRESS_LEFT_TO_RIGHT);
1007 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1008 prg, FALSE, FALSE, 4 );
1009 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1010 GTK_STOCK_CANCEL,
1011 GTK_RESPONSE_CANCEL );
1013 g_signal_connect ( (GObject *) dlg, "delete_event",
1014 (GCallback) sp_export_progress_delete, base);
1015 g_signal_connect ( (GObject *) btn, "clicked",
1016 (GCallback) sp_export_progress_cancel, base);
1017 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1018 gtk_widget_show_all (dlg);
1020 /* Do export */
1021 if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename,
1022 x0, y0, x1, y1, width, height,
1023 nv->pagecolor,
1024 sp_export_progress_callback, base)) {
1025 gchar * error;
1026 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1027 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1028 sp_ui_error_dialog(error);
1029 g_free(safeFile);
1030 g_free(error);
1031 }
1033 /* Reset the filename so that it can be changed again by changing
1034 selections and all that */
1035 g_free(original_name);
1036 original_name = g_strdup(filename);
1037 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1039 gtk_widget_destroy (dlg);
1040 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1042 /* Setup the values in the document */
1043 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1044 case SELECTION_PAGE:
1045 case SELECTION_DRAWING: {
1046 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1047 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1048 bool modified = FALSE;
1049 const gchar * temp_string;
1051 bool saved = sp_document_get_undo_sensitive(doc);
1052 sp_document_set_undo_sensitive(doc, FALSE);
1054 temp_string = repr->attribute("inkscape:export-filename");
1055 if (temp_string == NULL || strcmp(temp_string, filename)) {
1056 repr->setAttribute("inkscape:export-filename", filename);
1057 modified = TRUE;
1058 }
1059 temp_string = repr->attribute("inkscape:export-xdpi");
1060 if (temp_string == NULL || xdpi != atof(temp_string)) {
1061 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1062 modified = TRUE;
1063 }
1064 temp_string = repr->attribute("inkscape:export-ydpi");
1065 if (temp_string == NULL || xdpi != atof(temp_string)) {
1066 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1067 modified = TRUE;
1068 }
1070 if (modified)
1071 repr->setAttribute("sodipodi:modified", "TRUE");
1072 sp_document_set_undo_sensitive(doc, saved);
1073 break;
1074 }
1075 case SELECTION_SELECTION: {
1076 const GSList * reprlst;
1077 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1078 bool modified = FALSE;
1080 bool saved = sp_document_get_undo_sensitive(doc);
1081 sp_document_set_undo_sensitive(doc, FALSE);
1082 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1084 for(; reprlst != NULL; reprlst = reprlst->next) {
1085 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1086 const gchar * temp_string;
1088 if (repr->attribute("id") == NULL ||
1089 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1090 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1091 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1092 temp_string = repr->attribute("inkscape:export-filename");
1093 if (temp_string == NULL || strcmp(temp_string, filename)) {
1094 repr->setAttribute("inkscape:export-filename", filename);
1095 modified = TRUE;
1096 }
1097 }
1098 temp_string = repr->attribute("inkscape:export-xdpi");
1099 if (temp_string == NULL || xdpi != atof(temp_string)) {
1100 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1101 modified = TRUE;
1102 }
1103 temp_string = repr->attribute("inkscape:export-ydpi");
1104 if (temp_string == NULL || xdpi != atof(temp_string)) {
1105 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1106 modified = TRUE;
1107 }
1108 }
1110 if (modified) {
1111 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1112 repr->setAttribute("sodipodi:modified", "TRUE");
1113 }
1115 sp_document_set_undo_sensitive(doc, saved);
1116 break;
1117 }
1118 default:
1119 break;
1120 }
1123 return;
1124 } // end of sp_export_export_clicked()
1126 /// Called when Browse button is clicked
1127 static void
1128 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1129 {
1130 GtkWidget *fs, *fe;
1131 const gchar *filename;
1133 fs = gtk_file_selection_new (_("Select a filename for exporting"));
1134 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1136 sp_transientize (fs);
1138 gtk_window_set_modal(GTK_WINDOW (fs), true);
1140 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1142 if (*filename == '\0') {
1143 filename = homedir_path(NULL);
1144 }
1146 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1148 g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1149 "clicked",
1150 G_CALLBACK (sp_export_browse_store),
1151 (gpointer) fs );
1153 g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1154 "clicked",
1155 G_CALLBACK (gtk_widget_destroy),
1156 (gpointer) fs );
1158 g_signal_connect_swapped ( GTK_OBJECT
1159 (GTK_FILE_SELECTION (fs)->cancel_button),
1160 "clicked",
1161 G_CALLBACK (gtk_widget_destroy),
1162 (gpointer) fs );
1164 gtk_widget_show (fs);
1166 return;
1167 } // end of sp_export_browse_clicked()
1169 /// Called when OK clicked in file dialog
1170 static void
1171 sp_export_browse_store (GtkButton *button, gpointer userdata)
1172 {
1173 GtkWidget *fs = (GtkWidget *)userdata, *fe;
1174 const gchar *file;
1176 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1178 file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1179 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1180 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1181 g_free(utf8file);
1183 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1185 return;
1186 } // end of sp_export_browse_store()
1188 // TODO: Move this to nr-rect-fns.h.
1189 static bool
1190 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1191 {
1192 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1193 return (
1194 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1195 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1196 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1197 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1198 );
1199 }
1201 /**
1202 \brief This function is used to detect the current selection setting
1203 based on the values in the x0, y0, x1 and y0 fields.
1204 \param base The export dialog itself
1206 One of the most confusing parts of this function is why the array
1207 is built at the beginning. What needs to happen here is that we
1208 should always check the current selection to see if it is the valid
1209 one. While this is a performance improvement it is also a usability
1210 one during the cases where things like selections and drawings match
1211 size. This way buttons change less 'randomly' (atleast in the eyes
1212 of the user). To do this an array is built where the current selection
1213 type is placed first, and then the others in an order from smallest
1214 to largest (this can be configured by reshuffling \c test_order).
1216 All of the values in this function are rounded to two decimal places
1217 because that is what is shown to the user. While everything is kept
1218 more accurate than that, the user can't control more acurrate than
1219 that, so for this to work for them - it needs to check on that level
1220 of accuracy.
1222 \todo finish writing this up
1223 */
1224 static void
1225 sp_export_detect_size(GtkObject * base) {
1226 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1227 selection_type this_test[SELECTION_NUMBER_OF + 1];
1228 selection_type key = SELECTION_NUMBER_OF;
1230 NR::Point x(sp_export_value_get_px (base, "x0"),
1231 sp_export_value_get_px (base, "y0"));
1232 NR::Point y(sp_export_value_get_px (base, "x1"),
1233 sp_export_value_get_px (base, "y1"));
1234 NR::Rect current_bbox(x, y);
1235 //std::cout << "Current " << current_bbox;
1237 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1238 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1239 this_test[i + 1] = test_order[i];
1240 }
1242 for (int i = 0;
1243 i < SELECTION_NUMBER_OF + 1 &&
1244 key == SELECTION_NUMBER_OF &&
1245 SP_ACTIVE_DESKTOP != NULL;
1246 i++) {
1247 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1248 switch (this_test[i]) {
1249 case SELECTION_SELECTION:
1250 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1251 NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1253 //std::cout << "Selection " << bbox;
1254 if (sp_export_bbox_equal(bbox,current_bbox)) {
1255 key = SELECTION_SELECTION;
1256 }
1257 }
1258 break;
1259 case SELECTION_DRAWING: {
1260 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1262 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1264 // std::cout << "Drawing " << bbox2;
1265 if (sp_export_bbox_equal(bbox,current_bbox)) {
1266 key = SELECTION_DRAWING;
1267 }
1268 break;
1269 }
1271 case SELECTION_PAGE: {
1272 SPDocument *doc;
1274 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1276 NR::Point x(0.0, 0.0);
1277 NR::Point y(sp_document_width(doc),
1278 sp_document_height(doc));
1279 NR::Rect bbox(x, y);
1281 // std::cout << "Page " << bbox;
1282 if (sp_export_bbox_equal(bbox,current_bbox)) {
1283 key = SELECTION_PAGE;
1284 }
1286 break;
1287 }
1288 default:
1289 break;
1290 }
1291 }
1292 // std::cout << std::endl;
1294 if (key == SELECTION_NUMBER_OF) {
1295 key = SELECTION_CUSTOM;
1296 }
1298 /* We're now using a custom size, not a fixed one */
1299 /* printf("Detecting state: %s\n", selection_names[key]); */
1300 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1301 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1302 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1303 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1305 return;
1306 } /* sp_export_detect_size */
1308 /// Called when area x0 value is changed
1309 static void
1310 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1311 {
1312 float x0, x1, xdpi, width;
1314 if (gtk_object_get_data (base, "update"))
1315 return;
1317 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1318 (base, "units")))
1319 {
1320 return;
1321 }
1323 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1325 x0 = sp_export_value_get_px (base, "x0");
1326 x1 = sp_export_value_get_px (base, "x1");
1327 xdpi = sp_export_value_get (base, "xdpi");
1329 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1331 if (width < SP_EXPORT_MIN_SIZE) {
1332 const gchar *key;
1333 width = SP_EXPORT_MIN_SIZE;
1334 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1336 if (!strcmp (key, "x0")) {
1337 x1 = x0 + width * DPI_BASE / xdpi;
1338 sp_export_value_set_px (base, "x1", x1);
1339 } else {
1340 x0 = x1 - width * DPI_BASE / xdpi;
1341 sp_export_value_set_px (base, "x0", x0);
1342 }
1343 }
1345 sp_export_value_set_px (base, "width", x1 - x0);
1346 sp_export_value_set (base, "bmwidth", width);
1348 sp_export_detect_size(base);
1350 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1352 return;
1353 } // end of sp_export_area_x_value_changed()
1355 /// Called when area y0 value is changed.
1356 static void
1357 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1358 {
1359 float y0, y1, ydpi, height;
1361 if (gtk_object_get_data (base, "update"))
1362 return;
1364 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1365 (base, "units")))
1366 {
1367 return;
1368 }
1370 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1372 y0 = sp_export_value_get_px (base, "y0");
1373 y1 = sp_export_value_get_px (base, "y1");
1374 ydpi = sp_export_value_get (base, "ydpi");
1376 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1378 if (height < SP_EXPORT_MIN_SIZE) {
1379 const gchar *key;
1380 height = SP_EXPORT_MIN_SIZE;
1381 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1382 if (!strcmp (key, "y0")) {
1383 y1 = y0 + height * DPI_BASE / ydpi;
1384 sp_export_value_set_px (base, "y1", y1);
1385 } else {
1386 y0 = y1 - height * DPI_BASE / ydpi;
1387 sp_export_value_set_px (base, "y0", y0);
1388 }
1389 }
1391 sp_export_value_set_px (base, "height", y1 - y0);
1392 sp_export_value_set (base, "bmheight", height);
1394 sp_export_detect_size(base);
1396 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1398 return;
1399 } // end of sp_export_area_y_value_changed()
1401 /// Called when x1-x0 or area width is changed
1402 static void
1403 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1404 {
1405 float x0, x1, xdpi, width, bmwidth;
1407 if (gtk_object_get_data (base, "update"))
1408 return;
1410 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1411 (base, "units"))) {
1412 return;
1413 }
1415 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1417 x0 = sp_export_value_get_px (base, "x0");
1418 x1 = sp_export_value_get_px (base, "x1");
1419 xdpi = sp_export_value_get (base, "xdpi");
1420 width = sp_export_value_get_px (base, "width");
1421 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1423 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1425 bmwidth = SP_EXPORT_MIN_SIZE;
1426 width = bmwidth * DPI_BASE / xdpi;
1427 sp_export_value_set_px (base, "width", width);
1428 }
1430 sp_export_value_set_px (base, "x1", x0 + width);
1431 sp_export_value_set (base, "bmwidth", bmwidth);
1433 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1435 return;
1436 } // end of sp_export_area_width_value_changed()
1438 /// Called when y1-y0 or area height is changed.
1439 static void
1440 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1441 {
1443 float y0, y1, ydpi, height, bmheight;
1445 if (gtk_object_get_data (base, "update"))
1446 return;
1448 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1449 (base, "units"))) {
1450 return;
1451 }
1453 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1455 y0 = sp_export_value_get_px (base, "y0");
1456 y1 = sp_export_value_get_px (base, "y1");
1457 ydpi = sp_export_value_get (base, "ydpi");
1458 height = sp_export_value_get_px (base, "height");
1459 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1461 if (bmheight < SP_EXPORT_MIN_SIZE) {
1462 bmheight = SP_EXPORT_MIN_SIZE;
1463 height = bmheight * DPI_BASE / ydpi;
1464 sp_export_value_set_px (base, "height", height);
1465 }
1467 sp_export_value_set_px (base, "y1", y0 + height);
1468 sp_export_value_set (base, "bmheight", bmheight);
1470 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1472 return;
1473 } // end of sp_export_area_height_value_changed()
1475 /**
1476 \brief A function to set the ydpi
1477 \param base The export dialog
1479 This function grabs all of the y values and then figures out the
1480 new bitmap size based on the changing dpi value. The dpi value is
1481 gotten from the xdpi setting as these can not currently be independent.
1482 */
1483 static void
1484 sp_export_set_image_y (GtkObject *base)
1485 {
1486 float y0, y1, xdpi;
1488 y0 = sp_export_value_get_px (base, "y0");
1489 y1 = sp_export_value_get_px (base, "y1");
1490 xdpi = sp_export_value_get (base, "xdpi");
1492 sp_export_value_set (base, "ydpi", xdpi);
1493 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1495 return;
1496 } // end of sp_export_set_image_y()
1498 /// Called when pixel width is changed
1499 static void
1500 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1501 {
1502 float x0, x1, bmwidth, xdpi;
1504 if (gtk_object_get_data (base, "update"))
1505 return;
1507 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1508 (base, "units"))) {
1509 return;
1510 }
1512 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1514 x0 = sp_export_value_get_px (base, "x0");
1515 x1 = sp_export_value_get_px (base, "x1");
1516 bmwidth = sp_export_value_get (base, "bmwidth");
1518 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1519 bmwidth = SP_EXPORT_MIN_SIZE;
1520 sp_export_value_set (base, "bmwidth", bmwidth);
1521 }
1523 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1524 sp_export_value_set (base, "xdpi", xdpi);
1526 sp_export_set_image_y (base);
1528 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1530 return;
1531 } // end of sp_export_bitmap_width_value_changed()
1533 /**
1534 \brief A function to adjust the bitmap width when the xdpi value changes
1535 \param adj The adjustment that was changed
1536 \param base The export dialog itself
1538 The first thing this function checks is to see if we are doing an
1539 update. If we are, this function just returns because there is another
1540 instance of it that will handle everything for us. If there is a
1541 units change, we also assume that everyone is being updated appropriately
1542 and there is nothing for us to do.
1544 If we're the highest level function, we set the update flag, and
1545 continue on our way.
1547 All of the values are grabbed using the \c sp_export_value_get functions
1548 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1549 xdpi value is saved in the preferences for the next time the dialog
1550 is opened. (does the selection dpi need to be set here?)
1552 A check is done to to ensure that we aren't outputing an invalid width,
1553 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1554 changed to make it valid.
1556 After all of this the bitmap width is changed.
1558 We also change the ydpi. This is a temporary hack as these can not
1559 currently be independent. This is likely to change in the future.
1560 */
1561 void
1562 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1563 {
1564 float x0, x1, xdpi, bmwidth;
1566 if (gtk_object_get_data (base, "update"))
1567 return;
1569 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1570 (base, "units"))) {
1571 return;
1572 }
1574 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1576 x0 = sp_export_value_get_px (base, "x0");
1577 x1 = sp_export_value_get_px (base, "x1");
1578 xdpi = sp_export_value_get (base, "xdpi");
1580 // remember xdpi setting
1581 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1583 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1585 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1586 bmwidth = SP_EXPORT_MIN_SIZE;
1587 if (x1 != x0)
1588 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1589 else
1590 xdpi = DPI_BASE;
1591 sp_export_value_set (base, "xdpi", xdpi);
1592 }
1594 sp_export_value_set (base, "bmwidth", bmwidth);
1596 sp_export_set_image_y (base);
1598 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1600 return;
1601 } // end of sp_export_xdpi_value_changed()
1604 /**
1605 \brief A function to change the area that is used for the exported
1606 bitmap.
1607 \param base This is the export dialog
1608 \param x0 Horizontal upper left hand corner of the picture in points
1609 \param y0 Vertical upper left hand corner of the picture in points
1610 \param x1 Horizontal lower right hand corner of the picture in points
1611 \param y1 Vertical lower right hand corner of the picture in points
1613 This function just calls \c sp_export_value_set_px for each of the
1614 parameters that is passed in. This allows for setting them all in
1615 one convient area.
1617 Update is set to suspend all of the other test running while all the
1618 values are being set up. This allows for a performance increase, but
1619 it also means that the wrong type won't be detected with only some of
1620 the values set. After all the values are set everyone is told that
1621 there has been an update.
1622 */
1623 static void
1624 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1625 {
1626 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1627 sp_export_value_set_px (base, "x1", x1);
1628 sp_export_value_set_px (base, "y1", y1);
1629 sp_export_value_set_px (base, "x0", x0);
1630 sp_export_value_set_px (base, "y0", y0);
1631 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1633 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1634 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1636 return;
1637 }
1639 /**
1640 \brief Sets the value of an adjustment
1641 \param base The export dialog
1642 \param key Which adjustment to set
1643 \param val What value to set it to
1645 This function finds the adjustment using the data stored in the
1646 export dialog. After finding the adjustment it then sets
1647 the value of it.
1648 */
1649 static void
1650 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1651 {
1652 GtkAdjustment *adj;
1654 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1656 gtk_adjustment_set_value (adj, val);
1657 }
1659 /**
1660 \brief A function to set a value using the units points
1661 \param base The export dialog
1662 \param key Which value should be set
1663 \param val What the value should be in points
1665 This function first gets the adjustment for the key that is passed
1666 in. It then figures out what units are currently being used in the
1667 dialog. After doing all of that, it then converts the incoming
1668 value and sets the adjustment.
1669 */
1670 static void
1671 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1672 {
1673 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1675 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1677 return;
1678 }
1680 /**
1681 \brief Get the value of an adjustment in the export dialog
1682 \param base The export dialog
1683 \param key Which adjustment is being looked for
1684 \return The value in the specified adjustment
1686 This function gets the adjustment from the data field in the export
1687 dialog. It then grabs the value from the adjustment.
1688 */
1689 static float
1690 sp_export_value_get ( GtkObject *base, const gchar *key )
1691 {
1692 GtkAdjustment *adj;
1694 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1696 return adj->value;
1697 } // end of sp_export_value_get()
1699 /**
1700 \brief Grabs a value in the export dialog and converts the unit
1701 to points
1702 \param base The export dialog
1703 \param key Which value should be returned
1704 \return The value in the adjustment in points
1706 This function, at its most basic, is a call to \c sp_export_value_get
1707 to get the value of the adjustment. It then finds the units that
1708 are being used by looking at the "units" attribute of the export
1709 dialog. Using that it converts the returned value into points.
1710 */
1711 static float
1712 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1713 {
1714 float value = sp_export_value_get(base, key);
1715 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1717 return sp_units_get_pixels (value, *unit);
1718 } // end of sp_export_value_get_px()
1720 /**
1721 \brief This function is called when the filename is changed by
1722 anyone. It resets the virgin bit.
1723 \param object Text entry box
1724 \param data The export dialog
1725 \return None
1727 This function gets called when the text area is modified. It is
1728 looking for the case where the text area is modified from its
1729 original value. In that case it sets the "filename-modified" bit
1730 to TRUE. If the text dialog returns back to the original text, the
1731 bit gets reset. This should stop simple mistakes.
1732 */
1733 static void
1734 sp_export_filename_modified (GtkObject * object, gpointer data)
1735 {
1736 GtkWidget * text_entry = (GtkWidget *)object;
1737 GtkWidget * export_dialog = (GtkWidget *)data;
1739 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1740 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1741 // printf("Modified: FALSE\n");
1742 } else {
1743 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1744 // printf("Modified: TRUE\n");
1745 }
1747 return;
1748 } // end sp_export_filename_modified
1750 /*
1751 Local Variables:
1752 mode:c++
1753 c-file-style:"stroustrup"
1754 c-file-offsets:((innamespace . 0)(inline-open . 0))
1755 indent-tabs-mode:nil
1756 fill-column:99
1757 End:
1758 */
1759 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :