From 02df4842dc780e15999cae54bad38b11ed197b90 Mon Sep 17 00:00:00 2001 From: nicholasbishop Date: Tue, 10 Jul 2007 07:12:02 +0000 Subject: [PATCH] Filter effects interface: Modified the display of filter primitive connections to always show all connectins using decreasing widths for the connection cells in the treeview. Also made the "in2" connections work for feBlend and feComposite. --- src/ui/dialog/filter-effects-dialog.cpp | 382 ++++++++++++++---------- src/ui/dialog/filter-effects-dialog.h | 31 +- 2 files changed, 253 insertions(+), 160 deletions(-) diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index f6c61791b..c9cef7aea 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -187,63 +187,68 @@ void FilterEffectsDialog::FilterModifier::filter_name_edited(const Glib::ustring try_id_change((SPObject*)(*i)[_columns.filter], text); } -/*** PrimitiveList ***/ -class CellRendererConnection : public Gtk::CellRenderer +FilterEffectsDialog::CellRendererConnection::CellRendererConnection() + : Glib::ObjectBase(typeid(CellRendererConnection)), + _primitive(*this, "primitive", 0) { -public: - CellRendererConnection() - { - - } - - Glib::PropertyProxy property_connection(); - - static int input_count(const SPFilterPrimitive* prim) - { - /*TODO*/ +} - if(!prim) - return 0; - /*else if(SP_IS_FEBLEND(prim) || SP_IS_FECOMPOSITE(prim) || SP_IS_FEDISPLACEMENTMAP(prim)) - return 2;*/ - else - return 1; - } +Glib::PropertyProxy FilterEffectsDialog::CellRendererConnection::property_primitive() +{ + return _primitive.get_proxy(); +} - static const int size = 32; -protected: - virtual void get_size_vfunc(Gtk::Widget& widget, const Gdk::Rectangle* cell_area, - int* x_offset, int* y_offset, int* width, int* height) const - { - if(x_offset) - (*x_offset) = 0; - if(y_offset) - (*y_offset) = 0; - if(width) - (*width) = size; - if(height) - (*height) = size; +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 + return 1; +} + +void FilterEffectsDialog::CellRendererConnection::get_size_vfunc( + Gtk::Widget& widget, const Gdk::Rectangle* cell_area, + int* x_offset, int* y_offset, int* width, int* height) const +{ + PrimitiveList& primlist = dynamic_cast(widget); + + if(x_offset) + (*x_offset) = 0; + if(y_offset) + (*y_offset) = 0; + if(width) + (*width) = size * primlist.primitive_count(); + if(height) { + // Scale the height depending on the number of inputs, unless it's + // the first primitive, in which case their are no connections + SPFilterPrimitive* prim = (SPFilterPrimitive*)_primitive.get_value(); + (*height) = primlist.is_first(prim) ? size : size * input_count(prim); } -}; +} +/*** PrimitiveList ***/ FilterEffectsDialog::PrimitiveList::PrimitiveList(FilterEffectsDialog& d) : _dialog(d), _in_drag(0) { add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); - _primitive_model = Gtk::ListStore::create(_primitive_columns); + _model = Gtk::ListStore::create(_columns); + + // TODO: reenable this once it is possible to modify the order in the backend + //set_reorderable(true); - set_reorderable(true); - set_model(_primitive_model); - append_column(_("_Type"), _primitive_columns.type); + set_model(_model); + append_column(_("_Type"), _columns.type); signal_selection_changed().connect(sigc::mem_fun(*this, &PrimitiveList::queue_draw)); CellRendererConnection* cell = new CellRendererConnection; - int cols_count = append_column("Connections", *cell);/* - Gtk::TreeViewColumn* pColumn = get_column(cols_count - 1); - if(pColumn) - pColumn->add_attribute(cell->property_connection(), _columns.con);*/ + int cols_count = append_column("Connections", *cell); + Gtk::TreeViewColumn* col = get_column(cols_count - 1); + if(col) + col->add_attribute(cell->property_primitive(), _columns.primitive); } Glib::SignalProxy0 FilterEffectsDialog::PrimitiveList::signal_selection_changed() @@ -259,7 +264,7 @@ void FilterEffectsDialog::PrimitiveList::update() const SPFilterPrimitive* active_prim = get_selected(); bool active_found = false; - _primitive_model->clear(); + _model->clear(); if(f) { _dialog._primitive_box.set_sensitive(true); @@ -269,11 +274,11 @@ void FilterEffectsDialog::PrimitiveList::update() prim_obj = prim_obj->next) { SPFilterPrimitive *prim = SP_FILTER_PRIMITIVE(prim_obj); if(prim) { - Gtk::TreeModel::Row row = *_primitive_model->append(); - row[_primitive_columns.primitive] = prim; - row[_primitive_columns.type_id] = FPConverter.get_id_from_name(prim->repr->name()); - row[_primitive_columns.type] = FPConverter.get_label(row[_primitive_columns.type_id]); - row[_primitive_columns.id] = SP_OBJECT_ID(prim); + Gtk::TreeModel::Row row = *_model->append(); + row[_columns.primitive] = prim; + row[_columns.type_id] = FPConverter.get_id_from_name(prim->repr->name()); + row[_columns.type] = FPConverter.get_label(row[_columns.type_id]); + row[_columns.id] = SP_OBJECT_ID(prim); if(prim == active_prim) { get_selection()->select(row); @@ -282,8 +287,8 @@ void FilterEffectsDialog::PrimitiveList::update() } } - if(!active_found && _primitive_model->children().begin()) - get_selection()->select(_primitive_model->children().begin()); + if(!active_found && _model->children().begin()) + get_selection()->select(_model->children().begin()); } else { _dialog._primitive_box.set_sensitive(false); @@ -300,7 +305,7 @@ SPFilterPrimitive* FilterEffectsDialog::PrimitiveList::get_selected() if(_dialog._filter_modifier.get_selected_filter()) { Gtk::TreeModel::iterator i = get_selection()->get_selected(); if(i) - return (*i)[_primitive_columns.primitive]; + return (*i)[_columns.primitive]; } return 0; @@ -308,9 +313,9 @@ SPFilterPrimitive* FilterEffectsDialog::PrimitiveList::get_selected() void FilterEffectsDialog::PrimitiveList::select(SPFilterPrimitive* prim) { - for(Gtk::TreeIter i = _primitive_model->children().begin(); - i != _primitive_model->children().end(); ++i) { - if((*i)[_primitive_columns.primitive] == prim) + for(Gtk::TreeIter i = _model->children().begin(); + i != _model->children().end(); ++i) { + if((*i)[_columns.primitive] == prim) get_selection()->select(i); } } @@ -319,132 +324,184 @@ bool FilterEffectsDialog::PrimitiveList::on_expose_event(GdkEventExpose* e) { Gtk::TreeView::on_expose_event(e); - const int sz = CellRendererConnection::size; SPFilterPrimitive* prim = get_selected(); - if(prim) { - int row_index = 0; - for(Gtk::TreeIter row = get_model()->children().begin(); - row != get_model()->children().end(); ++row, ++row_index) { - const int inputs = CellRendererConnection::input_count(prim); - for(int i = 0; i < inputs; ++i) { - Gdk::Rectangle rct, clip(&e->area); - get_cell_area(get_model()->get_path(row), *get_column(1), rct); - const int x = rct.get_x() + sz / 4 + sz * i; - const int y = rct.get_y() + sz / 4; - const bool on_sel_row = (*row)[_primitive_columns.primitive] == prim; - - // Check mouse state - int mx, my; - Gdk::ModifierType mask; - get_bin_window()->get_pointer(mx, my, mask); - //const bool inside = mx >= x && my >= y && mx <= x + sz / 2 && my <= y + sz / 2; - - get_bin_window()->draw_rectangle(get_style()->get_black_gc(), on_sel_row, x, y, sz / 2, sz / 2); - - // draw input connection(s) - if(on_sel_row) { - if(i == 0 && _in_drag != 1) - draw_connection(find_result(row), x + sz / 4, y + sz / 4); - else if(i == 1 && _in_drag != 2) { - /*if(SP_IS_FEBLEND(prim)) - \ draw_connection(SP_FEBLEND(prim)->in2, x + sz / 4, y + sz / 4);*/ - } - else if(_in_drag > 0) { - draw_connection(row, mx, my); - } - } - } + int row_index = 0; + int row_count = get_model()->children().size(); + int fheight = 0; + for(Gtk::TreeIter row = get_model()->children().begin(); + row != get_model()->children().end(); ++row, ++row_index) { + Gdk::Rectangle rct, clip(&e->area); + get_cell_area(get_model()->get_path(row), *get_column(1), rct); + const int x = rct.get_x(), y = rct.get_y(), h = rct.get_height(); + + // For calculating the width of cells, the height of the first row is used + if(row_index == 0) + fheight = h; + + // Check mouse state + int mx, my; + Gdk::ModifierType mask; + get_bin_window()->get_pointer(mx, my, mask); + + // Outline the bottom of the connection area + const int outline_x = x + fheight * (row_count - row_index); + get_bin_window()->draw_line(get_style()->get_dark_gc(Gtk::STATE_NORMAL), + x, y + h, outline_x, y + h); + + // The first row can't have any inputs + if(row_index == 0) + continue; + + // Side outline + get_bin_window()->draw_line(get_style()->get_dark_gc(Gtk::STATE_NORMAL), + outline_x, y, outline_x, y + h); + + std::vector con_poly; + int con_drag_y; + bool inside; + // Draw "in" shape + inside = do_connection_node(row, 0, con_poly, mx, my); + con_drag_y = con_poly[2].get_y(); + get_bin_window()->draw_polygon(inside && mask & GDK_BUTTON1_MASK ? + get_style()->get_dark_gc(Gtk::STATE_NORMAL) : + get_style()->get_dark_gc(Gtk::STATE_ACTIVE), + inside, con_poly); + // Draw "in" connection + draw_connection(find_result(row, SP_ATTR_IN), outline_x, con_poly[2].get_y(), row_count); + + const SPFilterPrimitive* row_prim = (*row)[_columns.primitive]; + if(CellRendererConnection::input_count(row_prim) == 2) { + // Draw "in2" shape + inside = do_connection_node(row, 1, con_poly, mx, my); + if(_in_drag == 2) + con_drag_y = con_poly[2].get_y(); + get_bin_window()->draw_polygon(inside && mask & GDK_BUTTON1_MASK ? + get_style()->get_dark_gc(Gtk::STATE_NORMAL) : + get_style()->get_dark_gc(Gtk::STATE_ACTIVE), + inside, con_poly); + // Draw "in2" connection + draw_connection(find_result(row, SP_ATTR_IN2), outline_x, con_poly[2].get_y(), row_count); } - } - - return true; -} -const Gtk::TreeIter FilterEffectsDialog::PrimitiveList::find_result(const Gtk::TreeIter& start) -{ - const int image = ((SPFilterPrimitive*)(*start)[_primitive_columns.primitive])->image_in; - Gtk::TreeIter target = _primitive_model->children().end(); - - if(image >= 0) { - for(Gtk::TreeIter i = _primitive_model->children().begin(); - i != start; ++i) { - if(((SPFilterPrimitive*)(*i)[_primitive_columns.primitive])->image_out == image) - target = i; + // 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 target; + return true; } -void FilterEffectsDialog::PrimitiveList::draw_connection(const Gtk::TreeIter& input, const int x, const int y) +void FilterEffectsDialog::PrimitiveList::draw_connection(const Gtk::TreeIter& input, const int x1, const int y1, + const int row_count) { - if(input != _primitive_model->children().end()) { + if(input != _model->children().end()) { Gdk::Rectangle rct; - const int sz = CellRendererConnection::size; + + get_cell_area(get_model()->get_path(_model->children().begin()), *get_column(1), rct); + const int fheight = rct.get_height(); get_cell_area(get_model()->get_path(input), *get_column(1), rct); + const int row_index = find_index(input); + const int x2 = rct.get_x() + fheight * (row_count - row_index) - fheight / 2; + const int y2 = rct.get_y() + rct.get_height(); - get_bin_window()->draw_line(get_style()->get_black_gc(), x, y, - rct.get_x() + sz / 2, rct.get_y() + sz / 2); + // Draw an 'L'-shaped connection + get_bin_window()->draw_line(get_style()->get_black_gc(), x1, y1, x2, y1); + get_bin_window()->draw_line(get_style()->get_black_gc(), x2, y1, x2, y2); } } -// Gets (row, col) of the filter primitive input located at (x,y) -bool FilterEffectsDialog::PrimitiveList::get_coords_at_pos(const int x, const int y, int& row, - int& col, SPFilterPrimitive **prim) +// Creates a triangle outline of the connection node and returns true if (x,y) is inside the node +bool FilterEffectsDialog::PrimitiveList::do_connection_node(const Gtk::TreeIter& row, const int input, + std::vector& points, + const int ix, const int iy) { - Gtk::TreePath path; - Gtk::TreeViewColumn* treecol; Gdk::Rectangle rct; - int cx, cy; - if(get_path_at_pos(x, y, path, treecol, cx, cy)) { - get_cell_area(path, *treecol, rct); - - if(x >= rct.get_x() && y >= rct.get_y() && - x <= rct.get_x() + rct.get_width() && y <= rct.get_y() + rct.get_height()) { - if(treecol == get_column(1)) { - col = (x - rct.get_x()) / CellRendererConnection::size + 1; - if(col > 0 && col <= 2) { - row = 0; - Gtk::TreeIter key = _primitive_model->get_iter(path); - for(Gtk::TreeIter i = _primitive_model->children().begin(); - i != _primitive_model->children().end(); ++i, ++row) { - if(key == i) - break; - } - if(prim) - (*prim) = (*key)[_primitive_columns.primitive]; - return true; - } - } + get_cell_area(get_model()->get_path(_model->children().begin()), *get_column(1), rct); + const int h = rct.get_height(); + + get_cell_area(_model->get_path(row), *get_column(1), rct); + + const int x = rct.get_x() + h * (_model->children().size() - find_index(row)); + const int con_w = (int)(h * 0.35f); + const int con_y = rct.get_y() + h / 2 - con_w + input * h; + points.clear(); + points.push_back(Gdk::Point(x, con_y)); + points.push_back(Gdk::Point(x, con_y + con_w * 2)); + points.push_back(Gdk::Point(x - con_w, con_y + con_w)); + + return ix >= x - h && iy >= con_y && ix <= x && iy <= points[1].get_y(); +} + +const Gtk::TreeIter FilterEffectsDialog::PrimitiveList::find_result(const Gtk::TreeIter& start, + const SPAttributeEnum attr) +{ + 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;*/ + else + return target; + } + else + return target; + + if(image >= 0) { + for(Gtk::TreeIter i = _model->children().begin(); + i != start; ++i) { + if(((SPFilterPrimitive*)(*i)[_columns.primitive])->image_out == image) + target = i; } } - - return false; + + return target; +} + +int FilterEffectsDialog::PrimitiveList::find_index(const Gtk::TreeIter& target) +{ + int i = 0; + for(Gtk::TreeIter iter = _model->children().begin(); + iter != target; ++iter, ++i); + return i; } bool FilterEffectsDialog::PrimitiveList::on_button_press_event(GdkEventButton* e) { - int row, col; + Gtk::TreePath path; + Gtk::TreeViewColumn* col; + const int x = (int)e->x, y = (int)e->y; + int cx, cy; - if(get_coords_at_pos((int)e->x, (int)e->y, row, col, 0)) { - _in_drag = col; - - queue_draw(); + if(get_path_at_pos(x, y, path, col, cx, cy)) { + 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; + + queue_draw(); } - if(_in_drag) - return true; - else - return Gtk::TreeView::on_button_press_event(e); + return Gtk::TreeView::on_button_press_event(e); } bool FilterEffectsDialog::PrimitiveList::on_motion_notify_event(GdkEventMotion* e) { - if(_in_drag) - queue_draw(); + queue_draw(); return Gtk::TreeView::on_motion_notify_event(e); } @@ -454,9 +511,12 @@ bool FilterEffectsDialog::PrimitiveList::on_button_release_event(GdkEventButton* SPFilterPrimitive *prim = get_selected(), *target; if(_in_drag && prim) { - int row, col; - - if(get_coords_at_pos((int)e->x, (int)e->y, row, col, &target) && target != prim) { + Gtk::TreePath path; + Gtk::TreeViewColumn* col; + int cx, cy; + + if(get_path_at_pos((int)e->x, (int)e->y, path, col, cx, cy)) { + target = (*_model->get_iter(path))[_columns.primitive]; Inkscape::XML::Node *repr = SP_OBJECT_REPR(target); // Make sure the target has a result const gchar *gres = repr->attribute("result"); @@ -466,7 +526,10 @@ bool FilterEffectsDialog::PrimitiveList::on_button_release_event(GdkEventButton* repr->setAttribute("result", result.c_str()); } - SP_OBJECT_REPR(prim)->setAttribute("in", result.c_str()); + if(_in_drag == 1) + SP_OBJECT_REPR(prim)->setAttribute("in", result.c_str()); + else if(_in_drag == 2) + SP_OBJECT_REPR(prim)->setAttribute("in2", result.c_str()); } _in_drag = 0; @@ -490,12 +553,11 @@ bool FilterEffectsDialog::PrimitiveList::on_button_release_event(GdkEventButton* // Reorder the filter primitives to match the list order void FilterEffectsDialog::PrimitiveList::on_drag_end(const Glib::RefPtr&) { - int ndx = 0; SPFilter* filter = _dialog._filter_modifier.get_selected_filter(); - for(Gtk::TreeModel::iterator iter = _primitive_model->children().begin(); - iter != _primitive_model->children().end(); ++iter) { - SPFilterPrimitive* prim = (*iter)[_primitive_columns.primitive]; + for(Gtk::TreeModel::iterator iter = _model->children().begin(); + iter != _model->children().end(); ++iter) { + SPFilterPrimitive* prim = (*iter)[_columns.primitive]; if(prim) ;//reorder_primitive(filter, prim->repr->position(), ndx); /* FIXME */ } @@ -503,6 +565,16 @@ void FilterEffectsDialog::PrimitiveList::on_drag_end(const Glib::RefPtrdocument, SP_VERB_DIALOG_FILTER_EFFECTS, _("Reorder filter primitive")); } +int FilterEffectsDialog::PrimitiveList::primitive_count() const +{ + return _model->children().size(); +} + +bool FilterEffectsDialog::PrimitiveList::is_first(const SPFilterPrimitive* prim) const +{ + return (*_model->children().begin())[_columns.primitive] == prim; +} + /*** SettingsGroup ***/ FilterEffectsDialog::SettingsGroup::SettingsGroup() { diff --git a/src/ui/dialog/filter-effects-dialog.h b/src/ui/dialog/filter-effects-dialog.h index 6b1f48895..199e7d9ca 100644 --- a/src/ui/dialog/filter-effects-dialog.h +++ b/src/ui/dialog/filter-effects-dialog.h @@ -81,6 +81,22 @@ private: Gtk::TreeModelColumn id; }; + class CellRendererConnection : public Gtk::CellRenderer + { + public: + CellRendererConnection(); + Glib::PropertyProxy property_primitive(); + static int input_count(const SPFilterPrimitive* prim); + + static const int size = 24; + protected: + virtual void get_size_vfunc(Gtk::Widget& widget, const Gdk::Rectangle* cell_area, + int* x_offset, int* y_offset, int* width, int* height) const; + private: + // void* should be SPFilterPrimitive*, some weirdness with properties prevents this + Glib::Property _primitive; + }; + class PrimitiveList : public Gtk::TreeView { public: @@ -93,6 +109,9 @@ private: SPFilterPrimitive* get_selected(); void select(SPFilterPrimitive *prim); + + int primitive_count() const; + bool is_first(const SPFilterPrimitive*) const; protected: bool on_expose_event(GdkEventExpose*); bool on_button_press_event(GdkEventButton*); @@ -100,13 +119,15 @@ private: bool on_button_release_event(GdkEventButton*); void on_drag_end(const Glib::RefPtr&); private: - bool get_coords_at_pos(const int x, const int y, int& row, int& col, SPFilterPrimitive **prim); - const Gtk::TreeIter find_result(const Gtk::TreeIter& start); - void draw_connection(const Gtk::TreeIter&, const int x, const int y); + bool do_connection_node(const Gtk::TreeIter& row, const int input, std::vector& points, + const int ix, const int iy); + const Gtk::TreeIter find_result(const Gtk::TreeIter& start, const SPAttributeEnum attr); + int find_index(const Gtk::TreeIter& target); + void draw_connection(const Gtk::TreeIter&, const int x1, const int y1, const int row_count); FilterEffectsDialog& _dialog; - Glib::RefPtr _primitive_model; - PrimitiveColumns _primitive_columns; + Glib::RefPtr _model; + PrimitiveColumns _columns; Glib::RefPtr _primitive_menu; int _in_drag; }; -- 2.30.2