X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fui%2Fdialog%2Ffilter-effects-dialog.cpp;h=edcb1e9ca653ad501c4c0dc4f76ad75a9a921e9a;hb=9fc7d3473853276f4852719545d6d52e45cd9d65;hp=9a0efc536d1b0684ebea8a1e13c470bc9a2afdc8;hpb=d297097bc1b9d4e074089ea5d649c2a0d270ff42;p=inkscape.git diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 9a0efc536..edcb1e9ca 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include @@ -31,13 +33,23 @@ #include "filter-chemistry.h" #include "filter-effects-dialog.h" #include "inkscape.h" +#include "selection.h" #include "sp-feblend.h" +#include "sp-fecolormatrix.h" #include "sp-fecomposite.h" +#include "sp-feconvolvematrix.h" #include "sp-fedisplacementmap.h" +#include "sp-fedistantlight.h" #include "sp-femerge.h" +#include "sp-femergenode.h" +#include "sp-feoffset.h" +#include "sp-fepointlight.h" +#include "sp-fespotlight.h" #include "sp-filter-primitive.h" #include "sp-gaussian-blur.h" -#include "sp-feoffset.h" + +#include "style.h" +#include "svg/svg-color.h" #include "verbs.h" #include "xml/node.h" #include "xml/repr.h" @@ -51,16 +63,222 @@ namespace Inkscape { namespace UI { namespace Dialog { -/* Displays/Edits the kernel matrix for feConvolveMatrix */ -class FilterEffectsDialog::ConvolveMatrix : public Gtk::TreeView, public AttrWidget +// Returns the number of inputs available for the filter primitive type +int input_count(const SPFilterPrimitive* prim) +{ + if(!prim) + return 0; + else if(SP_IS_FEBLEND(prim) || SP_IS_FECOMPOSITE(prim) || SP_IS_FEDISPLACEMENTMAP(prim)) + return 2; + else if(SP_IS_FEMERGE(prim)) { + // Return the number of feMergeNode connections plus an extra + int count = 1; + for(const SPObject* o = prim->firstChild(); o; o = o->next, ++count); + return count; + } + else + return 1; +} + +class CheckButtonAttr : public Gtk::CheckButton, public AttrWidget +{ +public: + CheckButtonAttr(const Glib::ustring& label, + const Glib::ustring& tv, const Glib::ustring& fv, + const SPAttributeEnum a) + : Gtk::CheckButton(label), + AttrWidget(a), + _true_val(tv), _false_val(fv) + { + signal_toggled().connect(signal_attr_changed().make_slot()); + } + + Glib::ustring get_as_attribute() const + { + return get_active() ? _true_val : _false_val; + } + + void set_from_attribute(SPObject* o) + { + const gchar* val = attribute_value(o); + if(val) { + if(_true_val == val) + set_active(true); + else if(_false_val == val) + set_active(false); + } + } +private: + const Glib::ustring _true_val, _false_val; +}; + +class SpinButtonAttr : public Gtk::SpinButton, public AttrWidget +{ +public: + SpinButtonAttr(double lower, double upper, double step_inc, + double climb_rate, int digits, const SPAttributeEnum a) + : Gtk::SpinButton(climb_rate, digits), + AttrWidget(a) + { + set_range(lower, upper); + set_increments(step_inc, step_inc * 5); + + signal_value_changed().connect(signal_attr_changed().make_slot()); + } + + Glib::ustring get_as_attribute() const + { + const double val = get_value(); + + if(get_digits() == 0) + return Glib::Ascii::dtostr((int)val); + else + return Glib::Ascii::dtostr(val); + } + + void set_from_attribute(SPObject* o) + { + const gchar* val = attribute_value(o); + if(val) + set_value(Glib::Ascii::strtod(val)); + } +}; + +// Contains an arbitrary number of spin buttons that use seperate attributes +class MultiSpinButton : public Gtk::HBox { public: - ConvolveMatrix(const SPAttributeEnum a) + MultiSpinButton(double lower, double upper, double step_inc, + double climb_rate, int digits, std::vector attrs) + { + for(unsigned i = 0; i < attrs.size(); ++i) { + _spins.push_back(new SpinButtonAttr(lower, upper, step_inc, climb_rate, digits, attrs[i])); + pack_start(*_spins.back(), false, false); + } + } + + ~MultiSpinButton() + { + for(unsigned i = 0; i < _spins.size(); ++i) + delete _spins[i]; + } + + std::vector& get_spinbuttons() + { + return _spins; + } +private: + std::vector _spins; +}; + +// Contains two spinbuttons that describe a NumberOptNumber +class DualSpinButton : public Gtk::HBox, public AttrWidget +{ +public: + DualSpinButton(double lower, double upper, double step_inc, + double climb_rate, int digits, const SPAttributeEnum a) + : AttrWidget(a), + _s1(climb_rate, digits), _s2(climb_rate, digits) + { + _s1.set_range(lower, upper); + _s2.set_range(lower, upper); + _s1.set_increments(step_inc, step_inc * 5); + _s2.set_increments(step_inc, step_inc * 5); + + _s1.signal_value_changed().connect(signal_attr_changed().make_slot()); + _s2.signal_value_changed().connect(signal_attr_changed().make_slot()); + + pack_start(_s1, false, false); + pack_start(_s2, false, false); + } + + Gtk::SpinButton& get_spinbutton1() + { + return _s1; + } + + Gtk::SpinButton& get_spinbutton2() + { + return _s2; + } + + virtual Glib::ustring get_as_attribute() const + { + double v1 = _s1.get_value(); + double v2 = _s2.get_value(); + + if(_s1.get_digits() == 0) { + v1 = (int)v1; + v2 = (int)v2; + } + + return Glib::Ascii::dtostr(v1) + " " + Glib::Ascii::dtostr(v2); + } + + virtual void set_from_attribute(SPObject* o) + { + const gchar* val = attribute_value(o); + if(val) { + NumberOptNumber n; + n.set(val); + _s1.set_value(n.getNumber()); + _s2.set_value(n.getOptNumber()); + } + } +private: + Gtk::SpinButton _s1, _s2; +}; + +class ColorButton : public Gtk::ColorButton, public AttrWidget +{ +public: + ColorButton(const SPAttributeEnum a) + : AttrWidget(a) + { + signal_color_set().connect(signal_attr_changed().make_slot()); + + Gdk::Color col; + col.set_rgb(65535, 65535, 65535); + set_color(col); + } + + // Returns the color in 'rgb(r,g,b)' form. + Glib::ustring get_as_attribute() const + { + std::ostringstream os; + const Gdk::Color c = get_color(); + const int r = (c.get_red() + 1) / 256 - 1, g = (c.get_green() + 1) / 256 - 1, b = (c.get_blue() + 1) / 256 - 1; + os << "rgb(" << r << "," << g << "," << b << ")"; + return os.str(); + } + + + void set_from_attribute(SPObject* o) + { + const gchar* val = attribute_value(o); + if(val) { + const guint32 i = sp_svg_read_color(val, 0xFFFFFFFF); + const int r = SP_RGBA32_R_U(i) + 1, g = SP_RGBA32_G_U(i) + 1, b = SP_RGBA32_B_U(i) + 1; + Gdk::Color col; + col.set_rgb(r * 256 - 1, g * 256 - 1, b * 256 - 1); + set_color(col); + } + } +}; + +/* Displays/Edits the matrix for feConvolveMatrix or feColorMatrix */ +class FilterEffectsDialog::MatrixAttr : public Gtk::Frame, public AttrWidget +{ +public: + MatrixAttr(const SPAttributeEnum a) : AttrWidget(a) { _model = Gtk::ListStore::create(_columns); - set_model(_model); - set_headers_visible(false); + _tree.set_model(_model); + _tree.set_headers_visible(false); + _tree.show(); + add(_tree); + set_shadow_type(Gtk::SHADOW_IN); } Glib::ustring get_as_attribute() const @@ -69,7 +287,7 @@ public: for(Gtk::TreeIter iter = _model->children().begin(); iter != _model->children().end(); ++iter) { - for(unsigned c = 0; c < get_columns().size(); ++c) { + for(unsigned c = 0; c < _tree.get_columns().size(); ++c) { os << (*iter)[_columns.cols[c]] << " "; } } @@ -79,23 +297,25 @@ public: void set_from_attribute(SPObject* o) { - update(SP_FECONVOLVEMATRIX(o)); - } - - sigc::signal& signal_changed() - { - return _signal_changed; - } - - void update_direct(FilterEffectsDialog* d) - { - update(SP_FECONVOLVEMATRIX(d->_primitive_list.get_selected())); + if(o) { + if(SP_IS_FECONVOLVEMATRIX(o)) { + SPFeConvolveMatrix* conv = SP_FECONVOLVEMATRIX(o); + int cols, rows; + cols = (int)conv->order.getNumber(); + if(cols > 5) + cols = 5; + rows = conv->order.optNumber_set ? (int)conv->order.getOptNumber() : cols; + update(o, rows, cols); + } + else if(SP_IS_FECOLORMATRIX(o)) + update(o, 4, 5); + } } private: - class ConvolveMatrixColumns : public Gtk::TreeModel::ColumnRecord + class MatrixColumns : public Gtk::TreeModel::ColumnRecord { public: - ConvolveMatrixColumns() + MatrixColumns() { cols.resize(5); for(unsigned i = 0; i < cols.size(); ++i) @@ -104,167 +324,407 @@ private: std::vector > cols; }; - void update(SPFeConvolveMatrix* conv) - { - if(conv) { - int cols, rows; - - cols = (int)conv->order.getNumber(); - if(cols > 5) - cols = 5; - rows = conv->order.optNumber_set ? (int)conv->order.getOptNumber() : cols; - - update(conv, cols, rows); - } - } - - void update(SPFeConvolveMatrix* conv, const int rows, const int cols) + void update(SPObject* o, const int rows, const int cols) { _model->clear(); - remove_all_columns(); + _tree.remove_all_columns(); - if(conv) { + SPFeColorMatrix* col = 0; + SPFeConvolveMatrix* conv = 0; + if(SP_IS_FECOLORMATRIX(o)) + col = SP_FECOLORMATRIX(o); + else if(SP_IS_FECONVOLVEMATRIX(o)) + conv = SP_FECONVOLVEMATRIX(o); + else + return; + + if(o) { int ndx = 0; for(int i = 0; i < cols; ++i) { - append_column_numeric_editable("", _columns.cols[i], "%.2f"); - dynamic_cast(get_column(i)->get_first_cell_renderer())->signal_edited().connect( - sigc::mem_fun(*this, &ConvolveMatrix::rebind)); + _tree.append_column_numeric_editable("", _columns.cols[i], "%.2f"); + dynamic_cast(_tree.get_column(i)->get_first_cell_renderer())->signal_edited().connect( + sigc::mem_fun(*this, &MatrixAttr::rebind)); } for(int r = 0; r < rows; ++r) { Gtk::TreeRow row = *(_model->append()); - for(int c = 0; c < cols; ++c, ++ndx) - row[_columns.cols[c]] = ndx < (int)conv->kernelMatrix.size() ? conv->kernelMatrix[ndx] : 0; + for(int c = 0; c < cols; ++c, ++ndx) { + if(col) + row[_columns.cols[c]] = ndx < (int)col->values.size() ? col->values[ndx] : 0; + else + row[_columns.cols[c]] = ndx < (int)conv->kernelMatrix.size() ? conv->kernelMatrix[ndx] : 0; + } } } } void rebind(const Glib::ustring&, const Glib::ustring&) { - _signal_changed(); + signal_attr_changed()(); } + Gtk::TreeView _tree; Glib::RefPtr _model; - ConvolveMatrixColumns _columns; - sigc::signal _signal_changed; + MatrixColumns _columns; +}; + +// Displays a matrix or a slider for feColorMatrix +class FilterEffectsDialog::ColorMatrixValues : public Gtk::Frame, public AttrWidget +{ +public: + ColorMatrixValues() + : AttrWidget(SP_ATTR_VALUES), + _matrix(SP_ATTR_VALUES), + _saturation(0, 0, 1, 0.1, 0.01, 2, SP_ATTR_VALUES), + _angle(0, 0, 360, 0.1, 0.01, 1, SP_ATTR_VALUES), + _label(_("None"), Gtk::ALIGN_LEFT) + { + _matrix.show(); + _saturation.show(); + _angle.show(); + + _label.set_sensitive(false); + _label.show(); + + set_shadow_type(Gtk::SHADOW_NONE); + } + + virtual void set_from_attribute(SPObject* o) + { + if(SP_IS_FECOLORMATRIX(o)) { + SPFeColorMatrix* col = SP_FECOLORMATRIX(o); + remove(); + switch(col->type) { + case COLORMATRIX_SATURATE: + add(_saturation); + _saturation.set_from_attribute(o); + break; + case COLORMATRIX_HUEROTATE: + add(_angle); + _angle.set_from_attribute(o); + break; + case COLORMATRIX_LUMINANCETOALPHA: + add(_label); + break; + case COLORMATRIX_MATRIX: + default: + add(_matrix); + _matrix.set_from_attribute(o); + break; + } + } + } + + virtual Glib::ustring get_as_attribute() const + { + const Widget* w = get_child(); + if(w == &_label) + return ""; + else + return dynamic_cast(w)->get_as_attribute(); + } +private: + MatrixAttr _matrix; + SpinSlider _saturation; + SpinSlider _angle; + Gtk::Label _label; }; class FilterEffectsDialog::Settings { public: - Settings(FilterEffectsDialog& d) - : _dialog(d) + typedef sigc::slot SetAttrSlot; + + Settings(FilterEffectsDialog& d, SetAttrSlot slot, const int maxtypes) + : _dialog(d), _set_attr_slot(slot), _max_types(maxtypes) { - _sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); - _sizegroup->set_ignore_hidden(); + _groups.resize(_max_types); + _attrwidgets.resize(_max_types); - for(int i = 0; i < NR_FILTER_ENDPRIMITIVETYPE; ++i) { - _dialog._settings_box.add(_groups[i]); + for(int i = 0; i < _max_types; ++i) { + _groups[i] = new Gtk::VBox; + d._settings_box.add(*_groups[i]); + } + } + + ~Settings() + { + for(int i = 0; i < _max_types; ++i) { + delete _groups[i]; + for(unsigned j = 0; j < _attrwidgets[i].size(); ++j) + delete _attrwidgets[i][j]; } } // Show the active settings group and update all the AttrWidgets with new values - void show_and_update(const NR::FilterPrimitiveType t) + void show_and_update(const int t, SPObject* ob) { type(t); - _groups[t].show_all(); - - SPObject* ob = _dialog._primitive_list.get_selected(); + for(unsigned i = 0; i < _groups.size(); ++i) + _groups[i]->hide(); + _groups[t]->show_all(); + _dialog.set_attrs_locked(true); for(unsigned i = 0; i < _attrwidgets[_current_type].size(); ++i) _attrwidgets[_current_type][i]->set_from_attribute(ob); + _dialog.set_attrs_locked(false); } - void type(const NR::FilterPrimitiveType t) + void type(const int t) { _current_type = t; } - void add(Gtk::ColorButton& cb, const SPAttributeEnum attr, const Glib::ustring& label) + // LightSource + LightSourceControl* add_lightsource(const Glib::ustring& label); + + // CheckBox + CheckButtonAttr* add_checkbutton(const SPAttributeEnum attr, const Glib::ustring& label, + const Glib::ustring& tv, const Glib::ustring& fv) + { + CheckButtonAttr* cb = new CheckButtonAttr(label, tv, fv, attr); + add_widget(cb, ""); + add_attr_widget(cb); + return cb; + } + + // ColorButton + ColorButton* add_color(const SPAttributeEnum attr, const Glib::ustring& label) { - //generic_add(cb, label); - //cb.signal_color_set().connect( - // sigc::bind(sigc::mem_fun(_dialog, &FilterEffectsDialog::set_attr_color), attr, &cb)); + ColorButton* col = new ColorButton(attr); + add_widget(col, label); + add_attr_widget(col); + return col; } - // ConvolveMatrix - ConvolveMatrix* add(const SPAttributeEnum attr, const Glib::ustring& label) + // Matrix + MatrixAttr* add_matrix(const SPAttributeEnum attr, const Glib::ustring& label) { - ConvolveMatrix* conv = new ConvolveMatrix(attr); - add_widget(*conv, label); - _attrwidgets[_current_type].push_back(conv); - conv->signal_changed().connect( - sigc::bind(sigc::mem_fun(_dialog, &FilterEffectsDialog::set_attr_direct), attr, conv)); + MatrixAttr* conv = new MatrixAttr(attr); + add_widget(conv, label); + add_attr_widget(conv); return conv; } + // ColorMatrixValues + ColorMatrixValues* add_colormatrixvalues(const Glib::ustring& label) + { + ColorMatrixValues* cmv = new ColorMatrixValues; + add_widget(cmv, label); + add_attr_widget(cmv); + return cmv; + } + // SpinSlider - SpinSlider* add(const SPAttributeEnum attr, const Glib::ustring& label, - const double lo, const double hi, const double step_inc, const double climb, const int digits) + SpinSlider* add_spinslider(const SPAttributeEnum attr, const Glib::ustring& label, + const double lo, const double hi, const double step_inc, const double climb, const int digits) { SpinSlider* spinslider = new SpinSlider(lo, lo, hi, step_inc, climb, digits, attr); - add_widget(*spinslider, label); - _attrwidgets[_current_type].push_back(spinslider); - spinslider->signal_value_changed().connect( - sigc::bind(sigc::mem_fun(_dialog, &FilterEffectsDialog::set_attr_direct), attr, spinslider)); + add_widget(spinslider, label); + add_attr_widget(spinslider); return spinslider; } // DualSpinSlider - DualSpinSlider* add(const SPAttributeEnum attr, const Glib::ustring& label1, const Glib::ustring& label2, - const double lo, const double hi, const double step_inc, const double climb, const int digits) + DualSpinSlider* add_dualspinslider(const SPAttributeEnum attr, const Glib::ustring& label, + const double lo, const double hi, const double step_inc, + const double climb, const int digits) { DualSpinSlider* dss = new DualSpinSlider(lo, lo, hi, step_inc, climb, digits, attr); - add_widget(dss->get_spinslider1(), label1); - add_widget(dss->get_spinslider2(), label2); - _attrwidgets[_current_type].push_back(dss); - dss->signal_value_changed().connect( - sigc::bind(sigc::mem_fun(_dialog, &FilterEffectsDialog::set_attr_direct), attr, dss)); + add_widget(dss, label); + add_attr_widget(dss); return dss; } + // DualSpinButton + DualSpinButton* add_dualspinbutton(const SPAttributeEnum attr, const Glib::ustring& label, + const double lo, const double hi, const double step_inc, + const double climb, const int digits) + { + DualSpinButton* dsb = new DualSpinButton(lo, hi, step_inc, climb, digits, attr); + add_widget(dsb, label); + add_attr_widget(dsb); + return dsb; + } + + // MultiSpinButton + MultiSpinButton* add_multispinbutton(const SPAttributeEnum attr1, const SPAttributeEnum attr2, + const Glib::ustring& label, const double lo, const double hi, + const double step_inc, const double climb, const int digits) + { + std::vector attrs; + attrs.push_back(attr1); + attrs.push_back(attr2); + MultiSpinButton* msb = new MultiSpinButton(lo, hi, step_inc, climb, digits, attrs); + add_widget(msb, label); + for(unsigned i = 0; i < msb->get_spinbuttons().size(); ++i) + add_attr_widget(msb->get_spinbuttons()[i]); + return msb; + } + MultiSpinButton* add_multispinbutton(const SPAttributeEnum attr1, const SPAttributeEnum attr2, + const SPAttributeEnum attr3, const Glib::ustring& label, const double lo, + const double hi, const double step_inc, const double climb, const int digits) + { + std::vector attrs; + attrs.push_back(attr1); + attrs.push_back(attr2); + attrs.push_back(attr3); + MultiSpinButton* msb = new MultiSpinButton(lo, hi, step_inc, climb, digits, attrs); + add_widget(msb, label); + for(unsigned i = 0; i < msb->get_spinbuttons().size(); ++i) + add_attr_widget(msb->get_spinbuttons()[i]); + return msb; + } + // ComboBoxEnum - template ComboBoxEnum* add(const SPAttributeEnum attr, + template ComboBoxEnum* add_combo(const SPAttributeEnum attr, const Glib::ustring& label, const Util::EnumDataConverter& conv) { ComboBoxEnum* combo = new ComboBoxEnum(conv, attr); - add_widget(*combo, label); - _attrwidgets[_current_type].push_back(combo); - combo->signal_changed().connect( - sigc::bind(sigc::mem_fun(_dialog, &FilterEffectsDialog::set_attr_direct), attr, combo)); + add_widget(combo, label); + add_attr_widget(combo); return combo; } private: + void add_attr_widget(AttrWidget* a) + { + _attrwidgets[_current_type].push_back(a); + a->signal_attr_changed().connect(sigc::bind(_set_attr_slot, a)); + } + /* Adds a new settings widget using the specified label. The label will be formatted with a colon and all widgets within the setting group are aligned automatically. */ - void add_widget(Gtk::Widget& w, const Glib::ustring& label) + void add_widget(Gtk::Widget* w, const Glib::ustring& label) { Gtk::Label *lbl = Gtk::manage(new Gtk::Label(label + (label == "" ? "" : ":"), Gtk::ALIGN_LEFT)); Gtk::HBox *hb = Gtk::manage(new Gtk::HBox); hb->set_spacing(12); hb->pack_start(*lbl, false, false); - hb->pack_start(w); - _groups[_current_type].pack_start(*hb); + hb->pack_start(*w); + _groups[_current_type]->pack_start(*hb); - _sizegroup->add_widget(*lbl); + _dialog._sizegroup->add_widget(*lbl); hb->show(); lbl->show(); - w.show(); + w->show(); + } + + std::vector _groups; + + FilterEffectsDialog& _dialog; + SetAttrSlot _set_attr_slot; + std::vector > _attrwidgets; + int _current_type, _max_types; +}; + +// Settings for the three light source objects +class FilterEffectsDialog::LightSourceControl : public AttrWidget +{ +public: + LightSourceControl(FilterEffectsDialog& d) + : AttrWidget(SP_ATTR_INVALID), + _dialog(d), + _settings(d, sigc::mem_fun(_dialog, &FilterEffectsDialog::set_child_attr_direct), LIGHT_ENDSOURCE), + _light_source(LightSourceConverter) + { + _box.add(_light_source); + _box.reorder_child(_light_source, 0); + _light_source.signal_changed().connect(sigc::mem_fun(*this, &LightSourceControl::on_source_changed)); + + // FIXME: these range values are complete crap + + _settings.type(LIGHT_DISTANT); + _settings.add_spinslider(SP_ATTR_AZIMUTH, _("Azimuth"), 0, 360, 1, 1, 0); + _settings.add_spinslider(SP_ATTR_AZIMUTH, _("Elevation"), 0, 360, 1, 1, 0); + + _settings.type(LIGHT_POINT); + _settings.add_multispinbutton(SP_ATTR_X, SP_ATTR_Y, SP_ATTR_Z, _("Location"), -99999, 99999, 1, 100, 0); + + _settings.type(LIGHT_SPOT); + _settings.add_multispinbutton(SP_ATTR_X, SP_ATTR_Y, SP_ATTR_Z, _("Location"), -99999, 99999, 1, 100, 0); + _settings.add_multispinbutton(SP_ATTR_POINTSATX, SP_ATTR_POINTSATY, SP_ATTR_POINTSATZ, + _("Points At"), -99999, 99999, 1, 100, 0); + _settings.add_spinslider(SP_ATTR_SPECULAREXPONENT, _("Specular Exponent"), 1, 100, 1, 1, 0); + _settings.add_spinslider(SP_ATTR_LIMITINGCONEANGLE, _("Cone Angle"), 1, 100, 1, 1, 0); + } + + Gtk::VBox& get_box() + { + return _box; + } +protected: + Glib::ustring get_as_attribute() const + { + return ""; + } + void set_from_attribute(SPObject* o) + { + SPObject* child = o->children; + + if(SP_IS_FEDISTANTLIGHT(child)) + _light_source.set_active(0); + else if(SP_IS_FEPOINTLIGHT(child)) + _light_source.set_active(1); + else if(SP_IS_FESPOTLIGHT(child)) + _light_source.set_active(2); + + update(); + } +private: + void on_source_changed() + { + SPFilterPrimitive* prim = _dialog._primitive_list.get_selected(); + if(prim) { + SPObject* child = prim->children; + const int ls = _light_source.get_active_row_number(); + // Check if the light source type has changed + if(!(ls == 0 && SP_IS_FEDISTANTLIGHT(child)) && + !(ls == 1 && SP_IS_FEPOINTLIGHT(child)) && + !(ls == 2 && SP_IS_FESPOTLIGHT(child))) { + if(child) + sp_repr_unparent(child->repr); + + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(prim->document); + Inkscape::XML::Node *repr = xml_doc->createElement(_light_source.get_active_data()->key.c_str()); + repr->setAttribute("inkscape:collect", "always"); + prim->repr->appendChild(repr); + Inkscape::GC::release(repr); + sp_document_done(prim->document, SP_VERB_DIALOG_FILTER_EFFECTS, _("New light source")); + update(); + } + } } - Gtk::VBox _groups[NR::NR_FILTER_ENDPRIMITIVETYPE]; - Glib::RefPtr _sizegroup; + void update() + { + _box.hide_all(); + _box.show(); + _light_source.show_all(); + + SPFilterPrimitive* prim = _dialog._primitive_list.get_selected(); + if(prim && prim->children) + _settings.show_and_update(_light_source.get_active_data()->id, prim->children); + } FilterEffectsDialog& _dialog; - std::vector _attrwidgets[NR::NR_FILTER_ENDPRIMITIVETYPE]; - NR::FilterPrimitiveType _current_type; + Gtk::VBox _box; + Settings _settings; + ComboBoxEnum _light_source; }; +FilterEffectsDialog::LightSourceControl* FilterEffectsDialog::Settings::add_lightsource(const Glib::ustring& label) +{ + LightSourceControl* ls = new LightSourceControl(_dialog); + add_attr_widget(ls); + add_widget(&ls->get_box(), label); + return ls; +} + Glib::RefPtr create_popup_menu(Gtk::Widget& parent, sigc::slot dup, sigc::slot rem) { @@ -280,20 +740,9 @@ Glib::RefPtr create_popup_menu(Gtk::Widget& parent, sigc::slot return menu; } -static void try_id_change(SPObject* ob, const Glib::ustring& text) -{ - // FIXME: this needs more serious error checking... - if(ob && !SP_ACTIVE_DOCUMENT->getObjectById(text.c_str())) { - SPException ex; - SP_EXCEPTION_INIT(&ex); - sp_object_setAttribute(ob, "id", text.c_str(), &ex); - sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILTER_EFFECTS, _("Set object ID")); - } -} - /*** FilterModifier ***/ -FilterEffectsDialog::FilterModifier::FilterModifier() - : _add(Gtk::Stock::ADD) +FilterEffectsDialog::FilterModifier::FilterModifier(FilterEffectsDialog& d) + : _dialog(d), _add(Gtk::Stock::ADD) { Gtk::ScrolledWindow* sw = Gtk::manage(new Gtk::ScrolledWindow); pack_start(*sw); @@ -301,22 +750,112 @@ FilterEffectsDialog::FilterModifier::FilterModifier() sw->add(_list); _list.set_model(_model); - _list.append_column_editable(_("_Filter"), _columns.id); - ((Gtk::CellRendererText*)_list.get_column(0)->get_first_cell_renderer())-> - signal_edited().connect(sigc::mem_fun(*this, &FilterEffectsDialog::FilterModifier::filter_name_edited)); + const int selcol = _list.append_column("", _cell_sel); + Gtk::TreeViewColumn* col = _list.get_column(selcol - 1); + if(col) + col->add_attribute(_cell_sel.property_sel(), _columns.sel); + _list.append_column(_("_Filter"), _columns.label); sw->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); sw->set_shadow_type(Gtk::SHADOW_IN); show_all_children(); _add.signal_clicked().connect(sigc::mem_fun(*this, &FilterModifier::add_filter)); + _list.signal_button_press_event().connect_notify( + sigc::mem_fun(*this, &FilterModifier::filter_list_button_press)); _list.signal_button_release_event().connect_notify( sigc::mem_fun(*this, &FilterModifier::filter_list_button_release)); _menu = create_popup_menu(*this, sigc::mem_fun(*this, &FilterModifier::duplicate_filter), sigc::mem_fun(*this, &FilterModifier::remove_filter)); + _menu->items().push_back(Gtk::Menu_Helpers::MenuElem( + _("R_ename"), sigc::mem_fun(*this, &FilterModifier::rename_filter))); + _menu->accelerate(*this); + + g_signal_connect(G_OBJECT(INKSCAPE), "change_selection", + G_CALLBACK(&FilterModifier::on_inkscape_change_selection), this); update_filters(); } +FilterEffectsDialog::FilterModifier::CellRendererSel::CellRendererSel() + : Glib::ObjectBase(typeid(CellRendererSel)), + _size(10), + _sel(*this, "sel", 0) +{} + +Glib::PropertyProxy FilterEffectsDialog::FilterModifier::CellRendererSel::property_sel() +{ + return _sel.get_proxy(); +} + +void FilterEffectsDialog::FilterModifier::CellRendererSel::get_size_vfunc( + Gtk::Widget&, const Gdk::Rectangle*, int* x, int* y, int* w, int* h) const +{ + if(x) + (*x) = 0; + if(y) + (*y) = 0; + if(w) + (*w) = _size; + if(h) + (*h) = _size; +} + +void FilterEffectsDialog::FilterModifier::CellRendererSel::render_vfunc( + const Glib::RefPtr& win, Gtk::Widget& widget, const Gdk::Rectangle& bg_area, + const Gdk::Rectangle& cell_area, const Gdk::Rectangle& expose_area, Gtk::CellRendererState flags) +{ + const int sel = _sel.get_value(); + + if(sel > 0) { + const int s = _size - 2; + const int w = cell_area.get_width(); + const int h = cell_area.get_height(); + const int x = cell_area.get_x() + w / 2 - s / 2; + const int y = cell_area.get_y() + h / 2 - s / 2; + + win->draw_rectangle(widget.get_style()->get_text_gc(Gtk::STATE_NORMAL), (sel == 1), x, y, s, s); + } +} + +// When the selection changes, show the active filter(s) in the dialog +void FilterEffectsDialog::FilterModifier::on_inkscape_change_selection(Application *inkscape, + Selection *sel, + FilterModifier* fm) +{ + if(fm && sel) + fm->update_selection(sel); +} + +void FilterEffectsDialog::FilterModifier::update_selection(Selection *sel) +{ + std::set used; + + for(GSList const *i = sel->itemList(); i != NULL; i = i->next) { + SPObject *obj = SP_OBJECT (i->data); + SPStyle *style = SP_OBJECT_STYLE (obj); + if(!style || !SP_IS_ITEM(obj)) continue; + + if(style->filter.set && style->getFilter()) + used.insert(style->getFilter()); + else + used.insert(0); + } + + const int size = used.size(); + + for(Gtk::TreeIter iter = _model->children().begin(); + iter != _model->children().end(); ++iter) { + if(used.find((*iter)[_columns.filter]) != used.end()) { + // If only one filter is in use by the selection, select it + if(size == 1) + _list.get_selection()->select(iter); + (*iter)[_columns.sel] = size; + } + else + (*iter)[_columns.sel] = 0; + } +} + Glib::SignalProxy0 FilterEffectsDialog::FilterModifier::signal_selection_changed() { return _list.get_selection()->signal_changed(); @@ -347,6 +886,31 @@ void FilterEffectsDialog::FilterModifier::select_filter(const SPFilter* filter) } } +void FilterEffectsDialog::FilterModifier::filter_list_button_press(GdkEventButton* e) +{ + // Double-click + if(e->type == GDK_2BUTTON_PRESS) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPDocument *doc = sp_desktop_document(desktop); + SPFilter* filter = get_selected_filter(); + Inkscape::Selection *sel = sp_desktop_selection(desktop); + + GSList const *items = sel->itemList(); + + for (GSList const *i = items; i != NULL; i = i->next) { + SPItem * item = SP_ITEM(i->data); + SPStyle *style = SP_OBJECT_STYLE(item); + g_assert(style != NULL); + + sp_style_set_property_url(SP_OBJECT(item), "filter", SP_OBJECT(filter), false); + SP_OBJECT(item)->requestDisplayUpdate((SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG )); + } + + update_selection(sel); + sp_document_done(doc, SP_VERB_DIALOG_FILTER_EFFECTS, _("Apply filter")); + } +} + void FilterEffectsDialog::FilterModifier::filter_list_button_release(GdkEventButton* event) { if((event->type == GDK_BUTTON_RELEASE) && (event->button == 3)) { @@ -362,6 +926,11 @@ void FilterEffectsDialog::FilterModifier::add_filter() SPDocument* doc = sp_desktop_document(SP_ACTIVE_DESKTOP); SPFilter* filter = new_filter(doc); + const int count = _model->children().size(); + std::ostringstream os; + os << "filter" << count; + filter->setLabel(os.str().c_str()); + update_filters(); select_filter(filter); @@ -398,12 +967,30 @@ void FilterEffectsDialog::FilterModifier::duplicate_filter() } } -void FilterEffectsDialog::FilterModifier::filter_name_edited(const Glib::ustring& path, const Glib::ustring& text) +void FilterEffectsDialog::FilterModifier::rename_filter() { - Gtk::TreeModel::iterator i = _model->get_iter(path); - - if(i) - try_id_change((SPObject*)(*i)[_columns.filter], text); + SPFilter* filter = get_selected_filter(); + Gtk::Dialog m("", _dialog, true); + m.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + m.add_button(_("_Rename"), Gtk::RESPONSE_OK); + m.set_default_response(Gtk::RESPONSE_OK); + Gtk::Label lbl(_("Filter name:")); + Gtk::Entry entry; + entry.set_text(filter->label() ? filter->label() : ""); + Gtk::HBox hb; + hb.add(lbl); + hb.add(entry); + hb.set_spacing(12); + hb.show_all(); + m.get_vbox()->add(hb); + const int res = m.run(); + if(res == Gtk::RESPONSE_OK) { + filter->setLabel(entry.get_text().c_str()); + sp_document_done(filter->document, SP_VERB_DIALOG_FILTER_EFFECTS, _("Rename filter")); + Gtk::TreeIter iter = _list.get_selection()->get_selected(); + if(iter) + (*iter)[_columns.label] = entry.get_text(); + } } FilterEffectsDialog::CellRendererConnection::CellRendererConnection() @@ -416,22 +1003,6 @@ Glib::PropertyProxy FilterEffectsDialog::CellRendererConnection::property return _primitive.get_proxy(); } -int FilterEffectsDialog::CellRendererConnection::input_count(const SPFilterPrimitive* prim) -{ - if(!prim) - return 0; - else if(SP_IS_FEBLEND(prim) || SP_IS_FECOMPOSITE(prim) || SP_IS_FEDISPLACEMENTMAP(prim)) - return 2; - else if(SP_IS_FEMERGE(prim)) { - // Return the number of feMergeNode connections plus an extra one for adding a new input - int count = 1; - for(const SPObject* o = prim->firstChild(); o; o = o->next, ++count); - return count; - } - else - return 1; -} - void FilterEffectsDialog::CellRendererConnection::set_text_width(const int w) { _text_width = w; @@ -599,11 +1170,12 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_signal(GdkEventExpose* e) int vis_x, vis_y; tree_to_widget_coords(vis.get_x(), vis.get_y(), vis_x, vis_y); - text_start_x = rct.get_x() + row_count * fheight; + text_start_x = rct.get_x() + rct.get_width() - _connection_cell.get_text_width() * (FPInputConverter.end + 1) + 1; for(int i = 0; i < FPInputConverter.end; ++i) { _vertical_layout->set_text(FPInputConverter.get_label((FilterPrimitiveInput)i)); const int x = text_start_x + _connection_cell.get_text_width() * (i + 1); - get_bin_window()->draw_layout(get_style()->get_text_gc(Gtk::STATE_NORMAL), x, vis_y, _vertical_layout); + get_bin_window()->draw_rectangle(get_style()->get_bg_gc(Gtk::STATE_NORMAL), true, x, vis_y, _connection_cell.get_text_width(), vis.get_height()); + get_bin_window()->draw_layout(get_style()->get_text_gc(Gtk::STATE_NORMAL), x + 1, vis_y, _vertical_layout); get_bin_window()->draw_line(darkgc, x, vis_y, x, vis_y + vis.get_height()); } } @@ -623,13 +1195,13 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_signal(GdkEventExpose* e) get_bin_window()->draw_line(darkgc, x, y + h, outline_x, y + h); // Side outline - get_bin_window()->draw_line(darkgc, outline_x, y, outline_x, y + h); + get_bin_window()->draw_line(darkgc, outline_x, y - 1, outline_x, y + h); std::vector con_poly; int con_drag_y; bool inside; const SPFilterPrimitive* row_prim = (*row)[_columns.primitive]; - const int inputs = CellRendererConnection::input_count(row_prim); + const int inputs = input_count(row_prim); if(SP_IS_FEMERGE(row_prim)) { for(int i = 0; i < inputs; ++i) { @@ -638,7 +1210,11 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_signal(GdkEventExpose* e) darkgc : get_style()->get_dark_gc(Gtk::STATE_ACTIVE), inside, con_poly); - // TODO: draw connections for each of the feMergeNodes + if(_in_drag == (i + 1)) + con_drag_y = con_poly[2].get_y(); + + if(_in_drag != (i + 1) || row_prim != prim) + draw_connection(row, i, text_start_x, outline_x, con_poly[2].get_y(), row_count); } } else { @@ -648,6 +1224,7 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_signal(GdkEventExpose* e) get_bin_window()->draw_polygon(inside && mask & GDK_BUTTON1_MASK ? darkgc : get_style()->get_dark_gc(Gtk::STATE_ACTIVE), inside, con_poly); + // Draw "in" connection if(_in_drag != 1 || row_prim != prim) draw_connection(row, SP_ATTR_IN, text_start_x, outline_x, con_poly[2].get_y(), row_count); @@ -664,32 +1241,33 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_signal(GdkEventExpose* e) if(_in_drag != 2 || row_prim != prim) draw_connection(row, SP_ATTR_IN2, text_start_x, outline_x, con_poly[2].get_y(), row_count); } + } - // Draw drag connection - if(row_prim == prim && _in_drag) { - get_bin_window()->draw_line(get_style()->get_black_gc(), outline_x, con_drag_y, - mx, con_drag_y); - get_bin_window()->draw_line(get_style()->get_black_gc(), mx, con_drag_y, mx, my); - } + // Draw drag connection + if(row_prim == prim && _in_drag) { + get_bin_window()->draw_line(get_style()->get_black_gc(), outline_x, con_drag_y, + mx, con_drag_y); + get_bin_window()->draw_line(get_style()->get_black_gc(), mx, con_drag_y, mx, my); } } return true; } -void FilterEffectsDialog::PrimitiveList::draw_connection(const Gtk::TreeIter& input, const SPAttributeEnum attr, +void FilterEffectsDialog::PrimitiveList::draw_connection(const Gtk::TreeIter& input, const int attr, const int text_start_x, const int x1, const int y1, const int row_count) { - const Gtk::TreeIter res = find_result(input, attr); + int src_id; + const Gtk::TreeIter res = find_result(input, attr, src_id); Glib::RefPtr gc = get_style()->get_black_gc(); if(res == input) { // Draw straight connection to a standard input const int tw = _connection_cell.get_text_width(); - const int src = 1 + (int)FPInputConverter.get_id_from_key( - SP_OBJECT_REPR((*res)[_columns.primitive])->attribute((const gchar*)sp_attribute_name(attr))); - get_bin_window()->draw_line(gc, x1, y1, text_start_x + tw * src + (int)(tw * 0.5f), y1); + gint end_x = text_start_x + tw * (src_id + 1) + (int)(tw * 0.5f) + 1; + get_bin_window()->draw_rectangle(gc, true, end_x-2, y1-2, 5, 5); + get_bin_window()->draw_line(gc, x1, y1, end_x, y1); } else if(res != _model->children().end()) { Gdk::Rectangle rct; @@ -714,13 +1292,13 @@ bool FilterEffectsDialog::PrimitiveList::do_connection_node(const Gtk::TreeIter& const int ix, const int iy) { Gdk::Rectangle rct; - const int input_count = CellRendererConnection::input_count((*row)[_columns.primitive]); + const int icnt = input_count((*row)[_columns.primitive]); get_cell_area(get_model()->get_path(_model->children().begin()), *get_column(1), rct); const int fheight = CellRendererConnection::size; get_cell_area(_model->get_path(row), *get_column(1), rct); - const float h = rct.get_height() / input_count; + const float h = rct.get_height() / icnt; const int x = rct.get_x() + fheight * (_model->children().size() - find_index(row)); const int con_w = (int)(fheight * 0.35f); @@ -734,26 +1312,35 @@ bool FilterEffectsDialog::PrimitiveList::do_connection_node(const Gtk::TreeIter& } const Gtk::TreeIter FilterEffectsDialog::PrimitiveList::find_result(const Gtk::TreeIter& start, - const SPAttributeEnum attr) + const int attr, int& src_id) { SPFilterPrimitive* prim = (*start)[_columns.primitive]; Gtk::TreeIter target = _model->children().end(); int image; - if(attr == SP_ATTR_IN) - image = prim->image_in; - else if(attr == SP_ATTR_IN2) { - if(SP_IS_FEBLEND(prim)) - image = SP_FEBLEND(prim)->in2; - else if(SP_IS_FECOMPOSITE(prim)) - image = SP_FECOMPOSITE(prim)->in2; - /*else if(SP_IS_FEDISPLACEMENTMAP(prim)) - image = SP_FEDISPLACEMENTMAP(prim)->in2;*/ + if(SP_IS_FEMERGE(prim)) { + int c = 0; + for(const SPObject* o = prim->firstChild(); o; o = o->next, ++c) { + if(c == attr && SP_IS_FEMERGENODE(o)) + image = SP_FEMERGENODE(o)->input; + } + } + else { + if(attr == SP_ATTR_IN) + image = prim->image_in; + else if(attr == SP_ATTR_IN2) { + if(SP_IS_FEBLEND(prim)) + image = SP_FEBLEND(prim)->in2; + else if(SP_IS_FECOMPOSITE(prim)) + image = SP_FECOMPOSITE(prim)->in2; + else if(SP_IS_FEDISPLACEMENTMAP(prim)) + image = SP_FEDISPLACEMENTMAP(prim)->in2; + else + return target; + } else return target; } - else - return target; if(image >= 0) { for(Gtk::TreeIter i = _model->children().begin(); @@ -763,8 +1350,10 @@ const Gtk::TreeIter FilterEffectsDialog::PrimitiveList::find_result(const Gtk::T } return target; } - else if(image < -1) + else if(image < -1) { + src_id = -(image + 2); return start; + } return target; } @@ -789,13 +1378,18 @@ bool FilterEffectsDialog::PrimitiveList::on_button_press_event(GdkEventButton* e if(get_path_at_pos(x, y, path, col, cx, cy)) { Gtk::TreeIter iter = _model->get_iter(path); std::vector points; - if(do_connection_node(_model->get_iter(path), 0, points, x, y)) - _in_drag = 1; - else if(do_connection_node(_model->get_iter(path), 1, points, x, y)) - _in_drag = 2; + + _drag_prim = (*iter)[_columns.primitive]; + const int icnt = input_count(_drag_prim); + + for(int i = 0; i < icnt; ++i) { + if(do_connection_node(_model->get_iter(path), i, points, x, y)) { + _in_drag = i + 1; + break; + } + } queue_draw(); - _drag_prim = (*iter)[_columns.primitive]; } if(_in_drag) { @@ -828,13 +1422,14 @@ bool FilterEffectsDialog::PrimitiveList::on_button_release_event(GdkEventButton* Gtk::TreeIter target_iter = _model->get_iter(path); target = (*target_iter)[_columns.primitive]; - const int sources_x = CellRendererConnection::size * _model->children().size() + - _connection_cell.get_text_width(); - + Gdk::Rectangle rct; + get_cell_area(path, *col, rct); + const int twidth = _connection_cell.get_text_width(); + const int sources_x = rct.get_width() - twidth * FPInputConverter.end; if(cx > sources_x) { - int src = (cx - sources_x) / _connection_cell.get_text_width(); + int src = (cx - sources_x) / twidth; if(src < 0) - src = 1; + src = 0; else if(src >= FPInputConverter.end) src = FPInputConverter.end - 1; result = FPInputConverter.get_key((FilterPrimitiveInput)src); @@ -860,10 +1455,41 @@ bool FilterEffectsDialog::PrimitiveList::on_button_release_event(GdkEventButton* } } - if(_in_drag == 1) - _dialog.set_attr(SP_ATTR_IN, in_val); - else if(_in_drag == 2) - _dialog.set_attr(SP_ATTR_IN2, in_val); + if(SP_IS_FEMERGE(prim)) { + int c = 1; + bool handled = false; + for(SPObject* o = prim->firstChild(); o && !handled; o = o->next, ++c) { + if(c == _in_drag && SP_IS_FEMERGENODE(o)) { + // If input is null, delete it + if(!in_val) { + sp_repr_unparent(o->repr); + sp_document_done(prim->document, SP_VERB_DIALOG_FILTER_EFFECTS, + _("Remove merge node")); + (*get_selection()->get_selected())[_columns.primitive] = prim; + } + else + _dialog.set_attr(o, SP_ATTR_IN, in_val); + handled = true; + } + } + // Add new input? + if(!handled && c == _in_drag) { + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(prim->document); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:feMergeNode"); + repr->setAttribute("inkscape:collect", "always"); + prim->repr->appendChild(repr); + SPFeMergeNode *node = SP_FEMERGENODE(prim->document->getObjectByRepr(repr)); + Inkscape::GC::release(repr); + _dialog.set_attr(node, SP_ATTR_IN, in_val); + (*get_selection()->get_selected())[_columns.primitive] = prim; + } + } + else { + if(_in_drag == 1) + _dialog.set_attr(prim, SP_ATTR_IN, in_val); + else if(_in_drag == 2) + _dialog.set_attr(prim, SP_ATTR_IN2, in_val); + } } _in_drag = 0; @@ -955,13 +1581,18 @@ int FilterEffectsDialog::PrimitiveList::primitive_count() const FilterEffectsDialog::FilterEffectsDialog() : Dialog ("dialogs.filtereffects", SP_VERB_DIALOG_FILTER_EFFECTS), + _filter_modifier(*this), _primitive_list(*this), _add_primitive_type(FPConverter), _add_primitive(Gtk::Stock::ADD), - _empty_settings(_("No primitive selected"), Gtk::ALIGN_LEFT) + _empty_settings(_("No primitive selected"), Gtk::ALIGN_LEFT), + _locked(false) { - _settings = new Settings(*this); - + _settings = new Settings(*this, sigc::mem_fun(*this, &FilterEffectsDialog::set_attr_direct), + NR_FILTER_ENDPRIMITIVETYPE); + _sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); + _sizegroup->set_ignore_hidden(); + // Initialize widget hierarchy Gtk::HPaned* hpaned = Gtk::manage(new Gtk::HPaned); Gtk::ScrolledWindow* sw_prims = Gtk::manage(new Gtk::ScrolledWindow); @@ -1005,6 +1636,11 @@ FilterEffectsDialog::~FilterEffectsDialog() delete _settings; } +void FilterEffectsDialog::set_attrs_locked(const bool l) +{ + _locked = l; +} + void FilterEffectsDialog::init_settings_widgets() { // TODO: Find better range/climb-rate/digits values for the SpinSliders, @@ -1014,46 +1650,60 @@ void FilterEffectsDialog::init_settings_widgets() _settings_box.pack_start(_empty_settings); _settings->type(NR_FILTER_BLEND); - _settings->add(SP_ATTR_MODE, _("Mode"), BlendModeConverter); + _settings->add_combo(SP_ATTR_MODE, _("Mode"), BlendModeConverter); + + _settings->type(NR_FILTER_COLORMATRIX); + _settings->add_combo(SP_ATTR_TYPE, _("Type"), ColorMatrixTypeConverter); + _settings->add_colormatrixvalues(_("Value(s)")); _settings->type(NR_FILTER_COMPOSITE); - _settings->add(SP_ATTR_OPERATOR, _("Operator"), CompositeOperatorConverter); - _k1 = _settings->add(SP_ATTR_K1, _("K1"), -10, 10, 1, 0.01, 1); - _k2 = _settings->add(SP_ATTR_K2, _("K2"), -10, 10, 1, 0.01, 1); - _k3 = _settings->add(SP_ATTR_K3, _("K3"), -10, 10, 1, 0.01, 1); - _k4 = _settings->add(SP_ATTR_K4, _("K4"), -10, 10, 1, 0.01, 1); + _settings->add_combo(SP_ATTR_OPERATOR, _("Operator"), CompositeOperatorConverter); + _k1 = _settings->add_spinslider(SP_ATTR_K1, _("K1"), -10, 10, 1, 0.01, 1); + _k2 = _settings->add_spinslider(SP_ATTR_K2, _("K2"), -10, 10, 1, 0.01, 1); + _k3 = _settings->add_spinslider(SP_ATTR_K3, _("K3"), -10, 10, 1, 0.01, 1); + _k4 = _settings->add_spinslider(SP_ATTR_K4, _("K4"), -10, 10, 1, 0.01, 1); _settings->type(NR_FILTER_CONVOLVEMATRIX); - DualSpinSlider* order = _settings->add(SP_ATTR_ORDER, _("Rows"), _("Columns"), 1, 5, 1, 1, 0); - ConvolveMatrix* convmat = _settings->add(SP_ATTR_KERNELMATRIX, _("Kernel")); - order->signal_value_changed().connect( - sigc::bind(sigc::mem_fun(*convmat, &ConvolveMatrix::update_direct), this)); - order->set_update_policy(Gtk::UPDATE_DISCONTINUOUS); - _settings->add(SP_ATTR_DIVISOR, _("Divisor"), 0.01, 10, 1, 0.01, 1); - _settings->add(SP_ATTR_BIAS, _("Bias"), -10, 10, 1, 0.01, 1); + _convolve_order = _settings->add_dualspinbutton(SP_ATTR_ORDER, _("Size"), 1, 5, 1, 1, 0); + _convolve_target = _settings->add_multispinbutton(SP_ATTR_TARGETX, SP_ATTR_TARGETY, _("Target"), 0, 4, 1, 1, 0); + _convolve_matrix = _settings->add_matrix(SP_ATTR_KERNELMATRIX, _("Kernel")); + _convolve_order->signal_attr_changed().connect(sigc::mem_fun(*this, &FilterEffectsDialog::convolve_order_changed)); + _settings->add_spinslider(SP_ATTR_DIVISOR, _("Divisor"), 0.01, 10, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_BIAS, _("Bias"), -10, 10, 1, 0.01, 1); + _settings->add_combo(SP_ATTR_EDGEMODE, _("Edge Mode"), ConvolveMatrixEdgeModeConverter); + + _settings->type(NR_FILTER_DIFFUSELIGHTING); + _settings->add_color(SP_PROP_LIGHTING_COLOR, _("Diffuse Color")); + _settings->add_spinslider(SP_ATTR_SURFACESCALE, _("Surface Scale"), -10, 10, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_DIFFUSECONSTANT, _("Constant"), 0, 100, 1, 0.01, 1); + _settings->add_dualspinslider(SP_ATTR_KERNELUNITLENGTH, _("Kernel Unit Length"), 0.01, 10, 1, 0.01, 1); + _settings->add_lightsource(_("Light Source")); + + _settings->type(NR_FILTER_DISPLACEMENTMAP); + _settings->add_spinslider(SP_ATTR_SCALE, _("Scale"), 0, 100, 1, 0.01, 1); + _settings->add_combo(SP_ATTR_XCHANNELSELECTOR, _("X Channel"), DisplacementMapChannelConverter); + _settings->add_combo(SP_ATTR_YCHANNELSELECTOR, _("Y Channel"), DisplacementMapChannelConverter); _settings->type(NR_FILTER_GAUSSIANBLUR); - _settings->add(SP_ATTR_STDDEVIATION, _("Standard Deviation X"), _("Standard Deviation Y"), 0, 100, 1, 0.01, 1); + _settings->add_dualspinslider(SP_ATTR_STDDEVIATION, _("Standard Deviation"), 0.01, 100, 1, 0.01, 1); _settings->type(NR_FILTER_OFFSET); - _settings->add(SP_ATTR_DX, _("Delta X"), -100, 100, 1, 0.01, 1); - _settings->add(SP_ATTR_DY, _("Delta Y"), -100, 100, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_DX, _("Delta X"), -100, 100, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_DY, _("Delta Y"), -100, 100, 1, 0.01, 1); _settings->type(NR_FILTER_SPECULARLIGHTING); - //_settings->add(_specular_color, SP_PROP_LIGHTING_COLOR, _("Specular Color")); - _settings->add(SP_ATTR_SURFACESCALE, _("Surface Scale"), -10, 10, 1, 0.01, 1); - _settings->add(SP_ATTR_SPECULARCONSTANT, _("Constant"), 0, 100, 1, 0.01, 1); - _settings->add(SP_ATTR_SPECULAREXPONENT, _("Exponent"), 1, 128, 1, 0.01, 1); + _settings->add_color(SP_PROP_LIGHTING_COLOR, _("Specular Color")); + _settings->add_spinslider(SP_ATTR_SURFACESCALE, _("Surface Scale"), -10, 10, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_SPECULARCONSTANT, _("Constant"), 0, 100, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_SPECULAREXPONENT, _("Exponent"), 1, 128, 1, 0.01, 1); + _settings->add_dualspinslider(SP_ATTR_KERNELUNITLENGTH, _("Kernel Unit Length"), 0.01, 10, 1, 0.01, 1); + _settings->add_lightsource(_("Light Source")); _settings->type(NR_FILTER_TURBULENCE); - /*std::vector trb_grp; - trb_grp.push_back(&_turbulence_fractalnoise); - trb_grp.push_back(&_turbulence_turbulence); - _settings->add(trb_grp); - _turbulence.add_setting(_turbulence_numoctaves, _("Octaves")); - _turbulence.add_setting(_turbulence_basefrequency, _("Base Frequency")); - _turbulence.add_setting(_turbulence_seed, _("Seed")); - _turbulence.add_setting(_turbulence_stitchtiles);*/ + _settings->add_checkbutton(SP_ATTR_STITCHTILES, _("Stitch Tiles"), "stitch", "noStitch"); + _settings->add_dualspinslider(SP_ATTR_BASEFREQUENCY, _("Base Frequency"), 0, 100, 1, 0.01, 1); + _settings->add_spinslider(SP_ATTR_NUMOCTAVES, _("Octaves"), 1, 10, 1, 1, 0); + _settings->add_spinslider(SP_ATTR_SEED, _("Seed"), 0, 1000, 1, 1, 0); } void FilterEffectsDialog::add_primitive() @@ -1100,34 +1750,39 @@ void FilterEffectsDialog::duplicate_primitive() } } -void FilterEffectsDialog::set_attr_color(const SPAttributeEnum attr, const Gtk::ColorButton* input) +void FilterEffectsDialog::convolve_order_changed() { - if(input->is_sensitive()) { - std::ostringstream os; - const Gdk::Color c = input->get_color(); - const int r = 255 * c.get_red() / 65535, g = 255 * c.get_green() / 65535, b = 255 * c.get_blue() / 65535; - os << "rgb(" << r << "," << g << "," << b << ")"; - set_attr(attr, os.str().c_str()); - } + _convolve_matrix->set_from_attribute(SP_OBJECT(_primitive_list.get_selected())); + _convolve_target->get_spinbuttons()[0]->get_adjustment()->set_upper(_convolve_order->get_spinbutton1().get_value() - 1); + _convolve_target->get_spinbuttons()[1]->get_adjustment()->set_upper(_convolve_order->get_spinbutton2().get_value() - 1); } -void FilterEffectsDialog::set_attr_direct(const SPAttributeEnum attr, const AttrWidget* input) +void FilterEffectsDialog::set_attr_direct(const AttrWidget* input) { - set_attr(attr, input->get_as_attribute().c_str()); + set_attr(_primitive_list.get_selected(), input->get_attribute(), input->get_as_attribute().c_str()); } -void FilterEffectsDialog::set_attr(const SPAttributeEnum attr, const gchar* val) +void FilterEffectsDialog::set_child_attr_direct(const AttrWidget* input) { - SPFilter *filter = _filter_modifier.get_selected_filter(); - SPFilterPrimitive* prim = _primitive_list.get_selected(); - - if(filter && prim) { - update_settings_sensitivity(); - - SP_OBJECT_REPR(prim)->setAttribute((gchar*)sp_attribute_name(attr), val); - filter->requestModified(SP_OBJECT_MODIFIED_FLAG); + set_attr(_primitive_list.get_selected()->children, input->get_attribute(), input->get_as_attribute().c_str()); +} - sp_document_done(filter->document, SP_VERB_DIALOG_FILTER_EFFECTS, _("Set filter primitive attribute")); +void FilterEffectsDialog::set_attr(SPObject* o, const SPAttributeEnum attr, const gchar* val) +{ + if(!_locked) { + SPFilter *filter = _filter_modifier.get_selected_filter(); + const gchar* name = (const gchar*)sp_attribute_name(attr); + if(filter && name && o) { + update_settings_sensitivity(); + + SP_OBJECT_REPR(o)->setAttribute(name, val); + filter->requestModified(SP_OBJECT_MODIFIED_FLAG); + + Glib::ustring undokey = "filtereffects:"; + undokey += name; + sp_document_maybe_done(filter->document, undokey.c_str(), SP_VERB_DIALOG_FILTER_EFFECTS, + _("Set filter primitive attribute")); + } } } @@ -1143,10 +1798,7 @@ void FilterEffectsDialog::update_settings_view() _empty_settings.show(); if(prim) { - const FilterPrimitiveType tid = FPConverter.get_id_from_key(prim->repr->name()); - - _settings->show_and_update(tid); - + _settings->show_and_update(FPConverter.get_id_from_key(prim->repr->name()), prim); _settings_box.set_sensitive(true); _empty_settings.hide(); }