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 prefs_set_int_attribute (prefs_path, "x", x);
151 prefs_set_int_attribute (prefs_path, "y", y);
152 prefs_set_int_attribute (prefs_path, "w", w);
153 prefs_set_int_attribute (prefs_path, "h", h);
155 return FALSE; // which means, go ahead and destroy it
157 } // end of sp_export_dialog_delete()
159 /**
160 \brief Creates a new spin button for the export dialog
161 \param key The name of the spin button
162 \param val A default value for the spin button
163 \param min Minimum value for the spin button
164 \param max Maximum value for the spin button
165 \param step The step size for the spin button
166 \param page Size of the page increment
167 \param us Unit selector that effects this spin button
168 \param t Table to put the spin button in
169 \param x X location in the table \c t to start with
170 \param y Y location in the table \c t to start with
171 \param ll Text to put on the left side of the spin button (optional)
172 \param lr Text to put on the right side of the spin button (optional)
173 \param digits Number of digits to display after the decimal
174 \param sensitive Whether the spin button is sensitive or not
175 \param cb Callback for when this spin button is changed (optional)
176 \param dlg Export dialog the spin button is being placed in
178 */
179 static void
180 sp_export_spinbutton_new ( gchar *key, float val, float min, float max,
181 float step, float page, GtkWidget *us,
182 GtkWidget *t, int x, int y,
183 const gchar *ll, const gchar *lr,
184 int digits, unsigned int sensitive,
185 GCallback cb, GtkWidget *dlg )
186 {
187 GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page);
188 gtk_object_set_data (a, "key", key);
189 gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a);
191 if (us) {
192 sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us),
193 GTK_ADJUSTMENT (a) );
194 }
196 int pos = 0;
198 GtkWidget *l = NULL;
200 if (ll) {
202 l = gtk_label_new_with_mnemonic ((const gchar *)ll);
203 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
204 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
205 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
206 gtk_widget_set_sensitive (l, sensitive);
207 pos += 1;
209 }
211 GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits);
212 gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1,
213 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
214 gtk_widget_set_size_request (sb, 80, -1);
215 gtk_widget_set_sensitive (sb, sensitive);
216 pos += 1;
218 if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); }
220 if (lr) {
222 l = gtk_label_new_with_mnemonic ((const gchar *)lr);
223 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
224 gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1,
225 (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 );
226 gtk_widget_set_sensitive (l, sensitive);
227 pos += 1;
229 gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb);
230 }
232 if (cb)
233 gtk_signal_connect (a, "value_changed", cb, dlg);
235 return;
236 } // end of sp_export_spinbutton_new()
239 static GtkWidget *
240 sp_export_dialog_area_frame (GtkWidget * dlg)
241 {
242 GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox;
244 f = gtk_frame_new (_("Export area"));
245 vb = gtk_vbox_new (FALSE, 2);
246 gtk_container_add (GTK_CONTAINER (f), vb);
248 /* Units box */
249 unitbox = gtk_hbox_new (FALSE, 0);
250 gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4);
251 /* gets added to the vbox later, but the unit selector is needed
252 earlier than that */
254 us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
255 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
256 if (desktop)
257 sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units);
258 gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0);
259 l = gtk_label_new (_("Units:"));
260 gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3);
261 gtk_object_set_data (GTK_OBJECT (dlg), "units", us);
263 hb = gtk_hbox_new (TRUE, 0);
264 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
265 gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3);
267 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
268 b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i]));
269 gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i));
270 gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b);
271 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0);
272 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
273 GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg );
274 }
276 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
277 G_CALLBACK (sp_export_selection_changed), dlg );
278 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
279 G_CALLBACK (sp_export_selection_modified), dlg );
280 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
281 G_CALLBACK (sp_export_selection_changed), dlg );
283 t = gtk_table_new (2, 6, FALSE);
284 gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0);
285 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
286 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
287 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
289 sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
290 t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1,
291 G_CALLBACK ( sp_export_area_x_value_changed),
292 dlg );
294 sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
295 t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1,
296 G_CALLBACK (sp_export_area_x_value_changed),
297 dlg );
299 sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
300 us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1,
301 G_CALLBACK
302 (sp_export_area_width_value_changed),
303 dlg );
305 sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
306 t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1,
307 G_CALLBACK (sp_export_area_y_value_changed),
308 dlg );
310 sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us,
311 t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1,
312 G_CALLBACK (sp_export_area_y_value_changed),
313 dlg );
315 sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0,
316 us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1,
317 G_CALLBACK (sp_export_area_height_value_changed),
318 dlg );
320 /* Adding in the unit box */
321 gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0);
323 return f;
324 } // end of sp_export_dialog_area_frame
327 void
328 sp_export_dialog (void)
329 {
330 if (!dlg) {
331 GtkWidget *vb, *hb;
333 gchar title[500];
334 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title);
336 dlg = sp_window_new (title, TRUE);
338 if (x == -1000 || y == -1000) {
339 x = prefs_get_int_attribute (prefs_path, "x", 0);
340 y = prefs_get_int_attribute (prefs_path, "y", 0);
341 }
343 if (w ==0 || h == 0) {
344 w = prefs_get_int_attribute (prefs_path, "w", 0);
345 h = prefs_get_int_attribute (prefs_path, "h", 0);
346 }
348 if (x != 0 || y != 0) {
349 gtk_window_move ((GtkWindow *) dlg, x, y);
350 } else {
351 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
352 }
354 if (w && h)
355 gtk_window_resize ((GtkWindow *) dlg, w, h);
357 sp_transientize (dlg);
358 wd.win = dlg;
359 wd.stop = 0;
361 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
362 G_CALLBACK (sp_transientize_callback), &wd);
364 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
365 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
367 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
368 G_CALLBACK (sp_export_dialog_destroy), dlg);
370 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
371 G_CALLBACK (sp_export_dialog_delete), dlg);
373 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
374 G_CALLBACK (sp_export_dialog_delete), dlg);
376 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
377 G_CALLBACK (sp_dialog_hide), dlg);
379 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
380 G_CALLBACK (sp_dialog_unhide), dlg);
382 GtkTooltips *tt = gtk_tooltips_new();
384 vb = gtk_vbox_new (FALSE, 4);
385 gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
386 gtk_container_add (GTK_CONTAINER (dlg), vb);
388 /* Export area frame */
389 {
390 GtkWidget *f = sp_export_dialog_area_frame(dlg);
391 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
392 }
394 /* Bitmap size frame */
395 {
396 GtkWidget *f = gtk_frame_new (_("Bitmap size"));
397 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
398 GtkWidget *t = gtk_table_new (2, 5, FALSE);
399 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
400 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
401 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
402 gtk_container_add (GTK_CONTAINER (f), t);
404 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
405 NULL, t, 0, 0,
406 _("_Width:"), _("pixels at"), 0, 1,
407 G_CALLBACK
408 (sp_export_bitmap_width_value_changed),
409 dlg );
411 sp_export_spinbutton_new ( "xdpi",
412 prefs_get_double_attribute
413 ( "dialogs.export.defaultxdpi",
414 "value", DPI_BASE),
415 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
416 NULL, _("dp_i"), 2, 1,
417 G_CALLBACK (sp_export_xdpi_value_changed),
418 dlg );
420 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0,
421 NULL, t, 0, 1, _("Height:"), _("pixels at"),
422 0, 0, NULL, dlg );
424 /** \todo
425 * Needs fixing: there's no way to set ydpi currently, so we use
426 * the defaultxdpi value here, too...
427 */
428 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
429 ( "dialogs.export.defaultxdpi",
430 "value", DPI_BASE),
431 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
432 NULL, _("dpi"), 2, 0, NULL, dlg );
433 }
435 /* File entry */
436 {
437 GtkWidget *frame = gtk_frame_new ("");
438 GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
439 gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
440 gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
442 GtkWidget *fe = gtk_entry_new ();
444 /*
445 * set the default filename to be that of the current path + document
446 * with .png extension
447 *
448 * One thing to notice here is that this filename may get
449 * overwritten, but it won't happen here. The filename gets
450 * written into the text field, but then the button to select
451 * the area gets set. In that code the filename can be changed
452 * if there are some with presidence in the document. So, while
453 * this code sets the name first, it may not be the one users
454 * really see.
455 */
456 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
457 {
458 gchar *name;
459 SPDocument * doc = SP_ACTIVE_DOCUMENT;
460 const gchar *uri = SP_DOCUMENT_URI (doc);
461 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
462 const gchar * text_extension = repr->attribute("inkscape:output_extension");
463 Inkscape::Extension::Output * oextension = NULL;
465 if (text_extension != NULL) {
466 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
467 }
469 if (oextension != NULL) {
470 gchar * old_extension = oextension->get_extension();
471 if (g_str_has_suffix(uri, old_extension)) {
472 gchar * uri_copy;
473 gchar * extension_point;
474 gchar * final_name;
476 uri_copy = g_strdup(uri);
477 extension_point = g_strrstr(uri_copy, old_extension);
478 extension_point[0] = '\0';
480 final_name = g_strconcat(uri_copy, ".png", NULL);
481 gtk_entry_set_text (GTK_ENTRY (fe), final_name);
483 g_free(final_name);
484 g_free(uri_copy);
485 }
486 } else {
487 name = g_strconcat(uri, ".png", NULL);
488 gtk_entry_set_text (GTK_ENTRY (fe), name);
489 g_free(name);
490 }
492 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
493 }
494 g_signal_connect ( G_OBJECT (fe), "changed",
495 G_CALLBACK (sp_export_filename_modified), dlg);
497 hb = gtk_hbox_new (FALSE, 5);
498 gtk_container_add (GTK_CONTAINER (frame), hb);
499 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
501 {
502 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
503 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
504 g_signal_connect ( G_OBJECT (b), "clicked",
505 G_CALLBACK (sp_export_browse_clicked), NULL );
506 }
508 gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
509 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
510 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
511 original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
512 // pressing enter in the filename field is the same as clicking export:
513 g_signal_connect ( G_OBJECT (fe), "activate",
514 G_CALLBACK (sp_export_export_clicked), dlg );
515 // focus is in the filename initially:
516 gtk_widget_grab_focus (GTK_WIDGET (fe));
518 // mnemonic in frame label moves focus to filename:
519 gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
520 }
522 /* Buttons */
523 hb = gtk_hbox_new (FALSE, 0);
524 gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
526 {
527 GtkWidget *b = gtk_button_new ();
528 GtkWidget *l = gtk_label_new ("");
529 gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
530 gtk_container_add (GTK_CONTAINER(b), l);
531 gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
532 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
533 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
534 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
535 }
537 gtk_widget_show_all (vb);
539 } // end of if (!dlg)
541 sp_export_find_default_selection(dlg);
543 gtk_window_present ((GtkWindow *) dlg);
545 return;
546 } // end of sp_export_dialog()
548 static inline void
549 sp_export_find_default_selection(GtkWidget * dlg)
550 {
551 selection_type key = SELECTION_NUMBER_OF;
553 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
554 key = SELECTION_SELECTION;
555 }
557 /* Try using the preferences */
558 if (key == SELECTION_NUMBER_OF) {
559 const gchar *what = NULL;
560 int i = SELECTION_NUMBER_OF;
562 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
564 if (what != NULL) {
565 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
566 if (!strcmp (what, selection_names[i])) {
567 break;
568 }
569 }
570 }
572 key = (selection_type)i;
573 }
575 if (key == SELECTION_NUMBER_OF) {
576 key = SELECTION_SELECTION;
577 }
579 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
580 selection_names[key]);
581 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
583 return;
584 }
587 /**
588 * \brief If selection changed or a different document activated, we must
589 * recalculate any chosen areas
590 *
591 */
592 static void
593 sp_export_selection_changed ( Inkscape::Application *inkscape,
594 Inkscape::Selection *selection,
595 GtkObject *base )
596 {
597 selection_type current_key;
598 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
600 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
601 (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
602 was_empty) {
603 gtk_toggle_button_set_active
604 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
605 TRUE );
606 }
607 was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
609 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
611 if (inkscape &&
612 SP_IS_INKSCAPE (inkscape) &&
613 selection &&
614 SELECTION_CUSTOM != current_key) {
615 GtkToggleButton * button;
616 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
617 sp_export_area_toggled(button, base);
618 } // end of if()
620 return;
621 } // end of sp_export_selection_changed()
623 static void
624 sp_export_selection_modified ( Inkscape::Application *inkscape,
625 Inkscape::Selection *selection,
626 guint flags,
627 GtkObject *base )
628 {
629 selection_type current_key;
630 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
632 switch (current_key) {
633 case SELECTION_DRAWING:
634 if ( SP_ACTIVE_DESKTOP ) {
635 SPDocument *doc;
636 NRRect bbox;
637 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
638 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
640 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
641 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
642 }
643 }
644 break;
645 case SELECTION_SELECTION:
646 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
647 NRRect bbox;
648 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
649 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
650 }
651 break;
652 default:
653 /* Do nothing for page or for custom */
654 break;
655 }
657 return;
658 }
660 /// Called when one of the selection buttons was toggled.
661 static void
662 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
663 {
664 if (gtk_object_get_data (base, "update"))
665 return;
667 selection_type key, old_key;
668 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
669 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
671 /* Ignore all "turned off" events unless we're the only active button */
672 if (!gtk_toggle_button_get_active (tb) ) {
674 /* Don't let the current selection be deactived - but rerun the
675 activate to allow the user to renew the values */
676 if (key == old_key) {
677 gtk_toggle_button_set_active ( tb, TRUE );
678 }
680 return;
681 }
683 /* Turn off the currently active button unless it's us */
684 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
686 if (old_key != key) {
687 gtk_toggle_button_set_active
688 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
689 FALSE );
690 }
692 if ( SP_ACTIVE_DESKTOP )
693 {
694 SPDocument *doc;
695 NRRect bbox;
696 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
698 /* Notice how the switch is used to 'fall through' here to get
699 various backups. If you modify this without noticing you'll
700 probabaly screw something up. */
701 switch (key) {
702 case SELECTION_SELECTION:
703 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
704 {
705 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
706 /* Only if there is a selection that we can set
707 do we break, otherwise we fall through to the
708 drawing */
709 // std::cout << "Using selection: SELECTION" << std::endl;
710 key = SELECTION_SELECTION;
711 break;
712 }
713 case SELECTION_DRAWING:
714 /** \todo
715 * This returns wrong values if the document has a viewBox.
716 */
717 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
719 /* If the drawing is valid, then we'll use it and break
720 otherwise we drop through to the page settings */
721 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
722 // std::cout << "Using selection: DRAWING" << std::endl;
723 key = SELECTION_DRAWING;
724 break;
725 }
726 case SELECTION_PAGE:
727 bbox.x0 = 0.0;
728 bbox.y0 = 0.0;
729 bbox.x1 = sp_document_width (doc);
730 bbox.y1 = sp_document_height (doc);
731 // std::cout << "Using selection: PAGE" << std::endl;
732 key = SELECTION_PAGE;
733 break;
734 case SELECTION_CUSTOM:
735 default:
736 break;
737 } // switch
739 // remember area setting
740 prefs_set_string_attribute ( "dialogs.export.exportarea",
741 "value", selection_names[key]);
743 if (key != SELECTION_CUSTOM) {
744 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
745 }
747 } // end of if ( SP_ACTIVE_DESKTOP )
750 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
751 GtkWidget * file_entry;
752 const gchar * filename = NULL;
753 float xdpi = 0.0, ydpi = 0.0;
755 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
757 switch (key) {
758 case SELECTION_PAGE:
759 case SELECTION_DRAWING: {
760 SPDocument * doc = SP_ACTIVE_DOCUMENT;
761 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
762 const gchar * dpi_string;
764 filename = repr->attribute("inkscape:export-filename");
766 dpi_string = NULL;
767 dpi_string = repr->attribute("inkscape:export-xdpi");
768 if (dpi_string != NULL) {
769 xdpi = atof(dpi_string);
770 }
772 dpi_string = NULL;
773 dpi_string = repr->attribute("inkscape:export-ydpi");
774 if (dpi_string != NULL) {
775 ydpi = atof(dpi_string);
776 }
778 if (filename == NULL) {
779 if (doc_export_name != NULL) {
780 filename = g_strdup(doc_export_name);
781 } else {
782 filename = g_strdup("");
783 }
784 }
786 break;
787 }
788 case SELECTION_SELECTION:
789 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
790 const GSList * reprlst;
791 bool filename_search = TRUE;
792 bool xdpi_search = TRUE;
793 bool ydpi_search = TRUE;
795 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
796 for(; reprlst != NULL &&
797 filename_search &&
798 xdpi_search &&
799 ydpi_search;
800 reprlst = reprlst->next) {
801 const gchar * dpi_string;
802 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
804 if (filename_search) {
805 filename = repr->attribute("inkscape:export-filename");
806 if (filename != NULL)
807 filename_search = FALSE;
808 }
810 if (xdpi_search) {
811 dpi_string = NULL;
812 dpi_string = repr->attribute("inkscape:export-xdpi");
813 if (dpi_string != NULL) {
814 xdpi = atof(dpi_string);
815 xdpi_search = FALSE;
816 }
817 }
819 if (ydpi_search) {
820 dpi_string = NULL;
821 dpi_string = repr->attribute("inkscape:export-ydpi");
822 if (dpi_string != NULL) {
823 ydpi = atof(dpi_string);
824 ydpi_search = FALSE;
825 }
826 }
827 }
829 /* If we still don't have a filename -- let's build
830 one that's nice */
831 if (filename == NULL) {
832 const gchar * id = NULL;
833 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
834 for(; reprlst != NULL; reprlst = reprlst->next) {
835 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
836 if (repr->attribute("id")) {
837 id = repr->attribute("id");
838 break;
839 }
840 }
841 if (id == NULL) /* This should never happen */
842 id = "bitmap";
844 gchar * directory = NULL;
845 const gchar * file_entry_text;
847 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
848 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
849 // std::cout << "Directory from dialog" << std::endl;
850 directory = g_dirname(file_entry_text);
851 }
853 if (directory == NULL) {
854 /* Grab document directory */
855 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
856 // std::cout << "Directory from document" << std::endl;
857 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
858 }
859 }
861 if (directory == NULL) {
862 // std::cout << "Home Directory" << std::endl;
863 directory = homedir_path(NULL);
864 }
866 gchar * id_ext = g_strconcat(id, ".png", NULL);
867 filename = g_build_filename(directory, id_ext, NULL);
868 g_free(directory);
869 g_free(id_ext);
870 }
871 }
872 break;
873 case SELECTION_CUSTOM:
874 default:
875 break;
876 }
878 if (filename != NULL) {
879 g_free(original_name);
880 original_name = g_strdup(filename);
881 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
882 }
884 if (xdpi != 0.0) {
885 sp_export_value_set(base, "xdpi", xdpi);
886 }
888 /* These can't be seperate, and setting x sets y, so for
889 now setting this is disabled. Hopefully it won't be in
890 the future */
891 if (FALSE && ydpi != 0.0) {
892 sp_export_value_set(base, "ydpi", ydpi);
893 }
894 }
896 return;
897 } // end of sp_export_area_toggled()
899 /// Called when dialog is deleted
900 static gint
901 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
902 {
903 g_object_set_data (base, "cancel", (gpointer) 1);
904 return TRUE;
905 } // end of sp_export_progress_delete()
907 /// Called when progress is cancelled
908 static void
909 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
910 {
911 g_object_set_data (base, "cancel", (gpointer) 1);
912 } // end of sp_export_progress_cancel()
914 /// Called for every progress iteration
915 static unsigned int
916 sp_export_progress_callback (float value, void *data)
917 {
918 GtkWidget *prg;
919 int evtcount;
921 if (g_object_get_data ((GObject *) data, "cancel"))
922 return FALSE;
924 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
925 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
927 evtcount = 0;
928 while ((evtcount < 16) && gdk_events_pending ()) {
929 gtk_main_iteration_do (FALSE);
930 evtcount += 1;
931 }
933 gtk_main_iteration_do (FALSE);
935 return TRUE;
937 } // end of sp_export_progress_callback()
939 /// Called when export button is clicked
940 static void
941 sp_export_export_clicked (GtkButton *button, GtkObject *base)
942 {
943 if (!SP_ACTIVE_DESKTOP) return;
945 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
946 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
948 float const x0 = sp_export_value_get_px(base, "x0");
949 float const y0 = sp_export_value_get_px(base, "y0");
950 float const x1 = sp_export_value_get_px(base, "x1");
951 float const y1 = sp_export_value_get_px(base, "y1");
952 float const xdpi = sp_export_value_get(base, "xdpi");
953 float const ydpi = sp_export_value_get(base, "ydpi");
954 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
955 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
957 if (filename == NULL || *filename == '\0') {
958 sp_ui_error_dialog(_("You have to enter a filename"));
959 return;
960 }
962 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
963 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
964 return;
965 }
967 gchar *dirname = g_dirname(filename);
968 if ( dirname == NULL
969 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
970 {
971 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
972 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
973 safeDir);
974 sp_ui_error_dialog(error);
975 g_free(safeDir);
976 g_free(error);
977 g_free(dirname);
978 return;
979 }
980 g_free(dirname);
982 SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
983 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
984 char *fn;
985 gchar *text;
987 dlg = gtk_dialog_new ();
988 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
989 prg = gtk_progress_bar_new ();
990 sp_transientize (dlg);
991 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
992 g_object_set_data ((GObject *) base, "progress", prg);
993 fn = g_path_get_basename (filename);
994 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
995 fn, width, height);
996 g_free (fn);
997 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
998 g_free (text);
999 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1000 GTK_PROGRESS_LEFT_TO_RIGHT);
1001 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1002 prg, FALSE, FALSE, 4 );
1003 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1004 GTK_STOCK_CANCEL,
1005 GTK_RESPONSE_CANCEL );
1007 g_signal_connect ( (GObject *) dlg, "delete_event",
1008 (GCallback) sp_export_progress_delete, base);
1009 g_signal_connect ( (GObject *) btn, "clicked",
1010 (GCallback) sp_export_progress_cancel, base);
1011 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1012 gtk_widget_show_all (dlg);
1014 /* Do export */
1015 if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename,
1016 x0, y0, x1, y1, width, height,
1017 nv->pagecolor,
1018 sp_export_progress_callback, base)) {
1019 gchar * error;
1020 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1021 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1022 sp_ui_error_dialog(error);
1023 g_free(safeFile);
1024 g_free(error);
1025 }
1027 /* Reset the filename so that it can be changed again by changing
1028 selections and all that */
1029 g_free(original_name);
1030 original_name = g_strdup(filename);
1031 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1033 gtk_widget_destroy (dlg);
1034 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1036 /* Setup the values in the document */
1037 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1038 case SELECTION_PAGE:
1039 case SELECTION_DRAWING: {
1040 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1041 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1042 bool modified = FALSE;
1043 const gchar * temp_string;
1045 bool saved = sp_document_get_undo_sensitive(doc);
1046 sp_document_set_undo_sensitive(doc, FALSE);
1048 temp_string = repr->attribute("inkscape:export-filename");
1049 if (temp_string == NULL || strcmp(temp_string, filename)) {
1050 repr->setAttribute("inkscape:export-filename", filename);
1051 modified = TRUE;
1052 }
1053 temp_string = repr->attribute("inkscape:export-xdpi");
1054 if (temp_string == NULL || xdpi != atof(temp_string)) {
1055 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1056 modified = TRUE;
1057 }
1058 temp_string = repr->attribute("inkscape:export-ydpi");
1059 if (temp_string == NULL || xdpi != atof(temp_string)) {
1060 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1061 modified = TRUE;
1062 }
1064 if (modified)
1065 repr->setAttribute("sodipodi:modified", "TRUE");
1066 sp_document_set_undo_sensitive(doc, saved);
1067 break;
1068 }
1069 case SELECTION_SELECTION: {
1070 const GSList * reprlst;
1071 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1072 bool modified = FALSE;
1074 bool saved = sp_document_get_undo_sensitive(doc);
1075 sp_document_set_undo_sensitive(doc, FALSE);
1076 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1078 for(; reprlst != NULL; reprlst = reprlst->next) {
1079 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1080 const gchar * temp_string;
1082 if (repr->attribute("id") == NULL ||
1083 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1084 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1085 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1086 temp_string = repr->attribute("inkscape:export-filename");
1087 if (temp_string == NULL || strcmp(temp_string, filename)) {
1088 repr->setAttribute("inkscape:export-filename", filename);
1089 modified = TRUE;
1090 }
1091 }
1092 temp_string = repr->attribute("inkscape:export-xdpi");
1093 if (temp_string == NULL || xdpi != atof(temp_string)) {
1094 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1095 modified = TRUE;
1096 }
1097 temp_string = repr->attribute("inkscape:export-ydpi");
1098 if (temp_string == NULL || xdpi != atof(temp_string)) {
1099 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1100 modified = TRUE;
1101 }
1102 }
1104 if (modified) {
1105 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1106 repr->setAttribute("sodipodi:modified", "TRUE");
1107 }
1109 sp_document_set_undo_sensitive(doc, saved);
1110 break;
1111 }
1112 default:
1113 break;
1114 }
1117 return;
1118 } // end of sp_export_export_clicked()
1120 /// Called when Browse button is clicked
1121 static void
1122 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1123 {
1124 GtkWidget *fs, *fe;
1125 const gchar *filename;
1127 fs = gtk_file_selection_new (_("Select a filename for exporting"));
1128 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1130 sp_transientize (fs);
1132 gtk_window_set_modal(GTK_WINDOW (fs), true);
1134 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1136 if (*filename == '\0') {
1137 filename = homedir_path(NULL);
1138 }
1140 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1142 g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1143 "clicked",
1144 G_CALLBACK (sp_export_browse_store),
1145 (gpointer) fs );
1147 g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1148 "clicked",
1149 G_CALLBACK (gtk_widget_destroy),
1150 (gpointer) fs );
1152 g_signal_connect_swapped ( GTK_OBJECT
1153 (GTK_FILE_SELECTION (fs)->cancel_button),
1154 "clicked",
1155 G_CALLBACK (gtk_widget_destroy),
1156 (gpointer) fs );
1158 gtk_widget_show (fs);
1160 return;
1161 } // end of sp_export_browse_clicked()
1163 /// Called when OK clicked in file dialog
1164 static void
1165 sp_export_browse_store (GtkButton *button, gpointer userdata)
1166 {
1167 GtkWidget *fs = (GtkWidget *)userdata, *fe;
1168 const gchar *file;
1170 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1172 file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1173 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1174 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1175 g_free(utf8file);
1177 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1179 return;
1180 } // end of sp_export_browse_store()
1182 // TODO: Move this to nr-rect-fns.h.
1183 static bool
1184 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1185 {
1186 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1187 return (
1188 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1189 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1190 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1191 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1192 );
1193 }
1195 /**
1196 \brief This function is used to detect the current selection setting
1197 based on the values in the x0, y0, x1 and y0 fields.
1198 \param base The export dialog itself
1200 One of the most confusing parts of this function is why the array
1201 is built at the beginning. What needs to happen here is that we
1202 should always check the current selection to see if it is the valid
1203 one. While this is a performance improvement it is also a usability
1204 one during the cases where things like selections and drawings match
1205 size. This way buttons change less 'randomly' (atleast in the eyes
1206 of the user). To do this an array is built where the current selection
1207 type is placed first, and then the others in an order from smallest
1208 to largest (this can be configured by reshuffling \c test_order).
1210 All of the values in this function are rounded to two decimal places
1211 because that is what is shown to the user. While everything is kept
1212 more accurate than that, the user can't control more acurrate than
1213 that, so for this to work for them - it needs to check on that level
1214 of accuracy.
1216 \todo finish writing this up
1217 */
1218 static void
1219 sp_export_detect_size(GtkObject * base) {
1220 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1221 selection_type this_test[SELECTION_NUMBER_OF + 1];
1222 selection_type key = SELECTION_NUMBER_OF;
1224 NR::Point x(sp_export_value_get_px (base, "x0"),
1225 sp_export_value_get_px (base, "y0"));
1226 NR::Point y(sp_export_value_get_px (base, "x1"),
1227 sp_export_value_get_px (base, "y1"));
1228 NR::Rect current_bbox(x, y);
1229 //std::cout << "Current " << current_bbox;
1231 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1232 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1233 this_test[i + 1] = test_order[i];
1234 }
1236 for (int i = 0;
1237 i < SELECTION_NUMBER_OF + 1 &&
1238 key == SELECTION_NUMBER_OF &&
1239 SP_ACTIVE_DESKTOP != NULL;
1240 i++) {
1241 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1242 switch (this_test[i]) {
1243 case SELECTION_SELECTION:
1244 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1245 NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1247 //std::cout << "Selection " << bbox;
1248 if (sp_export_bbox_equal(bbox,current_bbox)) {
1249 key = SELECTION_SELECTION;
1250 }
1251 }
1252 break;
1253 case SELECTION_DRAWING: {
1254 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1256 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1258 // std::cout << "Drawing " << bbox2;
1259 if (sp_export_bbox_equal(bbox,current_bbox)) {
1260 key = SELECTION_DRAWING;
1261 }
1262 break;
1263 }
1265 case SELECTION_PAGE: {
1266 SPDocument *doc;
1268 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1270 NR::Point x(0.0, 0.0);
1271 NR::Point y(sp_document_width(doc),
1272 sp_document_height(doc));
1273 NR::Rect bbox(x, y);
1275 // std::cout << "Page " << bbox;
1276 if (sp_export_bbox_equal(bbox,current_bbox)) {
1277 key = SELECTION_PAGE;
1278 }
1280 break;
1281 }
1282 default:
1283 break;
1284 }
1285 }
1286 // std::cout << std::endl;
1288 if (key == SELECTION_NUMBER_OF) {
1289 key = SELECTION_CUSTOM;
1290 }
1292 /* We're now using a custom size, not a fixed one */
1293 /* printf("Detecting state: %s\n", selection_names[key]); */
1294 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1295 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1296 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1297 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1299 return;
1300 } /* sp_export_detect_size */
1302 /// Called when area x0 value is changed
1303 static void
1304 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1305 {
1306 float x0, x1, xdpi, width;
1308 if (gtk_object_get_data (base, "update"))
1309 return;
1311 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1312 (base, "units")))
1313 {
1314 return;
1315 }
1317 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1319 x0 = sp_export_value_get_px (base, "x0");
1320 x1 = sp_export_value_get_px (base, "x1");
1321 xdpi = sp_export_value_get (base, "xdpi");
1323 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1325 if (width < SP_EXPORT_MIN_SIZE) {
1326 const gchar *key;
1327 width = SP_EXPORT_MIN_SIZE;
1328 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1330 if (!strcmp (key, "x0")) {
1331 x1 = x0 + width * DPI_BASE / xdpi;
1332 sp_export_value_set_px (base, "x1", x1);
1333 } else {
1334 x0 = x1 - width * DPI_BASE / xdpi;
1335 sp_export_value_set_px (base, "x0", x0);
1336 }
1337 }
1339 sp_export_value_set_px (base, "width", x1 - x0);
1340 sp_export_value_set (base, "bmwidth", width);
1342 sp_export_detect_size(base);
1344 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1346 return;
1347 } // end of sp_export_area_x_value_changed()
1349 /// Called when area y0 value is changed.
1350 static void
1351 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1352 {
1353 float y0, y1, ydpi, height;
1355 if (gtk_object_get_data (base, "update"))
1356 return;
1358 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1359 (base, "units")))
1360 {
1361 return;
1362 }
1364 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1366 y0 = sp_export_value_get_px (base, "y0");
1367 y1 = sp_export_value_get_px (base, "y1");
1368 ydpi = sp_export_value_get (base, "ydpi");
1370 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1372 if (height < SP_EXPORT_MIN_SIZE) {
1373 const gchar *key;
1374 height = SP_EXPORT_MIN_SIZE;
1375 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1376 if (!strcmp (key, "y0")) {
1377 y1 = y0 + height * DPI_BASE / ydpi;
1378 sp_export_value_set_px (base, "y1", y1);
1379 } else {
1380 y0 = y1 - height * DPI_BASE / ydpi;
1381 sp_export_value_set_px (base, "y0", y0);
1382 }
1383 }
1385 sp_export_value_set_px (base, "height", y1 - y0);
1386 sp_export_value_set (base, "bmheight", height);
1388 sp_export_detect_size(base);
1390 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1392 return;
1393 } // end of sp_export_area_y_value_changed()
1395 /// Called when x1-x0 or area width is changed
1396 static void
1397 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1398 {
1399 float x0, x1, xdpi, width, bmwidth;
1401 if (gtk_object_get_data (base, "update"))
1402 return;
1404 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1405 (base, "units"))) {
1406 return;
1407 }
1409 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1411 x0 = sp_export_value_get_px (base, "x0");
1412 x1 = sp_export_value_get_px (base, "x1");
1413 xdpi = sp_export_value_get (base, "xdpi");
1414 width = sp_export_value_get_px (base, "width");
1415 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1417 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1419 bmwidth = SP_EXPORT_MIN_SIZE;
1420 width = bmwidth * DPI_BASE / xdpi;
1421 sp_export_value_set_px (base, "width", width);
1422 }
1424 sp_export_value_set_px (base, "x1", x0 + width);
1425 sp_export_value_set (base, "bmwidth", bmwidth);
1427 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1429 return;
1430 } // end of sp_export_area_width_value_changed()
1432 /// Called when y1-y0 or area height is changed.
1433 static void
1434 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1435 {
1437 float y0, y1, ydpi, height, bmheight;
1439 if (gtk_object_get_data (base, "update"))
1440 return;
1442 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1443 (base, "units"))) {
1444 return;
1445 }
1447 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1449 y0 = sp_export_value_get_px (base, "y0");
1450 y1 = sp_export_value_get_px (base, "y1");
1451 ydpi = sp_export_value_get (base, "ydpi");
1452 height = sp_export_value_get_px (base, "height");
1453 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1455 if (bmheight < SP_EXPORT_MIN_SIZE) {
1456 bmheight = SP_EXPORT_MIN_SIZE;
1457 height = bmheight * DPI_BASE / ydpi;
1458 sp_export_value_set_px (base, "height", height);
1459 }
1461 sp_export_value_set_px (base, "y1", y0 + height);
1462 sp_export_value_set (base, "bmheight", bmheight);
1464 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1466 return;
1467 } // end of sp_export_area_height_value_changed()
1469 /**
1470 \brief A function to set the ydpi
1471 \param base The export dialog
1473 This function grabs all of the y values and then figures out the
1474 new bitmap size based on the changing dpi value. The dpi value is
1475 gotten from the xdpi setting as these can not currently be independent.
1476 */
1477 static void
1478 sp_export_set_image_y (GtkObject *base)
1479 {
1480 float y0, y1, xdpi;
1482 y0 = sp_export_value_get_px (base, "y0");
1483 y1 = sp_export_value_get_px (base, "y1");
1484 xdpi = sp_export_value_get (base, "xdpi");
1486 sp_export_value_set (base, "ydpi", xdpi);
1487 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1489 return;
1490 } // end of sp_export_set_image_y()
1492 /// Called when pixel width is changed
1493 static void
1494 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1495 {
1496 float x0, x1, bmwidth, xdpi;
1498 if (gtk_object_get_data (base, "update"))
1499 return;
1501 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1502 (base, "units"))) {
1503 return;
1504 }
1506 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1508 x0 = sp_export_value_get_px (base, "x0");
1509 x1 = sp_export_value_get_px (base, "x1");
1510 bmwidth = sp_export_value_get (base, "bmwidth");
1512 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1513 bmwidth = SP_EXPORT_MIN_SIZE;
1514 sp_export_value_set (base, "bmwidth", bmwidth);
1515 }
1517 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1518 sp_export_value_set (base, "xdpi", xdpi);
1520 sp_export_set_image_y (base);
1522 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1524 return;
1525 } // end of sp_export_bitmap_width_value_changed()
1527 /**
1528 \brief A function to adjust the bitmap width when the xdpi value changes
1529 \param adj The adjustment that was changed
1530 \param base The export dialog itself
1532 The first thing this function checks is to see if we are doing an
1533 update. If we are, this function just returns because there is another
1534 instance of it that will handle everything for us. If there is a
1535 units change, we also assume that everyone is being updated appropriately
1536 and there is nothing for us to do.
1538 If we're the highest level function, we set the update flag, and
1539 continue on our way.
1541 All of the values are grabbed using the \c sp_export_value_get functions
1542 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1543 xdpi value is saved in the preferences for the next time the dialog
1544 is opened. (does the selection dpi need to be set here?)
1546 A check is done to to ensure that we aren't outputing an invalid width,
1547 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1548 changed to make it valid.
1550 After all of this the bitmap width is changed.
1552 We also change the ydpi. This is a temporary hack as these can not
1553 currently be independent. This is likely to change in the future.
1554 */
1555 void
1556 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1557 {
1558 float x0, x1, xdpi, bmwidth;
1560 if (gtk_object_get_data (base, "update"))
1561 return;
1563 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1564 (base, "units"))) {
1565 return;
1566 }
1568 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1570 x0 = sp_export_value_get_px (base, "x0");
1571 x1 = sp_export_value_get_px (base, "x1");
1572 xdpi = sp_export_value_get (base, "xdpi");
1574 // remember xdpi setting
1575 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1577 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1579 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1580 bmwidth = SP_EXPORT_MIN_SIZE;
1581 if (x1 != x0)
1582 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1583 else
1584 xdpi = DPI_BASE;
1585 sp_export_value_set (base, "xdpi", xdpi);
1586 }
1588 sp_export_value_set (base, "bmwidth", bmwidth);
1590 sp_export_set_image_y (base);
1592 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1594 return;
1595 } // end of sp_export_xdpi_value_changed()
1598 /**
1599 \brief A function to change the area that is used for the exported
1600 bitmap.
1601 \param base This is the export dialog
1602 \param x0 Horizontal upper left hand corner of the picture in points
1603 \param y0 Vertical upper left hand corner of the picture in points
1604 \param x1 Horizontal lower right hand corner of the picture in points
1605 \param y1 Vertical lower right hand corner of the picture in points
1607 This function just calls \c sp_export_value_set_px for each of the
1608 parameters that is passed in. This allows for setting them all in
1609 one convient area.
1611 Update is set to suspend all of the other test running while all the
1612 values are being set up. This allows for a performance increase, but
1613 it also means that the wrong type won't be detected with only some of
1614 the values set. After all the values are set everyone is told that
1615 there has been an update.
1616 */
1617 static void
1618 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1619 {
1620 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1621 sp_export_value_set_px (base, "x1", x1);
1622 sp_export_value_set_px (base, "y1", y1);
1623 sp_export_value_set_px (base, "x0", x0);
1624 sp_export_value_set_px (base, "y0", y0);
1625 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1627 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1628 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1630 return;
1631 }
1633 /**
1634 \brief Sets the value of an adjustment
1635 \param base The export dialog
1636 \param key Which adjustment to set
1637 \param val What value to set it to
1639 This function finds the adjustment using the data stored in the
1640 export dialog. After finding the adjustment it then sets
1641 the value of it.
1642 */
1643 static void
1644 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1645 {
1646 GtkAdjustment *adj;
1648 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1650 gtk_adjustment_set_value (adj, val);
1651 }
1653 /**
1654 \brief A function to set a value using the units points
1655 \param base The export dialog
1656 \param key Which value should be set
1657 \param val What the value should be in points
1659 This function first gets the adjustment for the key that is passed
1660 in. It then figures out what units are currently being used in the
1661 dialog. After doing all of that, it then converts the incoming
1662 value and sets the adjustment.
1663 */
1664 static void
1665 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1666 {
1667 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1669 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1671 return;
1672 }
1674 /**
1675 \brief Get the value of an adjustment in the export dialog
1676 \param base The export dialog
1677 \param key Which adjustment is being looked for
1678 \return The value in the specified adjustment
1680 This function gets the adjustment from the data field in the export
1681 dialog. It then grabs the value from the adjustment.
1682 */
1683 static float
1684 sp_export_value_get ( GtkObject *base, const gchar *key )
1685 {
1686 GtkAdjustment *adj;
1688 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1690 return adj->value;
1691 } // end of sp_export_value_get()
1693 /**
1694 \brief Grabs a value in the export dialog and converts the unit
1695 to points
1696 \param base The export dialog
1697 \param key Which value should be returned
1698 \return The value in the adjustment in points
1700 This function, at its most basic, is a call to \c sp_export_value_get
1701 to get the value of the adjustment. It then finds the units that
1702 are being used by looking at the "units" attribute of the export
1703 dialog. Using that it converts the returned value into points.
1704 */
1705 static float
1706 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1707 {
1708 float value = sp_export_value_get(base, key);
1709 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1711 return sp_units_get_pixels (value, *unit);
1712 } // end of sp_export_value_get_px()
1714 /**
1715 \brief This function is called when the filename is changed by
1716 anyone. It resets the virgin bit.
1717 \param object Text entry box
1718 \param data The export dialog
1719 \return None
1721 This function gets called when the text area is modified. It is
1722 looking for the case where the text area is modified from its
1723 original value. In that case it sets the "filename-modified" bit
1724 to TRUE. If the text dialog returns back to the original text, the
1725 bit gets reset. This should stop simple mistakes.
1726 */
1727 static void
1728 sp_export_filename_modified (GtkObject * object, gpointer data)
1729 {
1730 GtkWidget * text_entry = (GtkWidget *)object;
1731 GtkWidget * export_dialog = (GtkWidget *)data;
1733 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1734 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1735 // printf("Modified: FALSE\n");
1736 } else {
1737 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1738 // printf("Modified: TRUE\n");
1739 }
1741 return;
1742 } // end sp_export_filename_modified
1744 /*
1745 Local Variables:
1746 mode:c++
1747 c-file-style:"stroustrup"
1748 c-file-offsets:((innamespace . 0)(inline-open . 0))
1749 indent-tabs-mode:nil
1750 fill-column:99
1751 End:
1752 */
1753 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :