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 || y != 0) {
352 gtk_window_move ((GtkWindow *) dlg, x, y);
353 } else {
354 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
355 }
357 if (w && h)
358 gtk_window_resize ((GtkWindow *) dlg, w, h);
360 sp_transientize (dlg);
361 wd.win = dlg;
362 wd.stop = 0;
364 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop",
365 G_CALLBACK (sp_transientize_callback), &wd);
367 gtk_signal_connect ( GTK_OBJECT (dlg), "event",
368 GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
370 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy",
371 G_CALLBACK (sp_export_dialog_destroy), dlg);
373 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event",
374 G_CALLBACK (sp_export_dialog_delete), dlg);
376 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down",
377 G_CALLBACK (sp_export_dialog_delete), dlg);
379 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide",
380 G_CALLBACK (sp_dialog_hide), dlg);
382 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide",
383 G_CALLBACK (sp_dialog_unhide), dlg);
385 GtkTooltips *tt = gtk_tooltips_new();
387 vb = gtk_vbox_new (FALSE, 4);
388 gtk_container_set_border_width (GTK_CONTAINER (vb), 0);
389 gtk_container_add (GTK_CONTAINER (dlg), vb);
391 /* Export area frame */
392 {
393 GtkWidget *f = sp_export_dialog_area_frame(dlg);
394 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
395 }
397 /* Bitmap size frame */
398 {
399 GtkWidget *f = gtk_frame_new (_("Bitmap size"));
400 gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0);
401 GtkWidget *t = gtk_table_new (2, 5, FALSE);
402 gtk_table_set_row_spacings (GTK_TABLE (t), 4);
403 gtk_table_set_col_spacings (GTK_TABLE (t), 4);
404 gtk_container_set_border_width (GTK_CONTAINER (t), 4);
405 gtk_container_add (GTK_CONTAINER (f), t);
407 sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0,
408 NULL, t, 0, 0,
409 _("_Width:"), _("pixels at"), 0, 1,
410 G_CALLBACK
411 (sp_export_bitmap_width_value_changed),
412 dlg );
414 sp_export_spinbutton_new ( "xdpi",
415 prefs_get_double_attribute
416 ( "dialogs.export.defaultxdpi",
417 "value", DPI_BASE),
418 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0,
419 NULL, _("dp_i"), 2, 1,
420 G_CALLBACK (sp_export_xdpi_value_changed),
421 dlg );
423 sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0,
424 NULL, t, 0, 1, _("Height:"), _("pixels at"),
425 0, 0, NULL, dlg );
427 /** \todo
428 * Needs fixing: there's no way to set ydpi currently, so we use
429 * the defaultxdpi value here, too...
430 */
431 sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute
432 ( "dialogs.export.defaultxdpi",
433 "value", DPI_BASE),
434 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1,
435 NULL, _("dpi"), 2, 0, NULL, dlg );
436 }
438 /* File entry */
439 {
440 GtkWidget *frame = gtk_frame_new ("");
441 GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename"));
442 gtk_frame_set_label_widget (GTK_FRAME(frame), flabel);
443 gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0);
445 GtkWidget *fe = gtk_entry_new ();
447 /*
448 * set the default filename to be that of the current path + document
449 * with .png extension
450 *
451 * One thing to notice here is that this filename may get
452 * overwritten, but it won't happen here. The filename gets
453 * written into the text field, but then the button to select
454 * the area gets set. In that code the filename can be changed
455 * if there are some with presidence in the document. So, while
456 * this code sets the name first, it may not be the one users
457 * really see.
458 */
459 if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT))
460 {
461 gchar *name;
462 SPDocument * doc = SP_ACTIVE_DOCUMENT;
463 const gchar *uri = SP_DOCUMENT_URI (doc);
464 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
465 const gchar * text_extension = repr->attribute("inkscape:output_extension");
466 Inkscape::Extension::Output * oextension = NULL;
468 if (text_extension != NULL) {
469 oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
470 }
472 if (oextension != NULL) {
473 gchar * old_extension = oextension->get_extension();
474 if (g_str_has_suffix(uri, old_extension)) {
475 gchar * uri_copy;
476 gchar * extension_point;
477 gchar * final_name;
479 uri_copy = g_strdup(uri);
480 extension_point = g_strrstr(uri_copy, old_extension);
481 extension_point[0] = '\0';
483 final_name = g_strconcat(uri_copy, ".png", NULL);
484 gtk_entry_set_text (GTK_ENTRY (fe), final_name);
486 g_free(final_name);
487 g_free(uri_copy);
488 }
489 } else {
490 name = g_strconcat(uri, ".png", NULL);
491 gtk_entry_set_text (GTK_ENTRY (fe), name);
492 g_free(name);
493 }
495 doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe)));
496 }
497 g_signal_connect ( G_OBJECT (fe), "changed",
498 G_CALLBACK (sp_export_filename_modified), dlg);
500 hb = gtk_hbox_new (FALSE, 5);
501 gtk_container_add (GTK_CONTAINER (frame), hb);
502 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
504 {
505 GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse..."));
506 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4);
507 g_signal_connect ( G_OBJECT (b), "clicked",
508 G_CALLBACK (sp_export_browse_clicked), NULL );
509 }
511 gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0);
512 gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe);
513 gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE);
514 original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe)));
515 // pressing enter in the filename field is the same as clicking export:
516 g_signal_connect ( G_OBJECT (fe), "activate",
517 G_CALLBACK (sp_export_export_clicked), dlg );
518 // focus is in the filename initially:
519 gtk_widget_grab_focus (GTK_WIDGET (fe));
521 // mnemonic in frame label moves focus to filename:
522 gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe);
523 }
525 /* Buttons */
526 hb = gtk_hbox_new (FALSE, 0);
527 gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0);
529 {
530 GtkWidget *b = gtk_button_new ();
531 GtkWidget *l = gtk_label_new ("");
532 gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Export</b> "));
533 gtk_container_add (GTK_CONTAINER(b), l);
534 gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL);
535 gtk_signal_connect ( GTK_OBJECT (b), "clicked",
536 GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg );
537 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
538 }
540 gtk_widget_show_all (vb);
542 } // end of if (!dlg)
544 sp_export_find_default_selection(dlg);
546 gtk_window_present ((GtkWindow *) dlg);
548 return;
549 } // end of sp_export_dialog()
551 static inline void
552 sp_export_find_default_selection(GtkWidget * dlg)
553 {
554 selection_type key = SELECTION_NUMBER_OF;
556 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
557 key = SELECTION_SELECTION;
558 }
560 /* Try using the preferences */
561 if (key == SELECTION_NUMBER_OF) {
562 const gchar *what = NULL;
563 int i = SELECTION_NUMBER_OF;
565 what = prefs_get_string_attribute ("dialogs.export.exportarea", "value");
567 if (what != NULL) {
568 for (i = 0; i < SELECTION_NUMBER_OF; i++) {
569 if (!strcmp (what, selection_names[i])) {
570 break;
571 }
572 }
573 }
575 key = (selection_type)i;
576 }
578 if (key == SELECTION_NUMBER_OF) {
579 key = SELECTION_SELECTION;
580 }
582 GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg),
583 selection_names[key]);
584 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
586 return;
587 }
590 /**
591 * \brief If selection changed or a different document activated, we must
592 * recalculate any chosen areas
593 *
594 */
595 static void
596 sp_export_selection_changed ( Inkscape::Application *inkscape,
597 Inkscape::Selection *selection,
598 GtkObject *base )
599 {
600 selection_type current_key;
601 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
603 if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) &&
604 (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false &&
605 was_empty) {
606 gtk_toggle_button_set_active
607 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])),
608 TRUE );
609 }
610 was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty();
612 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
614 if (inkscape &&
615 SP_IS_INKSCAPE (inkscape) &&
616 selection &&
617 SELECTION_CUSTOM != current_key) {
618 GtkToggleButton * button;
619 button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]);
620 sp_export_area_toggled(button, base);
621 } // end of if()
623 return;
624 } // end of sp_export_selection_changed()
626 static void
627 sp_export_selection_modified ( Inkscape::Application *inkscape,
628 Inkscape::Selection *selection,
629 guint flags,
630 GtkObject *base )
631 {
632 selection_type current_key;
633 current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
635 switch (current_key) {
636 case SELECTION_DRAWING:
637 if ( SP_ACTIVE_DESKTOP ) {
638 SPDocument *doc;
639 NRRect bbox;
640 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
641 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
643 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
644 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
645 }
646 }
647 break;
648 case SELECTION_SELECTION:
649 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
650 NRRect bbox;
651 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
652 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
653 }
654 break;
655 default:
656 /* Do nothing for page or for custom */
657 break;
658 }
660 return;
661 }
663 /// Called when one of the selection buttons was toggled.
664 static void
665 sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base)
666 {
667 if (gtk_object_get_data (base, "update"))
668 return;
670 selection_type key, old_key;
671 key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key")));
672 old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
674 /* Ignore all "turned off" events unless we're the only active button */
675 if (!gtk_toggle_button_get_active (tb) ) {
677 /* Don't let the current selection be deactived - but rerun the
678 activate to allow the user to renew the values */
679 if (key == old_key) {
680 gtk_toggle_button_set_active ( tb, TRUE );
681 }
683 return;
684 }
686 /* Turn off the currently active button unless it's us */
687 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
689 if (old_key != key) {
690 gtk_toggle_button_set_active
691 ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])),
692 FALSE );
693 }
695 if ( SP_ACTIVE_DESKTOP )
696 {
697 SPDocument *doc;
698 NRRect bbox;
699 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
701 /* Notice how the switch is used to 'fall through' here to get
702 various backups. If you modify this without noticing you'll
703 probabaly screw something up. */
704 switch (key) {
705 case SELECTION_SELECTION:
706 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false)
707 {
708 (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox);
709 /* Only if there is a selection that we can set
710 do we break, otherwise we fall through to the
711 drawing */
712 // std::cout << "Using selection: SELECTION" << std::endl;
713 key = SELECTION_SELECTION;
714 break;
715 }
716 case SELECTION_DRAWING:
717 /** \todo
718 * This returns wrong values if the document has a viewBox.
719 */
720 sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox);
722 /* If the drawing is valid, then we'll use it and break
723 otherwise we drop through to the page settings */
724 if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) {
725 // std::cout << "Using selection: DRAWING" << std::endl;
726 key = SELECTION_DRAWING;
727 break;
728 }
729 case SELECTION_PAGE:
730 bbox.x0 = 0.0;
731 bbox.y0 = 0.0;
732 bbox.x1 = sp_document_width (doc);
733 bbox.y1 = sp_document_height (doc);
734 // std::cout << "Using selection: PAGE" << std::endl;
735 key = SELECTION_PAGE;
736 break;
737 case SELECTION_CUSTOM:
738 default:
739 break;
740 } // switch
742 // remember area setting
743 prefs_set_string_attribute ( "dialogs.export.exportarea",
744 "value", selection_names[key]);
746 if (key != SELECTION_CUSTOM) {
747 sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
748 }
750 } // end of if ( SP_ACTIVE_DESKTOP )
753 if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) {
754 GtkWidget * file_entry;
755 const gchar * filename = NULL;
756 float xdpi = 0.0, ydpi = 0.0;
758 file_entry = (GtkWidget *)gtk_object_get_data (base, "filename");
760 switch (key) {
761 case SELECTION_PAGE:
762 case SELECTION_DRAWING: {
763 SPDocument * doc = SP_ACTIVE_DOCUMENT;
764 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
765 const gchar * dpi_string;
767 filename = repr->attribute("inkscape:export-filename");
769 dpi_string = NULL;
770 dpi_string = repr->attribute("inkscape:export-xdpi");
771 if (dpi_string != NULL) {
772 xdpi = atof(dpi_string);
773 }
775 dpi_string = NULL;
776 dpi_string = repr->attribute("inkscape:export-ydpi");
777 if (dpi_string != NULL) {
778 ydpi = atof(dpi_string);
779 }
781 if (filename == NULL) {
782 if (doc_export_name != NULL) {
783 filename = g_strdup(doc_export_name);
784 } else {
785 filename = g_strdup("");
786 }
787 }
789 break;
790 }
791 case SELECTION_SELECTION:
792 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
793 const GSList * reprlst;
794 bool filename_search = TRUE;
795 bool xdpi_search = TRUE;
796 bool ydpi_search = TRUE;
798 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
799 for(; reprlst != NULL &&
800 filename_search &&
801 xdpi_search &&
802 ydpi_search;
803 reprlst = reprlst->next) {
804 const gchar * dpi_string;
805 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
807 if (filename_search) {
808 filename = repr->attribute("inkscape:export-filename");
809 if (filename != NULL)
810 filename_search = FALSE;
811 }
813 if (xdpi_search) {
814 dpi_string = NULL;
815 dpi_string = repr->attribute("inkscape:export-xdpi");
816 if (dpi_string != NULL) {
817 xdpi = atof(dpi_string);
818 xdpi_search = FALSE;
819 }
820 }
822 if (ydpi_search) {
823 dpi_string = NULL;
824 dpi_string = repr->attribute("inkscape:export-ydpi");
825 if (dpi_string != NULL) {
826 ydpi = atof(dpi_string);
827 ydpi_search = FALSE;
828 }
829 }
830 }
832 /* If we still don't have a filename -- let's build
833 one that's nice */
834 if (filename == NULL) {
835 const gchar * id = NULL;
836 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
837 for(; reprlst != NULL; reprlst = reprlst->next) {
838 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
839 if (repr->attribute("id")) {
840 id = repr->attribute("id");
841 break;
842 }
843 }
844 if (id == NULL) /* This should never happen */
845 id = "bitmap";
847 gchar * directory = NULL;
848 const gchar * file_entry_text;
850 file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry));
851 if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') {
852 // std::cout << "Directory from dialog" << std::endl;
853 directory = g_dirname(file_entry_text);
854 }
856 if (directory == NULL) {
857 /* Grab document directory */
858 if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) {
859 // std::cout << "Directory from document" << std::endl;
860 directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT));
861 }
862 }
864 if (directory == NULL) {
865 // std::cout << "Home Directory" << std::endl;
866 directory = homedir_path(NULL);
867 }
869 gchar * id_ext = g_strconcat(id, ".png", NULL);
870 filename = g_build_filename(directory, id_ext, NULL);
871 g_free(directory);
872 g_free(id_ext);
873 }
874 }
875 break;
876 case SELECTION_CUSTOM:
877 default:
878 break;
879 }
881 if (filename != NULL) {
882 g_free(original_name);
883 original_name = g_strdup(filename);
884 gtk_entry_set_text(GTK_ENTRY(file_entry), filename);
885 }
887 if (xdpi != 0.0) {
888 sp_export_value_set(base, "xdpi", xdpi);
889 }
891 /* These can't be seperate, and setting x sets y, so for
892 now setting this is disabled. Hopefully it won't be in
893 the future */
894 if (FALSE && ydpi != 0.0) {
895 sp_export_value_set(base, "ydpi", ydpi);
896 }
897 }
899 return;
900 } // end of sp_export_area_toggled()
902 /// Called when dialog is deleted
903 static gint
904 sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base )
905 {
906 g_object_set_data (base, "cancel", (gpointer) 1);
907 return TRUE;
908 } // end of sp_export_progress_delete()
910 /// Called when progress is cancelled
911 static void
912 sp_export_progress_cancel ( GtkWidget *widget, GObject *base )
913 {
914 g_object_set_data (base, "cancel", (gpointer) 1);
915 } // end of sp_export_progress_cancel()
917 /// Called for every progress iteration
918 static unsigned int
919 sp_export_progress_callback (float value, void *data)
920 {
921 GtkWidget *prg;
922 int evtcount;
924 if (g_object_get_data ((GObject *) data, "cancel"))
925 return FALSE;
927 prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress");
928 gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value);
930 evtcount = 0;
931 while ((evtcount < 16) && gdk_events_pending ()) {
932 gtk_main_iteration_do (FALSE);
933 evtcount += 1;
934 }
936 gtk_main_iteration_do (FALSE);
938 return TRUE;
940 } // end of sp_export_progress_callback()
942 /// Called when export button is clicked
943 static void
944 sp_export_export_clicked (GtkButton *button, GtkObject *base)
945 {
946 if (!SP_ACTIVE_DESKTOP) return;
948 GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename");
949 gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe));
951 float const x0 = sp_export_value_get_px(base, "x0");
952 float const y0 = sp_export_value_get_px(base, "y0");
953 float const x1 = sp_export_value_get_px(base, "x1");
954 float const y1 = sp_export_value_get_px(base, "y1");
955 float const xdpi = sp_export_value_get(base, "xdpi");
956 float const ydpi = sp_export_value_get(base, "ydpi");
957 int const width = int(sp_export_value_get(base, "bmwidth") + 0.5);
958 int const height = int(sp_export_value_get(base, "bmheight") + 0.5);
960 if (filename == NULL || *filename == '\0') {
961 sp_ui_error_dialog(_("You have to enter a filename"));
962 return;
963 }
965 if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
966 sp_ui_error_dialog (_("The chosen area to be exported is invalid"));
967 return;
968 }
970 gchar *dirname = g_dirname(filename);
971 if ( dirname == NULL
972 || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
973 {
974 gchar *safeDir = Inkscape::IO::sanitizeString(dirname);
975 gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
976 safeDir);
977 sp_ui_error_dialog(error);
978 g_free(safeDir);
979 g_free(error);
980 g_free(dirname);
981 return;
982 }
983 g_free(dirname);
985 SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP);
986 GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */
987 char *fn;
988 gchar *text;
990 dlg = gtk_dialog_new ();
991 gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress"));
992 prg = gtk_progress_bar_new ();
993 sp_transientize (dlg);
994 gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
995 g_object_set_data ((GObject *) base, "progress", prg);
996 fn = g_path_get_basename (filename);
997 text = g_strdup_printf ( _("Exporting %s (%d x %d)"),
998 fn, width, height);
999 g_free (fn);
1000 gtk_progress_bar_set_text ((GtkProgressBar *) prg, text);
1001 g_free (text);
1002 gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg,
1003 GTK_PROGRESS_LEFT_TO_RIGHT);
1004 gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox,
1005 prg, FALSE, FALSE, 4 );
1006 btn = gtk_dialog_add_button ( GTK_DIALOG (dlg),
1007 GTK_STOCK_CANCEL,
1008 GTK_RESPONSE_CANCEL );
1010 g_signal_connect ( (GObject *) dlg, "delete_event",
1011 (GCallback) sp_export_progress_delete, base);
1012 g_signal_connect ( (GObject *) btn, "clicked",
1013 (GCallback) sp_export_progress_cancel, base);
1014 gtk_window_set_modal ((GtkWindow *) dlg, TRUE);
1015 gtk_widget_show_all (dlg);
1017 /* Do export */
1018 if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename,
1019 x0, y0, x1, y1, width, height,
1020 nv->pagecolor,
1021 sp_export_progress_callback, base)) {
1022 gchar * error;
1023 gchar * safeFile = Inkscape::IO::sanitizeString(filename);
1024 error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
1025 sp_ui_error_dialog(error);
1026 g_free(safeFile);
1027 g_free(error);
1028 }
1030 /* Reset the filename so that it can be changed again by changing
1031 selections and all that */
1032 g_free(original_name);
1033 original_name = g_strdup(filename);
1034 gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE);
1036 gtk_widget_destroy (dlg);
1037 g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0);
1039 /* Setup the values in the document */
1040 switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) {
1041 case SELECTION_PAGE:
1042 case SELECTION_DRAWING: {
1043 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1044 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1045 bool modified = FALSE;
1046 const gchar * temp_string;
1048 bool saved = sp_document_get_undo_sensitive(doc);
1049 sp_document_set_undo_sensitive(doc, FALSE);
1051 temp_string = repr->attribute("inkscape:export-filename");
1052 if (temp_string == NULL || strcmp(temp_string, filename)) {
1053 repr->setAttribute("inkscape:export-filename", filename);
1054 modified = TRUE;
1055 }
1056 temp_string = repr->attribute("inkscape:export-xdpi");
1057 if (temp_string == NULL || xdpi != atof(temp_string)) {
1058 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1059 modified = TRUE;
1060 }
1061 temp_string = repr->attribute("inkscape:export-ydpi");
1062 if (temp_string == NULL || xdpi != atof(temp_string)) {
1063 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1064 modified = TRUE;
1065 }
1067 if (modified)
1068 repr->setAttribute("sodipodi:modified", "TRUE");
1069 sp_document_set_undo_sensitive(doc, saved);
1070 break;
1071 }
1072 case SELECTION_SELECTION: {
1073 const GSList * reprlst;
1074 SPDocument * doc = SP_ACTIVE_DOCUMENT;
1075 bool modified = FALSE;
1077 bool saved = sp_document_get_undo_sensitive(doc);
1078 sp_document_set_undo_sensitive(doc, FALSE);
1079 reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList();
1081 for(; reprlst != NULL; reprlst = reprlst->next) {
1082 Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data;
1083 const gchar * temp_string;
1085 if (repr->attribute("id") == NULL ||
1086 !(g_strrstr(filename, repr->attribute("id")) != NULL &&
1087 (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) ||
1088 strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) {
1089 temp_string = repr->attribute("inkscape:export-filename");
1090 if (temp_string == NULL || strcmp(temp_string, filename)) {
1091 repr->setAttribute("inkscape:export-filename", filename);
1092 modified = TRUE;
1093 }
1094 }
1095 temp_string = repr->attribute("inkscape:export-xdpi");
1096 if (temp_string == NULL || xdpi != atof(temp_string)) {
1097 sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi);
1098 modified = TRUE;
1099 }
1100 temp_string = repr->attribute("inkscape:export-ydpi");
1101 if (temp_string == NULL || xdpi != atof(temp_string)) {
1102 sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi);
1103 modified = TRUE;
1104 }
1105 }
1107 if (modified) {
1108 Inkscape::XML::Node * repr = sp_document_repr_root(doc);
1109 repr->setAttribute("sodipodi:modified", "TRUE");
1110 }
1112 sp_document_set_undo_sensitive(doc, saved);
1113 break;
1114 }
1115 default:
1116 break;
1117 }
1120 return;
1121 } // end of sp_export_export_clicked()
1123 /// Called when Browse button is clicked
1124 static void
1125 sp_export_browse_clicked (GtkButton *button, gpointer userdata)
1126 {
1127 GtkWidget *fs, *fe;
1128 const gchar *filename;
1130 fs = gtk_file_selection_new (_("Select a filename for exporting"));
1131 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1133 sp_transientize (fs);
1135 gtk_window_set_modal(GTK_WINDOW (fs), true);
1137 filename = gtk_entry_get_text (GTK_ENTRY (fe));
1139 if (*filename == '\0') {
1140 filename = homedir_path(NULL);
1141 }
1143 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename);
1145 g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1146 "clicked",
1147 G_CALLBACK (sp_export_browse_store),
1148 (gpointer) fs );
1150 g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button),
1151 "clicked",
1152 G_CALLBACK (gtk_widget_destroy),
1153 (gpointer) fs );
1155 g_signal_connect_swapped ( GTK_OBJECT
1156 (GTK_FILE_SELECTION (fs)->cancel_button),
1157 "clicked",
1158 G_CALLBACK (gtk_widget_destroy),
1159 (gpointer) fs );
1161 gtk_widget_show (fs);
1163 return;
1164 } // end of sp_export_browse_clicked()
1166 /// Called when OK clicked in file dialog
1167 static void
1168 sp_export_browse_store (GtkButton *button, gpointer userdata)
1169 {
1170 GtkWidget *fs = (GtkWidget *)userdata, *fe;
1171 const gchar *file;
1173 fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename");
1175 file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs));
1176 gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL );
1177 gtk_entry_set_text (GTK_ENTRY (fe), utf8file);
1178 g_free(utf8file);
1180 g_object_set_data (G_OBJECT (dlg), "filename", fe);
1182 return;
1183 } // end of sp_export_browse_store()
1185 // TODO: Move this to nr-rect-fns.h.
1186 static bool
1187 sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two)
1188 {
1189 double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
1190 return (
1191 (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) &&
1192 (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) &&
1193 (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) &&
1194 (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon)
1195 );
1196 }
1198 /**
1199 \brief This function is used to detect the current selection setting
1200 based on the values in the x0, y0, x1 and y0 fields.
1201 \param base The export dialog itself
1203 One of the most confusing parts of this function is why the array
1204 is built at the beginning. What needs to happen here is that we
1205 should always check the current selection to see if it is the valid
1206 one. While this is a performance improvement it is also a usability
1207 one during the cases where things like selections and drawings match
1208 size. This way buttons change less 'randomly' (atleast in the eyes
1209 of the user). To do this an array is built where the current selection
1210 type is placed first, and then the others in an order from smallest
1211 to largest (this can be configured by reshuffling \c test_order).
1213 All of the values in this function are rounded to two decimal places
1214 because that is what is shown to the user. While everything is kept
1215 more accurate than that, the user can't control more acurrate than
1216 that, so for this to work for them - it needs to check on that level
1217 of accuracy.
1219 \todo finish writing this up
1220 */
1221 static void
1222 sp_export_detect_size(GtkObject * base) {
1223 static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
1224 selection_type this_test[SELECTION_NUMBER_OF + 1];
1225 selection_type key = SELECTION_NUMBER_OF;
1227 NR::Point x(sp_export_value_get_px (base, "x0"),
1228 sp_export_value_get_px (base, "y0"));
1229 NR::Point y(sp_export_value_get_px (base, "x1"),
1230 sp_export_value_get_px (base, "y1"));
1231 NR::Rect current_bbox(x, y);
1232 //std::cout << "Current " << current_bbox;
1234 this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1235 for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
1236 this_test[i + 1] = test_order[i];
1237 }
1239 for (int i = 0;
1240 i < SELECTION_NUMBER_OF + 1 &&
1241 key == SELECTION_NUMBER_OF &&
1242 SP_ACTIVE_DESKTOP != NULL;
1243 i++) {
1244 // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl;
1245 switch (this_test[i]) {
1246 case SELECTION_SELECTION:
1247 if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) {
1248 NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds();
1250 //std::cout << "Selection " << bbox;
1251 if (sp_export_bbox_equal(bbox,current_bbox)) {
1252 key = SELECTION_SELECTION;
1253 }
1254 }
1255 break;
1256 case SELECTION_DRAWING: {
1257 SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1259 NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)));
1261 // std::cout << "Drawing " << bbox2;
1262 if (sp_export_bbox_equal(bbox,current_bbox)) {
1263 key = SELECTION_DRAWING;
1264 }
1265 break;
1266 }
1268 case SELECTION_PAGE: {
1269 SPDocument *doc;
1271 doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP);
1273 NR::Point x(0.0, 0.0);
1274 NR::Point y(sp_document_width(doc),
1275 sp_document_height(doc));
1276 NR::Rect bbox(x, y);
1278 // std::cout << "Page " << bbox;
1279 if (sp_export_bbox_equal(bbox,current_bbox)) {
1280 key = SELECTION_PAGE;
1281 }
1283 break;
1284 }
1285 default:
1286 break;
1287 }
1288 }
1289 // std::cout << std::endl;
1291 if (key == SELECTION_NUMBER_OF) {
1292 key = SELECTION_CUSTOM;
1293 }
1295 /* We're now using a custom size, not a fixed one */
1296 /* printf("Detecting state: %s\n", selection_names[key]); */
1297 selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")));
1298 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE);
1299 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE);
1300 gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key);
1302 return;
1303 } /* sp_export_detect_size */
1305 /// Called when area x0 value is changed
1306 static void
1307 sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base)
1308 {
1309 float x0, x1, xdpi, width;
1311 if (gtk_object_get_data (base, "update"))
1312 return;
1314 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1315 (base, "units")))
1316 {
1317 return;
1318 }
1320 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1322 x0 = sp_export_value_get_px (base, "x0");
1323 x1 = sp_export_value_get_px (base, "x1");
1324 xdpi = sp_export_value_get (base, "xdpi");
1326 width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
1328 if (width < SP_EXPORT_MIN_SIZE) {
1329 const gchar *key;
1330 width = SP_EXPORT_MIN_SIZE;
1331 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1333 if (!strcmp (key, "x0")) {
1334 x1 = x0 + width * DPI_BASE / xdpi;
1335 sp_export_value_set_px (base, "x1", x1);
1336 } else {
1337 x0 = x1 - width * DPI_BASE / xdpi;
1338 sp_export_value_set_px (base, "x0", x0);
1339 }
1340 }
1342 sp_export_value_set_px (base, "width", x1 - x0);
1343 sp_export_value_set (base, "bmwidth", width);
1345 sp_export_detect_size(base);
1347 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1349 return;
1350 } // end of sp_export_area_x_value_changed()
1352 /// Called when area y0 value is changed.
1353 static void
1354 sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base)
1355 {
1356 float y0, y1, ydpi, height;
1358 if (gtk_object_get_data (base, "update"))
1359 return;
1361 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1362 (base, "units")))
1363 {
1364 return;
1365 }
1367 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1369 y0 = sp_export_value_get_px (base, "y0");
1370 y1 = sp_export_value_get_px (base, "y1");
1371 ydpi = sp_export_value_get (base, "ydpi");
1373 height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
1375 if (height < SP_EXPORT_MIN_SIZE) {
1376 const gchar *key;
1377 height = SP_EXPORT_MIN_SIZE;
1378 key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key");
1379 if (!strcmp (key, "y0")) {
1380 y1 = y0 + height * DPI_BASE / ydpi;
1381 sp_export_value_set_px (base, "y1", y1);
1382 } else {
1383 y0 = y1 - height * DPI_BASE / ydpi;
1384 sp_export_value_set_px (base, "y0", y0);
1385 }
1386 }
1388 sp_export_value_set_px (base, "height", y1 - y0);
1389 sp_export_value_set (base, "bmheight", height);
1391 sp_export_detect_size(base);
1393 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1395 return;
1396 } // end of sp_export_area_y_value_changed()
1398 /// Called when x1-x0 or area width is changed
1399 static void
1400 sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1401 {
1402 float x0, x1, xdpi, width, bmwidth;
1404 if (gtk_object_get_data (base, "update"))
1405 return;
1407 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1408 (base, "units"))) {
1409 return;
1410 }
1412 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1414 x0 = sp_export_value_get_px (base, "x0");
1415 x1 = sp_export_value_get_px (base, "x1");
1416 xdpi = sp_export_value_get (base, "xdpi");
1417 width = sp_export_value_get_px (base, "width");
1418 bmwidth = floor (width * xdpi / DPI_BASE + 0.5);
1420 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1422 bmwidth = SP_EXPORT_MIN_SIZE;
1423 width = bmwidth * DPI_BASE / xdpi;
1424 sp_export_value_set_px (base, "width", width);
1425 }
1427 sp_export_value_set_px (base, "x1", x0 + width);
1428 sp_export_value_set (base, "bmwidth", bmwidth);
1430 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1432 return;
1433 } // end of sp_export_area_width_value_changed()
1435 /// Called when y1-y0 or area height is changed.
1436 static void
1437 sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base)
1438 {
1440 float y0, y1, ydpi, height, bmheight;
1442 if (gtk_object_get_data (base, "update"))
1443 return;
1445 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1446 (base, "units"))) {
1447 return;
1448 }
1450 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1452 y0 = sp_export_value_get_px (base, "y0");
1453 y1 = sp_export_value_get_px (base, "y1");
1454 ydpi = sp_export_value_get (base, "ydpi");
1455 height = sp_export_value_get_px (base, "height");
1456 bmheight = floor (height * ydpi / DPI_BASE + 0.5);
1458 if (bmheight < SP_EXPORT_MIN_SIZE) {
1459 bmheight = SP_EXPORT_MIN_SIZE;
1460 height = bmheight * DPI_BASE / ydpi;
1461 sp_export_value_set_px (base, "height", height);
1462 }
1464 sp_export_value_set_px (base, "y1", y0 + height);
1465 sp_export_value_set (base, "bmheight", bmheight);
1467 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1469 return;
1470 } // end of sp_export_area_height_value_changed()
1472 /**
1473 \brief A function to set the ydpi
1474 \param base The export dialog
1476 This function grabs all of the y values and then figures out the
1477 new bitmap size based on the changing dpi value. The dpi value is
1478 gotten from the xdpi setting as these can not currently be independent.
1479 */
1480 static void
1481 sp_export_set_image_y (GtkObject *base)
1482 {
1483 float y0, y1, xdpi;
1485 y0 = sp_export_value_get_px (base, "y0");
1486 y1 = sp_export_value_get_px (base, "y1");
1487 xdpi = sp_export_value_get (base, "xdpi");
1489 sp_export_value_set (base, "ydpi", xdpi);
1490 sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE);
1492 return;
1493 } // end of sp_export_set_image_y()
1495 /// Called when pixel width is changed
1496 static void
1497 sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base)
1498 {
1499 float x0, x1, bmwidth, xdpi;
1501 if (gtk_object_get_data (base, "update"))
1502 return;
1504 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1505 (base, "units"))) {
1506 return;
1507 }
1509 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1511 x0 = sp_export_value_get_px (base, "x0");
1512 x1 = sp_export_value_get_px (base, "x1");
1513 bmwidth = sp_export_value_get (base, "bmwidth");
1515 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1516 bmwidth = SP_EXPORT_MIN_SIZE;
1517 sp_export_value_set (base, "bmwidth", bmwidth);
1518 }
1520 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1521 sp_export_value_set (base, "xdpi", xdpi);
1523 sp_export_set_image_y (base);
1525 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1527 return;
1528 } // end of sp_export_bitmap_width_value_changed()
1530 /**
1531 \brief A function to adjust the bitmap width when the xdpi value changes
1532 \param adj The adjustment that was changed
1533 \param base The export dialog itself
1535 The first thing this function checks is to see if we are doing an
1536 update. If we are, this function just returns because there is another
1537 instance of it that will handle everything for us. If there is a
1538 units change, we also assume that everyone is being updated appropriately
1539 and there is nothing for us to do.
1541 If we're the highest level function, we set the update flag, and
1542 continue on our way.
1544 All of the values are grabbed using the \c sp_export_value_get functions
1545 (call to the _pt ones for x0 and x1 but just standard for xdpi). The
1546 xdpi value is saved in the preferences for the next time the dialog
1547 is opened. (does the selection dpi need to be set here?)
1549 A check is done to to ensure that we aren't outputing an invalid width,
1550 this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
1551 changed to make it valid.
1553 After all of this the bitmap width is changed.
1555 We also change the ydpi. This is a temporary hack as these can not
1556 currently be independent. This is likely to change in the future.
1557 */
1558 void
1559 sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base)
1560 {
1561 float x0, x1, xdpi, bmwidth;
1563 if (gtk_object_get_data (base, "update"))
1564 return;
1566 if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data
1567 (base, "units"))) {
1568 return;
1569 }
1571 gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE));
1573 x0 = sp_export_value_get_px (base, "x0");
1574 x1 = sp_export_value_get_px (base, "x1");
1575 xdpi = sp_export_value_get (base, "xdpi");
1577 // remember xdpi setting
1578 prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi);
1580 bmwidth = (x1 - x0) * xdpi / DPI_BASE;
1582 if (bmwidth < SP_EXPORT_MIN_SIZE) {
1583 bmwidth = SP_EXPORT_MIN_SIZE;
1584 if (x1 != x0)
1585 xdpi = bmwidth * DPI_BASE / (x1 - x0);
1586 else
1587 xdpi = DPI_BASE;
1588 sp_export_value_set (base, "xdpi", xdpi);
1589 }
1591 sp_export_value_set (base, "bmwidth", bmwidth);
1593 sp_export_set_image_y (base);
1595 gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE));
1597 return;
1598 } // end of sp_export_xdpi_value_changed()
1601 /**
1602 \brief A function to change the area that is used for the exported
1603 bitmap.
1604 \param base This is the export dialog
1605 \param x0 Horizontal upper left hand corner of the picture in points
1606 \param y0 Vertical upper left hand corner of the picture in points
1607 \param x1 Horizontal lower right hand corner of the picture in points
1608 \param y1 Vertical lower right hand corner of the picture in points
1610 This function just calls \c sp_export_value_set_px for each of the
1611 parameters that is passed in. This allows for setting them all in
1612 one convient area.
1614 Update is set to suspend all of the other test running while all the
1615 values are being set up. This allows for a performance increase, but
1616 it also means that the wrong type won't be detected with only some of
1617 the values set. After all the values are set everyone is told that
1618 there has been an update.
1619 */
1620 static void
1621 sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 )
1622 {
1623 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) );
1624 sp_export_value_set_px (base, "x1", x1);
1625 sp_export_value_set_px (base, "y1", y1);
1626 sp_export_value_set_px (base, "x0", x0);
1627 sp_export_value_set_px (base, "y0", y0);
1628 gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) );
1630 sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base);
1631 sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base);
1633 return;
1634 }
1636 /**
1637 \brief Sets the value of an adjustment
1638 \param base The export dialog
1639 \param key Which adjustment to set
1640 \param val What value to set it to
1642 This function finds the adjustment using the data stored in the
1643 export dialog. After finding the adjustment it then sets
1644 the value of it.
1645 */
1646 static void
1647 sp_export_value_set ( GtkObject *base, const gchar *key, double val )
1648 {
1649 GtkAdjustment *adj;
1651 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1653 gtk_adjustment_set_value (adj, val);
1654 }
1656 /**
1657 \brief A function to set a value using the units points
1658 \param base The export dialog
1659 \param key Which value should be set
1660 \param val What the value should be in points
1662 This function first gets the adjustment for the key that is passed
1663 in. It then figures out what units are currently being used in the
1664 dialog. After doing all of that, it then converts the incoming
1665 value and sets the adjustment.
1666 */
1667 static void
1668 sp_export_value_set_px (GtkObject *base, const gchar *key, double val)
1669 {
1670 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") );
1672 sp_export_value_set (base, key, sp_pixels_get_units (val, *unit));
1674 return;
1675 }
1677 /**
1678 \brief Get the value of an adjustment in the export dialog
1679 \param base The export dialog
1680 \param key Which adjustment is being looked for
1681 \return The value in the specified adjustment
1683 This function gets the adjustment from the data field in the export
1684 dialog. It then grabs the value from the adjustment.
1685 */
1686 static float
1687 sp_export_value_get ( GtkObject *base, const gchar *key )
1688 {
1689 GtkAdjustment *adj;
1691 adj = (GtkAdjustment *)gtk_object_get_data (base, key);
1693 return adj->value;
1694 } // end of sp_export_value_get()
1696 /**
1697 \brief Grabs a value in the export dialog and converts the unit
1698 to points
1699 \param base The export dialog
1700 \param key Which value should be returned
1701 \return The value in the adjustment in points
1703 This function, at its most basic, is a call to \c sp_export_value_get
1704 to get the value of the adjustment. It then finds the units that
1705 are being used by looking at the "units" attribute of the export
1706 dialog. Using that it converts the returned value into points.
1707 */
1708 static float
1709 sp_export_value_get_px ( GtkObject *base, const gchar *key )
1710 {
1711 float value = sp_export_value_get(base, key);
1712 const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units"));
1714 return sp_units_get_pixels (value, *unit);
1715 } // end of sp_export_value_get_px()
1717 /**
1718 \brief This function is called when the filename is changed by
1719 anyone. It resets the virgin bit.
1720 \param object Text entry box
1721 \param data The export dialog
1722 \return None
1724 This function gets called when the text area is modified. It is
1725 looking for the case where the text area is modified from its
1726 original value. In that case it sets the "filename-modified" bit
1727 to TRUE. If the text dialog returns back to the original text, the
1728 bit gets reset. This should stop simple mistakes.
1729 */
1730 static void
1731 sp_export_filename_modified (GtkObject * object, gpointer data)
1732 {
1733 GtkWidget * text_entry = (GtkWidget *)object;
1734 GtkWidget * export_dialog = (GtkWidget *)data;
1736 if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) {
1737 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE);
1738 // printf("Modified: FALSE\n");
1739 } else {
1740 gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE);
1741 // printf("Modified: TRUE\n");
1742 }
1744 return;
1745 } // end sp_export_filename_modified
1747 /*
1748 Local Variables:
1749 mode:c++
1750 c-file-style:"stroustrup"
1751 c-file-offsets:((innamespace . 0)(inline-open . 0))
1752 indent-tabs-mode:nil
1753 fill-column:99
1754 End:
1755 */
1756 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :