From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/dialogs/.cvsignore | 5 + src/dialogs/Makefile_insert | 75 + src/dialogs/clonetiler.cpp | 2578 +++++++++++++++++++++++++++++++++++ src/dialogs/clonetiler.h | 31 + src/dialogs/debugdialog.cpp | 348 +++++ src/dialogs/debugdialog.h | 104 ++ src/dialogs/dialog-events.cpp | 247 ++++ src/dialogs/dialog-events.h | 66 + src/dialogs/display-settings.cpp | 1575 +++++++++++++++++++++ src/dialogs/display-settings.h | 69 + src/dialogs/eek-preview.cpp | 522 +++++++ src/dialogs/eek-preview.h | 112 ++ src/dialogs/export.cpp | 1753 ++++++++++++++++++++++++ src/dialogs/export.h | 24 + src/dialogs/extensions.cpp | 128 ++ src/dialogs/extensions.h | 61 + src/dialogs/filedialog-win32.cpp | 381 ++++++ src/dialogs/filedialog.cpp | 1439 +++++++++++++++++++ src/dialogs/filedialog.h | 172 +++ src/dialogs/fill-style.cpp | 546 ++++++++ src/dialogs/fill-style.h | 35 + src/dialogs/find.cpp | 763 +++++++++++ src/dialogs/find.h | 31 + src/dialogs/iconpreview.cpp | 320 +++++ src/dialogs/iconpreview.h | 84 ++ src/dialogs/in-dt-coordsys.cpp | 37 + src/dialogs/in-dt-coordsys.h | 19 + src/dialogs/input.cpp | 273 ++++ src/dialogs/input.h | 32 + src/dialogs/item-properties.cpp | 497 +++++++ src/dialogs/item-properties.h | 38 + src/dialogs/layer-properties.cpp | 199 +++ src/dialogs/layer-properties.h | 110 ++ src/dialogs/makefile.in | 17 + src/dialogs/object-attributes.cpp | 140 ++ src/dialogs/object-attributes.h | 37 + src/dialogs/object-properties.cpp | 318 +++++ src/dialogs/object-properties.h | 34 + src/dialogs/rdf.cpp | 1002 ++++++++++++++ src/dialogs/rdf.h | 124 ++ src/dialogs/sp-attribute-widget.cpp | 802 +++++++++++ src/dialogs/sp-attribute-widget.h | 128 ++ src/dialogs/stroke-style.cpp | 1726 +++++++++++++++++++++++ src/dialogs/stroke-style.h | 35 + src/dialogs/swatches.cpp | 516 +++++++ src/dialogs/swatches.h | 93 ++ src/dialogs/text-edit.cpp | 915 +++++++++++++ src/dialogs/text-edit.h | 24 + src/dialogs/tiledialog.cpp | 872 ++++++++++++ src/dialogs/tiledialog.h | 190 +++ src/dialogs/unclump.cpp | 371 +++++ src/dialogs/unclump.h | 20 + src/dialogs/xml-tree.cpp | 1575 +++++++++++++++++++++ src/dialogs/xml-tree.h | 29 + 54 files changed, 21642 insertions(+) create mode 100644 src/dialogs/.cvsignore create mode 100644 src/dialogs/Makefile_insert create mode 100644 src/dialogs/clonetiler.cpp create mode 100644 src/dialogs/clonetiler.h create mode 100644 src/dialogs/debugdialog.cpp create mode 100644 src/dialogs/debugdialog.h create mode 100644 src/dialogs/dialog-events.cpp create mode 100644 src/dialogs/dialog-events.h create mode 100644 src/dialogs/display-settings.cpp create mode 100644 src/dialogs/display-settings.h create mode 100644 src/dialogs/eek-preview.cpp create mode 100644 src/dialogs/eek-preview.h create mode 100644 src/dialogs/export.cpp create mode 100644 src/dialogs/export.h create mode 100644 src/dialogs/extensions.cpp create mode 100644 src/dialogs/extensions.h create mode 100644 src/dialogs/filedialog-win32.cpp create mode 100644 src/dialogs/filedialog.cpp create mode 100644 src/dialogs/filedialog.h create mode 100644 src/dialogs/fill-style.cpp create mode 100644 src/dialogs/fill-style.h create mode 100644 src/dialogs/find.cpp create mode 100644 src/dialogs/find.h create mode 100644 src/dialogs/iconpreview.cpp create mode 100644 src/dialogs/iconpreview.h create mode 100644 src/dialogs/in-dt-coordsys.cpp create mode 100644 src/dialogs/in-dt-coordsys.h create mode 100644 src/dialogs/input.cpp create mode 100644 src/dialogs/input.h create mode 100644 src/dialogs/item-properties.cpp create mode 100644 src/dialogs/item-properties.h create mode 100644 src/dialogs/layer-properties.cpp create mode 100644 src/dialogs/layer-properties.h create mode 100644 src/dialogs/makefile.in create mode 100644 src/dialogs/object-attributes.cpp create mode 100644 src/dialogs/object-attributes.h create mode 100644 src/dialogs/object-properties.cpp create mode 100644 src/dialogs/object-properties.h create mode 100644 src/dialogs/rdf.cpp create mode 100644 src/dialogs/rdf.h create mode 100644 src/dialogs/sp-attribute-widget.cpp create mode 100644 src/dialogs/sp-attribute-widget.h create mode 100644 src/dialogs/stroke-style.cpp create mode 100644 src/dialogs/stroke-style.h create mode 100644 src/dialogs/swatches.cpp create mode 100644 src/dialogs/swatches.h create mode 100644 src/dialogs/text-edit.cpp create mode 100644 src/dialogs/text-edit.h create mode 100644 src/dialogs/tiledialog.cpp create mode 100644 src/dialogs/tiledialog.h create mode 100644 src/dialogs/unclump.cpp create mode 100644 src/dialogs/unclump.h create mode 100644 src/dialogs/xml-tree.cpp create mode 100644 src/dialogs/xml-tree.h (limited to 'src/dialogs') diff --git a/src/dialogs/.cvsignore b/src/dialogs/.cvsignore new file mode 100644 index 000000000..e8014d011 --- /dev/null +++ b/src/dialogs/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +makefile +.dirstamp diff --git a/src/dialogs/Makefile_insert b/src/dialogs/Makefile_insert new file mode 100644 index 000000000..06ef2975c --- /dev/null +++ b/src/dialogs/Makefile_insert @@ -0,0 +1,75 @@ +## Makefile.am fragment sourced by src/Makefile.am. +# +# Several object property dialogs +# Author: Lauris Kaplinski +# +# Here should be things, that use only xml interface, not objects themselves +# Currently we still have selection_changed functions, but these will be +# replaced by selection 'changed' signal handlers +# + +dialogs/all: dialogs/libspdialogs.a + +dialogs/clean: + rm -f dialogs/libspdialogs.a $(dialogs_libspdialogs_a_OBJECTS) + +dialogs_libspdialogs_a_SOURCES = \ + dialogs/debugdialog.cpp \ + dialogs/debugdialog.h \ + dialogs/dialog-events.cpp \ + dialogs/dialog-events.h \ + dialogs/display-settings.cpp \ + dialogs/display-settings.h \ + dialogs/eek-preview.h \ + dialogs/eek-preview.cpp \ + dialogs/export.cpp \ + dialogs/export.h \ + dialogs/extensions.cpp \ + dialogs/extensions.h \ + dialogs/filedialog.cpp \ + dialogs/filedialog.h \ + dialogs/fill-style.cpp \ + dialogs/fill-style.h \ + dialogs/in-dt-coordsys.cpp \ + dialogs/in-dt-coordsys.h \ + dialogs/input.cpp \ + dialogs/input.h \ + dialogs/item-properties.cpp \ + dialogs/item-properties.h \ + dialogs/layer-properties.cpp \ + dialogs/layer-properties.h \ + dialogs/object-attributes.cpp \ + dialogs/object-attributes.h \ + dialogs/object-properties.cpp \ + dialogs/object-properties.h \ + dialogs/sp-attribute-widget.cpp \ + dialogs/sp-attribute-widget.h \ + dialogs/stroke-style.cpp \ + dialogs/stroke-style.h \ + dialogs/swatches.cpp \ + dialogs/swatches.h \ + dialogs/text-edit.cpp \ + dialogs/text-edit.h \ + dialogs/tiledialog.cpp \ + dialogs/tiledialog.h \ + dialogs/xml-tree.cpp \ + dialogs/xml-tree.h \ + dialogs/rdf.cpp \ + dialogs/rdf.h \ + dialogs/find.cpp \ + dialogs/find.h \ + dialogs/clonetiler.cpp \ + dialogs/clonetiler.h \ + dialogs/unclump.cpp \ + dialogs/unclump.h \ + dialogs/iconpreview.cpp \ + dialogs/iconpreview.h + + +# dialogs/sp-widget.c \ +# dialogs/sp-widget.h \ +# dialogs/gradient-vector.c \ +# dialogs/gradient-vector.h \ +# dialogs/gradient-selector.c \ +# dialogs/gradient-selector.h + diff --git a/src/dialogs/clonetiler.cpp b/src/dialogs/clonetiler.cpp new file mode 100644 index 000000000..72cae3c47 --- /dev/null +++ b/src/dialogs/clonetiler.cpp @@ -0,0 +1,2578 @@ +#define __SP_CLONE_TILER_C__ + +/* + * Clone tiling dialog + * + * Authors: + * bulia byak + * + * Copyright (C) 2004-2005 Authors + * Released under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include + +#include "application/application.h" +#include "application/editor.h" +#include "helper/window.h" +#include "helper/unit-menu.h" +#include "helper/units.h" +#include "widgets/icon.h" +#include "../inkscape.h" +#include "../prefs-utils.h" +#include "dialog-events.h" +#include "../macros.h" +#include "../verbs.h" +#include "../interface.h" +#include "../selection.h" +#include "../style.h" +#include "../desktop-handles.h" +#include "../sp-namedview.h" +#include "../document.h" +#include "../message-stack.h" +#include "../sp-use.h" +#include "unclump.h" + +#include "xml/repr.h" + +#include "svg/svg.h" + +#include "libnr/nr-matrix-fns.h" +#include "libnr/nr-matrix-ops.h" + +#include "libnr/nr-matrix-translate-ops.h" +#include "libnr/nr-translate-ops.h" +#include "libnr/nr-translate-rotate-ops.h" +#include "libnr/nr-translate-scale-ops.h" + +#include "display/nr-arena.h" +#include "display/nr-arena-item.h" + +#include "ui/widget/color-picker.h" + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.clonetiler"; + +#define SB_MARGIN 1 +#define VB_MARGIN 4 + +enum { + PICK_COLOR, + PICK_OPACITY, + PICK_R, + PICK_G, + PICK_B, + PICK_H, + PICK_S, + PICK_L +}; + +static GtkSizeGroup* table_row_labels = NULL; + +static sigc::connection _shutdown_connection; +static sigc::connection _dialogs_hidden_connection; +static sigc::connection _dialogs_unhidden_connection; +static sigc::connection _desktop_activated_connection; +static sigc::connection _color_changed_connection; + +static Inkscape::UI::Widget::ColorPicker *color_picker; + +static void +clonetiler_dialog_destroy (GtkObject *object, gpointer data) +{ + if (Inkscape::NSApplication::Application::getNewGui()) + { + _shutdown_connection.disconnect(); + _dialogs_hidden_connection.disconnect(); + _dialogs_unhidden_connection.disconnect(); + _desktop_activated_connection.disconnect(); + } else { + sp_signal_disconnect_by_data (INKSCAPE, dlg); + } + _color_changed_connection.disconnect(); + + delete color_picker; + + wd.win = dlg = NULL; + wd.stop = 0; + +} + +static gboolean +clonetiler_dialog_delete (GtkObject *object, GdkEvent * /*event*/, gpointer data) +{ + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it + +} + +static void on_delete() +{ + (void)clonetiler_dialog_delete (0, 0, NULL); +} + +static void +on_picker_color_changed (guint rgba) +{ + static bool is_updating = false; + if (is_updating || !SP_ACTIVE_DESKTOP) + return; + + is_updating = true; + + Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, prefs_path); + gchar c[32]; + sp_svg_write_color(c, 32, rgba); + repr->setAttribute("initial_color", c); + + is_updating = false; +} + +static guint clonetiler_number_of_clones (SPObject *obj); + +static void +clonetiler_change_selection (Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg) +{ + GtkWidget *buttons = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "buttons_on_tiles"); + GtkWidget *status = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "status"); + + if (selection->isEmpty()) { + gtk_widget_set_sensitive (buttons, FALSE); + gtk_label_set_markup (GTK_LABEL(status), _("Nothing selected.")); + return; + } + + if (g_slist_length ((GSList *) selection->itemList()) > 1) { + gtk_widget_set_sensitive (buttons, FALSE); + gtk_label_set_markup (GTK_LABEL(status), _("More than one object selected.")); + return; + } + + guint n = clonetiler_number_of_clones(selection->singleItem()); + if (n > 0) { + gtk_widget_set_sensitive (buttons, TRUE); + gchar *sta = g_strdup_printf (_("Object has %d tiled clones."), n); + gtk_label_set_markup (GTK_LABEL(status), sta); + g_free (sta); + } else { + gtk_widget_set_sensitive (buttons, FALSE); + gtk_label_set_markup (GTK_LABEL(status), _("Object has no tiled clones.")); + } +} + +static void +clonetiler_external_change (Inkscape::Application * /*inkscape*/, GtkWidget *dlg) +{ + clonetiler_change_selection (NULL, SP_DT_SELECTION(SP_ACTIVE_DESKTOP), dlg); +} + +static void clonetiler_disconnect_gsignal (GObject *widget, gpointer source) { + if (source && G_IS_OBJECT(source)) + sp_signal_disconnect_by_data (source, widget); +} + + +enum { + TILE_P1, + TILE_P2, + TILE_PM, + TILE_PG, + TILE_CM, + TILE_PMM, + TILE_PMG, + TILE_PGG, + TILE_CMM, + TILE_P4, + TILE_P4M, + TILE_P4G, + TILE_P3, + TILE_P31M, + TILE_P3M1, + TILE_P6, + TILE_P6M +}; + + +static NR::Matrix +clonetiler_get_transform ( + // symmetry group + int type, + // row, column + int x, int y, + // center, width, height of the tile + double cx, double cy, + double w, double h, + + // values from the dialog: + double d_x_per_x, double d_y_per_x, double d_x_per_y, double d_y_per_y, + int alternate_x, int alternate_y, double rand_x, double rand_y, + double d_per_x_exp, double d_per_y_exp, + double d_rot_per_x, double d_rot_per_y, int alternate_rotx, int alternate_roty, double rand_rot, + double d_scalex_per_x, double d_scaley_per_x, double d_scalex_per_y, double d_scaley_per_y, + int alternate_scalex, int alternate_scaley, double rand_scalex, double rand_scaley + ) +{ + // in abs units + double eff_x = (alternate_x? (x%2) : pow ((double) x, d_per_x_exp)); + double eff_y = (alternate_y? (y%2) : pow ((double) y, d_per_y_exp)); + double dx = d_x_per_x * w * eff_x + d_x_per_y * w * eff_y + rand_x * w * g_random_double_range (-1, 1); + double dy = d_y_per_x * h * eff_x + d_y_per_y * h * eff_y + rand_y * h * g_random_double_range (-1, 1); + + NR::Matrix rect_translate (NR::translate (w * pow ((double) x, d_per_x_exp) + dx, h * pow ((double) y, d_per_y_exp) + dy)); + + // in deg + double eff_x_rot = (alternate_rotx? (x%2) : (x)); + double eff_y_rot = (alternate_roty? (y%2) : (y)); + double drot = d_rot_per_x * eff_x_rot + d_rot_per_y * eff_y_rot + rand_rot * 180 * g_random_double_range (-1, 1); + + // times the original + double eff_x_s = (alternate_scalex? (x%2) : (x)); + double eff_y_s = (alternate_scaley? (y%2) : (y)); + double rand_scale_x, rand_scale_y; + if (rand_scaley == rand_scalex) { + // if rands are equal, scale proportionally + rand_scale_x = rand_scale_y = rand_scalex * 1 * g_random_double_range (-1, 1); + } else { + rand_scale_x = rand_scalex * 1 * g_random_double_range (-1, 1); + rand_scale_y = rand_scaley * 1 * g_random_double_range (-1, 1); + } + double dscalex = 1 + d_scalex_per_x * eff_x_s + d_scalex_per_y * eff_y_s + rand_scale_x; + if (dscalex < 0) dscalex = 0; + double dscaley = 1 + d_scaley_per_x * eff_x_s + d_scaley_per_y * eff_y_s + rand_scale_y; + if (dscaley < 0) dscaley = 0; + + NR::Matrix drot_c = NR::translate(-cx, -cy) * NR::rotate (M_PI*drot/180) * NR::translate(cx, cy); + + NR::Matrix dscale_c = NR::translate(-cx, -cy) * NR::scale (dscalex, dscaley) * NR::translate(cx, cy); + + NR::Matrix d_s_r = dscale_c * drot_c; + + NR::Matrix rotate_180_c = NR::translate(-cx, -cy) * NR::rotate (M_PI) * NR::translate(cx, cy); + + NR::Matrix rotate_90_c = NR::translate(-cx, -cy) * NR::rotate (-M_PI/2) * NR::translate(cx, cy); + NR::Matrix rotate_m90_c = NR::translate(-cx, -cy) * NR::rotate (M_PI/2) * NR::translate(cx, cy); + + NR::Matrix rotate_120_c = NR::translate(-cx, -cy) * NR::rotate (-2*M_PI/3) * NR::translate(cx, cy); + NR::Matrix rotate_m120_c = NR::translate(-cx, -cy) * NR::rotate (2*M_PI/3) * NR::translate(cx, cy); + + NR::Matrix rotate_60_c = NR::translate(-cx, -cy) * NR::rotate (-M_PI/3) * NR::translate(cx, cy); + NR::Matrix rotate_m60_c = NR::translate(-cx, -cy) * NR::rotate (M_PI/3) * NR::translate(cx, cy); + + double cos60 = cos(M_PI/3); + double sin60 = sin(M_PI/3); + double cos30 = cos(M_PI/6); + double sin30 = sin(M_PI/6); + + NR::Matrix flip_x = NR::translate(-cx, -cy) * NR::scale (-1, 1) * NR::translate(cx, cy); + NR::Matrix flip_y = NR::translate(-cx, -cy) * NR::scale (1, -1) * NR::translate(cx, cy); + + x = (int) pow ((double) x, d_per_x_exp); + y = (int) pow ((double) y, d_per_y_exp); + + switch (type) { + + case TILE_P1: + return d_s_r * rect_translate; + break; + + case TILE_P2: + if (x % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * rotate_180_c * rect_translate; + } + break; + + case TILE_PM: + if (x % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + break; + + case TILE_PG: + if (y % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + break; + + case TILE_CM: + if ((x + y) % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + break; + + case TILE_PMM: + if (y % 2 == 0) { + if (x % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + } else { + if (x % 2 == 0) { + return d_s_r * flip_y * rect_translate; + } else { + return d_s_r * flip_x * flip_y * rect_translate; + } + } + break; + + case TILE_PMG: + if (y % 4 == 0) { + return d_s_r * rect_translate; + } else if (y % 4 == 1) { + return d_s_r * flip_y * rect_translate; + } else if (y % 4 == 2) { + return d_s_r * flip_x * rect_translate; + } else if (y % 4 == 3) { + return d_s_r * flip_x * flip_y * rect_translate; + } + break; + + case TILE_PGG: + if (y % 2 == 0) { + if (x % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_y * rect_translate; + } + } else { + if (x % 2 == 0) { + return d_s_r * rotate_180_c * rect_translate; + } else { + return d_s_r * rotate_180_c * flip_y * rect_translate; + } + } + break; + + case TILE_CMM: + if (y % 4 == 0) { + if (x % 2 == 0) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + } else if (y % 4 == 1) { + if (x % 2 == 0) { + return d_s_r * flip_y * rect_translate; + } else { + return d_s_r * flip_x * flip_y * rect_translate; + } + } else if (y % 4 == 2) { + if (x % 2 == 1) { + return d_s_r * rect_translate; + } else { + return d_s_r * flip_x * rect_translate; + } + } else { + if (x % 2 == 1) { + return d_s_r * flip_y * rect_translate; + } else { + return d_s_r * flip_x * flip_y * rect_translate; + } + } + break; + + case TILE_P4: + { + NR::Matrix ori (NR::translate ((w + h) * (x/2) + dx, (h + w) * (y/2) + dy)); + NR::Matrix dia1 (NR::translate (w/2 + h/2, -h/2 + w/2)); + NR::Matrix dia2 (NR::translate (-w/2 + h/2, h/2 + w/2)); + if (y % 2 == 0) { + if (x % 2 == 0) { + return d_s_r * ori; + } else { + return d_s_r * rotate_m90_c * dia1 * ori; + } + } else { + if (x % 2 == 0) { + return d_s_r * rotate_90_c * dia2 * ori; + } else { + return d_s_r * rotate_180_c * dia1 * dia2 * ori; + } + } + } + break; + + case TILE_P4M: + { + double max = MAX(w, h); + NR::Matrix ori (NR::translate ((max + max) * (x/4) + dx, (max + max) * (y/2) + dy)); + NR::Matrix dia1 (NR::translate (w/2 - h/2, h/2 - w/2)); + NR::Matrix dia2 (NR::translate (-h/2 + w/2, w/2 - h/2)); + if (y % 2 == 0) { + if (x % 4 == 0) { + return d_s_r * ori; + } else if (x % 4 == 1) { + return d_s_r * flip_y * rotate_m90_c * dia1 * ori; + } else if (x % 4 == 2) { + return d_s_r * rotate_m90_c * dia1 * NR::translate (h, 0) * ori; + } else if (x % 4 == 3) { + return d_s_r * flip_x * NR::translate (w, 0) * ori; + } + } else { + if (x % 4 == 0) { + return d_s_r * flip_y * NR::translate(0, h) * ori; + } else if (x % 4 == 1) { + return d_s_r * rotate_90_c * dia2 * NR::translate(0, h) * ori; + } else if (x % 4 == 2) { + return d_s_r * flip_y * rotate_90_c * dia2 * NR::translate(h, 0) * NR::translate(0, h) * ori; + } else if (x % 4 == 3) { + return d_s_r * flip_y * flip_x * NR::translate(w, 0) * NR::translate(0, h) * ori; + } + } + } + break; + + case TILE_P4G: + { + double max = MAX(w, h); + NR::Matrix ori (NR::translate ((max + max) * (x/4) + dx, (max + max) * y + dy)); + NR::Matrix dia1 (NR::translate (w/2 + h/2, h/2 - w/2)); + NR::Matrix dia2 (NR::translate (-h/2 + w/2, w/2 + h/2)); + if (((x/4) + y) % 2 == 0) { + if (x % 4 == 0) { + return d_s_r * ori; + } else if (x % 4 == 1) { + return d_s_r * rotate_m90_c * dia1 * ori; + } else if (x % 4 == 2) { + return d_s_r * rotate_90_c * dia2 * ori; + } else if (x % 4 == 3) { + return d_s_r * rotate_180_c * dia1 * dia2 * ori; + } + } else { + if (x % 4 == 0) { + return d_s_r * flip_y * NR::translate (0, h) * ori; + } else if (x % 4 == 1) { + return d_s_r * flip_y * rotate_m90_c * dia1 * NR::translate (-h, 0) * ori; + } else if (x % 4 == 2) { + return d_s_r * flip_y * rotate_90_c * dia2 * NR::translate (h, 0) * ori; + } else if (x % 4 == 3) { + return d_s_r * flip_x * NR::translate (w, 0) * ori; + } + } + } + break; + + case TILE_P3: + { + double width; + double height; + NR::Matrix dia1; + NR::Matrix dia2; + if (w > h) { + width = w + w * cos60; + height = 2 * w * sin60; + dia1 = NR::Matrix (NR::translate (w/2 + w/2 * cos60, -(w/2 * sin60))); + dia2 = dia1 * NR::Matrix (NR::translate (0, 2 * (w/2 * sin60))); + } else { + width = h * cos (M_PI/6); + height = h; + dia1 = NR::Matrix (NR::translate (h/2 * cos30, -(h/2 * sin30))); + dia2 = dia1 * NR::Matrix (NR::translate (0, h/2)); + } + NR::Matrix ori (NR::translate (width * (2*(x/3) + y%2) + dx, (height/2) * y + dy)); + if (x % 3 == 0) { + return d_s_r * ori; + } else if (x % 3 == 1) { + return d_s_r * rotate_m120_c * dia1 * ori; + } else if (x % 3 == 2) { + return d_s_r * rotate_120_c * dia2 * ori; + } + } + break; + + case TILE_P31M: + { + NR::Matrix ori; + NR::Matrix dia1; + NR::Matrix dia2; + NR::Matrix dia3; + NR::Matrix dia4; + if (w > h) { + ori = NR::Matrix(NR::translate (w * (x/6) + w/2 * (y%2) + dx, (w * cos30) * y + dy)); + dia1 = NR::Matrix (NR::translate (0, h/2) * NR::translate (w/2, 0) * NR::translate (w/2 * cos60, -w/2 * sin60) * NR::translate (-h/2 * cos30, -h/2 * sin30) ); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (0, 2 * (w/2 * sin60 - h/2 * sin30))); + dia4 = dia3 * NR::Matrix (NR::translate (-h * cos30, h * sin30)); + } else { + ori = NR::Matrix (NR::translate (2*h * cos30 * (x/6 + 0.5*(y%2)) + dx, (2*h - h * sin30) * y + dy)); + dia1 = NR::Matrix (NR::translate (0, -h/2) * NR::translate (h/2 * cos30, h/2 * sin30)); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (0, h/2)); + dia4 = dia3 * NR::Matrix (NR::translate (-h * cos30, h * sin30)); + } + if (x % 6 == 0) { + return d_s_r * ori; + } else if (x % 6 == 1) { + return d_s_r * flip_y * rotate_m120_c * dia1 * ori; + } else if (x % 6 == 2) { + return d_s_r * rotate_m120_c * dia2 * ori; + } else if (x % 6 == 3) { + return d_s_r * flip_y * rotate_120_c * dia3 * ori; + } else if (x % 6 == 4) { + return d_s_r * rotate_120_c * dia4 * ori; + } else if (x % 6 == 5) { + return d_s_r * flip_y * NR::translate(0, h) * ori; + } + } + break; + + case TILE_P3M1: + { + double width; + double height; + NR::Matrix dia1; + NR::Matrix dia2; + NR::Matrix dia3; + NR::Matrix dia4; + if (w > h) { + width = w + w * cos60; + height = 2 * w * sin60; + dia1 = NR::Matrix (NR::translate (0, h/2) * NR::translate (w/2, 0) * NR::translate (w/2 * cos60, -w/2 * sin60) * NR::translate (-h/2 * cos30, -h/2 * sin30) ); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (0, 2 * (w/2 * sin60 - h/2 * sin30))); + dia4 = dia3 * NR::Matrix (NR::translate (-h * cos30, h * sin30)); + } else { + width = 2 * h * cos (M_PI/6); + height = 2 * h; + dia1 = NR::Matrix (NR::translate (0, -h/2) * NR::translate (h/2 * cos30, h/2 * sin30)); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (0, h/2)); + dia4 = dia3 * NR::Matrix (NR::translate (-h * cos30, h * sin30)); + } + NR::Matrix ori (NR::translate (width * (2*(x/6) + y%2) + dx, (height/2) * y + dy)); + if (x % 6 == 0) { + return d_s_r * ori; + } else if (x % 6 == 1) { + return d_s_r * flip_y * rotate_m120_c * dia1 * ori; + } else if (x % 6 == 2) { + return d_s_r * rotate_m120_c * dia2 * ori; + } else if (x % 6 == 3) { + return d_s_r * flip_y * rotate_120_c * dia3 * ori; + } else if (x % 6 == 4) { + return d_s_r * rotate_120_c * dia4 * ori; + } else if (x % 6 == 5) { + return d_s_r * flip_y * NR::translate(0, h) * ori; + } + } + break; + + case TILE_P6: + { + NR::Matrix ori; + NR::Matrix dia1; + NR::Matrix dia2; + NR::Matrix dia3; + NR::Matrix dia4; + NR::Matrix dia5; + if (w > h) { + ori = NR::Matrix(NR::translate (2*w * (x/6) + w * (y%2) + dx, (2*w * sin60) * y + dy)); + dia1 = NR::Matrix (NR::translate (w/2 * cos60, -w/2 * sin60)); + dia2 = dia1 * NR::Matrix (NR::translate (w/2, 0)); + dia3 = dia2 * NR::Matrix (NR::translate (w/2 * cos60, w/2 * sin60)); + dia4 = dia3 * NR::Matrix (NR::translate (-w/2 * cos60, w/2 * sin60)); + dia5 = dia4 * NR::Matrix (NR::translate (-w/2, 0)); + } else { + ori = NR::Matrix(NR::translate (2*h * cos30 * (x/6 + 0.5*(y%2)) + dx, (h + h * sin30) * y + dy)); + dia1 = NR::Matrix (NR::translate (-w/2, -h/2) * NR::translate (h/2 * cos30, -h/2 * sin30) * NR::translate (w/2 * cos60, w/2 * sin60)); + dia2 = dia1 * NR::Matrix (NR::translate (-w/2 * cos60, -w/2 * sin60) * NR::translate (h/2 * cos30, -h/2 * sin30) * NR::translate (h/2 * cos30, h/2 * sin30) * NR::translate (-w/2 * cos60, w/2 * sin60)); + dia3 = dia2 * NR::Matrix (NR::translate (w/2 * cos60, -w/2 * sin60) * NR::translate (h/2 * cos30, h/2 * sin30) * NR::translate (-w/2, h/2)); + dia4 = dia3 * dia1.inverse(); + dia5 = dia3 * dia2.inverse(); + } + if (x % 6 == 0) { + return d_s_r * ori; + } else if (x % 6 == 1) { + return d_s_r * rotate_m60_c * dia1 * ori; + } else if (x % 6 == 2) { + return d_s_r * rotate_m120_c * dia2 * ori; + } else if (x % 6 == 3) { + return d_s_r * rotate_180_c * dia3 * ori; + } else if (x % 6 == 4) { + return d_s_r * rotate_120_c * dia4 * ori; + } else if (x % 6 == 5) { + return d_s_r * rotate_60_c * dia5 * ori; + } + } + break; + + case TILE_P6M: + { + + NR::Matrix ori; + NR::Matrix dia1, dia2, dia3, dia4, dia5, dia6, dia7, dia8, dia9, dia10; + if (w > h) { + ori = NR::Matrix(NR::translate (2*w * (x/12) + w * (y%2) + dx, (2*w * sin60) * y + dy)); + dia1 = NR::Matrix (NR::translate (w/2, h/2) * NR::translate (-w/2 * cos60, -w/2 * sin60) * NR::translate (-h/2 * cos30, h/2 * sin30)); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, -h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (-h/2 * cos30, h/2 * sin30) * NR::translate (w * cos60, 0) * NR::translate (-h/2 * cos30, -h/2 * sin30)); + dia4 = dia3 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia5 = dia4 * NR::Matrix (NR::translate (-h/2 * cos30, -h/2 * sin30) * NR::translate (-w/2 * cos60, w/2 * sin60) * NR::translate (w/2, -h/2)); + dia6 = dia5 * NR::Matrix (NR::translate (0, h)); + dia7 = dia6 * dia1.inverse(); + dia8 = dia6 * dia2.inverse(); + dia9 = dia6 * dia3.inverse(); + dia10 = dia6 * dia4.inverse(); + } else { + ori = NR::Matrix(NR::translate (4*h * cos30 * (x/12 + 0.5*(y%2)) + dx, (2*h + 2*h * sin30) * y + dy)); + dia1 = NR::Matrix (NR::translate (-w/2, -h/2) * NR::translate (h/2 * cos30, -h/2 * sin30) * NR::translate (w/2 * cos60, w/2 * sin60)); + dia2 = dia1 * NR::Matrix (NR::translate (h * cos30, -h * sin30)); + dia3 = dia2 * NR::Matrix (NR::translate (-w/2 * cos60, -w/2 * sin60) * NR::translate (h * cos30, 0) * NR::translate (-w/2 * cos60, w/2 * sin60)); + dia4 = dia3 * NR::Matrix (NR::translate (h * cos30, h * sin30)); + dia5 = dia4 * NR::Matrix (NR::translate (w/2 * cos60, -w/2 * sin60) * NR::translate (h/2 * cos30, h/2 * sin30) * NR::translate (-w/2, h/2)); + dia6 = dia5 * NR::Matrix (NR::translate (0, h)); + dia7 = dia6 * dia1.inverse(); + dia8 = dia6 * dia2.inverse(); + dia9 = dia6 * dia3.inverse(); + dia10 = dia6 * dia4.inverse(); + } + if (x % 12 == 0) { + return d_s_r * ori; + } else if (x % 12 == 1) { + return d_s_r * flip_y * rotate_m60_c * dia1 * ori; + } else if (x % 12 == 2) { + return d_s_r * rotate_m60_c * dia2 * ori; + } else if (x % 12 == 3) { + return d_s_r * flip_y * rotate_m120_c * dia3 * ori; + } else if (x % 12 == 4) { + return d_s_r * rotate_m120_c * dia4 * ori; + } else if (x % 12 == 5) { + return d_s_r * flip_x * dia5 * ori; + } else if (x % 12 == 6) { + return d_s_r * flip_x * flip_y * dia6 * ori; + } else if (x % 12 == 7) { + return d_s_r * flip_y * rotate_120_c * dia7 * ori; + } else if (x % 12 == 8) { + return d_s_r * rotate_120_c * dia8 * ori; + } else if (x % 12 == 9) { + return d_s_r * flip_y * rotate_60_c * dia9 * ori; + } else if (x % 12 == 10) { + return d_s_r * rotate_60_c * dia10 * ori; + } else if (x % 12 == 11) { + return d_s_r * flip_y * NR::translate (0, h) * ori; + } + } + break; + + default: + break; + } + + return NR::identity(); +} + +static bool +clonetiler_is_a_clone_of (SPObject *tile, SPObject *obj) +{ + char *id_href = NULL; + + if (obj) { + Inkscape::XML::Node *obj_repr = SP_OBJECT_REPR(obj); + id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); + } + + if (SP_IS_USE(tile) && + SP_OBJECT_REPR(tile)->attribute("xlink:href") && + (!id_href || !strcmp(id_href, SP_OBJECT_REPR(tile)->attribute("xlink:href"))) && + SP_OBJECT_REPR(tile)->attribute("inkscape:tiled-clone-of") && + (!id_href || !strcmp(id_href, SP_OBJECT_REPR(tile)->attribute("inkscape:tiled-clone-of")))) + { + if (id_href) + g_free (id_href); + return true; + } else { + if (id_href) + g_free (id_href); + return false; + } +} + +static NRArena const *trace_arena = NULL; +static unsigned trace_visionkey; +static NRArenaItem *trace_root; +static gdouble trace_zoom; + +static void +clonetiler_trace_hide_tiled_clones_recursively (SPObject *from) +{ + if (!trace_arena) + return; + + for (SPObject *o = sp_object_first_child(from); o != NULL; o = SP_OBJECT_NEXT(o)) { + if (SP_IS_ITEM(o) && clonetiler_is_a_clone_of (o, NULL)) + sp_item_invoke_hide(SP_ITEM(o), trace_visionkey); // FIXME: hide each tiled clone's original too! + clonetiler_trace_hide_tiled_clones_recursively (o); + } +} + +static void +clonetiler_trace_setup (SPDocument *doc, gdouble zoom, SPItem *original) +{ + trace_arena = NRArena::create(); + /* Create ArenaItem and set transform */ + trace_visionkey = sp_item_display_key_new(1); + trace_root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (doc)), + (NRArena *) trace_arena, trace_visionkey, SP_ITEM_SHOW_DISPLAY); + + // hide the (current) original and any tiled clones, we only want to pick the background + sp_item_invoke_hide(original, trace_visionkey); + clonetiler_trace_hide_tiled_clones_recursively (SP_OBJECT(SP_DOCUMENT_ROOT (doc))); + + sp_document_root (doc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + sp_document_ensure_up_to_date(doc); + + trace_zoom = zoom; +} + +static guint32 +clonetiler_trace_pick (NR::Rect box) +{ + if (!trace_arena) + return 0; + + NRMatrix t; + nr_matrix_set_scale(&t, trace_zoom, trace_zoom); + nr_arena_item_set_transform(trace_root, &t); + NRGC gc(NULL); + nr_matrix_set_identity(&gc.transform); + nr_arena_item_invoke_update( trace_root, NULL, &gc, + NR_ARENA_ITEM_STATE_ALL, + NR_ARENA_ITEM_STATE_NONE ); + + /* Item integer bbox in points */ + NRRectL ibox; + ibox.x0 = (int) floor(trace_zoom * box.min()[NR::X] + 0.5); + ibox.y0 = (int) floor(trace_zoom * box.min()[NR::Y] + 0.5); + ibox.x1 = (int) floor(trace_zoom * box.max()[NR::X] + 0.5); + ibox.y1 = (int) floor(trace_zoom * box.max()[NR::Y] + 0.5); + + /* Find visible area */ + int width = ibox.x1 - ibox.x0; + int height = ibox.y1 - ibox.y0; + + /* Set up pixblock */ + guchar *px = nr_new(guchar, 4 * width * height); + memset(px, 0x00, 4 * width * height); + + /* Render */ + NRPixBlock pb; + nr_pixblock_setup_extern( &pb, NR_PIXBLOCK_MODE_R8G8B8A8N, + ibox.x0, ibox.y0, ibox.x1, ibox.y1, + px, 4 * width, FALSE, FALSE ); + nr_arena_item_invoke_render( trace_root, &ibox, &pb, + NR_ARENA_ITEM_RENDER_NO_CACHE ); + + double R = 0, G = 0, B = 0, A = 0; + double count = 0; + double weight = 0; + + for (int y = ibox.y0; y < ibox.y1; y++) { + const unsigned char *s = NR_PIXBLOCK_PX (&pb) + (y - ibox.y0) * pb.rs; + for (int x = ibox.x0; x < ibox.x1; x++) { + count += 1; + weight += s[3] / 255.0; + R += s[0] / 255.0; + G += s[1] / 255.0; + B += s[2] / 255.0; + A += s[3] / 255.0; + s += 4; + } + } + + nr_pixblock_release(&pb); + + R = R / weight; + G = G / weight; + B = B / weight; + A = A / count; + + R = CLAMP (R, 0.0, 1.0); + G = CLAMP (G, 0.0, 1.0); + B = CLAMP (B, 0.0, 1.0); + A = CLAMP (A, 0.0, 1.0); + + return SP_RGBA32_F_COMPOSE (R, G, B, A); +} + +static void +clonetiler_trace_finish () +{ + if (trace_arena) { + ((NRObject *) trace_arena)->unreference(); + trace_arena = NULL; + } +} + +static void +clonetiler_unclump (GtkWidget *widget, void *) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + // check if something is selected + if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select one object whose tiled clones to unclump.")); + return; + } + + SPObject *obj = SP_OBJECT(selection->singleItem()); + SPObject *parent = SP_OBJECT_PARENT (obj); + + GSList *to_unclump = NULL; // not including the original + + for (SPObject *child = sp_object_first_child(parent); child != NULL; child = SP_OBJECT_NEXT(child)) { + if (clonetiler_is_a_clone_of (child, obj)) { + to_unclump = g_slist_prepend (to_unclump, child); + } + } + + sp_document_ensure_up_to_date(SP_DT_DOCUMENT(desktop)); + + unclump (to_unclump); + + g_slist_free (to_unclump); + + sp_document_done (SP_DT_DOCUMENT (desktop)); +} + +static guint +clonetiler_number_of_clones (SPObject *obj) +{ + SPObject *parent = SP_OBJECT_PARENT (obj); + + guint n = 0; + + for (SPObject *child = sp_object_first_child(parent); child != NULL; child = SP_OBJECT_NEXT(child)) { + if (clonetiler_is_a_clone_of (child, obj)) { + n ++; + } + } + + return n; +} + +static void +clonetiler_remove (GtkWidget *widget, void *, bool do_undo = true) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + // check if something is selected + if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select one object whose tiled clones to remove.")); + return; + } + + SPObject *obj = SP_OBJECT(selection->singleItem()); + SPObject *parent = SP_OBJECT_PARENT (obj); + +// remove old tiling + GSList *to_delete = NULL; + for (SPObject *child = sp_object_first_child(parent); child != NULL; child = SP_OBJECT_NEXT(child)) { + if (clonetiler_is_a_clone_of (child, obj)) { + to_delete = g_slist_prepend (to_delete, child); + } + } + for (GSList *i = to_delete; i; i = i->next) { + SP_OBJECT(i->data)->deleteObject(); + } + g_slist_free (to_delete); + + clonetiler_change_selection (NULL, selection, dlg); + + if (do_undo) + sp_document_done (SP_DT_DOCUMENT (desktop)); +} + +static NR::Rect +transform_rect(NR::Rect const &r, NR::Matrix const &m) +{ + using NR::X; + using NR::Y; + NR::Point const p1 = r.corner(1) * m; + NR::Point const p2 = r.corner(2) * m; + NR::Point const p3 = r.corner(3) * m; + NR::Point const p4 = r.corner(4) * m; + return NR::Rect( + NR::Point( + std::min(std::min(p1[X], p2[X]), std::min(p3[X], p4[X])), + std::min(std::min(p1[Y], p2[Y]), std::min(p3[Y], p4[Y]))), + NR::Point( + std::max(std::max(p1[X], p2[X]), std::max(p3[X], p4[X])), + std::max(std::max(p1[Y], p2[Y]), std::max(p3[Y], p4[Y])))); +} + +/** +Randomizes \a val by \a rand, with 0 < val < 1 and all values (including 0, 1) having the same +probability of being displaced. + */ +static double +randomize01 (double val, double rand) +{ + double base = MIN (val - rand, 1 - 2*rand); + if (base < 0) base = 0; + val = base + g_random_double_range (0, MIN (2 * rand, 1 - base)); + return CLAMP(val, 0, 1); // this should be unnecessary with the above provisions, but just in case... +} + + +static void +clonetiler_apply (GtkWidget *widget, void *) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + // check if something is selected + if (selection->isEmpty()) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select an object to clone.")); + return; + } + + // Check if more than one object is selected. + if (g_slist_length((GSList *) selection->itemList()) > 1) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, group them and clone the group.")); + return; + } + + SPObject *obj = SP_OBJECT(selection->singleItem()); + Inkscape::XML::Node *obj_repr = SP_OBJECT_REPR(obj); + const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); + SPObject *parent = SP_OBJECT_PARENT (obj); + + clonetiler_remove (NULL, NULL, false); + + double d_x_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_x_per_x", 0, -100, 1000); + double d_y_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_y_per_x", 0, -100, 1000); + double d_per_x_exp = prefs_get_double_attribute_limited (prefs_path, "d_per_x_exp", 1, 0, 10); + double d_x_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_x_per_y", 0, -100, 1000); + double d_y_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_y_per_y", 0, -100, 1000); + double d_per_y_exp = prefs_get_double_attribute_limited (prefs_path, "d_per_y_exp", 1, 0, 10); + int alternate_x = prefs_get_int_attribute (prefs_path, "alternate_x", 0); + int alternate_y = prefs_get_int_attribute (prefs_path, "alternate_y", 0); + double rand_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_x", 0, 0, 1000); + double rand_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_y", 0, 0, 1000); + + double d_scalex_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_scalex_per_x", 0, -100, 1000); + double d_scaley_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_scaley_per_x", 0, -100, 1000); + double d_scalex_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_scalex_per_y", 0, -100, 1000); + double d_scaley_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_scaley_per_y", 0, -100, 1000); + int alternate_scalex = prefs_get_int_attribute (prefs_path, "alternate_scalex", 0); + int alternate_scaley = prefs_get_int_attribute (prefs_path, "alternate_scaley", 0); + double rand_scalex = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_scalex", 0, 0, 1000); + double rand_scaley = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_scaley", 0, 0, 1000); + + double d_rot_per_x = prefs_get_double_attribute_limited (prefs_path, "d_rot_per_x", 0, -180, 180); + double d_rot_per_y = prefs_get_double_attribute_limited (prefs_path, "d_rot_per_y", 0, -180, 180); + int alternate_rotx = prefs_get_int_attribute (prefs_path, "alternate_rotx", 0); + int alternate_roty = prefs_get_int_attribute (prefs_path, "alternate_roty", 0); + double rand_rot = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_rot", 0, 0, 100); + + double d_opacity_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_opacity_per_y", 0, 0, 100); + double d_opacity_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_opacity_per_x", 0, 0, 100); + int alternate_opacityy = prefs_get_int_attribute (prefs_path, "alternate_opacityy", 0); + int alternate_opacityx = prefs_get_int_attribute (prefs_path, "alternate_opacityx", 0); + double rand_opacity = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_opacity", 0, 0, 100); + + const gchar *initial_color = prefs_get_string_attribute (prefs_path, "initial_color"); + double d_hue_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_hue_per_y", 0, -100, 100); + double d_hue_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_hue_per_x", 0, -100, 100); + double rand_hue = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_hue", 0, 0, 100); + double d_saturation_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_saturation_per_y", 0, -100, 100); + double d_saturation_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_saturation_per_x", 0, -100, 100); + double rand_saturation = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_saturation", 0, 0, 100); + double d_lightness_per_y = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_lightness_per_y", 0, -100, 100); + double d_lightness_per_x = 0.01 * prefs_get_double_attribute_limited (prefs_path, "d_lightness_per_x", 0, -100, 100); + double rand_lightness = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_lightness", 0, 0, 100); + int alternate_color_y = prefs_get_int_attribute (prefs_path, "alternate_color_y", 0); + int alternate_color_x = prefs_get_int_attribute (prefs_path, "alternate_color_x", 0); + + int type = prefs_get_int_attribute (prefs_path, "symmetrygroup", 0); + + int keepbbox = prefs_get_int_attribute (prefs_path, "keepbbox", 1); + + int xmax = prefs_get_int_attribute (prefs_path, "xmax", 2); + int ymax = prefs_get_int_attribute (prefs_path, "ymax", 2); + + int fillrect = prefs_get_int_attribute (prefs_path, "fillrect", 0); + double fillwidth = prefs_get_double_attribute_limited (prefs_path, "fillwidth", 50, 0, 6000); + double fillheight = prefs_get_double_attribute_limited (prefs_path, "fillheight", 50, 0, 6000); + + int dotrace = prefs_get_int_attribute (prefs_path, "dotrace", 0); + int pick = prefs_get_int_attribute (prefs_path, "pick", 0); + int pick_to_presence = prefs_get_int_attribute (prefs_path, "pick_to_presence", 0); + int pick_to_size = prefs_get_int_attribute (prefs_path, "pick_to_size", 0); + int pick_to_color = prefs_get_int_attribute (prefs_path, "pick_to_color", 0); + int pick_to_opacity = prefs_get_int_attribute (prefs_path, "pick_to_opacity", 0); + double rand_picked = 0.01 * prefs_get_double_attribute_limited (prefs_path, "rand_picked", 0, 0, 100); + int invert_picked = prefs_get_int_attribute (prefs_path, "invert_picked", 0); + double gamma_picked = prefs_get_double_attribute_limited (prefs_path, "gamma_picked", 0, -10, 10); + + if (dotrace) { + clonetiler_trace_setup (SP_DT_DOCUMENT(desktop), 1.0, SP_ITEM (obj)); + } + + NR::Point c; + double w; + double h; + + if (keepbbox && + obj_repr->attribute("inkscape:tile-w") && + obj_repr->attribute("inkscape:tile-h") && + obj_repr->attribute("inkscape:tile-cx") && + obj_repr->attribute("inkscape:tile-cy")) { + + double cx = sp_repr_get_double_attribute (obj_repr, "inkscape:tile-cx", 0); + double cy = sp_repr_get_double_attribute (obj_repr, "inkscape:tile-cy", 0); + + c = NR::Point (cx, cy); + + w = sp_repr_get_double_attribute (obj_repr, "inkscape:tile-w", 0); + h = sp_repr_get_double_attribute (obj_repr, "inkscape:tile-h", 0); + } else { + NR::Rect const r = SP_ITEM(obj)->invokeBbox(sp_item_i2doc_affine(SP_ITEM(obj))); + c = r.midpoint(); + w = r.dimensions()[NR::X]; + h = r.dimensions()[NR::Y]; + + sp_repr_set_svg_double(obj_repr, "inkscape:tile-w", w); + sp_repr_set_svg_double(obj_repr, "inkscape:tile-h", h); + sp_repr_set_svg_double(obj_repr, "inkscape:tile-cx", c[NR::X]); + sp_repr_set_svg_double(obj_repr, "inkscape:tile-cy", c[NR::Y]); + } + + NR::Point cur = NR::Point (0, 0); + NR::Rect bbox_original = NR::Rect (NR::Point (c[NR::X] - w/2, c[NR::Y] - h/2), NR::Point (c[NR::X] + w/2, c[NR::Y] + h/2)); + + for (int x = 0; + fillrect? + (fabs(cur[NR::X]) < fillwidth && x < 200) // prevent "freezing" with too large fillrect, arbitrarily limit rows + : (x < xmax); + x ++) { + for (int y = 0; + fillrect? + (fabs(cur[NR::Y]) < fillheight && y < 200) // prevent "freezing" with too large fillrect, arbitrarily limit cols + : (y < ymax); + y ++) { + + // Note: We create a clone at 0,0 too, right over the original, in case our clones are colored + + // Get transform from symmetry, shift, scale, rotation + NR::Matrix t = clonetiler_get_transform (type, x, y, c[NR::X], c[NR::Y], w, h, + d_x_per_x, d_y_per_x, d_x_per_y, d_y_per_y, alternate_x, alternate_y, rand_x, rand_y, + d_per_x_exp, d_per_y_exp, + d_rot_per_x, d_rot_per_y, alternate_rotx, alternate_roty, rand_rot, + d_scalex_per_x, d_scaley_per_x, d_scalex_per_y, d_scaley_per_y, + alternate_scalex, alternate_scaley, rand_scalex, rand_scaley); + + cur = c * t - c; + if (fillrect) { + if ((cur[NR::X] > fillwidth) || (cur[NR::Y] > fillheight)) { // off limits + continue; + } + } + + gchar color_string[32]; *color_string = 0; + + // Color tab + if (initial_color) { + guint32 rgba = sp_svg_read_color (initial_color, 0x000000ff); + float hsl[3]; + sp_color_rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_B_F(rgba)); + + double eff_x = (alternate_color_x? (x%2) : (x)); + double eff_y = (alternate_color_y? (y%2) : (y)); + + hsl[0] += d_hue_per_x * eff_x + d_hue_per_y * eff_y + rand_hue * g_random_double_range (-1, 1); + if (hsl[0] < 0) hsl[0] += 1; + if (hsl[0] > 1) hsl[0] -= 1; + hsl[1] += d_saturation_per_x * eff_x + d_saturation_per_y * eff_y + rand_saturation * g_random_double_range (-1, 1); + hsl[1] = CLAMP (hsl[1], 0, 1); + hsl[2] += d_lightness_per_x * eff_x + d_lightness_per_y * eff_y + rand_lightness * g_random_double_range (-1, 1); + hsl[2] = CLAMP (hsl[2], 0, 1); + + float rgb[3]; + sp_color_hsl_to_rgb_floatv (rgb, hsl[0], hsl[1], hsl[2]); + sp_svg_write_color(color_string, 32, SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1.0)); + } + + // Opacity tab + double opacity = 1.0; + int eff_x = (alternate_opacityx? (x%2) : (x)); + int eff_y = (alternate_opacityy? (y%2) : (y)); + opacity = 1 - (d_opacity_per_x * eff_x + d_opacity_per_y * eff_y + rand_opacity * g_random_double_range (-1, 1)); + opacity = CLAMP (opacity, 0, 1); + + // Trace tab + if (dotrace) { + NR::Rect bbox_t = transform_rect (bbox_original, t); + + guint32 rgba = clonetiler_trace_pick (bbox_t); + float r = SP_RGBA32_R_F(rgba); + float g = SP_RGBA32_G_F(rgba); + float b = SP_RGBA32_B_F(rgba); + float a = SP_RGBA32_A_F(rgba); + + float hsl[3]; + sp_color_rgb_to_hsl_floatv (hsl, r, g, b); + + gdouble val = 0; + switch (pick) { + case PICK_COLOR: + val = 1 - hsl[2]; // inverse lightness; to match other picks where black = max + break; + case PICK_OPACITY: + val = a; + break; + case PICK_R: + val = r; + break; + case PICK_G: + val = g; + break; + case PICK_B: + val = b; + break; + case PICK_H: + val = hsl[0]; + break; + case PICK_S: + val = hsl[1]; + break; + case PICK_L: + val = 1 - hsl[2]; + break; + default: + break; + } + + if (rand_picked > 0) { + val = randomize01 (val, rand_picked); + r = randomize01 (r, rand_picked); + g = randomize01 (g, rand_picked); + b = randomize01 (b, rand_picked); + } + + if (gamma_picked != 0) { + double power; + if (gamma_picked < 0) + power = 1/(1 + fabs(gamma_picked)); + else + power = 1 + gamma_picked; + + val = pow (val, power); + r = pow (r, power); + g = pow (g, power); + b = pow (b, power); + } + + if (invert_picked) { + val = 1 - val; + r = 1 - r; + g = 1 - g; + b = 1 - b; + } + + val = CLAMP (val, 0, 1); + r = CLAMP (r, 0, 1); + g = CLAMP (g, 0, 1); + b = CLAMP (b, 0, 1); + + // recompose tweaked color + rgba = SP_RGBA32_F_COMPOSE(r, g, b, a); + + if (pick_to_presence) { + if (g_random_double_range (0, 1) > val) { + continue; // skip! + } + } + if (pick_to_size) { + t = NR::translate(-c[NR::X], -c[NR::Y]) * NR::scale (val, val) * NR::translate(c[NR::X], c[NR::Y]) * t; + } + if (pick_to_opacity) { + opacity *= val; + } + if (pick_to_color) { + sp_svg_write_color(color_string, 32, rgba); + } + } + + if (opacity < 1e-6) { // invisibly transparent, skip + continue; + } + + if (fabs(t[0]) + fabs (t[1]) + fabs(t[2]) + fabs(t[3]) < 1e-6) { // too small, skip + continue; + } + + // Create the clone + Inkscape::XML::Node *clone = sp_repr_new("svg:use"); + clone->setAttribute("x", "0"); + clone->setAttribute("y", "0"); + clone->setAttribute("inkscape:tiled-clone-of", id_href); + clone->setAttribute("xlink:href", id_href); + + gchar affinestr[80]; + if (sp_svg_transform_write(affinestr, 79, t)) { + clone->setAttribute("transform", affinestr); + } else { + clone->setAttribute("transform", NULL); + } + + if (opacity < 1.0) { + sp_repr_set_css_double(clone, "opacity", opacity); + } + + if (*color_string) { + clone->setAttribute("fill", color_string); + clone->setAttribute("stroke", color_string); + } + + // add the new clone to the top of the original's parent + SP_OBJECT_REPR(parent)->appendChild(clone); + Inkscape::GC::release(clone); + } + cur[NR::Y] = 0; + } + + if (dotrace) { + clonetiler_trace_finish (); + } + + clonetiler_change_selection (NULL, selection, dlg); + + sp_document_done(SP_DT_DOCUMENT(desktop)); +} + +static GtkWidget * +clonetiler_new_tab (GtkWidget *nb, const gchar *label) +{ + GtkWidget *l = gtk_label_new_with_mnemonic (label); + GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN); + gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l); + return vb; +} + +static void +clonetiler_checkbox_toggled (GtkToggleButton *tb, gpointer *data) +{ + const gchar *attr = (const gchar *) data; + prefs_set_int_attribute (prefs_path, attr, gtk_toggle_button_get_active (tb)); +} + +static GtkWidget * +clonetiler_checkbox (GtkTooltips *tt, const char *tip, const char *attr) +{ + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + + GtkWidget *b = gtk_check_button_new (); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, tip, NULL); + + int value = prefs_get_int_attribute (prefs_path, attr, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(b), value); + + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, TRUE, 0); + gtk_signal_connect ( GTK_OBJECT (b), "clicked", + GTK_SIGNAL_FUNC (clonetiler_checkbox_toggled), (gpointer) attr); + + g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE)); + + return hb; +} + + +static void +clonetiler_value_changed (GtkAdjustment *adj, gpointer data) +{ + const gchar *pref = (const gchar *) data; + prefs_set_double_attribute (prefs_path, pref, adj->value); +} + +static GtkWidget * +clonetiler_spinbox (GtkTooltips *tt, const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent = false) +{ + GtkWidget *hb = gtk_hbox_new(FALSE, 0); + + { + GtkObject *a; + if (exponent) + a = gtk_adjustment_new(1.0, lower, upper, 0.01, 0.05, 0.1); + else + a = gtk_adjustment_new(0.0, lower, upper, 0.1, 0.5, 2); + + GtkWidget *sb; + if (exponent) + sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 0.01, 2); + else + sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 0.1, 1); + + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), sb, tip, NULL); + gtk_entry_set_width_chars (GTK_ENTRY (sb), 4); + gtk_box_pack_start (GTK_BOX (hb), sb, FALSE, FALSE, SB_MARGIN); + + double value = prefs_get_double_attribute_limited (prefs_path, attr, exponent? 1 : 0, lower, upper); + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), value); + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(clonetiler_value_changed), (gpointer) attr); + + if (exponent) + g_object_set_data (G_OBJECT(sb), "oneable", GINT_TO_POINTER(TRUE)); + else + g_object_set_data (G_OBJECT(sb), "zeroable", GINT_TO_POINTER(TRUE)); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), suffix); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + return hb; +} + +static void +clonetiler_symgroup_changed (GtkMenuItem *item, gpointer data) +{ + gint group_new = GPOINTER_TO_INT (data); + prefs_set_int_attribute ( prefs_path, "symmetrygroup", group_new ); +} + +static void +clonetiler_xy_changed (GtkAdjustment *adj, gpointer data) +{ + const gchar *pref = (const gchar *) data; + prefs_set_int_attribute (prefs_path, pref, (int) floor(adj->value + 0.5)); +} + +static void +clonetiler_keep_bbox_toggled (GtkToggleButton *tb, gpointer data) +{ + prefs_set_int_attribute (prefs_path, "keepbbox", gtk_toggle_button_get_active (tb)); +} + +static void +clonetiler_pick_to (GtkToggleButton *tb, gpointer data) +{ + const gchar *pref = (const gchar *) data; + prefs_set_int_attribute (prefs_path, pref, gtk_toggle_button_get_active (tb)); +} + + +static void +clonetiler_reset_recursive (GtkWidget *w) +{ + if (w && GTK_IS_OBJECT(w)) { + { + int r = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT(w), "zeroable")); + if (r && GTK_IS_SPIN_BUTTON(w)) { // spinbutton + GtkAdjustment *a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(w)); + gtk_adjustment_set_value (a, 0); + } + } + { + int r = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT(w), "oneable")); + if (r && GTK_IS_SPIN_BUTTON(w)) { // spinbutton + GtkAdjustment *a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(w)); + gtk_adjustment_set_value (a, 1); + } + } + { + int r = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT(w), "uncheckable")); + if (r && GTK_IS_TOGGLE_BUTTON(w)) { // checkbox + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), FALSE); + } + } + } + + if (GTK_IS_CONTAINER(w)) { + GList *ch = gtk_container_get_children (GTK_CONTAINER(w)); + for (GList *i = ch; i != NULL; i = i->next) { + clonetiler_reset_recursive (GTK_WIDGET(i->data)); + } + g_list_free (ch); + } +} + +static void +clonetiler_reset (GtkWidget *widget, void *) +{ + clonetiler_reset_recursive (dlg); +} + +static void +clonetiler_table_attach (GtkWidget *table, GtkWidget *widget, float align, int row, int col) +{ + GtkWidget *a = gtk_alignment_new (align, 0, 0, 0); + gtk_container_add(GTK_CONTAINER(a), widget); + gtk_table_attach ( GTK_TABLE (table), a, col, col + 1, row, row + 1, (GtkAttachOptions)4, (GtkAttachOptions)0, 0, 0 ); +} + +static GtkWidget * +clonetiler_table_x_y_rand (int values) +{ + GtkWidget *table = gtk_table_new (values + 2, 5, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 8); + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + + GtkWidget *i = sp_icon_new (GTK_ICON_SIZE_MENU, "clonetiler_per_row"); + gtk_box_pack_start (GTK_BOX (hb), i, FALSE, FALSE, 2); + + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Per row:")); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 2); + + clonetiler_table_attach (table, hb, 0, 1, 2); + } + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + + GtkWidget *i = sp_icon_new (GTK_ICON_SIZE_MENU, "clonetiler_per_column"); + gtk_box_pack_start (GTK_BOX (hb), i, FALSE, FALSE, 2); + + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Per column:")); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 2); + + clonetiler_table_attach (table, hb, 0, 1, 3); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Randomize:")); + clonetiler_table_attach (table, l, 0, 1, 4); + } + + return table; +} + +static void +clonetiler_pick_switched (GtkToggleButton *tb, gpointer data) +{ + guint v = GPOINTER_TO_INT (data); + prefs_set_int_attribute (prefs_path, "pick", v); +} + + +static void +clonetiler_switch_to_create (GtkToggleButton *tb, GtkWidget *dlg) +{ + GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols"); + GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight"); + + if (rowscols) { + gtk_widget_set_sensitive (rowscols, TRUE); + } + if (widthheight) { + gtk_widget_set_sensitive (widthheight, FALSE); + } + + prefs_set_int_attribute (prefs_path, "fillrect", 0); +} + + +static void +clonetiler_switch_to_fill (GtkToggleButton *tb, GtkWidget *dlg) +{ + GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols"); + GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight"); + + if (rowscols) { + gtk_widget_set_sensitive (rowscols, FALSE); + } + if (widthheight) { + gtk_widget_set_sensitive (widthheight, TRUE); + } + + prefs_set_int_attribute (prefs_path, "fillrect", 1); +} + + + + +static void +clonetiler_fill_width_changed (GtkAdjustment *adj, GtkWidget *u) +{ + gdouble const raw_dist = adj->value; + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const pixels = sp_units_get_pixels (raw_dist, unit); + + prefs_set_double_attribute (prefs_path, "fillwidth", pixels); +} + +static void +clonetiler_fill_height_changed (GtkAdjustment *adj, GtkWidget *u) +{ + gdouble const raw_dist = adj->value; + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const pixels = sp_units_get_pixels (raw_dist, unit); + + prefs_set_double_attribute (prefs_path, "fillheight", pixels); +} + + +static void +clonetiler_do_pick_toggled (GtkToggleButton *tb, gpointer data) +{ + GtkWidget *vvb = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "dotrace"); + + prefs_set_int_attribute (prefs_path, "dotrace", gtk_toggle_button_get_active (tb)); + + if (vvb) + gtk_widget_set_sensitive (vvb, gtk_toggle_button_get_active (tb)); +} + + + + +void +clonetiler_dialog (void) +{ + if (!dlg) + { + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_CLONETILER), title); + + dlg = sp_window_new (title, TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) { + gtk_window_resize ((GtkWindow *) dlg, w, h); + } + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (clonetiler_dialog_destroy), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (clonetiler_dialog_delete), dlg); + + if (Inkscape::NSApplication::Application::getNewGui()) + { + _shutdown_connection = Inkscape::NSApplication::Editor::connectShutdown (&on_delete); + _dialogs_hidden_connection = Inkscape::NSApplication::Editor::connectDialogsHidden (sigc::bind (&on_dialog_hide, dlg)); + _dialogs_unhidden_connection = Inkscape::NSApplication::Editor::connectDialogsUnhidden (sigc::bind (&on_dialog_unhide, dlg)); + _desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::bind (&on_transientize, &wd)); + } else { + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (clonetiler_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd); + } + + GtkTooltips *tt = gtk_tooltips_new(); + + GtkWidget *mainbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (mainbox), 6); + gtk_container_add (GTK_CONTAINER (dlg), mainbox); + + GtkWidget *nb = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (mainbox), nb, FALSE, FALSE, 0); + + +// Symmetry + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Symmetry")); + + GtkWidget *om = gtk_option_menu_new (); + /* TRANSLATORS: For the following 17 symmetry groups, see + * http://www.bib.ulb.ac.be/coursmath/doc/17.htm (visual examples); + * http://www.clarku.edu/~djoyce/wallpaper/seventeen.html (English vocabulary); or + * http://membres.lycos.fr/villemingerard/Geometri/Sym1D.htm (French vocabulary). + */ + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), om, _("Select one of the 17 symmetry groups for the tiling"), NULL); + gtk_box_pack_start (GTK_BOX (vb), om, FALSE, FALSE, SB_MARGIN); + + GtkWidget *m = gtk_menu_new (); + int current = prefs_get_int_attribute (prefs_path, "symmetrygroup", 0); + + struct SymGroups { + int group; + gchar const *label; + } const sym_groups[] = { + // TRANSLATORS: "translation" means "shift" / "displacement" here. + {TILE_P1, _("P1: simple translation")}, + {TILE_P2, _("P2: 180° rotation")}, + {TILE_PM, _("PM: reflection")}, + // TRANSLATORS: "glide reflection" is a reflection and a translation combined. + // For more info, see http://mathforum.org/sum95/suzanne/symsusan.html + {TILE_PG, _("PG: glide reflection")}, + {TILE_CM, _("CM: reflection + glide reflection")}, + {TILE_PMM, _("PMM: reflection + reflection")}, + {TILE_PMG, _("PMG: reflection + 180° rotation")}, + {TILE_PGG, _("PGG: glide reflection + 180° rotation")}, + {TILE_CMM, _("CMM: reflection + reflection + 180° rotation")}, + {TILE_P4, _("P4: 90° rotation")}, + {TILE_P4M, _("P4M: 90° rotation + 45° reflection")}, + {TILE_P4G, _("P4G: 90° rotation + 90° reflection")}, + {TILE_P3, _("P3: 120° rotation")}, + {TILE_P31M, _("P31M: reflection + 120° rotation, dense")}, + {TILE_P3M1, _("P3M1: reflection + 120° rotation, sparse")}, + {TILE_P6, _("P6: 60° rotation")}, + {TILE_P6M, _("P6M: reflection + 60° rotation")}, + }; + + for (unsigned j = 0; j < G_N_ELEMENTS(sym_groups); ++j) { + SymGroups const &sg = sym_groups[j]; + + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), sg.label); + gtk_misc_set_alignment (GTK_MISC(l), 0, 0.5); + + GtkWidget *item = gtk_menu_item_new (); + gtk_container_add (GTK_CONTAINER (item), l); + + gtk_signal_connect ( GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (clonetiler_symgroup_changed), + GINT_TO_POINTER (sg.group) ); + + gtk_menu_append (GTK_MENU (m), item); + } + + gtk_option_menu_set_menu (GTK_OPTION_MENU (om), m); + gtk_option_menu_set_history ( GTK_OPTION_MENU (om), current); + } + + table_row_labels = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + +// Shift + { + GtkWidget *vb = clonetiler_new_tab (nb, _("S_hift")); + + GtkWidget *table = clonetiler_table_x_y_rand (3); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // X + { + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "shift" means: the tiles will be shifted (offset) horizontally by this amount + // xgettext:no-c-format + gtk_label_set_markup (GTK_LABEL(l), _("Shift X:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Horizontal shift per row (in % of tile width)"), "d_x_per_y", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Horizontal shift per column (in % of tile width)"), "d_x_per_x", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the horizontal shift by this percentage"), "rand_x", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + // Y + { + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "shift" means: the tiles will be shifted (offset) vertically by this amount + // xgettext:no-c-format + gtk_label_set_markup (GTK_LABEL(l), _("Shift Y:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Vertical shift per row (in % of tile height)"), "d_y_per_y", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Vertical shift per column (in % of tile height)"), "d_y_per_x", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the vertical shift by this percentage"), "rand_y", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + // Exponent + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Exponent:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Whether rows are spaced evenly (1), converge (<1) or diverge (>1)"), "d_per_y_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Whether columns are spaced evenly (1), converge (<1) or diverge (>1)"), "d_per_x_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("Alternate:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of shifts for each row"), "alternate_y"); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of shifts for each column"), "alternate_x"); + clonetiler_table_attach (table, l, 0, 5, 3); + } + + } + + +// Scale + { + GtkWidget *vb = clonetiler_new_tab (nb, _("Sc_ale")); + + GtkWidget *table = clonetiler_table_x_y_rand (2); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // X + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Scale X:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Horizontal scale per row (in % of tile width)"), "d_scalex_per_y", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Horizontal scale per column (in % of tile width)"), "d_scalex_per_x", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the horizontal scale by this percentage"), "rand_scalex", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + // Y + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Scale Y:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Vertical scale per row (in % of tile height)"), "d_scaley_per_y", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Vertical scale per column (in % of tile height)"), "d_scaley_per_x", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the vertical scale by this percentage"), "rand_scaley", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("Alternate:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of scales for each row"), "alternate_scaley"); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of scales for each column"), "alternate_scalex"); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + } + + +// Rotation + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Rotation")); + + GtkWidget *table = clonetiler_table_x_y_rand (1); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // Angle + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Angle:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Rotate tiles by this angle for each row"), "d_rot_per_y", + -180, 180, "°"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + // xgettext:no-c-format + _("Rotate tiles by this angle for each column"), "d_rot_per_x", + -180, 180, "°"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the rotation angle by this percentage"), "rand_rot", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("Alternate:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the rotation direction for each row"), "alternate_roty"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the rotation direction for each column"), "alternate_rotx"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + } + + +// Opacity + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Opacity")); + + GtkWidget *table = clonetiler_table_x_y_rand (1); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // Dissolve + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Fade out:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Decrease tile opacity by this percentage for each row"), "d_opacity_per_y", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Decrease tile opacity by this percentage for each column"), "d_opacity_per_x", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the tile opacity by this percentage"), "rand_opacity", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("Alternate:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of opacity change for each row"), "alternate_opacityy"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of opacity change for each column"), "alternate_opacityx"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + } + + +// Color + { + GtkWidget *vb = clonetiler_new_tab (nb, _("Co_lor")); + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + + GtkWidget *l = gtk_label_new (_("Initial color: ")); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + + guint32 rgba = 0x000000ff | sp_svg_read_color (prefs_get_string_attribute(prefs_path, "initial_color"), 0x000000ff); + color_picker = new Inkscape::UI::Widget::ColorPicker (*new Glib::ustring(_("Initial color of tiled clones")), *new Glib::ustring(_("Initial color for clones (works only if the original has unset fill or stroke)")), rgba, false); + _color_changed_connection = color_picker->connectChanged (sigc::ptr_fun(on_picker_color_changed)); + + gtk_box_pack_start (GTK_BOX (hb), reinterpret_cast(color_picker->gobj()), FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + } + + + GtkWidget *table = clonetiler_table_x_y_rand (3); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // Hue + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("H:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the tile hue by this percentage for each row"), "d_hue_per_y", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the tile hue by this percentage for each column"), "d_hue_per_x", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the tile hue by this percentage"), "rand_hue", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + + // Saturation + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("S:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the color saturation by this percentage for each row"), "d_saturation_per_y", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the color saturation by this percentage for each column"), "d_saturation_per_x", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the color saturation by this percentage"), "rand_saturation", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + // Lightness + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("L:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the color lightness by this percentage for each row"), "d_lightness_per_y", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Change the color lightness by this percentage for each column"), "d_lightness_per_x", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the color lightness by this percentage"), "rand_lightness", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 4); + } + + + { // alternates + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Alternate:")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of color changes for each row"), "alternate_color_y"); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (tt, _("Alternate the sign of color changes for each column"), "alternate_color_x"); + clonetiler_table_attach (table, l, 0, 5, 3); + } + + } + +// Trace + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Trace")); + + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + GtkWidget *b = gtk_check_button_new_with_label (_("Trace the drawing under the tiles")); + g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE)); + gint old = prefs_get_int_attribute (prefs_path, "dotrace", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("For each clone, pick a value from the drawing in that clone's location and apply it to the clone"), NULL); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_do_pick_toggled), dlg); + } + + { + GtkWidget *vvb = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vb), vvb, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT(dlg), "dotrace", (gpointer) vvb); + + + { + GtkWidget *frame = gtk_frame_new (_("1. Pick from the drawing:")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); + + GtkWidget *table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + + GtkWidget* radio; + { + radio = gtk_radio_button_new_with_label (NULL, _("Color")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the visible color and opacity"), NULL); + clonetiler_table_attach (table, radio, 0.0, 1, 1); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_COLOR)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_COLOR); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), _("Opacity")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the total accumulated opacity"), NULL); + clonetiler_table_attach (table, radio, 0.0, 2, 1); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_OPACITY)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_OPACITY); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), _("R")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the Red component of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 1, 2); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_R)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_R); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), _("G")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the Green component of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 2, 2); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_G)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_G); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), _("B")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the Blue component of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 3, 2); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_B)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_B); + } + { + //TRANSLATORS: only translate "string" in "context|string". + // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), Q_("clonetiler|H")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the hue of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 1, 3); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_H)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_H); + } + { + //TRANSLATORS: only translate "string" in "context|string". + // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), Q_("clonetiler|S")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the saturation of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 2, 3); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_S)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_S); + } + { + //TRANSLATORS: only translate "string" in "context|string". + // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), Q_("clonetiler|L")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Pick the lightness of the color"), NULL); + clonetiler_table_attach (table, radio, 0.0, 3, 3); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", + GTK_SIGNAL_FUNC (clonetiler_pick_switched), GINT_TO_POINTER(PICK_L)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs_get_int_attribute(prefs_path, "pick", 0) == PICK_L); + } + + } + + { + GtkWidget *frame = gtk_frame_new (_("2. Tweak the picked value:")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, VB_MARGIN); + + GtkWidget *table = gtk_table_new (4, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Gamma-correct:")); + clonetiler_table_attach (table, l, 1.0, 1, 1); + } + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Shift the mid-range of the picked value upwards (>0) or downwards (<0)"), "gamma_picked", + -10, 10, ""); + clonetiler_table_attach (table, l, 0.0, 1, 2); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Randomize:")); + clonetiler_table_attach (table, l, 1.0, 1, 3); + } + { + GtkWidget *l = clonetiler_spinbox (tt, + _("Randomize the picked value by this percentage"), "rand_picked", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0.0, 1, 4); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Invert:")); + clonetiler_table_attach (table, l, 1.0, 2, 1); + } + { + GtkWidget *l = clonetiler_checkbox (tt, _("Invert the picked value"), "invert_picked"); + clonetiler_table_attach (table, l, 0.0, 2, 2); + } + } + + { + GtkWidget *frame = gtk_frame_new (_("3. Apply the value to the clones':")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); + + + GtkWidget *table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Presence")); + gint old = prefs_get_int_attribute (prefs_path, "pick_to_presence", 1); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Each clone is created with the probability determined by the picked value in that point"), NULL); + clonetiler_table_attach (table, b, 0.0, 1, 1); + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_pick_to), (gpointer) "pick_to_presence"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Size")); + gint old = prefs_get_int_attribute (prefs_path, "pick_to_size", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Each clone's size is determined by the picked value in that point"), NULL); + clonetiler_table_attach (table, b, 0.0, 2, 1); + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_pick_to), (gpointer) "pick_to_size"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Color")); + gint old = prefs_get_int_attribute (prefs_path, "pick_to_color", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Each clone is painted by the picked color (the original must have unset fill or stroke)"), NULL); + clonetiler_table_attach (table, b, 0.0, 1, 2); + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_pick_to), (gpointer) "pick_to_color"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Opacity")); + gint old = prefs_get_int_attribute (prefs_path, "pick_to_opacity", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Each clone's opacity is determined by the picked value in that point"), NULL); + clonetiler_table_attach (table, b, 0.0, 2, 2); + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_pick_to), (gpointer) "pick_to_opacity"); + } + } + gtk_widget_set_sensitive (vvb, prefs_get_int_attribute (prefs_path, "dotrace", 0)); + } + } + +// Rows/columns, width/height + { + GtkWidget *table = gtk_table_new (2, 2, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (mainbox), table, FALSE, FALSE, 0); + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + g_object_set_data (G_OBJECT(dlg), "rowscols", (gpointer) hb); + + { + GtkObject *a = gtk_adjustment_new(0.0, 1, 500, 1, 10, 10); + int value = prefs_get_int_attribute (prefs_path, "ymax", 2); + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), value); + GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), sb, _("How many rows in the tiling"), NULL); + gtk_entry_set_width_chars (GTK_ENTRY (sb), 5); + gtk_box_pack_start (GTK_BOX (hb), sb, TRUE, TRUE, 0); + + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(clonetiler_xy_changed), (gpointer) "ymax"); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), "×"); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } + + { + GtkObject *a = gtk_adjustment_new(0.0, 1, 500, 1, 10, 10); + int value = prefs_get_int_attribute (prefs_path, "xmax", 2); + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), value); + GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), sb, _("How many columns in the tiling"), NULL); + gtk_entry_set_width_chars (GTK_ENTRY (sb), 5); + gtk_box_pack_start (GTK_BOX (hb), sb, TRUE, TRUE, 0); + + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(clonetiler_xy_changed), (gpointer) "xmax"); + } + + clonetiler_table_attach (table, hb, 0.0, 1, 2); + } + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + g_object_set_data (G_OBJECT(dlg), "widthheight", (gpointer) hb); + + // unitmenu + GtkWidget *u = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); + sp_unit_selector_set_unit (SP_UNIT_SELECTOR(u), SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP)->doc_units); + + { + // Width spinbutton + GtkObject *a = gtk_adjustment_new (0.0, -SP_DESKTOP_SCROLL_LIMIT, SP_DESKTOP_SCROLL_LIMIT, 1.0, 10.0, 10.0); + sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a)); + + double value = prefs_get_double_attribute (prefs_path, "fillwidth", 50); + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const units = sp_pixels_get_units (value, unit); + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), units); + + GtkWidget *e = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0 , 2); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), e, _("Width of the rectangle to be filled"), NULL); + gtk_entry_set_width_chars (GTK_ENTRY (e), 5); + gtk_box_pack_start (GTK_BOX (hb), e, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(clonetiler_fill_width_changed), u); + } + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), "×"); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } + + { + // Height spinbutton + GtkObject *a = gtk_adjustment_new (0.0, -SP_DESKTOP_SCROLL_LIMIT, SP_DESKTOP_SCROLL_LIMIT, 1.0, 10.0, 10.0); + sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a)); + + double value = prefs_get_double_attribute (prefs_path, "fillheight", 50); + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const units = sp_pixels_get_units (value, unit); + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), units); + + + GtkWidget *e = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0 , 2); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), e, _("Height of the rectangle to be filled"), NULL); + gtk_entry_set_width_chars (GTK_ENTRY (e), 5); + gtk_box_pack_start (GTK_BOX (hb), e, TRUE, TRUE, 0); + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(clonetiler_fill_height_changed), u); + } + + gtk_box_pack_start (GTK_BOX (hb), u, TRUE, TRUE, 0); + clonetiler_table_attach (table, hb, 0.0, 2, 2); + + } + + // Switch + GtkWidget* radio; + { + radio = gtk_radio_button_new_with_label (NULL, _("Rows, columns: ")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Create the specified number of rows and columns"), NULL); + clonetiler_table_attach (table, radio, 0.0, 1, 1); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", GTK_SIGNAL_FUNC (clonetiler_switch_to_create), (gpointer) dlg); + } + if (prefs_get_int_attribute(prefs_path, "fillrect", 0) == 0) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_group (GTK_RADIO_BUTTON (radio)), _("Width, height: ")); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), radio, _("Fill the specified width and height with the tiling"), NULL); + clonetiler_table_attach (table, radio, 0.0, 2, 1); + gtk_signal_connect (GTK_OBJECT (radio), "toggled", GTK_SIGNAL_FUNC (clonetiler_switch_to_fill), (gpointer) dlg); + } + if (prefs_get_int_attribute(prefs_path, "fillrect", 0) == 1) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); + } + } + + +// Use saved pos + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + + GtkWidget *b = gtk_check_button_new_with_label (_("Use saved size and position of the tile")); + gint keepbbox = prefs_get_int_attribute (prefs_path, "keepbbox", 1); + gtk_toggle_button_set_active ((GtkToggleButton *) b, keepbbox != 0); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Pretend that the size and position of the tile are the same as the last time you tiled it (if any), instead of using the current size"), NULL); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(clonetiler_keep_bbox_toggled), NULL); + } + +// Statusbar + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_end (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + GtkWidget *l = gtk_label_new(""); + g_object_set_data (G_OBJECT(dlg), "status", (gpointer) l); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + +// Buttons + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + + { + GtkWidget *b = gtk_button_new (); + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" _Create ")); + gtk_container_add (GTK_CONTAINER(b), l); + gtk_tooltips_set_tip (tt, b, _("Create and tile the clones of the selection"), NULL); + gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (clonetiler_apply), NULL); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + + { // buttons which are enabled only when there are tiled clones + GtkWidget *sb = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end (GTK_BOX (hb), sb, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT(dlg), "buttons_on_tiles", (gpointer) sb); + { + // TRANSLATORS: if a group of objects are "clumped" together, then they + // are unevenly spread in the given amount of space - as shown in the + // diagrams on the left in the following screenshot: + // http://www.inkscape.org/screenshots/gallery/inkscape-0.42-CVS-tiles-unclump.png + // So unclumping is the process of spreading a number of objects out more evenly. + GtkWidget *b = gtk_button_new_with_mnemonic (_(" _Unclump ")); + gtk_tooltips_set_tip (tt, b, _("Spread out clones to reduce clumping; can be applied repeatedly"), NULL); + gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (clonetiler_unclump), NULL); + gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_button_new_with_mnemonic (_(" Re_move ")); + gtk_tooltips_set_tip (tt, b, _("Remove existing tiled clones of the selected object (siblings only)"), NULL); + gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (clonetiler_remove), NULL); + gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); + } + + // connect to global selection changed signal (so we can change desktops) and + // external_change (so we're not fooled by undo) + g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (clonetiler_change_selection), dlg); + g_signal_connect (G_OBJECT (INKSCAPE), "external_change", G_CALLBACK (clonetiler_external_change), dlg); + g_signal_connect(G_OBJECT(dlg), "destroy", G_CALLBACK(clonetiler_disconnect_gsignal), G_OBJECT (INKSCAPE)); + + // update now + clonetiler_change_selection (NULL, SP_DT_SELECTION(SP_ACTIVE_DESKTOP), dlg); + } + + { + GtkWidget *b = gtk_button_new_with_mnemonic (_(" R_eset ")); + // TRANSLATORS: "change" is a noun here + gtk_tooltips_set_tip (tt, b, _("Reset all shifts, scales, rotates, opacity and color changes in the dialog to zero"), NULL); + gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (clonetiler_reset), NULL); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + } + + gtk_widget_show_all (mainbox); + + } // end of if (!dlg) + + gtk_window_present ((GtkWindow *) dlg); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/clonetiler.h b/src/dialogs/clonetiler.h new file mode 100644 index 000000000..82206818c --- /dev/null +++ b/src/dialogs/clonetiler.h @@ -0,0 +1,31 @@ +#ifndef __SP_CLONE_TILER_H__ +#define __SP_CLONE_TILER_H__ + +/** + * \brief Clone tiling dialog + * + * Authors: + * bulia byak + * + * Copyright (C) 2004 Authors + * + */ + +#include + +#include + +void clonetiler_dialog ( void ); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/debugdialog.cpp b/src/dialogs/debugdialog.cpp new file mode 100644 index 000000000..e90f0a419 --- /dev/null +++ b/src/dialogs/debugdialog.cpp @@ -0,0 +1,348 @@ +/* + * A very simple dialog for displaying Inkscape messages. Messages + * sent to g_log(), g_warning(), g_message(), ets, are routed here, + * in order to avoid messing with the startup console. + * + * Authors: + * Bob Jamison + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 The Inkscape Organization + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "debugdialog.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + +//######################################################################### +//## I M P L E M E N T A T I O N +//######################################################################### + +/** + * A dialog that displays log messages + */ +class DebugDialogImpl : public DebugDialog, public Gtk::Dialog +{ + + public: + + + /** + * Constructor + */ + DebugDialogImpl(); + + /** + * Destructor + */ + ~DebugDialogImpl(); + + + /** + * Show the dialog + */ + void show(); + + /** + * Do not show the dialog + */ + void hide(); + + /** + * Clear all information from the dialog + */ + void clear(); + + /** + * Display a message + */ + void message(char const *msg); + + /** + * Redirect g_log() messages to this widget + */ + void captureLogMessages(); + + /** + * Return g_log() messages to normal handling + */ + void releaseLogMessages(); + + + + private: + + + Gtk::MenuBar menuBar; + + Gtk::Menu fileMenu; + + Gtk::ScrolledWindow textScroll; + + Gtk::TextView messageText; + + //Handler ID's + guint handlerDefault; + guint handlerGlibmm; + guint handlerAtkmm; + guint handlerPangomm; + guint handlerGdkmm; + guint handlerGtkmm; + +}; + + + + +//######################################################################### +//## E V E N T S +//######################################################################### + +/** + * Also a public method. Remove all text from the dialog + */ +void DebugDialogImpl::clear() +{ + Glib::RefPtr buffer = messageText.get_buffer(); + buffer->erase(buffer->begin(), buffer->end()); +} + + +//######################################################################### +//## C O N S T R U C T O R / D E S T R U C T O R +//######################################################################### +/** + * Constructor + */ +DebugDialogImpl::DebugDialogImpl() +{ + set_title(_("Messages")); + set_size_request(300, 400); + + Gtk::VBox *mainVBox = get_vbox(); + + //## Add a menu for clear() + menuBar.items().push_back( Gtk::Menu_Helpers::MenuElem(_("_File"), fileMenu) ); + fileMenu.items().push_back( Gtk::Menu_Helpers::MenuElem(_("_Clear"), + sigc::mem_fun(*this, &DebugDialogImpl::clear) ) ); + fileMenu.items().push_back( Gtk::Menu_Helpers::MenuElem(_("Capture log messages"), + sigc::mem_fun(*this, &DebugDialogImpl::captureLogMessages) ) ); + fileMenu.items().push_back( Gtk::Menu_Helpers::MenuElem(_("Release log messages"), + sigc::mem_fun(*this, &DebugDialogImpl::releaseLogMessages) ) ); + mainVBox->pack_start(menuBar, Gtk::PACK_SHRINK); + + + //### Set up the text widget + messageText.set_editable(false); + textScroll.add(messageText); + textScroll.set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS); + mainVBox->pack_start(textScroll); + + show_all_children(); + + message("ready."); + message("enable log display by setting "); + message("dialogs.debug 'redirect' attribute to 1 in preferences.xml"); + + handlerDefault = 0; + handlerGlibmm = 0; + handlerAtkmm = 0; + handlerPangomm = 0; + handlerGdkmm = 0; + handlerGtkmm = 0; +} + +/** + * Factory method. Use this to create a new DebugDialog + */ +DebugDialog *DebugDialog::create() +{ + DebugDialog *dialog = new DebugDialogImpl(); + return dialog; +} + + +/** + * Constructor + */ +DebugDialogImpl::~DebugDialogImpl() +{ + + +} + + +//######################################################################### +//## M E T H O D S +//######################################################################### + +void DebugDialogImpl::show() +{ + //call super() + Gtk::Dialog::show(); + //sp_transientize((GtkWidget *)gobj()); //Make transient + raise(); + Gtk::Dialog::present(); +} + + + +void DebugDialogImpl::hide() +{ + //call super() + Gtk::Dialog::hide(); +} + + + +void DebugDialogImpl::message(char const *msg) +{ + Glib::RefPtr buffer = messageText.get_buffer(); + Glib::ustring uMsg = msg; + if (uMsg[uMsg.length()-1] != '\n') + uMsg += '\n'; + buffer->insert (buffer->end(), uMsg); +} + + +/* static instance, to reduce dependencies */ +static DebugDialog *debugDialogInstance = NULL; + +DebugDialog *DebugDialog::getInstance() +{ + if ( !debugDialogInstance ) + { + debugDialogInstance = new DebugDialogImpl(); + } + return debugDialogInstance; +} + + + +void DebugDialog::showInstance() +{ + DebugDialog *debugDialog = getInstance(); + debugDialog->show(); +} + + + + +/*##### THIS IS THE IMPORTANT PART ##### */ +void dialogLoggingFunction(const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *messageText, + gpointer user_data) +{ + DebugDialogImpl *dlg = (DebugDialogImpl *)user_data; + + dlg->message(messageText); + +} + + +void DebugDialogImpl::captureLogMessages() +{ + /* + This might likely need more code, to capture Gtkmm + and Glibmm warnings, or maybe just simply grab stdout/stderr + */ + GLogLevelFlags flags = (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | + G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | + G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG); + if ( !handlerDefault ) + { + handlerDefault = g_log_set_handler(NULL, flags, + dialogLoggingFunction, (gpointer)this); + } + if ( !handlerGlibmm ) + { + handlerGlibmm = g_log_set_handler("glibmm", flags, + dialogLoggingFunction, (gpointer)this); + } + if ( !handlerAtkmm ) + { + handlerAtkmm = g_log_set_handler("atkmm", flags, + dialogLoggingFunction, (gpointer)this); + } + if ( !handlerPangomm ) + { + handlerPangomm = g_log_set_handler("pangomm", flags, + dialogLoggingFunction, (gpointer)this); + } + if ( !handlerGdkmm ) + { + handlerGdkmm = g_log_set_handler("gdkmm", flags, + dialogLoggingFunction, (gpointer)this); + } + if ( !handlerGtkmm ) + { + handlerGtkmm = g_log_set_handler("gtkmm", flags, + dialogLoggingFunction, (gpointer)this); + } + message("log capture started"); +} + +void DebugDialogImpl::releaseLogMessages() +{ + if ( handlerDefault ) + { + g_log_remove_handler(NULL, handlerDefault); + handlerDefault = 0; + } + if ( handlerGlibmm ) + { + g_log_remove_handler("glibmm", handlerGlibmm); + handlerGlibmm = 0; + } + if ( handlerAtkmm ) + { + g_log_remove_handler("atkmm", handlerAtkmm); + handlerAtkmm = 0; + } + if ( handlerPangomm ) + { + g_log_remove_handler("pangomm", handlerPangomm); + handlerPangomm = 0; + } + if ( handlerGdkmm ) + { + g_log_remove_handler("gdkmm", handlerGdkmm); + handlerGdkmm = 0; + } + if ( handlerGtkmm ) + { + g_log_remove_handler("gtkmm", handlerGtkmm); + handlerGtkmm = 0; + } + message("log capture discontinued"); +} + + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + +//######################################################################### +//## E N D O F F I L E +//######################################################################### + + + diff --git a/src/dialogs/debugdialog.h b/src/dialogs/debugdialog.h new file mode 100644 index 000000000..1a4a036fd --- /dev/null +++ b/src/dialogs/debugdialog.h @@ -0,0 +1,104 @@ +#ifndef __DEBUGDIALOG_H__ +#define __DEBUGDIALOG_H__ +/* + * A very simple dialog for displaying Inkscape messages. Messages + * sent to g_log(), g_warning(), g_message(), ets, are routed here, + * in order to avoid messing with the startup console. + * + * Authors: + * Bob Jamison + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 The Inkscape Organization + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + +/** + * A dialog that displays log messages + */ +class DebugDialog +{ + + public: + + + /** + * Constructor + */ + DebugDialog() {}; + + + /** + * Factory method + */ + static DebugDialog *create(); + + /** + * Destructor + */ + virtual ~DebugDialog() {}; + + + /** + * Show the dialog + */ + virtual void show() = 0; + + /** + * Do not show the dialog + */ + virtual void hide() = 0; + + /** + * Clear all information from the dialog + */ + virtual void clear() = 0; + + /** + * Display a message + */ + virtual void message(char const *msg) = 0; + + /** + * Redirect g_log() messages to this widget + */ + virtual void captureLogMessages() = 0; + + /** + * Return g_log() messages to normal handling + */ + virtual void releaseLogMessages() = 0; + + /** + * Get a shared singleton instance + */ + static DebugDialog *getInstance(); + + /** + * Show the instance above + */ + static void showInstance(); + + + + +}; + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + + +#endif /* __DEBUGDIALOG_H__ */ + diff --git a/src/dialogs/dialog-events.cpp b/src/dialogs/dialog-events.cpp new file mode 100644 index 000000000..4e6a93729 --- /dev/null +++ b/src/dialogs/dialog-events.cpp @@ -0,0 +1,247 @@ +#define __DIALOG_EVENTS_C__ + +/** + * \brief Event handler for dialog windows + * + * Authors: + * bulia byak + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "macros.h" +#include +#include "desktop.h" +#include "inkscape-private.h" +#include "prefs-utils.h" +#include "event-context.h" + +#include "dialog-events.h" + + + +/** +* \brief This function is called to zero the transientize semaphore by a +* timeout. +*/ +gboolean +sp_allow_again (gpointer *wd) +{ + ((win_data *) wd)->stop = 0; + return FALSE; // so that it is only called once +} + + + +/** + * \brief Remove focus from window to whoever it is transient for... + * + */ +void +sp_dialog_defocus (GtkWindow *win) +{ + GtkWindow *w; + //find out the document window we're transient for + w = gtk_window_get_transient_for ((GtkWindow *) win); + //switch to it + + if (w) { + gtk_window_present (w); + } +} + + + +/** + * \brief Callback to defocus a widget's parent dialog. + * + */ +void +sp_dialog_defocus_callback (GtkWindow *win, gpointer data) +{ + sp_dialog_defocus ((GtkWindow *) + gtk_widget_get_toplevel ((GtkWidget *) data)); +} + + + +void +sp_dialog_defocus_on_enter (GtkWidget *w) +{ + g_signal_connect ( G_OBJECT (w), "activate", + G_CALLBACK (sp_dialog_defocus_callback), w ); +} + + + +gboolean +sp_dialog_event_handler (GtkWindow *win, GdkEvent *event, gpointer data) +{ + +// if the focus is inside the Text and Font textview, do nothing + GObject *dlg = (GObject *) data; + if (g_object_get_data (dlg, "eatkeys")) { + return FALSE; + } + + gboolean ret = FALSE; + + switch (event->type) { + + case GDK_KEY_PRESS: + + switch (get_group0_keyval (&event->key)) { + case GDK_Escape: + sp_dialog_defocus (win); + ret = TRUE; + break; + case GDK_F4: + case GDK_w: + case GDK_W: + // close dialog + if (MOD__CTRL_ONLY) { + + /* this code sends a delete_event to the dialog, + * instead of just destroying it, so that the + * dialog can do some housekeeping, such as remember + * its position. + */ + GdkEventAny event; + GtkWidget *widget = (GtkWidget *) win; + event.type = GDK_DELETE; + event.window = widget->window; + event.send_event = TRUE; + g_object_ref (G_OBJECT (event.window)); + gtk_main_do_event ((GdkEvent*)&event); + g_object_unref (G_OBJECT (event.window)); + + ret = TRUE; + } + break; + default: // pass keypress to the canvas + break; + } + default: + ; + } + + return ret; + +} + + + +/** + * \brief Make the argument dialog transient to the currently active document + window. + */ +void +sp_transientize (GtkWidget *dialog) +{ + if (prefs_get_int_attribute ( "options.dialogsskiptaskbar", "value", 0)) { + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE); + } + + gint transient_policy = prefs_get_int_attribute_limited ( "options.transientpolicy", "value", 1, 0, 2 ); + + if (transient_policy) { + + // if there's an active document window, attach dialog to it as a transient: + + if ( SP_ACTIVE_DESKTOP ) + { + SP_ACTIVE_DESKTOP->setWindowTransient (dialog, transient_policy); + } + } +} // end of sp_transientize() + +void on_transientize (SPDesktop *desktop, win_data *wd ) +{ + sp_transientize_callback (0, desktop, wd); +} + +void +sp_transientize_callback ( Inkscape::Application * /*inkscape*/, + SPDesktop *desktop, win_data *wd ) +{ + gint transient_policy = + prefs_get_int_attribute_limited ( "options.transientpolicy", "value", + 1, 0, 2); + + if (!transient_policy) + return; + + if (wd->stop) { + /* + * if retransientizing of this dialog is still forbidden after + * previous call warning turned off because it was confusingly fired + * when loading many files from command line + */ + // g_warning("Retranzientize aborted! You're switching windows too fast!"); + return; + } + + if (wd->win) + { + wd->stop = 1; // disallow other attempts to retranzientize this dialog + desktop->setWindowTransient (wd->win, transient_policy); + } + + // we're done, allow next retransientizing not sooner than after 6 msec + gtk_timeout_add (6, (GtkFunction) sp_allow_again, (gpointer) wd); +} + +void on_dialog_hide (GtkWidget *w) +{ + if (w) + gtk_widget_hide (w); +} + +void on_dialog_unhide (GtkWidget *w) +{ + if (w) + gtk_widget_show (w); +} + +gboolean +sp_dialog_hide (GtkObject *object, gpointer data) +{ + GtkWidget *dlg = (GtkWidget *) data; + + if (dlg) + gtk_widget_hide (dlg); + + return TRUE; +} + + + +gboolean +sp_dialog_unhide (GtkObject *object, gpointer data) +{ + GtkWidget *dlg = (GtkWidget *) data; + + if (dlg) + gtk_widget_show (dlg); + + return TRUE; +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/dialog-events.h b/src/dialogs/dialog-events.h new file mode 100644 index 000000000..7cc64c6a2 --- /dev/null +++ b/src/dialogs/dialog-events.h @@ -0,0 +1,66 @@ +#ifndef __DIALOG_EVENTS_H__ +#define __DIALOG_EVENTS_H__ + +/** + * \brief Event handler for dialog windows + * + * Authors: + * bulia byak + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include + +/* + * event callback can only accept one argument, but we need two, + * hence this struct. + * each dialog has a local static copy: + * win is the dialog window + * stop is the transientize semaphore: when 0, retransientizing this dialog + * is allowed + */ + +typedef struct { + GtkWidget *win; + guint stop; +} win_data; + + +gboolean sp_dialog_event_handler ( GtkWindow *win, + GdkEvent *event, + gpointer data ); + +void sp_dialog_defocus ( GtkWindow *win ); +void sp_dialog_defocus_callback ( GtkWindow *win, gpointer data ); +void sp_dialog_defocus_on_enter ( GtkWidget *w ); +void sp_transientize ( GtkWidget *win ); + +void on_transientize ( SPDesktop *desktop, + win_data *wd ); + +void sp_transientize_callback ( Inkscape::Application *inkscape, + SPDesktop *desktop, + win_data *wd ); + +void on_dialog_hide (GtkWidget *w); +void on_dialog_unhide (GtkWidget *w); +gboolean sp_dialog_hide (GtkObject *object, gpointer data); +gboolean sp_dialog_unhide (GtkObject *object, gpointer data); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/display-settings.cpp b/src/dialogs/display-settings.cpp new file mode 100644 index 000000000..f083cca06 --- /dev/null +++ b/src/dialogs/display-settings.cpp @@ -0,0 +1,1575 @@ +#define __SP_DISPLAY_SETTINGS_C__ + +/* +* Inkscape Preferences dialog +* +* Authors: +* Lauris Kaplinski +* bulia byak +* +* Copyright (C) 2001 Ximian, Inc. +* Copyright (C) 2001-2004 Authors +* +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "helper/window.h" +#include "../inkscape.h" +#include "../prefs-utils.h" +#include "dialog-events.h" +#include "../macros.h" +#include "../prefs-utils.h" +#include "../verbs.h" +#include "../interface.h" +#include "../message-stack.h" +#include "../enums.h" +#include "../selcue.h" +#include "../selection.h" +#include "../selection-chemistry.h" +#include "../style.h" +#include "../desktop-handles.h" +#include "../unit-constants.h" +#include "xml/repr.h" +#include "ui/widget/style-swatch.h" + + + + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000; +static gchar *prefs_path = "dialogs.preferences"; + +extern gint nr_arena_image_x_sample; +extern gint nr_arena_image_y_sample; + +#define SB_WIDTH 90 +#define SB_LONG_ADJUSTMENT 20 +#define SB_MARGIN 1 +#define SUFFIX_WIDTH 70 +#define HB_MARGIN 4 +#define VB_MARGIN 4 +#define VB_SKIP 1 + +static void +sp_display_dialog_destroy (GtkObject *object, gpointer data) +{ + + sp_signal_disconnect_by_data (INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; + +} // edn of sp_display_dialog_destroy() + + + +static gboolean +sp_display_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data) +{ + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + + return FALSE; // which means, go ahead and destroy it + +} // end of sp_display_dialog_delete() + +static void +prefs_switch_page (GtkNotebook *notebook, + GtkNotebookPage *page, + guint page_num, + gchar *attr) +{ + prefs_set_int_attribute ("dialogs.preferences", attr, page_num); +} + +static gint +get_int_value_data(GtkToggleButton *button) +{ + return GPOINTER_TO_INT((gchar const*)gtk_object_get_data(GTK_OBJECT(button), "value")); +} + +static void +options_selector_show_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + gchar const *val = (gchar const*)gtk_object_get_data(GTK_OBJECT(button), "value"); + prefs_set_string_attribute ("tools.select", "show", val); + } +} + +static void +options_store_transform_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + guint const val = get_int_value_data(button); + prefs_set_int_attribute ("options.preservetransform", "value", val); + } +} + +static void +options_clone_compensation_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + guint const val = get_int_value_data(button); + prefs_set_int_attribute ("options.clonecompensation", "value", val); + } +} + +static void +options_clone_orphans_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + guint const val = get_int_value_data(button); + prefs_set_int_attribute ("options.cloneorphans", "value", val); + } +} + +static void +options_selcue_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + guint const val = get_int_value_data(button); + prefs_set_int_attribute ("options.selcue", "value", val); + } +} + +static void +options_scale_origin_toggled (GtkToggleButton *button) +{ + if (gtk_toggle_button_get_active (button)) { + gchar const *val = (gchar const *) gtk_object_get_data(GTK_OBJECT(button), "value"); + prefs_set_string_attribute ("tools.select", "scale_origin", val); + } +} + + +/** +* Small helper function to make options_selector a little less +* verbose. +* +* \param b Another radio button in the group, or NULL for the first. +* \param fb Box to add the button to. +* \param n Label for the button. +* \param tip Tooltip. +* \param v_string Key for the button's value, if it is a string. +* \param v_uint Key for the button's value, if it is a uint. +* \param isint Whether this is astring or uint. +* \param s Initial state of the button. +* \param h Toggled handler function. +*/ +static GtkWidget* sp_select_context_add_radio ( + GtkWidget *b, + GtkWidget *fb, + GtkTooltips *tt, + gchar const *n, + gchar const *tip, + char const *v_string, + guint v_uint, + bool isint, + gboolean s, + void (*h)(GtkToggleButton*) + ) +{ + GtkWidget* r = gtk_radio_button_new_with_label ( + b ? gtk_radio_button_group (GTK_RADIO_BUTTON (b)) : NULL, n + ); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), r, tip, NULL); + gtk_widget_show (r); + + if (isint) + gtk_object_set_data (GTK_OBJECT (r), "value", GUINT_TO_POINTER (v_uint)); + else + gtk_object_set_data (GTK_OBJECT (r), "value", (void*) v_string); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (r), s); + gtk_box_pack_start (GTK_BOX (fb), r, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT (r), "toggled", GTK_SIGNAL_FUNC (h), NULL); + + return r; +} + +static GtkWidget * +options_selector () +{ + GtkWidget *vb, *f, *fb, *b; + + GtkTooltips *tt = gtk_tooltips_new(); + + vb = gtk_vbox_new (FALSE, VB_MARGIN); + + f = gtk_frame_new (_("When transforming, show:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + fb = gtk_hbox_new (FALSE, 10); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gchar const *show = prefs_get_string_attribute ("tools.select", "show"); + + b = sp_select_context_add_radio ( + NULL, fb, tt, _("Objects"), + _("Show the actual objects when moving or transforming"), + "content", 0, false, + (show == NULL) || !strcmp (show, "content"), + options_selector_show_toggled + ); + + sp_select_context_add_radio( + b, fb, tt, _("Box outline"), + _("Show only a box outline of the objects when moving or transforming"), + "outline", 0, false, + show && !strcmp (show, "outline"), + options_selector_show_toggled + ); + + f = gtk_frame_new (_("Per-object selection cue:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + fb = gtk_hbox_new (FALSE, 10); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gint cue = prefs_get_int_attribute ("options.selcue", "value", Inkscape::SelCue::MARK); + + b = sp_select_context_add_radio ( + NULL, fb, tt, _("None"), + _("No per-object selection indication"), NULL, Inkscape::SelCue::NONE, true, + cue == Inkscape::SelCue::NONE, + options_selcue_toggled + ); + + b = sp_select_context_add_radio ( + b, fb, tt, _("Mark"), + _("Each selected object has a diamond mark in the top left corner"), + NULL, Inkscape::SelCue::MARK, true, + cue == Inkscape::SelCue::MARK, + options_selcue_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Box"), + _("Each selected object displays its bounding box"), NULL, Inkscape::SelCue::BBOX, true, + cue == Inkscape::SelCue::BBOX, + options_selcue_toggled + ); + + f = gtk_frame_new (_("Default scale origin:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + fb = gtk_hbox_new (FALSE, 10); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gchar const *scale_orig = prefs_get_string_attribute ("tools.select", "scale_origin"); + + b = sp_select_context_add_radio ( + NULL, fb, tt, _("Opposite bounding box edge"), + _("Default scale origin will be on the bounding box of the item"), "bbox", 0, false, + (scale_orig == NULL) || !strcmp (scale_orig, "bbox"), + options_scale_origin_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Farthest opposite node"), + _("Default scale origin will be on the bounding box of the item's points"), + "points", 0, false, + scale_orig && !strcmp (scale_orig, "points"), + options_scale_origin_toggled + ); + + return vb; +} + + +static void +sp_display_dialog_set_oversample (GtkMenuItem *item, gpointer data) +{ + gint os; + + os = GPOINTER_TO_INT (data); + + g_return_if_fail (os >= 0); + g_return_if_fail (os <= 4); + + nr_arena_image_x_sample = os; + nr_arena_image_y_sample = os; + + inkscape_refresh_display (INKSCAPE); + + prefs_set_int_attribute ( "options.bitmapoversample", "value", os ); + +} + + +static void +options_rotation_steps_changed (GtkMenuItem *item, gpointer data) +{ + gint snaps_new = GPOINTER_TO_INT (data); + prefs_set_int_attribute ( "options.rotationsnapsperpi", "value", snaps_new ); +} + +static void +options_dialogs_ontop_changed (GtkMenuItem *item, gpointer data) +{ + gint policy_new = GPOINTER_TO_INT (data); + prefs_set_int_attribute ( "options.transientpolicy", "value", policy_new ); +} + +void +options_rotation_steps (GtkWidget *vb, GtkTooltips *tt) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, HB_MARGIN); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + { + GtkWidget *l = gtk_label_new (_("degrees")); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5); + gtk_widget_set_size_request (l, SUFFIX_WIDTH, -1); + gtk_widget_show (l); + gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + { + GtkWidget *om = gtk_option_menu_new (); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), om, _("Rotating with Ctrl pressed snaps every that much degrees; also, pressing [ or ] rotates by this amount"), NULL); + gtk_widget_set_size_request (om, SB_WIDTH, -1); + gtk_widget_show (om); + gtk_box_pack_end (GTK_BOX (hb), om, FALSE, FALSE, SB_MARGIN); + + GtkWidget *m = gtk_menu_new (); + gtk_widget_show (m); + + int snaps_current = prefs_get_int_attribute ("options.rotationsnapsperpi", "value", 12); + int position_current = 0; + + struct RotSteps { + double degrees; + int snaps; + } const rot_snaps[] = { + {90, 2}, + {60, 3}, + {45, 4}, + {30, 6}, + {15, 12}, + {10, 18}, + {7.5, 24}, + {6, 30}, + {5, 36}, + {3, 60}, + {2, 90}, + {1, 180}, + {1, 0}, + }; + + for (unsigned j = 0; j < G_N_ELEMENTS(rot_snaps); ++j) { + RotSteps const &rs = rot_snaps[j]; + + gchar const *label = NULL; + if (rs.snaps == 0) { + // sorationsnapsperpi == 0 means no snapping + label = _("None"); + } else { + label = g_strdup_printf ("%.2g", rs.degrees); + } + + if (rs.snaps == snaps_current) + position_current = j; + + GtkWidget *item = gtk_menu_item_new_with_label (label); + gtk_signal_connect ( GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (options_rotation_steps_changed), + GINT_TO_POINTER (rs.snaps) ); + gtk_widget_show (item); + gtk_menu_append (GTK_MENU (m), item); + } + + gtk_option_menu_set_menu (GTK_OPTION_MENU (om), m); + gtk_option_menu_set_history ( GTK_OPTION_MENU (om), position_current); + } + + { + GtkWidget *l = gtk_label_new (_("Rotation snaps every:")); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } +} + +void +options_dialogs_ontop (GtkWidget *vb, GtkTooltips *tt) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, HB_MARGIN); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + { // empty label for alignment + GtkWidget *l = gtk_label_new (""); + gtk_widget_set_size_request (l, SUFFIX_WIDTH - SB_LONG_ADJUSTMENT, -1); + gtk_widget_show (l); + gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + { + GtkWidget *om = gtk_option_menu_new (); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), om, _("None: dialogs are treated as regular windows; Normal: dialogs stay on top of document windows; Aggressive: same as Normal but may work better with some window managers."), NULL); + gtk_widget_set_size_request (om, SB_WIDTH + SB_LONG_ADJUSTMENT, -1); + gtk_widget_show (om); + gtk_box_pack_end (GTK_BOX (hb), om, FALSE, FALSE, SB_MARGIN); + + GtkWidget *m = gtk_menu_new (); + gtk_widget_show (m); + + int current = prefs_get_int_attribute ("options.transientpolicy", "value", 1); + + { + const gchar *label = _("None"); + GtkWidget *item = gtk_menu_item_new_with_label (label); + gtk_signal_connect ( GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (options_dialogs_ontop_changed), + GINT_TO_POINTER (0) ); + gtk_widget_show (item); + gtk_menu_append (GTK_MENU (m), item); + } + + { + const gchar *label = _("Normal"); + GtkWidget *item = gtk_menu_item_new_with_label (label); + gtk_signal_connect ( GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (options_dialogs_ontop_changed), + GINT_TO_POINTER (1) ); + gtk_widget_show (item); + gtk_menu_append (GTK_MENU (m), item); + } + + { + const gchar *label = _("Aggressive"); + GtkWidget *item = gtk_menu_item_new_with_label (label); + gtk_signal_connect ( GTK_OBJECT (item), "activate", + GTK_SIGNAL_FUNC (options_dialogs_ontop_changed), + GINT_TO_POINTER (2) ); + gtk_widget_show (item); + gtk_menu_append (GTK_MENU (m), item); + } + + gtk_option_menu_set_menu (GTK_OPTION_MENU (om), m); + gtk_option_menu_set_history ( GTK_OPTION_MENU (om), current); + } + + { + GtkWidget *l = gtk_label_new (_("Dialogs on top:")); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } +} + + +static void +sp_display_dialog_cursor_tolerance_changed (GtkAdjustment *adj, gpointer data) +{ + prefs_set_double_attribute ( "options.cursortolerance", "value", + adj->value ); +} + +static void +options_freehand_tolerance_changed (GtkAdjustment *adj, gpointer data) +{ + prefs_set_double_attribute ("tools.freehand.pencil", "tolerance", adj->value); +} + +static void +options_changed_double (GtkAdjustment *adj, gpointer data) +{ + const gchar *prefs_path = (const gchar *) data; + prefs_set_double_attribute (prefs_path, "value", adj->value); +} + +static void +options_changed_int (GtkAdjustment *adj, gpointer data) +{ + const gchar *prefs_path = (const gchar *) data; + prefs_set_int_attribute (prefs_path, "value", (int) adj->value); +} + +static void +options_changed_percent (GtkAdjustment *adj, gpointer data) +{ + const gchar *prefs_path = (const gchar *) data; + prefs_set_double_attribute (prefs_path, "value", (adj->value)/100.0); +} + +static void +options_changed_boolean (GtkToggleButton *tb, gpointer data) +{ + const gchar *prefs_path = (const gchar *) data; + const gchar *prefs_attr = (const gchar *) g_object_get_data (G_OBJECT(tb), "attr"); + prefs_set_int_attribute (prefs_path, prefs_attr, gtk_toggle_button_get_active (tb)); +} + +void +options_sb ( + gchar const *label, + gchar const *tooltip, GtkTooltips *tt, + gchar const *suffix, + GtkWidget *box, + gdouble lower, gdouble upper, gdouble step_increment, gdouble page_increment, gdouble page_size, + gchar const *prefs_path, gchar const *attr, gdouble def, + bool isint, bool ispercent, + void (*changed)(GtkAdjustment *, gpointer) +) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, HB_MARGIN); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (box), hb, FALSE, FALSE, VB_SKIP); + + { + GtkWidget *l = gtk_label_new (suffix); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5); + gtk_widget_set_size_request (l, SUFFIX_WIDTH, -1); + gtk_widget_show (l); + gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + { + GtkObject *a = gtk_adjustment_new(0.0, lower, upper, step_increment, page_increment, page_size); + + gdouble value; + if (isint) + if (ispercent) + value = 100 * (gdouble) prefs_get_double_attribute_limited (prefs_path, attr, def, lower/100.0, upper/100.0); + else + value = (gdouble) prefs_get_int_attribute_limited (prefs_path, attr, (int) def, (int) lower, (int) upper); + else + value = prefs_get_double_attribute_limited (prefs_path, attr, def, lower, upper); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (a), value); + + GtkWidget *sb; + if (isint) { + sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, 0); + } else { + if (step_increment < 0.1) + sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 0.01, 3); + else + sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 0.01, 2); + } + + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), sb, tooltip, NULL); + gtk_entry_set_width_chars (GTK_ENTRY (sb), 6); + gtk_widget_set_size_request (sb, SB_WIDTH, -1); + gtk_widget_show (sb); + gtk_box_pack_end (GTK_BOX (hb), sb, FALSE, FALSE, SB_MARGIN); + + gtk_signal_connect(GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(changed), (gpointer) prefs_path); + } + + { + GtkWidget *l = gtk_label_new (label); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } + +} + + +void +options_checkbox ( + gchar const *label, + gchar const *tooltip, GtkTooltips *tt, + GtkWidget *box, + gchar const *prefs_path, gchar const *attr, gint def, + void (*changed)(GtkToggleButton *, gpointer) +) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, HB_MARGIN); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (box), hb, FALSE, FALSE, VB_SKIP); + + { // empty label for alignment + GtkWidget *l = gtk_label_new (""); + gtk_widget_set_size_request (l, SUFFIX_WIDTH, -1); + gtk_widget_show (l); + gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + { + + GtkWidget *b = gtk_check_button_new (); + + gint value = prefs_get_int_attribute (prefs_path, attr, def); + + gtk_toggle_button_set_active ((GtkToggleButton *) b, value != 0); + + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, tooltip, NULL); + + gtk_widget_set_size_request (b, SB_WIDTH, -1); + gtk_widget_show (b); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, SB_MARGIN); + + g_object_set_data (G_OBJECT(b), "attr", (void *) attr); + + gtk_signal_connect(GTK_OBJECT(b), "toggled", + GTK_SIGNAL_FUNC(changed), (gpointer) prefs_path); + } + + { + GtkWidget *l = gtk_label_new (label); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } +} + +void +selcue_checkbox (GtkWidget *vb, GtkTooltips *tt, const gchar *path) +{ + options_checkbox ( + _("Show selection cue"), + _("Whether selected objects display a selection cue (the same as in selector)"), tt, + vb, + path, "selcue", 1, + options_changed_boolean + ); +} + +void +gradientdrag_checkbox (GtkWidget *vb, GtkTooltips *tt, const gchar *path) +{ + options_checkbox ( + _("Enable gradient editing"), + _("Whether selected objects display gradient editing controls"), tt, + vb, + path, "gradientdrag", 1, + options_changed_boolean + ); +} + + +/** +* Helper function for new_objects_style +* +* \param b Another radio button in the group, or NULL for the first. +* \param fb Box to add the button to. +* \param n Label for the button. +* \param tip Tooltip. +* \param v_uint Key for the button's value +* \param s Initial state of the button. +* \param h Toggled handler function. +*/ +static GtkWidget* new_objects_style_add_radio ( + GtkWidget* b, + GtkWidget* fb, + GtkTooltips* tt, + const gchar* n, + const gchar* tip, + guint v_uint, + gboolean s, + void (*h)(GtkToggleButton*, gpointer), + gchar const *prefs_path, + gchar const *attr, + GtkWidget *button + ) +{ + GtkWidget* r = gtk_radio_button_new_with_label ( + b ? gtk_radio_button_group (GTK_RADIO_BUTTON (b)) : NULL, n + ); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), r, tip, NULL); + gtk_widget_show (r); + + gtk_object_set_data (GTK_OBJECT (r), "value", GUINT_TO_POINTER (v_uint)); + gtk_object_set_data (GTK_OBJECT (r), "attr", (gpointer) attr); + gtk_object_set_data (GTK_OBJECT (r), "button_to_activate", (gpointer) button); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (r), s); + gtk_signal_connect (GTK_OBJECT (r), "toggled", GTK_SIGNAL_FUNC (h), (gpointer) prefs_path); + + return r; +} + +static void +style_from_selection_to_tool(GtkWidget *widget, gchar const *prefs_path) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop == NULL) + return; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + if (selection->isEmpty()) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, + _("No objects selected to take the style from.")); + return; + } + SPItem *item = selection->singleItem(); + if (!item) { + /* TODO: If each item in the selection has the same style then don't consider it an error. + * Maybe we should try to handle multiple selections anyway, e.g. the intersection of the + * style attributes for the selected items. */ + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, + _("More than one object selected. Cannot take style from multiple objects.")); + return; + } + + SPCSSAttr *css = take_style_from_item (item); + + if (!css) return; + + // only store text style for the text tool + if (!g_strrstr ((const gchar *) prefs_path, "text")) { + css = sp_css_attr_unset_text (css); + } + + // we cannot store properties with uris - they will be invalid in other documents + css = sp_css_attr_unset_uris (css); + + sp_repr_css_change (inkscape_get_repr (INKSCAPE, prefs_path), css, "style"); + sp_repr_css_attr_unref (css); + + // update the swatch + Inkscape::UI::Widget::StyleSwatch *swatch = + (Inkscape::UI::Widget::StyleSwatch *) gtk_object_get_data (GTK_OBJECT (widget), "swatch"); + if (swatch) { + Inkscape::XML::Node *tool_repr = inkscape_get_repr(INKSCAPE, prefs_path); + if (tool_repr) { + SPCSSAttr *css = sp_repr_css_attr_inherited(tool_repr, "style"); + + swatch->setStyle (css); + + sp_repr_css_attr_unref(css); + } + } +} + +static void +options_changed_radio (GtkToggleButton *tb, gpointer data) +{ + const gchar *prefs_path = (const gchar *) data; + const gchar *prefs_attr = (const gchar *) g_object_get_data (G_OBJECT(tb), "attr"); + const guint val = GPOINTER_TO_INT((const gchar*)gtk_object_get_data(GTK_OBJECT(tb), "value")); + + if (prefs_path && prefs_attr && gtk_toggle_button_get_active (tb)) { + prefs_set_int_attribute (prefs_path, prefs_attr, val); + } + + GtkWidget *button = (GtkWidget *) g_object_get_data (G_OBJECT(tb), "button_to_activate"); + if (button) + gtk_widget_set_sensitive (button, !val); +} + +void +new_objects_style (GtkWidget *vb, GtkTooltips *tt, const gchar *path) +{ + GtkWidget *f = gtk_frame_new (_("Create new objects with:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + GtkWidget *fb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + guint usecurrent = prefs_get_int_attribute (path, "usecurrent", 0); + + GtkWidget *take = gtk_button_new_with_label (_("Take from selection")); + gtk_tooltips_set_tip (tt, take, _("Remember the style of the (first) selected object as this tool's style"), NULL); + gtk_widget_show (take); + + GtkWidget *b; + { + b = new_objects_style_add_radio ( + NULL, fb, tt, _("Last used style"), _("Apply the style you last set on an object"), + 1, + usecurrent != 0, + options_changed_radio, + path, "usecurrent", take + ); + + GtkWidget *hb = gtk_hbox_new(FALSE, HB_MARGIN); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + gtk_widget_show_all(hb); + + gtk_box_pack_start (GTK_BOX (fb), hb, FALSE, FALSE, 0); + } + + { + b = new_objects_style_add_radio ( + b, fb, tt, _("This tool's own style:"), _("Each tool may store its own style to apply to the newly created objects. Use the button below to set it."), + 0, + usecurrent == 0, + options_changed_radio, + path, "usecurrent", take + ); + + GtkWidget *hb = gtk_hbox_new(FALSE, HB_MARGIN); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + + // style swatch + Inkscape::XML::Node *tool_repr = inkscape_get_repr(INKSCAPE, path); + if (tool_repr) { + SPCSSAttr *css = sp_repr_css_attr_inherited(tool_repr, "style"); + Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(css); + gtk_box_pack_start (GTK_BOX (hb), (GtkWidget *) swatch->gobj(), FALSE, FALSE, 0); + sp_repr_css_attr_unref(css); + gtk_object_set_data (GTK_OBJECT (take), "swatch", (gpointer) swatch); + } + + // add "take from selection" button + gtk_widget_set_sensitive (take, (usecurrent == 0)); + gtk_box_pack_start (GTK_BOX (hb), take, FALSE, FALSE, 0); + gtk_signal_connect (GTK_OBJECT (take), "clicked", GTK_SIGNAL_FUNC (style_from_selection_to_tool), (void *) path); + + gtk_widget_show_all(hb); + + gtk_box_pack_start (GTK_BOX (fb), hb, FALSE, FALSE, 0); + } +} + + +static GtkWidget * +options_dropper () +{ + GtkTooltips *tt = gtk_tooltips_new(); + + GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); + + selcue_checkbox (vb, tt, "tools.dropper"); + gradientdrag_checkbox (vb, tt, "tools.dropper"); + + return vb; +} + +GtkWidget * +new_tab (GtkWidget *nb, const gchar *label) +{ + GtkWidget *l = gtk_label_new (label); + gtk_widget_show (l); + GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_widget_show (vb); + gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN); + gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l); + return vb; +} + +void +sp_display_dialog (void) +{ + + GtkWidget *nb, *l, *vb, *vbvb, *hb, *om, *m, *i, *frame; + + if (!dlg) + { + + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_DISPLAY), title); + + dlg = sp_window_new (title, TRUE); + gtk_window_set_resizable ((GtkWindow *) dlg, FALSE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", + G_CALLBACK (sp_transientize_callback), &wd); + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_display_dialog_destroy), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_display_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_display_dialog_delete), dlg); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); + + GtkTooltips *tt = gtk_tooltips_new(); + + nb = gtk_notebook_new (); + gtk_widget_show (nb); + gtk_container_add (GTK_CONTAINER (dlg), nb); + +// Mouse + vb = new_tab (nb, _("Mouse")); + + options_sb ( + /* TRANSLATORS: "Grab" is a noun here. "Grab sensitivity" is intended to mean how + * close on the screen you need to be to an object to be able to grab it with mouse (in + * pixels). */ + _("Grab sensitivity:"), + _("How close on the screen you need to be to an object to be able to grab it with mouse (in screen pixels)"), tt, + _("pixels"), + /* todo: allow real-world units. */ + vb, + 0.0, 30.0, 1.0, 1.0, 1.0, + "options.cursortolerance", "value", 8.0, + true, false, + sp_display_dialog_cursor_tolerance_changed + ); + + options_sb ( + _("Click/drag threshold:"), + _("Maximum mouse drag (in screen pixels) which is considered a click, not a drag"), tt, + _("pixels"), + vb, + 0.0, 20.0, 1.0, 1.0, 1.0, + "options.dragtolerance", "value", 4.0, + true, false, + options_changed_int + ); + + +// Scrolling + vb = new_tab (nb, _("Scrolling")); + + options_sb ( + _("Mouse wheel scrolls by:"), + _("One mouse wheel notch scrolls by this distance in screen pixels (horizontally with Shift)"), tt, + _("pixels"), + vb, + 0.0, 1000.0, 1.0, 1.0, 1.0, + "options.wheelscroll", "value", 40.0, + true, false, + options_changed_int + ); + + frame = gtk_frame_new (_("Ctrl+arrows")); + gtk_widget_show (frame); + gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0); + vbvb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_widget_show (vbvb); + gtk_container_add (GTK_CONTAINER (frame), vbvb); + + options_sb ( + _("Scroll by:"), + _("Pressing Ctrl+arrow key scrolls by this distance (in screen pixels)"), tt, + _("pixels"), + vbvb, + 0.0, 1000.0, 1.0, 1.0, 1.0, + "options.keyscroll", "value", 10.0, + true, false, + options_changed_int + ); + + options_sb ( + _("Acceleration:"), + _("Pressing and holding Ctrl+arrow will gradually speed up scrolling (0 for no acceleration)"), tt, + "", + vbvb, + 0.0, 5.0, 0.01, 1.0, 1.0, + "options.scrollingacceleration", "value", 0.35, + false, false, + options_changed_double + ); + + frame = gtk_frame_new (_("Autoscrolling")); + gtk_widget_show (frame); + gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0); + vbvb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_widget_show (vbvb); + gtk_container_add (GTK_CONTAINER (frame), vbvb); + + options_sb ( + _("Speed:"), + _("How fast the canvas autoscrolls when you drag beyond canvas edge (0 to turn autoscroll off)"), tt, + "", + vbvb, + 0.0, 5.0, 0.01, 1.0, 1.0, + "options.autoscrollspeed", "value", 0.7, + false, false, + options_changed_double + ); + + options_sb ( + _("Threshold:"), + _("How far (in screen pixels) you need to be from the canvas edge to trigger autoscroll; positive is outside the canvas, negative is within the canvas"), tt, + _("pixels"), + vbvb, + -600.0, 600.0, 1.0, 1.0, 1.0, + "options.autoscrolldistance", "value", -10.0, + true, false, + options_changed_int + ); + +// Steps + vb = new_tab (nb, _("Steps")); + + options_sb ( + _("Arrow keys move by:"), + _("Pressing an arrow key moves selected object(s) or node(s) by this distance (in px units)"), tt, + _("px"), + vb, + 0.0, 3000.0, 0.01, 1.0, 1.0, + "options.nudgedistance", "value", 2.0, + false, false, + options_changed_double + ); + + options_sb ( + _("> and < scale by:"), + _("Pressing > or < scales selection up or down by this increment (in px units)"), tt, + _("px"), + vb, + 0.0, 3000.0, 0.01, 1.0, 1.0, + "options.defaultscale", "value", 2.0, + false, false, + options_changed_double + ); + + options_sb ( + _("Inset/Outset by:"), + _("Inset and Outset commands displace the path by this distance (in px units)"), tt, + _("px"), + vb, + 0.0, 3000.0, 0.01, 1.0, 1.0, + "options.defaultoffsetwidth", "value", 2.0, + false, false, + options_changed_double + ); + + options_rotation_steps (vb, tt); + +options_checkbox ( + _("Compass-like display of angles"), + // TRANSLATORS: "positive clockwise" means "increasing in clockwise direction" + _("When on, angles are displayed with 0 at north, 0 to 360 range, positive clockwise; otherwise with 0 at east, -180 to 180 range, positive counterclockwise"), tt, + vb, + "options.compassangledisplay", "value", 1, + options_changed_boolean + ); + + options_sb ( + _("Zoom in/out by:"), + _("Zoom tool click, +/- keys, and middle click zoom in and out by this multiplier"), tt, + _("%"), + vb, + 101.0, 500.0, 1.0, 1.0, 1.0, + "options.zoomincrement", "value", 1.414213562, + true, true, + options_changed_percent + ); + +// Tools + vb = new_tab (nb, _("Tools")); + + GtkWidget *nb_tools = gtk_notebook_new (); + gtk_widget_show (nb_tools); + gtk_container_add (GTK_CONTAINER (vb), nb_tools); + + // Selector + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Selector")); + + GtkWidget *selector_page = options_selector (); + gtk_widget_show (selector_page); + gtk_container_add (GTK_CONTAINER (vb_tool), selector_page); + + gradientdrag_checkbox (vb_tool, tt, "tools.select"); + } + + // Node + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Node")); + + selcue_checkbox (vb_tool, tt, "tools.nodes"); + gradientdrag_checkbox (vb_tool, tt, "tools.nodes"); + } + + // Zoom + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Zoom")); + + selcue_checkbox (vb_tool, tt, "tools.zoom"); + gradientdrag_checkbox (vb_tool, tt, "tools.zoom"); + } + + { // The 4 shape tools + GtkWidget *vb_shapes = new_tab (nb_tools, _("Shapes")); + + GtkWidget *nb_shapes = gtk_notebook_new (); + gtk_widget_show (nb_shapes); + gtk_container_add (GTK_CONTAINER (vb_shapes), nb_shapes); + + // Rect + { + GtkWidget *vb_tool = new_tab(nb_shapes, _("Rectangle")); + new_objects_style (vb_tool, tt, "tools.shapes.rect"); + } + + // Ellipse + { + GtkWidget *vb_tool = new_tab(nb_shapes, _("Ellipse")); + new_objects_style (vb_tool, tt, "tools.shapes.arc"); + } + + // Star + { + GtkWidget *vb_tool = new_tab(nb_shapes, _("Star")); + new_objects_style (vb_tool, tt, "tools.shapes.star"); + } + + // Spiral + { + GtkWidget *vb_tool = new_tab(nb_shapes, _("Spiral")); + new_objects_style (vb_tool, tt, "tools.shapes.spiral"); + } + + // common for all shapes + selcue_checkbox (vb_shapes, tt, "tools.shapes"); + gradientdrag_checkbox (vb_shapes, tt, "tools.shapes"); + + g_signal_connect(GTK_OBJECT (nb_shapes), "switch-page", GTK_SIGNAL_FUNC (prefs_switch_page), (void *) "page_shapes"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (nb_shapes), prefs_get_int_attribute ("dialogs.preferences", "page_shapes", 0)); + } + + // Pencil + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Pencil")); + + options_sb ( + _("Tolerance:"), + _("This value affects the amount of smoothing applied to freehand lines; lower values produce more uneven paths with more nodes"), tt, + "", + vb_tool, + 0.0, 100.0, 0.5, 1.0, 1.0, + "tools.freehand.pencil", "tolerance", 10.0, + false, false, + options_freehand_tolerance_changed + ); + + new_objects_style (vb_tool, tt, "tools.freehand.pencil"); + + selcue_checkbox (vb_tool, tt, "tools.freehand.pencil"); + } + + // Pen + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Pen")); + + new_objects_style (vb_tool, tt, "tools.freehand.pen"); + + selcue_checkbox (vb_tool, tt, "tools.freehand.pen"); + } + + // Calligraphy + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Calligraphy")); + + new_objects_style (vb_tool, tt, "tools.calligraphic"); + } + + // Text + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Text")); + + new_objects_style (vb_tool, tt, "tools.text"); + + selcue_checkbox (vb_tool, tt, "tools.text"); + gradientdrag_checkbox (vb_tool, tt, "tools.text"); + } + + // Gradient + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Gradient")); + + selcue_checkbox (vb_tool, tt, "tools.gradient"); + } + + // Connector + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Connector")); + + selcue_checkbox (vb_tool, tt, "tools.connector"); + } + + // Dropper + { + GtkWidget *vb_tool = new_tab (nb_tools, _("Dropper")); + + GtkWidget *dropper_page = options_dropper (); + gtk_widget_show (dropper_page); + gtk_container_add (GTK_CONTAINER (vb_tool), dropper_page); + } + + g_signal_connect(GTK_OBJECT (nb_tools), "switch-page", GTK_SIGNAL_FUNC (prefs_switch_page), (void *) "page_tools"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (nb_tools), prefs_get_int_attribute ("dialogs.preferences", "page_tools", 0)); + + +// Windows + vb = new_tab (nb, _("Windows")); + + options_dialogs_ontop (vb, tt); + +options_checkbox ( + _("Save window geometry"), + _("Save the window size and position with each document (only for Inkscape SVG format)"), tt, + vb, + "options.savewindowgeometry", "value", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Dialogs are hidden in taskbar"), + _("Whether dialog windows are to be hidden in the window manager taskbar"), tt, + vb, + "options.dialogsskiptaskbar", "value", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Zoom when window is resized"), + _("Zoom drawing when document window is resized, to keep the same area visible (this is the default which can be changed in any window using the button above the right scrollbar)"), tt, + vb, + "options.stickyzoom", "value", 0, + options_changed_boolean + ); + + +// Clones + vb = new_tab (nb, _("Clones")); + + // Clone compensation + { + GtkWidget *f = gtk_frame_new (_("When the original moves, its clones and linked offsets:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + GtkWidget *fb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gint compense = prefs_get_int_attribute ("options.clonecompensation", "value", SP_CLONE_COMPENSATION_PARALLEL); + + GtkWidget *b = + sp_select_context_add_radio ( + NULL, fb, tt, _("Move in parallel"), + _("Clones are translated by the same vector as their original."), + NULL, SP_CLONE_COMPENSATION_PARALLEL, true, + compense == SP_CLONE_COMPENSATION_PARALLEL, + options_clone_compensation_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Stay unmoved"), + _("Clones preserve their positions when their original is moved."), + NULL, SP_CLONE_COMPENSATION_UNMOVED, true, + compense == SP_CLONE_COMPENSATION_UNMOVED, + options_clone_compensation_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Move according to transform"), + _("Each clone moves according to the value of its transform= attribute. For example, a rotated clone will move in a different direction than its original."), + NULL, SP_CLONE_COMPENSATION_NONE, true, + compense == SP_CLONE_COMPENSATION_NONE, + options_clone_compensation_toggled + ); + } + + // Original deletion + { + GtkWidget *f = gtk_frame_new (_("When the original is deleted, its clones:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + GtkWidget *fb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gint orphans = prefs_get_int_attribute ("options.cloneorphans", "value", SP_CLONE_ORPHANS_UNLINK); + + GtkWidget *b = + sp_select_context_add_radio ( + NULL, fb, tt, _("Are unlinked"), _("Orphaned clones are converted to regular objects."), NULL, SP_CLONE_ORPHANS_UNLINK, true, + orphans == SP_CLONE_ORPHANS_UNLINK, + options_clone_orphans_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Are deleted"), _("Orphaned clones are deleted along with their original."), NULL, SP_CLONE_ORPHANS_DELETE, true, + orphans == SP_CLONE_ORPHANS_DELETE, + options_clone_orphans_toggled + ); + +/* + sp_select_context_add_radio ( + b, fb, tt, _("Ask me"), _("Ask me what to do with the clones when their originals are deleted."), NULL, SP_CLONE_ORPHANS_ASKME, true, + orphans == SP_CLONE_ORPHANS_ASKME, + options_clone_orphans_toggled + ); +*/ + } + +// Transforms + /* TRANSLATORS: Noun, i.e. transformations. */ + vb = new_tab (nb, _("Transforms")); + +options_checkbox ( + _("Scale stroke width"), + _("When scaling objects, scale the stroke width by the same proportion"), tt, + vb, + "options.transform", "stroke", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Scale rounded corners in rectangles"), + _("When scaling rectangles, scale the radii of rounded corners"), tt, + vb, + "options.transform", "rectcorners", 0, + options_changed_boolean + ); + +options_checkbox ( + _("Transform gradients"), + _("Transform gradients (in fill or stroke) along with the objects"), tt, + vb, + "options.transform", "gradient", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Transform patterns"), + _("Transform patterns (in fill or stroke) along with the objects"), tt, + vb, + "options.transform", "pattern", 1, + options_changed_boolean + ); + + + // Store transformation (global) + { + /* TRANSLATORS: How to specify the affine transformation in the SVG file. */ + GtkWidget *f = gtk_frame_new (_("Store transformation:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + GtkWidget *fb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + + gint preserve = prefs_get_int_attribute ("options.preservetransform", "value", 0); + + GtkWidget *b = sp_select_context_add_radio ( + NULL, fb, tt, _("Optimized"), + _("If possible, apply transformation to objects without adding a transform= attribute"), + NULL, 0, true, + preserve == 0, + options_store_transform_toggled + ); + + sp_select_context_add_radio ( + b, fb, tt, _("Preserved"), + _("Always store transformation as a transform= attribute on objects"), + NULL, 1, true, + preserve != 0, + options_store_transform_toggled + ); + } + +// Selecting + vb = new_tab (nb, _("Selecting")); + + GtkWidget *f = gtk_frame_new (_("Ctrl+A, Tab, Shift+Tab:")); + gtk_widget_show (f); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + + GtkWidget *fb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (fb); + gtk_container_add (GTK_CONTAINER (f), fb); + +options_checkbox ( + _("Select only within current layer"), + _("Uncheck this to make keyboard selection commands work on objects in all layers"), tt, + fb, + "options.kbselection", "inlayer", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Ignore hidden objects"), + _("Uncheck this to be able to select objects that are hidden (either by themselves or by being in a hidden group or layer)"), tt, + fb, + "options.kbselection", "onlyvisible", 1, + options_changed_boolean + ); + +options_checkbox ( + _("Ignore locked objects"), + _("Uncheck this to be able to select objects that are locked (either by themselves or by being in a locked group or layer)"), tt, + fb, + "options.kbselection", "onlysensitive", 1, + options_changed_boolean + ); + + +// To be broken into: Display, Save, Export, SVG, Commands + vb = new_tab (nb, _("Misc")); + + options_sb ( + _("Default export resolution:"), + _("Default bitmap resolution (in dots per inch) in the Export dialog"), tt, // FIXME: add "Used for new exports; once exported, documents remember this value on per-object basis" when implemented + _("dpi"), + vb, + 0.0, 6000.0, 1.0, 1.0, 1.0, + "dialogs.export.defaultxdpi", "value", PX_PER_IN, + true, false, + options_changed_int + ); + + options_checkbox ( + /* TRANSLATORS: When on, an imported bitmap creates an element; otherwise it is a + * rectangle with bitmap fill. */ + _("Import bitmap as "), + _("When on, an imported bitmap creates an element; otherwise it is a rectangle with bitmap fill"), tt, + vb, + "options.importbitmapsasimages", "value", 1, + options_changed_boolean + ); + + options_checkbox ( + /* TRANSLATORS: When on, the print out (currently Postscript) will have + * a comment with the each object's label visible, marking the section + * of the printing commands that represent the given object. */ + _("Add label comments to printing output"), + _("When on, a comment will be added to the raw print output, marking the rendered output for an object with its label"), tt, + vb, + "printing.debug", "show-label-comments", 0, + options_changed_boolean + ); + + options_checkbox ( + /* TRANSLATORS: When on, enable the effects menu, default is off */ + _("Enable script effects (requires restart) - EXPERIMENTAL"), + _("When on, the effects menu is enabled, allowing external effect scripts to be called, requires restart before effective - EXPERIMENTAL"), tt, + vb, + "extensions", "show-effects-menu", 0, + options_changed_boolean + ); + + options_sb ( + /* TRANSLATORS: The maximum length of the Open Recent list in the File menu. */ + _("Max recent documents:"), + _("The maximum length of the Open Recent list in the File menu"), tt, + "", + vb, + 0.0, 1000.0, 1.0, 1.0, 1.0, + "options.maxrecentdocuments", "value", 20.0, + true, false, + options_changed_int + ); + + options_sb ( + _("Simplification threshold:"), + _("How strong is the Simplify command by default. If you invoke this command several times in quick succession, it will act more and more aggressively; invoking it again after a pause restores the default threshold."), tt, + "", + vb, + 0.0, 1.0, 0.001, 0.01, 0.01, + "options.simplifythreshold", "value", 0.002, + false, false, + options_changed_double + ); + + + /* Oversample */ + hb = gtk_hbox_new (FALSE, HB_MARGIN); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + // empty label for alignment + l = gtk_label_new (""); + gtk_widget_set_size_request (l, SUFFIX_WIDTH, -1); + gtk_widget_show (l); + gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 0); + + l = gtk_label_new (_("Oversample bitmaps:")); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + + om = gtk_option_menu_new (); + gtk_widget_set_size_request (om, SB_WIDTH, -1); + gtk_widget_show (om); + gtk_box_pack_end (GTK_BOX (hb), om, FALSE, FALSE, SB_MARGIN); + + m = gtk_menu_new (); + gtk_widget_show (m); + + i = gtk_menu_item_new_with_label (_("None")); + gtk_signal_connect ( GTK_OBJECT (i), "activate", + GTK_SIGNAL_FUNC (sp_display_dialog_set_oversample), + GINT_TO_POINTER (0) ); + gtk_widget_show (i); + gtk_menu_append (GTK_MENU (m), i); + i = gtk_menu_item_new_with_label (_("2x2")); + gtk_signal_connect ( GTK_OBJECT (i), "activate", + GTK_SIGNAL_FUNC (sp_display_dialog_set_oversample), + GINT_TO_POINTER (1) ); + gtk_widget_show (i); + gtk_menu_append (GTK_MENU (m), i); + i = gtk_menu_item_new_with_label (_("4x4")); + gtk_signal_connect ( GTK_OBJECT (i), "activate", + GTK_SIGNAL_FUNC (sp_display_dialog_set_oversample), + GINT_TO_POINTER (2) ); + gtk_widget_show (i); + gtk_menu_append (GTK_MENU (m), i); + i = gtk_menu_item_new_with_label (_("8x8")); + gtk_signal_connect ( GTK_OBJECT (i), "activate", + GTK_SIGNAL_FUNC (sp_display_dialog_set_oversample), + GINT_TO_POINTER (3)); + gtk_widget_show (i); + gtk_menu_append (GTK_MENU (m), i); + i = gtk_menu_item_new_with_label (_("16x16")); + gtk_signal_connect ( GTK_OBJECT (i), "activate", + GTK_SIGNAL_FUNC (sp_display_dialog_set_oversample), + GINT_TO_POINTER (4) ); + gtk_widget_show (i); + gtk_menu_append (GTK_MENU (m), i); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (om), m); + + gtk_option_menu_set_history ( GTK_OPTION_MENU (om), + nr_arena_image_x_sample); + + g_signal_connect(GTK_OBJECT (nb), "switch-page", GTK_SIGNAL_FUNC (prefs_switch_page), (void *) "page_top"); + gtk_notebook_set_current_page (GTK_NOTEBOOK (nb), prefs_get_int_attribute ("dialogs.preferences", "page_top", 0)); + + } // end of if (!dlg) + + gtk_window_present ((GtkWindow *) dlg); + +} // end of sp_display_dialog() + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/display-settings.h b/src/dialogs/display-settings.h new file mode 100644 index 000000000..2a28ea971 --- /dev/null +++ b/src/dialogs/display-settings.h @@ -0,0 +1,69 @@ +#ifndef __SP_DISPLAY_SETTINGS_H__ +#define __SP_DISPLAY_SETTINGS_H__ + +/** + * \brief Display settings dialog + * + * Author: + * Lauris Kaplinski + * + * Copyright (C) 2001 Ximian, Inc. + * + */ + +#include + +#include + +void sp_display_dialog ( void ); +void sp_display_dialog_apply ( GtkWidget * widget ); +void sp_display_dialog_close ( GtkWidget * widget ); + +// UPDATE THIS IF YOU'RE CHANGING OR REARRANGING PREFS PAGES. +// Otherwise the commands that open the dialog with a given page may become out of sync. + +enum { + PREFS_PAGE_MOUSE, + PREFS_PAGE_SCROLLING, + PREFS_PAGE_STEPS, + PREFS_PAGE_TOOLS, + PREFS_PAGE_WINDOWS, + PREFS_PAGE_CLONES, + PREFS_PAGE_TRANSFORMS, + PREFS_PAGE_SELECTING, + PREFS_PAGE_MISC +}; + +enum { + PREFS_PAGE_TOOLS_SELECTOR, + PREFS_PAGE_TOOLS_NODE, + PREFS_PAGE_TOOLS_ZOOM, + PREFS_PAGE_TOOLS_SHAPES, + PREFS_PAGE_TOOLS_PENCIL, + PREFS_PAGE_TOOLS_PEN, + PREFS_PAGE_TOOLS_CALLIGRAPHY, + PREFS_PAGE_TOOLS_TEXT, + PREFS_PAGE_TOOLS_GRADIENT, + PREFS_PAGE_TOOLS_CONNECTOR, + PREFS_PAGE_TOOLS_DROPPER +}; + +enum { + PREFS_PAGE_TOOLS_SHAPES_RECT, + PREFS_PAGE_TOOLS_SHAPES_ELLIPSE, + PREFS_PAGE_TOOLS_SHAPES_STAR, + PREFS_PAGE_TOOLS_SHAPES_SPIRAL +}; + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/eek-preview.cpp b/src/dialogs/eek-preview.cpp new file mode 100644 index 000000000..74cf0abf7 --- /dev/null +++ b/src/dialogs/eek-preview.cpp @@ -0,0 +1,522 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Eek Preview Stuffs. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "eek-preview.h" + +#define PRIME_BUTTON_MAGIC_NUMBER 1 + +#define FOCUS_PROP_ID 1 + + + +static void eek_preview_class_init( EekPreviewClass *klass ); +static void eek_preview_init( EekPreview *preview ); + +static GtkWidgetClass* parent_class = 0; + +void eek_preview_set_color( EekPreview* preview, int r, int g, int b ) +{ + preview->_r = r; + preview->_g = g; + preview->_b = b; +} + + +GType eek_preview_get_type(void) +{ + static GType preview_type = 0; + + if (!preview_type) { + static const GTypeInfo preview_info = { + sizeof( EekPreviewClass ), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)eek_preview_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof( EekPreview ), + 0, /* n_preallocs */ + (GInstanceInitFunc)eek_preview_init, + NULL /* value_table */ + }; + + + preview_type = g_type_register_static( GTK_TYPE_DRAWING_AREA, "EekPreview", &preview_info, (GTypeFlags)0 ); + } + + return preview_type; +} + +GtkWidget* eek_preview_area_new(void) +{ + return NULL; +} + +static void eek_preview_size_request( GtkWidget* widget, GtkRequisition* req ) +{ + gint width = 0; + gint height = 0; + EekPreview* preview = EEK_PREVIEW(widget); + gboolean worked = gtk_icon_size_lookup( preview->_size, &width, &height ); + if ( !worked ) { + width = 16; + height = 16; + } + if ( preview->_view == VIEW_TYPE_LIST ) { + width *= 3; + } + req->width = width; + req->height = height; +} + +enum { + CLICKED_SIGNAL, + ALTCLICKED_SIGNAL, + LAST_SIGNAL +}; + + +static guint eek_preview_signals[LAST_SIGNAL] = { 0 }; + + +gboolean eek_preview_expose_event( GtkWidget* widget, GdkEventExpose* event ) +{ +/* g_message("Exposed!!! %s", GTK_WIDGET_HAS_FOCUS(widget) ? "XXX" : "---" ); */ + gint insetX = 0; + gint insetY = 0; + +/* + gint lower = widget->allocation.width; + lower = (widget->allocation.height < lower) ? widget->allocation.height : lower; + if ( lower > 16 ) { + insetX++; + if ( lower > 18 ) { + insetX++; + if ( lower > 22 ) { + insetX++; + if ( lower > 24 ) { + insetX++; + if ( lower > 32 ) { + insetX++; + } + } + } + } + insetY = insetX; + } +*/ + + if ( GTK_WIDGET_DRAWABLE( widget ) ) { + GtkStyle* style = gtk_widget_get_style( widget ); + + if ( insetX > 0 || insetY > 0 ) { + gtk_paint_flat_box( style, + widget->window, + (GtkStateType)GTK_WIDGET_STATE(widget), + GTK_SHADOW_NONE, + NULL, + widget, + NULL, + 0, 0, + widget->allocation.width, widget->allocation.height); + } + + GdkGC *gc = gdk_gc_new( widget->window ); + EekPreview* preview = EEK_PREVIEW(widget); + GdkColor fg = {0, preview->_r, preview->_g, preview->_b}; + gdk_colormap_alloc_color( gdk_colormap_get_system(), &fg, FALSE, TRUE ); + + gdk_gc_set_foreground( gc, &fg ); + + gdk_draw_rectangle( widget->window, + gc, + TRUE, + insetX, insetY, + widget->allocation.width - (insetX * 2), widget->allocation.height - (insetY * 2) ); + + if ( GTK_WIDGET_HAS_FOCUS(widget) ) { + gtk_paint_focus( style, + widget->window, + GTK_STATE_NORMAL, + NULL, /* GdkRectangle *area, */ + widget, + NULL, + 0 + 1, 0 + 1, + widget->allocation.width - 2, widget->allocation.height - 2 ); + } + } + + + return FALSE; +} + + +static gboolean eek_preview_enter_cb( GtkWidget* widget, GdkEventCrossing* event ) +{ + if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) { + EekPreview* preview = EEK_PREVIEW(widget); + preview->_within = TRUE; + gtk_widget_set_state( widget, preview->_hot ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT ); + } + return FALSE; +} + +static gboolean eek_preview_leave_cb( GtkWidget* widget, GdkEventCrossing* event ) +{ + if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) { + EekPreview* preview = EEK_PREVIEW(widget); + preview->_within = FALSE; + gtk_widget_set_state( widget, GTK_STATE_NORMAL ); + } + return FALSE; +} + +/* +static gboolean eek_preview_focus_in_event( GtkWidget* widget, GdkEventFocus* event ) +{ + g_message("focus IN"); + gboolean blip = parent_class->focus_in_event ? parent_class->focus_in_event(widget, event) : FALSE; + return blip; +} + +static gboolean eek_preview_focus_out_event( GtkWidget* widget, GdkEventFocus* event ) +{ + g_message("focus OUT"); + gboolean blip = parent_class->focus_out_event ? parent_class->focus_out_event(widget, event) : FALSE; + return blip; +} +*/ + +static gboolean eek_preview_button_press_cb( GtkWidget* widget, GdkEventButton* event ) +{ + if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) { + EekPreview* preview = EEK_PREVIEW(widget); + + if ( preview->_takesFocus && !GTK_WIDGET_HAS_FOCUS(widget) ) { + gtk_widget_grab_focus(widget); + } + + if ( event->button == PRIME_BUTTON_MAGIC_NUMBER ) { + preview->_hot = TRUE; + if ( preview->_within ) { + gtk_widget_set_state( widget, GTK_STATE_ACTIVE ); + } + } + } + + return FALSE; +} + +static gboolean eek_preview_button_release_cb( GtkWidget* widget, GdkEventButton* event ) +{ + if ( gtk_get_event_widget( (GdkEvent*)event ) == widget ) { + EekPreview* preview = EEK_PREVIEW(widget); + preview->_hot = FALSE; + gtk_widget_set_state( widget, GTK_STATE_NORMAL ); + if ( preview->_within && event->button == PRIME_BUTTON_MAGIC_NUMBER ) { + gboolean isAlt = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK; + + if ( isAlt ) { + g_signal_emit( widget, eek_preview_signals[ALTCLICKED_SIGNAL], 0, 2 ); + } else { + g_signal_emit( widget, eek_preview_signals[CLICKED_SIGNAL], 0 ); + } + } + } + return FALSE; +} + +gboolean eek_preview_key_press_event( GtkWidget* widget, GdkEventKey* event) +{ + g_message("TICK"); + return FALSE; +} + +gboolean eek_preview_key_release_event( GtkWidget* widget, GdkEventKey* event) +{ + g_message("tock"); + return FALSE; +} + +static void eek_preview_get_property( GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class); + switch ( property_id ) { + case FOCUS_PROP_ID: + { + EekPreview* preview = EEK_PREVIEW( object ); + g_value_set_boolean( value, preview->_takesFocus ); + } + break; + default: + { + if ( gobjClass->get_property ) { + gobjClass->get_property( object, property_id, value, pspec ); + } + } + } +} + +static void eek_preview_set_property( GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GObjectClass* gobjClass = G_OBJECT_CLASS(parent_class); + switch ( property_id ) { + case FOCUS_PROP_ID: + { + EekPreview* preview = EEK_PREVIEW( object ); + gboolean val = g_value_get_boolean( value ); + if ( val != preview->_takesFocus ) { + preview->_takesFocus = val; + } + } + break; + default: + { + if ( gobjClass->set_property ) { + gobjClass->set_property( object, property_id, value, pspec ); + } + } + } +} + + +static gboolean eek_preview_popup_menu( GtkWidget* widget ) +{ +/* g_message("Do the popup!"); */ + gboolean blip = parent_class->popup_menu ? parent_class->popup_menu(widget) : FALSE; + return blip; +} + + +static void eek_preview_class_init( EekPreviewClass *klass ) +{ + GObjectClass* gobjClass = G_OBJECT_CLASS(klass); + /*GtkObjectClass* objectClass = (GtkObjectClass*)klass;*/ + GtkWidgetClass* widgetClass = (GtkWidgetClass*)klass; + + gobjClass->set_property = eek_preview_set_property; + gobjClass->get_property = eek_preview_get_property; + + /*objectClass->destroy = eek_preview_destroy;*/ + + parent_class = (GtkWidgetClass*)g_type_class_peek_parent( klass ); + + /*widgetClass->map = ;*/ + /*widgetClass->unmap = ;*/ + /*widgetClass->realize = ;*/ + /*widgetClass->unrealize = ;*/ + widgetClass->size_request = eek_preview_size_request; + /*widgetClass->size_allocate = ;*/ + /*widgetClass->state_changed = ;*/ + /*widgetClass->style_set = ;*/ + /*widgetClass->grab_notify = ;*/ + + widgetClass->button_press_event = eek_preview_button_press_cb; + widgetClass->button_release_event = eek_preview_button_release_cb; + /*widgetClass->delete_event = ;*/ + /*widgetClass->destroy_event = ;*/ + widgetClass->expose_event = eek_preview_expose_event; +/* widgetClass->key_press_event = eek_preview_key_press_event; */ +/* widgetClass->key_release_event = eek_preview_key_release_event; */ + widgetClass->enter_notify_event = eek_preview_enter_cb; + widgetClass->leave_notify_event = eek_preview_leave_cb; + /*widgetClass->configure_event = ;*/ + /*widgetClass->focus_in_event = eek_preview_focus_in_event;*/ + /*widgetClass->focus_out_event = eek_preview_focus_out_event;*/ + + /* selection */ + /*widgetClass->selection_get = ;*/ + /*widgetClass->selection_received = ;*/ + + + /* drag source: */ + /*widgetClass->drag_begin = ;*/ + /*widgetClass->drag_end = ;*/ + /*widgetClass->drag_data_get = ;*/ + /*widgetClass->drag_data_delete = ;*/ + + /* drag target: */ + /*widgetClass->drag_leave = ;*/ + /*widgetClass->drag_motion = ;*/ + /*widgetClass->drag_drop = ;*/ + /*widgetClass->drag_data_received = ;*/ + + /* For keybindings: */ + widgetClass->popup_menu = eek_preview_popup_menu; + /*widgetClass->show_help = ;*/ + + /* Accessibility support: */ + /*widgetClass->get_accessible = ;*/ + /*widgetClass->screen_changed = ;*/ + /*widgetClass->can_activate_accel = ;*/ + + + eek_preview_signals[CLICKED_SIGNAL] = + g_signal_new( "clicked", + G_TYPE_FROM_CLASS( klass ), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), + G_STRUCT_OFFSET( EekPreviewClass, clicked ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); + + eek_preview_signals[ALTCLICKED_SIGNAL] = + g_signal_new( "alt-clicked", + G_TYPE_FROM_CLASS( klass ), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), + G_STRUCT_OFFSET( EekPreviewClass, clicked ), + NULL, NULL, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, + 1, G_TYPE_INT ); + + + g_object_class_install_property( gobjClass, + FOCUS_PROP_ID, + g_param_spec_boolean( + "focus-on-click", + NULL, + "flag to grab focus when clicked", + TRUE, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT) + ) + ); +} + +gboolean eek_preview_get_focus_on_click( EekPreview* preview ) +{ + return preview->_takesFocus; +} + +void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click ) +{ + if ( focus_on_click != preview->_takesFocus ) { + preview->_takesFocus = focus_on_click; + } +} + +void eek_preview_set_details( EekPreview* preview, PreviewStyle prevstyle, ViewType view, GtkIconSize size ) +{ + preview->_prevstyle = prevstyle; + preview->_view = view; + preview->_size = size; + + gtk_widget_queue_draw(GTK_WIDGET(preview)); +} + +static void eek_preview_init( EekPreview *preview ) +{ + GtkWidget* widg = GTK_WIDGET(preview); + GTK_WIDGET_SET_FLAGS( widg, GTK_CAN_FOCUS ); + GTK_WIDGET_SET_FLAGS( widg, GTK_RECEIVES_DEFAULT ); + + gtk_widget_set_sensitive( widg, TRUE ); + + gtk_widget_add_events(widg, GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_KEY_PRESS_MASK + | GDK_KEY_RELEASE_MASK + | GDK_FOCUS_CHANGE_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK ); + +/* gtk_widget_add_events( widg, GDK_ALL_EVENTS_MASK );*/ + + preview->_r = 0x80; + preview->_g = 0x80; + preview->_b = 0xcc; + + preview->_hot = FALSE; + preview->_within = FALSE; + preview->_takesFocus = FALSE; + + preview->_prevstyle = PREVIEW_STYLE_ICON; + preview->_view = VIEW_TYPE_LIST; + preview->_size = GTK_ICON_SIZE_BUTTON; + +/* + GdkColor color = {0}; + color.red = (255 << 8) | 255; + + GdkColor whack = {0}; + whack.green = (255 << 8) | 255; + + gtk_widget_modify_bg( widg, GTK_STATE_NORMAL, &color ); + gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &whack ); +*/ + +/* GTK_STATE_ACTIVE, */ +/* GTK_STATE_PRELIGHT, */ +/* GTK_STATE_SELECTED, */ +/* GTK_STATE_INSENSITIVE */ + + if ( 0 ) { + GdkColor color = {0,0,0,0}; + + color.red = 0xffff; + color.green = 0; + color.blue = 0xffff; + gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE ); + gtk_widget_modify_bg(widg, GTK_STATE_ACTIVE, &color); + + color.red = 0; + color.green = 0xffff; + color.blue = 0; + gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE ); + gtk_widget_modify_bg(widg, GTK_STATE_SELECTED, &color); + + color.red = 0xffff; + color.green = 0; + color.blue = 0; + gdk_colormap_alloc_color( gdk_colormap_get_system(), &color, FALSE, TRUE ); + gtk_widget_modify_bg( widg, GTK_STATE_PRELIGHT, &color ); + } +} + + +GtkWidget* eek_preview_new(void) +{ + return GTK_WIDGET( g_object_new( EEK_PREVIEW_TYPE, NULL ) ); +} + diff --git a/src/dialogs/eek-preview.h b/src/dialogs/eek-preview.h new file mode 100644 index 000000000..1d4e445a5 --- /dev/null +++ b/src/dialogs/eek-preview.h @@ -0,0 +1,112 @@ +#ifndef SEEN_EEK_PREVIEW_H +#define SEEN_EEK_PREVIEW_H +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Eek Preview Stuffs. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +G_BEGIN_DECLS + + +#define EEK_PREVIEW_TYPE (eek_preview_get_type()) +#define EEK_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST( (obj), EEK_PREVIEW_TYPE, EekPreview)) +#define EEK_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST( (klass), EEK_PREVIEW_TYPE, EekPreviewClass)) +#define IS_EEK_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE( (obj), EEK_PREVIEW_TYPE)) +#define IS_EEK_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE( (klass), EEK_PREVIEW_TYPE)) +#define EEK_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS( (obj), EEK_PREVIEW_TYPE, EekPreviewClass)) + +typedef enum { + PREVIEW_STYLE_ICON = 0, + PREVIEW_STYLE_PREVIEW, + PREVIEW_STYLE_NAME, + PREVIEW_STYLE_BLURB, + PREVIEW_STYLE_ICON_NAME, + PREVIEW_STYLE_ICON_BLURB, + PREVIEW_STYLE_PREVIEW_NAME, + PREVIEW_STYLE_PREVIEW_BLURB +} PreviewStyle; + +typedef enum { + VIEW_TYPE_LIST = 0, + VIEW_TYPE_GRID +} ViewType; + + +typedef struct _EekPreview EekPreview; +typedef struct _EekPreviewClass EekPreviewClass; + + +struct _EekPreview +{ + GtkDrawingArea drawing; + + int _r; + int _g; + int _b; + + gboolean _hot; + gboolean _within; + gboolean _takesFocus; + + PreviewStyle _prevstyle; + ViewType _view; + GtkIconSize _size; +}; + +struct _EekPreviewClass +{ + GtkDrawingAreaClass parent_class; + + void (*clicked) (EekPreview* splat); +}; + + +GType eek_preview_get_type(void) G_GNUC_CONST; +GtkWidget* eek_preview_new(void); + +void eek_preview_set_details( EekPreview* splat, PreviewStyle prevstyle, ViewType view, GtkIconSize size ); +void eek_preview_set_color( EekPreview* splat, int r, int g, int b ); + +gboolean eek_preview_get_focus_on_click( EekPreview* preview ); +void eek_preview_set_focus_on_click( EekPreview* preview, gboolean focus_on_click ); + +G_END_DECLS + + +#endif /* SEEN_EEK_PREVIEW_H */ diff --git a/src/dialogs/export.cpp b/src/dialogs/export.cpp new file mode 100644 index 000000000..9f758bf9e --- /dev/null +++ b/src/dialogs/export.cpp @@ -0,0 +1,1753 @@ +#define __SP_EXPORT_C__ + +/** \file + * \brief PNG export dialog + */ + +/* + * Authors: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 1999-2005 Authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include "helper/unit-menu.h" +#include "helper/units.h" +#include "unit-constants.h" +#include "helper/window.h" +#include "inkscape-private.h" +#include "document.h" +#include "desktop-handles.h" +#include "sp-item.h" +#include "selection.h" +#include "file.h" +#include "macros.h" +#include "sp-namedview.h" + +#include "dialog-events.h" +#include "../prefs-utils.h" +#include "../verbs.h" +#include "../interface.h" + +#include "extension/output.h" +#include "extension/db.h" + +#include "io/sys.h" + + +#define SP_EXPORT_MIN_SIZE 1.0 + +#define DPI_BASE PX_PER_IN + +#define EXPORT_COORD_PRECISION 3 + +static void sp_export_area_toggled ( GtkToggleButton *tb, GtkObject *base ); +static void sp_export_export_clicked ( GtkButton *button, GtkObject *base ); +static void sp_export_browse_clicked ( GtkButton *button, gpointer userdata ); +static void sp_export_browse_store ( GtkButton *button, gpointer userdata ); + + +static void sp_export_area_x_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_area_y_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_area_width_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_area_height_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_bitmap_width_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_xdpi_value_changed ( GtkAdjustment *adj, + GtkObject *base); + +static void sp_export_selection_changed ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + GtkObject *base); +static void sp_export_selection_modified ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + guint flags, + GtkObject *base ); + +static void sp_export_set_area (GtkObject *base, double x0, double y0, double x1, double y1); +static void sp_export_value_set (GtkObject *base, const gchar *key, double val); +static void sp_export_value_set_px (GtkObject *base, const gchar *key, double val); +static float sp_export_value_get ( GtkObject *base, const gchar *key ); +static float sp_export_value_get_px ( GtkObject *base, const gchar *key ); + +static void sp_export_filename_modified (GtkObject * object, gpointer data); +static inline void sp_export_find_default_selection(GtkWidget * dlg); +static void sp_export_detect_size(GtkObject * base); + +static const gchar *prefs_path = "dialogs.export"; + +// these all need to be reinitialized to their defaults during dialog_destroy +static GtkWidget *dlg = NULL; +static win_data wd; +static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs +static gchar * original_name = NULL; +static gchar * doc_export_name = NULL; +static bool was_empty = TRUE; + +/** What type of button is being pressed */ +enum selection_type { + SELECTION_PAGE = 0, /**< Export the whole page */ + SELECTION_DRAWING, /**< Export everything drawn on the page */ + SELECTION_SELECTION, /**< Export everything that is selected */ + SELECTION_CUSTOM, /**< Allows the user to set the region exported */ + SELECTION_NUMBER_OF /**< A counter for the number of these guys */ +}; + +/** A list of strings that is used both in the preferences, and in the + data fields to describe the various values of \c selection_type. */ +static const char * selection_names[SELECTION_NUMBER_OF] = { + "page", "drawing", "selection", "custom"}; + +/** The names on the buttons for the various selection types. */ +static const char * selection_labels[SELECTION_NUMBER_OF] = { + N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")}; + +static void +sp_export_dialog_destroy ( GtkObject *object, gpointer data ) +{ + sp_signal_disconnect_by_data (INKSCAPE, dlg); + + wd.win = dlg = NULL; + wd.stop = 0; + x = -1000; y = -1000; w = 0; h = 0; + g_free(original_name); + original_name = NULL; + g_free(doc_export_name); + doc_export_name = NULL; + was_empty = TRUE; + + return; +} // end of sp_export_dialog_destroy() + +/// Called when dialog is closed or inkscape is shut down. +static bool +sp_export_dialog_delete ( GtkObject *object, GdkEvent *event, gpointer data ) +{ + + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it + +} // end of sp_export_dialog_delete() + +/** + \brief Creates a new spin button for the export dialog + \param key The name of the spin button + \param val A default value for the spin button + \param min Minimum value for the spin button + \param max Maximum value for the spin button + \param step The step size for the spin button + \param page Size of the page increment + \param us Unit selector that effects this spin button + \param t Table to put the spin button in + \param x X location in the table \c t to start with + \param y Y location in the table \c t to start with + \param ll Text to put on the left side of the spin button (optional) + \param lr Text to put on the right side of the spin button (optional) + \param digits Number of digits to display after the decimal + \param sensitive Whether the spin button is sensitive or not + \param cb Callback for when this spin button is changed (optional) + \param dlg Export dialog the spin button is being placed in + +*/ +static void +sp_export_spinbutton_new ( gchar *key, float val, float min, float max, + float step, float page, GtkWidget *us, + GtkWidget *t, int x, int y, + const gchar *ll, const gchar *lr, + int digits, unsigned int sensitive, + GCallback cb, GtkWidget *dlg ) +{ + GtkObject *a = gtk_adjustment_new (val, min, max, step, page, page); + gtk_object_set_data (a, "key", key); + gtk_object_set_data (GTK_OBJECT (dlg), (const gchar *)key, a); + + if (us) { + sp_unit_selector_add_adjustment ( SP_UNIT_SELECTOR (us), + GTK_ADJUSTMENT (a) ); + } + + int pos = 0; + + GtkWidget *l = NULL; + + if (ll) { + + l = gtk_label_new_with_mnemonic ((const gchar *)ll); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1, + (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 ); + gtk_widget_set_sensitive (l, sensitive); + pos += 1; + + } + + GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 1.0, digits); + gtk_table_attach ( GTK_TABLE (t), sb, x + pos, x + pos + 1, y, y + 1, + (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 ); + gtk_widget_set_size_request (sb, 80, -1); + gtk_widget_set_sensitive (sb, sensitive); + pos += 1; + + if (ll) { gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); } + + if (lr) { + + l = gtk_label_new_with_mnemonic ((const gchar *)lr); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5); + gtk_table_attach ( GTK_TABLE (t), l, x + pos, x + pos + 1, y, y + 1, + (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0 ); + gtk_widget_set_sensitive (l, sensitive); + pos += 1; + + gtk_label_set_mnemonic_widget (GTK_LABEL(l), sb); + } + + if (cb) + gtk_signal_connect (a, "value_changed", cb, dlg); + + return; +} // end of sp_export_spinbutton_new() + + +static GtkWidget * +sp_export_dialog_area_frame (GtkWidget * dlg) +{ + GtkWidget * f, * t, * hb, * b, * us, * l, * vb, * unitbox; + + f = gtk_frame_new (_("Export area")); + vb = gtk_vbox_new (FALSE, 2); + gtk_container_add (GTK_CONTAINER (f), vb); + + /* Units box */ + unitbox = gtk_hbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (unitbox), 4); + /* gets added to the vbox later, but the unit selector is needed + earlier than that */ + + us = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) + sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units); + gtk_box_pack_end (GTK_BOX (unitbox), us, FALSE, FALSE, 0); + l = gtk_label_new (_("Units:")); + gtk_box_pack_end (GTK_BOX (unitbox), l, FALSE, FALSE, 3); + gtk_object_set_data (GTK_OBJECT (dlg), "units", us); + + hb = gtk_hbox_new (TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hb), 4); + gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 3); + + for (int i = 0; i < SELECTION_NUMBER_OF; i++) { + b = gtk_toggle_button_new_with_mnemonic (_(selection_labels[i])); + gtk_object_set_data (GTK_OBJECT (b), "key", GINT_TO_POINTER(i)); + gtk_object_set_data (GTK_OBJECT (dlg), selection_names[i], b); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, TRUE, 0); + gtk_signal_connect ( GTK_OBJECT (b), "clicked", + GTK_SIGNAL_FUNC (sp_export_area_toggled), dlg ); + } + + g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", + G_CALLBACK (sp_export_selection_changed), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", + G_CALLBACK (sp_export_selection_modified), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", + G_CALLBACK (sp_export_selection_changed), dlg ); + + t = gtk_table_new (2, 6, FALSE); + gtk_box_pack_start(GTK_BOX(vb), t, FALSE, FALSE, 0); + gtk_table_set_row_spacings (GTK_TABLE (t), 4); + gtk_table_set_col_spacings (GTK_TABLE (t), 4); + gtk_container_set_border_width (GTK_CONTAINER (t), 4); + + sp_export_spinbutton_new ( "x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, + t, 0, 0, _("_x0:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK ( sp_export_area_x_value_changed), + dlg ); + + sp_export_spinbutton_new ( "x1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, + t, 2, 0, _("x_1:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK (sp_export_area_x_value_changed), + dlg ); + + sp_export_spinbutton_new ( "width", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, + us, t, 4, 0, _("Width:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK + (sp_export_area_width_value_changed), + dlg ); + + sp_export_spinbutton_new ( "y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, + t, 0, 1, _("_y0:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK (sp_export_area_y_value_changed), + dlg ); + + sp_export_spinbutton_new ( "y1", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, us, + t, 2, 1, _("y_1:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK (sp_export_area_y_value_changed), + dlg ); + + sp_export_spinbutton_new ( "height", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, + us, t, 4, 1, _("Height:"), NULL, EXPORT_COORD_PRECISION, 1, + G_CALLBACK (sp_export_area_height_value_changed), + dlg ); + + /* Adding in the unit box */ + gtk_box_pack_start(GTK_BOX(vb), unitbox, FALSE, FALSE, 0); + + return f; +} // end of sp_export_dialog_area_frame + + +void +sp_export_dialog (void) +{ + if (!dlg) { + GtkWidget *vb, *hb; + + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_FILE_EXPORT), title); + + dlg = sp_window_new (title, TRUE); + + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) + gtk_window_resize ((GtkWindow *) dlg, w, h); + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", + G_CALLBACK (sp_transientize_callback), &wd); + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", + GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", + G_CALLBACK (sp_export_dialog_destroy), dlg); + + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", + G_CALLBACK (sp_export_dialog_delete), dlg); + + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", + G_CALLBACK (sp_export_dialog_delete), dlg); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", + G_CALLBACK (sp_dialog_hide), dlg); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", + G_CALLBACK (sp_dialog_unhide), dlg); + + GtkTooltips *tt = gtk_tooltips_new(); + + vb = gtk_vbox_new (FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (vb), 0); + gtk_container_add (GTK_CONTAINER (dlg), vb); + + /* Export area frame */ + { + GtkWidget *f = sp_export_dialog_area_frame(dlg); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + } + + /* Bitmap size frame */ + { + GtkWidget *f = gtk_frame_new (_("Bitmap size")); + gtk_box_pack_start (GTK_BOX (vb), f, FALSE, FALSE, 0); + GtkWidget *t = gtk_table_new (2, 5, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (t), 4); + gtk_table_set_col_spacings (GTK_TABLE (t), 4); + gtk_container_set_border_width (GTK_CONTAINER (t), 4); + gtk_container_add (GTK_CONTAINER (f), t); + + sp_export_spinbutton_new ( "bmwidth", 16.0, 1.0, 1000000.0, 1.0, 10.0, + NULL, t, 0, 0, + _("_Width:"), _("pixels at"), 0, 1, + G_CALLBACK + (sp_export_bitmap_width_value_changed), + dlg ); + + sp_export_spinbutton_new ( "xdpi", + prefs_get_double_attribute + ( "dialogs.export.defaultxdpi", + "value", DPI_BASE), + 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 0, + NULL, _("dp_i"), 2, 1, + G_CALLBACK (sp_export_xdpi_value_changed), + dlg ); + + sp_export_spinbutton_new ( "bmheight", 16.0, 1.0, 1000000.0, 1, 10.0, + NULL, t, 0, 1, _("Height:"), _("pixels at"), + 0, 0, NULL, dlg ); + + /** \todo + * Needs fixing: there's no way to set ydpi currently, so we use + * the defaultxdpi value here, too... + */ + sp_export_spinbutton_new ( "ydpi", prefs_get_double_attribute + ( "dialogs.export.defaultxdpi", + "value", DPI_BASE), + 1.0, 9600.0, 0.1, 1.0, NULL, t, 3, 1, + NULL, _("dpi"), 2, 0, NULL, dlg ); + } + + /* File entry */ + { + GtkWidget *frame = gtk_frame_new (""); + GtkWidget *flabel = gtk_label_new_with_mnemonic (_("_Filename")); + gtk_frame_set_label_widget (GTK_FRAME(frame), flabel); + gtk_box_pack_start (GTK_BOX (vb), frame, FALSE, FALSE, 0); + + GtkWidget *fe = gtk_entry_new (); + + /* + * set the default filename to be that of the current path + document + * with .png extension + * + * One thing to notice here is that this filename may get + * overwritten, but it won't happen here. The filename gets + * written into the text field, but then the button to select + * the area gets set. In that code the filename can be changed + * if there are some with presidence in the document. So, while + * this code sets the name first, it may not be the one users + * really see. + */ + if (SP_ACTIVE_DOCUMENT && SP_DOCUMENT_URI (SP_ACTIVE_DOCUMENT)) + { + gchar *name; + SPDocument * doc = SP_ACTIVE_DOCUMENT; + const gchar *uri = SP_DOCUMENT_URI (doc); + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + const gchar * text_extension = repr->attribute("inkscape:output_extension"); + Inkscape::Extension::Output * oextension = NULL; + + if (text_extension != NULL) { + oextension = dynamic_cast(Inkscape::Extension::db.get(text_extension)); + } + + if (oextension != NULL) { + gchar * old_extension = oextension->get_extension(); + if (g_str_has_suffix(uri, old_extension)) { + gchar * uri_copy; + gchar * extension_point; + gchar * final_name; + + uri_copy = g_strdup(uri); + extension_point = g_strrstr(uri_copy, old_extension); + extension_point[0] = '\0'; + + final_name = g_strconcat(uri_copy, ".png", NULL); + gtk_entry_set_text (GTK_ENTRY (fe), final_name); + + g_free(final_name); + g_free(uri_copy); + } + } else { + name = g_strconcat(uri, ".png", NULL); + gtk_entry_set_text (GTK_ENTRY (fe), name); + g_free(name); + } + + doc_export_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(fe))); + } + g_signal_connect ( G_OBJECT (fe), "changed", + G_CALLBACK (sp_export_filename_modified), dlg); + + hb = gtk_hbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (frame), hb); + gtk_container_set_border_width (GTK_CONTAINER (hb), 4); + + { + GtkWidget *b = gtk_button_new_with_mnemonic (_("_Browse...")); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 4); + g_signal_connect ( G_OBJECT (b), "clicked", + G_CALLBACK (sp_export_browse_clicked), NULL ); + } + + gtk_box_pack_start (GTK_BOX (hb), fe, TRUE, TRUE, 0); + gtk_object_set_data (GTK_OBJECT (dlg), "filename", fe); + gtk_object_set_data (GTK_OBJECT (dlg), "filename-modified", (gpointer)FALSE); + original_name = g_strdup(gtk_entry_get_text (GTK_ENTRY (fe))); + // pressing enter in the filename field is the same as clicking export: + g_signal_connect ( G_OBJECT (fe), "activate", + G_CALLBACK (sp_export_export_clicked), dlg ); + // focus is in the filename initially: + gtk_widget_grab_focus (GTK_WIDGET (fe)); + + // mnemonic in frame label moves focus to filename: + gtk_label_set_mnemonic_widget (GTK_LABEL(flabel), fe); + } + + /* Buttons */ + hb = gtk_hbox_new (FALSE, 0); + gtk_box_pack_end (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + { + GtkWidget *b = gtk_button_new (); + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" _Export ")); + gtk_container_add (GTK_CONTAINER(b), l); + gtk_tooltips_set_tip (tt, b, _("Export the bitmap file with these settings"), NULL); + gtk_signal_connect ( GTK_OBJECT (b), "clicked", + GTK_SIGNAL_FUNC (sp_export_export_clicked), dlg ); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + + gtk_widget_show_all (vb); + + } // end of if (!dlg) + + sp_export_find_default_selection(dlg); + + gtk_window_present ((GtkWindow *) dlg); + + return; +} // end of sp_export_dialog() + +static inline void +sp_export_find_default_selection(GtkWidget * dlg) +{ + selection_type key = SELECTION_NUMBER_OF; + + if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) { + key = SELECTION_SELECTION; + } + + /* Try using the preferences */ + if (key == SELECTION_NUMBER_OF) { + const gchar *what = NULL; + int i = SELECTION_NUMBER_OF; + + what = prefs_get_string_attribute ("dialogs.export.exportarea", "value"); + + if (what != NULL) { + for (i = 0; i < SELECTION_NUMBER_OF; i++) { + if (!strcmp (what, selection_names[i])) { + break; + } + } + } + + key = (selection_type)i; + } + + if (key == SELECTION_NUMBER_OF) { + key = SELECTION_SELECTION; + } + + GtkWidget *button = (GtkWidget *)g_object_get_data(G_OBJECT(dlg), + selection_names[key]); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + + return; +} + + +/** + * \brief If selection changed or a different document activated, we must + * recalculate any chosen areas + * + */ +static void +sp_export_selection_changed ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + GtkObject *base ) +{ + selection_type current_key; + current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + + if ((current_key == SELECTION_DRAWING || current_key == SELECTION_PAGE) && + (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false && + was_empty) { + gtk_toggle_button_set_active + ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[SELECTION_SELECTION])), + TRUE ); + } + was_empty = (SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty(); + + current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + + if (inkscape && + SP_IS_INKSCAPE (inkscape) && + selection && + SELECTION_CUSTOM != current_key) { + GtkToggleButton * button; + button = (GtkToggleButton *)gtk_object_get_data(base, selection_names[current_key]); + sp_export_area_toggled(button, base); + } // end of if() + + return; +} // end of sp_export_selection_changed() + +static void +sp_export_selection_modified ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + guint flags, + GtkObject *base ) +{ + selection_type current_key; + current_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + + switch (current_key) { + case SELECTION_DRAWING: + if ( SP_ACTIVE_DESKTOP ) { + SPDocument *doc; + NRRect bbox; + doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP); + sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox); + + if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { + sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1); + } + } + break; + case SELECTION_SELECTION: + if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) { + NRRect bbox; + (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox); + sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1); + } + break; + default: + /* Do nothing for page or for custom */ + break; + } + + return; +} + +/// Called when one of the selection buttons was toggled. +static void +sp_export_area_toggled (GtkToggleButton *tb, GtkObject *base) +{ + if (gtk_object_get_data (base, "update")) + return; + + selection_type key, old_key; + key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data (GTK_OBJECT (tb), "key"))); + old_key = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + + /* Ignore all "turned off" events unless we're the only active button */ + if (!gtk_toggle_button_get_active (tb) ) { + + /* Don't let the current selection be deactived - but rerun the + activate to allow the user to renew the values */ + if (key == old_key) { + gtk_toggle_button_set_active ( tb, TRUE ); + } + + return; + } + + /* Turn off the currently active button unless it's us */ + gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key); + + if (old_key != key) { + gtk_toggle_button_set_active + ( GTK_TOGGLE_BUTTON ( gtk_object_get_data (base, selection_names[old_key])), + FALSE ); + } + + if ( SP_ACTIVE_DESKTOP ) + { + SPDocument *doc; + NRRect bbox; + doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP); + + /* Notice how the switch is used to 'fall through' here to get + various backups. If you modify this without noticing you'll + probabaly screw something up. */ + switch (key) { + case SELECTION_SELECTION: + if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) + { + (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(&bbox); + /* Only if there is a selection that we can set + do we break, otherwise we fall through to the + drawing */ + // std::cout << "Using selection: SELECTION" << std::endl; + key = SELECTION_SELECTION; + break; + } + case SELECTION_DRAWING: + /** \todo + * This returns wrong values if the document has a viewBox. + */ + sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc)), &bbox); + + /* If the drawing is valid, then we'll use it and break + otherwise we drop through to the page settings */ + if (!(bbox.x0 > bbox.x1 && bbox.y0 > bbox.y1)) { + // std::cout << "Using selection: DRAWING" << std::endl; + key = SELECTION_DRAWING; + break; + } + case SELECTION_PAGE: + bbox.x0 = 0.0; + bbox.y0 = 0.0; + bbox.x1 = sp_document_width (doc); + bbox.y1 = sp_document_height (doc); + // std::cout << "Using selection: PAGE" << std::endl; + key = SELECTION_PAGE; + break; + case SELECTION_CUSTOM: + default: + break; + } // switch + + // remember area setting + prefs_set_string_attribute ( "dialogs.export.exportarea", + "value", selection_names[key]); + + if (key != SELECTION_CUSTOM) { + sp_export_set_area (base, bbox.x0, bbox.y0, bbox.x1, bbox.y1); + } + + } // end of if ( SP_ACTIVE_DESKTOP ) + + + if (SP_ACTIVE_DESKTOP && !gtk_object_get_data(GTK_OBJECT(base), "filename-modified")) { + GtkWidget * file_entry; + const gchar * filename = NULL; + float xdpi = 0.0, ydpi = 0.0; + + file_entry = (GtkWidget *)gtk_object_get_data (base, "filename"); + + switch (key) { + case SELECTION_PAGE: + case SELECTION_DRAWING: { + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + const gchar * dpi_string; + + filename = repr->attribute("inkscape:export-filename"); + + dpi_string = NULL; + dpi_string = repr->attribute("inkscape:export-xdpi"); + if (dpi_string != NULL) { + xdpi = atof(dpi_string); + } + + dpi_string = NULL; + dpi_string = repr->attribute("inkscape:export-ydpi"); + if (dpi_string != NULL) { + ydpi = atof(dpi_string); + } + + if (filename == NULL) { + if (doc_export_name != NULL) { + filename = g_strdup(doc_export_name); + } else { + filename = g_strdup(""); + } + } + + break; + } + case SELECTION_SELECTION: + if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) { + const GSList * reprlst; + bool filename_search = TRUE; + bool xdpi_search = TRUE; + bool ydpi_search = TRUE; + + reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList(); + for(; reprlst != NULL && + filename_search && + xdpi_search && + ydpi_search; + reprlst = reprlst->next) { + const gchar * dpi_string; + Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data; + + if (filename_search) { + filename = repr->attribute("inkscape:export-filename"); + if (filename != NULL) + filename_search = FALSE; + } + + if (xdpi_search) { + dpi_string = NULL; + dpi_string = repr->attribute("inkscape:export-xdpi"); + if (dpi_string != NULL) { + xdpi = atof(dpi_string); + xdpi_search = FALSE; + } + } + + if (ydpi_search) { + dpi_string = NULL; + dpi_string = repr->attribute("inkscape:export-ydpi"); + if (dpi_string != NULL) { + ydpi = atof(dpi_string); + ydpi_search = FALSE; + } + } + } + + /* If we still don't have a filename -- let's build + one that's nice */ + if (filename == NULL) { + const gchar * id = NULL; + reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList(); + for(; reprlst != NULL; reprlst = reprlst->next) { + Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data; + if (repr->attribute("id")) { + id = repr->attribute("id"); + break; + } + } + if (id == NULL) /* This should never happen */ + id = "bitmap"; + + gchar * directory = NULL; + const gchar * file_entry_text; + + file_entry_text = gtk_entry_get_text(GTK_ENTRY(file_entry)); + if (directory == NULL && file_entry_text != NULL && file_entry_text[0] != '\0') { + // std::cout << "Directory from dialog" << std::endl; + directory = g_dirname(file_entry_text); + } + + if (directory == NULL) { + /* Grab document directory */ + if (SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)) { + // std::cout << "Directory from document" << std::endl; + directory = g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT)); + } + } + + if (directory == NULL) { + // std::cout << "Home Directory" << std::endl; + directory = homedir_path(NULL); + } + + gchar * id_ext = g_strconcat(id, ".png", NULL); + filename = g_build_filename(directory, id_ext, NULL); + g_free(directory); + g_free(id_ext); + } + } + break; + case SELECTION_CUSTOM: + default: + break; + } + + if (filename != NULL) { + g_free(original_name); + original_name = g_strdup(filename); + gtk_entry_set_text(GTK_ENTRY(file_entry), filename); + } + + if (xdpi != 0.0) { + sp_export_value_set(base, "xdpi", xdpi); + } + + /* These can't be seperate, and setting x sets y, so for + now setting this is disabled. Hopefully it won't be in + the future */ + if (FALSE && ydpi != 0.0) { + sp_export_value_set(base, "ydpi", ydpi); + } + } + + return; +} // end of sp_export_area_toggled() + +/// Called when dialog is deleted +static gint +sp_export_progress_delete ( GtkWidget *widget, GdkEvent *event, GObject *base ) +{ + g_object_set_data (base, "cancel", (gpointer) 1); + return TRUE; +} // end of sp_export_progress_delete() + +/// Called when progress is cancelled +static void +sp_export_progress_cancel ( GtkWidget *widget, GObject *base ) +{ + g_object_set_data (base, "cancel", (gpointer) 1); +} // end of sp_export_progress_cancel() + +/// Called for every progress iteration +static unsigned int +sp_export_progress_callback (float value, void *data) +{ + GtkWidget *prg; + int evtcount; + + if (g_object_get_data ((GObject *) data, "cancel")) + return FALSE; + + prg = (GtkWidget *) g_object_get_data ((GObject *) data, "progress"); + gtk_progress_bar_set_fraction ((GtkProgressBar *) prg, value); + + evtcount = 0; + while ((evtcount < 16) && gdk_events_pending ()) { + gtk_main_iteration_do (FALSE); + evtcount += 1; + } + + gtk_main_iteration_do (FALSE); + + return TRUE; + +} // end of sp_export_progress_callback() + +/// Called when export button is clicked +static void +sp_export_export_clicked (GtkButton *button, GtkObject *base) +{ + if (!SP_ACTIVE_DESKTOP) return; + + GtkWidget *fe = (GtkWidget *)gtk_object_get_data(base, "filename"); + gchar const *filename = gtk_entry_get_text(GTK_ENTRY(fe)); + + float const x0 = sp_export_value_get_px(base, "x0"); + float const y0 = sp_export_value_get_px(base, "y0"); + float const x1 = sp_export_value_get_px(base, "x1"); + float const y1 = sp_export_value_get_px(base, "y1"); + float const xdpi = sp_export_value_get(base, "xdpi"); + float const ydpi = sp_export_value_get(base, "ydpi"); + int const width = int(sp_export_value_get(base, "bmwidth") + 0.5); + int const height = int(sp_export_value_get(base, "bmheight") + 0.5); + + if (filename == NULL || *filename == '\0') { + sp_ui_error_dialog(_("You have to enter a filename")); + return; + } + + if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) { + sp_ui_error_dialog (_("The chosen area to be exported is invalid")); + return; + } + + gchar *dirname = g_dirname(filename); + if ( dirname == NULL + || !Inkscape::IO::file_test(dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) ) + { + gchar *safeDir = Inkscape::IO::sanitizeString(dirname); + gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"), + safeDir); + sp_ui_error_dialog(error); + g_free(safeDir); + g_free(error); + g_free(dirname); + return; + } + g_free(dirname); + + SPNamedView *nv = SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP); + GtkWidget *dlg, *prg, *btn; /* progressbar-stuff */ + char *fn; + gchar *text; + + dlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dlg), _("Export in progress")); + prg = gtk_progress_bar_new (); + sp_transientize (dlg); + gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE); + g_object_set_data ((GObject *) base, "progress", prg); + fn = g_path_get_basename (filename); + text = g_strdup_printf ( _("Exporting %s (%d x %d)"), + fn, width, height); + g_free (fn); + gtk_progress_bar_set_text ((GtkProgressBar *) prg, text); + g_free (text); + gtk_progress_bar_set_orientation ( (GtkProgressBar *) prg, + GTK_PROGRESS_LEFT_TO_RIGHT); + gtk_box_pack_start ((GtkBox *) ((GtkDialog *) dlg)->vbox, + prg, FALSE, FALSE, 4 ); + btn = gtk_dialog_add_button ( GTK_DIALOG (dlg), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL ); + + g_signal_connect ( (GObject *) dlg, "delete_event", + (GCallback) sp_export_progress_delete, base); + g_signal_connect ( (GObject *) btn, "clicked", + (GCallback) sp_export_progress_cancel, base); + gtk_window_set_modal ((GtkWindow *) dlg, TRUE); + gtk_widget_show_all (dlg); + + /* Do export */ + if (!sp_export_png_file (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), filename, + x0, y0, x1, y1, width, height, + nv->pagecolor, + sp_export_progress_callback, base)) { + gchar * error; + gchar * safeFile = Inkscape::IO::sanitizeString(filename); + error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile); + sp_ui_error_dialog(error); + g_free(safeFile); + g_free(error); + } + + /* Reset the filename so that it can be changed again by changing + selections and all that */ + g_free(original_name); + original_name = g_strdup(filename); + gtk_object_set_data (GTK_OBJECT (base), "filename-modified", (gpointer)FALSE); + + gtk_widget_destroy (dlg); + g_object_set_data (G_OBJECT (base), "cancel", (gpointer) 0); + + /* Setup the values in the document */ + switch ((selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type")))) { + case SELECTION_PAGE: + case SELECTION_DRAWING: { + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + bool modified = FALSE; + const gchar * temp_string; + + bool saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive(doc, FALSE); + + temp_string = repr->attribute("inkscape:export-filename"); + if (temp_string == NULL || strcmp(temp_string, filename)) { + repr->setAttribute("inkscape:export-filename", filename); + modified = TRUE; + } + temp_string = repr->attribute("inkscape:export-xdpi"); + if (temp_string == NULL || xdpi != atof(temp_string)) { + sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi); + modified = TRUE; + } + temp_string = repr->attribute("inkscape:export-ydpi"); + if (temp_string == NULL || xdpi != atof(temp_string)) { + sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi); + modified = TRUE; + } + + if (modified) + repr->setAttribute("sodipodi:modified", "TRUE"); + sp_document_set_undo_sensitive(doc, saved); + break; + } + case SELECTION_SELECTION: { + const GSList * reprlst; + SPDocument * doc = SP_ACTIVE_DOCUMENT; + bool modified = FALSE; + + bool saved = sp_document_get_undo_sensitive(doc); + sp_document_set_undo_sensitive(doc, FALSE); + reprlst = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->reprList(); + + for(; reprlst != NULL; reprlst = reprlst->next) { + Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data; + const gchar * temp_string; + + if (repr->attribute("id") == NULL || + !(g_strrstr(filename, repr->attribute("id")) != NULL && + (!SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT) || + strcmp(g_dirname(filename), g_dirname(SP_DOCUMENT_URI(SP_ACTIVE_DOCUMENT))) == 0))) { + temp_string = repr->attribute("inkscape:export-filename"); + if (temp_string == NULL || strcmp(temp_string, filename)) { + repr->setAttribute("inkscape:export-filename", filename); + modified = TRUE; + } + } + temp_string = repr->attribute("inkscape:export-xdpi"); + if (temp_string == NULL || xdpi != atof(temp_string)) { + sp_repr_set_svg_double(repr, "inkscape:export-xdpi", xdpi); + modified = TRUE; + } + temp_string = repr->attribute("inkscape:export-ydpi"); + if (temp_string == NULL || xdpi != atof(temp_string)) { + sp_repr_set_svg_double(repr, "inkscape:export-ydpi", ydpi); + modified = TRUE; + } + } + + if (modified) { + Inkscape::XML::Node * repr = sp_document_repr_root(doc); + repr->setAttribute("sodipodi:modified", "TRUE"); + } + + sp_document_set_undo_sensitive(doc, saved); + break; + } + default: + break; + } + + + return; +} // end of sp_export_export_clicked() + +/// Called when Browse button is clicked +static void +sp_export_browse_clicked (GtkButton *button, gpointer userdata) +{ + GtkWidget *fs, *fe; + const gchar *filename; + + fs = gtk_file_selection_new (_("Select a filename for exporting")); + fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename"); + + sp_transientize (fs); + + gtk_window_set_modal(GTK_WINDOW (fs), true); + + filename = gtk_entry_get_text (GTK_ENTRY (fe)); + + if (*filename == '\0') { + filename = homedir_path(NULL); + } + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs), filename); + + g_signal_connect ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button), + "clicked", + G_CALLBACK (sp_export_browse_store), + (gpointer) fs ); + + g_signal_connect_swapped ( GTK_OBJECT (GTK_FILE_SELECTION (fs)->ok_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + (gpointer) fs ); + + g_signal_connect_swapped ( GTK_OBJECT + (GTK_FILE_SELECTION (fs)->cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + (gpointer) fs ); + + gtk_widget_show (fs); + + return; +} // end of sp_export_browse_clicked() + +/// Called when OK clicked in file dialog +static void +sp_export_browse_store (GtkButton *button, gpointer userdata) +{ + GtkWidget *fs = (GtkWidget *)userdata, *fe; + const gchar *file; + + fe = (GtkWidget *)g_object_get_data (G_OBJECT (dlg), "filename"); + + file = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); + gchar * utf8file = g_filename_to_utf8( file, -1, NULL, NULL, NULL ); + gtk_entry_set_text (GTK_ENTRY (fe), utf8file); + g_free(utf8file); + + g_object_set_data (G_OBJECT (dlg), "filename", fe); + + return; +} // end of sp_export_browse_store() + +// TODO: Move this to nr-rect-fns.h. +static bool +sp_export_bbox_equal(NR::Rect const &one, NR::Rect const &two) +{ + double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION); + return ( + (fabs(one.min()[NR::X] - two.min()[NR::X]) < epsilon) && + (fabs(one.min()[NR::Y] - two.min()[NR::Y]) < epsilon) && + (fabs(one.max()[NR::X] - two.max()[NR::X]) < epsilon) && + (fabs(one.max()[NR::Y] - two.max()[NR::Y]) < epsilon) + ); +} + +/** + \brief This function is used to detect the current selection setting + based on the values in the x0, y0, x1 and y0 fields. + \param base The export dialog itself + + One of the most confusing parts of this function is why the array + is built at the beginning. What needs to happen here is that we + should always check the current selection to see if it is the valid + one. While this is a performance improvement it is also a usability + one during the cases where things like selections and drawings match + size. This way buttons change less 'randomly' (atleast in the eyes + of the user). To do this an array is built where the current selection + type is placed first, and then the others in an order from smallest + to largest (this can be configured by reshuffling \c test_order). + + All of the values in this function are rounded to two decimal places + because that is what is shown to the user. While everything is kept + more accurate than that, the user can't control more acurrate than + that, so for this to work for them - it needs to check on that level + of accuracy. + + \todo finish writing this up +*/ +static void +sp_export_detect_size(GtkObject * base) { + static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM}; + selection_type this_test[SELECTION_NUMBER_OF + 1]; + selection_type key = SELECTION_NUMBER_OF; + + NR::Point x(sp_export_value_get_px (base, "x0"), + sp_export_value_get_px (base, "y0")); + NR::Point y(sp_export_value_get_px (base, "x1"), + sp_export_value_get_px (base, "y1")); + NR::Rect current_bbox(x, y); + //std::cout << "Current " << current_bbox; + + this_test[0] = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + for (int i = 0; i < SELECTION_NUMBER_OF; i++) { + this_test[i + 1] = test_order[i]; + } + + for (int i = 0; + i < SELECTION_NUMBER_OF + 1 && + key == SELECTION_NUMBER_OF && + SP_ACTIVE_DESKTOP != NULL; + i++) { + // std::cout << "Looking at: " << selection_names[this_test[i]] << std::endl; + switch (this_test[i]) { + case SELECTION_SELECTION: + if ((SP_DT_SELECTION(SP_ACTIVE_DESKTOP))->isEmpty() == false) { + NR::Rect bbox = (SP_DT_SELECTION (SP_ACTIVE_DESKTOP))->bounds(); + + //std::cout << "Selection " << bbox; + if (sp_export_bbox_equal(bbox,current_bbox)) { + key = SELECTION_SELECTION; + } + } + break; + case SELECTION_DRAWING: { + SPDocument *doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP); + + NR::Rect bbox = sp_item_bbox_desktop (SP_ITEM (SP_DOCUMENT_ROOT (doc))); + + // std::cout << "Drawing " << bbox2; + if (sp_export_bbox_equal(bbox,current_bbox)) { + key = SELECTION_DRAWING; + } + break; + } + + case SELECTION_PAGE: { + SPDocument *doc; + + doc = SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP); + + NR::Point x(0.0, 0.0); + NR::Point y(sp_document_width(doc), + sp_document_height(doc)); + NR::Rect bbox(x, y); + + // std::cout << "Page " << bbox; + if (sp_export_bbox_equal(bbox,current_bbox)) { + key = SELECTION_PAGE; + } + + break; + } + default: + break; + } + } + // std::cout << std::endl; + + if (key == SELECTION_NUMBER_OF) { + key = SELECTION_CUSTOM; + } + + /* We're now using a custom size, not a fixed one */ + /* printf("Detecting state: %s\n", selection_names[key]); */ + selection_type old = (selection_type)(GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(base), "selection-type"))); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[old])), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gtk_object_get_data(base, selection_names[key])), TRUE); + gtk_object_set_data(GTK_OBJECT(base), "selection-type", (gpointer)key); + + return; +} /* sp_export_detect_size */ + +/// Called when area x0 value is changed +static void +sp_export_area_x_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + float x0, x1, xdpi, width; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) + { + return; + } + + gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) ); + + x0 = sp_export_value_get_px (base, "x0"); + x1 = sp_export_value_get_px (base, "x1"); + xdpi = sp_export_value_get (base, "xdpi"); + + width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5); + + if (width < SP_EXPORT_MIN_SIZE) { + const gchar *key; + width = SP_EXPORT_MIN_SIZE; + key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key"); + + if (!strcmp (key, "x0")) { + x1 = x0 + width * DPI_BASE / xdpi; + sp_export_value_set_px (base, "x1", x1); + } else { + x0 = x1 - width * DPI_BASE / xdpi; + sp_export_value_set_px (base, "x0", x0); + } + } + + sp_export_value_set_px (base, "width", x1 - x0); + sp_export_value_set (base, "bmwidth", width); + + sp_export_detect_size(base); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_area_x_value_changed() + +/// Called when area y0 value is changed. +static void +sp_export_area_y_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + float y0, y1, ydpi, height; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) + { + return; + } + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE)); + + y0 = sp_export_value_get_px (base, "y0"); + y1 = sp_export_value_get_px (base, "y1"); + ydpi = sp_export_value_get (base, "ydpi"); + + height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5); + + if (height < SP_EXPORT_MIN_SIZE) { + const gchar *key; + height = SP_EXPORT_MIN_SIZE; + key = (const gchar *)gtk_object_get_data (GTK_OBJECT (adj), "key"); + if (!strcmp (key, "y0")) { + y1 = y0 + height * DPI_BASE / ydpi; + sp_export_value_set_px (base, "y1", y1); + } else { + y0 = y1 - height * DPI_BASE / ydpi; + sp_export_value_set_px (base, "y0", y0); + } + } + + sp_export_value_set_px (base, "height", y1 - y0); + sp_export_value_set (base, "bmheight", height); + + sp_export_detect_size(base); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_area_y_value_changed() + +/// Called when x1-x0 or area width is changed +static void +sp_export_area_width_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + float x0, x1, xdpi, width, bmwidth; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) { + return; + } + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE)); + + x0 = sp_export_value_get_px (base, "x0"); + x1 = sp_export_value_get_px (base, "x1"); + xdpi = sp_export_value_get (base, "xdpi"); + width = sp_export_value_get_px (base, "width"); + bmwidth = floor (width * xdpi / DPI_BASE + 0.5); + + if (bmwidth < SP_EXPORT_MIN_SIZE) { + + bmwidth = SP_EXPORT_MIN_SIZE; + width = bmwidth * DPI_BASE / xdpi; + sp_export_value_set_px (base, "width", width); + } + + sp_export_value_set_px (base, "x1", x0 + width); + sp_export_value_set (base, "bmwidth", bmwidth); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_area_width_value_changed() + +/// Called when y1-y0 or area height is changed. +static void +sp_export_area_height_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + + float y0, y1, ydpi, height, bmheight; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) { + return; + } + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE)); + + y0 = sp_export_value_get_px (base, "y0"); + y1 = sp_export_value_get_px (base, "y1"); + ydpi = sp_export_value_get (base, "ydpi"); + height = sp_export_value_get_px (base, "height"); + bmheight = floor (height * ydpi / DPI_BASE + 0.5); + + if (bmheight < SP_EXPORT_MIN_SIZE) { + bmheight = SP_EXPORT_MIN_SIZE; + height = bmheight * DPI_BASE / ydpi; + sp_export_value_set_px (base, "height", height); + } + + sp_export_value_set_px (base, "y1", y0 + height); + sp_export_value_set (base, "bmheight", bmheight); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_area_height_value_changed() + +/** + \brief A function to set the ydpi + \param base The export dialog + + This function grabs all of the y values and then figures out the + new bitmap size based on the changing dpi value. The dpi value is + gotten from the xdpi setting as these can not currently be independent. +*/ +static void +sp_export_set_image_y (GtkObject *base) +{ + float y0, y1, xdpi; + + y0 = sp_export_value_get_px (base, "y0"); + y1 = sp_export_value_get_px (base, "y1"); + xdpi = sp_export_value_get (base, "xdpi"); + + sp_export_value_set (base, "ydpi", xdpi); + sp_export_value_set (base, "bmheight", (y1 - y0) * xdpi / DPI_BASE); + + return; +} // end of sp_export_set_image_y() + +/// Called when pixel width is changed +static void +sp_export_bitmap_width_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + float x0, x1, bmwidth, xdpi; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) { + return; + } + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE)); + + x0 = sp_export_value_get_px (base, "x0"); + x1 = sp_export_value_get_px (base, "x1"); + bmwidth = sp_export_value_get (base, "bmwidth"); + + if (bmwidth < SP_EXPORT_MIN_SIZE) { + bmwidth = SP_EXPORT_MIN_SIZE; + sp_export_value_set (base, "bmwidth", bmwidth); + } + + xdpi = bmwidth * DPI_BASE / (x1 - x0); + sp_export_value_set (base, "xdpi", xdpi); + + sp_export_set_image_y (base); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_bitmap_width_value_changed() + +/** + \brief A function to adjust the bitmap width when the xdpi value changes + \param adj The adjustment that was changed + \param base The export dialog itself + + The first thing this function checks is to see if we are doing an + update. If we are, this function just returns because there is another + instance of it that will handle everything for us. If there is a + units change, we also assume that everyone is being updated appropriately + and there is nothing for us to do. + + If we're the highest level function, we set the update flag, and + continue on our way. + + All of the values are grabbed using the \c sp_export_value_get functions + (call to the _pt ones for x0 and x1 but just standard for xdpi). The + xdpi value is saved in the preferences for the next time the dialog + is opened. (does the selection dpi need to be set here?) + + A check is done to to ensure that we aren't outputing an invalid width, + this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is + changed to make it valid. + + After all of this the bitmap width is changed. + + We also change the ydpi. This is a temporary hack as these can not + currently be independent. This is likely to change in the future. +*/ +void +sp_export_xdpi_value_changed (GtkAdjustment *adj, GtkObject *base) +{ + float x0, x1, xdpi, bmwidth; + + if (gtk_object_get_data (base, "update")) + return; + + if (sp_unit_selector_update_test ((SPUnitSelector *)gtk_object_get_data + (base, "units"))) { + return; + } + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (TRUE)); + + x0 = sp_export_value_get_px (base, "x0"); + x1 = sp_export_value_get_px (base, "x1"); + xdpi = sp_export_value_get (base, "xdpi"); + + // remember xdpi setting + prefs_set_double_attribute ("dialogs.export.defaultxdpi", "value", xdpi); + + bmwidth = (x1 - x0) * xdpi / DPI_BASE; + + if (bmwidth < SP_EXPORT_MIN_SIZE) { + bmwidth = SP_EXPORT_MIN_SIZE; + if (x1 != x0) + xdpi = bmwidth * DPI_BASE / (x1 - x0); + else + xdpi = DPI_BASE; + sp_export_value_set (base, "xdpi", xdpi); + } + + sp_export_value_set (base, "bmwidth", bmwidth); + + sp_export_set_image_y (base); + + gtk_object_set_data (base, "update", GUINT_TO_POINTER (FALSE)); + + return; +} // end of sp_export_xdpi_value_changed() + + +/** + \brief A function to change the area that is used for the exported + bitmap. + \param base This is the export dialog + \param x0 Horizontal upper left hand corner of the picture in points + \param y0 Vertical upper left hand corner of the picture in points + \param x1 Horizontal lower right hand corner of the picture in points + \param y1 Vertical lower right hand corner of the picture in points + + This function just calls \c sp_export_value_set_px for each of the + parameters that is passed in. This allows for setting them all in + one convient area. + + Update is set to suspend all of the other test running while all the + values are being set up. This allows for a performance increase, but + it also means that the wrong type won't be detected with only some of + the values set. After all the values are set everyone is told that + there has been an update. +*/ +static void +sp_export_set_area ( GtkObject *base, double x0, double y0, double x1, double y1 ) +{ + gtk_object_set_data ( base, "update", GUINT_TO_POINTER (TRUE) ); + sp_export_value_set_px (base, "x1", x1); + sp_export_value_set_px (base, "y1", y1); + sp_export_value_set_px (base, "x0", x0); + sp_export_value_set_px (base, "y0", y0); + gtk_object_set_data ( base, "update", GUINT_TO_POINTER (FALSE) ); + + sp_export_area_x_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "x1"), base); + sp_export_area_y_value_changed ((GtkAdjustment *)gtk_object_get_data (base, "y1"), base); + + return; +} + +/** + \brief Sets the value of an adjustment + \param base The export dialog + \param key Which adjustment to set + \param val What value to set it to + + This function finds the adjustment using the data stored in the + export dialog. After finding the adjustment it then sets + the value of it. +*/ +static void +sp_export_value_set ( GtkObject *base, const gchar *key, double val ) +{ + GtkAdjustment *adj; + + adj = (GtkAdjustment *)gtk_object_get_data (base, key); + + gtk_adjustment_set_value (adj, val); +} + +/** + \brief A function to set a value using the units points + \param base The export dialog + \param key Which value should be set + \param val What the value should be in points + + This function first gets the adjustment for the key that is passed + in. It then figures out what units are currently being used in the + dialog. After doing all of that, it then converts the incoming + value and sets the adjustment. +*/ +static void +sp_export_value_set_px (GtkObject *base, const gchar *key, double val) +{ + const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units") ); + + sp_export_value_set (base, key, sp_pixels_get_units (val, *unit)); + + return; +} + +/** + \brief Get the value of an adjustment in the export dialog + \param base The export dialog + \param key Which adjustment is being looked for + \return The value in the specified adjustment + + This function gets the adjustment from the data field in the export + dialog. It then grabs the value from the adjustment. +*/ +static float +sp_export_value_get ( GtkObject *base, const gchar *key ) +{ + GtkAdjustment *adj; + + adj = (GtkAdjustment *)gtk_object_get_data (base, key); + + return adj->value; +} // end of sp_export_value_get() + +/** + \brief Grabs a value in the export dialog and converts the unit + to points + \param base The export dialog + \param key Which value should be returned + \return The value in the adjustment in points + + This function, at its most basic, is a call to \c sp_export_value_get + to get the value of the adjustment. It then finds the units that + are being used by looking at the "units" attribute of the export + dialog. Using that it converts the returned value into points. +*/ +static float +sp_export_value_get_px ( GtkObject *base, const gchar *key ) +{ + float value = sp_export_value_get(base, key); + const SPUnit *unit = sp_unit_selector_get_unit ((SPUnitSelector *)gtk_object_get_data (base, "units")); + + return sp_units_get_pixels (value, *unit); +} // end of sp_export_value_get_px() + +/** + \brief This function is called when the filename is changed by + anyone. It resets the virgin bit. + \param object Text entry box + \param data The export dialog + \return None + + This function gets called when the text area is modified. It is + looking for the case where the text area is modified from its + original value. In that case it sets the "filename-modified" bit + to TRUE. If the text dialog returns back to the original text, the + bit gets reset. This should stop simple mistakes. +*/ +static void +sp_export_filename_modified (GtkObject * object, gpointer data) +{ + GtkWidget * text_entry = (GtkWidget *)object; + GtkWidget * export_dialog = (GtkWidget *)data; + + if (!strcmp(original_name, gtk_entry_get_text(GTK_ENTRY(text_entry)))) { + gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)FALSE); +// printf("Modified: FALSE\n"); + } else { + gtk_object_set_data (GTK_OBJECT (export_dialog), "filename-modified", (gpointer)TRUE); +// printf("Modified: TRUE\n"); + } + + return; +} // end sp_export_filename_modified + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/export.h b/src/dialogs/export.h new file mode 100644 index 000000000..4fb6ce2a4 --- /dev/null +++ b/src/dialogs/export.h @@ -0,0 +1,24 @@ +#ifndef SP_EXPORT_H +#define SP_EXPORT_H + +/** + * \brief text-edit + * + * Text editing and font changes + * + */ + +void sp_export_dialog (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/extensions.cpp b/src/dialogs/extensions.cpp new file mode 100644 index 000000000..36ec67744 --- /dev/null +++ b/src/dialogs/extensions.cpp @@ -0,0 +1,128 @@ +/* + * A simple dialog for previewing icon representation. + * + * Authors: + * Jon A. Cruz + * + * Copyright (C) 2005 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include //for GTK_RESPONSE* types +#include + +#include "extension/db.h" +#include "extensions.h" + + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +using Inkscape::Extension::Extension; + +ExtensionsPanel* ExtensionsPanel::instance = 0; + + +ExtensionsPanel& ExtensionsPanel::getInstance() +{ + if ( !instance ) { + instance = new ExtensionsPanel(); + } + + instance->rescan(); + + return *instance; +} + + + +/** + * Constructor + */ +ExtensionsPanel::ExtensionsPanel() : + _showAll(false) +{ + Gtk::ScrolledWindow* scroller = new Gtk::ScrolledWindow(); + + _view.set_editable(false); + + scroller->add(_view); + add(*scroller); + + rescan(); + + show_all_children(); +} + +void ExtensionsPanel::set_full(bool full) +{ + if ( full != _showAll ) { + _showAll = full; + rescan(); + } +} + +void ExtensionsPanel::listCB( Inkscape::Extension::Extension * in_plug, gpointer in_data ) +{ + ExtensionsPanel * self = (ExtensionsPanel*)in_data; + + const char* stateStr; + Extension::state_t state = in_plug->get_state(); + switch ( state ) { + case Extension::STATE_LOADED: + { + stateStr = "loaded"; + } + break; + case Extension::STATE_UNLOADED: + { + stateStr = "unloaded"; + } + break; + case Extension::STATE_DEACTIVATED: + { + stateStr = "deactivated"; + } + break; + default: + stateStr = "unknown"; + } + + if ( self->_showAll || in_plug->deactivated() ) { +// gchar* line = g_strdup_printf( " extension %c %c %s |%s|%s|", +// (in_plug->loaded() ? 'X' : '-'), +// (in_plug->deactivated() ? 'X' : '-'), +// stateStr, in_plug->get_id(), +// in_plug->get_name() ); + gchar* line = g_strdup_printf( "%s %s\n \"%s\"", stateStr, in_plug->get_name(), in_plug->get_id() ); + + self->_view.get_buffer()->insert( self->_view.get_buffer()->end(), line ); + self->_view.get_buffer()->insert( self->_view.get_buffer()->end(), "\n" ); + //g_message( "%s", line ); + } + + + + return; +} + +void ExtensionsPanel::rescan() +{ + _view.get_buffer()->set_text("Extensions:\n"); +// g_message("/------------------"); + + Inkscape::Extension::db.foreach(listCB, (gpointer)this); + +// g_message("\\------------------"); +} + + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape diff --git a/src/dialogs/extensions.h b/src/dialogs/extensions.h new file mode 100644 index 000000000..f4dbada9e --- /dev/null +++ b/src/dialogs/extensions.h @@ -0,0 +1,61 @@ + +#ifndef SEEN_EXTENSIONS_H +#define SEEN_EXTENSIONS_H +/* + * A simple dialog for previewing icon representation. + * + * Authors: + * Jon A. Cruz + * + * Copyright (C) 2005 The Inkscape Organization + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include "ui/widget/panel.h" + +namespace Inkscape { + namespace Extension { + class Extension; + } +} + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + +/** + * A panel that displays information about extensions. + */ +class ExtensionsPanel : public Inkscape::UI::Widget::Panel +{ +public: + ExtensionsPanel(); + + static ExtensionsPanel& getInstance(); + + void set_full(bool full); + +private: + ExtensionsPanel(ExtensionsPanel const &); // no copy + ExtensionsPanel &operator=(ExtensionsPanel const &); // no assign + + static ExtensionsPanel* instance; + + static void listCB( Inkscape::Extension::Extension * in_plug, gpointer in_data ); + + void rescan(); + + bool _showAll; + Gtk::TextView _view; +}; + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + +#endif // SEEN_EXTENSIONS_H diff --git a/src/dialogs/filedialog-win32.cpp b/src/dialogs/filedialog-win32.cpp new file mode 100644 index 000000000..ca9ce5f6c --- /dev/null +++ b/src/dialogs/filedialog-win32.cpp @@ -0,0 +1,381 @@ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "filedialog.h" + +//#include "extension/internal/win32.h" + +#include + +#include + +#include +#include + +#define UNSAFE_SCRATCH_BUFFER_SIZE 4096 + +namespace Inkscape +{ +namespace UI +{ +namespace Dialogs +{ + +/*################################# +# U T I L I T Y +#################################*/ +static gboolean +win32_is_os_wide() +{ + static gboolean initialized = FALSE; + static gboolean is_wide = FALSE; + static OSVERSIONINFOA osver; + + if ( !initialized ) + { + BOOL result; + + initialized = TRUE; + + memset (&osver, 0, sizeof(OSVERSIONINFOA)); + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + result = GetVersionExA (&osver); + if (result) + { + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) + is_wide = TRUE; + } + // If we can't even call to get the version, fall back to ANSI API + } + + return is_wide; +} + +/*################################# +# F I L E O P E N +#################################*/ + +struct FileOpenNativeData_def { + char *dir; + FileDialogType fileTypes; + char *title; +}; + +FileOpenDialog::FileOpenDialog( + const char *dir, FileDialogType fileTypes, const char *title) { + + nativeData = (FileOpenNativeData *) + g_malloc(sizeof (FileOpenNativeData)); + if ( !nativeData ) { + // do we want exceptions? + return; + } + + if ( !dir ) + dir = ""; + nativeData->dir = g_strdup(dir); + nativeData->fileTypes = fileTypes; + nativeData->title = g_strdup(title); + + extension = NULL; + filename = NULL; +} + + + +FileOpenDialog::~FileOpenDialog() { + + //do any cleanup here + if ( nativeData ) { + g_free(nativeData->dir); + g_free(nativeData->title); + g_free(nativeData); + } + + if (filename) g_free(filename); + extension = NULL; +} + + + +bool +FileOpenDialog::show() { + + if ( !nativeData ) { + //error + return FALSE; + } + + gint retval = FALSE; + + + //Jon's UNICODE patch + if ( win32_is_os_wide() ) { + gunichar2 fnbufW[UNSAFE_SCRATCH_BUFFER_SIZE * sizeof(gunichar2)] = {0}; + gunichar2* dirW = + g_utf8_to_utf16( nativeData->dir, -1, NULL, NULL, NULL ); + gunichar2 *filterW = (gunichar2 *) L""; + if ( nativeData->fileTypes == SVG_TYPES ) + filterW = (gunichar2 *) L"SVG files\0*.svg;*.svgz\0All files\0*\0"; + else if ( nativeData->fileTypes == IMPORT_TYPES ) + filterW = (gunichar2 *) L"Image files\0*.svg;*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tiff;*.xpm\0" + L"SVG files\0*.svg\0" + L"All files\0*\0"; + gunichar2* titleW = + g_utf8_to_utf16( nativeData->title, -1, NULL, NULL, NULL ); + OPENFILENAMEW ofn = { + sizeof (OPENFILENAMEW), + NULL, // hwndOwner + NULL, // hInstance + (const WCHAR *)filterW, // lpstrFilter + NULL, // lpstrCustomFilter + 0, // nMaxCustFilter + 1, // nFilterIndex + (WCHAR *)fnbufW, // lpstrFile + sizeof (fnbufW) / sizeof(WCHAR), // nMaxFile + NULL, // lpstrFileTitle + 0, // nMaxFileTitle + (const WCHAR *)dirW, // lpstrInitialDir + (const WCHAR *)titleW, // lpstrTitle + OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, // Flags + 0, // nFileOffset + 0, // nFileExtension + NULL, // lpstrDefExt + 0, // lCustData + NULL, // lpfnHook + NULL // lpTemplateName + }; + + retval = GetOpenFileNameW (&ofn); + if (retval) + filename = g_utf16_to_utf8( fnbufW, -1, NULL, NULL, NULL ); + + g_free( dirW ); + g_free( titleW ); + + } else { + gchar *dir = nativeData->dir; + gchar *title = nativeData->title; + gchar fnbuf[UNSAFE_SCRATCH_BUFFER_SIZE] = {0}; + + gchar *filter = ""; + if ( nativeData->fileTypes == SVG_TYPES ) + filter = "SVG files\0*.svg;*.svgz\0All files\0*\0"; + else if ( nativeData->fileTypes == IMPORT_TYPES ) + filter = "Image files\0*.svg;*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tiff;*.xpm\0" + "SVG files\0*.svg\0" + "All files\0*\0"; + + OPENFILENAMEA ofn = { + sizeof (OPENFILENAMEA), + NULL, // hwndOwner + NULL, // hInstance + (const CHAR *)filter, // lpstrFilter + NULL, // lpstrCustomFilter + 0, // nMaxCustFilter + 1, // nFilterIndex + fnbuf, // lpstrFile + sizeof (fnbuf), // nMaxFile + NULL, // lpstrFileTitle + 0, // nMaxFileTitle + (const CHAR *)dir, // lpstrInitialDir + (const CHAR *)title, // lpstrTitle + OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, // Flags + 0, // nFileOffset + 0, // nFileExtension + NULL, // lpstrDefExt + 0, // lCustData + NULL, // lpfnHook + NULL // lpTemplateName + }; + + retval = GetOpenFileNameA (&ofn); + if ( retval ) { + filename = g_strdup( fnbuf ); + /* ### We need to try something like this instead: + GError *err = NULL; + filename = g_filename_to_utf8(fnbuf, -1, NULL, NULL, &err); + if ( !filename && err ) { + g_warning("Charset conversion in show()[%d]%s\n", + err->code, err->message); + } + */ + } + } + + if ( !retval ) { + //int errcode = CommDlgExtendedError(); + return FALSE; + } + + return TRUE; + +} + + + +/*################################# +# F I L E S A V E +#################################*/ + +struct FileSaveNativeData_def { + OPENFILENAME ofn; + gchar filter[UNSAFE_SCRATCH_BUFFER_SIZE]; + gchar fnbuf[4096]; +}; + + + +FileSaveDialog::FileSaveDialog( + const char *dir, FileDialogType fileTypes, const char *title, const char * default_key) { + + nativeData = (FileSaveNativeData *) + g_malloc(sizeof (FileSaveNativeData)); + if ( !nativeData ) { + //do we want exceptions? + return; + } + + extension = NULL; + filename = NULL; + + int default_item = 0; + + GSList* extension_list = Inkscape::Extension::db.get_output_list(); + g_assert (extension_list != NULL); + + /* Make up the filter string for the save dialogue using the list + ** of available output types. + */ + + gchar *p = nativeData->filter; + int N = UNSAFE_SCRATCH_BUFFER_SIZE; + + int n = 1; + for (GSList* i = g_slist_next (extension_list); i != NULL; i = g_slist_next(i)) { + + Inkscape::Extension::DB::IOExtensionDescription* d = + reinterpret_cast(i->data); + + if (!d->sensitive) + continue; + + int w = snprintf (p, N, "%s", d->name); + N -= w + 1; + p += w + 1; + + w = snprintf (p, N, "*"); + N -= w + 1; + p += w + 1; + + g_assert (N >= 0); + + /* Look to see if this extension is the default */ + if (default_key && + d->extension->get_id() && + strcmp (default_key, d->extension->get_id()) == 0) { + default_item = n; + extension = d->extension; + } + + n++; + } + + *p = '\0'; + + nativeData->fnbuf[0] = '\0'; + + if (dir) { + /* We must check that dir is not something like + ** c:\foo\ (ie with a trailing \). If it is, + ** GetSaveFileName will give an error. + */ + int n = strlen(dir); + if (n > 0 && dir[n - 1] != '\\') { + strncpy(nativeData->fnbuf, dir, sizeof(nativeData->fnbuf)); + } + } + + OPENFILENAME ofn = { + sizeof (OPENFILENAME), + NULL, // hwndOwner + NULL, // hInstance + nativeData->filter, // lpstrFilter + NULL, // lpstrCustomFilter + 0, // nMaxCustFilter + default_item, // nFilterIndex + nativeData->fnbuf, // lpstrFile + sizeof (nativeData->fnbuf), // nMaxFile + NULL, // lpstrFileTitle + 0, // nMaxFileTitle + (const CHAR *)dir, // lpstrInitialDir + (const CHAR *)title, // lpstrTitle + OFN_HIDEREADONLY | OFN_NOCHANGEDIR, // Flags + 0, // nFileOffset + 0, // nFileExtension + NULL, // lpstrDefExt + 0, // lCustData + NULL, // lpfnHook + NULL // lpTemplateName + }; + + nativeData->ofn = ofn; +} + +FileSaveDialog::~FileSaveDialog() { + + //do any cleanup here + g_free(nativeData); + if (filename) g_free(filename); + extension = NULL; +} + +bool +FileSaveDialog::show() { + + if (!nativeData) + return FALSE; + int retval = GetSaveFileName (&(nativeData->ofn)); + if (!retval) { + //int errcode = CommDlgExtendedError(); + return FALSE; + } + + GSList* extension_list = Inkscape::Extension::db.get_output_list(); + g_assert (extension_list != NULL); + + /* Work out which extension corresponds to the user's choice of + ** file type. + */ + int n = nativeData->ofn.nFilterIndex - 1; + GSList* i = g_slist_next (extension_list); + + while (n > 0 && i) { + n--; + i = g_slist_next(i); + } + + Inkscape::Extension::DB::IOExtensionDescription* d = + reinterpret_cast(i->data); + + extension = d->extension; + + filename = g_strdup (nativeData->fnbuf); + return TRUE; +} + + + + + + + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + + diff --git a/src/dialogs/filedialog.cpp b/src/dialogs/filedialog.cpp new file mode 100644 index 000000000..bf9eaceb5 --- /dev/null +++ b/src/dialogs/filedialog.cpp @@ -0,0 +1,1439 @@ +/* + * Implementation of the file dialog interfaces defined in filedialog.h + * + * Authors: + * Bob Jamison + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 The Inkscape Organization + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + + + +//Temporary ugly hack +//Remove these after the get_filter() calls in +//show() on both classes are fixed +#include + +//Another hack +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prefs-utils.h" +#include +#include +#include +#include +#include "inkscape.h" +#include "svg-view-widget.h" +#include "filedialog.h" + +#undef INK_DUMP_FILENAME_CONV + +#ifdef INK_DUMP_FILENAME_CONV +void dump_str( const gchar* str, const gchar* prefix ); +void dump_ustr( const Glib::ustring& ustr ); +#endif + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +void FileDialogExtensionToPattern (Glib::ustring &pattern, gchar * in_file_extension); + +/*######################################################################### +### SVG Preview Widget +#########################################################################*/ +/** + * Simple class for displaying an SVG file in the "preview widget." + * Currently, this is just a wrapper of the sp_svg_view Gtk widget. + * Hopefully we will eventually replace with a pure Gtkmm widget. + */ +class SVGPreview : public Gtk::VBox +{ +public: + SVGPreview(); + ~SVGPreview(); + + bool setDocument(SPDocument *doc); + + bool setFileName(Glib::ustring &fileName); + + bool setFromMem(char const *xmlBuffer); + + bool set(Glib::ustring &fileName, int dialogType); + + bool setURI(URI &uri); + + /** + * Show image embedded in SVG + */ + void showImage(Glib::ustring &fileName); + + /** + * Show the "No preview" image + */ + void showNoPreview(); + + /** + * Show the "Too large" image + */ + void showTooLarge(long fileLength); + +private: + /** + * The svg document we are currently showing + */ + SPDocument *document; + + /** + * The sp_svg_view widget + */ + GtkWidget *viewerGtk; + + /** + * are we currently showing the "no preview" image? + */ + bool showingNoPreview; + +}; + + +bool SVGPreview::setDocument(SPDocument *doc) +{ + if (document) + sp_document_unref(document); + + sp_document_ref(doc); + document = doc; + + //This should remove it from the box, and free resources + if (viewerGtk) { + gtk_widget_destroy(viewerGtk); + } + + viewerGtk = sp_svg_view_widget_new(doc); + GtkWidget *vbox = (GtkWidget *)gobj(); + gtk_box_pack_start(GTK_BOX(vbox), viewerGtk, TRUE, TRUE, 0); + gtk_widget_show(viewerGtk); + + return true; +} + +bool SVGPreview::setFileName(Glib::ustring &theFileName) +{ + Glib::ustring fileName = theFileName; + + fileName = Glib::filename_to_utf8(fileName); + + SPDocument *doc = sp_document_new (fileName.c_str(), 0); + if (!doc) { + g_warning("SVGView: error loading document '%s'\n", fileName.c_str()); + return false; + } + + setDocument(doc); + + sp_document_unref(doc); + + return true; +} + + + +bool SVGPreview::setFromMem(char const *xmlBuffer) +{ + if (!xmlBuffer) + return false; + + gint len = (gint)strlen(xmlBuffer); + SPDocument *doc = sp_document_new_from_mem(xmlBuffer, len, 0); + if (!doc) { + g_warning("SVGView: error loading buffer '%s'\n",xmlBuffer); + return false; + } + + setDocument(doc); + + sp_document_unref(doc); + + return true; +} + + + +void SVGPreview::showImage(Glib::ustring &theFileName) +{ + Glib::ustring fileName = theFileName; + + + /*##################################### + # LET'S HAVE SOME FUN WITH SVG! + # Instead of just loading an image, why + # don't we make a lovely little svg and + # display it nicely? + #####################################*/ + + //Arbitrary size of svg doc -- rather 'portrait' shaped + gint previewWidth = 400; + gint previewHeight = 600; + + //Get some image info. Smart pointer does not need to be deleted + Glib::RefPtr img = Gdk::Pixbuf::create_from_file(fileName); + gint imgWidth = img->get_width(); + gint imgHeight = img->get_height(); + + //Find the minimum scale to fit the image inside the preview area + double scaleFactorX = (0.9 *(double)previewWidth) / ((double)imgWidth); + double scaleFactorY = (0.9 *(double)previewHeight) / ((double)imgHeight); + double scaleFactor = scaleFactorX; + if (scaleFactorX > scaleFactorY) + scaleFactor = scaleFactorY; + + //Now get the resized values + gint scaledImgWidth = (int) (scaleFactor * (double)imgWidth); + gint scaledImgHeight = (int) (scaleFactor * (double)imgHeight); + + //center the image on the area + gint imgX = (previewWidth - scaledImgWidth) / 2; + gint imgY = (previewHeight - scaledImgHeight) / 2; + + //wrap a rectangle around the image + gint rectX = imgX-1; + gint rectY = imgY-1; + gint rectWidth = scaledImgWidth +2; + gint rectHeight = scaledImgHeight+2; + + //Our template. Modify to taste + gchar const *xformat = + "\n" + "\n" + "\n" + "\n" + "\n" + "%d x %d\n" + "\n\n"; + + //if (!Glib::get_charset()) //If we are not utf8 + fileName = Glib::filename_to_utf8(fileName); + + //Fill in the template + /* FIXME: Do proper XML quoting for fileName. */ + gchar *xmlBuffer = g_strdup_printf(xformat, + previewWidth, previewHeight, + imgX, imgY, scaledImgWidth, scaledImgHeight, + fileName.c_str(), + rectX, rectY, rectWidth, rectHeight, + imgWidth, imgHeight); + + //g_message("%s\n", xmlBuffer); + + //now show it! + setFromMem(xmlBuffer); + g_free(xmlBuffer); +} + + + +void SVGPreview::showNoPreview() +{ + //Are we already showing it? + if (showingNoPreview) + return; + + //Arbitrary size of svg doc -- rather 'portrait' shaped + gint previewWidth = 300; + gint previewHeight = 600; + + //Our template. Modify to taste + gchar const *xformat = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " \n" + "%s\n" + "\n\n"; + + //Fill in the template + gchar *xmlBuffer = g_strdup_printf(xformat, + previewWidth, previewHeight, _("No preview")); + + //g_message("%s\n", xmlBuffer); + + //now show it! + setFromMem(xmlBuffer); + g_free(xmlBuffer); + showingNoPreview = true; + +} + +void SVGPreview::showTooLarge(long fileLength) +{ + + //Arbitrary size of svg doc -- rather 'portrait' shaped + gint previewWidth = 300; + gint previewHeight = 600; + + //Our template. Modify to taste + gchar const *xformat = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "%5.1f MB\n" + "%s\n" + "\n\n"; + + //Fill in the template + double floatFileLength = ((double)fileLength) / 1048576.0; + //printf("%ld %f\n", fileLength, floatFileLength); + gchar *xmlBuffer = g_strdup_printf(xformat, + previewWidth, previewHeight, floatFileLength, + _("too large for preview")); + + //g_message("%s\n", xmlBuffer); + + //now show it! + setFromMem(xmlBuffer); + g_free(xmlBuffer); + +} + +static bool +hasSuffix(Glib::ustring &str, Glib::ustring &ext) +{ + int strLen = str.length(); + int extLen = ext.length(); + if (extLen > strLen) + { + return false; + } + int strpos = strLen-1; + for (int extpos = extLen-1 ; extpos>=0 ; extpos--, strpos--) + { + Glib::ustring::value_type ch = str[strpos]; + if (ch != ext[extpos]) + { + if ( ((ch & 0xff80) != 0) || + static_cast( g_ascii_tolower( static_cast(0x07f & ch) ) ) != ext[extpos] ) + { + return false; + } + } + } + return true; +} + + +/** + * Return true if the image is loadable by Gdk, else false + */ +static bool +isValidImageFile(Glib::ustring &fileName) +{ + std::vectorformats = Gdk::Pixbuf::get_formats(); + for (unsigned int i=0; iextensions = format.get_extensions(); + for (unsigned int j=0; j 0x150000L) + { + showingNoPreview = false; + showTooLarge(fileLen); + return FALSE; + } + } + + Glib::ustring svg = ".svg"; + Glib::ustring svgz = ".svgz"; + + if ((dialogType == SVG_TYPES || dialogType == IMPORT_TYPES) && + (hasSuffix(fileName, svg) || hasSuffix(fileName, svgz) ) + ) + { + bool retval = setFileName(fileName); + showingNoPreview = false; + return retval; + } + else if (isValidImageFile(fileName)) + { + showImage(fileName); + showingNoPreview = false; + return true; + } + else + { + showNoPreview(); + return false; + } +} + + +SVGPreview::SVGPreview() +{ + if (!INKSCAPE) + inkscape_application_init("",false); + document = NULL; + viewerGtk = NULL; + set_size_request(150,150); + showingNoPreview = false; +} + +SVGPreview::~SVGPreview() +{ + +} + + + + + +/*######################################################################### +### F I L E O P E N +#########################################################################*/ + +/** + * Our implementation class for the FileOpenDialog interface.. + */ +class FileOpenDialogImpl : public FileOpenDialog, public Gtk::FileChooserDialog +{ +public: + FileOpenDialogImpl(char const *dir, + FileDialogType fileTypes, + char const *title); + + virtual ~FileOpenDialogImpl(); + + bool show(); + + Inkscape::Extension::Extension *getSelectionType(); + + gchar *getFilename(); + + Glib::SListHandle getFilenames (); +protected: + + + +private: + + + /** + * What type of 'open' are we? (open, import, place, etc) + */ + FileDialogType dialogType; + + /** + * Our svg preview widget + */ + SVGPreview svgPreview; + + /** + * Callback for seeing if the preview needs to be drawn + */ + void updatePreviewCallback(); + + /** + * Fix to allow the user to type the file name + */ + Gtk::Entry fileNameEntry; + + /** + * Create a filter menu for this type of dialog + */ + void createFilterMenu(); + + /** + * Callback for user input into fileNameEntry + */ + void fileNameEntryChangedCallback(); + + /** + * Callback for user changing which item is selected on the list + */ + void fileSelectedCallback(); + + + /** + * Filter name->extension lookup + */ + std::map extensionMap; + + /** + * The extension to use to write this file + */ + Inkscape::Extension::Extension *extension; + + /** + * Filename that was given + */ + Glib::ustring myFilename; + +}; + + + + + +/** + * Callback for checking if the preview needs to be redrawn + */ +void FileOpenDialogImpl::updatePreviewCallback() +{ + Glib::ustring fileName = get_preview_filename(); + + if (fileName.length() < 1) + return; + + svgPreview.set(fileName, dialogType); +} + + + + + +/** + * Callback for fileNameEntry widget + */ +void FileOpenDialogImpl::fileNameEntryChangedCallback() +{ + Glib::ustring fileName = fileNameEntry.get_text(); + + // TODO remove this leak + fileName = Glib::filename_from_utf8(fileName); + + //g_message("User hit return. Text is '%s'\n", fName.c_str()); + + if (!Glib::path_is_absolute(fileName)) { + //try appending to the current path + // not this way: fileName = get_current_folder() + "/" + fName; + std::vector pathSegments; + pathSegments.push_back( get_current_folder() ); + pathSegments.push_back( fileName ); + fileName = Glib::build_filename(pathSegments); + } + + //g_message("path:'%s'\n", fName.c_str()); + + if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) { + set_current_folder(fileName); + } else if (Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)) { + //dialog with either (1) select a regular file or (2) cd to dir + //simulate an 'OK' + set_filename(fileName); + response(Gtk::RESPONSE_OK); + } +} + + + + + +/** + * Callback for fileNameEntry widget + */ +void FileOpenDialogImpl::fileSelectedCallback() +{ + Glib::ustring fileName = get_filename(); + if (!Glib::get_charset()) //If we are not utf8 + fileName = Glib::filename_to_utf8(fileName); + //g_message("User selected '%s'\n", + // filename().c_str()); + +#ifdef INK_DUMP_FILENAME_CONV + ::dump_ustr( get_filename() ); +#endif + fileNameEntry.set_text(fileName); +} + + + + +void FileOpenDialogImpl::createFilterMenu() +{ + //patterns added dynamically below + Gtk::FileFilter allImageFilter; + allImageFilter.set_name(_("All Images")); + extensionMap[Glib::ustring(_("All Images"))]=NULL; + add_filter(allImageFilter); + + Gtk::FileFilter allFilter; + allFilter.set_name(_("All Files")); + extensionMap[Glib::ustring(_("All Files"))]=NULL; + allFilter.add_pattern("*"); + add_filter(allFilter); + + //patterns added dynamically below + Gtk::FileFilter allInkscapeFilter; + allInkscapeFilter.set_name(_("All Inkscape Files")); + extensionMap[Glib::ustring(_("All Inkscape Files"))]=NULL; + add_filter(allInkscapeFilter); + + Inkscape::Extension::DB::InputList extension_list; + Inkscape::Extension::db.get_input_list(extension_list); + + for (Inkscape::Extension::DB::InputList::iterator current_item = extension_list.begin(); + current_item != extension_list.end(); current_item++) + { + Inkscape::Extension::Input * imod = *current_item; + + // FIXME: would be nice to grey them out instead of not listing them + if (imod->deactivated()) continue; + + Glib::ustring upattern("*"); + FileDialogExtensionToPattern (upattern, imod->get_extension()); + + Gtk::FileFilter filter; + Glib::ustring uname(_(imod->get_filetypename())); + filter.set_name(uname); + filter.add_pattern(upattern); + add_filter(filter); + extensionMap[uname] = imod; + + //g_message("ext %s:%s '%s'\n", ioext->name, ioext->mimetype, upattern.c_str()); + allInkscapeFilter.add_pattern(upattern); + if ( strncmp("image", imod->get_mimetype(), 5)==0 ) + allImageFilter.add_pattern(upattern); + } + + return; +} + + + +/** + * Constructor. Not called directly. Use the factory. + */ +FileOpenDialogImpl::FileOpenDialogImpl(char const *dir, + FileDialogType fileTypes, + char const *title) : + Gtk::FileChooserDialog(Glib::ustring(title)) +{ + + + /* One file at a time */ + /* And also Multiple Files */ + set_select_multiple(true); + + /* Initalize to Autodetect */ + extension = NULL; + /* No filename to start out with */ + myFilename = ""; + + /* Set our dialog type (open, import, etc...)*/ + dialogType = fileTypes; + + + /* Set the pwd and/or the filename */ + if (dir != NULL) + { + Glib::ustring udir(dir); + Glib::ustring::size_type len = udir.length(); + // leaving a trailing backslash on the directory name leads to the infamous + // double-directory bug on win32 + if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1); + set_current_folder(udir.c_str()); + } + + //###### Add the file types menu + createFilterMenu(); + + //###### Add a preview widget + set_preview_widget(svgPreview); + set_preview_widget_active(true); + set_use_preview_label (false); + + //Catch selection-changed events, so we can adjust the text widget + signal_update_preview().connect( + sigc::mem_fun(*this, &FileOpenDialogImpl::updatePreviewCallback) ); + + + //###### Add a text entry bar, and tie it to file chooser events + fileNameEntry.set_text(get_current_folder()); + set_extra_widget(fileNameEntry); + fileNameEntry.grab_focus(); + + //Catch when user hits [return] on the text field + fileNameEntry.signal_activate().connect( + sigc::mem_fun(*this, &FileOpenDialogImpl::fileNameEntryChangedCallback) ); + + //Catch selection-changed events, so we can adjust the text widget + signal_selection_changed().connect( + sigc::mem_fun(*this, &FileOpenDialogImpl::fileSelectedCallback) ); + + add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + +} + + + + + +/** + * Public factory. Called by file.cpp, among others. + */ +FileOpenDialog *FileOpenDialog::create(char const *path, + FileDialogType fileTypes, + char const *title) +{ + FileOpenDialog *dialog = new FileOpenDialogImpl(path, fileTypes, title); + return dialog; +} + + + + +/** + * Destructor + */ +FileOpenDialogImpl::~FileOpenDialogImpl() +{ + +} + + +/** + * Show this dialog modally. Return true if user hits [OK] + */ +bool +FileOpenDialogImpl::show() +{ + set_current_folder(get_current_folder()); //hack to force initial dir listing + set_modal (TRUE); //Window + sp_transientize((GtkWidget *)gobj()); //Make transient + gint b = run(); //Dialog + hide(); + + if (b == Gtk::RESPONSE_OK) + { + //This is a hack, to avoid the warning messages that + //Gtk::FileChooser::get_filter() returns + //should be: Gtk::FileFilter *filter = get_filter(); + GtkFileChooser *gtkFileChooser = Gtk::FileChooser::gobj(); + GtkFileFilter *filter = gtk_file_chooser_get_filter(gtkFileChooser); + if (filter) + { + //Get which extension was chosen, if any + extension = extensionMap[gtk_file_filter_get_name(filter)]; + } + myFilename = get_filename(); + return TRUE; + } + else + { + return FALSE; + } +} + + + + +/** + * Get the file extension type that was selected by the user. Valid after an [OK] + */ +Inkscape::Extension::Extension * +FileOpenDialogImpl::getSelectionType() +{ + return extension; +} + + +/** + * Get the file name chosen by the user. Valid after an [OK] + */ +gchar * +FileOpenDialogImpl::getFilename (void) +{ + return g_strdup(myFilename.c_str()); +} + + +/** + * To Get Multiple filenames selected at-once. + */ +Glib::SListHandleFileOpenDialogImpl::getFilenames() +{ + return get_filenames(); +} + + + + + + +/*######################################################################### +# F I L E S A V E +#########################################################################*/ + +class FileType +{ + public: + FileType() {} + ~FileType() {} + Glib::ustring name; + Glib::ustring pattern; + Inkscape::Extension::Extension *extension; +}; + +/** + * Our implementation of the FileSaveDialog interface. + */ +class FileSaveDialogImpl : public FileSaveDialog, public Gtk::FileChooserDialog +{ + +public: + FileSaveDialogImpl(char const *dir, + FileDialogType fileTypes, + char const *title, + char const *default_key); + + virtual ~FileSaveDialogImpl(); + + bool show(); + + Inkscape::Extension::Extension *getSelectionType(); + + gchar *getFilename(); + + +private: + + /** + * What type of 'open' are we? (save, export, etc) + */ + FileDialogType dialogType; + + /** + * Our svg preview widget + */ + SVGPreview svgPreview; + + /** + * Fix to allow the user to type the file name + */ + Gtk::Entry *fileNameEntry; + + /** + * Callback for seeing if the preview needs to be drawn + */ + void updatePreviewCallback(); + + + + /** + * Allow the specification of the output file type + */ + Gtk::HBox fileTypeBox; + + /** + * Allow the specification of the output file type + */ + Gtk::ComboBoxText fileTypeComboBox; + + + /** + * Data mirror of the combo box + */ + std::vector fileTypes; + + //# Child widgets + Gtk::CheckButton fileTypeCheckbox; + + + /** + * Callback for user input into fileNameEntry + */ + void fileTypeChangedCallback(); + + /** + * Create a filter menu for this type of dialog + */ + void createFileTypeMenu(); + + + bool append_extension; + + /** + * The extension to use to write this file + */ + Inkscape::Extension::Extension *extension; + + /** + * Callback for user input into fileNameEntry + */ + void fileNameEntryChangedCallback(); + + /** + * Filename that was given + */ + Glib::ustring myFilename; +}; + + + + + + +/** + * Callback for checking if the preview needs to be redrawn + */ +void FileSaveDialogImpl::updatePreviewCallback() +{ + Glib::ustring fileName = get_preview_filename(); + if (!fileName.c_str()) + return; + bool retval = svgPreview.set(fileName, dialogType); + set_preview_widget_active(retval); +} + + + +/** + * Callback for fileNameEntry widget + */ +void FileSaveDialogImpl::fileNameEntryChangedCallback() +{ + if (!fileNameEntry) + return; + + Glib::ustring fileName = fileNameEntry->get_text(); + if (!Glib::get_charset()) //If we are not utf8 + fileName = Glib::filename_to_utf8(fileName); + + //g_message("User hit return. Text is '%s'\n", fileName.c_str()); + + if (!Glib::path_is_absolute(fileName)) { + //try appending to the current path + // not this way: fileName = get_current_folder() + "/" + fileName; + std::vector pathSegments; + pathSegments.push_back( get_current_folder() ); + pathSegments.push_back( fileName ); + fileName = Glib::build_filename(pathSegments); + } + + //g_message("path:'%s'\n", fileName.c_str()); + + if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) { + set_current_folder(fileName); + } else if (/*Glib::file_test(fileName, Glib::FILE_TEST_IS_REGULAR)*/1) { + //dialog with either (1) select a regular file or (2) cd to dir + //simulate an 'OK' + set_filename(fileName); + response(Gtk::RESPONSE_OK); + } +} + + + +/** + * Callback for fileNameEntry widget + */ +void FileSaveDialogImpl::fileTypeChangedCallback() +{ + int sel = fileTypeComboBox.get_active_row_number(); + if (sel<0 || sel >= (int)fileTypes.size()) + return; + FileType type = fileTypes[sel]; + //g_message("selected: %s\n", type.name.c_str()); + Gtk::FileFilter filter; + filter.add_pattern(type.pattern); + set_filter(filter); +} + + + +void FileSaveDialogImpl::createFileTypeMenu() +{ + Inkscape::Extension::DB::OutputList extension_list; + Inkscape::Extension::db.get_output_list(extension_list); + + for (Inkscape::Extension::DB::OutputList::iterator current_item = extension_list.begin(); + current_item != extension_list.end(); current_item++) + { + Inkscape::Extension::Output * omod = *current_item; + + // FIXME: would be nice to grey them out instead of not listing them + if (omod->deactivated()) continue; + + FileType type; + type.name = (_(omod->get_filetypename())); + type.pattern = "*"; + FileDialogExtensionToPattern (type.pattern, omod->get_extension()); + type.extension= omod; + fileTypeComboBox.append_text(type.name); + fileTypes.push_back(type); + } + + //#Let user choose + FileType guessType; + guessType.name = _("Guess from extension"); + guessType.pattern = "*"; + guessType.extension = NULL; + fileTypeComboBox.append_text(guessType.name); + fileTypes.push_back(guessType); + + + fileTypeComboBox.set_active(0); + fileTypeChangedCallback(); //call at least once to set the filter +} + + +void findEntryWidgets(Gtk::Container *parent, std::vector &result) +{ + if (!parent) + return; + std::vector children = parent->get_children(); + for (unsigned int i=0; igobj(); + if (GTK_IS_ENTRY(wid)) + result.push_back((Gtk::Entry *)child); + else if (GTK_IS_CONTAINER(wid)) + findEntryWidgets((Gtk::Container *)child, result); + } + +} + +void findExpanderWidgets(Gtk::Container *parent, std::vector &result) +{ + if (!parent) + return; + std::vector children = parent->get_children(); + for (unsigned int i=0; igobj(); + if (GTK_IS_EXPANDER(wid)) + result.push_back((Gtk::Expander *)child); + else if (GTK_IS_CONTAINER(wid)) + findExpanderWidgets((Gtk::Container *)child, result); + } + +} + + +/** + * Constructor + */ +FileSaveDialogImpl::FileSaveDialogImpl(char const *dir, + FileDialogType fileTypes, + char const *title, + char const *default_key) : + Gtk::FileChooserDialog(Glib::ustring(title), + Gtk::FILE_CHOOSER_ACTION_SAVE) +{ + append_extension = (bool)prefs_get_int_attribute("dialogs.save_as", "append_extension", 1); + + /* One file at a time */ + set_select_multiple(false); + + /* Initalize to Autodetect */ + extension = NULL; + /* No filename to start out with */ + myFilename = ""; + + /* Set our dialog type (save, export, etc...)*/ + dialogType = fileTypes; + + /* Set the pwd and/or the filename */ + if (dir != NULL) + { + Glib::ustring udir(dir); + Glib::ustring::size_type len = udir.length(); + // leaving a trailing backslash on the directory name leads to the infamous + // double-directory bug on win32 + if (len != 0 && udir[len - 1] == '\\') udir.erase(len - 1); + set_current_folder(udir.c_str()); + } + + //###### Add the file types menu + //createFilterMenu(); + + //###### Do we want the .xxx extension automatically added? + fileTypeCheckbox.set_label(Glib::ustring(_("Append filename extension automatically"))); + fileTypeCheckbox.set_active(append_extension); + + fileTypeBox.pack_start(fileTypeCheckbox); + createFileTypeMenu(); + fileTypeComboBox.set_size_request(200,40); + fileTypeComboBox.signal_changed().connect( + sigc::mem_fun(*this, &FileSaveDialogImpl::fileTypeChangedCallback) ); + + fileTypeBox.pack_start(fileTypeComboBox); + + set_extra_widget(fileTypeBox); + //get_vbox()->pack_start(fileTypeBox, false, false, 0); + //get_vbox()->reorder_child(fileTypeBox, 2); + + //###### Add a preview widget + set_preview_widget(svgPreview); + set_preview_widget_active(true); + set_use_preview_label (false); + + //Catch selection-changed events, so we can adjust the text widget + signal_update_preview().connect( + sigc::mem_fun(*this, &FileSaveDialogImpl::updatePreviewCallback) ); + + + //Let's do some customization + fileNameEntry = NULL; + Gtk::Container *cont = get_toplevel(); + std::vector entries; + findEntryWidgets(cont, entries); + //g_message("Found %d entry widgets\n", entries.size()); + if (entries.size() >=1 ) + { + //Catch when user hits [return] on the text field + fileNameEntry = entries[0]; + fileNameEntry->signal_activate().connect( + sigc::mem_fun(*this, &FileSaveDialogImpl::fileNameEntryChangedCallback) ); + } + + //Let's do more customization + std::vector expanders; + findExpanderWidgets(cont, expanders); + //g_message("Found %d expander widgets\n", expanders.size()); + if (expanders.size() >=1 ) + { + //Always show the file list + Gtk::Expander *expander = expanders[0]; + expander->set_expanded(true); + } + + + //if (extension == NULL) + // checkbox.set_sensitive(FALSE); + + add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + show_all_children(); +} + + + +/** + * Public factory method. Used in file.cpp + */ +FileSaveDialog *FileSaveDialog::create(char const *path, + FileDialogType fileTypes, + char const *title, + char const *default_key) +{ + FileSaveDialog *dialog = new FileSaveDialogImpl(path, fileTypes, title, default_key); + return dialog; +} + + + + + +/** + * Destructor + */ +FileSaveDialogImpl::~FileSaveDialogImpl() +{ +} + + + + +/** + * Show this dialog modally. Return true if user hits [OK] + */ +bool +FileSaveDialogImpl::show() +{ + set_current_folder(get_current_folder()); //hack to force initial dir listing + set_modal (TRUE); //Window + sp_transientize((GtkWidget *)gobj()); //Make transient + gint b = run(); //Dialog + hide(); + + if (b == Gtk::RESPONSE_OK) + { + int sel = fileTypeComboBox.get_active_row_number (); + if (sel>=0 && sel< (int)fileTypes.size()) + { + FileType &type = fileTypes[sel]; + extension = type.extension; + } + myFilename = get_filename(); + + /* + + // FIXME: Why do we have more code + + append_extension = checkbox.get_active(); + prefs_set_int_attribute("dialogs.save_as", "append_extension", append_extension); + prefs_set_string_attribute("dialogs.save_as", "default", + ( extension != NULL ? extension->get_id() : "" )); + */ + return TRUE; + } + else + { + return FALSE; + } +} + + +/** + * Get the file extension type that was selected by the user. Valid after an [OK] + */ +Inkscape::Extension::Extension * +FileSaveDialogImpl::getSelectionType() +{ + return extension; +} + + +/** + * Get the file name chosen by the user. Valid after an [OK] + */ +gchar * +FileSaveDialogImpl::getFilename() +{ + return g_strdup(myFilename.c_str()); +} + +/** + \brief A quick function to turn a standard extension into a searchable + pattern for the file dialogs + \param pattern The patter that the extension should be written to + \param in_file_extension The C string that represents the extension + + This function just goes through the string, and takes all characters + and puts a [] so that both are searched and shown in + the file dialog. This function edits the pattern string to make + this happen. +*/ +void +FileDialogExtensionToPattern (Glib::ustring &pattern, gchar * in_file_extension) +{ + Glib::ustring tmp(in_file_extension); + + for ( guint i = 0; i < tmp.length(); i++ ) { + Glib::ustring::value_type ch = tmp.at(i); + if ( Glib::Unicode::isalpha(ch) ) { + pattern += '['; + pattern += Glib::Unicode::toupper(ch); + pattern += Glib::Unicode::tolower(ch); + pattern += ']'; + } else { + pattern += ch; + } + } +} + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/filedialog.h b/src/dialogs/filedialog.h new file mode 100644 index 000000000..ab3615bf6 --- /dev/null +++ b/src/dialogs/filedialog.h @@ -0,0 +1,172 @@ +#ifndef __FILE_DIALOG_H__ +#define __FILE_DIALOG_H__ + +/** \file Defines classes FileOpenDialog, FileSaveDialog, + * and enums FileDialogType, FileDialogSelectionType. */ + +#include +#include + +namespace Inkscape { + +namespace Extension { +class Extension; +} + +namespace UI { +namespace Dialogs { + + +/** + * Used for setting filters and options, and + * reading them back from user selections. + */ +typedef enum { + SVG_TYPES, + IMPORT_TYPES, + EXPORT_TYPES + } FileDialogType; + +/** + * Used for returning the type selected in a SaveAs + */ +typedef enum { + SVG_NAMESPACE, + SVG_NAMESPACE_WITH_EXTENSIONS + } FileDialogSelectionType; + +/** + * Architecture-specific data + */ +typedef struct FileOpenNativeData_def FileOpenNativeData; + + +/** + * This class provides an implementation-independent API for + * file "Open" dialogs. Using a standard interface obviates the need + * for ugly #ifdefs in file open code + */ +class FileOpenDialog +{ +public: + + + /** + * Constructor .. do not call directly + * @param path the directory where to start searching + * @param fileTypes one of FileDialogTypes + * @param title the title of the dialog + */ + FileOpenDialog() + {}; + + /** + * Factory. + * @param path the directory where to start searching + * @param fileTypes one of FileDialogTypes + * @param title the title of the dialog + */ + static FileOpenDialog *create(const char *path, FileDialogType fileTypes, const char *title); + + + /** + * Destructor. + * Perform any necessary cleanups. + */ + virtual ~FileOpenDialog() {}; + + /** + * Show an OpenFile file selector. + * @return the selected path if user selected one, else NULL + */ + virtual bool show() =0; + + /** + * Return the 'key' (filetype) of the selection, if any + * @return a pointer to a string if successful (which must + * be later freed with g_free(), else NULL. + */ + virtual Inkscape::Extension::Extension * getSelectionType() = 0; + + virtual gchar * getFilename () =0; + + virtual Glib::SListHandle getFilenames () = 0; + +}; //FileOpenDialog + + + + + + +/** + * This class provides an implementation-independent API for + * file "Save" dialogs. + */ +class FileSaveDialog +{ +public: + + /** + * Constructor. Do not call directly . Use the factory. + * @param path the directory where to start searching + * @param fileTypes one of FileDialogTypes + * @param title the title of the dialog + * @param key a list of file types from which the user can select + */ + FileSaveDialog () + {}; + + /** + * Factory. + * @param path the directory where to start searching + * @param fileTypes one of FileDialogTypes + * @param title the title of the dialog + * @param key a list of file types from which the user can select + */ + static FileSaveDialog *create(const char *path, FileDialogType fileTypes, const char *title, const char * default_key); + + + /** + * Destructor. + * Perform any necessary cleanups. + */ + virtual ~FileSaveDialog() {}; + + + /** + * Show an SaveAs file selector. + * @return the selected path if user selected one, else NULL + */ + virtual bool show() =0; + + /** + * Return the 'key' (filetype) of the selection, if any + * @return a pointer to a string if successful (which must + * be later freed with g_free(), else NULL. + */ + virtual Inkscape::Extension::Extension * getSelectionType() = 0; + + virtual gchar * getFilename () =0; + + +}; //FileSaveDialog + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + +#endif /* __FILE_DIALOG_H__ */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/fill-style.cpp b/src/dialogs/fill-style.cpp new file mode 100644 index 000000000..1c151deeb --- /dev/null +++ b/src/dialogs/fill-style.cpp @@ -0,0 +1,546 @@ +#define __SP_FILL_STYLE_C__ + +/** + * \brief Fill style widget + * + * Authors: + * Lauris Kaplinski + * Frank Felfe + * bulia byak + * + * Copyright (C) 1999-2005 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#define noSP_FS_VERBOSE + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// These can be deleted once we sort out the libart dependence. + +#define ART_WIND_RULE_NONZERO 0 + +static void sp_fill_style_widget_construct ( SPWidget *spw, + SPPaintSelector *psel ); + +static void sp_fill_style_widget_modify_selection ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + SPPaintSelector *psel ); + +static void sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw ); + +static void sp_fill_style_widget_change_selection ( SPWidget *spw, + Inkscape::Selection *selection, + SPPaintSelector *psel ); + +static void sp_fill_style_widget_update (SPWidget *spw); + +static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel, + SPPaintSelectorMode mode, + SPWidget *spw ); +static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel, + SPPaintSelectorFillRule mode, + SPWidget *spw ); + +static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw ); +static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw ); + +GtkWidget * +sp_fill_style_widget_new (void) +{ + GtkWidget *spw = sp_widget_new_global (INKSCAPE); + + GtkWidget *vb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vb); + gtk_container_add (GTK_CONTAINER (spw), vb); + + GtkWidget *psel = sp_paint_selector_new (true); // with fillrule selector + gtk_widget_show (psel); + gtk_box_pack_start (GTK_BOX (vb), psel, TRUE, TRUE, 0); + g_object_set_data (G_OBJECT (spw), "paint-selector", psel); + + g_signal_connect ( G_OBJECT (psel), "mode_changed", + G_CALLBACK (sp_fill_style_widget_paint_mode_changed), + spw ); + + g_signal_connect ( G_OBJECT (psel), "dragged", + G_CALLBACK (sp_fill_style_widget_paint_dragged), + spw ); + + g_signal_connect ( G_OBJECT (psel), "changed", + G_CALLBACK (sp_fill_style_widget_paint_changed), + spw ); + + g_signal_connect ( G_OBJECT (psel), "fillrule_changed", + G_CALLBACK (sp_fill_style_widget_fillrule_changed), + spw ); + + + g_signal_connect ( G_OBJECT (spw), "construct", + G_CALLBACK (sp_fill_style_widget_construct), psel); + +//FIXME: switch these from spw signals to global inkscape object signals; spw just retranslates +//those anyway; then eliminate spw + g_signal_connect ( G_OBJECT (spw), "modify_selection", + G_CALLBACK (sp_fill_style_widget_modify_selection), psel); + + g_signal_connect ( G_OBJECT (spw), "change_selection", + G_CALLBACK (sp_fill_style_widget_change_selection), psel); + + g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_fill_style_widget_change_subselection), spw); + + sp_fill_style_widget_update (SP_WIDGET (spw)); + + return spw; + +} // end of sp_fill_style_widget_new() + + + +static void +sp_fill_style_widget_construct ( SPWidget *spw, SPPaintSelector *psel ) +{ + +#ifdef SP_FS_VERBOSE + g_print ( "Fill style widget constructed: inkscape %p repr %p\n", + spw->inkscape, spw->repr ); +#endif + if (spw->inkscape) { + + sp_fill_style_widget_update (spw); + + } + +} // end of sp_fill_style_widget_construct() + +static void +sp_fill_style_widget_modify_selection ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + SPPaintSelector *psel ) +{ + if (flags & ( SP_OBJECT_MODIFIED_FLAG | + SP_OBJECT_PARENT_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG) ) + { + sp_fill_style_widget_update (spw); + } +} + +static void +sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, + SPDesktop *desktop, + SPWidget *spw ) +{ + sp_fill_style_widget_update (spw); +} + +static void +sp_fill_style_widget_change_selection ( SPWidget *spw, + Inkscape::Selection *selection, + SPPaintSelector *psel ) +{ + sp_fill_style_widget_update (spw); +} + +/** +* \param sel Selection to use, or NULL. +*/ +static void +sp_fill_style_widget_update (SPWidget *spw) +{ + if (g_object_get_data (G_OBJECT (spw), "update")) + return; + + if (g_object_get_data (G_OBJECT (spw), "local")) { + g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag + return; + } + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE)); + + SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection + int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL); + + switch (result) { + case QUERY_STYLE_NOTHING: + { + /* No paint at all */ + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY); + break; + } + + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector + case QUERY_STYLE_MULTIPLE_SAME: + { + SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true); + sp_paint_selector_set_mode (psel, pselmode); + + sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO? + SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD); + + if (query->fill.set && query->fill.type == SP_PAINT_TYPE_COLOR) { + gfloat d[3]; + sp_color_get_rgb_floatv (&query->fill.value.color, d); + SPColor color; + sp_color_set_rgb_float (&color, d[0], d[1], d[2]); + sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value)); + + } else if (query->fill.set && query->fill.type == SP_PAINT_TYPE_PAINTSERVER) { + + SPPaintServer *server = SP_STYLE_FILL_SERVER (query); + + if (SP_IS_LINEARGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_linear (psel, vector); + + SPLinearGradient *lg = SP_LINEARGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (lg), + SP_GRADIENT_SPREAD (lg)); + } else if (SP_IS_RADIALGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_radial (psel, vector); + + SPRadialGradient *rg = SP_RADIALGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (rg), + SP_GRADIENT_SPREAD (rg)); + } else if (SP_IS_PATTERN (server)) { + SPPattern *pat = pattern_getroot (SP_PATTERN (server)); + sp_update_pattern_list (psel, pat); + } + } + break; + } + + case QUERY_STYLE_MULTIPLE_DIFFERENT: + { + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + break; + } + } + + g_free (query); + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE)); + +} + + +static void +sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel, + SPPaintSelectorMode mode, + SPWidget *spw ) +{ + if (g_object_get_data (G_OBJECT (spw), "update")) + return; + + /* TODO: Does this work? */ + /* TODO: Not really, here we have to get old color back from object */ + /* Instead of relying on paint widget having meaningful colors set */ + sp_fill_style_widget_paint_changed (psel, spw); +} + +static void +sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel, + SPPaintSelectorFillRule mode, + SPWidget *spw ) +{ + if (g_object_get_data (G_OBJECT (spw), "update")) + return; + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero"); + + sp_desktop_set_style (desktop, css); + + sp_repr_css_attr_unref (css); + + sp_document_done (SP_ACTIVE_DOCUMENT); +} + +static gchar *undo_label_1 = "fill:flatcolor:1"; +static gchar *undo_label_2 = "fill:flatcolor:2"; +static gchar *undo_label = undo_label_1; + +/** +This is called repeatedly while you are dragging a color slider, only for flat color +modes. Previously it set the color in style but did not update the repr for efficiency, however +this was flakey and didn't buy us almost anything. So now it does the same as _changed, except +lumps all its changes for undo. + */ +static void +sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw) +{ + if (!spw->inkscape) { + return; + } + + if (g_object_get_data (G_OBJECT (spw), "update")) { + return; + } + + if (g_object_get_data (G_OBJECT (spw), "local")) { + // previous local flag not cleared yet; + // this means dragged events come too fast, so we better skip this one to speed up display + // (it's safe to do this in any case) + return; + } + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE)); + + switch (psel->mode) { + + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP), undo_label); + g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection + break; + } + + default: + g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'", + __FILE__, __LINE__, psel->mode ); + break; + + } + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE)); +} + + +/** +This is called (at least) when: +1 paint selector mode is switched (e.g. flat color -> gradient) +2 you finished dragging a gradient node and released mouse +3 you changed a gradient selector parameter (e.g. spread) +Must update repr. + */ +static void +sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, + SPWidget *spw ) +{ + if (g_object_get_data (G_OBJECT (spw), "update")) { + return; + } + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE)); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (!desktop) { + return; + } + SPDocument *document = SP_DT_DOCUMENT (desktop); + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + + switch (psel->mode) { + + case SP_PAINT_SELECTOR_MODE_EMPTY: + // This should not happen. + g_warning ( "file %s: line %d: Paint %d should not emit 'changed'", + __FILE__, __LINE__, psel->mode); + break; + case SP_PAINT_SELECTOR_MODE_MULTIPLE: + // This happens when you switch multiple objects with different gradients to flat color; + // nothing to do here. + break; + + case SP_PAINT_SELECTOR_MODE_NONE: + { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, "fill", "none"); + + sp_desktop_set_style (desktop, css); + + sp_repr_css_attr_unref (css); + + sp_document_done (document); + break; + } + + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(desktop), undo_label); + + // on release, toggle undo_label so that the next drag will not be lumped with this one + if (undo_label == undo_label_1) + undo_label = undo_label_2; + else + undo_label = undo_label_1; + + break; + } + + case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: + case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: + if (items) { + SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR + ? SP_GRADIENT_TYPE_LINEAR + : SP_GRADIENT_TYPE_RADIAL ); + + // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "fill-opacity", "1.0"); + + SPGradient *vector = sp_paint_selector_get_gradient_vector(psel); + if (!vector) { + /* No vector in paint selector should mean that we just changed mode */ + + SPStyle *query = sp_style_new (); + int result = objects_query_fillstroke ((GSList *) items, query, true); + guint32 common_rgb = 0; + if (result == QUERY_STYLE_MULTIPLE_SAME) { + if (query->fill.type != SP_PAINT_TYPE_COLOR) { + common_rgb = sp_desktop_get_color(desktop, true); + } else { + common_rgb = sp_color_get_rgba32_ualpha(&query->fill.value.color, 0xff); + } + vector = sp_document_default_gradient_vector(document, common_rgb); + } + g_free (query); + + for (GSList const *i = items; i != NULL; i = i->next) { + //FIXME: see above + sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style"); + + if (!vector) { + sp_item_set_gradient(SP_ITEM(i->data), + sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true), + gradient_type, true); + } else { + sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true); + } + } + } else { + /* We have changed from another gradient type, or modified spread/units within + * this gradient type. */ + vector = sp_gradient_ensure_vector_normalized (vector); + for (GSList const *i = items; i != NULL; i = i->next) { + //FIXME: see above + sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style"); + + SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true); + sp_gradient_selector_attrs_to_gradient (gr, psel); + } + } + + sp_repr_css_attr_unref (css); + + sp_document_done (document); + } + break; + + case SP_PAINT_SELECTOR_MODE_PATTERN: + + if (items) { + + SPPattern *pattern = sp_paint_selector_get_pattern (psel); + if (!pattern) { + + /* No Pattern in paint selector should mean that we just + * changed mode - dont do jack. + */ + + } else { + Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern); + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id")); + sp_repr_css_set_property (css, "fill", urltext); + + // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs + sp_repr_css_set_property(css, "fill-opacity", "1.0"); + + // cannot just call sp_desktop_set_style, because we don't want to touch those + // objects who already have the same root pattern but through a different href + // chain. FIXME: move this to a sp_item_set_pattern + for (GSList const *i = items; i != NULL; i = i->next) { + SPObject *selobj = SP_OBJECT (i->data); + + SPStyle *style = SP_OBJECT_STYLE (selobj); + if (style && style->fill.type == SP_PAINT_TYPE_PAINTSERVER) { + SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj); + if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern) + // only if this object's pattern is not rooted in our selected pattern, apply + continue; + } + + sp_desktop_apply_css_recursive (selobj, css, true); + } + + sp_repr_css_attr_unref (css); + g_free (urltext); + + } // end if + + sp_document_done (document); + + } // end if + + break; + + case SP_PAINT_SELECTOR_MODE_UNSET: + if (items) { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_unset_property (css, "fill"); + + sp_desktop_set_style (desktop, css); + sp_repr_css_attr_unref (css); + + sp_document_done (document); + } + break; + + default: + g_warning ( "file %s: line %d: Paint selector should not be in " + "mode %d", + __FILE__, __LINE__, psel->mode ); + break; + } + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE)); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/fill-style.h b/src/dialogs/fill-style.h new file mode 100644 index 000000000..96cb06987 --- /dev/null +++ b/src/dialogs/fill-style.h @@ -0,0 +1,35 @@ +#ifndef __SP_FILL_STYLE_H__ +#define __SP_FILL_STYLE_H__ + +/** + * \brief Fill style configuration + * + * Authors: + * Lauris Kaplinski + * + * Copyright (C) 2002 Lauris Kaplinski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include + +#include "forward.h" + + +GtkWidget *sp_fill_style_widget_new (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/find.cpp b/src/dialogs/find.cpp new file mode 100644 index 000000000..85146f8a3 --- /dev/null +++ b/src/dialogs/find.cpp @@ -0,0 +1,763 @@ +#define __SP_TRANSFORMATION_C__ + +/** + * \brief Find dialog + * + * Authors: + * bulia byak + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include "widgets/icon.h" + +#include "message-stack.h" + +//TODO : delete this +GtkWidget * sp_find_dialog_old (void); + +void +//GtkWidget * +sp_find_dialog(){ + // DialogFind::get().present(); + sp_find_dialog_old (); + return; +} + + +#include + +#include +#include "helper/window.h" +#include "macros.h" +#include "inkscape.h" +#include "document.h" +#include "desktop.h" +#include "selection.h" +#include "desktop-handles.h" + +#include "dialog-events.h" +#include "../prefs-utils.h" +#include "../verbs.h" +#include "../interface.h" +#include "../sp-text.h" +#include "../sp-flowtext.h" +#include "../text-editing.h" +#include "../sp-tspan.h" +#include "../selection-chemistry.h" +#include "../sp-defs.h" +#include "../sp-rect.h" +#include "../sp-ellipse.h" +#include "../sp-star.h" +#include "../sp-spiral.h" +#include "../sp-path.h" +#include "../sp-line.h" +#include "../sp-polyline.h" +#include "../sp-item-group.h" +#include "../sp-use.h" +#include "../sp-image.h" +#include "../sp-offset.h" +#include + +using NR::X; +using NR::Y; + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.find"; + + + + +static void sp_find_dialog_destroy(GtkObject *object, gpointer) +{ + sp_signal_disconnect_by_data (INKSCAPE, object); + wd.win = dlg = NULL; + wd.stop = 0; +} + + + +static gboolean sp_find_dialog_delete(GtkObject *, GdkEvent *, gpointer data) +{ + gtk_window_get_position (GTK_WINDOW (dlg), &x, &y); + gtk_window_get_size (GTK_WINDOW (dlg), &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it +} + +void +sp_find_squeeze_window() +{ + GtkRequisition r; + gtk_widget_size_request(dlg, &r); + gtk_window_resize ((GtkWindow *) dlg, r.width, r.height); +} + +bool +item_id_match (SPItem *item, const gchar *id, bool exact) +{ + if (SP_OBJECT_REPR (item) == NULL) + return false; + + if (SP_IS_STRING(item)) // SPStrings have "on demand" ids which are useless for searching + return false; + + const gchar *item_id = (SP_OBJECT_REPR (item))->attribute("id"); + if (item_id == NULL) + return false; + + if (exact) { + return ((bool) !strcmp(item_id, id)); + } else { +// g_print ("strstr: %s %s: %s\n", item_id, id, strstr(item_id, id) != NULL? "yes":"no"); + return ((bool) (strstr(item_id, id) != NULL)); + } +} + +bool +item_text_match (SPItem *item, const gchar *text, bool exact) +{ + if (SP_OBJECT_REPR (item) == NULL) + return false; + + if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { + const gchar *item_text = sp_te_get_string_multiline (item); + if (item_text == NULL) + return false; + bool ret; + if (exact) { + ret = ((bool) !strcasecmp(item_text, text)); + } else { + //FIXME: strcasestr + ret = ((bool) (strstr(item_text, text) != NULL)); + } + g_free ((void*) item_text); + return ret; + } + return false; +} + +bool +item_style_match (SPItem *item, const gchar *text, bool exact) +{ + if (SP_OBJECT_REPR (item) == NULL) + return false; + + const gchar *item_text = (SP_OBJECT_REPR (item))->attribute("style"); + if (item_text == NULL) + return false; + + if (exact) { + return ((bool) !strcmp(item_text, text)); + } else { + return ((bool) (strstr(item_text, text) != NULL)); + } +} + +bool +item_attr_match (SPItem *item, const gchar *name, bool exact) +{ + if (SP_OBJECT_REPR (item) == NULL) + return false; + + if (exact) { + const gchar *attr_value = (SP_OBJECT_REPR (item))->attribute(name); + return ((bool) (attr_value != NULL)); + } else { + return SP_OBJECT_REPR (item)->matchAttributeName(name); + } +} + + +GSList * +filter_onefield (GSList *l, GObject *dlg, const gchar *field, bool (*match_function)(SPItem *, const gchar *, bool), bool exact) +{ + GtkWidget *widget = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dlg), field)); + const gchar *text = gtk_entry_get_text (GTK_ENTRY(widget)); + + if (strlen (text) != 0) { + GSList *n = NULL; + for (GSList *i = l; i != NULL; i = i->next) { + if (match_function (SP_ITEM(i->data), text, exact)) { + n = g_slist_prepend (n, i->data); + } + } + return n; + } else { + return l; + } + + return NULL; +} + + +bool +type_checkbox (GtkWidget *widget, const gchar *data) +{ + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (widget), data))); +} + +bool +item_type_match (SPItem *item, GtkWidget *widget) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if (SP_IS_RECT(item)) { + return (type_checkbox (widget, "shapes") || type_checkbox (widget, "rects")); + + } else if (SP_IS_GENERICELLIPSE(item) || SP_IS_ELLIPSE(item) || SP_IS_ARC(item) || SP_IS_CIRCLE(item)) { + return (type_checkbox (widget, "shapes") || type_checkbox (widget, "ellipses")); + + } else if (SP_IS_STAR(item) || SP_IS_POLYGON(item)) { + return (type_checkbox (widget, "shapes") || type_checkbox (widget, "stars")); + + } else if (SP_IS_SPIRAL(item)) { + return (type_checkbox (widget, "shapes") || type_checkbox (widget, "spirals")); + + } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) { + return (type_checkbox (widget, "paths")); + + } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_STRING(item)) { + return (type_checkbox (widget, "texts")); + + } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers! + return (type_checkbox (widget, "groups")); + + } else if (SP_IS_USE(item)) { + return (type_checkbox (widget, "clones")); + + } else if (SP_IS_IMAGE(item)) { + return (type_checkbox (widget, "images")); + + } else if (SP_IS_OFFSET(item)) { + return (type_checkbox (widget, "offsets")); + } + + return false; +} + +GSList * +filter_types (GSList *l, GObject *dlg, bool (*match_function)(SPItem *, GtkWidget *)) +{ + GtkWidget *widget = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dlg), "types")); + + GtkWidget *alltypes = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (widget), "all")); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (alltypes))) + return l; + + + GSList *n = NULL; + for (GSList *i = l; i != NULL; i = i->next) { + if (match_function (SP_ITEM(i->data), widget)) { + n = g_slist_prepend (n, i->data); + } + } + return n; +} + + +GSList * +filter_list (GSList *l, GObject *dlg, bool exact) +{ + l = filter_onefield (l, dlg, "text", item_text_match, exact); + l = filter_onefield (l, dlg, "id", item_id_match, exact); + l = filter_onefield (l, dlg, "style", item_style_match, exact); + l = filter_onefield (l, dlg, "attr", item_attr_match, exact); + + l = filter_types (l, dlg, item_type_match); + + return l; +} + +GSList * +all_items (SPObject *r, GSList *l, bool hidden, bool locked) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if (SP_IS_DEFS(r)) + return l; // we're not interested in items in defs + + if (!strcmp (SP_OBJECT_REPR (r)->name(), "svg:metadata")) + return l; // we're not interested in metadata + + for (SPObject *child = sp_object_first_child(r); child; child = SP_OBJECT_NEXT (child)) { + if (SP_IS_ITEM (child) && !SP_OBJECT_IS_CLONED (child) && !desktop->isLayer(SP_ITEM(child))) { + if ((hidden || !desktop->itemIsHidden(SP_ITEM(child))) && (locked || !SP_ITEM(child)->isLocked())) { + l = g_slist_prepend (l, child); + } + } + l = all_items (child, l, hidden, locked); + } + return l; +} + +GSList * +all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + for (GSList *i = (GSList *) s->itemList(); i != NULL; i = i->next) { + if (SP_IS_ITEM (i->data) && !SP_OBJECT_IS_CLONED (i->data) && !desktop->isLayer(SP_ITEM(i->data))) { + if (!ancestor || ancestor->isAncestorOf(SP_OBJECT (i->data))) { + if ((hidden || !desktop->itemIsHidden(SP_ITEM(i->data))) && (locked || !SP_ITEM(i->data)->isLocked())) { + l = g_slist_prepend (l, i->data); + } + } + } + if (!ancestor || ancestor->isAncestorOf(SP_OBJECT (i->data))) { + l = all_items (SP_OBJECT (i->data), l, hidden, locked); + } + } + return l; +} + + +void sp_find_dialog_find(GObject *, GObject *dlg) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + bool hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (dlg), "includehidden"))); + bool locked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (dlg), "includelocked"))); + + GSList *l = NULL; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (dlg), "inselection")))) { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (dlg), "inlayer")))) { + l = all_selection_items (desktop->selection, l, desktop->currentLayer(), hidden, locked); + } else { + l = all_selection_items (desktop->selection, l, NULL, hidden, locked); + } + } else { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (dlg), "inlayer")))) { + l = all_items (desktop->currentLayer(), l, hidden, locked); + } else { + l = all_items (SP_DOCUMENT_ROOT (SP_DT_DOCUMENT (desktop)), l, hidden, locked); + } + } + guint all = g_slist_length (l); + + bool exact = true; + GSList *n = NULL; + n = filter_list (l, dlg, exact); + if (n == NULL) { + exact = false; + n = filter_list (l, dlg, exact); + } + + if (n != NULL) { + int count = g_slist_length (n); + desktop->messageStack()->flashF(Inkscape::NORMAL_MESSAGE, + // TRANSLATORS: "%s" is replaced with "exact" or "partial" when this string is displayed + ngettext("%d object found (out of %d), %s match.", + "%d objects found (out of %d), %s match.", + count), + count, all, exact? _("exact") : _("partial")); + + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + selection->clear(); + selection->setList(n); + scroll_to_show_item (desktop, SP_ITEM(n->data)); + } else { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No objects found")); + } +} + +void +sp_find_reset_searchfield (GObject *dlg, const gchar *field) +{ + GtkWidget *widget = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dlg), field)); + gtk_entry_set_text (GTK_ENTRY(widget), ""); +} + + +void +sp_find_dialog_reset (GObject *, GObject *dlg) +{ + sp_find_reset_searchfield (dlg, "text"); + sp_find_reset_searchfield (dlg, "id"); + sp_find_reset_searchfield (dlg, "style"); + sp_find_reset_searchfield (dlg, "attr"); + + GtkWidget *types = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dlg), "types")); + GtkToggleButton *tb = GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (types), "all")); + gtk_toggle_button_toggled (tb); + gtk_toggle_button_set_active (tb, TRUE); +} + + +#define FIND_LABELWIDTH 80 + +void +sp_find_new_searchfield (GtkWidget *dlg, GtkWidget *vb, const gchar *label, const gchar *id, GtkTooltips *tt, const gchar *tip) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + GtkWidget *l = gtk_label_new_with_mnemonic (label); + gtk_widget_set_size_request (l, FIND_LABELWIDTH, -1); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + + GtkWidget *tf = gtk_entry_new (); + gtk_entry_set_max_length (GTK_ENTRY (tf), 64); + gtk_box_pack_start (GTK_BOX (hb), tf, TRUE, TRUE, 0); + gtk_object_set_data (GTK_OBJECT (dlg), id, tf); + gtk_tooltips_set_tip (tt, tf, tip, NULL); + g_signal_connect ( G_OBJECT (tf), "activate", G_CALLBACK (sp_find_dialog_find), dlg ); + gtk_label_set_mnemonic_widget (GTK_LABEL(l), tf); + + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); +} + +void +sp_find_new_button (GtkWidget *dlg, GtkWidget *hb, const gchar *label, GtkTooltips *tt, const gchar *tip, void (*function) (GObject *, GObject *)) +{ + GtkWidget *b = gtk_button_new_with_mnemonic (label); + gtk_tooltips_set_tip (tt, b, tip, NULL); + gtk_box_pack_start (GTK_BOX (hb), b, TRUE, TRUE, 0); + g_signal_connect ( G_OBJECT (b), "clicked", G_CALLBACK (function), dlg ); + gtk_widget_show (b); +} + +void +toggle_alltypes (GtkToggleButton *tb, gpointer data) +{ + GtkWidget *alltypes_pane = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (data), "all-pane")); + if (gtk_toggle_button_get_active (tb)) { + gtk_widget_hide_all (alltypes_pane); + } else { + gtk_widget_show_all (alltypes_pane); + + // excplicit toggle to make sure its handler gets called, no matter what was the original state + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "shapes"))); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "shapes")), TRUE); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "paths")), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "texts")), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "groups")), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "clones")), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "images")), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "offsets")), TRUE); + } + sp_find_squeeze_window(); +} + +void +toggle_shapes (GtkToggleButton *tb, gpointer data) +{ + GtkWidget *shapes_pane = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (data), "shapes-pane")); + if (gtk_toggle_button_get_active (tb)) { + gtk_widget_hide_all (shapes_pane); + } else { + gtk_widget_show_all (shapes_pane); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "rects")), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "ellipses")), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "stars")), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_object_get_data (GTK_OBJECT (data), "spirals")), FALSE); + } + sp_find_squeeze_window(); +} + + +GtkWidget * +sp_find_types_checkbox (GtkWidget *w, const gchar *data, gboolean active, + GtkTooltips *tt, const gchar *tip, + const gchar *label, + void (*toggled)(GtkToggleButton *, gpointer)) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + + { + GtkWidget *b = gtk_check_button_new_with_label (label); + gtk_widget_show (b); + gtk_toggle_button_set_active ((GtkToggleButton *) b, active); + gtk_object_set_data (GTK_OBJECT (w), data, b); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, tip, NULL); + if (toggled) + gtk_signal_connect (GTK_OBJECT (b), "toggled", GTK_SIGNAL_FUNC (toggled), w); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + + return hb; +} + +GtkWidget * +sp_find_types_checkbox_indented (GtkWidget *w, const gchar *data, gboolean active, + GtkTooltips *tt, const gchar *tip, + const gchar *label, + void (*toggled)(GtkToggleButton *, gpointer), guint indent) +{ + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + + { // empty label for indent + GtkWidget *l = gtk_label_new (""); + gtk_widget_show (l); + gtk_widget_set_size_request (l, FIND_LABELWIDTH + indent, -1); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + GtkWidget *c = sp_find_types_checkbox (w, data, active, tt, tip, label, toggled); + gtk_box_pack_start (GTK_BOX (hb), c, FALSE, FALSE, 0); + + return hb; +} + + +GtkWidget * +sp_find_types () +{ + GtkTooltips *tt = gtk_tooltips_new (); + + GtkWidget *vb = gtk_vbox_new (FALSE, 4); + gtk_widget_show (vb); + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + + { + GtkWidget *l = gtk_label_new_with_mnemonic (_("T_ype: ")); + gtk_widget_show (l); + gtk_widget_set_size_request (l, FIND_LABELWIDTH, -1); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + GtkWidget *alltypes = sp_find_types_checkbox (vb, "all", TRUE, tt, _("Search in all object types"), _("All types"), toggle_alltypes); + gtk_box_pack_start (GTK_BOX (hb), alltypes, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + } + + { + GtkWidget *vb_all = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vb_all); + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "shapes", FALSE, tt, _("Search all shapes"), _("All shapes"), toggle_shapes, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + + { // empty label for alignment + GtkWidget *l = gtk_label_new (""); + gtk_widget_show (l); + gtk_widget_set_size_request (l, FIND_LABELWIDTH + 20, -1); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox (vb, "rects", FALSE, tt, _("Search rectangles"), _("Rectangles"), NULL); + gtk_box_pack_start (GTK_BOX (hb), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox (vb, "ellipses", FALSE, tt, _("Search ellipses, arcs, circles"), _("Ellipses"), NULL); + gtk_box_pack_start (GTK_BOX (hb), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox (vb, "stars", FALSE, tt, _("Search stars and polygons"), _("Stars"), NULL); + gtk_box_pack_start (GTK_BOX (hb), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox (vb, "spirals", FALSE, tt, _("Search spirals"), _("Spirals"), NULL); + gtk_box_pack_start (GTK_BOX (hb), c, FALSE, FALSE, 0); + } + + gtk_object_set_data (GTK_OBJECT (vb), "shapes-pane", hb); + + gtk_box_pack_start (GTK_BOX (vb_all), hb, FALSE, FALSE, 0); + gtk_widget_hide_all (hb); + } + + { + // TRANSLATORS: polyline is a set of connected straight line segments + // http://www.w3.org/TR/SVG11/shapes.html#PolylineElement + GtkWidget *c = sp_find_types_checkbox_indented (vb, "paths", TRUE, tt, _("Search paths, lines, polylines"), _("Paths"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "texts", TRUE, tt, _("Search text objects"), _("Texts"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "groups", TRUE, tt, _("Search groups"), _("Groups"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "clones", TRUE, tt, _("Search clones"), _("Clones"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "images", TRUE, tt, _("Search images"), _("Images"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + { + GtkWidget *c = sp_find_types_checkbox_indented (vb, "offsets", TRUE, tt, _("Search offset objects"), _("Offsets"), NULL, 10); + gtk_box_pack_start (GTK_BOX (vb_all), c, FALSE, FALSE, 0); + } + + gtk_box_pack_start (GTK_BOX (vb), vb_all, FALSE, FALSE, 0); + gtk_object_set_data (GTK_OBJECT (vb), "all-pane", vb_all); + gtk_widget_hide_all (vb_all); + } + + return vb; +} + + +GtkWidget * +sp_find_dialog_old (void) +{ + if (!dlg) + { + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_FIND), title); + + dlg = sp_window_new (title, TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + if (w && h) + gtk_window_resize ((GtkWindow *) dlg, w, h); + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd ); + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_find_dialog_destroy), NULL ); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_find_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_find_dialog_delete), dlg); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); + + GtkTooltips *tt = gtk_tooltips_new (); + + gtk_container_set_border_width (GTK_CONTAINER (dlg), 4); + + /* Toplevel vbox */ + GtkWidget *vb = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (dlg), vb); + + sp_find_new_searchfield (dlg, vb, _("_Text: "), "text", tt, _("Find objects by their text content (exact or partial match)")); + sp_find_new_searchfield (dlg, vb, _("_ID: "), "id", tt, _("Find objects by the value of the id attribute (exact or partial match)")); + sp_find_new_searchfield (dlg, vb, _("_Style: "), "style", tt, _("Find objects by the value of the style attribute (exact or partial match)")); + sp_find_new_searchfield (dlg, vb, _("_Attribute: "), "attr", tt ,_("Find objects by the name of an attribute (exact or partial match)")); + + gtk_widget_show_all (vb); + + GtkWidget *types = sp_find_types (); + gtk_object_set_data (GTK_OBJECT (dlg), "types", types); + gtk_box_pack_start (GTK_BOX (vb), types, FALSE, FALSE, 0); + + { + GtkWidget *w = gtk_hseparator_new (); + gtk_widget_show (w); + gtk_box_pack_start (GTK_BOX (vb), w, FALSE, FALSE, 3); + + { + GtkWidget *b = gtk_check_button_new_with_mnemonic (_("Search in s_election")); + gtk_widget_show (b); + gtk_toggle_button_set_active ((GtkToggleButton *) b, FALSE); + gtk_object_set_data (GTK_OBJECT (dlg), "inselection", b); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Limit search to the current selection"), NULL); + gtk_box_pack_start (GTK_BOX (vb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_check_button_new_with_mnemonic (_("Search in current _layer")); + gtk_widget_show (b); + gtk_toggle_button_set_active ((GtkToggleButton *) b, FALSE); + gtk_object_set_data (GTK_OBJECT (dlg), "inlayer", b); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Limit search to the current layer"), NULL); + gtk_box_pack_start (GTK_BOX (vb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_check_button_new_with_mnemonic (_("Include _hidden")); + gtk_widget_show (b); + gtk_toggle_button_set_active ((GtkToggleButton *) b, FALSE); + gtk_object_set_data (GTK_OBJECT (dlg), "includehidden", b); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Include hidden objects in search"), NULL); + gtk_box_pack_start (GTK_BOX (vb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_check_button_new_with_mnemonic (_("Include l_ocked")); + gtk_widget_show (b); + gtk_toggle_button_set_active ((GtkToggleButton *) b, FALSE); + gtk_object_set_data (GTK_OBJECT (dlg), "includelocked", b); + gtk_tooltips_set_tip (GTK_TOOLTIPS (tt), b, _("Include locked objects in search"), NULL); + gtk_box_pack_start (GTK_BOX (vb), b, FALSE, FALSE, 0); + } + } + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + // TRANSLATORS: "Clear" is a verb here + sp_find_new_button (dlg, hb, _("_Clear"), tt, _("Clear values"), sp_find_dialog_reset); + sp_find_new_button (dlg, hb, _("_Find"), tt, _("Select objects matching all of the fields you filled in"), sp_find_dialog_find); + } + } + + gtk_widget_show((GtkWidget *) dlg); + gtk_window_present ((GtkWindow *) dlg); + sp_find_dialog_reset (NULL, G_OBJECT (dlg)); + + return dlg; +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/find.h b/src/dialogs/find.h new file mode 100644 index 000000000..38200d532 --- /dev/null +++ b/src/dialogs/find.h @@ -0,0 +1,31 @@ +#ifndef SEEN_FIND_H +#define SEEN_FIND_H + +/** + * \brief Find dialog + * + * Authors: + * bulia byak + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +void sp_find_dialog(); + + +#endif /* !SEEN_FIND_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/iconpreview.cpp b/src/dialogs/iconpreview.cpp new file mode 100644 index 000000000..b3ea55df3 --- /dev/null +++ b/src/dialogs/iconpreview.cpp @@ -0,0 +1,320 @@ +/* + * A simple dialog for previewing icon representation. + * + * Authors: + * Jon A. Cruz + * Bob Jamison + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 Bob Jamison + * Copyright (C) 2005 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "iconpreview.h" + +#include + +#include //for GTK_RESPONSE* types +#include +#include +#include + +#include "prefs-utils.h" +#include "inkscape.h" +#include "document.h" +#include "desktop-handles.h" +#include "selection.h" +#include "desktop.h" +#include "display/nr-arena.h" +#include "sp-root.h" +#include "xml/repr.h" + +extern "C" { +// takes doc, root, icon, and icon name to produce pixels +guchar * +sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root, + const gchar *name, unsigned int psize ); +} + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + +IconPreviewPanel* IconPreviewPanel::instance = 0; + + +IconPreviewPanel& IconPreviewPanel::getInstance() +{ + if ( !instance ) { + instance = new IconPreviewPanel(); + } + + instance->refreshPreview(); + + return *instance; +} + +//######################################################################### +//## E V E N T S +//######################################################################### + +void IconPreviewPanel::on_button_clicked(int which) +{ + if ( hot != which ) { + buttons[hot]->set_active( false ); + + hot = which; + updateMagnify(); + queue_draw(); + } +} + + + + +//######################################################################### +//## C O N S T R U C T O R / D E S T R U C T O R +//######################################################################### +/** + * Constructor + */ +IconPreviewPanel::IconPreviewPanel() : + Panel(), + hot(1), + refreshButton(0), + selectionButton(0) +{ + numEntries = 0; + Inkscape::XML::Node *things = inkscape_get_repr(INKSCAPE, "iconpreview.sizes.default"); + if (things) { + std::vector rawSizes; + for ( Inkscape::XML::Node *child = things->firstChild(); child; child = child->next() ) + { + gchar const *id = child->attribute("id"); + if ( id ) + { + std::string path("iconpreview.sizes.default."); + path += id; + gint show = prefs_get_int_attribute_limited( path.c_str(), "show", 1, 0, 1 ); + gint sizeVal = prefs_get_int_attribute( path.c_str(), "value", -1 ); + if ( show && (sizeVal > 0) ) + { + rawSizes.push_back( sizeVal ); + } + } + } + + if ( !rawSizes.empty() ) + { + numEntries = rawSizes.size(); + sizes = new int[numEntries]; + int i = 0; + for ( std::vector::iterator it = rawSizes.begin(); it != rawSizes.end(); ++it, ++i ) { + sizes[i] = *it; + } + } + } + + if ( numEntries < 1 ) + { + numEntries = 5; + sizes = new int[numEntries]; + sizes[0] = 16; + sizes[1] = 24; + sizes[2] = 32; + sizes[3] = 48; + sizes[5] = 128; + } + + pixMem = new guchar*[numEntries]; + images = new Gtk::Image*[numEntries]; + labels = new Glib::ustring*[numEntries]; + buttons = new Gtk::ToggleToolButton*[numEntries]; + + + for ( int i = 0; i < numEntries; i++ ) { + char *label = g_strdup_printf(_("%d x %d"), sizes[i], sizes[i]); + labels[i] = new Glib::ustring(label); + g_free(label); + pixMem[i] = 0; + images[i] = 0; + } + + + magLabel.set_label( *labels[hot] ); + + Gtk::VBox* magBox = new Gtk::VBox(); + + magBox->pack_start( magnified ); + magBox->pack_start( magLabel, Gtk::PACK_SHRINK ); + + + Gtk::VBox * verts = new Gtk::VBox(); + for ( int i = 0; i < numEntries; i++ ) { + pixMem[i] = new guchar[4 * sizes[i] * sizes[i]]; + memset( pixMem[i], 0x00, 4 * sizes[i] * sizes[i] ); + + GdkPixbuf *pb = gdk_pixbuf_new_from_data( pixMem[i], GDK_COLORSPACE_RGB, TRUE, 8, sizes[i], sizes[i], sizes[i] * 4, /*(GdkPixbufDestroyNotify)nr_free*/NULL, NULL ); + GtkImage* img = GTK_IMAGE( gtk_image_new_from_pixbuf( pb ) ); + images[i] = Glib::wrap(img); + Glib::ustring label(*labels[i]); + buttons[i] = new Gtk::ToggleToolButton(label); + buttons[i]->set_active( i == hot ); + buttons[i]->set_icon_widget(*images[i]); + + tips.set_tip((*buttons[i]), label); + + buttons[i]->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &IconPreviewPanel::on_button_clicked), i) ); + + + verts->add(*buttons[i]); + } + + iconBox.pack_start(splitter); + splitter.pack1( *magBox, true, true ); + splitter.pack2( *verts, false, false ); + + + //## The Refresh button + + + Gtk::HButtonBox* holder = new Gtk::HButtonBox( Gtk::BUTTONBOX_END ); + pack_end( *holder, false, false ); + + selectionButton = new Gtk::ToggleButton(_("Selection")); // , GTK_RESPONSE_APPLY + holder->pack_start( *selectionButton, false, false ); + tips.set_tip((*selectionButton), _("Selection only or whole document")); + selectionButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::modeToggled) ); + + gint val = prefs_get_int_attribute_limited( "iconpreview", "selectionOnly", 0, 0, 1 ); + selectionButton->set_active( val != 0 ); + + refreshButton = new Gtk::Button(Gtk::Stock::REFRESH); // , GTK_RESPONSE_APPLY + holder->pack_end( *refreshButton, false, false ); + tips.set_tip((*refreshButton), _("Refresh the icons")); + refreshButton->signal_clicked().connect( sigc::mem_fun(*this, &IconPreviewPanel::refreshPreview) ); + + + pack_start(iconBox, Gtk::PACK_EXPAND_WIDGET); + + show_all_children(); +} + +//######################################################################### +//## M E T H O D S +//######################################################################### + + +void IconPreviewPanel::refreshPreview() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if ( desktop ) { + + if ( selectionButton && selectionButton->get_active() ) + { + Inkscape::Selection * sel = SP_DT_SELECTION(desktop); + if ( sel ) { + //g_message("found a selection to play with"); + + GSList const *items = sel->itemList(); + SPObject *target = 0; + while ( items && !target ) { + SPItem* item = SP_ITEM( items->data ); + SPObject * obj = SP_OBJECT(item); + gchar const *id = SP_OBJECT_ID( obj ); + if ( id ) { + target = obj; + } + + items = g_slist_next(items); + } + if ( target ) { + renderPreview(target); + } + } + } + else + { + SPObject *target = desktop->currentRoot(); + if ( target ) { + renderPreview(target); + } + } + } +} + +void IconPreviewPanel::modeToggled() +{ + prefs_set_int_attribute( "iconpreview", "selectionOnly", (selectionButton && selectionButton->get_active()) ? 1 : 0 ); + + refreshPreview(); +} + +void IconPreviewPanel::renderPreview( SPObject* obj ) +{ + SPDocument * doc = SP_OBJECT_DOCUMENT(obj); + gchar * id = SP_OBJECT_ID(obj); + +// g_message(" setting up to render '%s' as the icon", id ); + + NRArenaItem *root = NULL; + + /* Create new arena */ + NRArena *arena = NRArena::create(); + + /* Create ArenaItem and set transform */ + unsigned int visionkey = sp_item_display_key_new(1); + + /* fixme: Memory manage root if needed (Lauris) */ + root = sp_item_invoke_show ( SP_ITEM( SP_DOCUMENT_ROOT(doc) ), + arena, visionkey, SP_ITEM_SHOW_DISPLAY ); + + for ( int i = 0; i < numEntries; i++ ) { + guchar * px = sp_icon_doc_icon( doc, root, id, sizes[i] ); +// g_message( " size %d %s", sizes[i], (px ? "worked" : "failed") ); + if ( px ) { + memcpy( pixMem[i], px, sizes[i] * sizes[i] * 4 ); + g_free( px ); + px = 0; + } else { + memset( pixMem[i], 0, sizes[i] * sizes[i] * 4 ); + } + images[i]->queue_draw(); + } + updateMagnify(); +} + +void IconPreviewPanel::updateMagnify() +{ + Glib::RefPtr buf = images[hot]->get_pixbuf()->scale_simple( 128, 128, Gdk::INTERP_NEAREST ); + magLabel.set_label( *labels[hot] ); + magnified.set( buf ); + magnified.queue_draw(); + magnified.get_parent()->queue_draw(); +} + + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : + +//######################################################################### +//## E N D O F F I L E +//######################################################################### diff --git a/src/dialogs/iconpreview.h b/src/dialogs/iconpreview.h new file mode 100644 index 000000000..e4be831d3 --- /dev/null +++ b/src/dialogs/iconpreview.h @@ -0,0 +1,84 @@ +#ifndef SEEN_ICON_PREVIEW_H +#define SEEN_ICON_PREVIEW_H +/* + * A simple dialog for previewing icon representation. + * + * Authors: + * Jon A. Cruz + * Bob Jamison + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004,2005 The Inkscape Organization + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include +#include +#include + +#include "ui/widget/panel.h" + +struct SPObject; + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + +/** + * A panel that displays an icon preview + */ +class IconPreviewPanel : public Inkscape::UI::Widget::Panel +{ +public: + IconPreviewPanel(); + //IconPreviewPanel(Glib::ustring const &label); + + static IconPreviewPanel& getInstance(); + + void refreshPreview(); + void modeToggled(); + +private: + IconPreviewPanel(IconPreviewPanel const &); // no copy + IconPreviewPanel &operator=(IconPreviewPanel const &); // no assign + + + void on_button_clicked(int which); + void renderPreview( SPObject* obj ); + void updateMagnify(); + + static IconPreviewPanel* instance; + + Gtk::Tooltips tips; + + Gtk::VBox iconBox; + Gtk::HPaned splitter; + + int hot; + int numEntries; + int* sizes; + + Gtk::Image magnified; + Gtk::Label magLabel; + + Gtk::Button *refreshButton; + Gtk::ToggleButton *selectionButton; + + guchar** pixMem; + Gtk::Image** images; + Glib::ustring** labels; + Gtk::ToggleToolButton** buttons; +}; + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + +#endif // SEEN_ICON_PREVIEW_H diff --git a/src/dialogs/in-dt-coordsys.cpp b/src/dialogs/in-dt-coordsys.cpp new file mode 100644 index 000000000..54d9ac326 --- /dev/null +++ b/src/dialogs/in-dt-coordsys.cpp @@ -0,0 +1,37 @@ +#include "sp-root.h" + +/** Returns true iff \a item is suitable to be included in the selection, in particular + whether it has a bounding box in the desktop coordinate system for rendering resize handles. + + Descendents of nodes (markers etc.) return false, for example. +*/ +bool in_dt_coordsys(SPObject const &item) +{ + /* Definition based on sp_item_i2doc_affine. */ + SPObject const *child = &item; + g_return_val_if_fail(child != NULL, false); + for(;;) { + if (!SP_IS_ITEM(child)) { + return false; + } + SPObject const * const parent = SP_OBJECT_PARENT(child); + if (parent == NULL) { + break; + } + child = parent; + } + g_assert(SP_IS_ROOT(child)); + /* Relevance: Otherwise, I'm not sure whether to return true or false. */ + return true; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/in-dt-coordsys.h b/src/dialogs/in-dt-coordsys.h new file mode 100644 index 000000000..6078f9b41 --- /dev/null +++ b/src/dialogs/in-dt-coordsys.h @@ -0,0 +1,19 @@ +#ifndef SEEN_IN_DT_COORDSYS +#define SEEN_IN_DT_COORDSYS +#include "forward.h" + +bool in_dt_coordsys(SPObject const &item); + + +#endif /* !SEEN_IN_DT_COORDSYS */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/input.cpp b/src/dialogs/input.cpp new file mode 100644 index 000000000..da8eba196 --- /dev/null +++ b/src/dialogs/input.cpp @@ -0,0 +1,273 @@ +#define __SP_INPUT_C__ + +/* + * Extended input devices dialog + * + * Authors: + * Nicklas Lindgren + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "../inkscape.h" +#include "../macros.h" +#include "../verbs.h" +#include "../interface.h" +#include "../xml/repr.h" + +#include "dialog-events.h" +#include "../prefs-utils.h" + + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.input"; + +static void +sp_input_dialog_destroy (GtkObject *object, gpointer data) +{ + sp_signal_disconnect_by_data (INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; +} + +static gboolean +sp_input_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data) +{ + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it + +} + +static gchar *axis_use_strings[GDK_AXIS_LAST] = { + "ignore", "x", "y", "pressure", "xtilt", "ytilt", "wheel" +}; + +void +sp_input_load_from_preferences (void) +{ + Inkscape::XML::Node *devices = inkscape_get_repr(INKSCAPE, "devices"); + if (devices == NULL) + return; + + Inkscape::XML::Node *repr; + GList *list_ptr; + + for (list_ptr = gdk_devices_list(); list_ptr != NULL; list_ptr = list_ptr->next) { + GdkDevice *device = static_cast(list_ptr->data); + repr = sp_repr_lookup_child(devices, "id", device->name); + if (repr != NULL) { + GdkInputMode mode; + const gchar *attribute = repr->attribute("mode"); + + if (attribute == NULL) + mode = GDK_MODE_DISABLED; + else if (!strcmp(attribute, "screen")) + mode = GDK_MODE_SCREEN; + else if (!strcmp(attribute, "window")) + mode = GDK_MODE_WINDOW; + else + mode = GDK_MODE_DISABLED; + + if (device->mode != mode) { + gdk_device_set_mode(device, mode); + } + + const gchar *temp_ptr; + std::string::size_type pos0; + std::string::size_type pos1; + gint i; + gint j; + + GdkAxisUse axis_use; + + temp_ptr = repr->attribute("axes"); + if (temp_ptr != NULL) { + const std::string temp_str = temp_ptr; + pos0 = pos1 = 0; + for (i=0; i < device->num_axes; i++) { + pos1 = temp_str.find(";", pos0); + if (pos1 == std::string::npos) + break; // Too few axis specifications + + axis_use = GDK_AXIS_IGNORE; + for (j=0; j < GDK_AXIS_LAST; j++) + if (!strcmp(temp_str.substr(pos0, pos1-pos0).c_str(), axis_use_strings[j])) { + axis_use = static_cast(j); + break; + } + gdk_device_set_axis_use(device, i, axis_use); + pos0 = pos1 + 1; + } + } + + guint keyval; + GdkModifierType modifier; + + temp_ptr = repr->attribute("keys"); + if (temp_ptr != NULL) { + const std::string temp_str = temp_ptr; + pos0 = pos1 = 0; + for (i=0; i < device->num_keys; i++) { + pos1 = temp_str.find(";", pos0); + if (pos1 == std::string::npos) + break; // Too few key specifications + + gtk_accelerator_parse(temp_str.substr(pos0, pos1-pos0).c_str(), &keyval, &modifier); + gdk_device_set_key(device, i, keyval, modifier); + pos0 = pos1 + 1; + } + } + } + } +} + +void +sp_input_save_to_preferences (void) +{ + Inkscape::XML::Node *devices = inkscape_get_repr(INKSCAPE, "devices"); + if (devices == NULL) + // TODO: find a clean way to add a node to the preferences root, or + // give an error message + return; + + Inkscape::XML::Node *repr; + GList *list_ptr; + + for (list_ptr = gdk_devices_list(); list_ptr != NULL; list_ptr = list_ptr->next) { + gint i; + std::string temp_attribute; + GdkDevice *device = static_cast(list_ptr->data); + + repr = sp_repr_lookup_child(devices, "id", device->name); + if (repr == NULL) { + repr = sp_repr_new("group"); + repr->setAttribute("id", device->name); + devices->appendChild(repr); + Inkscape::GC::release(repr); + } + switch (device->mode) { + default: + case GDK_MODE_DISABLED: { + repr->setAttribute("mode", "disabled"); + break; + } + case GDK_MODE_SCREEN: { + repr->setAttribute("mode", "screen"); + break; + } + case GDK_MODE_WINDOW: { + repr->setAttribute("mode", "window"); + break; + } + } + + temp_attribute = ""; + for (i=0; i < device->num_axes; i++) { + temp_attribute += axis_use_strings[device->axes[i].use]; + temp_attribute += ";"; + } + repr->setAttribute("axes", temp_attribute.c_str()); + + temp_attribute = ""; + for (i=0; i < device->num_keys; i++) { + temp_attribute += gtk_accelerator_name(device->keys[i].keyval, device->keys[i].modifiers); + temp_attribute += ";"; + } + repr->setAttribute("keys", temp_attribute.c_str()); + } +} + +static void +sp_input_save_button (GtkObject *object, gpointer data) +{ + sp_input_save_to_preferences(); +} + +/** + * \brief Dialog + * + */ +void +sp_input_dialog (void) +{ + if (dlg == NULL) { + + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_INPUT), title); + + dlg = gtk_input_dialog_new(); + + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) { + gtk_window_resize ((GtkWindow *) dlg, w, h); + } + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd); + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_input_dialog_destroy), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_input_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_input_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); + + // Dialog-specific stuff + gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(dlg)->close_button), + "clicked", + (GtkSignalFunc)gtk_widget_destroy, + GTK_OBJECT(dlg)); + gtk_signal_connect (GTK_OBJECT(GTK_INPUT_DIALOG(dlg)->save_button), + "clicked", + (GtkSignalFunc)sp_input_save_button, NULL); + } + + gtk_window_present ((GtkWindow *) dlg); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/input.h b/src/dialogs/input.h new file mode 100644 index 000000000..70e68774e --- /dev/null +++ b/src/dialogs/input.h @@ -0,0 +1,32 @@ +#ifndef __SP_INPUT_H__ +#define __SP_INPUT_H__ + +/** + * \brief Extended input device dialog + * + * Author: + * Nicklas Lindgren + * + * Copyright (C) 2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +void sp_input_load_from_preferences (void); +void sp_input_save_to_preferences (void); +void sp_input_dialog (void); + + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/item-properties.cpp b/src/dialogs/item-properties.cpp new file mode 100644 index 000000000..a8031b13e --- /dev/null +++ b/src/dialogs/item-properties.cpp @@ -0,0 +1,497 @@ +#define __SP_ITEM_PROPERTIES_C__ + +/* + * Object properties dialog + * + * Authors: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 1999-2005 Authors + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "helper/window.h" +#include "../widgets/sp-widget.h" +#include "../inkscape.h" +#include "../document.h" +#include "../desktop-handles.h" +#include "../selection.h" +#include "../sp-item.h" +#include "../macros.h" +#include "../verbs.h" +#include "../interface.h" + +#include "dialog-events.h" +#include "../prefs-utils.h" + + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.object"; + +static void sp_item_widget_modify_selection (SPWidget *spw, Inkscape::Selection *selection, guint flags, GtkWidget *itemw); +static void sp_item_widget_change_selection (SPWidget *spw, Inkscape::Selection *selection, GtkWidget *itemw); +static void sp_item_widget_setup (SPWidget *spw, Inkscape::Selection *selection); +static void sp_item_widget_sensitivity_toggled (GtkWidget *widget, SPWidget *spw); +static void sp_item_widget_hidden_toggled (GtkWidget *widget, SPWidget *spw); +static void sp_item_widget_label_changed (GtkWidget *widget, SPWidget *spw); + +static void +sp_item_dialog_destroy (GtkObject *object, gpointer data) +{ + sp_signal_disconnect_by_data (INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; +} + +static gboolean +sp_item_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data) +{ + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it + +} + +/** + * \brief Creates new instance of item properties widget + * + */ +GtkWidget * +sp_item_widget_new (void) +{ + + GtkWidget *spw, *vb, *t, *cb, *l, *f, *tf, *pb; + GtkTextBuffer *desc_buffer; + + GtkTooltips *tt = gtk_tooltips_new(); + + /* Create container widget */ + spw = sp_widget_new_global (INKSCAPE); + gtk_signal_connect ( GTK_OBJECT (spw), "modify_selection", + GTK_SIGNAL_FUNC (sp_item_widget_modify_selection), + spw ); + gtk_signal_connect ( GTK_OBJECT (spw), "change_selection", + GTK_SIGNAL_FUNC (sp_item_widget_change_selection), + spw ); + + vb = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (spw), vb); + + t = gtk_table_new (3, 4, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(t), 4); + gtk_table_set_row_spacings (GTK_TABLE (t), 4); + gtk_table_set_col_spacings (GTK_TABLE (t), 4); + gtk_box_pack_start (GTK_BOX (vb), t, TRUE, TRUE, 0); + + + /* Create the label for the object id */ + l = gtk_label_new_with_mnemonic (_("_Id")); + gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5); + gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 0, 1, + (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "id_label", l); + + /* Create the entry box for the object id */ + tf = gtk_entry_new (); + gtk_tooltips_set_tip (tt, tf, _("The id= attribute (only letters, digits, and the characters .-_: allowed)"), NULL); + gtk_entry_set_max_length (GTK_ENTRY (tf), 64); + gtk_table_attach ( GTK_TABLE (t), tf, 1, 2, 0, 1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "id", tf); + gtk_label_set_mnemonic_widget (GTK_LABEL(l), tf); + + // pressing enter in the id field is the same as clicking Set: + g_signal_connect ( G_OBJECT (tf), "activate", G_CALLBACK (sp_item_widget_label_changed), spw); + // focus is in the id field initially: + gtk_widget_grab_focus (GTK_WIDGET (tf)); + + /* Button for setting the object's id, label, title and description. */ + pb = gtk_button_new_with_mnemonic (_("_Set")); + gtk_table_attach ( GTK_TABLE (t), pb, 2, 3, 0, 1, + (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_signal_connect ( GTK_OBJECT (pb), "clicked", + GTK_SIGNAL_FUNC (sp_item_widget_label_changed), + spw ); + + /* Create the label for the object label */ + l = gtk_label_new_with_mnemonic (_("_Label")); + gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5); + gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 1, 2, + (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "label_label", l); + + /* Create the entry box for the object label */ + tf = gtk_entry_new (); + gtk_tooltips_set_tip (tt, tf, _("A freeform label for the object"), NULL); + gtk_entry_set_max_length (GTK_ENTRY (tf), 256); + gtk_table_attach ( GTK_TABLE (t), tf, 1, 2, 1, 2, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "label", tf); + gtk_label_set_mnemonic_widget (GTK_LABEL(l), tf); + + // pressing enter in the label field is the same as clicking Set: + g_signal_connect ( G_OBJECT (tf), "activate", G_CALLBACK (sp_item_widget_label_changed), spw); + + /* Create the label for the object title */ + l = gtk_label_new (_("Title")); + gtk_misc_set_alignment (GTK_MISC (l), 1, 0.5); + gtk_table_attach ( GTK_TABLE (t), l, 0, 1, 2, 3, + (GtkAttachOptions)( GTK_SHRINK | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "title_label", l); + + /* Create the entry box for the object title */ + tf = gtk_entry_new (); + gtk_widget_set_sensitive (GTK_WIDGET (tf), FALSE); + gtk_entry_set_max_length (GTK_ENTRY (tf), 256); + gtk_table_attach ( GTK_TABLE (t), tf, 1, 3, 2, 3, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "title", tf); + + /* Create the frame for the object description */ + f = gtk_frame_new (_("Description")); + gtk_table_attach ( GTK_TABLE (t), f, 0, 3, 3, 4, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), 0, 0 ); + gtk_object_set_data (GTK_OBJECT (spw), "desc_frame", l); + + /* Create the text view box for the object description */ + GtkWidget *textframe = gtk_frame_new(NULL); + gtk_container_set_border_width(GTK_CONTAINER(textframe), 4); + gtk_widget_set_sensitive (GTK_WIDGET (textframe), FALSE); + gtk_container_add (GTK_CONTAINER (f), textframe); + gtk_frame_set_shadow_type (GTK_FRAME (textframe), GTK_SHADOW_IN); + + tf = gtk_text_view_new(); + desc_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tf)); + gtk_text_buffer_set_text(desc_buffer, "", -1); + gtk_container_add (GTK_CONTAINER (textframe), tf); + gtk_object_set_data (GTK_OBJECT (spw), "desc", tf); + + /* Check boxes */ + GtkWidget *hb_cb = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vb), hb_cb, FALSE, FALSE, 0); + t = gtk_table_new (1, 2, TRUE); + gtk_container_set_border_width(GTK_CONTAINER(t), 0); + gtk_box_pack_start (GTK_BOX (hb_cb), t, TRUE, TRUE, 10); + + /* Hide */ + cb = gtk_check_button_new_with_mnemonic (_("_Hide")); + gtk_tooltips_set_tip (tt, cb, _("Check to make the object invisible"), NULL); + gtk_table_attach ( GTK_TABLE (t), cb, 0, 1, 0, 1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + g_signal_connect (G_OBJECT(cb), "toggled", G_CALLBACK(sp_item_widget_hidden_toggled), spw); + gtk_object_set_data(GTK_OBJECT(spw), "hidden", cb); + + /* Lock */ + // TRANSLATORS: "Lock" is a verb here + cb = gtk_check_button_new_with_mnemonic (_("L_ock")); + gtk_tooltips_set_tip (tt, cb, _("Check to make the object insensitive (not selectable by mouse)"), NULL); + gtk_table_attach ( GTK_TABLE (t), cb, 1, 2, 0, 1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_signal_connect ( GTK_OBJECT (cb), "toggled", + GTK_SIGNAL_FUNC (sp_item_widget_sensitivity_toggled), + spw ); + gtk_object_set_data (GTK_OBJECT (spw), "sensitive", cb); + + gtk_widget_show_all (spw); + + sp_item_widget_setup (SP_WIDGET (spw), SP_DT_SELECTION (SP_ACTIVE_DESKTOP)); + + return (GtkWidget *) spw; + +} //end of sp_item_widget_new() + + + +static void +sp_item_widget_modify_selection ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + GtkWidget *itemw ) +{ + sp_item_widget_setup (spw, selection); +} + + + +static void +sp_item_widget_change_selection ( SPWidget *spw, + Inkscape::Selection *selection, + GtkWidget *itemw ) +{ + sp_item_widget_setup (spw, selection); +} + + +/** +* \param selection Selection to use; should not be NULL. +*/ +static void +sp_item_widget_setup ( SPWidget *spw, Inkscape::Selection *selection ) +{ + g_assert (selection != NULL); + + if (gtk_object_get_data (GTK_OBJECT (spw), "blocked")) + return; + + if (!selection->singleItem()) { + gtk_widget_set_sensitive (GTK_WIDGET (spw), FALSE); + return; + } else { + gtk_widget_set_sensitive (GTK_WIDGET (spw), TRUE); + } + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE)); + + SPItem *item = selection->singleItem(); + + /* Sensitive */ + GtkWidget *w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "sensitive")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), item->isLocked()); + + /* Hidden */ + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "hidden")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), item->isExplicitlyHidden()); + + if (SP_OBJECT_IS_CLONED (item)) { + + /* ID */ + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id")); + gtk_entry_set_text (GTK_ENTRY (w), ""); + gtk_widget_set_sensitive (w, FALSE); + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label")); + gtk_label_set_text (GTK_LABEL (w), _("Ref")); + + /* Label */ + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label")); + gtk_entry_set_text (GTK_ENTRY (w), ""); + gtk_widget_set_sensitive (w, FALSE); + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label_label")); + gtk_label_set_text (GTK_LABEL (w), _("Ref")); + + } else { + SPObject *obj = (SPObject*)item; + + /* ID */ + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id")); + gtk_entry_set_text (GTK_ENTRY (w), obj->id); + gtk_widget_set_sensitive (w, TRUE); + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label")); + gtk_label_set_markup_with_mnemonic (GTK_LABEL (w), _("_Id")); + + /* Label */ + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label")); + gtk_entry_set_text (GTK_ENTRY (w), obj->defaultLabel()); + gtk_widget_set_sensitive (w, TRUE); + w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label_label")); + } + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE)); + + +} // end of sp_item_widget_setup() + + + +static void +sp_item_widget_sensitivity_toggled (GtkWidget *widget, SPWidget *spw) +{ + if (gtk_object_get_data (GTK_OBJECT (spw), "blocked")) + return; + + SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem(); + g_return_if_fail (item != NULL); + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE)); + + item->setLocked(gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))); + + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "ItemDialog:insensitive"); + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE)); +} + +void +sp_item_widget_hidden_toggled(GtkWidget *widget, SPWidget *spw) +{ + if (gtk_object_get_data (GTK_OBJECT (spw), "blocked")) + return; + + SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem(); + g_return_if_fail (item != NULL); + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE)); + + item->setExplicitlyHidden(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); + + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "ItemDialog:visiblity"); + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE)); +} + +static void +sp_item_widget_label_changed (GtkWidget *widget, SPWidget *spw) +{ + if (gtk_object_get_data (GTK_OBJECT (spw), "blocked")) + return; + + SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem(); + g_return_if_fail (item != NULL); + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (TRUE)); + + /* Retrieve the label widget for the object's id */ + GtkWidget *id_entry = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id")); + gchar *id = (gchar *) gtk_entry_get_text (GTK_ENTRY (id_entry)); + g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_'); + GtkWidget *id_label = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label")); + if (!strcmp (id, SP_OBJECT_ID(item))) { + gtk_label_set_markup_with_mnemonic (GTK_LABEL (id_label), _("_Id")); + } else if (!*id || !isalnum (*id)) { + gtk_label_set_text (GTK_LABEL (id_label), _("Id invalid! ")); + } else if (SP_ACTIVE_DOCUMENT->getObjectById(id) != NULL) { + gtk_label_set_text (GTK_LABEL (id_label), _("Id exists! ")); + } else { + SPException ex; + gtk_label_set_markup_with_mnemonic (GTK_LABEL (id_label), _("_Id")); + SP_EXCEPTION_INIT (&ex); + sp_object_setAttribute (SP_OBJECT (item), "id", id, &ex); + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "ItemDialog:id"); + } + + /* Retrieve the label widget for the object's label */ + GtkWidget *label_entry = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label")); + gchar *label = (gchar *)gtk_entry_get_text (GTK_ENTRY (label_entry)); + g_assert(label != NULL); + + /* Give feedback on success of setting the drawing object's label + * using the widget's label text + */ + SPObject *obj = (SPObject*)item; + if (strcmp (label, obj->defaultLabel())) { + obj->setLabel(label); + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "inkscape:label"); + } + + /* Retrieve the title */ + GtkWidget *w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "title")); + gchar *title = (gchar *)gtk_entry_get_text (GTK_ENTRY (w)); + if (title != NULL) { + obj->setTitle(title); + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "title"); + } + + /* Retrieve the description */ + gchar *desc = NULL; /* TODO: get text from text buffer */ + if (desc != NULL) { + obj->setDesc(desc); + sp_document_maybe_done (SP_ACTIVE_DOCUMENT, "desc"); + } + + gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE)); + +} // end of sp_item_widget_label_changed() + + +/** + * \brief Dialog + * + */ +void +sp_item_dialog (void) +{ + if (dlg == NULL) { + + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_ITEM), title); + + dlg = sp_window_new (title, TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) { + gtk_window_resize ((GtkWindow *) dlg, w, h); + } + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd); + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_item_dialog_destroy), dlg); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_item_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_item_dialog_delete), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); + + // Dialog-specific stuff + GtkWidget *itemw = sp_item_widget_new (); + gtk_widget_show (itemw); + gtk_container_add (GTK_CONTAINER (dlg), itemw); + + } + + gtk_window_present ((GtkWindow *) dlg); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/item-properties.h b/src/dialogs/item-properties.h new file mode 100644 index 000000000..a81034aae --- /dev/null +++ b/src/dialogs/item-properties.h @@ -0,0 +1,38 @@ +#ifndef __SP_ITEM_PROPERTIES_H__ +#define __SP_ITEM_PROPERTIES_H__ + +/** + * \brief Display settings dialog + * + * Author: + * Lauris Kaplinski + * + * Copyright (C) 2001 Ximian, Inc. + * + */ + +#include + + + +#include +#include "../forward.h" + +GtkWidget *sp_item_widget_new (void); + +void sp_item_dialog (void); + + + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/layer-properties.cpp b/src/dialogs/layer-properties.cpp new file mode 100644 index 000000000..8806e97f0 --- /dev/null +++ b/src/dialogs/layer-properties.cpp @@ -0,0 +1,199 @@ +/** + * + * \brief Dialog for renaming layers + * + * Author: + * Bryce W. Harrington + * + * Copyright (C) 2004 Bryce Harrington + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include "inkscape.h" +#include "desktop.h" +#include "document.h" +#include "message-stack.h" +#include "desktop-handles.h" +#include "layer-fns.h" +#include "sp-object.h" + +#include "layer-properties.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +LayerPropertiesDialog::LayerPropertiesDialog() +: _strategy(NULL), _desktop(NULL), _layer(NULL) +{ + GtkWidget *dlg = GTK_WIDGET(gobj()); + g_assert(dlg); + + Gtk::VBox *mainVBox = get_vbox(); + + // Layer name widgets + _layer_name_entry.set_activates_default(true); + _layer_name_hbox.pack_end(_layer_name_entry, false, false, 4); + _layer_name_label.set_label(_("Layer name:")); + _layer_name_hbox.pack_end(_layer_name_label, false, false, 4); + mainVBox->pack_start(_layer_name_hbox, false, false, 4); + + // Buttons + _close_button.set_use_stock(true); + _close_button.set_label(Gtk::Stock::CANCEL.id); + _close_button.set_flags(Gtk::CAN_DEFAULT); + + _apply_button.set_use_underline(true); + _apply_button.set_flags(Gtk::CAN_DEFAULT); + + _close_button.signal_clicked() + .connect(sigc::mem_fun(*this, &LayerPropertiesDialog::_close)); + _apply_button.signal_clicked() + .connect(sigc::mem_fun(*this, &LayerPropertiesDialog::_apply)); + + signal_delete_event().connect( + sigc::bind_return( + sigc::hide(sigc::mem_fun(*this, &LayerPropertiesDialog::_close)), + true + ) + ); + + add_action_widget(_close_button, Gtk::RESPONSE_CLOSE); + add_action_widget(_apply_button, Gtk::RESPONSE_APPLY); + + _apply_button.grab_default(); + + show_all_children(); +} + +LayerPropertiesDialog::~LayerPropertiesDialog() { + _setDesktop(NULL); + _setLayer(NULL); +} + +void LayerPropertiesDialog::_showDialog(LayerPropertiesDialog::Strategy &strategy, + SPDesktop *desktop, SPObject *layer) +{ + LayerPropertiesDialog *dialog = new LayerPropertiesDialog(); + + dialog->_strategy = &strategy; + dialog->_setDesktop(desktop); + dialog->_setLayer(layer); + + dialog->_strategy->setup(*dialog); + + dialog->set_modal(true); + desktop->setWindowTransient (dialog->gobj()); + dialog->property_destroy_with_parent() = true; + + dialog->show(); + dialog->present(); +} + +void +LayerPropertiesDialog::_apply() +{ + g_assert(_strategy != NULL); + + _strategy->perform(*this); + sp_document_done(SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP)); + + _close(); +} + +void +LayerPropertiesDialog::_close() +{ + _setLayer(NULL); + _setDesktop(NULL); + destroy_(); + Glib::signal_idle().connect( + sigc::bind_return( + sigc::bind(sigc::ptr_fun(&::operator delete), this), + false + ) + ); +} + +void LayerPropertiesDialog::Rename::setup(LayerPropertiesDialog &dialog) { + SPDesktop *desktop=dialog._desktop; + dialog.set_title(_("Rename Layer")); + gchar const *name = desktop->currentLayer()->label(); + dialog._layer_name_entry.set_text(( name ? name : "" )); + dialog._apply_button.set_label(_("_Rename")); +} + +void LayerPropertiesDialog::Rename::perform(LayerPropertiesDialog &dialog) { + SPDesktop *desktop=dialog._desktop; + Glib::ustring name(dialog._layer_name_entry.get_text()); + desktop->currentLayer()->setLabel( + ( name.empty() ? NULL : (gchar *)name.c_str() ) + ); + sp_document_done(SP_DT_DOCUMENT(desktop)); + // TRANSLATORS: This means "The layer has been renamed" + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Renamed layer")); +} + +void LayerPropertiesDialog::Create::setup(LayerPropertiesDialog &dialog) { + dialog.set_title(_("Add Layer")); + dialog._layer_name_entry.set_text(""); + dialog._apply_button.set_label(_("_Add")); +} + +void LayerPropertiesDialog::Create::perform(LayerPropertiesDialog &dialog) { + SPDesktop *desktop=dialog._desktop; + SPObject *new_layer=Inkscape::create_layer( + desktop->currentRoot(), dialog._layer + ); + Glib::ustring name(dialog._layer_name_entry.get_text()); + if (!name.empty()) { + new_layer->setLabel((gchar *)name.c_str()); + } + SP_DT_SELECTION(desktop)->clear(); + desktop->setCurrentLayer(new_layer); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("New layer created.")); +} + +void LayerPropertiesDialog::_setDesktop(SPDesktop *desktop) { + if (desktop) { + Inkscape::GC::anchor (desktop); + } + if (_desktop) { + Inkscape::GC::release (_desktop); + } + _desktop = desktop; +} + +void LayerPropertiesDialog::_setLayer(SPObject *layer) { + if (layer) { + sp_object_ref(layer, NULL); + } + if (_layer) { + sp_object_unref(_layer, NULL); + } + _layer = layer; +} + +} // namespace +} // namespace +} // namespace + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/layer-properties.h b/src/dialogs/layer-properties.h new file mode 100644 index 000000000..15404b2e5 --- /dev/null +++ b/src/dialogs/layer-properties.h @@ -0,0 +1,110 @@ +/** + * + * \brief Dialog for renaming layers + * + * Author: + * Bryce W. Harrington + * + * Copyright (C) 2004 Bryce Harrington + * + * Released under GNU GPL. Read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_DIALOG_LAYER_PROPERTIES_H +#define INKSCAPE_DIALOG_LAYER_PROPERTIES_H + +#include +#include +#include +#include +#include +#include +#include + +#include "selection.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +class LayerPropertiesDialog : public Gtk::Dialog { + public: + LayerPropertiesDialog(); + virtual ~LayerPropertiesDialog(); + + Glib::ustring getName() const { return "LayerPropertiesDialog"; } + + static void showRename(SPDesktop *desktop, SPObject *layer) { + _showDialog(Rename::instance(), desktop, layer); + } + static void showCreate(SPDesktop *desktop, SPObject *layer) { + _showDialog(Create::instance(), desktop, layer); + } + +protected: + struct Strategy { + virtual ~Strategy() {} + virtual void setup(LayerPropertiesDialog &)=0; + virtual void perform(LayerPropertiesDialog &)=0; + }; + struct Rename : public Strategy { + static Rename &instance() { static Rename instance; return instance; } + void setup(LayerPropertiesDialog &dialog); + void perform(LayerPropertiesDialog &dialog); + }; + struct Create : public Strategy { + static Create &instance() { static Create instance; return instance; } + void setup(LayerPropertiesDialog &dialog); + void perform(LayerPropertiesDialog &dialog); + }; + + friend class Rename; + friend class Create; + + Strategy *_strategy; + SPDesktop *_desktop; + SPObject *_layer; + + Gtk::HBox _layer_name_hbox; + Gtk::Label _layer_name_label; + Gtk::Entry _layer_name_entry; + + Gtk::Button _close_button; + Gtk::Button _apply_button; + + sigc::connection _destroy_connection; + + static LayerPropertiesDialog &_instance() { + static LayerPropertiesDialog instance; + return instance; + } + + void _setDesktop(SPDesktop *desktop); + void _setLayer(SPObject *layer); + + static void _showDialog(Strategy &strategy, SPDesktop *desktop, SPObject *layer); + void _apply(); + void _close(); + +private: + LayerPropertiesDialog(LayerPropertiesDialog const &); // no copy + LayerPropertiesDialog &operator=(LayerPropertiesDialog const &); // no assign +}; + +} // namespace +} // namespace +} // namespace + + +#endif //INKSCAPE_DIALOG_LAYER_PROPERTIES_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/makefile.in b/src/dialogs/makefile.in new file mode 100644 index 000000000..5f39e1899 --- /dev/null +++ b/src/dialogs/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +# Explicit so that it's the default rule. +all: + cd .. && $(MAKE) dialogs/all + +clean %.a %.o: + cd .. && $(MAKE) dialogs/$@ + +.PHONY: all clean + +OBJEXT = @OBJEXT@ + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/dialogs/object-attributes.cpp b/src/dialogs/object-attributes.cpp new file mode 100644 index 000000000..05370d1d0 --- /dev/null +++ b/src/dialogs/object-attributes.cpp @@ -0,0 +1,140 @@ +#define __SP_OBJECT_ATTRIBUTES_C__ + +/** + * \brief Generic properties editor + * + * Authors: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 1999-2005 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include "helper/window.h" +#include "macros.h" +#include "sp-anchor.h" +#include "sp-attribute-widget.h" + +struct SPAttrDesc { + gchar const *label; + gchar const *attribute; +}; + +static const SPAttrDesc anchor_desc[] = { + { N_("Href:"), "xlink:href"}, + { N_("Target:"), "target"}, + { N_("Type:"), "xlink:type"}, + // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/linking.html#AElementXLinkRoleAttribute + // Identifies the type of the related resource with an absolute URI + { N_("Role:"), "xlink:role"}, + // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/linking.html#AElementXLinkArcRoleAttribute + // For situations where the nature/role alone isn't enough, this offers an additional URI defining the purpose of the link. + { N_("Arcrole:"), "xlink:arcrole"}, + // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/linking.html#AElementXLinkTitleAttribute + { N_("Title:"), "xlink:title"}, + { N_("Show:"), "xlink:show"}, + // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/linking.html#AElementXLinkActuateAttribute + { N_("Actuate:"), "xlink:actuate"}, + { NULL, NULL} +}; + +static const SPAttrDesc image_desc[] = { + { N_("URL:"), "xlink:href"}, + { N_("X:"), "x"}, + { N_("Y:"), "y"}, + { N_("Width:"), "width"}, + { N_("Height:"), "height"}, + { NULL, NULL} +}; + + +static void +object_released (GtkObject *object, GtkWidget *widget) +{ + gtk_widget_destroy (widget); +} + + + +static void +window_destroyed (GtkObject *window, GtkObject *object) +{ + sp_signal_disconnect_by_data (object, window); +} + + + +static void +sp_object_attr_show_dialog ( SPObject *object, + const SPAttrDesc *desc, + const gchar *tag ) +{ + const gchar **labels, **attrs; + gint len, i; + gchar *title; + GtkWidget *w, *t; + + len = 0; + while (desc[len].label) len += 1; + + labels = (const gchar **)alloca (len * sizeof (char *)); + attrs = (const gchar **)alloca (len * sizeof (char *)); + + for (i = 0; i < len; i++) { + labels[i] = desc[i].label; + attrs[i] = desc[i].attribute; + } + + title = g_strdup_printf (_("%s attributes"), tag); + w = sp_window_new (title, TRUE); + g_free (title); + + t = sp_attribute_table_new (object, len, labels, attrs); + gtk_widget_show (t); + gtk_container_add (GTK_CONTAINER (w), t); + + g_signal_connect ( G_OBJECT (w), "destroy", + G_CALLBACK (window_destroyed), object ); + + g_signal_connect ( G_OBJECT (object), "release", + G_CALLBACK (object_released), w ); + + gtk_widget_show (w); + +} // end of sp_object_attr_show_dialog() + + + +void +sp_object_attributes_dialog (SPObject *object, const gchar *tag) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (SP_IS_OBJECT (object)); + g_return_if_fail (tag != NULL); + + if (!strcmp (tag, "Link")) { + sp_object_attr_show_dialog (object, anchor_desc, tag); + } else if (!strcmp (tag, "Image")) { + sp_object_attr_show_dialog (object, image_desc, tag); + } + +} // end of sp_object_attributes_dialog() + + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/object-attributes.h b/src/dialogs/object-attributes.h new file mode 100644 index 000000000..2fc562e16 --- /dev/null +++ b/src/dialogs/object-attributes.h @@ -0,0 +1,37 @@ +#ifndef __SP_OBJECT_ATTRIBUTES_H__ +#define __SP_OBJECT_ATTRIBUTES_H__ + +/** + * \brief Generic object attribute editor + * + * Author: + * Lauris Kaplinski + * + * Copyright (C) 2001 Ximian, Inc. + * + * Licensed under GNU GPL + */ + +#include + + + +#include +#include "../forward.h" + +void sp_object_attributes_dialog (SPObject *object, const gchar *tag); + + + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/object-properties.cpp b/src/dialogs/object-properties.cpp new file mode 100644 index 000000000..9cbf1c3ea --- /dev/null +++ b/src/dialogs/object-properties.cpp @@ -0,0 +1,318 @@ +#define __OBJECT_PROPERTIES_C__ + +/** + * \brief Fill, stroke, and stroke style dialog + * + * Authors: + * Lauris Kaplinski + * Frank Felfe + * bulia byak + * + * Copyright (C) 1999-2005 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include "helper/window.h" +#include "widgets/sp-widget.h" +#include "widgets/icon.h" +#include "macros.h" +#include "inkscape.h" +#include "fill-style.h" +#include "stroke-style.h" +#include "dialog-events.h" +#include "verbs.h" +#include "interface.h" +#include "style.h" +#include "inkscape-stock.h" +#include "prefs-utils.h" +#include "svg/css-ostringstream.h" +#include "desktop-handles.h" +#include "desktop-style.h" +#include "document.h" +#include "xml/repr.h" + +static GtkWidget *dlg = NULL; +static win_data wd; + +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.fillstroke"; + +static void sp_fillstroke_selection_modified ( Inkscape::Application *inkscape, Inkscape::Selection *selection, guint flags, GtkObject *base ); +static void sp_fillstroke_selection_changed ( Inkscape::Application *inkscape, Inkscape::Selection *selection, GtkObject *base ); +static void sp_fillstroke_opacity_changed (GtkAdjustment *a, SPWidget *dlg); + +static void +sp_object_properties_dialog_destroy (GtkObject *object, gpointer data) +{ + sp_signal_disconnect_by_data (INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; +} + +static gboolean +sp_object_properties_dialog_delete ( GtkObject *object, + GdkEvent *event, + gpointer data ) +{ + + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it + +} + + +void +sp_object_properties_page( GtkWidget *nb, + GtkWidget *page, + char *label, + char *dlg_name, + char *label_image ) +{ + GtkWidget *hb, *l, *px; + + hb = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hb); + + px = sp_icon_new( GTK_ICON_SIZE_MENU, label_image ); + gtk_widget_show (px); + gtk_box_pack_start (GTK_BOX (hb), px, FALSE, FALSE, 2); + + l = gtk_label_new_with_mnemonic (label); + gtk_widget_show (l); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + + gtk_widget_show (page); + gtk_notebook_append_page (GTK_NOTEBOOK (nb), page, hb); + gtk_object_set_data (GTK_OBJECT (dlg), dlg_name, page); +} + +void +sp_object_properties_dialog (void) +{ + if (!dlg) { + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_FILL_STROKE), title); + + dlg = sp_window_new (title, TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + if (x != 0 || y != 0) + gtk_window_move ((GtkWindow *) dlg, x, y); + else + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h); + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd ); + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg ); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_object_properties_dialog_destroy), dlg ); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_object_properties_dialog_delete), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_object_properties_dialog_delete), dlg ); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg ); + + GtkWidget *vb = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vb); + gtk_container_add (GTK_CONTAINER (dlg), vb); + + GtkWidget *nb = gtk_notebook_new (); + gtk_widget_show (nb); + gtk_box_pack_start (GTK_BOX (vb), nb, TRUE, TRUE, 0); + gtk_object_set_data (GTK_OBJECT (dlg), "notebook", nb); + + /* Fill page */ + { + GtkWidget *page = sp_fill_style_widget_new (); + sp_object_properties_page(nb, page, _("_Fill"), "fill", + INKSCAPE_STOCK_PROPERTIES_FILL_PAGE); + } + + /* Stroke paint page */ + { + GtkWidget *page = sp_stroke_style_paint_widget_new (); + sp_object_properties_page(nb, page, _("Stroke _paint"), "stroke-paint", + INKSCAPE_STOCK_PROPERTIES_STROKE_PAINT_PAGE); + } + + /* Stroke style page */ + { + GtkWidget *page = sp_stroke_style_line_widget_new (); + sp_object_properties_page(nb, page, _("Stroke st_yle"), "stroke-line", + INKSCAPE_STOCK_PROPERTIES_STROKE_PAGE); + } + + /* Opacity */ + + GtkWidget *o_vb = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vb), o_vb, FALSE, FALSE, 2); + gtk_object_set_data (GTK_OBJECT (dlg), "master_opacity", o_vb); + + GtkWidget *l_hb = gtk_hbox_new (FALSE, 4); + GtkWidget *l = gtk_label_new_with_mnemonic (_("Master _opacity")); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 1.0); + gtk_box_pack_start (GTK_BOX (l_hb), l, FALSE, FALSE, 4); + gtk_box_pack_start (GTK_BOX (o_vb), l_hb, FALSE, FALSE, 0); + + GtkWidget *hb = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (o_vb), hb, FALSE, FALSE, 0); + + GtkObject *a = gtk_adjustment_new (1.0, 0.0, 1.0, 0.01, 0.1, 0.0); + gtk_object_set_data(GTK_OBJECT(dlg), "master_opacity_adjustment", a); + + GtkWidget *s = gtk_hscale_new (GTK_ADJUSTMENT (a)); + gtk_scale_set_draw_value (GTK_SCALE (s), FALSE); + gtk_box_pack_start (GTK_BOX (hb), s, TRUE, TRUE, 4); + gtk_label_set_mnemonic_widget (GTK_LABEL(l), s); + + GtkWidget *sb = gtk_spin_button_new (GTK_ADJUSTMENT (a), 0.01, 3); + gtk_box_pack_start (GTK_BOX (hb), sb, FALSE, FALSE, 0); + + gtk_signal_connect ( a, "value_changed", + GTK_SIGNAL_FUNC (sp_fillstroke_opacity_changed), + dlg ); + + gtk_widget_show_all (o_vb); + + // these callbacks are only for the master opacity update; the tabs above take care of themselves + g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (sp_fillstroke_selection_changed), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (sp_fillstroke_selection_modified), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_fillstroke_selection_changed), dlg ); + + sp_fillstroke_selection_changed(NULL, NULL, NULL); + + gtk_widget_show (dlg); + + } else { + gtk_window_present (GTK_WINDOW (dlg)); + } + +} // end of sp_object_properties_dialog() + +void sp_object_properties_fill (void) +{ + sp_object_properties_dialog (); + GtkWidget *nb = (GtkWidget *)gtk_object_get_data (GTK_OBJECT (dlg), "notebook"); + gtk_notebook_set_page (GTK_NOTEBOOK (nb), 0); +} + +void sp_object_properties_stroke (void) +{ + sp_object_properties_dialog (); + GtkWidget *nb = (GtkWidget *)gtk_object_get_data (GTK_OBJECT (dlg), "notebook"); + gtk_notebook_set_page (GTK_NOTEBOOK (nb), 1); +} + +void sp_object_properties_stroke_style (void) +{ + sp_object_properties_dialog (); + GtkWidget *nb = (GtkWidget *)gtk_object_get_data (GTK_OBJECT (dlg), "notebook"); + gtk_notebook_set_page (GTK_NOTEBOOK (nb), 2); +} + + + +static void +sp_fillstroke_selection_modified ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + guint flags, + GtkObject *base ) +{ + sp_fillstroke_selection_changed ( inkscape, selection, base ); +} + + +static void +sp_fillstroke_selection_changed ( Inkscape::Application *inkscape, + Inkscape::Selection *selection, + GtkObject *base ) +{ + if (gtk_object_get_data (GTK_OBJECT (dlg), "blocked")) + return; + gtk_object_set_data (GTK_OBJECT (dlg), "blocked", GUINT_TO_POINTER (TRUE)); + + GtkWidget *opa = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dlg), "master_opacity")); + GtkAdjustment *a = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(dlg), "master_opacity_adjustment")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection + int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_MASTEROPACITY); + + switch (result) { + case QUERY_STYLE_NOTHING: + gtk_widget_set_sensitive (opa, FALSE); + break; + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently + case QUERY_STYLE_MULTIPLE_SAME: + gtk_widget_set_sensitive (opa, TRUE); + gtk_adjustment_set_value(a, SP_SCALE24_TO_FLOAT(query->opacity.value)); + break; + } + + g_free (query); + gtk_object_set_data (GTK_OBJECT (dlg), "blocked", GUINT_TO_POINTER (FALSE)); +} + +static void +sp_fillstroke_opacity_changed (GtkAdjustment *a, SPWidget *dlg) +{ + if (gtk_object_get_data (GTK_OBJECT (dlg), "blocked")) + return; + + gtk_object_set_data (GTK_OBJECT (dlg), "blocked", GUINT_TO_POINTER (TRUE)); + + SPCSSAttr *css = sp_repr_css_attr_new (); + + Inkscape::CSSOStringStream os; + os << CLAMP (a->value, 0.0, 1.0); + sp_repr_css_set_property (css, "opacity", os.str().c_str()); + + sp_desktop_set_style (SP_ACTIVE_DESKTOP, css); + + sp_repr_css_attr_unref (css); + + sp_document_maybe_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP), "fillstroke:opacity"); + + gtk_object_set_data (GTK_OBJECT (dlg), "blocked", GUINT_TO_POINTER (FALSE)); +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/object-properties.h b/src/dialogs/object-properties.h new file mode 100644 index 000000000..c78142095 --- /dev/null +++ b/src/dialogs/object-properties.h @@ -0,0 +1,34 @@ +#ifndef __OBJECT_PROPERTIES_H__ +#define __OBJECT_PROPERTIES_H__ + +/** + * \brief Basic object style dialog + * + * Authors: + * Lauris Kaplinski + * Frank Felfe + * + * Copyright (C) 2000-2001 Lauris Kaplinski and Frank Felfe + * Copyright (C) 2001-2002 Ximian, Inc. and Lauris Kaplinski + * + * Released under GNU GPL + */ + +void sp_object_properties_dialog (void); + +void sp_object_properties_fill (void); +void sp_object_properties_stroke (void); +void sp_object_properties_stroke_style (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/rdf.cpp b/src/dialogs/rdf.cpp new file mode 100644 index 000000000..d9949c05b --- /dev/null +++ b/src/dialogs/rdf.cpp @@ -0,0 +1,1002 @@ +/** + * \brief RDF manipulation functions + * + * FIXME: move these to xml/ instead of dialogs/ + * + * Authors: + * Kees Cook + * + * Copyright (C) 2004 Kees Cook + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + + + +#include "xml/repr.h" +#include "rdf.h" +#include "sp-item-group.h" + +/* + + Example RDF XML from various places... + + + + title of work + year + description of work + + creator + + + holder + + + + + + + + + + SVG Road Signs + + John Cliff + + + + + + + + + + + + + + +Bag example: + + + +open clip art logo +images +logo +clip art +ocal +logotype +filetype + + + + +*/ + +struct rdf_double_t rdf_license_empty [] = { + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a_sa [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { "cc:requires", "http://web.resource.org/cc/ShareAlike", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a_nd [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a_nc [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a_nc_sa [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { "cc:requires", "http://web.resource.org/cc/ShareAlike", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_cc_a_nc_nd [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_gpl [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { "cc:requires", "http://web.resource.org/cc/ShareAlike", }, + { "cc:requires", "http://web.resource.org/cc/SourceCode", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_pd [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { NULL, NULL } +}; + +struct rdf_double_t rdf_license_freeart [] = { + { "cc:permits", "http://web.resource.org/cc/Reproduction", }, + { "cc:permits", "http://web.resource.org/cc/Distribution", }, + { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", }, + { "cc:requires", "http://web.resource.org/cc/ShareAlike", }, + { "cc:requires", "http://web.resource.org/cc/Notice", }, + { "cc:requires", "http://web.resource.org/cc/Attribution", }, + { NULL, NULL } +}; + +struct rdf_license_t rdf_licenses [] = { + { "CC Attribution", + "http://creativecommons.org/licenses/by/2.0/", + rdf_license_cc_a, + }, + + { "CC Attribution-ShareAlike", + "http://creativecommons.org/licenses/by-sa/2.0/", + rdf_license_cc_a_sa, + }, + + { "CC Attribution-NoDerivs", + "http://creativecommons.org/licenses/by-nd/2.0/", + rdf_license_cc_a_nd, + }, + + { "CC Attribution-NonCommercial", + "http://creativecommons.org/licenses/by-nc/2.0/", + rdf_license_cc_a_nc, + }, + + { "CC Attribution-NonCommercial-ShareAlike", + "http://creativecommons.org/licenses/by-nc-sa/2.0/", + rdf_license_cc_a_nc_sa, + }, + + { "CC Attribution-NonCommercial-NoDerivs", + "http://creativecommons.org/licenses/by-nc-nd/2.0/", + rdf_license_cc_a_nc_nd, + }, + + { "GNU General Public License", + "http://creativecommons.org/licenses/GPL/2.0/", + rdf_license_gpl, + }, + + { "GNU Lesser General Public License", + "http://creativecommons.org/licenses/LGPL/2.1/", + rdf_license_gpl, + }, + + { "Public Domain", + "http://web.resource.org/cc/PublicDomain", + rdf_license_pd, + }, + + { "FreeArt", + "http://artlibre.org/licence.php/lalgb.html", + rdf_license_freeart, + }, + + { NULL, NULL, rdf_license_empty, } +}; + +#define XML_TAG_NAME_SVG "svg:svg" +#define XML_TAG_NAME_METADATA "svg:metadata" +#define XML_TAG_NAME_RDF "rdf:RDF" +#define XML_TAG_NAME_WORK "cc:Work" +#define XML_TAG_NAME_LICENSE "cc:License" + +// Remember when using the "title" and "tip" elements to pass them through +// the localization functions when you use them! +struct rdf_work_entity_t rdf_work_entities [] = { + { "title", N_("Title"), "dc:title", RDF_CONTENT, + N_("Name by which this document is formally known."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "date", N_("Date"), "dc:date", RDF_CONTENT, + N_("Date associated with the creation of this document (YYYY-MM-DD)."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "format", N_("Format"), "dc:format", RDF_CONTENT, + N_("The physical or digital manifestation of this document (MIME type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED, + }, + { "type", N_("Type"), "dc:type", RDF_RESOURCE, + N_("Type of document (DCMI Type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED, + }, + + { "creator", N_("Creator"), "dc:creator", RDF_AGENT, + N_("Name of entity primarily responsible for making the content of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "rights", N_("Rights"), "dc:rights", RDF_AGENT, + N_("Name of entity with rights to the Intellectual Property of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "publisher", N_("Publisher"), "dc:publisher", RDF_AGENT, + N_("Name of entity responsible for making this document available."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + + { "identifier", N_("Identifier"), "dc:identifier", RDF_CONTENT, + N_("Unique URI to reference this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "source", N_("Source"), "dc:source", RDF_CONTENT, + N_("Unique URI to reference the source of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "relation", N_("Relation"), "dc:relation", RDF_CONTENT, + N_("Unique URI to a related document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "language", N_("Language"), "dc:language", RDF_CONTENT, + N_("Two-letter language tag with optional subtags for the language of this document. (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + { "subject", N_("Keywords"), "dc:subject", RDF_BAG, + N_("The topic of this document as comma-separated key words, phrases, or classifications."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content. + // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/ + { "coverage", N_("Coverage"), "dc:coverage", RDF_CONTENT, + N_("Extent or scope of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC, + }, + + { "description", N_("Description"), "dc:description", RDF_CONTENT, + N_("A short account of the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC, + }, + + // FIXME: need to handle 1 agent per line of input + { "contributor", N_("Contributors"), "dc:contributor", RDF_AGENT, + N_("Names of entities responsible for making contributions to the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC, + }, + + // TRANSLATORS: URL to a page that defines the license for the document + { "license_uri", N_("URI"), "cc:license", RDF_RESOURCE, + // TRANSLATORS: this is where you put a URL to a page that defines the license + N_("URI to this document's license's namespace definition."), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL, + }, + + // TRANSLATORS: fragment of XML representing the license of the document + { "license_fragment", N_("Fragment"), "License", RDF_XML, + N_("XML fragment for the RDF 'License' section."), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL, + }, + + { NULL, NULL, NULL, RDF_CONTENT, + NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED, + } +}; + +/** + * \brief Retrieves a known RDF/Work entity by name + * \return A pointer to an RDF/Work entity + * \param name The desired RDF/Work entity + * + */ +struct rdf_work_entity_t * +rdf_find_entity(gchar const * name) +{ + struct rdf_work_entity_t *entity; + for (entity=rdf_work_entities; entity->name; entity++) { + if (strcmp(entity->name,name)==0) break; + } + if (entity->name) return entity; + return NULL; +} + +/* + * Takes the inkscape rdf struct and spits out a static RDF, which is only + * useful for testing. We must merge the rdf struct into the XML DOM for + * changes to be saved. + */ +/* + + Since g_markup_printf_escaped doesn't exist for most people's glib + right now, this function will remain commented out since it's only + for generic debug anyway. --Kees + +gchar * +rdf_string(struct rdf_t * rdf) +{ + gulong overall=0; + gchar *string=NULL; + + gchar *rdf_head="\ +\ +"; + gchar *work_head="\ +\ + \ +"; + gchar *work_title=NULL; + gchar *work_date=NULL; + gchar *work_description=NULL; + gchar *work_creator=NULL; + gchar *work_owner=NULL; + gchar *work_source=NULL; + gchar *work_license=NULL; + gchar *license_head=NULL; + gchar *license=NULL; + gchar *license_end="\n"; + gchar *work_end="\n"; + gchar *rdf_end="\n"; + + if (rdf && rdf->work_title && rdf->work_title[0]) { + work_title=g_markup_printf_escaped(" %s\n", + rdf->work_title); + overall+=strlen(work_title); + } + if (rdf && rdf->work_date && rdf->work_date[0]) { + work_date=g_markup_printf_escaped(" %s\n", + rdf->work_date); + overall+=strlen(work_date); + } + if (rdf && rdf->work_description && rdf->work_description[0]) { + work_description=g_markup_printf_escaped(" %s\n", + rdf->work_description); + overall+=strlen(work_description); + } + if (rdf && rdf->work_creator && rdf->work_creator[0]) { + work_creator=g_markup_printf_escaped(" \ + %s\ + \n", + rdf->work_creator); + overall+=strlen(work_creator); + } + if (rdf && rdf->work_owner && rdf->work_owner[0]) { + work_owner=g_markup_printf_escaped(" \ + %s\ + \n", + rdf->work_owner); + overall+=strlen(work_owner); + } + if (rdf && rdf->work_source && rdf->work_source[0]) { + work_source=g_markup_printf_escaped(" \n", + rdf->work_source); + overall+=strlen(work_source); + } + if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) { + work_license=g_markup_printf_escaped(" \n", + rdf->license->work_rdf); + overall+=strlen(work_license); + + license_head=g_markup_printf_escaped("\n", + rdf->license->work_rdf); + overall+=strlen(license_head); + overall+=strlen(rdf->license->license_rdf); + overall+=strlen(license_end); + } + + overall+=strlen(rdf_head)+strlen(rdf_end); + overall+=strlen(work_head)+strlen(work_end); + + overall++; // NULL term + + if (!(string=(gchar*)g_malloc(overall))) { + return NULL; + } + + string[0]='\0'; + strcat(string,rdf_head); + strcat(string,work_head); + + if (work_title) strcat(string,work_title); + if (work_date) strcat(string,work_date); + if (work_description) strcat(string,work_description); + if (work_creator) strcat(string,work_creator); + if (work_owner) strcat(string,work_owner); + if (work_source) strcat(string,work_source); + if (work_license) strcat(string,work_license); + + strcat(string,work_end); + if (license_head) { + strcat(string,license_head); + strcat(string,rdf->license->license_rdf); + strcat(string,license_end); + } + strcat(string,rdf_end); + + return string; +} +*/ + + +/** + * \brief Pull the text out of an RDF entity, depends on how it's stored + * \return A pointer to the entity's static contents as a string + * \param repr The XML element to extract from + * \param entity The desired RDF/Work entity + * + */ +const gchar * +rdf_get_repr_text ( Inkscape::XML::Node * repr, struct rdf_work_entity_t * entity ) +{ + g_return_val_if_fail (repr != NULL, NULL); + g_return_val_if_fail (entity != NULL, NULL); + static gchar * bag = NULL; + gchar * holder = NULL; + + Inkscape::XML::Node * temp=NULL; + switch (entity->datatype) { + case RDF_CONTENT: + temp = sp_repr_children(repr); + if ( temp == NULL ) return NULL; + + return temp->content(); + + case RDF_AGENT: + temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 ); + if ( temp == NULL ) return NULL; + + temp = sp_repr_lookup_name ( temp, "dc:title", 1 ); + if ( temp == NULL ) return NULL; + + temp = sp_repr_children(temp); + if ( temp == NULL ) return NULL; + + return temp->content(); + + case RDF_RESOURCE: + return repr->attribute("rdf:resource"); + + case RDF_XML: + return "xml goes here"; + + case RDF_BAG: + /* clear the static string. yucky. */ + if (bag) g_free(bag); + bag = NULL; + + temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 ); + if ( temp == NULL ) { + /* backwards compatible: read contents */ + temp = sp_repr_children(repr); + if ( temp == NULL ) return NULL; + + return temp->content(); + } + + for ( temp = sp_repr_children(temp) ; + temp ; + temp = sp_repr_next(temp) ) { + if (!strcmp(temp->name(),"rdf:li") && + temp->firstChild()) { + const gchar * str = temp->firstChild()->content(); + if (bag) { + holder = bag; + bag = g_strconcat(holder, ", ", str, NULL); + g_free(holder); + } + else { + bag = g_strdup(str); + } + } + } + return bag; + + default: + break; + } + return NULL; +} + +unsigned int +rdf_set_repr_text ( Inkscape::XML::Node * repr, + struct rdf_work_entity_t * entity, + gchar const * text ) +{ + g_return_val_if_fail ( repr != NULL, 0); + g_return_val_if_fail ( entity != NULL, 0); + g_return_val_if_fail ( text != NULL, 0); + gchar * str = NULL; + gchar** strlist = NULL; + int i; + + Inkscape::XML::Node * temp=NULL; + Inkscape::XML::Node * child=NULL; + Inkscape::XML::Node * parent=repr; + switch (entity->datatype) { + case RDF_CONTENT: + temp = sp_repr_children(parent); + if ( temp == NULL ) { + temp = sp_repr_new_text( text ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + + return TRUE; + } + else { + temp->setContent(text); + return TRUE; + } + + case RDF_AGENT: + temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 ); + if ( temp == NULL ) { + temp = sp_repr_new ( "cc:Agent" ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + } + parent = temp; + + temp = sp_repr_lookup_name ( parent, "dc:title", 1 ); + if ( temp == NULL ) { + temp = sp_repr_new ( "dc:title" ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + } + parent = temp; + + temp = sp_repr_children(parent); + if ( temp == NULL ) { + temp = sp_repr_new_text( text ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + + return TRUE; + } + else { + temp->setContent(text); + return TRUE; + } + + case RDF_RESOURCE: + parent->setAttribute("rdf:resource", text ); + return true; + + case RDF_XML: + return 1; + + case RDF_BAG: + /* find/create the rdf:Bag item */ + temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 ); + if ( temp == NULL ) { + /* backward compatibility: drop the dc:subject contents */ + while ( (temp = sp_repr_children( parent )) ) { + parent->removeChild(temp); + } + + temp = sp_repr_new ( "rdf:Bag" ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + } + parent = temp; + + /* toss all the old list items */ + while ( (temp = sp_repr_children( parent )) ) { + parent->removeChild(temp); + } + + /* chop our list up on commas */ + strlist = g_strsplit( text, ",", 0); + + for (i = 0; (str = strlist[i]); i++) { + temp = sp_repr_new ( "rdf:li" ); + g_return_val_if_fail (temp != NULL, 0); + + parent->appendChild(temp); + Inkscape::GC::release(temp); + + child = sp_repr_new_text( g_strstrip(str) ); + g_return_val_if_fail (child != NULL, 0); + + temp->appendChild(child); + Inkscape::GC::release(child); + } + g_strfreev( strlist ); + + return 1; + + default: + break; + } + return 0; +} + +Inkscape::XML::Node * +rdf_get_rdf_root_repr ( SPDocument * doc, bool build ) +{ + g_return_val_if_fail (doc != NULL, NULL); + g_return_val_if_fail (doc->rroot != NULL, NULL); + + Inkscape::XML::Node * rdf = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_RDF ); + + if (rdf == NULL) { + //printf("missing XML '%s'\n",XML_TAG_NAME_RDF); + if (!build) return NULL; + + Inkscape::XML::Node * svg = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_SVG ); + g_return_val_if_fail ( svg != NULL, NULL ); + + Inkscape::XML::Node * parent = sp_repr_lookup_name ( svg, XML_TAG_NAME_METADATA ); + if ( parent == NULL ) { + parent = sp_repr_new( XML_TAG_NAME_METADATA ); + g_return_val_if_fail ( parent != NULL, NULL); + + svg->appendChild(parent); + Inkscape::GC::release(parent); + } + + rdf = sp_repr_new( XML_TAG_NAME_RDF ); + g_return_val_if_fail (rdf != NULL, NULL); + + parent->appendChild(rdf); + Inkscape::GC::release(rdf); + } + + /* + * some implementations do not put RDF stuff inside , + * so we need to check for it and add it if we don't see it + */ + Inkscape::XML::Node * want_metadata = sp_repr_parent ( rdf ); + g_return_val_if_fail (want_metadata != NULL, NULL); + if (strcmp( want_metadata->name(), XML_TAG_NAME_METADATA )) { + Inkscape::XML::Node * metadata = sp_repr_new( XML_TAG_NAME_METADATA ); + g_return_val_if_fail (metadata != NULL, NULL); + + /* attach the metadata node */ + want_metadata->appendChild(metadata); + Inkscape::GC::release(metadata); + + /* move the RDF into it */ + Inkscape::GC::anchor(rdf); + sp_repr_unparent ( rdf ); + metadata->appendChild(rdf); + Inkscape::GC::release(rdf); + } + + return rdf; +} + +Inkscape::XML::Node * +rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build ) +{ + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (doc != NULL, NULL); + g_return_val_if_fail (doc->rroot != NULL, NULL); + + Inkscape::XML::Node * rdf = rdf_get_rdf_root_repr ( doc, build ); + if (!rdf) return NULL; + + Inkscape::XML::Node * xml = sp_repr_lookup_name ( rdf, name ); + if (xml == NULL) { + //printf("missing XML '%s'\n",name); + if (!build) return NULL; + + xml = sp_repr_new( name ); + g_return_val_if_fail (xml != NULL, NULL); + + xml->setAttribute("rdf:about", "" ); + + rdf->appendChild(xml); + Inkscape::GC::release(xml); + } + + return xml; +} + +Inkscape::XML::Node * +rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build ) +{ + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (doc != NULL, NULL); + g_return_val_if_fail (doc->rroot != NULL, NULL); + + Inkscape::XML::Node * work = rdf_get_xml_repr ( doc, XML_TAG_NAME_WORK, build ); + if (!work) return NULL; + + Inkscape::XML::Node * item = sp_repr_lookup_name ( work, name, 1 ); + if (item == NULL) { + //printf("missing XML '%s'\n",name); + if (!build) return NULL; + + item = sp_repr_new( name ); + g_return_val_if_fail (item != NULL, NULL); + + work->appendChild(item); + Inkscape::GC::release(item); + } + + return item; +} + + + +/** + * \brief Retrieves a known RDF/Work entity's contents from the document XML by name + * \return A pointer to the entity's static contents as a string, or NULL if no entity exists + * \param entity The desired RDF/Work entity + * + */ +const gchar * +rdf_get_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity) +{ + g_return_val_if_fail (doc != NULL, NULL); + if ( entity == NULL ) return NULL; + //printf("want '%s'\n",entity->title); + + Inkscape::XML::Node * item; + if ( entity->datatype == RDF_XML ) { + item = rdf_get_xml_repr ( doc, entity->tag, FALSE ); + } + else { + item = rdf_get_work_repr( doc, entity->tag, FALSE ); + } + if ( item == NULL ) return NULL; + + const gchar * result = rdf_get_repr_text ( item, entity ); + //printf("found '%s' == '%s'\n", entity->title, result ); + return result; +} + +/** + * \brief Stores a string into a named RDF/Work entity in the document XML + * \param entity The desired RDF/Work entity to replace + * \param string The string to replace the entity contents with + * + */ +unsigned int +rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity, + const gchar * text) +{ + g_return_val_if_fail ( entity != NULL, 0 ); + if (text == NULL) { + // FIXME: on a "NULL" text, delete the entity. For now, blank it. + text=""; + } + + /* + printf("changing '%s' (%s) to '%s'\n", + entity->title, + entity->tag, + text); + */ + + Inkscape::XML::Node * item = rdf_get_work_repr( doc, entity->tag, TRUE ); + g_return_val_if_fail ( item != NULL, 0 ); + + return rdf_set_repr_text ( item, entity, text ); +} + +#undef DEBUG_MATCH + +bool +rdf_match_license ( Inkscape::XML::Node * repr, struct rdf_license_t * license ) +{ + g_assert ( repr != NULL ); + g_assert ( license != NULL ); + + bool result=TRUE; +#ifdef DEBUG_MATCH + printf("checking against '%s'\n",license->name); +#endif + + int count = 0; + for (struct rdf_double_t * details = license->details; + details->name; details++ ) { + count++; + } + bool * matched = (bool*)calloc(count,sizeof(bool)); + + for (Inkscape::XML::Node * current = sp_repr_children ( repr ); + current; + current = sp_repr_next ( current ) ) { + + gchar const * attr = current->attribute("rdf:resource"); + if ( attr == NULL ) continue; + +#ifdef DEBUG_MATCH + printf("\texamining '%s' => '%s'\n", current->name(), attr); +#endif + + bool found_match=FALSE; + for (int i=0; iname(), license->details[i].name); + printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource); +#endif + + if (!strcmp( current->name(), + license->details[i].name ) && + !strcmp( attr, + license->details[i].resource )) { + matched[i]=TRUE; + found_match=TRUE; +#ifdef DEBUG_MATCH + printf("\t\tgood!\n"); +#endif + break; + } + } + if (!found_match) { + // if we checked each known item of the license + // and didn't find it, we must abort + result=FALSE; +#ifdef DEBUG_MATCH + printf("\t\tno '%s' element matched XML (bong)!\n",license->name); +#endif + break; + } + } +#ifdef DEBUG_MATCH + if (result) printf("\t\tall XML found matching elements!\n"); +#endif + for (int i=0; result && iname); +#endif + } + } + +#ifdef DEBUG_MATCH + printf("\t\tall '%s' elements used to match!\n",license->name); +#endif + + free(matched); + +#ifdef DEBUG_MATCH + if (result) printf("matched '%s'\n",license->name); +#endif + return result; +} + +/** + * \brief Attempts to match and retrieve a known RDF/License from the document XML + * \return A pointer to the static RDF license structure + * + */ +struct rdf_license_t * +rdf_get_license(SPDocument * document) +{ + Inkscape::XML::Node * repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE ); + if (repr) { + for (struct rdf_license_t * license = rdf_licenses; + license->name; license++ ) { + if ( rdf_match_license ( repr, license ) ) return license; + } + } +#ifdef DEBUG_MATCH + else { + printf("no license XML\n"); + } +#endif + return NULL; +} + +/** + * \brief Stores an RDF/License XML in the document XML + * \param document Which document to update + * \param license The desired RDF/License structure to store; NULL drops old license, so can be used for proprietary license. + * + */ +void +rdf_set_license(SPDocument * document, struct rdf_license_t const * license) +{ + // drop old license section + Inkscape::XML::Node * repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE ); + if (repr) sp_repr_unparent(repr); + + if (!license) return; + + // build new license section + repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, TRUE ); + g_assert ( repr ); + + repr->setAttribute("rdf:about", license->uri ); + + for (struct rdf_double_t * detail = license->details; + detail->name; detail++) { + Inkscape::XML::Node * child = sp_repr_new( detail->name ); + g_assert ( child != NULL ); + + child->setAttribute("rdf:resource", detail->resource ); + repr->appendChild(child); + Inkscape::GC::release(child); + } +} + +struct rdf_entity_default_t { + gchar const * name; + gchar const * text; +}; +struct rdf_entity_default_t rdf_defaults[] = { + { "format", "image/svg+xml", }, + { "type", "http://purl.org/dc/dcmitype/StillImage", }, + { NULL, NULL, } +}; + +void +rdf_set_defaults ( SPDocument * document ) +{ + g_assert ( document != NULL ); + + // Create metadata node if it doesn't already exist + if (!sp_item_group_get_child_by_name ((SPGroup *) document->root, NULL, + XML_TAG_NAME_METADATA)) { + // create repr + Inkscape::XML::Node * rnew = sp_repr_new (XML_TAG_NAME_METADATA); + // insert into the document + document->rroot->addChild(rnew, NULL); + // clean up + Inkscape::GC::release(rnew); + } + + /* install defaults */ + for ( struct rdf_entity_default_t * rdf_default = rdf_defaults; + rdf_default->name; + rdf_default++) { + struct rdf_work_entity_t * entity = rdf_find_entity ( rdf_default->name ); + g_assert ( entity != NULL ); + + if ( rdf_get_work_entity ( document, entity ) == NULL ) { + rdf_set_work_entity ( document, entity, rdf_default->text ); + } + } +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/rdf.h b/src/dialogs/rdf.h new file mode 100644 index 000000000..29da859c2 --- /dev/null +++ b/src/dialogs/rdf.h @@ -0,0 +1,124 @@ +/** + * + * \brief headers for RDF types + * + * Authors: + * Kees Cook + * + * Copyright (C) 2004 Kees Cook + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + */ +#ifndef _RDF_H_ +#define _RDF_H_ + +#include + +#include +#include "document.h" + +// yeah, it's not a triple yet... +/** + * \brief Holds license name/resource doubles for rdf_license_t entries + */ +struct rdf_double_t { + gchar *name; + gchar *resource; +}; + +/** + * \brief Holds license name and RDF information + */ +struct rdf_license_t { + gchar *name; /* localized name of this license */ + gchar *uri; /* URL for the RDF/Work/license element */ + struct rdf_double_t *details; /* the license details */ +// gchar *fragment; /* XML contents for the RDF/License tag */ +}; + +extern rdf_license_t rdf_licenses []; + +/** + * \brief Describes how a given RDF entity is stored in XML + */ +enum RDFType { + RDF_CONTENT, // direct between-XML-tags content + RDF_AGENT, // requires the "Agent" hierarchy before doing content + RDF_RESOURCE, // stored in "rdf:resource" element + RDF_XML, // literal XML + RDF_BAG // rdf:Bag resources +}; + +/** + * \brief Describes how a given RDF entity should be edited + */ +enum RDF_Format { + RDF_FORMAT_LINE, // uses single line data (GtkEntry) + RDF_FORMAT_MULTILINE, // uses multiline data (GtkTextView) + RDF_FORMAT_SPECIAL // uses some other edit methods +}; + +enum RDF_Editable { + RDF_EDIT_GENERIC, // editable via generic widgets + RDF_EDIT_SPECIAL, // special widgets are needed + RDF_EDIT_HARDCODED // isn't editable +}; + +/** + * \brief Holds known RDF/Work tags + */ +struct rdf_work_entity_t { + char *name; /* unique name of this entity for internal reference */ + gchar *title; /* localized title of this entity for data entry */ + gchar *tag; /* namespace tag for the RDF/Work element */ + RDFType datatype; /* how to extract/inject the RDF information */ + gchar *tip; /* tool tip to explain the meaning of the entity */ + RDF_Format format; /* in what format is this data edited? */ + RDF_Editable editable;/* in what way is the data editable? */ +}; + +extern rdf_work_entity_t rdf_work_entities []; + +/** + * \brief Generic collection of RDF information for the RDF debug function + */ +struct rdf_t { + gchar* work_title; + gchar* work_date; + gchar* work_creator; + gchar* work_owner; + gchar* work_publisher; + gchar* work_type; + gchar* work_source; + gchar* work_subject; + gchar* work_description; + struct rdf_license_t* license; +}; + +struct rdf_work_entity_t * rdf_find_entity(gchar const * name); + +const gchar * rdf_get_work_entity(SPDocument * doc, + struct rdf_work_entity_t * entity); +unsigned int rdf_set_work_entity(SPDocument * doc, + struct rdf_work_entity_t * entity, + const gchar * text); + +struct rdf_license_t * rdf_get_license(SPDocument * doc); +void rdf_set_license(SPDocument * doc, + struct rdf_license_t const * license); + +void rdf_set_defaults ( SPDocument * document ); + +#endif // _RDF_H_ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/sp-attribute-widget.cpp b/src/dialogs/sp-attribute-widget.cpp new file mode 100644 index 000000000..d7c4316e1 --- /dev/null +++ b/src/dialogs/sp-attribute-widget.cpp @@ -0,0 +1,802 @@ +#define __SP_ATTRIBUTE_WIDGET_C__ + +/** + * \brief SPAttributeWidget + * + * Widget, that listens and modifies repr attributes + * + * Authors: + * Lauris Kaplinski + * + * Copyright (C) 2001 Ximian, Inc. + * + * Licensed under GNU GPL + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "xml/repr.h" +#include "macros.h" +#include "document.h" +#include "sp-object.h" +#include + +#include "sp-attribute-widget.h" + +static void sp_attribute_widget_class_init (SPAttributeWidgetClass *klass); +static void sp_attribute_widget_init (SPAttributeWidget *widget); +static void sp_attribute_widget_destroy (GtkObject *object); + +static void sp_attribute_widget_changed (GtkEditable *editable); + +static void sp_attribute_widget_object_modified ( SPObject *object, + guint flags, + SPAttributeWidget *spaw ); +static void sp_attribute_widget_object_release ( SPObject *object, + SPAttributeWidget *spaw ); + +static GtkEntryClass *parent_class; + + + + +GtkType +sp_attribute_widget_get_type (void) +{ + static GtkType type = 0; + if (!type) { + static const GtkTypeInfo info = { + "SPAttributeWidget", + sizeof (SPAttributeWidget), + sizeof (SPAttributeWidgetClass), + (GtkClassInitFunc) sp_attribute_widget_class_init, + (GtkObjectInitFunc) sp_attribute_widget_init, + NULL, NULL, NULL + }; + type = gtk_type_unique (GTK_TYPE_ENTRY, &info); + } + return type; + +} // end of sp_attribute_widget_get_type() + + + +static void +sp_attribute_widget_class_init (SPAttributeWidgetClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkEditableClass *editable_class; + + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + editable_class = GTK_EDITABLE_CLASS (klass); + + parent_class = (GtkEntryClass*)gtk_type_class (GTK_TYPE_ENTRY); + + object_class->destroy = sp_attribute_widget_destroy; + + editable_class->changed = sp_attribute_widget_changed; + +} // end of sp_attribute_widget_class_init() + + + +static void +sp_attribute_widget_init (SPAttributeWidget *spaw) +{ + spaw->blocked = FALSE; + spaw->hasobj = FALSE; + + spaw->src.object = NULL; + + spaw->attribute = NULL; +} + + + +static void +sp_attribute_widget_destroy (GtkObject *object) +{ + + SPAttributeWidget *spaw; + + spaw = SP_ATTRIBUTE_WIDGET (object); + + if (spaw->attribute) { + g_free (spaw->attribute); + spaw->attribute = NULL; + } + + + if (spaw->hasobj) { + + if (spaw->src.object) { + sp_signal_disconnect_by_data (spaw->src.object, spaw); + spaw->src.object = NULL; + } + } else { + + if (spaw->src.repr) { + spaw->src.repr = Inkscape::GC::release(spaw->src.repr); + } + } // end of if() + + ((GtkObjectClass *) parent_class)->destroy (object); + +} + + + +static void +sp_attribute_widget_changed (GtkEditable *editable) +{ + + SPAttributeWidget *spaw; + + spaw = SP_ATTRIBUTE_WIDGET (editable); + + if (!spaw->blocked) { + + const gchar *text; + spaw->blocked = TRUE; + text = gtk_entry_get_text (GTK_ENTRY (spaw)); + if (!*text) + text = NULL; + + if (spaw->hasobj && spaw->src.object) { + + if (!sp_repr_set_attr ( SP_OBJECT_REPR (spaw->src.object), + spaw->attribute, text) ) + { + /* Cannot set attribute */ + text = SP_OBJECT_REPR (spaw->src.object)->attribute(spaw->attribute); + gtk_entry_set_text (GTK_ENTRY (spaw), text ? text : ""); + } + sp_document_done (SP_OBJECT_DOCUMENT (spaw->src.object)); + + } else if (spaw->src.repr) { + + if (!sp_repr_set_attr (spaw->src.repr, spaw->attribute, text)) + { + /* Cannot set attribute */ + text = spaw->src.repr->attribute(spaw->attribute); + gtk_entry_set_text (GTK_ENTRY (spaw), text ? text : ""); + } + /* TODO: Warning! Undo will not be flushed in given case */ + } + spaw->blocked = FALSE; + } + +} // end of sp_attribute_widget_changed() + + + +GtkWidget * +sp_attribute_widget_new ( SPObject *object, const gchar *attribute ) +{ + SPAttributeWidget *spaw; + + g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL); + g_return_val_if_fail (!object || attribute, NULL); + + spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET); + + sp_attribute_widget_set_object (spaw, object, attribute); + + return GTK_WIDGET (spaw); + +} // end of sp_attribute_widget_new() + + + +GtkWidget * +sp_attribute_widget_new_repr ( Inkscape::XML::Node *repr, const gchar *attribute ) +{ + SPAttributeWidget *spaw; + + spaw = (SPAttributeWidget*)gtk_type_new (SP_TYPE_ATTRIBUTE_WIDGET); + + sp_attribute_widget_set_repr (spaw, repr, attribute); + + return GTK_WIDGET (spaw); +} + + + +void +sp_attribute_widget_set_object ( SPAttributeWidget *spaw, + SPObject *object, + const gchar *attribute ) +{ + + g_return_if_fail (spaw != NULL); + g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw)); + g_return_if_fail (!object || SP_IS_OBJECT (object)); + g_return_if_fail (!object || attribute); + g_return_if_fail (attribute != NULL); + + if (spaw->attribute) { + g_free (spaw->attribute); + spaw->attribute = NULL; + } + + if (spaw->hasobj) { + + if (spaw->src.object) { + sp_signal_disconnect_by_data (spaw->src.object, spaw); + spaw->src.object = NULL; + } + } else { + + if (spaw->src.repr) { + spaw->src.repr = Inkscape::GC::release(spaw->src.repr); + } + } + + spaw->hasobj = TRUE; + + if (object) { + const gchar *val; + + spaw->blocked = TRUE; + spaw->src.object = object; + g_signal_connect ( G_OBJECT (object), "modified", + G_CALLBACK (sp_attribute_widget_object_modified), + spaw ); + g_signal_connect ( G_OBJECT (object), "release", + G_CALLBACK (sp_attribute_widget_object_release), + spaw ); + + spaw->attribute = g_strdup (attribute); + + val = SP_OBJECT_REPR (object)->attribute(attribute); + gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) ""); + spaw->blocked = FALSE; + } + + gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.object != NULL)); + +} // end of sp_attribute_widget_set_object() + + + +void +sp_attribute_widget_set_repr ( SPAttributeWidget *spaw, + Inkscape::XML::Node *repr, + const gchar *attribute ) +{ + + g_return_if_fail (spaw != NULL); + g_return_if_fail (SP_IS_ATTRIBUTE_WIDGET (spaw)); + g_return_if_fail (attribute != NULL); + + if (spaw->attribute) { + g_free (spaw->attribute); + spaw->attribute = NULL; + } + + if (spaw->hasobj) { + + if (spaw->src.object) { + sp_signal_disconnect_by_data (spaw->src.object, spaw); + spaw->src.object = NULL; + } + } else { + + if (spaw->src.repr) { + spaw->src.repr = Inkscape::GC::release(spaw->src.repr); + } + } + + spaw->hasobj = FALSE; + + if (repr) { + const gchar *val; + + spaw->blocked = TRUE; + spaw->src.repr = Inkscape::GC::anchor(repr); + spaw->attribute = g_strdup (attribute); + + val = repr->attribute(attribute); + gtk_entry_set_text (GTK_ENTRY (spaw), val ? val : (const gchar *) ""); + spaw->blocked = FALSE; + } + + gtk_widget_set_sensitive (GTK_WIDGET (spaw), (spaw->src.repr != NULL)); + +} // end of sp_attribute_widget_set_repr() + + + +static void +sp_attribute_widget_object_modified ( SPObject *object, + guint flags, + SPAttributeWidget *spaw ) +{ + + if (flags && SP_OBJECT_MODIFIED_FLAG) { + + const gchar *val, *text; + val = SP_OBJECT_REPR (spaw->src.object)->attribute(spaw->attribute); + text = gtk_entry_get_text (GTK_ENTRY (spaw)); + + if (val || text) { + + if (!val || !text || strcmp (val, text)) { + /* We are different */ + spaw->blocked = TRUE; + gtk_entry_set_text ( GTK_ENTRY (spaw), + val ? val : (const gchar *) ""); + spaw->blocked = FALSE; + } // end of if() + + } // end of if() + + } //end of if() + +} // end of sp_attribute_widget_object_modified() + + + +static void +sp_attribute_widget_object_release ( SPObject *object, + SPAttributeWidget *spaw ) +{ + sp_attribute_widget_set_object (spaw, NULL, NULL); +} + + + +/* SPAttributeTable */ + +static void sp_attribute_table_class_init (SPAttributeTableClass *klass); +static void sp_attribute_table_init (SPAttributeTable *widget); +static void sp_attribute_table_destroy (GtkObject *object); + +static void sp_attribute_table_object_modified (SPObject *object, guint flags, SPAttributeTable *spaw); +static void sp_attribute_table_object_release (SPObject *object, SPAttributeTable *spaw); +static void sp_attribute_table_entry_changed (GtkEditable *editable, SPAttributeTable *spat); + +static GtkVBoxClass *table_parent_class; + + + + +GtkType +sp_attribute_table_get_type (void) +{ + static GtkType type = 0; + if (!type) { + static const GtkTypeInfo info = { + "SPAttributeTable", + sizeof (SPAttributeTable), + sizeof (SPAttributeTableClass), + (GtkClassInitFunc) sp_attribute_table_class_init, + (GtkObjectInitFunc) sp_attribute_table_init, + NULL, NULL, NULL + }; + type = gtk_type_unique (GTK_TYPE_VBOX, &info); + } + return type; + +} // end of sp_attribute_table_get_type() + + + +static void +sp_attribute_table_class_init (SPAttributeTableClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + table_parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX); + + object_class->destroy = sp_attribute_table_destroy; + +} // end of sp_attribute_table_class_init() + + + +static void +sp_attribute_table_init ( SPAttributeTable *spat ) +{ + spat->blocked = FALSE; + spat->hasobj = FALSE; + spat->table = NULL; + spat->src.object = NULL; + spat->num_attr = 0; + spat->attributes = NULL; + spat->entries = NULL; +} + +static void +sp_attribute_table_destroy ( GtkObject *object ) +{ + SPAttributeTable *spat; + + spat = SP_ATTRIBUTE_TABLE (object); + + if (spat->attributes) { + gint i; + for (i = 0; i < spat->num_attr; i++) { + g_free (spat->attributes[i]); + } + g_free (spat->attributes); + spat->attributes = NULL; + } + + if (spat->hasobj) { + + if (spat->src.object) { + sp_signal_disconnect_by_data (spat->src.object, spat); + spat->src.object = NULL; + } + } else { + if (spat->src.repr) { + spat->src.repr = Inkscape::GC::release(spat->src.repr); + } + } // end of if() + + + if (spat->entries) { + g_free (spat->entries); + spat->entries = NULL; + } + + spat->table = NULL; + + if (((GtkObjectClass *) table_parent_class)->destroy) { + (* ((GtkObjectClass *) table_parent_class)->destroy) (object); + } + +} // end of sp_attribute_table_destroy() + + +GtkWidget * +sp_attribute_table_new ( SPObject *object, + gint num_attr, + const gchar **labels, + const gchar **attributes ) +{ + SPAttributeTable *spat; + + g_return_val_if_fail (!object || SP_IS_OBJECT (object), NULL); + g_return_val_if_fail (!object || (num_attr > 0), NULL); + g_return_val_if_fail (!num_attr || (labels && attributes), NULL); + + spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE); + + sp_attribute_table_set_object (spat, object, num_attr, labels, attributes); + + return GTK_WIDGET (spat); + +} // end of sp_attribute_table_new() + + + +GtkWidget * +sp_attribute_table_new_repr ( Inkscape::XML::Node *repr, + gint num_attr, + const gchar **labels, + const gchar **attributes ) +{ + SPAttributeTable *spat; + + g_return_val_if_fail (!num_attr || (labels && attributes), NULL); + + spat = (SPAttributeTable*)gtk_type_new (SP_TYPE_ATTRIBUTE_TABLE); + + sp_attribute_table_set_repr (spat, repr, num_attr, labels, attributes); + + return GTK_WIDGET (spat); + +} // end of sp_attribute_table_new_repr() + + + +#define XPAD 4 +#define YPAD 0 + +void +sp_attribute_table_set_object ( SPAttributeTable *spat, + SPObject *object, + gint num_attr, + const gchar **labels, + const gchar **attributes ) +{ + + g_return_if_fail (spat != NULL); + g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat)); + g_return_if_fail (!object || SP_IS_OBJECT (object)); + g_return_if_fail (!object || (num_attr > 0)); + g_return_if_fail (!num_attr || (labels && attributes)); + + if (spat->table) { + gtk_widget_destroy (spat->table); + spat->table = NULL; + } + + if (spat->attributes) { + gint i; + for (i = 0; i < spat->num_attr; i++) { + g_free (spat->attributes[i]); + } + g_free (spat->attributes); + spat->attributes = NULL; + } + + if (spat->entries) { + g_free (spat->entries); + spat->entries = NULL; + } + + if (spat->hasobj) { + if (spat->src.object) { + sp_signal_disconnect_by_data (spat->src.object, spat); + spat->src.object = NULL; + } + } else { + if (spat->src.repr) { + spat->src.repr = Inkscape::GC::release(spat->src.repr); + } + } + + spat->hasobj = TRUE; + + if (object) { + gint i; + + spat->blocked = TRUE; + + /* Set up object */ + spat->src.object = object; + spat->num_attr = num_attr; + g_signal_connect ( G_OBJECT (object), "modified", + G_CALLBACK (sp_attribute_table_object_modified), + spat ); + g_signal_connect ( G_OBJECT (object), "release", + G_CALLBACK (sp_attribute_table_object_release), + spat ); + /* Create table */ + spat->table = gtk_table_new (num_attr, 2, FALSE); + gtk_container_add (GTK_CONTAINER (spat), spat->table); + /* Arrays */ + spat->attributes = g_new0 (gchar *, num_attr); + spat->entries = g_new0 (GtkWidget *, num_attr); + /* Fill rows */ + for (i = 0; i < num_attr; i++) { + GtkWidget *w; + const gchar *val; + + spat->attributes[i] = g_strdup (attributes[i]); + w = gtk_label_new (_(labels[i])); + gtk_widget_show (w); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1, + GTK_FILL, + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + XPAD, YPAD ); + w = gtk_entry_new (); + gtk_widget_show (w); + val = SP_OBJECT_REPR (object)->attribute(attributes[i]); + gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) ""); + gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1, + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + XPAD, YPAD ); + spat->entries[i] = w; + g_signal_connect ( G_OBJECT (w), "changed", + G_CALLBACK (sp_attribute_table_entry_changed), + spat ); + } + /* Show table */ + gtk_widget_show (spat->table); + + spat->blocked = FALSE; + } + + gtk_widget_set_sensitive ( GTK_WIDGET (spat), + (spat->src.object != NULL) ); + +} // end of sp_attribute_table_set_object() + + + +void +sp_attribute_table_set_repr ( SPAttributeTable *spat, + Inkscape::XML::Node *repr, + gint num_attr, + const gchar **labels, + const gchar **attributes ) +{ + g_return_if_fail (spat != NULL); + g_return_if_fail (SP_IS_ATTRIBUTE_TABLE (spat)); + g_return_if_fail (!num_attr || (labels && attributes)); + + if (spat->table) { + gtk_widget_destroy (spat->table); + spat->table = NULL; + } + + if (spat->attributes) { + gint i; + for (i = 0; i < spat->num_attr; i++) { + g_free (spat->attributes[i]); + } + g_free (spat->attributes); + spat->attributes = NULL; + } + + if (spat->entries) { + g_free (spat->entries); + spat->entries = NULL; + } + + if (spat->hasobj) { + if (spat->src.object) { + sp_signal_disconnect_by_data (spat->src.object, spat); + spat->src.object = NULL; + } + } else { + if (spat->src.repr) { + spat->src.repr = Inkscape::GC::release(spat->src.repr); + } + } + + spat->hasobj = FALSE; + + if (repr) { + gint i; + + spat->blocked = TRUE; + + /* Set up repr */ + spat->src.repr = Inkscape::GC::anchor(repr); + spat->num_attr = num_attr; + /* Create table */ + spat->table = gtk_table_new (num_attr, 2, FALSE); + gtk_container_add (GTK_CONTAINER (spat), spat->table); + /* Arrays */ + spat->attributes = g_new0 (gchar *, num_attr); + spat->entries = g_new0 (GtkWidget *, num_attr); + + /* Fill rows */ + for (i = 0; i < num_attr; i++) { + GtkWidget *w; + const gchar *val; + + spat->attributes[i] = g_strdup (attributes[i]); + w = gtk_label_new (labels[i]); + gtk_widget_show (w); + gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5); + gtk_table_attach ( GTK_TABLE (spat->table), w, 0, 1, i, i + 1, + GTK_FILL, + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + XPAD, YPAD ); + w = gtk_entry_new (); + gtk_widget_show (w); + val = repr->attribute(attributes[i]); + gtk_entry_set_text (GTK_ENTRY (w), val ? val : (const gchar *) ""); + gtk_table_attach ( GTK_TABLE (spat->table), w, 1, 2, i, i + 1, + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + XPAD, YPAD ); + spat->entries[i] = w; + g_signal_connect ( G_OBJECT (w), "changed", + G_CALLBACK (sp_attribute_table_entry_changed), + spat ); + } + /* Show table */ + gtk_widget_show (spat->table); + + spat->blocked = FALSE; + } + + gtk_widget_set_sensitive (GTK_WIDGET (spat), (spat->src.repr != NULL)); + +} // end of sp_attribute_table_set_repr() + + + +static void +sp_attribute_table_object_modified ( SPObject *object, + guint flags, + SPAttributeTable *spat ) +{ + if (flags && SP_OBJECT_MODIFIED_FLAG) + { + gint i; + for (i = 0; i < spat->num_attr; i++) { + const gchar *val, *text; + val = SP_OBJECT_REPR (spat->src.object)->attribute(spat->attributes[i]); + text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i])); + if (val || text) { + if (!val || !text || strcmp (val, text)) { + /* We are different */ + spat->blocked = TRUE; + gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]), + val ? val : (const gchar *) ""); + spat->blocked = FALSE; + } + } + } + } // end of if() + +} // end of sp_attribute_table_object_modified() + + + +static void +sp_attribute_table_object_release (SPObject *object, SPAttributeTable *spat) +{ + sp_attribute_table_set_object (spat, NULL, 0, NULL, NULL); +} + + + +static void +sp_attribute_table_entry_changed ( GtkEditable *editable, + SPAttributeTable *spat ) +{ + if (!spat->blocked) + { + gint i; + for (i = 0; i < spat->num_attr; i++) { + + if (GTK_WIDGET (editable) == spat->entries[i]) { + const gchar *text; + spat->blocked = TRUE; + text = gtk_entry_get_text (GTK_ENTRY (spat->entries[i])); + + if (!*text) + text = NULL; + + if (spat->hasobj && spat->src.object) { + if (!sp_repr_set_attr ( SP_OBJECT_REPR (spat->src.object), + spat->attributes[i], text)) + { + /* Cannot set attribute */ + text = SP_OBJECT_REPR (spat->src.object)->attribute(spat->attributes[i]); + gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]), + text ? text : (const gchar *) ""); + } + sp_document_done (SP_OBJECT_DOCUMENT (spat->src.object)); + + } else if (spat->src.repr) { + + if (!sp_repr_set_attr (spat->src.repr, + spat->attributes[i], text)) + { + /* Cannot set attribute */ + text = spat->src.repr->attribute(spat->attributes[i]); + gtk_entry_set_text ( GTK_ENTRY (spat->entries[i]), + text ? text : (const gchar *) "" ); + } + /* TODO: Warning! Undo will not be flushed in given case */ + } + spat->blocked = FALSE; + return; + } + } + g_warning ("file %s: line %d: Entry signalled change, but there is no such entry", __FILE__, __LINE__); + } // end of if() + +} // end of sp_attribute_table_entry_changed() + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/sp-attribute-widget.h b/src/dialogs/sp-attribute-widget.h new file mode 100644 index 000000000..d5b2075ce --- /dev/null +++ b/src/dialogs/sp-attribute-widget.h @@ -0,0 +1,128 @@ +#ifndef __SP_ATTRIBUTE_WIDGET_H__ +#define __SP_ATTRIBUTE_WIDGET_H__ + +/** + * \brief SPAttributeWidget + * + * Widget, that listens and modifies repr attributes + * + * Authors: + * Lauris Kaplinski + * + * Copyright (C) 2002 authors + * Copyright (C) 2001 Ximian, Inc. + * + * Licensed under GNU GPL, read the file 'COPYING' for more information + */ + +#include + + + +#define SP_TYPE_ATTRIBUTE_WIDGET (sp_attribute_widget_get_type ()) +#define SP_ATTRIBUTE_WIDGET(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_ATTRIBUTE_WIDGET, SPAttributeWidget)) +#define SP_ATTRIBUTE_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_ATTRIBUTE_WIDGET, SPAttributeWidgetClass)) +#define SP_IS_ATTRIBUTE_WIDGET(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_ATTRIBUTE_WIDGET)) +#define SP_IS_ATTRIBUTE_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_ATTRIBUTE_WIDGET)) + +#define SP_TYPE_ATTRIBUTE_TABLE (sp_attribute_table_get_type ()) +#define SP_ATTRIBUTE_TABLE(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_ATTRIBUTE_TABLE, SPAttributeTable)) +#define SP_ATTRIBUTE_TABLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_ATTRIBUTE_TABLE, SPAttributeTableClass)) +#define SP_IS_ATTRIBUTE_TABLE(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_ATTRIBUTE_TABLE)) +#define SP_IS_ATTRIBUTE_TABLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_ATTRIBUTE_TABLE)) + +namespace Inkscape { +namespace XML { +class Node; +} +} + + +struct SPAttributeWidget; +struct SPAttributeWidgetClass; + +struct SPAttributeTable; +struct SPAttributeTableClass; + +#include +#include + +#include + +struct SPAttributeWidget { + GtkEntry entry; + guint blocked : 1; + guint hasobj : 1; + union { + SPObject *object; + Inkscape::XML::Node *repr; + } src; + gchar *attribute; +}; + +struct SPAttributeWidgetClass { + GtkEntryClass entry_class; +}; + +GtkType sp_attribute_widget_get_type (void); + +GtkWidget *sp_attribute_widget_new (SPObject *object, const gchar *attribute); +GtkWidget *sp_attribute_widget_new_repr (Inkscape::XML::Node *repr, const gchar *attribute); + +void sp_attribute_widget_set_object ( SPAttributeWidget *spw, + SPObject *object, + const gchar *attribute ); +void sp_attribute_widget_set_repr ( SPAttributeWidget *spw, + Inkscape::XML::Node *repr, + const gchar *attribute ); + +/* SPAttributeTable */ + +struct SPAttributeTable { + GtkVBox vbox; + guint blocked : 1; + guint hasobj : 1; + GtkWidget *table; + union { + SPObject *object; + Inkscape::XML::Node *repr; + } src; + gint num_attr; + gchar **attributes; + GtkWidget **entries; +}; + +struct SPAttributeTableClass { + GtkEntryClass entry_class; +}; + +GtkType sp_attribute_table_get_type (void); + +GtkWidget *sp_attribute_table_new ( SPObject *object, gint num_attr, + const gchar **labels, + const gchar **attributes ); +GtkWidget *sp_attribute_table_new_repr ( Inkscape::XML::Node *repr, gint num_attr, + const gchar **labels, + const gchar **attributes ); +void sp_attribute_table_set_object ( SPAttributeTable *spw, + SPObject *object, gint num_attr, + const gchar **labels, + const gchar **attrs ); +void sp_attribute_table_set_repr ( SPAttributeTable *spw, + Inkscape::XML::Node *repr, gint num_attr, + const gchar **labels, + const gchar **attrs ); + + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/stroke-style.cpp b/src/dialogs/stroke-style.cpp new file mode 100644 index 000000000..b252cd125 --- /dev/null +++ b/src/dialogs/stroke-style.cpp @@ -0,0 +1,1726 @@ +#define __SP_STROKE_STYLE_C__ + +/** + * \brief Stroke style dialog + * + * Authors: + * Lauris Kaplinski + * Bryce Harrington + * bulia byak + * + * Copyright (C) 2001-2005 authors + * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2004 John Cliff + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#define noSP_SS_VERBOSE + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + + +#include + +#include +#include "helper/unit-menu.h" +#include "helper/units.h" +#include "svg/css-ostringstream.h" +#include "widgets/sp-widget.h" +#include "widgets/spw-utilities.h" +#include "sp-linear-gradient.h" +#include "sp-radial-gradient.h" +#include "sp-marker.h" +#include +#include +#include +#include "style.h" +#include "gradient-chemistry.h" +#include "sp-namedview.h" +#include "desktop-handles.h" +#include "desktop-style.h" +#include "selection.h" +#include "inkscape.h" +#include "inkscape-stock.h" +#include "dialogs/dialog-events.h" +#include "sp-text.h" +#include "sp-rect.h" +#include "document-private.h" +#include "display/nr-arena.h" +#include "display/nr-arena-item.h" +#include "path-prefix.h" +#include "widgets/icon.h" +#include "helper/stock-items.h" +#include "io/sys.h" + +#include "dialogs/stroke-style.h" + +/* Paint */ + +static void sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel); +static void sp_stroke_style_paint_selection_modified (SPWidget *spw, Inkscape::Selection *selection, guint flags, SPPaintSelector *psel); +static void sp_stroke_style_paint_selection_changed (SPWidget *spw, Inkscape::Selection *selection, SPPaintSelector *psel); +static void sp_stroke_style_paint_update(SPWidget *spw); + +static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelectorMode mode, SPWidget *spw); +static void sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw); +static void sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw); + +static void sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw ); + +GtkWidget * +sp_stroke_style_paint_widget_new(void) +{ + GtkWidget *spw, *psel; + + spw = sp_widget_new_global(INKSCAPE); + + psel = sp_paint_selector_new(false); // without fillrule selector + gtk_widget_show(psel); + gtk_container_add(GTK_CONTAINER(spw), psel); + gtk_object_set_data(GTK_OBJECT(spw), "paint-selector", psel); + + gtk_signal_connect(GTK_OBJECT(spw), "construct", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_construct), + psel); + gtk_signal_connect(GTK_OBJECT(spw), "modify_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_modified), + psel); + gtk_signal_connect(GTK_OBJECT(spw), "change_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_selection_changed), + psel); + + g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_stroke_style_widget_change_subselection), spw); + + gtk_signal_connect(GTK_OBJECT(psel), "mode_changed", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_mode_changed), + spw); + gtk_signal_connect(GTK_OBJECT(psel), "dragged", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_dragged), + spw); + gtk_signal_connect(GTK_OBJECT(psel), "changed", + GTK_SIGNAL_FUNC(sp_stroke_style_paint_changed), + spw); + + sp_stroke_style_paint_update (SP_WIDGET(spw)); + return spw; +} + +static void +sp_stroke_style_paint_construct(SPWidget *spw, SPPaintSelector *psel) +{ +#ifdef SP_SS_VERBOSE + g_print( "Stroke style widget constructed: inkscape %p repr %p\n", + spw->inkscape, spw->repr ); +#endif + if (spw->inkscape) { + sp_stroke_style_paint_update (spw); + } +} + +static void +sp_stroke_style_paint_selection_modified ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + SPPaintSelector *psel) +{ + if (flags & ( SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG) ) { + sp_stroke_style_paint_update(spw); + } +} + + +static void +sp_stroke_style_paint_selection_changed ( SPWidget *spw, + Inkscape::Selection *selection, + SPPaintSelector *psel ) +{ + sp_stroke_style_paint_update (spw); +} + +static void +sp_stroke_style_widget_change_subselection ( Inkscape::Application *inkscape, + SPDesktop *desktop, + SPWidget *spw ) +{ + sp_stroke_style_paint_update (spw); +} + +static void +sp_stroke_style_paint_update (SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + SPPaintSelector *psel = SP_PAINT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "paint-selector")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query into it + int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKE); + + switch (result) { + case QUERY_STYLE_NOTHING: + { + /* No paint at all */ + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY); + break; + } + + case QUERY_STYLE_SINGLE: + case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector + case QUERY_STYLE_MULTIPLE_SAME: + { + SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false); + sp_paint_selector_set_mode (psel, pselmode); + + if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_COLOR) { + gfloat d[3]; + sp_color_get_rgb_floatv (&query->stroke.value.color, d); + SPColor color; + sp_color_set_rgb_float (&color, d[0], d[1], d[2]); + sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value)); + + } else if (query->stroke.set && query->stroke.type == SP_PAINT_TYPE_PAINTSERVER) { + + SPPaintServer *server = SP_STYLE_STROKE_SERVER (query); + + if (SP_IS_LINEARGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_linear (psel, vector); + + SPLinearGradient *lg = SP_LINEARGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (lg), + SP_GRADIENT_SPREAD (lg)); + } else if (SP_IS_RADIALGRADIENT (server)) { + SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); + sp_paint_selector_set_gradient_radial (psel, vector); + + SPRadialGradient *rg = SP_RADIALGRADIENT (server); + sp_paint_selector_set_gradient_properties (psel, + SP_GRADIENT_UNITS (rg), + SP_GRADIENT_SPREAD (rg)); + } else if (SP_IS_PATTERN (server)) { + SPPattern *pat = pattern_getroot (SP_PATTERN (server)); + sp_update_pattern_list (psel, pat); + } + } + break; + } + + case QUERY_STYLE_MULTIPLE_DIFFERENT: + { + sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + break; + } + } + + g_free (query); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_paint_mode_changed( SPPaintSelector *psel, + SPPaintSelectorMode mode, + SPWidget *spw ) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + /* TODO: Does this work? + * Not really, here we have to get old color back from object + * Instead of relying on paint widget having meaningful colors set + */ + sp_stroke_style_paint_changed(psel, spw); +} + +static gchar *undo_label_1 = "stroke:flatcolor:1"; +static gchar *undo_label_2 = "stroke:flatcolor:2"; +static gchar *undo_label = undo_label_1; + +static void +sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + switch (psel->mode) { + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP), undo_label); + break; + } + + default: + g_warning( "file %s: line %d: Paint %d should not emit 'dragged'", + __FILE__, __LINE__, psel->mode); + break; + } +} + +static void +sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE)); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *document = SP_DT_DOCUMENT (desktop); + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + + switch (psel->mode) { + case SP_PAINT_SELECTOR_MODE_EMPTY: + // This should not happen. + g_warning ( "file %s: line %d: Paint %d should not emit 'changed'", + __FILE__, __LINE__, psel->mode); + break; + case SP_PAINT_SELECTOR_MODE_MULTIPLE: + // This happens when you switch multiple objects with different gradients to flat color; + // nothing to do here. + break; + + case SP_PAINT_SELECTOR_MODE_NONE: + { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "stroke", "none"); + + sp_desktop_set_style (desktop, css); + + sp_repr_css_attr_unref(css); + + sp_document_done(document); + break; + } + + case SP_PAINT_SELECTOR_MODE_COLOR_RGB: + case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + { + sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity"); + sp_document_maybe_done (SP_DT_DOCUMENT(desktop), undo_label); + + // on release, toggle undo_label so that the next drag will not be lumped with this one + if (undo_label == undo_label_1) + undo_label = undo_label_2; + else + undo_label = undo_label_1; + + break; + } + + case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: + case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: + if (items) { + SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR + ? SP_GRADIENT_TYPE_LINEAR + : SP_GRADIENT_TYPE_RADIAL ); + SPGradient *vector = sp_paint_selector_get_gradient_vector(psel); + if (!vector) { + /* No vector in paint selector should mean that we just changed mode */ + + SPStyle *query = sp_style_new (); + int result = objects_query_fillstroke ((GSList *) items, query, false); + guint32 common_rgb = 0; + if (result == QUERY_STYLE_MULTIPLE_SAME) { + if (query->fill.type != SP_PAINT_TYPE_COLOR) { + common_rgb = sp_desktop_get_color(desktop, false); + } else { + common_rgb = sp_color_get_rgba32_ualpha(&query->stroke.value.color, 0xff); + } + vector = sp_document_default_gradient_vector(document, common_rgb); + } + g_free (query); + + for (GSList const *i = items; i != NULL; i = i->next) { + if (!vector) { + sp_item_set_gradient(SP_ITEM(i->data), + sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), false), + gradient_type, false); + } else { + sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false); + } + } + } else { + vector = sp_gradient_ensure_vector_normalized(vector); + for (GSList const *i = items; i != NULL; i = i->next) { + SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false); + sp_gradient_selector_attrs_to_gradient(gr, psel); + } + } + + sp_document_done(document); + } + break; + + case SP_PAINT_SELECTOR_MODE_PATTERN: + + if (items) { + + SPPattern *pattern = sp_paint_selector_get_pattern (psel); + if (!pattern) { + + /* No Pattern in paint selector should mean that we just + * changed mode - dont do jack. + */ + + } else { + Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern); + SPCSSAttr *css = sp_repr_css_attr_new (); + gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id")); + sp_repr_css_set_property (css, "stroke", urltext); + + for (GSList const *i = items; i != NULL; i = i->next) { + Inkscape::XML::Node *selrepr = SP_OBJECT_REPR (i->data); + SPObject *selobj = SP_OBJECT (i->data); + if (!selrepr) + continue; + + SPStyle *style = SP_OBJECT_STYLE (selobj); + if (style && style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) { + SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (selobj); + if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern) + // only if this object's pattern is not rooted in our selected pattern, apply + continue; + } + + sp_repr_css_change_recursive (selrepr, css, "style"); + } + + sp_repr_css_attr_unref (css); + g_free (urltext); + + } // end if + + sp_document_done (document); + } // end if + + break; + + case SP_PAINT_SELECTOR_MODE_UNSET: + if (items) { + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_unset_property (css, "stroke"); + + sp_desktop_set_style (desktop, css); + sp_repr_css_attr_unref (css); + + sp_document_done (document); + } + break; + + default: + g_warning( "file %s: line %d: Paint selector should not be in " + "mode %d", + __FILE__, __LINE__, + psel->mode ); + break; + } + + g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE)); +} + + + + + +/* Line */ + +static void sp_stroke_style_line_construct(SPWidget *spw, gpointer data); +static void sp_stroke_style_line_selection_modified (SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + gpointer data); + +static void sp_stroke_style_line_selection_changed (SPWidget *spw, + Inkscape::Selection *selection, + gpointer data ); + +static void sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel); + +static void sp_stroke_style_set_join_buttons(SPWidget *spw, + GtkWidget *active); + +static void sp_stroke_style_set_cap_buttons(SPWidget *spw, + GtkWidget *active); + +static void sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw); +static void sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw); +static void sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw); +static void sp_stroke_style_line_dash_changed(SPDashSelector *dsel, + SPWidget *spw); + +static void sp_stroke_style_update_marker_menus(SPWidget *spw, GSList const *objects); + +static SPObject *ink_extract_marker_name(gchar const *n); + + +/** + * Helper function for creating radio buttons. This should probably be re-thought out + * when reimplementing this with Gtkmm. + */ +static GtkWidget * +sp_stroke_radio_button(GtkWidget *tb, char const *icon, + GtkWidget *hb, GtkWidget *spw, + gchar const *key, gchar const *data) +{ + g_assert(icon != NULL); + g_assert(hb != NULL); + g_assert(spw != NULL); + + if (tb == NULL) { + tb = gtk_radio_button_new(NULL); + } else { + tb = gtk_radio_button_new(gtk_radio_button_group(GTK_RADIO_BUTTON(tb)) ); + } + + gtk_widget_show(tb); + gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(tb), FALSE); + gtk_box_pack_start(GTK_BOX(hb), tb, FALSE, FALSE, 0); + gtk_object_set_data(GTK_OBJECT(spw), icon, tb); + gtk_object_set_data(GTK_OBJECT(tb), key, (gpointer*)data); + gtk_signal_connect(GTK_OBJECT(tb), "toggled", + GTK_SIGNAL_FUNC(sp_stroke_style_any_toggled), + spw); + GtkWidget *px = sp_icon_new(GTK_ICON_SIZE_LARGE_TOOLBAR, icon); + g_assert(px != NULL); + gtk_widget_show(px); + gtk_container_add(GTK_CONTAINER(tb), px); + + return tb; + +} + +static GtkWidget * +sp_marker_prev_new(unsigned size, gchar const *mname, + SPDocument *source, SPDocument *sandbox, + gchar *menu_id, NRArena const *arena, unsigned visionkey, NRArenaItem *root) +{ + // the object of the marker + SPObject const *marker = source->getObjectById(mname); + if (marker == NULL) + return NULL; + + // the repr of the marker; make a copy with id="sample" + Inkscape::XML::Node *mrepr = SP_OBJECT_REPR (marker)->duplicate(); + mrepr->setAttribute("id", "sample"); + + // replace the old sample in the sandbox by the new one + Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (sandbox->getObjectById("defs")); + SPObject *oldmarker = sandbox->getObjectById("sample"); + if (oldmarker) + oldmarker->deleteObject(false); + defsrepr->appendChild(mrepr); + Inkscape::GC::release(mrepr); + +// Uncomment this to get the sandbox documents saved (useful for debugging) + //FILE *fp = fopen (g_strconcat(mname, ".svg", NULL), "w"); + //sp_repr_save_stream (sp_document_repr_doc (sandbox), fp); + //fclose (fp); + + // object to render; note that the id is the same as that of the menu we're building + SPObject *object = sandbox->getObjectById(menu_id); + sp_document_root (sandbox)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + sp_document_ensure_up_to_date(sandbox); + + if (object == NULL || !SP_IS_ITEM(object)) + return NULL; // sandbox broken? + + // Find object's bbox in document + NR::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object))); + + NR::Rect const dbox = SP_ITEM(object)->invokeBbox(i2doc); + + if (dbox.isEmpty()) { + return NULL; + } + + /* Update to renderable state */ + NRMatrix t; + double sf = 0.8; + nr_matrix_set_scale(&t, sf, sf); + nr_arena_item_set_transform(root, &t); + NRGC gc(NULL); + nr_matrix_set_identity(&gc.transform); + nr_arena_item_invoke_update( root, NULL, &gc, + NR_ARENA_ITEM_STATE_ALL, + NR_ARENA_ITEM_STATE_NONE ); + + /* Item integer bbox in points */ + NRRectL ibox; + ibox.x0 = (int) floor(sf * dbox.min()[NR::X] + 0.5); + ibox.y0 = (int) floor(sf * dbox.min()[NR::Y] + 0.5); + ibox.x1 = (int) floor(sf * dbox.max()[NR::X] + 0.5); + ibox.y1 = (int) floor(sf * dbox.max()[NR::Y] + 0.5); + + /* Find visible area */ + int width = ibox.x1 - ibox.x0; + int height = ibox.y1 - ibox.y0; + int dx = size; + int dy = size; + dx=(dx - width)/2; // watch out for size, since 'unsigned'-'signed' can cause problems if the result is negative + dy=(dy - height)/2; + + NRRectL area; + area.x0 = ibox.x0 - dx; + area.y0 = ibox.y0 - dy; + area.x1 = area.x0 + size; + area.y1 = area.y0 + size; + + /* Actual renderable area */ + NRRectL ua; + ua.x0 = MAX(ibox.x0, area.x0); + ua.y0 = MAX(ibox.y0, area.y0); + ua.x1 = MIN(ibox.x1, area.x1); + ua.y1 = MIN(ibox.y1, area.y1); + + /* Set up pixblock */ + guchar *px = nr_new(guchar, 4 * size * size); + memset(px, 0x00, 4 * size * size); + + /* Render */ + NRPixBlock B; + nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N, + ua.x0, ua.y0, ua.x1, ua.y1, + px + 4 * size * (ua.y0 - area.y0) + + 4 * (ua.x0 - area.x0), + 4 * size, FALSE, FALSE ); + nr_arena_item_invoke_render( root, &ua, &B, + NR_ARENA_ITEM_RENDER_NO_CACHE ); + nr_pixblock_release(&B); + + // Create widget + GtkWidget *pb = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_data(px, + GDK_COLORSPACE_RGB, + TRUE, + 8, size, size, size * 4, + (GdkPixbufDestroyNotify)nr_free, + NULL)); + return pb; +} + + +#define MARKER_ITEM_MARGIN 0 + + +/** + * sp_marker_list_from_doc() + * + * \brief Pick up all markers from source, except those that are in + * current_doc (if non-NULL), and add items to the m menu + * + */ +static void +sp_marker_list_from_doc (GtkWidget *m, SPDocument *current_doc, SPDocument *source, SPDocument *markers_doc, SPDocument *sandbox, gchar *menu_id) +{ + + // search through defs + GSList *ml = NULL; + SPDefs *defs= (SPDefs *) SP_DOCUMENT_DEFS (source); + for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(defs)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT (ochild) ) { + if (SP_IS_MARKER(ochild)) { + ml = g_slist_prepend (ml, ochild); + } + } + + // Do this here, outside of loop, to speed up preview generation: + /* Create new arena */ + NRArena const *arena = NRArena::create(); + /* Create ArenaItem and set transform */ + unsigned const visionkey = sp_item_display_key_new(1); + NRArenaItem *root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT (sandbox)), (NRArena *) arena, visionkey, SP_ITEM_SHOW_DISPLAY ); + + for (; ml != NULL; ml = ml->next) { + + if (!SP_IS_MARKER(ml->data)) + continue; + + Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) ml->data); + + bool stock_dupe = false; + + if (markers_doc && repr->attribute("inkscape:stockid")) { + // find out if markers_doc has a marker with the same stockid, and if so, skip this + for (SPObject *child = sp_object_first_child(SP_OBJECT(SP_DOCUMENT_DEFS(markers_doc))) ; + child != NULL; + child = SP_OBJECT_NEXT(child) ) + { + if (SP_IS_MARKER(child) && + SP_OBJECT_REPR(child)->attribute("inkscape:stockid") && + !strcmp(repr->attribute("inkscape:stockid"), SP_OBJECT_REPR(child)->attribute("inkscape:stockid"))) { + stock_dupe = true; + } + } + } + + if (stock_dupe) // stock item, dont add to list from current doc + continue; + + GtkWidget *i = gtk_menu_item_new(); + gtk_widget_show(i); + + if (repr->attribute("inkscape:stockid")) + g_object_set_data (G_OBJECT(i), "stockid", (void *) "true"); + else + g_object_set_data (G_OBJECT(i), "stockid", (void *) "false"); + + gchar const *markid = repr->attribute("id"); + g_object_set_data (G_OBJECT(i), "marker", (void *) markid); + + GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN); + gtk_widget_show(hb); + + // generate preview + GtkWidget *prv = sp_marker_prev_new (22, markid, source, sandbox, menu_id, arena, visionkey, root); + gtk_widget_show(prv); + gtk_box_pack_start(GTK_BOX(hb), prv, FALSE, FALSE, 6); + + // create label + GtkWidget *l = gtk_label_new(repr->attribute("id")); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5); + + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(i), hb); + + gtk_menu_append(GTK_MENU(m), i); + } + + g_slist_free (ml); +} + + +SPDocument * +ink_markers_preview_doc () +{ +gchar const *buffer = "" +" " + +" " +" " +" " +" " + +" " +" " +" " +" " + +" " +" " +" " +" " + +""; + + return sp_document_new_from_mem (buffer, strlen(buffer), FALSE); +} + + + +static GtkWidget * +ink_marker_menu( GtkWidget *tbl, gchar *menu_id, SPDocument *sandbox) +{ + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *doc = SP_DT_DOCUMENT(desktop); + GtkWidget *mnu = gtk_option_menu_new(); + + /* Create new menu widget */ + GtkWidget *m = gtk_menu_new(); + gtk_widget_show(m); + + g_object_set_data(G_OBJECT(mnu), "updating", (gpointer) FALSE); + + if (!doc) { + GtkWidget *i = gtk_menu_item_new_with_label(_("No document selected")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(mnu, FALSE); + + } else { + + // add "None" + { + GtkWidget *i = gtk_menu_item_new(); + gtk_widget_show(i); + + g_object_set_data(G_OBJECT(i), "marker", (void *) "none"); + + GtkWidget *hb = gtk_hbox_new(FALSE, MARKER_ITEM_MARGIN); + gtk_widget_show(hb); + + GtkWidget *l = gtk_label_new( _("None") ); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 0.0, 0.5); + + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(i), hb); + gtk_menu_append(GTK_MENU(m), i); + } + + // find and load markers.svg + static SPDocument *markers_doc = NULL; + char *markers_source = g_build_filename(INKSCAPE_MARKERSDIR, "markers.svg", NULL); + if (Inkscape::IO::file_test(markers_source, G_FILE_TEST_IS_REGULAR)) { + markers_doc = sp_document_new(markers_source, FALSE); + } + g_free(markers_source); + + // suck in from current doc + sp_marker_list_from_doc ( m, NULL, doc, markers_doc, sandbox, menu_id ); + + // add separator + { + GtkWidget *i = gtk_separator_menu_item_new(); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + } + + // suck in from markers.svg + if (markers_doc) { + sp_document_ensure_up_to_date(doc); + sp_marker_list_from_doc ( m, doc, markers_doc, NULL, sandbox, menu_id ); + } + + gtk_widget_set_sensitive(mnu, TRUE); + } + + gtk_object_set_data(GTK_OBJECT(mnu), "menu_id", menu_id); + gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m); + + /* Set history */ + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + + return mnu; +} + + +/*user selected existing marker from list*/ +static void +sp_marker_select(GtkOptionMenu *mnu, GtkWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *document = SP_DT_DOCUMENT(desktop); + if (!document) { + return; + } + + /* Get Marker */ + if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "marker")) + { + return; + } + gchar *markid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "marker"); + gchar *marker = ""; + if (strcmp(markid, "none")){ + gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), + "stockid"); + + gchar *markurn = markid; + if (!strcmp(stockid,"true")) markurn = g_strconcat("urn:inkscape:marker:",markid,NULL); + SPObject *mark = get_stock_item(markurn); + if (mark) { + Inkscape::XML::Node *repr = SP_OBJECT_REPR(mark); + marker = g_strconcat("url(#", repr->attribute("id"), ")", NULL); + } + } else { + marker = markid; + } + + SPCSSAttr *css = sp_repr_css_attr_new(); + gchar *menu_id = (gchar *) g_object_get_data(G_OBJECT(mnu), "menu_id"); + sp_repr_css_set_property(css, menu_id, marker); + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + GSList const *items = selection->itemList(); + for (; items != NULL; items = items->next) { + SPItem *item = (SPItem *) items->data; + if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) // can't set marker to rect, until it's converted to using + continue; + Inkscape::XML::Node *selrepr = SP_OBJECT_REPR((SPItem *) items->data); + if (selrepr) { + sp_repr_css_change_recursive(selrepr, css, "style"); + } + SP_OBJECT(items->data)->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } + + sp_repr_css_attr_unref(css); + + sp_document_done(document); +} + +static gboolean stroke_width_set_unit(SPUnitSelector *, + SPUnit const *old, + SPUnit const *new_units, + GObject *spw) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if (!desktop) { + return FALSE; + } + + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + if (selection->isEmpty()) + return FALSE; + + GSList const *objects = selection->itemList(); + + if ((old->base == SP_UNIT_ABSOLUTE || old->base == SP_UNIT_DEVICE) && + (new_units->base == SP_UNIT_DIMENSIONLESS)) { + + /* Absolute to percentage */ + g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE)); + + GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width")); + float w = sp_units_get_pixels (a->value, *old); + + gdouble average = stroke_average_width (objects); + + if (average == NR_HUGE || average == 0) + return FALSE; + + gtk_adjustment_set_value (a, 100.0 * w / average); + + g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE)); + return TRUE; + + } else if ((old->base == SP_UNIT_DIMENSIONLESS) && + (new_units->base == SP_UNIT_ABSOLUTE || new_units->base == SP_UNIT_DEVICE)) { + + /* Percentage to absolute */ + g_object_set_data (spw, "update", GUINT_TO_POINTER (TRUE)); + + GtkAdjustment *a = GTK_ADJUSTMENT(g_object_get_data (spw, "width")); + + gdouble average = stroke_average_width (objects); + + gtk_adjustment_set_value (a, sp_pixels_get_units (0.01 * a->value * average, *new_units)); + + g_object_set_data (spw, "update", GUINT_TO_POINTER (FALSE)); + return TRUE; + } + + return FALSE; +} + + +/** + * \brief Creates a new widget for the line stroke style. + * + */ +GtkWidget * +sp_stroke_style_line_widget_new(void) +{ + GtkWidget *spw, *f, *t, *hb, *sb, *us, *tb, *ds; + GtkObject *a; + + GtkTooltips *tt = gtk_tooltips_new(); + + spw = sp_widget_new_global(INKSCAPE); + + f = gtk_hbox_new (FALSE, 0); + gtk_widget_show(f); + gtk_container_add(GTK_CONTAINER(spw), f); + + t = gtk_table_new(3, 6, FALSE); + gtk_widget_show(t); + gtk_container_set_border_width(GTK_CONTAINER(t), 4); + gtk_table_set_row_spacings(GTK_TABLE(t), 4); + gtk_container_add(GTK_CONTAINER(f), t); + gtk_object_set_data(GTK_OBJECT(spw), "stroke", t); + + gint i = 0; + + /* Stroke width */ + spw_label(t, _("Width:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + +// TODO: when this is gtkmmified, use an Inkscape::UI::Widget::ScalarUnit instead of the separate +// spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use +// setHundredPercent to remember the aeraged width corresponding to 100%. Then the +// stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself), and +// with it, the two remaining calls of stroke_average_width, allowing us to get rid of that +// function in desktop-style. + + a = gtk_adjustment_new(1.0, 0.0, 1000.0, 0.1, 10.0, 10.0); + gtk_object_set_data(GTK_OBJECT(spw), "width", a); + sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 3); + gtk_tooltips_set_tip(tt, sb, _("Stroke width"), NULL); + gtk_widget_show(sb); + + sp_dialog_defocus_on_enter(sb); + + gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0); + us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) + sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(desktop)->doc_units); + sp_unit_selector_add_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT), 0); + g_signal_connect ( G_OBJECT (us), "set_unit", G_CALLBACK (stroke_width_set_unit), spw ); + gtk_widget_show(us); + sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a) ); + gtk_box_pack_start(GTK_BOX(hb), us, FALSE, FALSE, 0); + gtk_object_set_data(GTK_OBJECT(spw), "units", us); + + gtk_signal_connect( GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(sp_stroke_style_width_changed), spw ); + i++; + + /* Join type */ + // TRANSLATORS: The line join style specifies the shape to be used at the + // corners of paths. It can be "miter", "round" or "bevel". + spw_label(t, _("Join:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + + tb = NULL; + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_MITER, + hb, spw, "join", "miter"); + + // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner. + // For an example, draw a triangle with a large stroke width and modify the + // "Join" option (in the Fill and Stroke dialog). + gtk_tooltips_set_tip(tt, tb, _("Miter join"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_ROUND, + hb, spw, "join", "round"); + + // TRANSLATORS: Round join: joining lines with a rounded corner. + // For an example, draw a triangle with a large stroke width and modify the + // "Join" option (in the Fill and Stroke dialog). + gtk_tooltips_set_tip(tt, tb, _("Round join"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_JOIN_BEVEL, + hb, spw, "join", "bevel"); + + // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner. + // For an example, draw a triangle with a large stroke width and modify the + // "Join" option (in the Fill and Stroke dialog). + gtk_tooltips_set_tip(tt, tb, _("Bevel join"), NULL); + + i++; + + /* Miterlimit */ + // TRANSLATORS: Miter limit: only for "miter join", this limits the length + // of the sharp "spike" when the lines connect at too sharp an angle. + // When two line segments meet at a sharp angle, a miter join results in a + // spike that extends well beyond the connection point. The purpose of the + // miter limit is to cut off such spikes (i.e. convert them into bevels) + // when they become too long. + spw_label(t, _("Miter limit:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + + a = gtk_adjustment_new(4.0, 0.0, 100.0, 0.1, 10.0, 10.0); + gtk_object_set_data(GTK_OBJECT(spw), "miterlimit", a); + + sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), 0.1, 2); + gtk_tooltips_set_tip(tt, sb, _("Maximum length of the miter (in units of stroke width)"), NULL); + gtk_widget_show(sb); + gtk_object_set_data(GTK_OBJECT(spw), "miterlimit_sb", sb); + sp_dialog_defocus_on_enter(sb); + + gtk_box_pack_start(GTK_BOX(hb), sb, FALSE, FALSE, 0); + + gtk_signal_connect( GTK_OBJECT(a), "value_changed", + GTK_SIGNAL_FUNC(sp_stroke_style_miterlimit_changed), spw ); + i++; + + /* Cap type */ + // TRANSLATORS: cap type specifies the shape for the ends of lines + spw_label(t, _("Cap:"), 0, i); + + hb = spw_hbox(t, 3, 1, i); + + tb = NULL; + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_BUTT, + hb, spw, "cap", "butt"); + + // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point + // of the line; the ends of the line are square + gtk_tooltips_set_tip(tt, tb, _("Butt cap"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_ROUND, + hb, spw, "cap", "round"); + + // TRANSLATORS: Round cap: the line shape extends beyond the end point of the + // line; the ends of the line are rounded + gtk_tooltips_set_tip(tt, tb, _("Round cap"), NULL); + + tb = sp_stroke_radio_button(tb, INKSCAPE_STOCK_CAP_SQUARE, + hb, spw, "cap", "square"); + + // TRANSLATORS: Square cap: the line shape extends beyond the end point of the + // line; the ends of the line are square + gtk_tooltips_set_tip(tt, tb, _("Square cap"), NULL); + + i++; + + + /* Dash */ + spw_label(t, _("Dashes:"), 0, i); + ds = sp_dash_selector_new( inkscape_get_repr( INKSCAPE, + "palette.dashes") ); + + gtk_widget_show(ds); + gtk_table_attach( GTK_TABLE(t), ds, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "dash", ds); + gtk_signal_connect( GTK_OBJECT(ds), "changed", + GTK_SIGNAL_FUNC(sp_stroke_style_line_dash_changed), + spw ); + i++; + + /* Drop down marker selectors*/ + + // doing this here once, instead of for each preview, to speed things up + SPDocument *sandbox = ink_markers_preview_doc (); + + // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes + // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path. + spw_label(t, _("Start Markers:"), 0, i); + GtkWidget *mnu = ink_marker_menu( spw ,"marker-start", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "start_mark_menu", mnu); + + i++; + spw_label(t, _("Mid Markers:"), 0, i); + mnu = NULL; + mnu = ink_marker_menu( spw ,"marker-mid", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "mid_mark_menu", mnu); + + i++; + spw_label(t, _("End Markers:"), 0, i); + mnu = NULL; + mnu = ink_marker_menu( spw ,"marker-end", sandbox); + gtk_signal_connect( GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_marker_select), spw ); + gtk_widget_show(mnu); + gtk_table_attach( GTK_TABLE(t), mnu, 1, 4, i, i+1, + (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), + (GtkAttachOptions)0, 0, 0 ); + gtk_object_set_data(GTK_OBJECT(spw), "end_mark_menu", mnu); + + i++; + + gtk_signal_connect( GTK_OBJECT(spw), "construct", + GTK_SIGNAL_FUNC(sp_stroke_style_line_construct), + NULL ); + gtk_signal_connect( GTK_OBJECT(spw), "modify_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_modified), + NULL ); + gtk_signal_connect( GTK_OBJECT(spw), "change_selection", + GTK_SIGNAL_FUNC(sp_stroke_style_line_selection_changed), + NULL ); + + sp_stroke_style_line_update( SP_WIDGET(spw), desktop ? SP_DT_SELECTION(desktop) : NULL); + + return spw; +} + + + +static void +sp_stroke_style_line_construct(SPWidget *spw, gpointer data) +{ + +#ifdef SP_SS_VERBOSE + g_print( "Stroke style widget constructed: inkscape %p repr %p\n", + spw->inkscape, spw->repr ); +#endif + if (spw->inkscape) { + sp_stroke_style_line_update(spw, + ( SP_ACTIVE_DESKTOP + ? SP_DT_SELECTION(SP_ACTIVE_DESKTOP) + : NULL )); + } +} + +static void +sp_stroke_style_line_selection_modified ( SPWidget *spw, + Inkscape::Selection *selection, + guint flags, + gpointer data ) +{ + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + sp_stroke_style_line_update (spw, selection); + } + +} + +static void +sp_stroke_style_line_selection_changed ( SPWidget *spw, + Inkscape::Selection *selection, + gpointer data ) +{ + sp_stroke_style_line_update (spw, selection); +} + + +static void +sp_dash_selector_set_from_style (GtkWidget *dsel, SPStyle *style) +{ + if (style->stroke_dash.n_dash > 0) { + double d[64]; + int len = MIN(style->stroke_dash.n_dash, 64); + for (int i = 0; i < len; i++) { + if (style->stroke_width.computed != 0) + d[i] = style->stroke_dash.dash[i] / style->stroke_width.computed; + else + d[i] = style->stroke_dash.dash[i]; // is there a better thing to do for stroke_width==0? + } + sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), len, d, + style->stroke_width.computed != 0? + style->stroke_dash.offset / style->stroke_width.computed : + style->stroke_dash.offset); + } else { + sp_dash_selector_set_dash(SP_DASH_SELECTOR(dsel), 0, NULL, 0.0); + } +} + +static void +sp_jointype_set (SPWidget *spw, unsigned const jointype) +{ + GtkWidget *tb = NULL; + switch (jointype) { + case SP_STROKE_LINEJOIN_MITER: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_MITER)); + break; + case SP_STROKE_LINEJOIN_ROUND: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_ROUND)); + break; + case SP_STROKE_LINEJOIN_BEVEL: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_JOIN_BEVEL)); + break; + default: + break; + } + sp_stroke_style_set_join_buttons (spw, tb); +} + +static void +sp_captype_set (SPWidget *spw, unsigned const captype) +{ + GtkWidget *tb = NULL; + switch (captype) { + case SP_STROKE_LINECAP_BUTT: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_BUTT)); + break; + case SP_STROKE_LINECAP_ROUND: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_ROUND)); + break; + case SP_STROKE_LINECAP_SQUARE: + tb = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), INKSCAPE_STOCK_CAP_SQUARE)); + break; + default: + break; + } + sp_stroke_style_set_cap_buttons (spw, tb); +} + +static void +sp_stroke_style_line_update(SPWidget *spw, Inkscape::Selection *sel) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + GtkWidget *sset = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "stroke")); + GtkObject *width = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "width")); + GtkObject *ml = GTK_OBJECT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit")); + GtkWidget *us = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "units")); + GtkWidget *dsel = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "dash")); + + // create temporary style + SPStyle *query = sp_style_new (); + // query into it + int result_sw = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEWIDTH); + int result_ml = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT); + int result_cap = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKECAP); + int result_join = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_STROKEJOIN); + + if (result_sw == QUERY_STYLE_NOTHING) { + /* No objects stroked, set insensitive */ + gtk_widget_set_sensitive(sset, FALSE); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); + return; + } else { + gtk_widget_set_sensitive(sset, TRUE); + + SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + + if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) { + sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), &sp_unit_get_by_id(SP_UNIT_PERCENT)); + } else { + // same width, or only one object; no sense to keep percent, switch to absolute + if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) { + sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), SP_DT_NAMEDVIEW(SP_ACTIVE_DESKTOP)->doc_units); + } + } + + unit = sp_unit_selector_get_unit (SP_UNIT_SELECTOR (us)); + + if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) { + double avgwidth = sp_pixels_get_units (query->stroke_width.computed, *unit); + gtk_adjustment_set_value(GTK_ADJUSTMENT(width), avgwidth); + } else { + gtk_adjustment_set_value(GTK_ADJUSTMENT(width), 100); + } + } + + if (result_ml != QUERY_STYLE_NOTHING) + gtk_adjustment_set_value(GTK_ADJUSTMENT(ml), query->stroke_miterlimit.value); // TODO: reflect averagedness? + + if (result_join != QUERY_STYLE_MULTIPLE_DIFFERENT) { + sp_jointype_set (spw, query->stroke_linejoin.value); + } else { + sp_stroke_style_set_join_buttons(spw, NULL); + } + + if (result_cap != QUERY_STYLE_MULTIPLE_DIFFERENT) { + sp_captype_set (spw, query->stroke_linecap.value); + } else { + sp_stroke_style_set_cap_buttons(spw, NULL); + } + + g_free (query); + + GSList const *objects = sel->itemList(); + SPObject * const object = SP_OBJECT(objects->data); + SPStyle * const style = SP_OBJECT_STYLE(object); + + /* Markers */ + sp_stroke_style_update_marker_menus(spw, objects); // FIXME: make this desktop query too + + /* Dash */ + sp_dash_selector_set_from_style (dsel, style); // FIXME: make this desktop query too + + gtk_widget_set_sensitive(sset, TRUE); + + gtk_object_set_data(GTK_OBJECT(spw), "update", + GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_set_scaled_dash(SPCSSAttr *css, + int ndash, double *dash, double offset, + double scale) +{ + if (ndash > 0) { + Inkscape::CSSOStringStream osarray; + for (int i = 0; i < ndash; i++) { + osarray << dash[i] * scale; + if (i < (ndash - 1)) { + osarray << ","; + } + } + sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str()); + + Inkscape::CSSOStringStream osoffset; + osoffset << offset * scale; + sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str()); + } else { + sp_repr_css_set_property(css, "stroke-dasharray", "none"); + sp_repr_css_set_property(css, "stroke-dashoffset", NULL); + } +} + +static void +sp_stroke_style_scale_line(SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(TRUE)); + + GtkAdjustment *wadj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "width")); + SPUnitSelector *us = SP_UNIT_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "units")); + SPDashSelector *dsel = SP_DASH_SELECTOR(gtk_object_get_data(GTK_OBJECT(spw), "dash")); + GtkAdjustment *ml = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(spw), "miterlimit")); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *document = SP_DT_DOCUMENT (desktop); + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + + /* TODO: Create some standardized method */ + SPCSSAttr *css = sp_repr_css_attr_new(); + + if (items) { + + double width_typed = wadj->value; + double const miterlimit = ml->value; + + SPUnit const *const unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us)); + + double *dash, offset; + int ndash; + sp_dash_selector_get_dash(dsel, &ndash, &dash, &offset); + + for (GSList const *i = items; i != NULL; i = i->next) { + /* Set stroke width */ + double width; + if (unit->base == SP_UNIT_ABSOLUTE || unit->base == SP_UNIT_DEVICE) { + width = sp_units_get_pixels (width_typed, *unit); + } else { // percentage + gdouble old_w = SP_OBJECT_STYLE (i->data)->stroke_width.computed; + width = old_w * width_typed / 100; + } + + { + Inkscape::CSSOStringStream os_width; + os_width << width; + sp_repr_css_set_property(css, "stroke-width", os_width.str().c_str()); + } + + { + Inkscape::CSSOStringStream os_ml; + os_ml << miterlimit; + sp_repr_css_set_property(css, "stroke-miterlimit", os_ml.str().c_str()); + } + + /* Set dash */ + sp_stroke_style_set_scaled_dash(css, ndash, dash, offset, width); + + sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true); + } + + g_free(dash); + + if (unit->base != SP_UNIT_ABSOLUTE && unit->base != SP_UNIT_DEVICE) { + // reset to 100 percent + gtk_adjustment_set_value (wadj, 100.0); + } + + } + + // we have already changed the items, so set style without changing selection + // FIXME: move the above stroke-setting stuff, including percentages, to desktop-style + sp_desktop_set_style (desktop, css, false); + + sp_repr_css_attr_unref(css); + + sp_document_done(document); + + gtk_object_set_data(GTK_OBJECT(spw), "update", GINT_TO_POINTER(FALSE)); +} + + + +static void +sp_stroke_style_width_changed(GtkAdjustment *adj, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + +static void +sp_stroke_style_miterlimit_changed(GtkAdjustment *adj, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + +static void +sp_stroke_style_line_dash_changed(SPDashSelector *dsel, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + sp_stroke_style_scale_line(spw); +} + + + +/** + * \brief This routine handles toggle events for buttons in the stroke style + * dialog. + * When activated, this routine gets the data for the various widgets, and then + * calls the respective routines to update css properties, etc. + * + */ +static void +sp_stroke_style_any_toggled(GtkToggleButton *tb, SPWidget *spw) +{ + if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { + return; + } + + if (gtk_toggle_button_get_active(tb)) { + + gchar const *join + = static_cast(gtk_object_get_data(GTK_OBJECT(tb), "join")); + gchar const *cap + = static_cast(gtk_object_get_data(GTK_OBJECT(tb), "cap")); + + if (join) { + GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb")); + gtk_widget_set_sensitive (ml, !strcmp(join, "miter")); + } + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + /* TODO: Create some standardized method */ + SPCSSAttr *css = sp_repr_css_attr_new(); + + if (join) { + sp_repr_css_set_property(css, "stroke-linejoin", join); + + sp_desktop_set_style (desktop, css); + + sp_stroke_style_set_join_buttons(spw, GTK_WIDGET(tb)); + } else if (cap) { + sp_repr_css_set_property(css, "stroke-linecap", cap); + + sp_desktop_set_style (desktop, css); + + sp_stroke_style_set_cap_buttons(spw, GTK_WIDGET(tb)); + } + + sp_repr_css_attr_unref(css); + + sp_document_done(SP_DT_DOCUMENT(desktop)); + } +} + + +static void +sp_stroke_style_set_join_buttons(SPWidget *spw, GtkWidget *active) +{ + GtkWidget *tb; + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_MITER) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + + GtkWidget *ml = GTK_WIDGET(g_object_get_data(G_OBJECT(spw), "miterlimit_sb")); + gtk_widget_set_sensitive(ml, (active == tb)); + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_ROUND) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_JOIN_BEVEL) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); +} + + + +static void +sp_stroke_style_set_cap_buttons(SPWidget *spw, GtkWidget *active) +{ + GtkWidget *tb; + + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_BUTT)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_ROUND) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); + tb = GTK_WIDGET(gtk_object_get_data( GTK_OBJECT(spw), + INKSCAPE_STOCK_CAP_SQUARE) ); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), (active == tb)); +} + +static void +ink_marker_menu_set_current(SPObject *marker, GtkOptionMenu *mnu) +{ + gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(TRUE)); + + GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(mnu)); + if (marker != NULL) { + bool mark_is_stock = false; + if (SP_OBJECT_REPR(marker)->attribute("inkscape:stockid")) + mark_is_stock = true; + + gchar *markname; + if (mark_is_stock) + markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("inkscape:stockid")); + else + markname = g_strdup(SP_OBJECT_REPR(marker)->attribute("id")); + + int markpos = 0; + GList *kids = GTK_MENU_SHELL(m)->children; + int i = 0; + for (; kids != NULL; kids = kids->next) { + gchar *mark = (gchar *) g_object_get_data(G_OBJECT(kids->data), "marker"); + if ( mark && strcmp(mark, markname) == 0 ) { + if ( mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "true")) + markpos = i; + if ( !mark_is_stock && !strcmp((gchar *) g_object_get_data(G_OBJECT(kids->data), "stockid"), "false")) + markpos = i; + } + i++; + } + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), markpos); + + g_free (markname); + } + else { + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + } + gtk_object_set_data(GTK_OBJECT(mnu), "update", GINT_TO_POINTER(FALSE)); +} + +static void +sp_stroke_style_update_marker_menus( SPWidget *spw, + GSList const *objects) +{ + struct { char const *key; int loc; } const keyloc[] = { + { "start_mark_menu", SP_MARKER_LOC_START }, + { "mid_mark_menu", SP_MARKER_LOC_MID }, + { "end_mark_menu", SP_MARKER_LOC_END } + }; + + bool all_texts = true; + for (GSList *i = (GSList *) objects; i != NULL; i = i->next) { + if (!SP_IS_TEXT (i->data)) { + all_texts = false; + } + } + + for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { + GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key); + if (all_texts) { + // Per SVG spec, text objects cannot have markers; disable menus if only texts are selected + gtk_widget_set_sensitive (GTK_WIDGET(mnu), FALSE); + } else { + gtk_widget_set_sensitive (GTK_WIDGET(mnu), TRUE); + } + } + + // We show markers of the first object in the list only + // FIXME: use the first in the list that has the marker of each type, if any + SPObject *object = SP_OBJECT(objects->data); + + for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { + // For all three marker types, + + // find the corresponding menu + GtkOptionMenu *mnu = (GtkOptionMenu *) g_object_get_data(G_OBJECT(spw), keyloc[i].key); + + // Quit if we're in update state + if (gtk_object_get_data(GTK_OBJECT(mnu), "update")) { + return; + } + + if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) { + // If the object has this type of markers, + + // Extract the name of the marker that the object uses + SPObject *marker = ink_extract_marker_name(object->style->marker[keyloc[i].loc].value); + // Scroll the menu to that marker + ink_marker_menu_set_current (marker, mnu); + + } else { + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); + } + } +} + + +/** Extract the actual name of the link + * e.g. get mTriangle from url(#mTriangle). + * \return Buffer containing the actual name, allocated from GLib; + * the caller should free the buffer when they no longer need it. + */ +static SPObject* +ink_extract_marker_name(gchar const *n) +{ + gchar const *p = n; + while (*p != '\0' && *p != '#') { + p++; + } + + if (*p == '\0' || p[1] == '\0') { + return NULL; + } + + p++; + int c = 0; + while (p[c] != '\0' && p[c] != ')') { + c++; + } + + if (p[c] == '\0') { + return NULL; + } + + gchar* b = g_strdup(p); + b[c] = '\0'; + + + SPDesktop *desktop = inkscape_active_desktop(); + SPDocument *doc = SP_DT_DOCUMENT(desktop); + SPObject *marker = doc->getObjectById(b); + return marker; +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/dialogs/stroke-style.h b/src/dialogs/stroke-style.h new file mode 100644 index 000000000..209407b3f --- /dev/null +++ b/src/dialogs/stroke-style.h @@ -0,0 +1,35 @@ +#ifndef __SP_STROKE_STYLE_H__ +#define __SP_STROKE_STYLE_H__ + +/** + * \brief Stroke style dialog + * + * Author: + * Lauris Kaplinski + * + * Copyright (C) 2001 Ximian, Inc. + * + */ + +#include + +#include + +#include "forward.h" +#include "display/canvas-bpath.h" + +GtkWidget *sp_stroke_style_paint_widget_new (void); +GtkWidget *sp_stroke_style_line_widget_new (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/swatches.cpp b/src/dialogs/swatches.cpp new file mode 100644 index 000000000..739cda3b2 --- /dev/null +++ b/src/dialogs/swatches.cpp @@ -0,0 +1,516 @@ +/* + * A simple panel for color swatches + * + * Authors: + * Jon A. Cruz + * + * Copyright (C) 2005 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include //for GTK_RESPONSE* types +#include + +#include +#include "inkscape.h" +#include "document.h" +#include "desktop-handles.h" +#include "extension/db.h" +#include "inkscape.h" +#include "svg/svg.h" +#include "desktop-style.h" +#include "io/sys.h" +#include "path-prefix.h" +#include "swatches.h" + +#include "eek-preview.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +SwatchesPanel* SwatchesPanel::instance = 0; + + +ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) : + _r(r), + _g(g), + _b(b), + _name(name) +{ +} + +ColorItem::~ColorItem() +{ +} + +ColorItem::ColorItem(ColorItem const &other) : + Inkscape::UI::Previewable() +{ + if ( this != &other ) { + *this = other; + } +} + +ColorItem &ColorItem::operator=(ColorItem const &other) +{ + if ( this != &other ) { + _r = other._r; + _g = other._g; + _b = other._b; + _name = other._name; + } + return *this; +} + +typedef enum { + XCOLOR_DATA = 0, + TEXT_DATA +} colorFlavorType; + +static const GtkTargetEntry color_entries[] = { + {"application/x-color", 0, XCOLOR_DATA}, + {"text/plain", 0, TEXT_DATA}, +}; + +static void dragGetColorData( GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + static GdkAtom typeXColor = gdk_atom_intern("application/x-color", FALSE); + static GdkAtom typeText = gdk_atom_intern("text/plain", FALSE); + + ColorItem* item = reinterpret_cast(user_data); + if ( info == 1 ) { + gchar* tmp = g_strdup_printf("#%02x%02x%02x", item->_r, item->_g, item->_b); + + gtk_selection_data_set( data, + typeText, + 8, // format + (guchar*)tmp, + strlen((const char*)tmp) + 1); + g_free(tmp); + tmp = 0; + } else { + guchar tmp[8]; + tmp[0] = item->_r; + tmp[1] = item->_r; + tmp[2] = item->_g; + tmp[3] = item->_g; + tmp[4] = item->_b; + tmp[5] = item->_b; + tmp[6] = 0x0ff; + tmp[7] = 0x0ff; + gtk_selection_data_set( data, + typeXColor, + 8, // format + tmp, + (3+1) * 2); + } +} + +//"drag-drop" +gboolean dragDropColorData( GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + guint time, + gpointer user_data) +{ +// TODO finish + return TRUE; +} + +static void bouncy( GtkWidget* widget, gpointer callback_data ) { + ColorItem* item = reinterpret_cast(callback_data); + if ( item ) { + item->buttonClicked(false); + } +} + +static void bouncy2( GtkWidget* widget, gint arg1, gpointer callback_data ) { + ColorItem* item = reinterpret_cast(callback_data); + if ( item ) { + item->buttonClicked(true); + } +} + +Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, Gtk::BuiltinIconSize size) +{ + Gtk::Widget* widget = 0; + if ( style == PREVIEW_STYLE_BLURB ) { + Gtk::Label *lbl = new Gtk::Label(_name); + lbl->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER); + widget = lbl; + } else { + Glib::ustring blank(" "); + if ( size == Gtk::ICON_SIZE_MENU ) { + blank = " "; + } + + GtkWidget* eekWidget = eek_preview_new(); + EekPreview * preview = EEK_PREVIEW(eekWidget); + Gtk::Widget* newBlot = Glib::wrap(eekWidget); + + eek_preview_set_color( preview, (_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b); + + eek_preview_set_details( preview, (::PreviewStyle)style, (::ViewType)view, (::GtkIconSize)size ); + + GValue val = {0, {{0}, {0}}}; + g_value_init( &val, G_TYPE_BOOLEAN ); + g_value_set_boolean( &val, FALSE ); + g_object_set_property( G_OBJECT(preview), "focus-on-click", &val ); + +/* + Gtk::Button *btn = new Gtk::Button(blank); + Gdk::Color color; + color.set_rgb((_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b); + btn->modify_bg(Gtk::STATE_NORMAL, color); + btn->modify_bg(Gtk::STATE_ACTIVE, color); + btn->modify_bg(Gtk::STATE_PRELIGHT, color); + btn->modify_bg(Gtk::STATE_SELECTED, color); + + Gtk::Widget* newBlot = btn; +*/ + + tips.set_tip((*newBlot), _name); + +/* + newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) ); + + sigc::signal type_signal_something; +*/ + g_signal_connect( G_OBJECT(newBlot->gobj()), + "clicked", + G_CALLBACK(bouncy), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "alt-clicked", + G_CALLBACK(bouncy2), + this); + + gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()), + GDK_BUTTON1_MASK, + color_entries, + G_N_ELEMENTS(color_entries), + GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) ); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "drag-data-get", + G_CALLBACK(dragGetColorData), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "drag-drop", + G_CALLBACK(dragDropColorData), + this); + + widget = newBlot; + } + + return widget; +} + +void ColorItem::buttonClicked(bool secondary) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + char const * attrName = secondary ? "stroke" : "fill"; + guint32 rgba = (_r << 24) | (_g << 16) | (_b << 8) | 0xff; + gchar c[64]; + sp_svg_write_color(c, 64, rgba); + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, attrName, c ); + sp_desktop_set_style(desktop, css); + + sp_repr_css_attr_unref(css); + sp_document_done (SP_DT_DOCUMENT (desktop)); + } +} + + + + +static char* trim( char* str ) { + char* ret = str; + while ( *str && (*str == ' ' || *str == '\t') ) { + str++; + } + ret = str; + while ( *str ) { + str++; + } + str--; + while ( str > ret && ( *str == ' ' || *str == '\t' ) || *str == '\r' || *str == '\n' ) { + *str-- = 0; + } + return ret; +} + +void skipWhitespace( char*& str ) { + while ( *str == ' ' || *str == '\t' ) { + str++; + } +} + +bool parseNum( char*& str, int& val ) { + val = 0; + while ( '0' <= *str && *str <= '9' ) { + val = val * 10 + (*str - '0'); + str++; + } + bool retval = !(*str == 0 || *str == ' ' || *str == '\t' || *str == '\r' || *str == '\n'); + return retval; +} + + +class JustForNow +{ +public: + Glib::ustring _name; + std::vector _colors; +}; + +static std::vector possible; + +static void loadPaletteFile( gchar const *filename ) +{ + char block[1024]; + FILE *f = Inkscape::IO::fopen_utf8name( filename, "r" ); + if ( f ) { + char* result = fgets( block, sizeof(block), f ); + if ( result ) { + if ( strncmp( "GIMP Palette", block, 12 ) == 0 ) { + bool inHeader = true; + bool hasErr = false; + + JustForNow *onceMore = new JustForNow(); + + do { + result = fgets( block, sizeof(block), f ); + block[sizeof(block) - 1] = 0; + if ( result ) { + if ( block[0] == '#' ) { + // ignore comment + } else { + char *ptr = block; + // very simple check for header versus entry + while ( *ptr == ' ' || *ptr == '\t' ) { + ptr++; + } + if ( *ptr == 0 ) { + // blank line. skip it. + } else if ( '0' <= *ptr && *ptr <= '9' ) { + // should be an entry link + inHeader = false; + ptr = block; + Glib::ustring name(""); + int r = 0; + int g = 0; + int b = 0; + skipWhitespace(ptr); + if ( *ptr ) { + hasErr = parseNum(ptr, r); + if ( !hasErr ) { + skipWhitespace(ptr); + hasErr = parseNum(ptr, g); + } + if ( !hasErr ) { + skipWhitespace(ptr); + hasErr = parseNum(ptr, b); + } + if ( !hasErr && *ptr ) { + char* n = trim(ptr); + if (n != NULL) { + name = n; + } + } + if ( !hasErr ) { + // Add the entry now + Glib::ustring nameStr(name); + ColorItem* item = new ColorItem( r, g, b, nameStr ); + onceMore->_colors.push_back(item); + } + } else { + hasErr = true; + } + } else { + if ( !inHeader ) { + // Hmmm... probably bad. Not quite the format we want? + hasErr = true; + } else { + char* sep = strchr(result, ':'); + if ( sep ) { + *sep = 0; + char* val = trim(sep + 1); + char* name = trim(result); + if ( *name ) { + if ( strcmp( "Name", name ) == 0 ) { + onceMore->_name = val; + } + } else { + // error + hasErr = true; + } + } else { + // error + hasErr = true; + } + } + } + } + } + } while ( result && !hasErr ); + if ( !hasErr ) { + possible.push_back(onceMore); + } else { + delete onceMore; + } + } + } + + fclose(f); + } +} + +static void loadEmUp() +{ + static bool beenHere = false; + if ( !beenHere ) { + beenHere = true; + + std::list sources; + sources.push_back( profile_path("palettes") ); + sources.push_back( g_strdup(INKSCAPE_PALETTESDIR) ); + + // Use this loop to iterate through a list of possible document locations. + while (!sources.empty()) { + gchar *dirname = sources.front(); + + if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) { + GError *err = 0; + GDir *directory = g_dir_open(dirname, 0, &err); + if (!directory) { + gchar *safeDir = Inkscape::IO::sanitizeString(dirname); + g_warning(_("Palettes directory (%s) is unavailable."), safeDir); + g_free(safeDir); + } else { + gchar *filename = 0; + while ((filename = (gchar *)g_dir_read_name(directory)) != NULL) { + gchar* full = g_build_filename(dirname, filename, NULL); + if ( !Inkscape::IO::file_test( full, (GFileTest)(G_FILE_TEST_IS_DIR ) ) ) { + loadPaletteFile(full); + } + g_free(full); + } + g_dir_close(directory); + } + } + + // toss the dirname + g_free(dirname); + sources.pop_front(); + } + } +} + + + + + + + + + +SwatchesPanel& SwatchesPanel::getInstance() +{ + if ( !instance ) { + instance = new SwatchesPanel(); + } + + return *instance; +} + + + +/** + * Constructor + */ +SwatchesPanel::SwatchesPanel() : + Inkscape::UI::Widget::Panel ("dialogs.swatches"), + _holder(0) +{ + _holder = new PreviewHolder(); + loadEmUp(); + + if ( !possible.empty() ) { + JustForNow* first = possible.front(); + for ( std::vector::iterator it = first->_colors.begin(); it != first->_colors.end(); it++ ) { + _holder->addPreview(*it); + } + + Gtk::RadioMenuItem::Group groupOne; + int i = 0; + for ( std::vector::iterator it = possible.begin(); it != possible.end(); it++ ) { + JustForNow* curr = *it; + Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name)); + _regItem( single, 3, i ); + i++; + } + + } + + + pack_start(*_holder, Gtk::PACK_EXPAND_WIDGET); + _setTargetFillable(_holder); + + show_all_children(); + + restorePanelPrefs(); +} + +SwatchesPanel::~SwatchesPanel() +{ +} + +void SwatchesPanel::_handleAction( int setId, int itemId ) +{ + switch( setId ) { + case 3: + { + if ( itemId >= 0 && itemId < static_cast(possible.size()) ) { + _holder->clear(); + JustForNow* curr = possible[itemId]; + for ( std::vector::iterator it = curr->_colors.begin(); it != curr->_colors.end(); it++ ) { + _holder->addPreview(*it); + } + } + } + break; + } +} + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/swatches.h b/src/dialogs/swatches.h new file mode 100644 index 000000000..ca17cd066 --- /dev/null +++ b/src/dialogs/swatches.h @@ -0,0 +1,93 @@ + +#ifndef SEEN_SWATCHES_H +#define SEEN_SWATCHES_H +/* + * A simple dialog for previewing icon representation. + * + * Authors: + * Jon A. Cruz + * + * Copyright (C) 2005 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include + +#include "ui/widget/panel.h" +#include "ui/previewholder.h" + +namespace Inkscape { +namespace UI { +namespace Dialogs { + + + +/** + * The color swatch you see on screen as a clickable box. + */ +class ColorItem : public Inkscape::UI::Previewable +{ +public: + ColorItem( unsigned int r, unsigned int g, unsigned int b, + Glib::ustring& name ); + virtual ~ColorItem(); + ColorItem(ColorItem const &other); + virtual ColorItem &operator=(ColorItem const &other); + virtual Gtk::Widget* getPreview(PreviewStyle style, + ViewType view, + Gtk::BuiltinIconSize size); + void buttonClicked(bool secondary = false); + unsigned int _r; + unsigned int _g; + unsigned int _b; + Glib::ustring _name; + +private: + Gtk::Tooltips tips; +}; + + + +/** + * A panel that displays color swatches. + */ +class SwatchesPanel : public Inkscape::UI::Widget::Panel +{ +public: + SwatchesPanel(); + virtual ~SwatchesPanel(); + + static SwatchesPanel& getInstance(); + +protected: + virtual void _handleAction( int setId, int itemId ); + +private: + SwatchesPanel(SwatchesPanel const &); // no copy + SwatchesPanel &operator=(SwatchesPanel const &); // no assign + + static SwatchesPanel* instance; + + PreviewHolder* _holder; +}; + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + + + +#endif // SEEN_SWATCHES_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/text-edit.cpp b/src/dialogs/text-edit.cpp new file mode 100644 index 000000000..b4dcd4bce --- /dev/null +++ b/src/dialogs/text-edit.cpp @@ -0,0 +1,915 @@ +#define __SP_TEXT_EDIT_C__ + +/** + * \brief Text editing dialog + * + * Author: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#ifdef WITH_GTKSPELL +extern "C" { +# include +} +#endif + +#include "macros.h" +#include +#include "helper/window.h" +#include "../widgets/font-selector.h" +#include "../inkscape.h" +#include "../document.h" +#include "../desktop-style.h" +#include "../desktop-handles.h" +#include "../selection.h" +#include "../style.h" +#include "../sp-text.h" +#include "../sp-flowtext.h" +#include "../text-editing.h" +#include "../inkscape-stock.h" +#include + +#include "dialog-events.h" +#include "../prefs-utils.h" +#include "../verbs.h" +#include "../interface.h" +#include "svg/css-ostringstream.h" +#include "widgets/icon.h" +#include + + +#define VB_MARGIN 4 + +static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg); +static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg); +static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg); + +static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg); +static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg); +static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg); + +static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content); + +static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg); +static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg); +static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg); +static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg); + +static SPItem *sp_ted_get_selected_text_item (void); +static unsigned sp_ted_get_selected_text_count (void); + + +static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL}; + +static GtkWidget *dlg = NULL; +static win_data wd; +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar const *prefs_path = "dialogs.textandfont"; + + + + +static void +sp_text_edit_dialog_destroy (GtkObject *object, gpointer data) +{ + sp_signal_disconnect_by_data (INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; +} + + + +static gboolean +sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data) +{ + gtk_window_get_position ((GtkWindow *) dlg, &x, &y); + gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute (prefs_path, "x", x); + prefs_set_int_attribute (prefs_path, "y", y); + prefs_set_int_attribute (prefs_path, "w", w); + prefs_set_int_attribute (prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it +} + + +/** + These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left. + This flag is used to prevent passing keys from the dialog to canvas, so that the text editor + can handle keys like Esc and Ctrl+Z itself. + */ +gboolean +text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data) +{ + GObject *dlg = (GObject *) data; + g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE)); + return FALSE; +} + +gboolean +text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data) +{ + GObject *dlg = (GObject *) data; + g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE)); + return FALSE; +} + + +void +sp_text_edit_dialog (void) +{ + + if (!dlg) { + + gchar title[500]; + sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title); + + dlg = sp_window_new (title, TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute (prefs_path, "x", 0); + y = prefs_get_int_attribute (prefs_path, "y", 0); + } + + if (w ==0 || h == 0) { + w = prefs_get_int_attribute (prefs_path, "w", 0); + h = prefs_get_int_attribute (prefs_path, "h", 0); + } + + if (x != 0 || y != 0) { + gtk_window_move ((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) + gtk_window_resize ((GtkWindow *) dlg, w, h); + + sp_transientize (dlg); + wd.win = dlg; + wd.stop = 0; + g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd ); + + gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg ); + + gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg ); + gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg ); + + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg ); + g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg ); + + gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE); + + GtkTooltips *tt = gtk_tooltips_new(); + + // box containing the notebook and the bottom buttons + GtkWidget *mainvb = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (dlg), mainvb); + + // notebook + GtkWidget *nb = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0); + g_object_set_data (G_OBJECT (dlg), "notebook", nb); + + + + // Font tab + { + GtkWidget *l = gtk_label_new (_("Font")); + GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN); + gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l); + + /* HBox containing font selection and layout */ + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0); + + // font and style selector + GtkWidget *fontsel = sp_font_selector_new (); + g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg ); + gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0); + g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel); + + // Layout + { + GtkWidget *f = gtk_frame_new (_("Layout")); + gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4); + GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_container_add (GTK_CONTAINER (f), l_vb); + + { + GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN); + GtkWidget *group; + + // align left + { + GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR ); + GtkWidget *b = group = gtk_radio_button_new (NULL); + gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL); + gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE); + g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE ); + gtk_container_add (GTK_CONTAINER (b), px); + gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b); + } + + // align center + { + GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR ); + GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group))); + /* TRANSLATORS: `Center' here is a verb. */ + gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL); + gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE); + g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg ); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE); + gtk_container_add (GTK_CONTAINER (b), px); + gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b); + } + + // align right + { + GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR ); + GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group))); + gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL); + gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE); + g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg ); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE); + gtk_container_add (GTK_CONTAINER (b), px); + gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b); + } + + gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0); + } + + + { + GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN); + GtkWidget *group; + + // horizontal + { + GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_WRITING_MODE_LR ); + GtkWidget *b = group = gtk_radio_button_new (NULL); + gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL); + gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE); + g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg ); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE); + gtk_container_add (GTK_CONTAINER (b), px); + gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b); + } + + // vertical + { + GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_WRITING_MODE_TB ); + GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group))); + gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL); + gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE); + g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg ); + gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE); + gtk_container_add (GTK_CONTAINER (b), px); + gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b); + } + + gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0); + } + + { + GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN); + + l = gtk_label_new (_("Line spacing:")); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN); + + gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0); + } + + { + GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN); + + GtkWidget *c = gtk_combo_new (); + gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE); + gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE); + gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE); + gtk_widget_set_size_request (c, 90, -1); + + { /* Setup strings */ + GList *sl = NULL; + for (int i = 0; spacings[i]; i++) { + sl = g_list_prepend (sl, (void *) spacings[i]); + } + sl = g_list_reverse (sl); + gtk_combo_set_popdown_strings ((GtkCombo *) c, sl); + g_list_free (sl); + } + + g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry, + "changed", + (GCallback) sp_text_edit_dialog_line_spacing_changed, + dlg ); + gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN); + g_object_set_data (G_OBJECT (dlg), "line_spacing", c); + + gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN); + } + } + + /* Font preview */ + GtkWidget *preview = sp_font_preview_new (); + gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4); + g_object_set_data (G_OBJECT (dlg), "preview", preview); + } + + + // Text tab + { + GtkWidget *l = gtk_label_new (_("Text")); + GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); + gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN); + gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l); + + GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL ); + gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN ); + gtk_widget_show (scroller); + + GtkTextBuffer *tb = gtk_text_buffer_new (NULL); + GtkWidget *txt = gtk_text_view_new_with_buffer (tb); + gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD); +#ifdef WITH_GTKSPELL + GError *error = NULL; + char *errortext = NULL; + /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the + language (either as 2nd arg of gtkspell_new_attach, or with explicit + gtkspell_set_language call in; see advanced.c example in gtkspell docs). + sp_text_edit_dialog_read_selection looks like a suitable place. */ + if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) { + g_print("gtkspell error: %s\n", error->message); + errortext = g_strdup_printf("GtkSpell was unable to initialize.\n" + "%s", error->message); + g_error_free(error); + } +#endif + gtk_widget_set_size_request (txt, -1, 64); + gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE); + gtk_container_add (GTK_CONTAINER (scroller), txt); + gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0); + g_signal_connect ( G_OBJECT (tb), "changed", + G_CALLBACK (sp_text_edit_dialog_text_changed), dlg ); + g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg); + g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg); + g_object_set_data (G_OBJECT (dlg), "text", tb); + g_object_set_data (G_OBJECT (dlg), "textw", txt); + } + + /* Buttons */ + GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN); + gtk_container_set_border_width (GTK_CONTAINER (hb), 4); + gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0); + + { + GtkWidget *b = gtk_button_new_with_label (_("Set as default")); + g_signal_connect ( G_OBJECT (b), "clicked", + G_CALLBACK (sp_text_edit_dialog_set_default), + dlg ); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT (dlg), "default", b); + } + + { + GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + g_signal_connect ( G_OBJECT (b), "clicked", + G_CALLBACK (sp_text_edit_dialog_close), dlg ); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY); + g_signal_connect ( G_OBJECT (b), "clicked", + G_CALLBACK (sp_text_edit_dialog_apply), dlg ); + gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 ); + g_object_set_data (G_OBJECT (dlg), "apply", b); + } + + g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", + G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg); + g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", + G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg); + g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg); + + gtk_widget_show_all (dlg); + + sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE); + } + + gtk_window_present ((GtkWindow *) dlg); + +} // end of sp_text_edit_dialog() + + + +static void +sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, + Inkscape::Selection *sel, + guint flags, + GtkWidget *dlg ) +{ + gboolean style, content; + + style = + ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 ); + + content = + ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 ); + + sp_text_edit_dialog_read_selection (dlg, style, content); + +} + + + +static void +sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, + Inkscape::Selection *sel, + GtkWidget *dlg ) +{ + sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE); +} + +static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg ) +{ + sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE); +} + +static void +sp_text_edit_dialog_update_object_text ( SPItem *text ) +{ + GtkTextBuffer *tb; + GtkTextIter start, end; + gchar *str; + + tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text"); + + /* write text */ + if (gtk_text_buffer_get_modified (tb)) { + gtk_text_buffer_get_bounds (tb, &start, &end); + str = gtk_text_buffer_get_text (tb, &start, &end, TRUE); + sp_te_set_repr_text_multiline (text, str); + g_free (str); + gtk_text_buffer_set_modified (tb, FALSE); + } +} + +SPCSSAttr * +sp_get_text_dialog_style () +{ + GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel"); + + SPCSSAttr *css = sp_repr_css_attr_new (); + + /* font */ + font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel)); + + if ( font ) { + gchar c[256]; + font->Family(c, 256); + sp_repr_css_set_property (css, "font-family", c); + + font->Attribute( "weight", c, 256); + sp_repr_css_set_property (css, "font-weight", c); + + font->Attribute("style", c, 256); + sp_repr_css_set_property (css, "font-style", c); + + font->Attribute("stretch", c, 256); + sp_repr_css_set_property (css, "font-stretch", c); + + font->Attribute("variant", c, 256); + sp_repr_css_set_property (css, "font-variant", c); + + Inkscape::CSSOStringStream os; + os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)); + sp_repr_css_set_property (css, "font-size", os.str().c_str()); + + font->Unref(); + font=NULL; + } + + /* Layout */ + GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start"); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { + sp_repr_css_set_property (css, "text-anchor", "start"); + sp_repr_css_set_property (css, "text-align", "start"); + } else { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), + "text_anchor_middle"); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { + sp_repr_css_set_property (css, "text-anchor", "middle"); + sp_repr_css_set_property (css, "text-align", "center"); + } else { + sp_repr_css_set_property (css, "text-anchor", "end"); + sp_repr_css_set_property (css, "text-align", "end"); + } + } + + b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR ); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) { + sp_repr_css_set_property (css, "writing-mode", "lr"); + } else { + sp_repr_css_set_property (css, "writing-mode", "tb"); + } + + // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set + // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext + GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing"); + const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry); + sp_repr_css_set_property (css, "line-height", sstr); + + return css; +} + + +static void +sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg) +{ + GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + SPCSSAttr *css = sp_get_text_dialog_style (); + + g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE)); + sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style"); + g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE)); + + sp_repr_css_attr_unref (css); + + gtk_widget_set_sensitive (def, FALSE); +} + + + +static void +sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg) +{ + g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE)); + + GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + unsigned items = 0; + const GSList *item_list = SP_DT_SELECTION(desktop)->itemList(); + + SPCSSAttr *css = sp_get_text_dialog_style (); + + sp_desktop_set_style(desktop, css, true); + + for (; item_list != NULL; item_list = item_list->next) { + // apply style to the reprs of all text objects in the selection + if (SP_IS_TEXT (item_list->data)) { + + // backwards compatibility: + SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); + + ++items; + } + else if (SP_IS_FLOWTEXT (item_list->data)) + // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext + ++items; + } + + if (items == 0) { + // no text objects; apply style to prefs for new objects + sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style"); + gtk_widget_set_sensitive (def, FALSE); + } else if (items == 1) { + /* exactly one text object; now set its text, too */ + SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem(); + if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) { + sp_text_edit_dialog_update_object_text (item); + } + } + + // complete the transaction + sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP)); + + gtk_widget_set_sensitive (apply, FALSE); + + sp_repr_css_attr_unref (css); + + g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE)); +} + + + +static void +sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg) +{ + gtk_widget_destroy (GTK_WIDGET (dlg)); +} + +static void +sp_text_edit_dialog_read_selection ( GtkWidget *dlg, + gboolean dostyle, + gboolean docontent ) +{ + if (g_object_get_data (G_OBJECT (dlg), "blocked")) + return; + + g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE)); + + GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook"); + GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw"); + GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel"); + GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview"); + GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text"); + + SPItem *text = sp_ted_get_selected_text_item (); + + Inkscape::XML::Node *repr; + if (text) + { + guint items = sp_ted_get_selected_text_count (); + if (items == 1) { + gtk_widget_set_sensitive (textw, TRUE); + } else { + gtk_widget_set_sensitive (textw, FALSE); + } + gtk_widget_set_sensitive (apply, FALSE); + gtk_widget_set_sensitive (def, TRUE); + + if (docontent) { + gchar *str; + str = sp_te_get_string_multiline (text); + + if (str) { + int pos; + pos = 0; + + if (items == 1) { + gtk_text_buffer_set_text (tb, str, strlen (str)); + gtk_text_buffer_set_modified (tb, FALSE); + } + sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str); + g_free (str); + + } else { + gtk_text_buffer_set_text (tb, "", 0); + sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL); + } + } // end of if (docontent) + repr = SP_OBJECT_REPR (text); + + } else { + gtk_widget_set_sensitive (textw, FALSE); + gtk_widget_set_sensitive (apply, FALSE); + gtk_widget_set_sensitive (def, FALSE); + } + + if (dostyle) { + + // create temporary style + SPStyle *query = sp_style_new (); + // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection + int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); + int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); + int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + + // If querying returned nothing, read the style from the text tool prefs (default style for new texts) + if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) { + repr = inkscape_get_repr (INKSCAPE, "tools.text"); + if (repr) { + gtk_widget_set_sensitive (notebook, TRUE); + sp_style_read_from_repr (query, repr); + } else { + gtk_widget_set_sensitive (notebook, FALSE); + } + } + + // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists + font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) ); + if (font) { + // the font is oversized, so we need to pass the true size separately + sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed); + sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel)); + font->Unref(); + font=NULL; + } + + GtkWidget *b; + if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" ); + } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" ); + } else { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" ); + } + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE); + + if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR ); + } else { + b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB ); + } + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE); + + GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" ); + double height; + if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL; + else if (query->line_height.unit == SP_CSS_UNIT_PERCENT) + height = query->line_height.value; + else height = query->line_height.computed; + gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5)); + gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr); + g_free(sstr); + + g_free (query); + } + + g_object_set_data (G_OBJECT (dlg), "blocked", NULL); +} + + +static void +sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg) +{ + GtkWidget *textw, *preview, *apply, *def; + GtkTextIter start, end; + gchar *str; + + if (g_object_get_data (G_OBJECT (dlg), "blocked")) + return; + + SPItem *text = sp_ted_get_selected_text_item (); + + textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw"); + preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview"); + apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + gtk_text_buffer_get_bounds (tb, &start, &end); + str = gtk_text_buffer_get_text (tb, &start, &end, TRUE); + + if (str && *str) { + sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str); + } else { + sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL); + } + g_free (str); + + if (text) { + gtk_widget_set_sensitive (apply, TRUE); + } + gtk_widget_set_sensitive (def, TRUE); + +} // end of sp_text_edit_dialog_text_changed() + + + +static void +sp_text_edit_dialog_font_changed ( SPFontSelector *fsel, + font_instance *font, + GtkWidget *dlg ) +{ + GtkWidget *preview, *apply, *def; + + if (g_object_get_data (G_OBJECT (dlg), "blocked")) + return; + + SPItem *text = sp_ted_get_selected_text_item (); + + preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview"); + apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel)); + + if (text) { + gtk_widget_set_sensitive (apply, TRUE); + } + gtk_widget_set_sensitive (def, TRUE); + +} // end of sp_text_edit_dialog_font_changed() + + + +static void +sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg) +{ + GtkWidget *apply, *def; + + if (g_object_get_data (G_OBJECT (dlg), "blocked")) + return; + + SPItem *text = sp_ted_get_selected_text_item (); + + apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + if (text) { + gtk_widget_set_sensitive (apply, TRUE); + } + gtk_widget_set_sensitive (def, TRUE); +} + + + +static void +sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg) +{ + GtkWidget *apply, *def; + + if (g_object_get_data ((GObject *) dlg, "blocked")) + return; + + SPItem *text = sp_ted_get_selected_text_item (); + + apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply"); + def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default"); + + if (text) { + gtk_widget_set_sensitive (apply, TRUE); + } + gtk_widget_set_sensitive (def, TRUE); +} + + + +static SPItem * +sp_ted_get_selected_text_item (void) +{ + if (!SP_ACTIVE_DESKTOP) + return NULL; + + for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList(); + item != NULL; + item = item->next) + { + if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) + return SP_ITEM (item->data); + } + + return NULL; +} + + + +static unsigned +sp_ted_get_selected_text_count (void) +{ + if (!SP_ACTIVE_DESKTOP) + return 0; + + unsigned int items = 0; + + for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList(); + item != NULL; + item = item->next) + { + if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) + ++items; + } + + return items; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/text-edit.h b/src/dialogs/text-edit.h new file mode 100644 index 000000000..9350de219 --- /dev/null +++ b/src/dialogs/text-edit.h @@ -0,0 +1,24 @@ +#ifndef SP_TEXT_EDIT_H +#define SP_TEXT_EDIT_H + +/** + * \brief text-edit + * + * Text editing and font changes + * + */ + +void sp_text_edit_dialog (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/tiledialog.cpp b/src/dialogs/tiledialog.cpp new file mode 100644 index 000000000..12c94ed81 --- /dev/null +++ b/src/dialogs/tiledialog.cpp @@ -0,0 +1,872 @@ +/* + * A simple dialog for creating grid type arrangements of selected objects + * + * Authors: + * Bob Jamison ( based off trace dialog) + * John Cliff + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 Bob Jamison + * Copyright (C) 2004 John Cliff + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +//#define DEBUG_GRID_ARRANGE 1 + +#ifdef HAVE_CONFIG_H +# include +#endif + + +#include //for GTK_RESPONSE* types +#include +#include +#include + +#include "libnr/nr-matrix-ops.h" +#include "verbs.h" +#include "prefs-utils.h" +#include "inkscape.h" +#include "desktop-handles.h" +#include "selection.h" +#include "document.h" +#include "sp-item.h" +#include "widgets/icon.h" +#include "tiledialog.h" + + + +/* + * Sort items by their x co-ordinates, taking account of y (keeps rows intact) + * + * <0 *elem1 goes before *elem2 + * 0 *elem1 == *elem2 + * >0 *elem1 goes after *elem2 + */ +int +sp_compare_x_position(SPItem *first, SPItem *second) +{ + using NR::X; + using NR::Y; + + NR::Rect const a = first->invokeBbox(sp_item_i2doc_affine(first)); + double const a_height = a.dimensions()[Y]; + + NR::Rect const b = second->invokeBbox(sp_item_i2doc_affine(second)); + double const b_height = b.dimensions()[Y]; + + bool a_in_b_vert = false; + if ((a.min()[Y] < b.min()[Y] + 0.1) && (a.min()[Y] > b.min()[Y] - b_height)) { + a_in_b_vert = true; + } else if ((b.min()[Y] < a.min()[Y] + 0.1) && (b.min()[Y] > a.min()[Y] - a_height)) { + a_in_b_vert = true; + } else if (b.min()[Y] == a.min()[Y]) { + a_in_b_vert = true; + } else { + a_in_b_vert = false; + } + + if (!a_in_b_vert) { + return -1; + } + if (a_in_b_vert && a.min()[X] > b.min()[X]) { + return 1; + } + if (a_in_b_vert && a.min()[X] < b.min()[X]) { + return -1; + } + return 0; +} + +/* + * Sort items by their y co-ordinates. + */ +int +sp_compare_y_position(SPItem *first, SPItem *second) +{ + NR::Rect const a = first->invokeBbox(sp_item_i2doc_affine(first)); + NR::Rect const b = second->invokeBbox(sp_item_i2doc_affine(second)); + + if (a.min()[NR::Y] > b.min()[NR::Y]) { + return 1; + } + if (a.min()[NR::Y] < b.min()[NR::Y]) { + return -1; + } + + return 0; +} + +namespace Inkscape { +namespace UI { +namespace Dialog { + + +//######################################################################### +//## E V E N T S +//######################################################################### + +/* + * + * This arranges the selection in a grid pattern. + * + */ + +void TileDialog::Grid_Arrange () +{ + + int cnt,row_cnt,col_cnt,a,row,col; + double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y,cx,cy; + double total_col_width,total_row_height; + col_width = 0; + row_height = 0; + total_col_width=0; + total_row_height=0; + + + // set padding to manual values + paddingx = XPadSpinner.get_value(); + paddingy = YPadSpinner.get_value(); + + std::vector row_heights; + std::vector col_widths; + std::vector row_ys; + std::vector col_xs; + + int NoOfCols = NoOfColsSpinner.get_value_as_int(); + int NoOfRows = NoOfRowsSpinner.get_value_as_int(); + + width = 0; + for (a=0;aitemList(); + cnt=0; + for (; items != NULL; items = items->next) { + SPItem *item = SP_ITEM(items->data); + NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item)); + width = b.dimensions()[NR::X]; + height = b.dimensions()[NR::Y]; + cx = b.midpoint()[NR::X]; + cy = b.midpoint()[NR::Y]; + + if (b.min()[NR::X] < grid_left) { + grid_left = b.min()[NR::X]; + } + if (b.min()[NR::Y] < grid_top) { + grid_top = b.min()[NR::Y]; + } + if (width > col_width) { + col_width = width; + } + if (height > row_height) { + row_height = height; + } + } + + + // require the sorting done before we can calculate row heights etc. + + const GSList *items2 = selection->itemList(); + GSList *rev = g_slist_copy((GSList *) items2); + GSList *sorted = NULL; + rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position); + sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position); + + + // Calculate individual Row and Column sizes if necessary + + + cnt=0; + const GSList *sizes = sorted; + for (; sizes != NULL; sizes = sizes->next) { + SPItem *item = SP_ITEM(sizes->data); + NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item)); + width = b.dimensions()[NR::X]; + height = b.dimensions()[NR::Y]; + if (width > col_widths[(cnt % NoOfCols)]) { + col_widths[(cnt % NoOfCols)] = width; + } + if (height > row_heights[(cnt / NoOfCols)]) { + row_heights[(cnt / NoOfCols)] = height; + } + cnt++; + } + + + /// Make sure the top and left of the grid dont move by compensating for align values. + if (RowHeightButton.get_active()){ + grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign)); + } + if (ColumnWidthButton.get_active()){ + grid_left = grid_left - (((col_width - col_widths[0]) /2)*(HorizAlign)); + } + + #ifdef DEBUG_GRID_ARRANGE + g_print("\n cx = %f cy= %f gridleft=%f",cx,cy,grid_left); + #endif + + // Calculate total widths and heights, allowing for columns and rows non uniformly sized. + + if (ColumnWidthButton.get_active()){ + total_col_width = col_width * NoOfCols; + col_widths.clear(); + for (a=0;abounds(); +#ifdef DEBUG_GRID_ARRANGE +g_print("\n row = %f col = %f selection x= %f selection y = %f", total_row_height,total_col_width, b.extent(NR::X), b.extent(NR::Y)); +#endif + paddingx = (b.extent(NR::X) - total_col_width) / (NoOfCols -1); + paddingy = (b.extent(NR::Y) - total_row_height) / (NoOfRows -1); + } + +/* + Horizontal align - Left = 0 + Centre = 1 + Right = 2 + + Vertical align - Top = 0 + Middle = 1 + Bottom = 2 + + X position is calculated by taking the grids left co-ord, adding the distance to the column, + then adding 1/2 the spacing multiplied by the align variable above, + Y position likewise, takes the top of the grid, adds the y to the current row then adds the padding in to align it. + +*/ + + // Calculate row and column x and y coords required to allow for columns and rows which are non uniformly sized. + + for (a=0;adata); + sorted = sorted->next; + } + + for (; current_row != NULL; current_row = current_row->next) { + SPItem *item=SP_ITEM(current_row->data); + Inkscape::XML::Node *repr = SP_OBJECT_REPR(item); + NR::Rect const b = item->invokeBbox(sp_item_i2doc_affine(item)); + width = b.dimensions()[NR::X]; + height = b.dimensions()[NR::Y]; + row = cnt / NoOfCols; + col = cnt % NoOfCols; + + // original before I started fecking about with it. + // new_x = grid_left + (((col_width - width)/2)*HorizAlign) + (( col_width + paddingx ) * (cnt % NoOfCols)); + // new_y = grid_top + (((row_height - height)/2)*VertAlign) +(( row_height + paddingy ) * (cnt / NoOfCols)); + + new_x = grid_left + (((col_widths[col] - width)/2)*HorizAlign) + col_xs[col]; + new_y = grid_top + (((row_heights[row] - height)/2)*VertAlign) + row_ys[row]; + + NR::Point move = NR::Point(new_x - b.min()[NR::X], b.min()[NR::Y] - new_y); // why are the two args the opposite ways round??? + NR::Matrix const &affine = NR::Matrix(NR::translate(move)); + sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * affine); + sp_item_write_transform(item, repr, item->transform, NULL); + SP_OBJECT (current_row->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT); + cnt +=1; + } + g_slist_free (current_row); + } + + NRRect b; + selection->bounds(&b); + + sp_document_done (SP_DT_DOCUMENT (desktop)); + +} + + +//######################################################################### +//## E V E N T S +//######################################################################### + + +void TileDialog::_apply() +{ + Grid_Arrange(); +} + + +/** + * changed value in # of columns spinbox. + */ +void TileDialog::on_row_spinbutton_changed() +{ + // quit if run by the attr_changed listener + if (updating) { + return; + } + + // in turn, prevent listener from responding + updating = true; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + int selcount = g_slist_length((GSList *)items); + + double PerCol = ceil(selcount / NoOfColsSpinner.get_value()); + NoOfRowsSpinner.set_value(PerCol); + prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", NoOfColsSpinner.get_value()); + updating=false; +} + +/** + * changed value in # of rows spinbox. + */ +void TileDialog::on_col_spinbutton_changed() +{ + // quit if run by the attr_changed listener + if (updating) { + return; + } + + // in turn, prevent listener from responding + updating = true; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + + GSList const *items = selection->itemList(); + int selcount = g_slist_length((GSList *)items); + + double PerRow = ceil(selcount / NoOfRowsSpinner.get_value()); + NoOfColsSpinner.set_value(PerRow); + prefs_set_double_attribute ("dialogs.gridtiler", "NoOfCols", PerRow); + + updating=false; +} + +/** + * changed value in x padding spinbox. + */ +void TileDialog::on_xpad_spinbutton_changed() +{ + + prefs_set_double_attribute ("dialogs.gridtiler", "XPad", XPadSpinner.get_value()); + +} + +/** + * changed value in y padding spinbox. + */ +void TileDialog::on_ypad_spinbutton_changed() +{ + + prefs_set_double_attribute ("dialogs.gridtiler", "YPad", YPadSpinner.get_value()); + +} + + +/** + * checked/unchecked autosize Rows button. + */ +void TileDialog::on_RowSize_checkbutton_changed() +{ + + if (RowHeightButton.get_active()) { + prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", 20); + } else { + prefs_set_double_attribute ("dialogs.gridtiler", "AutoRowSize", -20); + } + RowHeightBox.set_sensitive ( !RowHeightButton.get_active()); +} + +/** + * checked/unchecked autosize Rows button. + */ +void TileDialog::on_ColSize_checkbutton_changed() +{ + + if (ColumnWidthButton.get_active()) { + prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", 20); + } else { + prefs_set_double_attribute ("dialogs.gridtiler", "AutoColSize", -20); + } + ColumnWidthBox.set_sensitive ( !ColumnWidthButton.get_active()); + +} + +/** + * changed value in columns spinbox. + */ +void TileDialog::on_rowSize_spinbutton_changed() +{ + // quit if run by the attr_changed listener + if (updating) { + return; + } + + // in turn, prevent listener from responding + updating = true; + prefs_set_double_attribute ("dialogs.gridtiler", "RowHeight", RowHeightSpinner.get_value()); + updating=false; + +} + +/** + * changed value in rows spinbox. + */ +void TileDialog::on_colSize_spinbutton_changed() +{ + // quit if run by the attr_changed listener + if (updating) { + return; + } + + // in turn, prevent listener from responding + updating = true; + prefs_set_double_attribute ("dialogs.gridtiler", "ColWidth", ColumnWidthSpinner.get_value()); + updating=false; + +} + +/** + * changed Radio button in Spacing group. + */ +void TileDialog::Spacing_button_changed() +{ + if (SpaceManualRadioButton.get_active()) { + prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", 20); + } else { + prefs_set_double_attribute ("dialogs.gridtiler", "SpacingType", -20); + } + + SizesHBox.set_sensitive ( SpaceManualRadioButton.get_active()); +} + +/** + * changed Radio button in Vertical Align group. + */ +void TileDialog::VertAlign_changed() +{ + if (VertTopRadioButton.get_active()) { + VertAlign = 0; + prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 0); + } else if (VertCentreRadioButton.get_active()){ + VertAlign = 1; + prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 1); + } else if (VertBotRadioButton.get_active()){ + VertAlign = 2; + prefs_set_double_attribute ("dialogs.gridtiler", "VertAlign", 2); + } + +} + +/** + * changed Radio button in Vertical Align group. + */ +void TileDialog::HorizAlign_changed() +{ + if (HorizLeftRadioButton.get_active()) { + HorizAlign = 0; + prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 0); + } else if (HorizCentreRadioButton.get_active()){ + HorizAlign = 1; + prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 1); + } else if (HorizRightRadioButton.get_active()){ + HorizAlign = 2; + prefs_set_double_attribute ("dialogs.gridtiler", "HorizAlign", 2); + } + +} + +/** + * Desktop selection changed + */ +void TileDialog::updateSelection() +{ + double col_width, row_height; + // quit if run by the attr_changed listener + if (updating) { + return; + } + + col_width=0; + row_height=0; + // in turn, prevent listener from responding + updating = true; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + const GSList *items = selection->itemList(); + int selcount = g_slist_length((GSList *)items); + + if (NoOfColsSpinner.get_value()>1){ + // Update the number of rows assuming number of columns wanted remains same. + double NoOfRows = ceil(selcount / NoOfColsSpinner.get_value()); + NoOfRowsSpinner.set_value(NoOfRows); + + // if the selection has less than the number set for one row, reduce it appropriately + if (selcountupdateSelection(); +} + + +//######################################################################### +//## C O N S T R U C T O R / D E S T R U C T O R +//######################################################################### +/** + * Constructor + */ +TileDialog::TileDialog() + : Dialog ("dialogs.gridtiler", SP_VERB_SELECTION_GRIDTILE) +{ + // bool used by spin button callbacks to stop loops where they change each other. + updating = false; + + // could not do this in gtkmm - there's no Gtk::SizeGroup public constructor (!) + GtkSizeGroup *_col1 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + GtkSizeGroup *_col2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + GtkSizeGroup *_col3 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + { + // Selection Change signal + g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (updateSelectionCallback), this); + } + + Gtk::VBox *mainVBox = get_vbox(); + +#define MARGIN 2 + + //##Set up the panel + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + Inkscape::Selection *selection = SP_DT_SELECTION (desktop); + int selcount = 1; + if (!selection->isEmpty()) { + GSList const *items = selection->itemList(); + selcount = g_slist_length((GSList *)items); + } + + + /*#### Number of Rows ####*/ + + double PerRow = selcount; + double PerCol = 1; + + #ifdef DEBUG_GRID_ARRANGE + g_print("/n PerRox = %f PerCol = %f selcount = %d",PerRow,PerCol,selcount); + #endif + + NoOfRowsLabel.set_label(_("Rows:")); + NoOfRowsBox.pack_start(NoOfRowsLabel, false, false, MARGIN); + + NoOfRowsSpinner.set_digits(0); + NoOfRowsSpinner.set_increments(1, 5); + NoOfRowsSpinner.set_range(1.0, 100.0); + NoOfRowsSpinner.set_value(PerCol); + NoOfRowsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_col_spinbutton_changed)); + tips.set_tip(NoOfRowsSpinner, _("Number of rows")); + NoOfRowsBox.pack_start(NoOfRowsSpinner, false, false, MARGIN); + gtk_size_group_add_widget(_col1, (GtkWidget *) NoOfRowsBox.gobj()); + + RowHeightButton.set_label(_("Equal height")); + double AutoRow = prefs_get_double_attribute ("dialogs.gridtiler", "AutoRowSize", 15); + if (AutoRow>0) + AutoRowSize=true; + else + AutoRowSize=false; + RowHeightButton.set_active(AutoRowSize); + + NoOfRowsBox.pack_start(RowHeightButton, false, false, MARGIN); + + tips.set_tip(RowHeightButton, _("If not set, each row has the height of the tallest object in it")); + RowHeightButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_RowSize_checkbutton_changed)); + + { + /*#### Radio buttons to control vertical alignment ####*/ + + VertAlignLabel.set_label(_("Align:")); + VertAlignHBox.pack_start(VertAlignLabel, false, false, MARGIN); + + VertTopRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed)); + VertAlignGroup = VertTopRadioButton.get_group(); + VertAlignVBox.pack_start(VertTopRadioButton, false, false, 0); + + VertCentreRadioButton.set_group(VertAlignGroup); + VertCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed)); + VertAlignVBox.pack_start(VertCentreRadioButton, false, false, 0); + + VertBotRadioButton.set_group(VertAlignGroup); + VertBotRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::VertAlign_changed)); + VertAlignVBox.pack_start(VertBotRadioButton, false, false, 0); + + VertAlign = prefs_get_double_attribute ("dialogs.gridtiler", "VertAlign", 1); + if (VertAlign == 0) { + VertTopRadioButton.set_active(TRUE); + } + else if (VertAlign == 1) { + VertCentreRadioButton.set_active(TRUE); + } + else if (VertAlign == 2){ + VertBotRadioButton.set_active(TRUE); + } + VertAlignHBox.pack_start(VertAlignVBox, false, false, MARGIN); + NoOfRowsBox.pack_start(VertAlignHBox, false, false, MARGIN); + } + + SpinsHBox.pack_start(NoOfRowsBox, false, false, MARGIN); + + + /*#### Label for X ####*/ + padXByYLabel.set_label(" "); + XByYLabelVBox.pack_start(padXByYLabel, false, false, MARGIN); + XByYLabel.set_markup(" × "); + XByYLabelVBox.pack_start(XByYLabel, false, false, MARGIN); + SpinsHBox.pack_start(XByYLabelVBox, false, false, MARGIN); + gtk_size_group_add_widget(_col2, (GtkWidget *) XByYLabelVBox.gobj()); + + /*#### Number of columns ####*/ + + NoOfColsLabel.set_label(_("Columns:")); + NoOfColsBox.pack_start(NoOfColsLabel, false, false, MARGIN); + + NoOfColsSpinner.set_digits(0); + NoOfColsSpinner.set_increments(1, 5); + NoOfColsSpinner.set_range(1.0, 100.0); + NoOfColsSpinner.set_value(PerRow); + NoOfColsSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_row_spinbutton_changed)); + tips.set_tip(NoOfColsSpinner, _("Number of columns")); + NoOfColsBox.pack_start(NoOfColsSpinner, false, false, MARGIN); + gtk_size_group_add_widget(_col3, (GtkWidget *) NoOfColsBox.gobj()); + + ColumnWidthButton.set_label(_("Equal width")); + double AutoCol = prefs_get_double_attribute ("dialogs.gridtiler", "AutoColSize", 15); + if (AutoCol>0) + AutoColSize=true; + else + AutoColSize=false; + ColumnWidthButton.set_active(AutoColSize); + NoOfColsBox.pack_start(ColumnWidthButton, false, false, MARGIN); + + tips.set_tip(ColumnWidthButton, _("If not set, each column has the width of the widest object in it")); + ColumnWidthButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::on_ColSize_checkbutton_changed)); + + + { + /*#### Radio buttons to control horizontal alignment ####*/ + + HorizAlignLabel.set_label(_("Align:")); + HorizAlignVBox.pack_start(HorizAlignLabel, false, false, MARGIN); + + HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut + + HorizLeftRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed)); + HorizAlignGroup = HorizLeftRadioButton.get_group(); + HorizAlignHBox.pack_start(HorizLeftRadioButton, false, false, 0); + + HorizCentreRadioButton.set_group(HorizAlignGroup); + HorizCentreRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed)); + HorizAlignHBox.pack_start(HorizCentreRadioButton, false, false, 0); + + HorizRightRadioButton.set_group(HorizAlignGroup); + HorizRightRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::HorizAlign_changed)); + HorizAlignHBox.pack_start(HorizRightRadioButton, false, false, 0); + + HorizAlignHBox.pack_start(*(new Gtk::HBox()), true, true, 0); // centering strut + + HorizAlign = prefs_get_double_attribute ("dialogs.gridtiler", "HorizAlign", 1); + if (HorizAlign == 0) { + HorizLeftRadioButton.set_active(TRUE); + } + else if (HorizAlign == 1) { + HorizCentreRadioButton.set_active(TRUE); + } + else if (HorizAlign == 2) { + HorizRightRadioButton.set_active(TRUE); + } + HorizAlignVBox.pack_start(HorizAlignHBox, false, false, MARGIN); + NoOfColsBox.pack_start(HorizAlignVBox, false, false, MARGIN); + } + + SpinsHBox.pack_start(NoOfColsBox, false, false, MARGIN); + + TileBox.pack_start(SpinsHBox, false, false, MARGIN); + + { + /*#### Radio buttons to control spacing manually or to fit selection bbox ####*/ + SpaceByBBoxRadioButton.set_label(_("Fit into selection box")); + SpaceByBBoxRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed)); + SpacingGroup = SpaceByBBoxRadioButton.get_group(); + + SpacingVBox.pack_start(SpaceByBBoxRadioButton, false, false, MARGIN); + + SpaceManualRadioButton.set_label(_("Set spacing:")); + SpaceManualRadioButton.set_group(SpacingGroup); + SpaceManualRadioButton.signal_toggled().connect(sigc::mem_fun(*this, &TileDialog::Spacing_button_changed)); + SpacingVBox.pack_start(SpaceManualRadioButton, false, false, MARGIN); + + TileBox.pack_start(SpacingVBox, false, false, MARGIN); + } + + { + /*#### Y Padding ####*/ + + GtkWidget *i = sp_icon_new (GTK_ICON_SIZE_MENU, "clonetiler_per_row"); + YPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN); + + YPadSpinner.set_digits(1); + YPadSpinner.set_increments(0.2, 2); + YPadSpinner.set_range(-10000, 10000); + double YPad = prefs_get_double_attribute ("dialogs.gridtiler", "YPad", 15); + YPadSpinner.set_value(YPad); + YPadBox.pack_start(YPadSpinner, true, true, MARGIN); + tips.set_tip(YPadSpinner, _("Vertical spacing between rows (px units)")); + YPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_ypad_spinbutton_changed)); + gtk_size_group_add_widget(_col1, (GtkWidget *) YPadBox.gobj()); + + SizesHBox.pack_start(YPadBox, false, false, MARGIN); + } + + { + Gtk::HBox *spacer = new Gtk::HBox; + SizesHBox.pack_start(*spacer, false, false, 0); + gtk_size_group_add_widget(_col2, (GtkWidget *) spacer->gobj()); + } + + { + /*#### X padding ####*/ + + GtkWidget *i = sp_icon_new (GTK_ICON_SIZE_MENU, "clonetiler_per_column"); + XPadBox.pack_start (*(Glib::wrap(i)), false, false, MARGIN); + + XPadSpinner.set_digits(1); + XPadSpinner.set_increments(0.2, 2); + XPadSpinner.set_range(-10000, 10000); + double XPad = prefs_get_double_attribute ("dialogs.gridtiler", "XPad", 15); + XPadSpinner.set_value(XPad); + XPadBox.pack_start(XPadSpinner, true, true, MARGIN); + tips.set_tip(XPadSpinner, _("Horizontal spacing between columns (px units)")); + XPadSpinner.signal_changed().connect(sigc::mem_fun(*this, &TileDialog::on_xpad_spinbutton_changed)); + gtk_size_group_add_widget(_col3, (GtkWidget *) XPadBox.gobj()); + + SizesHBox.pack_start(XPadBox, false, false, MARGIN); + } + + + TileBox.pack_start(SizesHBox, false, false, MARGIN); + + mainVBox->pack_start(TileBox); + + double SpacingType = prefs_get_double_attribute ("dialogs.gridtiler", "SpacingType", 15); + if (SpacingType>0) { + ManualSpacing=true; + } else { + ManualSpacing=false; + } + SpaceManualRadioButton.set_active(ManualSpacing); + SpaceByBBoxRadioButton.set_active(!ManualSpacing); + SizesHBox.set_sensitive (ManualSpacing); + + //## The OK button + TileOkButton = add_button(Gtk::Stock::APPLY, GTK_RESPONSE_APPLY); + tips.set_tip((*TileOkButton), _("Arrange selected objects")); + + show_all_children(); +} + + + + + + +} //namespace Dialog +} //namespace UI +} //namespace Inkscape + +//######################################################################### +//## E N D O F F I L E +//######################################################################### + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : + + + diff --git a/src/dialogs/tiledialog.h b/src/dialogs/tiledialog.h new file mode 100644 index 000000000..69a5ea3e2 --- /dev/null +++ b/src/dialogs/tiledialog.h @@ -0,0 +1,190 @@ +#ifndef __TILEDIALOG_H__ +#define __TILEDIALOG_H__ +/* + * A simple dialog for creating grid type arrangements of selected objects + * + * Authors: + * Bob Jamison ( based off trace dialog) + * John Cliff + * Other dudes from The Inkscape Organization + * + * Copyright (C) 2004 Bob Jamison + * Copyright (C) 2004 John Cliff + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "ui/dialog/dialog.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + + +/** + * A dialog that displays log messages + */ +class TileDialog : public Dialog { + +public: + + /** + * Constructor + */ + TileDialog() ; + + + /** + * Factory method + */ + static TileDialog *create() { return new TileDialog(); } + + /** + * Destructor + */ + virtual ~TileDialog() {}; + + /** + * Do the actual work + */ + void Grid_Arrange(); + + /** + * Respond to selection change + */ + void updateSelection(); + + + /** + * Callback from Apply + */ + virtual void _apply(); + + /** + * Callback from spinbuttons + */ + void on_row_spinbutton_changed(); + void on_col_spinbutton_changed(); + void on_xpad_spinbutton_changed(); + void on_ypad_spinbutton_changed(); + void on_RowSize_checkbutton_changed(); + void on_ColSize_checkbutton_changed(); + void on_rowSize_spinbutton_changed(); + void on_colSize_spinbutton_changed(); + void Spacing_button_changed(); + void VertAlign_changed(); + void HorizAlign_changed(); + + +private: + TileDialog(TileDialog const &d); // no copy + void operator=(TileDialog const &d); // no assign + + bool userHidden; + bool updating; + + + + Gtk::Notebook notebook; + Gtk::Tooltips tips; + + Gtk::VBox TileBox; + Gtk::Button *TileOkButton; + Gtk::Button *TileCancelButton; + + // Number selected label + Gtk::Label SelectionContentsLabel; + + + Gtk::HBox AlignHBox; + Gtk::HBox SpinsHBox; + Gtk::HBox SizesHBox; + + // Number per Row + Gtk::VBox NoOfColsBox; + Gtk::Label NoOfColsLabel; + Gtk::SpinButton NoOfColsSpinner; + bool AutoRowSize; + Gtk::CheckButton RowHeightButton; + + Gtk::VBox XByYLabelVBox; + Gtk::Label padXByYLabel; + Gtk::Label XByYLabel; + + // Number per Column + Gtk::VBox NoOfRowsBox; + Gtk::Label NoOfRowsLabel; + Gtk::SpinButton NoOfRowsSpinner; + bool AutoColSize; + Gtk::CheckButton ColumnWidthButton; + + // Vertical align + Gtk::Label VertAlignLabel; + Gtk::HBox VertAlignHBox; + Gtk::VBox VertAlignVBox; + Gtk::RadioButtonGroup VertAlignGroup; + Gtk::RadioButton VertCentreRadioButton; + Gtk::RadioButton VertTopRadioButton; + Gtk::RadioButton VertBotRadioButton; + double VertAlign; + + // Horizontal align + Gtk::Label HorizAlignLabel; + Gtk::VBox HorizAlignVBox; + Gtk::HBox HorizAlignHBox; + Gtk::RadioButtonGroup HorizAlignGroup; + Gtk::RadioButton HorizCentreRadioButton; + Gtk::RadioButton HorizLeftRadioButton; + Gtk::RadioButton HorizRightRadioButton; + double HorizAlign; + + // padding in x + Gtk::VBox XPadBox; + Gtk::Label XPadLabel; + Gtk::SpinButton XPadSpinner; + + // padding in y + Gtk::VBox YPadBox; + Gtk::Label YPadLabel; + Gtk::SpinButton YPadSpinner; + + // BBox or manual spacing + Gtk::VBox SpacingVBox; + Gtk::RadioButtonGroup SpacingGroup; + Gtk::RadioButton SpaceByBBoxRadioButton; + Gtk::RadioButton SpaceManualRadioButton; + bool ManualSpacing; + + + + // Row height + Gtk::VBox RowHeightVBox; + Gtk::HBox RowHeightBox; + Gtk::Label RowHeightLabel; + Gtk::SpinButton RowHeightSpinner; + + // Column width + Gtk::VBox ColumnWidthVBox; + Gtk::HBox ColumnWidthBox; + Gtk::Label ColumnWidthLabel; + Gtk::SpinButton ColumnWidthSpinner; + +}; + + +} //namespace Dialog +} //namespace UI +} //namespace Inkscape + + +#endif /* __TILEDIALOG_H__ */ + diff --git a/src/dialogs/unclump.cpp b/src/dialogs/unclump.cpp new file mode 100644 index 000000000..64c348be5 --- /dev/null +++ b/src/dialogs/unclump.cpp @@ -0,0 +1,371 @@ +#define __UNCLUMP_C__ + +/* + * Unclumping objects + * + * Authors: + * bulia byak + * + * Copyright (C) 2005 Authors + * Released under GNU GPL + */ + + +#include +#include "libnr/nr-matrix-ops.h" +#include "sp-item.h" + + +// Taking bbox of an item is an expensive operation, and we need to do it many times, so here we +// cache the centers, widths, and heights of items + +//FIXME: make a class with these cashes as members instead of globals +std::map c_cache; +std::map wh_cache; + +/** +Center of bbox of item +*/ +NR::Point +unclump_center (SPItem *item) +{ + std::map::iterator i = c_cache.find(SP_OBJECT_ID(item)); + if ( i != c_cache.end() ) { + return i->second; + } + + NR::Rect const r = item->invokeBbox(sp_item_i2d_affine(item)); + NR::Point const c = r.midpoint(); + c_cache[SP_OBJECT_ID(item)] = c; + return c; +} + +NR::Point +unclump_wh (SPItem *item) +{ + NR::Point wh; + std::map::iterator i = wh_cache.find(SP_OBJECT_ID(item)); + if ( i != wh_cache.end() ) { + wh = i->second; + } else { + NR::Rect const r = item->invokeBbox(sp_item_i2d_affine(item)); + wh = r.dimensions(); + wh_cache[SP_OBJECT_ID(item)] = wh; + } + + return wh; +} + +/** +Distance between "edges" of item1 and item2. An item is considered to be an ellipse inscribed into its w/h, +so its radius (distance from center to edge) depends on the w/h and the angle towards the other item. +May be negative if the edge of item1 is between the center and the edge of item2. +*/ +double +unclump_dist (SPItem *item1, SPItem *item2) +{ + NR::Point c1 = unclump_center (item1); + NR::Point c2 = unclump_center (item2); + + NR::Point wh1 = unclump_wh (item1); + NR::Point wh2 = unclump_wh (item2); + + // angle from each item's center to the other's, unsqueezed by its w/h, normalized to 0..pi/2 + double a1 = atan2 ((c2 - c1)[NR::Y], (c2 - c1)[NR::X] * wh1[NR::Y]/wh1[NR::X]); + a1 = fabs (a1); + if (a1 > M_PI/2) a1 = M_PI - a1; + + double a2 = atan2 ((c1 - c2)[NR::Y], (c1 - c2)[NR::X] * wh2[NR::Y]/wh2[NR::X]); + a2 = fabs (a2); + if (a2 > M_PI/2) a2 = M_PI - a2; + + // get the radius of each item for the given angle + double r1 = 0.5 * (wh1[NR::X] + (wh1[NR::Y] - wh1[NR::X]) * (a1/(M_PI/2))); + double r2 = 0.5 * (wh2[NR::X] + (wh2[NR::Y] - wh2[NR::X]) * (a2/(M_PI/2))); + + // dist between centers minus angle-adjusted radii + double dist_r = (NR::L2 (c2 - c1) - r1 - r2); + + double stretch1 = wh1[NR::Y]/wh1[NR::X]; + double stretch2 = wh2[NR::Y]/wh2[NR::X]; + + if ((stretch1 > 1.5 || stretch1 < 0.66) && (stretch2 > 1.5 || stretch2 < 0.66)) { + + std::vector dists; + dists.push_back (dist_r); + + // If both objects are not circle-like, find dists between four corners + std::vector c1_points(2); + { + double y_closest; + if (c2[NR::Y] > c1[NR::Y] + wh1[NR::Y]/2) { + y_closest = c1[NR::Y] + wh1[NR::Y]/2; + } else if (c2[NR::Y] < c1[NR::Y] - wh1[NR::Y]/2) { + y_closest = c1[NR::Y] - wh1[NR::Y]/2; + } else { + y_closest = c2[NR::Y]; + } + c1_points[0] = NR::Point (c1[NR::X], y_closest); + double x_closest; + if (c2[NR::X] > c1[NR::X] + wh1[NR::X]/2) { + x_closest = c1[NR::X] + wh1[NR::X]/2; + } else if (c2[NR::X] < c1[NR::X] - wh1[NR::X]/2) { + x_closest = c1[NR::X] - wh1[NR::X]/2; + } else { + x_closest = c2[NR::X]; + } + c1_points[1] = NR::Point (x_closest, c1[NR::Y]); + } + + + std::vector c2_points(2); + { + double y_closest; + if (c1[NR::Y] > c2[NR::Y] + wh2[NR::Y]/2) { + y_closest = c2[NR::Y] + wh2[NR::Y]/2; + } else if (c1[NR::Y] < c2[NR::Y] - wh2[NR::Y]/2) { + y_closest = c2[NR::Y] - wh2[NR::Y]/2; + } else { + y_closest = c1[NR::Y]; + } + c2_points[0] = NR::Point (c2[NR::X], y_closest); + double x_closest; + if (c1[NR::X] > c2[NR::X] + wh2[NR::X]/2) { + x_closest = c2[NR::X] + wh2[NR::X]/2; + } else if (c1[NR::X] < c2[NR::X] - wh2[NR::X]/2) { + x_closest = c2[NR::X] - wh2[NR::X]/2; + } else { + x_closest = c1[NR::X]; + } + c2_points[1] = NR::Point (x_closest, c2[NR::Y]); + } + + for (int i = 0; i < 2; i ++) { + for (int j = 0; j < 2; j ++) { + dists.push_back (NR::L2 (c1_points[i] - c2_points[j])); + } + } + + // return the minimum of all dists + return *std::min_element(dists.begin(), dists.end()); + } else { + return dist_r; + } +} + +/** +Average unclump_dist from item to others +*/ +double unclump_average (SPItem *item, GSList *others) +{ + int n = 0; + double sum = 0; + + for (GSList *i = others; i != NULL; i = i->next) { + SPItem *other = SP_ITEM (i->data); + + if (other == item) + continue; + + n++; + sum += unclump_dist (item, other); + } + + if (n != 0) + return sum/n; + else + return 0; +} + +/** +Closest to item among others + */ +SPItem *unclump_closest (SPItem *item, GSList *others) +{ + double min = HUGE_VAL; + SPItem *closest = NULL; + + for (GSList *i = others; i != NULL; i = i->next) { + SPItem *other = SP_ITEM (i->data); + + if (other == item) + continue; + + double dist = unclump_dist (item, other); + if (dist < min && fabs (dist) < 1e6) { + min = dist; + closest = other; + } + } + + return closest; +} + +/** +Most distant from item among others + */ +SPItem *unclump_farest (SPItem *item, GSList *others) +{ + double max = -HUGE_VAL; + SPItem *farest = NULL; + + for (GSList *i = others; i != NULL; i = i->next) { + SPItem *other = SP_ITEM (i->data); + + if (other == item) + continue; + + double dist = unclump_dist (item, other); + if (dist > max && fabs (dist) < 1e6) { + max = dist; + farest = other; + } + } + + return farest; +} + +/** +Removes from the \a rest list those items that are "behind" \a closest as seen from \a item, +i.e. those on the other side of the line through \a closest perpendicular to the direction from \a +item to \a closest. Returns a newly created list which must be freed. + */ +GSList * +unclump_remove_behind (SPItem *item, SPItem *closest, GSList *rest) +{ + NR::Point it = unclump_center (item); + NR::Point p1 = unclump_center (closest); + + // perpendicular through closest to the direction to item: + NR::Point perp = NR::rot90(it - p1); + NR::Point p2 = p1 + perp; + + // get the standard Ax + By + C = 0 form for p1-p2: + double A = p1[NR::Y] - p2[NR::Y]; + double B = p2[NR::X] - p1[NR::X]; + double C = p2[NR::Y] * p1[NR::X] - p1[NR::Y] * p2[NR::X]; + + // substitute the item into it: + double val_item = A * it[NR::X] + B * it[NR::Y] + C; + + GSList *out = NULL; + + for (GSList *i = rest; i != NULL; i = i->next) { + SPItem *other = SP_ITEM (i->data); + + if (other == item) + continue; + + NR::Point o = unclump_center (other); + double val_other = A * o[NR::X] + B * o[NR::Y] + C; + + if (val_item * val_other <= 1e-6) { + // different signs, which means item and other are on the different sides of p1-p2 line; skip + } else { + out = g_slist_prepend (out, other); + } + } + + return out; +} + +/** +Moves \a what away from \a from by \a dist + */ +void +unclump_push (SPItem *from, SPItem *what, double dist) +{ + NR::Point it = unclump_center (what); + NR::Point p = unclump_center (from); + NR::Point by = dist * NR::unit_vector (- (p - it)); + + NR::Matrix move = NR::Matrix (NR::translate (by)); + + std::map::iterator i = c_cache.find(SP_OBJECT_ID(what)); + if ( i != c_cache.end() ) { + i->second *= move; + } + + //g_print ("push %s at %g,%g from %g,%g by %g,%g, dist %g\n", SP_OBJECT_ID(what), it[NR::X],it[NR::Y], p[NR::X],p[NR::Y], by[NR::X],by[NR::Y], dist); + + sp_item_set_i2d_affine(what, sp_item_i2d_affine(what) * move); + sp_item_write_transform(what, SP_OBJECT_REPR(what), what->transform, NULL); +} + +/** +Moves \a what towards \a to by \a dist + */ +void +unclump_pull (SPItem *to, SPItem *what, double dist) +{ + NR::Point it = unclump_center (what); + NR::Point p = unclump_center (to); + NR::Point by = dist * NR::unit_vector (p - it); + + NR::Matrix move = NR::Matrix (NR::translate (by)); + + std::map::iterator i = c_cache.find(SP_OBJECT_ID(what)); + if ( i != c_cache.end() ) { + i->second *= move; + } + + //g_print ("pull %s at %g,%g to %g,%g by %g,%g, dist %g\n", SP_OBJECT_ID(what), it[NR::X],it[NR::Y], p[NR::X],p[NR::Y], by[NR::X],by[NR::Y], dist); + + sp_item_set_i2d_affine(what, sp_item_i2d_affine(what) * move); + sp_item_write_transform(what, SP_OBJECT_REPR(what), what->transform, NULL); +} + + +/** +Unclumps the items in \a items, reducing local unevenness in their distribution. Produces an effect +similar to "engraver dots". The only distribution which is unchanged by unclumping is a hexagonal +grid. May be called repeatedly for stronger effect. + */ +void +unclump (GSList *items) +{ + c_cache.clear(); + wh_cache.clear(); + + for (GSList *i = items; i != NULL; i = i->next) { // for each original/clone x: + SPItem *item = SP_ITEM (i->data); + + GSList *nei = NULL; + + GSList *rest = g_slist_copy (items); + rest = g_slist_remove (rest, item); + + while (rest != NULL) { + SPItem *closest = unclump_closest (item, rest); + if (closest) { + nei = g_slist_prepend (nei, closest); + rest = g_slist_remove (rest, closest); + GSList *new_rest = unclump_remove_behind (item, closest, rest); + g_slist_free (rest); + rest = new_rest; + } else { + g_slist_free (rest); + break; + } + } + + if (g_slist_length (nei) >= 2) { + double ave = unclump_average (item, nei); + + SPItem *closest = unclump_closest (item, nei); + SPItem *farest = unclump_farest (item, nei); + + double dist_closest = unclump_dist (closest, item); + double dist_farest = unclump_dist (farest, item); + + //g_print ("NEI %d for item %s closest %s at %g farest %s at %g ave %g\n", g_slist_length(nei), SP_OBJECT_ID(item), SP_OBJECT_ID(closest), dist_closest, SP_OBJECT_ID(farest), dist_farest, ave); + + if (fabs (ave) < 1e6 && fabs (dist_closest) < 1e6 && fabs (dist_farest) < 1e6) { // otherwise the items are bogus + // increase these coefficients to make unclumping more aggressive and less stable + // the pull coefficient is a bit bigger to counteract the long-term expansion trend + unclump_push (closest, item, 0.3 * (ave - dist_closest)); + unclump_pull (farest, item, 0.35 * (dist_farest - ave)); + } + } + } +} diff --git a/src/dialogs/unclump.h b/src/dialogs/unclump.h new file mode 100644 index 000000000..6cebc0caf --- /dev/null +++ b/src/dialogs/unclump.h @@ -0,0 +1,20 @@ +#ifndef UNCLUMP_H_SEEN +#define UNCLUMP_H_SEEN + +/** \file + * Unclumping objects + */ +/* + * Authors: + * bulia byak + * + * Copyright (C) 2005 Authors + * Released under GNU GPL + */ + +#include + +void unclump(GSList *items); + + +#endif /* !UNCLUMP_H_SEEN */ diff --git a/src/dialogs/xml-tree.cpp b/src/dialogs/xml-tree.cpp new file mode 100644 index 000000000..079a09b5a --- /dev/null +++ b/src/dialogs/xml-tree.cpp @@ -0,0 +1,1575 @@ +#define __SP_XMLVIEW_TREE_C__ + +/** + * \brief XML View + * + * Authors: + * Lauris Kaplinski + * MenTaLguY + * bulia byak + * + * Copyright (C) 1999-2005 Authors + * Copyright (C) 2004 David Turner + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "helper/window.h" +#include "macros.h" +#include "../inkscape.h" +#include "../document.h" +#include "../desktop-handles.h" +#include "desktop.h" +#include "../selection.h" +#include "../sp-string.h" +#include "../sp-tspan.h" +#include "../sp-root.h" +#include "../event-context.h" +#include "in-dt-coordsys.h" + + +#include "../widgets/sp-xmlview-tree.h" +#include "../widgets/sp-xmlview-content.h" +#include "../widgets/sp-xmlview-attr-list.h" + +#include "../inkscape-stock.h" +#include "widgets/icon.h" + +#include "dialog-events.h" +#include "../prefs-utils.h" +#include "../verbs.h" +#include "../interface.h" + +#include "shortcuts.h" +#include + +#include "message-stack.h" +#include "message-context.h" + +struct EditableDest { + GtkEditable *editable; + gchar *text; +}; + +static GtkWidget *dlg = NULL; +static sigc::connection sel_changed_connection; +static sigc::connection document_uri_set_connection; +static sigc::connection document_replaced_connection; +static win_data wd; +// impossible original values to make sure they are read from prefs +static gint x = -1000, y = -1000, w = 0, h = 0; +static gchar *prefs_path = "dialogs.xml"; +static GtkWidget *status = NULL; +static Inkscape::MessageStack *_message_stack = NULL; +static Inkscape::MessageContext *_message_context = NULL; +static sigc::connection _message_changed_connection; + +static GtkTooltips *tooltips = NULL; +static GtkEditable *attr_name = NULL; +static GtkTextView *attr_value = NULL; +static SPXMLViewTree *tree = NULL; +static SPXMLViewAttrList *attributes = NULL; +static SPXMLViewContent *content = NULL; + +static gint blocked = 0; +static SPDesktop *current_desktop = NULL; +static SPDocument *current_document = NULL; +static gint selected_attr = 0; +static Inkscape::XML::Node *selected_repr = NULL; + +static void sp_xmltree_desktop_change( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dialog ); + +static void set_tree_desktop(SPDesktop *desktop); +static void set_tree_document(SPDocument *document); +static void set_tree_repr(Inkscape::XML::Node *repr); + +static void set_tree_select(Inkscape::XML::Node *repr); +static void propagate_tree_select(Inkscape::XML::Node *repr); + +static Inkscape::XML::Node *get_dt_select(); +static void set_dt_select(Inkscape::XML::Node *repr); + +static void on_tree_select_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_unselect_row(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data); +static void on_destroy(GtkObject *object, gpointer data); +static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data); + +static void on_tree_select_row_enable_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); + +static void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); +static void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, gint column, gpointer data); + +static void on_attr_select_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); +static void on_attr_unselect_row(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); +static void on_attr_row_changed( GtkCList *list, gint row, gpointer data ); + +static void on_attr_select_row_enable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); +static void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); + +static void on_attr_select_row_set_name_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); +static void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); +static void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, GdkEventButton *event, gpointer data); + +static void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, gpointer data); + +static void on_desktop_selection_changed(Inkscape::Selection *selection); +static void on_document_replaced(SPDesktop *dt, SPDocument *document); +static void on_document_uri_set(gchar const *uri, SPDocument *document); + +static void on_clicked_get_editable_text(GtkWidget *widget, gpointer data); + +static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog); + +static void cmd_new_element_node(GtkObject *object, gpointer data); +static void cmd_new_text_node(GtkObject *object, gpointer data); +static void cmd_duplicate_node(GtkObject *object, gpointer data); +static void cmd_delete_node(GtkObject *object, gpointer data); + +static void cmd_raise_node(GtkObject *object, gpointer data); +static void cmd_lower_node(GtkObject *object, gpointer data); +static void cmd_indent_node(GtkObject *object, gpointer data); +static void cmd_unindent_node(GtkObject *object, gpointer data); + +static void cmd_delete_attr(GtkObject *object, gpointer data); +static void cmd_set_attr(GtkObject *object, gpointer data); + +static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event); + + +/* + * \brief Sets the XML status bar when the tree is selected. + */ +void tree_reset_context() +{ + _message_context->set(Inkscape::NORMAL_MESSAGE, + _("Click to select nodes, drag to rearrange.")); +} + + +/* + * \brief Sets the XML status bar, depending on which attr is selected. + */ +void attr_reset_context(gint attr) +{ + if (attr == 0) { + _message_context->set(Inkscape::NORMAL_MESSAGE, + _("Click attribute to edit.")); + } + else { + const gchar *name = g_quark_to_string(attr); + gchar *message = g_strdup_printf(_("Attribute %s selected. Press Ctrl+Enter when done editing to commit changes."), name); + _message_context->set(Inkscape::NORMAL_MESSAGE, message); + g_free(message); + } +} + + +void sp_xml_tree_dialog() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + if (!desktop) { + return; + } + + if (dlg == NULL) + { // very long block + + GtkWidget *box, *sw, *paned, *toolbar, *button; + GtkWidget *text_container, *attr_container, *attr_subpaned_container, *box2; + GtkWidget *set_attr; + + tooltips = gtk_tooltips_new(); + gtk_tooltips_enable(tooltips); + + dlg = sp_window_new("", TRUE); + if (x == -1000 || y == -1000) { + x = prefs_get_int_attribute(prefs_path, "x", 0); + y = prefs_get_int_attribute(prefs_path, "y", 0); + } + if (w ==0 || h == 0) { + w = prefs_get_int_attribute(prefs_path, "w", 0); + h = prefs_get_int_attribute(prefs_path, "h", 0); + } + if (x != 0 || y != 0) { + gtk_window_move((GtkWindow *) dlg, x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + + if (w && h) { + gtk_window_resize((GtkWindow *) dlg, w, h); + } + sp_transientize(dlg); + wd.win = dlg; + wd.stop = 0; + g_signal_connect ( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd ); + + gtk_signal_connect( GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg ); + + gtk_signal_connect( GTK_OBJECT(dlg), "destroy", G_CALLBACK(on_destroy), dlg); + gtk_signal_connect( GTK_OBJECT(dlg), "delete_event", G_CALLBACK(on_delete), dlg); + g_signal_connect ( G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(on_delete), dlg); + + g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg); + g_signal_connect ( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg); + + + gtk_container_set_border_width(GTK_CONTAINER(dlg), 0); + gtk_window_set_default_size(GTK_WINDOW(dlg), 640, 384); + + GtkWidget *vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(dlg), vbox); + + GtkWidget *hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 2); + + status = gtk_label_new(NULL); + gtk_misc_set_alignment(GTK_MISC(status), 0.0, 0.5); + gtk_widget_set_size_request(status, 1, -1); + gtk_label_set_markup(GTK_LABEL(status), ""); + gtk_box_pack_start(GTK_BOX(hbox), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 4); + gtk_box_pack_start(GTK_BOX(hbox), status, TRUE, TRUE, 0); + + paned = gtk_hpaned_new(); + gtk_paned_set_position(GTK_PANED(paned), 256); + gtk_box_pack_start(GTK_BOX(vbox), paned, TRUE, TRUE, 0); + + _message_stack = new Inkscape::MessageStack(); + _message_context = new Inkscape::MessageContext(_message_stack); + _message_changed_connection = _message_stack->connectChanged( + sigc::bind(sigc::ptr_fun(_set_status_message), dlg) + ); + + /* tree view */ + + box = gtk_vbox_new(FALSE, 0); + gtk_paned_pack1(GTK_PANED(paned), box, FALSE, FALSE); + + tree = SP_XMLVIEW_TREE(sp_xmlview_tree_new(NULL, NULL, NULL)); + gtk_tooltips_set_tip( tooltips, GTK_WIDGET(tree), + _("Drag to reorder nodes"), NULL ); + + g_signal_connect( G_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row), NULL ); + + g_signal_connect( G_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row), NULL ); + + g_signal_connect_after( G_OBJECT(tree), "tree_move", + G_CALLBACK(after_tree_move), NULL); + + /* TODO: replace gtk_signal_connect_while_alive() with something + * else... + */ + toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); + gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), + NULL, + _("New element node"), + NULL, + sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_ADD_XML_ELEMENT_NODE ), + G_CALLBACK(cmd_new_element_node), + NULL); + + gtk_signal_connect_while_alive( GTK_OBJECT(tree), + "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_element), + button, + GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), + "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, + GTK_OBJECT(button)); + + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), + NULL, _("New text node"), NULL, + sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_ADD_XML_TEXT_NODE ), + G_CALLBACK(cmd_new_text_node), + NULL); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), + "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_element), + button, + GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), + "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, + GTK_OBJECT(button)); + + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), + NULL, _("Duplicate node"), NULL, + sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_DUPLICATE_XML_NODE ), + G_CALLBACK(cmd_duplicate_node), + NULL); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), + "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_mutable), + button, + GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, GTK_OBJECT(button)); + + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), + NULL, _("Delete node"), NULL, + sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_DELETE_XML_NODE ), + G_CALLBACK(cmd_delete_node), NULL ); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_mutable), + button, GTK_OBJECT(button)); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, GTK_OBJECT(button)); + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); + + button = gtk_toolbar_append_item( GTK_TOOLBAR(toolbar), "<", + _("Unindent node"), NULL, + gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_IN), + G_CALLBACK(cmd_unindent_node), NULL); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_has_grandparent), + button, GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, GTK_OBJECT(button)); + + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), ">", + _("Indent node"), NULL, + gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_IN), + G_CALLBACK(cmd_indent_node), NULL); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_indentable), + button, GTK_OBJECT(button)); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_disable, + button, GTK_OBJECT(button)); + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "^", + _("Raise node"), NULL, + gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_IN), + G_CALLBACK(cmd_raise_node), NULL); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_not_first_child), + button, GTK_OBJECT(button)); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, GTK_OBJECT(button)); + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "v", + _("Lower node"), NULL, + gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_IN), + G_CALLBACK(cmd_lower_node), NULL); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + G_CALLBACK(on_tree_select_row_enable_if_not_last_child), + button, GTK_OBJECT(button)); + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + G_CALLBACK(on_tree_unselect_row_disable), + button, GTK_OBJECT(button)); + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + gtk_box_pack_start(GTK_BOX(box), toolbar, FALSE, TRUE, 0); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(tree)); + + /* node view */ + + box = gtk_vbox_new(FALSE, 0); + gtk_paned_pack2(GTK_PANED(paned), box, TRUE, TRUE); + + /* attributes */ + + attr_container = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start( GTK_BOX(box), GTK_WIDGET(attr_container), + TRUE, TRUE, 0 ); + + attributes = SP_XMLVIEW_ATTR_LIST(sp_xmlview_attr_list_new(NULL)); + g_signal_connect( G_OBJECT(attributes), "select_row", + G_CALLBACK(on_attr_select_row), NULL); + g_signal_connect( G_OBJECT(attributes), "unselect_row", + G_CALLBACK(on_attr_unselect_row), NULL); + g_signal_connect( G_OBJECT(attributes), "row-value-changed", + G_CALLBACK(on_attr_row_changed), NULL); + + toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); + gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0); + + button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), + NULL, _("Delete attribute"), NULL, + sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR, + INKSCAPE_STOCK_DELETE_XML_ATTRIBUTE ), + (GCallback) cmd_delete_attr, NULL); + + gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "select_row", + (GCallback) on_attr_select_row_enable, button, + GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(attributes), "unselect_row", + (GCallback) on_attr_unselect_row_disable, button, + GTK_OBJECT(button)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_disable, button, + GTK_OBJECT(button)); + + gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); + + gtk_box_pack_start( GTK_BOX(attr_container), + GTK_WIDGET(toolbar), FALSE, TRUE, 0 ); + + attr_subpaned_container = gtk_vpaned_new(); + gtk_box_pack_start( GTK_BOX(attr_container), + GTK_WIDGET(attr_subpaned_container), + TRUE, TRUE, 0 ); + gtk_widget_show(attr_subpaned_container); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_paned_pack1( GTK_PANED(attr_subpaned_container), + GTK_WIDGET(sw), TRUE, TRUE ); + gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(attributes)); + + toolbar = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(toolbar), 4); + + box2 = gtk_hbox_new(FALSE, 4); + gtk_box_pack_start( GTK_BOX(toolbar), GTK_WIDGET(box2), + FALSE, TRUE, 0); + + attr_name = GTK_EDITABLE(gtk_entry_new()); + gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_name), + // TRANSLATORS: "Attribute" is a noun here + _("Attribute name"), NULL ); + + gtk_signal_connect( GTK_OBJECT(attributes), "select_row", + (GCallback) on_attr_select_row_set_name_content, + attr_name); + + gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row", + (GCallback) on_attr_unselect_row_clear_text, + attr_name); + + gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_clear_text, + attr_name); + + gtk_box_pack_start( GTK_BOX(box2), GTK_WIDGET(attr_name), + TRUE, TRUE, 0); + + set_attr = gtk_button_new(); + gtk_tooltips_set_tip( tooltips, GTK_WIDGET(set_attr), + // TRANSLATORS: "Set" is a verb here + _("Set attribute"), NULL ); + // TRANSLATORS: "Set" is a verb here + GtkWidget *set_label = gtk_label_new(_("Set")); + gtk_container_add(GTK_CONTAINER(set_attr), set_label); + + gtk_signal_connect( GTK_OBJECT(set_attr), "clicked", + (GCallback) cmd_set_attr, NULL); + gtk_signal_connect( GTK_OBJECT(attr_name), "changed", + (GCallback) on_editable_changed_enable_if_valid_xml_name, + set_attr ); + gtk_widget_set_sensitive(GTK_WIDGET(set_attr), FALSE); + + gtk_box_pack_start(GTK_BOX(box2), set_attr, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN ); + gtk_box_pack_start(GTK_BOX(toolbar), sw, TRUE, TRUE, 0); + + attr_value =(GtkTextView *) gtk_text_view_new(); + gtk_text_view_set_wrap_mode((GtkTextView *) attr_value, GTK_WRAP_CHAR); + gtk_tooltips_set_tip( tooltips, GTK_WIDGET(attr_value), + // TRANSLATORS: "Attribute" is a noun here + _("Attribute value"), NULL ); + gtk_signal_connect( GTK_OBJECT(attributes), "select_row", + (GCallback) on_attr_select_row_set_value_content, + attr_value ); + gtk_signal_connect( GTK_OBJECT(attributes), "unselect_row", + (GCallback) on_attr_unselect_row_clear_text, + attr_value ); + gtk_signal_connect( GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_clear_text, + attr_value ); + gtk_text_view_set_editable(attr_value, TRUE); + gtk_container_add( GTK_CONTAINER(sw), + GTK_WIDGET(attr_value) ); + + gtk_paned_pack2( GTK_PANED(attr_subpaned_container), + GTK_WIDGET(toolbar), FALSE, TRUE ); + + /* text */ + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN ); + gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(sw), TRUE, TRUE, 0); + + content = SP_XMLVIEW_CONTENT(sp_xmlview_content_new(NULL)); + gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(content)); + + text_container = sw; + + /* initial show/hide */ + + gtk_widget_show_all(GTK_WIDGET(dlg)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + (GCallback) on_tree_select_row_show_if_element, + attr_container, GTK_OBJECT(attr_container)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_hide, + attr_container, GTK_OBJECT(attr_container)); + + gtk_widget_hide(attr_container); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_select_row", + (GCallback) on_tree_select_row_show_if_text, + text_container, GTK_OBJECT(text_container)); + + gtk_signal_connect_while_alive(GTK_OBJECT(tree), "tree_unselect_row", + (GCallback) on_tree_unselect_row_hide, + text_container, GTK_OBJECT(text_container)); + + gtk_widget_hide(text_container); + + g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop", + G_CALLBACK(sp_xmltree_desktop_change), dlg); + + g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop", + G_CALLBACK(sp_xmltree_desktop_change), dlg); + + g_signal_connect((GObject *) dlg, "key_press_event", (GCallback) sp_xml_tree_key_press, NULL); + + tree_reset_context(); + } // end of if (dlg == NULL) + + gtk_window_present((GtkWindow *) dlg); + + g_assert(desktop != NULL); + set_tree_desktop(desktop); + +} // end of sp_xml_tree_dialog() + +static gboolean sp_xml_tree_key_press(GtkWidget *widget, GdkEventKey *event) +{ + + unsigned int shortcut = get_group0_keyval(event) | + ( event->state & GDK_SHIFT_MASK ? + SP_SHORTCUT_SHIFT_MASK : 0 ) | + ( event->state & GDK_CONTROL_MASK ? + SP_SHORTCUT_CONTROL_MASK : 0 ) | + ( event->state & GDK_MOD1_MASK ? + SP_SHORTCUT_ALT_MASK : 0 ); + + /* fixme: if you need to add more xml-tree-specific callbacks, you should probably upgrade + * the sp_shortcut mechanism to take into account windows. */ + if (shortcut == (SP_SHORTCUT_CONTROL_MASK | GDK_Return)) { + cmd_set_attr(NULL, NULL); + return true; + } + return false; +} + + +static void sp_xmltree_desktop_change(Inkscape::Application *inkscape, + SPDesktop *desktop, + GtkWidget *dialog ) +{ + if (!desktop) { + return; + } + set_tree_desktop(desktop); +} + + +void set_tree_desktop(SPDesktop *desktop) +{ + if ( desktop == current_desktop ) { + return; + } + + if (current_desktop) { + sel_changed_connection.disconnect(); + document_replaced_connection.disconnect(); + } + current_desktop = desktop; + if (desktop) { + sel_changed_connection = SP_DT_SELECTION(desktop)->connectChanged(&on_desktop_selection_changed); + document_replaced_connection = desktop->connectDocumentReplaced(&on_document_replaced); + set_tree_document(SP_DT_DOCUMENT(desktop)); + } else { + set_tree_document(NULL); + } + +} // end of set_tree_desktop() + + + +void set_tree_document(SPDocument *document) +{ + if (document == current_document) { + return; + } + + if (current_document) { + document_uri_set_connection.disconnect(); + } + current_document = document; + if (current_document) { + + document_uri_set_connection = current_document->connectURISet(sigc::bind(sigc::ptr_fun(&on_document_uri_set), current_document)); + on_document_uri_set(SP_DOCUMENT_URI(current_document), current_document); + set_tree_repr(sp_document_repr_root(current_document)); + + } else { + set_tree_repr(NULL); + } +} + + + +void set_tree_repr(Inkscape::XML::Node *repr) +{ + if (repr == selected_repr) { + return; + } + + gtk_clist_freeze(GTK_CLIST(tree)); + + sp_xmlview_tree_set_repr(tree, repr); + + if (repr) { + set_tree_select(get_dt_select()); + } else { + set_tree_select(NULL); + } + + gtk_clist_thaw(GTK_CLIST(tree)); + + propagate_tree_select(selected_repr); + +} + + + +void set_tree_select(Inkscape::XML::Node *repr) +{ + if (selected_repr) { + Inkscape::GC::release(selected_repr); + } + + selected_repr = repr; + if (repr) { + GtkCTreeNode *node; + + Inkscape::GC::anchor(selected_repr); + + node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), repr); + if (node) { + GtkCTreeNode *parent; + + gtk_ctree_select(GTK_CTREE(tree), node); + + parent = GTK_CTREE_ROW(node)->parent; + while (parent) { + gtk_ctree_expand(GTK_CTREE(tree), parent); + parent = GTK_CTREE_ROW(parent)->parent; + } + + gtk_ctree_node_moveto(GTK_CTREE(tree), node, 0, 0.66, 0.0); + } + } else { + gtk_clist_unselect_all(GTK_CLIST(tree)); + } + propagate_tree_select(repr); +} + + + +void propagate_tree_select(Inkscape::XML::Node *repr) +{ + if (repr && repr->type() == Inkscape::XML::ELEMENT_NODE) { + sp_xmlview_attr_list_set_repr(attributes, repr); + } else { + sp_xmlview_attr_list_set_repr(attributes, NULL); + } + + if (repr && ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE ) ) { + sp_xmlview_content_set_repr(content, repr); + } else { + sp_xmlview_content_set_repr(content, NULL); + } +} + + +Inkscape::XML::Node *get_dt_select() +{ + if (!current_desktop) { + return NULL; + } + + return SP_DT_SELECTION(current_desktop)->singleRepr(); +} + + + +void set_dt_select(Inkscape::XML::Node *repr) +{ + if (!current_desktop) { + return; + } + + Inkscape::Selection *selection = SP_DT_SELECTION(current_desktop); + + SPObject *object; + if (repr) { + while ( ( repr->type() != Inkscape::XML::ELEMENT_NODE ) + && sp_repr_parent(repr) ) + { + repr = sp_repr_parent(repr); + } // end of while loop + + object = SP_DT_DOCUMENT(current_desktop)->getObjectByRepr(repr); + } else { + object = NULL; + } + + blocked++; + if ( object && in_dt_coordsys(*object) + && !( SP_IS_TSPAN(object) || + SP_IS_STRING(object) || + SP_IS_ROOT(object) ) ) + { + /* We cannot set selection to tspan, string, or root; failures and + * crashes will occur. */ + /* TODO: when a tspan is highlighted, set selection to its parent + * text + */ + selection->set(SP_ITEM(object)); + } + blocked--; + +} // end of set_dt_select() + + +void on_tree_select_row(GtkCTree *tree, + GtkCTreeNode *node, + gint column, + gpointer data) +{ + if (blocked) { + return; + } + + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + g_assert(repr != NULL); + + if (selected_repr == repr) { + return; + } + + if (selected_repr) { + Inkscape::GC::release(selected_repr); + selected_repr = NULL; + } + selected_repr = repr; + Inkscape::GC::anchor(selected_repr); + + propagate_tree_select(selected_repr); + + set_dt_select(selected_repr); + + tree_reset_context(); +} + +void on_tree_unselect_row(GtkCTree *tree, + GtkCTreeNode *node, + gint column, + gpointer data) +{ + if (blocked) { + return; + } + + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + propagate_tree_select(NULL); + set_dt_select(NULL); + + if (selected_repr && (selected_repr == repr)) { + Inkscape::GC::release(selected_repr); + selected_repr = NULL; + selected_attr = 0; + } +} + + + +void after_tree_move(GtkCTree *tree, + GtkCTreeNode *node, + GtkCTreeNode *new_parent, + GtkCTreeNode *new_sibling, + gpointer data) +{ + if (GTK_CTREE_ROW(node)->parent == new_parent && + GTK_CTREE_ROW(node)->sibling == new_sibling) + { + sp_document_done(current_document); + } else { + sp_document_cancel(current_document); + } +} + + +static void on_destroy(GtkObject *object, gpointer data) +{ + set_tree_desktop(NULL); + gtk_object_destroy(GTK_OBJECT(tooltips)); + tooltips = NULL; + sp_signal_disconnect_by_data(INKSCAPE, dlg); + wd.win = dlg = NULL; + wd.stop = 0; + + _message_changed_connection.disconnect(); + delete _message_context; + _message_context = NULL; + Inkscape::GC::release(_message_stack); + _message_stack = NULL; + _message_changed_connection.~connection(); + + status = NULL; +} + + + +static gboolean on_delete(GtkObject *object, GdkEvent *event, gpointer data) +{ + gtk_window_get_position((GtkWindow *) dlg, &x, &y); + gtk_window_get_size((GtkWindow *) dlg, &w, &h); + + prefs_set_int_attribute(prefs_path, "x", x); + prefs_set_int_attribute(prefs_path, "y", y); + prefs_set_int_attribute(prefs_path, "w", w); + prefs_set_int_attribute(prefs_path, "h", h); + + return FALSE; // which means, go ahead and destroy it +} + + +static void _set_status_message(Inkscape::MessageType type, const gchar *message, GtkWidget *dialog) +{ + if (status) { + gtk_label_set_markup(GTK_LABEL(status), message ? message : ""); + } +} + + +void on_tree_select_row_enable(GtkCTree *tree, + GtkCTreeNode *node, + gint column, + gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); +} + + + +void on_tree_select_row_enable_if_element(GtkCTree *tree, + GtkCTreeNode *node, + gint column, + gpointer data ) +{ + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + if (repr->type() == Inkscape::XML::ELEMENT_NODE) { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } +} + + + +void on_tree_select_row_show_if_element(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + if (repr->type() == Inkscape::XML::ELEMENT_NODE) { + gtk_widget_show(GTK_WIDGET(data)); + } else { + gtk_widget_hide(GTK_WIDGET(data)); + } +} + + + +void on_tree_select_row_show_if_text(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + if ( repr->type() == Inkscape::XML::TEXT_NODE || repr->type() == Inkscape::XML::COMMENT_NODE ) { + gtk_widget_show(GTK_WIDGET(data)); + } else { + gtk_widget_hide(GTK_WIDGET(data)); + } +} + + +gboolean xml_tree_node_mutable(GtkCTreeNode *node) +{ + // top-level is immutable, obviously + if (!GTK_CTREE_ROW(node)->parent) { + return false; + } + + // if not in base level (where namedview, defs, etc go), we're mutable + if (GTK_CTREE_ROW(GTK_CTREE_ROW(node)->parent)->parent) { + return true; + } + + Inkscape::XML::Node *repr; + repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + g_assert(repr); + + // don't let "defs" or "namedview" disappear + if ( !strcmp(repr->name(),"svg:defs") || + !strcmp(repr->name(),"sodipodi:namedview") ) { + return false; + } + + // everyone else is okay, I guess. :) + return true; +} + + +void on_tree_select_row_enable_if_mutable(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), xml_tree_node_mutable(node)); +} + + + +void on_tree_unselect_row_disable(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); +} + + + +void on_tree_unselect_row_hide(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + gtk_widget_hide(GTK_WIDGET(data)); +} + + + +void on_tree_unselect_row_clear_text(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + if (GTK_IS_EDITABLE(data)) { + gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1); + } else if (GTK_IS_TEXT_VIEW(data)) { + GtkTextBuffer *tb; + tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data)); + gtk_text_buffer_set_text(tb, "", 0); + } +} + + +void on_attr_select_row(GtkCList *list, gint row, gint column, + GdkEventButton *event, gpointer data) +{ + selected_attr = sp_xmlview_attr_list_get_row_key(list, row); + gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(attr_value)); + + attr_reset_context(selected_attr); +} + + +void on_attr_unselect_row(GtkCList *list, gint row, gint column, + GdkEventButton *event, gpointer data) +{ + selected_attr = 0; + attr_reset_context(selected_attr); +} + + +void on_attr_row_changed(GtkCList *list, gint row, gpointer data) +{ + gint attr = sp_xmlview_attr_list_get_row_key(list, row); + + if (attr == selected_attr) { + /* if the attr changed, reselect the row in the list to sync + the edit box */ + + /* + // get current attr values + const gchar * name = g_quark_to_string (sp_xmlview_attr_list_get_row_key (list, row)); + const gchar * value = selected_repr->attribute(name); + + g_warning("value: '%s'",value); + + // get the edit box value + GtkTextIter start, end; + gtk_text_buffer_get_bounds ( gtk_text_view_get_buffer (attr_value), + &start, &end ); + gchar * text = gtk_text_buffer_get_text ( gtk_text_view_get_buffer (attr_value), + &start, &end, TRUE ); + g_warning("text: '%s'",text); + + // compare to edit box + if (strcmp(text,value)) { + // issue warning if they're different + _message_stack->flash(Inkscape::WARNING_MESSAGE, + _("Attribute changed in GUI while editing values!")); + } + g_free (text); + + */ + gtk_clist_unselect_row( GTK_CLIST(list), row, 0 ); + gtk_clist_select_row( GTK_CLIST(list), row, 0 ); + } +} + + +void on_attr_select_row_set_name_content(GtkCList *list, gint row, + gint column, GdkEventButton *event, + gpointer data) +{ + GtkEditable *editable = GTK_EDITABLE(data); + const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row)); + gtk_editable_delete_text(editable, 0, -1); + gint pos = 0; + gtk_editable_insert_text(editable, name, strlen(name), &pos); +} + + + +void on_attr_select_row_set_value_content(GtkCList *list, gint row, gint column, + GdkEventButton *event, + gpointer data) +{ + GtkTextBuffer *tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data)); + const gchar *name = g_quark_to_string(sp_xmlview_attr_list_get_row_key(list, row)); + const gchar *value = selected_repr->attribute(name); + if (!value) { + value = ""; + } + gtk_text_buffer_set_text(tb, value, strlen(value)); +} + + +void on_tree_select_row_enable_if_indentable(GtkCTree *tree, GtkCTreeNode *node, + gint column, gpointer data) +{ + gboolean indentable = FALSE; + + if (xml_tree_node_mutable(node)) { + Inkscape::XML::Node *repr, *prev; + repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + Inkscape::XML::Node *parent=repr->parent(); + if ( parent && repr != parent->firstChild() ) { + g_assert(parent->firstChild()); + + // skip to the child just before the current repr + for ( prev = parent->firstChild() ; + prev && prev->next() != repr ; + prev = prev->next() ); + + if (prev && prev->type() == Inkscape::XML::ELEMENT_NODE) { + indentable = TRUE; + } + } + } + + gtk_widget_set_sensitive(GTK_WIDGET(data), indentable); +} + + + +void on_tree_select_row_enable_if_not_first_child(GtkCTree *tree, + GtkCTreeNode *node, + gint column, + gpointer data) +{ + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + Inkscape::XML::Node *parent=repr->parent(); + if ( parent && repr != parent->firstChild() ) { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } +} + + + +void on_tree_select_row_enable_if_not_last_child(GtkCTree *tree, + GtkCTreeNode *node, + gint column, gpointer data) +{ + Inkscape::XML::Node *repr = sp_xmlview_tree_node_get_repr(SP_XMLVIEW_TREE(tree), node); + + Inkscape::XML::Node *parent=repr->parent(); + if ( parent && parent->parent() && repr->next() ) { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } +} + + + +void on_tree_select_row_enable_if_has_grandparent(GtkCTree *tree, + GtkCTreeNode *node, + gint column, gpointer data) +{ + GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent; + + if (parent) { + GtkCTreeNode *grandparent = GTK_CTREE_ROW(parent)->parent; + if (grandparent) { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } +} + + + +void on_attr_select_row_enable(GtkCList *list, gint row, gint column, + GdkEventButton *event, gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); +} + + + +void on_attr_unselect_row_disable(GtkCList *list, gint row, gint column, + GdkEventButton *event, gpointer data) +{ + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); +} + + + +void on_attr_unselect_row_clear_text(GtkCList *list, gint row, gint column, + GdkEventButton *event, gpointer data) +{ + if (GTK_IS_EDITABLE(data)) { + gtk_editable_delete_text(GTK_EDITABLE(data), 0, -1); + } else if (GTK_IS_TEXT_VIEW(data)) { + GtkTextBuffer *tb; + tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data)); + gtk_text_buffer_set_text(tb, "", 0); + } +} + + + +void on_editable_changed_enable_if_valid_xml_name(GtkEditable *editable, + gpointer data) +{ + gchar *text = gtk_editable_get_chars(editable, 0, -1); + + /* TODO: need to do checking a little more rigorous than this */ + + if (strlen(text)) { + gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE); + } + g_free(text); +} + + + +void on_desktop_selection_changed(Inkscape::Selection *selection) +{ + if (!blocked++) { + set_tree_select(get_dt_select()); + } + blocked--; +} + +static void on_document_replaced(SPDesktop *dt, SPDocument *doc) +{ + if (current_desktop) + sel_changed_connection.disconnect(); + + sel_changed_connection = SP_DT_SELECTION(dt)->connectChanged(&on_desktop_selection_changed); + set_tree_document(doc); +} + +void on_document_uri_set(gchar const *uri, SPDocument *document) +{ + gchar title[500]; + sp_ui_dialog_title_string(Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), title); + gchar *t = g_strdup_printf("%s: %s", SP_DOCUMENT_NAME(document), title); + gtk_window_set_title(GTK_WINDOW(dlg), t); + g_free(t); +} + + + +void on_clicked_get_editable_text(GtkWidget *widget, gpointer data) +{ + EditableDest *dest = (EditableDest *) data; + dest->text = gtk_editable_get_chars(dest->editable, 0, -1); +} + + + +void cmd_new_element_node(GtkObject *object, gpointer data) +{ + EditableDest name; + GtkWidget *window, *create, *cancel, *vbox, *entry, *bbox, *sep; + + g_assert(selected_repr != NULL); + + window = sp_window_new(NULL, TRUE); + gtk_container_set_border_width(GTK_CONTAINER(window), 4); + gtk_window_set_title(GTK_WINDOW(window), _("New element node...")); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dlg)); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL); + + vbox = gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(window), vbox); + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0); + + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, TRUE, 0); + + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 4); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); + + cancel = gtk_button_new_with_label(_("Cancel")); + gtk_signal_connect_object( GTK_OBJECT(cancel), "clicked", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(window) ); + gtk_container_add(GTK_CONTAINER(bbox), cancel); + + create = gtk_button_new_with_label(_("Create")); + gtk_widget_set_sensitive(GTK_WIDGET(create), FALSE); + gtk_signal_connect( GTK_OBJECT(entry), "changed", + G_CALLBACK(on_editable_changed_enable_if_valid_xml_name), + create ); + gtk_signal_connect( GTK_OBJECT(create), "clicked", + G_CALLBACK(on_clicked_get_editable_text), &name ); + gtk_signal_connect_object( GTK_OBJECT(create), "clicked", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(window) ); + GTK_WIDGET_SET_FLAGS( GTK_WIDGET(create), + GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT ); + gtk_container_add(GTK_CONTAINER(bbox), create); + + gtk_widget_show_all(GTK_WIDGET(window)); + gtk_window_set_default(GTK_WINDOW(window), GTK_WIDGET(create)); + gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(entry)); + + name.editable = GTK_EDITABLE(entry); + name.text = NULL; + + gtk_main(); + + g_assert(selected_repr != NULL); + + if (name.text) { + Inkscape::XML::Node *new_repr; + new_repr = sp_repr_new(name.text); + g_free(name.text); + selected_repr->appendChild(new_repr); + set_tree_select(new_repr); + set_dt_select(new_repr); + } + +} // end of cmd_new_element_node() + + + +void cmd_new_text_node(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + + Inkscape::XML::Node *text = sp_repr_new_text(""); + selected_repr->appendChild(text); + + sp_document_done(current_document); + + set_tree_select(text); + set_dt_select(text); + + gtk_window_set_focus(GTK_WINDOW(dlg), GTK_WIDGET(content)); + +} + +void cmd_duplicate_node(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + + Inkscape::XML::Node *parent = sp_repr_parent(selected_repr); + Inkscape::XML::Node *dup = selected_repr->duplicate(); + parent->addChild(dup, selected_repr); + + sp_document_done(current_document); + + GtkCTreeNode *node = sp_xmlview_tree_get_repr_node(SP_XMLVIEW_TREE(tree), dup); + + if (node) { + gtk_ctree_select(GTK_CTREE(tree), node); + } +} + + + +void cmd_delete_node(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + sp_repr_unparent(selected_repr); + + sp_document_done(current_document); +} + + + +void cmd_delete_attr(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + g_assert(selected_attr != 0); + selected_repr->setAttribute(g_quark_to_string(selected_attr), NULL); + + SPObject *updated=current_document->getObjectByRepr(selected_repr); + if (updated) { + // force immediate update of dependant attributes + updated->updateRepr(); + } + + sp_document_done(current_document); +} + + + +void cmd_set_attr(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + + gchar *name = gtk_editable_get_chars(attr_name, 0, -1); + GtkTextIter start; + GtkTextIter end; + gtk_text_buffer_get_bounds( gtk_text_view_get_buffer(attr_value), + &start, &end ); + gchar *value = gtk_text_buffer_get_text( gtk_text_view_get_buffer(attr_value), + &start, &end, TRUE ); + + if (!sp_repr_set_attr(selected_repr, name, value)) { + gchar *message = g_strdup_printf(_("Cannot set %s: Another element with value %s already exists!"), name, value); + _message_stack->flash(Inkscape::WARNING_MESSAGE, message); + g_free(message); + } + + g_free(name); + g_free(value); + + SPObject *updated = current_document->getObjectByRepr(selected_repr); + if (updated) { + // force immediate update of dependant attributes + updated->updateRepr(); + } + + sp_document_done(current_document); + + /* TODO: actually, the row won't have been created yet. why? */ + gint row = sp_xmlview_attr_list_find_row_from_key(GTK_CLIST(attributes), + g_quark_from_string(name)); + if (row != -1) { + gtk_clist_select_row(GTK_CLIST(attributes), row, 0); + } +} + + + +void cmd_raise_node(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + + Inkscape::XML::Node *parent = sp_repr_parent(selected_repr); + g_return_if_fail(parent != NULL); + g_return_if_fail(parent->firstChild() != selected_repr); + + Inkscape::XML::Node *ref = NULL; + Inkscape::XML::Node *before = parent->firstChild(); + while (before && before->next() != selected_repr) { + ref = before; + before = before->next(); + } + + parent->changeOrder(selected_repr, ref); + + sp_document_done(current_document); + + set_tree_select(selected_repr); + set_dt_select(selected_repr); +} + + + +void cmd_lower_node(GtkObject *object, gpointer data) +{ + g_assert(selected_repr != NULL); + g_return_if_fail(selected_repr->next() != NULL); + Inkscape::XML::Node *parent = sp_repr_parent(selected_repr); + + parent->changeOrder(selected_repr, selected_repr->next()); + + sp_document_done(current_document); + + set_tree_select(selected_repr); + set_dt_select(selected_repr); +} + +void cmd_indent_node(GtkObject *object, gpointer data) +{ + Inkscape::XML::Node *repr = selected_repr; + g_assert(repr != NULL); + Inkscape::XML::Node *parent = sp_repr_parent(repr); + g_return_if_fail(parent != NULL); + g_return_if_fail(parent->firstChild() != repr); + + Inkscape::XML::Node* prev = parent->firstChild(); + while (prev && prev->next() != repr) { + prev = prev->next(); + } + g_return_if_fail(prev != NULL); + g_return_if_fail(prev->type() == Inkscape::XML::ELEMENT_NODE); + + Inkscape::XML::Node* ref = NULL; + if (prev->firstChild()) { + for( ref = prev->firstChild() ; ref->next() ; ref = ref->next() ); + } + + parent->removeChild(repr); + prev->addChild(repr, ref); + + sp_document_done(current_document); + set_tree_select(repr); + set_dt_select(repr); + +} // end of cmd_indent_node() + + + +void cmd_unindent_node(GtkObject *object, gpointer data) +{ + Inkscape::XML::Node *repr = selected_repr; + g_assert(repr != NULL); + Inkscape::XML::Node *parent = sp_repr_parent(repr); + g_return_if_fail(parent); + Inkscape::XML::Node *grandparent = sp_repr_parent(parent); + g_return_if_fail(grandparent); + + parent->removeChild(repr); + grandparent->addChild(repr, parent); + + sp_document_done(current_document); + set_tree_select(repr); + set_dt_select(repr); + +} // end of cmd_unindent_node() + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/dialogs/xml-tree.h b/src/dialogs/xml-tree.h new file mode 100644 index 000000000..6d0d44a7d --- /dev/null +++ b/src/dialogs/xml-tree.h @@ -0,0 +1,29 @@ +#ifndef SP_XML_TREE_H +#define SP_XML_TREE_H + +/** + * \brief XML tree editing dialog for Inkscape + * + * Copyright Lauris Kaplinski, 2000 + * + * Released under GNU General Public License. + * + * This is XML tree editor, which allows direct modifying of all elements + * of Inkscape document, including foreign ones. + * + */ + +void sp_xml_tree_dialog (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -- cgit v1.2.3