//$Id: form-win-widgettree.cc,v 1.14 2006/10/06 22:35:40 cactus Exp $ -*- c++ -*- /* Guikachu Copyright (C) 2001-2006 ÉRDI Gergõ <cactus@cactus.rulez.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "form-win-widgettree.h" #include "form-editor/widget-util.h" #include "form-editor/widget-util-gui.h" #include "resource-util-gui.h" #include "cellrenderer-icontext.h" #include "cellrenderer-indent.h" #include <gdk/gdkkeysyms.h> using namespace Guikachu::GUI::FormWindow_Helpers; WidgetTreeWrapper::WidgetTreeWrapper (Gtk::TreeView &treeview_, Resources::Form *form_): treeview (treeview_), form (form_), selection_block (false), menu_block (false) { Gtk::TreeModel::ColumnRecord cols; cols.add (col_widget); treestore = Gtk::TreeStore::create (cols); treeview.set_model (treestore); treeview.set_headers_visible (false); CellRendererIndent<CellRendererIconText>::setup_view ( treeview, "", sigc::mem_fun (*this, &WidgetTreeWrapper::cell_label_cb)); // Create root item: the form itself root_tree = *(treestore->append ()); root_tree[col_widget] = 0; form->changed.connect (sigc::mem_fun (*this, &WidgetTreeWrapper::form_changed_cb)); form_changed_cb (); form->widget_created.connect (sigc::mem_fun (*this, &WidgetTreeWrapper::widget_created_cb)); form->widget_removed.connect (sigc::mem_fun (*this, &WidgetTreeWrapper::widget_removed_cb)); treeview.get_selection ()->set_select_function ( sigc::mem_fun (*this, &WidgetTreeWrapper::selection_changed_cb)); // Create tree items for existing widgets const std::set<Guikachu::Widget*> widgets = form->get_widgets (); std::for_each (widgets.begin (), widgets.end (), sigc::mem_fun (*this, &WidgetTreeWrapper::widget_created_cb)); treeview.expand_all (); // Set selection mode treeview.get_selection ()->set_mode (Gtk::SELECTION_MULTIPLE); treeview.add_events (Gdk::BUTTON_PRESS_MASK | Gdk::KEY_PRESS_MASK); treeview.signal_button_press_event ().connect_notify ( sigc::mem_fun (*this, &WidgetTreeWrapper::button_press_cb)); treeview.signal_key_press_event ().connect ( sigc::bind_return (sigc::mem_fun (*this, &WidgetTreeWrapper::key_press_cb), false)); } void WidgetTreeWrapper::cell_label_cb (Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) const { CellRendererIconText *cell_icontext = dynamic_cast<CellRendererIconText*> (cell); g_return_if_fail (cell_icontext); Glib::ustring label; Glib::RefPtr<Gdk::Pixbuf> pixbuf; Guikachu::Widget *widget = (*iter)[col_widget]; if (widget) { label = widget->id; pixbuf = Widgets::get_type_pixbuf (widget->get_type ()); } else { label = form->id; pixbuf = Resources::get_type_pixbuf (Resources::RESOURCE_FORM); } cell_icontext->property_text () = label; cell_icontext->property_pixbuf () = pixbuf; } void WidgetTreeWrapper::widget_removed_cb (Guikachu::Widget *widget) { row_map_t::iterator row_found = row_map.find (widget); g_return_if_fail (row_found != row_map.end ()); treestore->erase (row_found->second); row_map.erase (row_found); treeview.expand_all (); } bool WidgetTreeWrapper::compare_treerow (const Gtk::TreeRow &row, Guikachu::Widget *widget) const { Widget *curr_widget = row[col_widget]; if (!curr_widget) return true; return IDManager::NoCase () (curr_widget->id, widget->id); } Gtk::TreeStore::iterator WidgetTreeWrapper::get_place (Guikachu::Widget *widget) const { return std::lower_bound (root_tree.children ().begin (), root_tree.children ().end (), widget, sigc::mem_fun (*this, &WidgetTreeWrapper::compare_treerow)); } void WidgetTreeWrapper::widget_created_cb (Guikachu::Widget *widget) { Gtk::TreeRow row = *(treestore->insert (get_place (widget))); row[col_widget] = widget; row_map[widget] = row; widget->changed.connect (sigc::bind ( sigc::mem_fun (*this, &WidgetTreeWrapper::widget_changed_cb), widget)); widget->selected.connect (sigc::bind ( sigc::mem_fun (*this, &WidgetTreeWrapper::widget_selected_cb), widget)); treeview.expand_all (); } void WidgetTreeWrapper::widget_changed_cb (Guikachu::Widget *widget) { g_return_if_fail (widget); Gtk::TreeRow row = row_map[widget]; bool selected = treeview.get_selection ()->is_selected (row); treestore->erase (row); row = *(treestore->insert (get_place (widget))); row[col_widget] = widget; row_map[widget] = row; treeview.expand_all (); if (selected) treeview.get_selection ()->select (row); } void WidgetTreeWrapper::form_changed_cb () { treestore->row_changed (treestore->get_path (root_tree), root_tree); } void WidgetTreeWrapper::widget_selected_cb (bool selected, Guikachu::Widget *widget) { row_map_t::const_iterator row_iter = row_map.find (widget); if (row_iter == row_map.end ()) return; selection_block = true; Glib::RefPtr<Gtk::TreeSelection> tree_selection = treeview.get_selection (); Gtk::TreeRow row = row_iter->second; if (selected) { tree_selection->select (row); tree_selection->unselect (root_tree); } else { tree_selection->unselect (row); } selection_block = false; } bool WidgetTreeWrapper::selection_changed_cb (const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool previously_selected) { if (menu_block) return false; if (selection_block) return true; bool selected = !previously_selected; if (path.get_depth () < 2) { if (selected) form_selected.emit (); return true; } Gtk::TreeModel::iterator iter = model->get_iter (path); Guikachu::Widget *widget = (*iter)[col_widget]; widget_selected.emit (widget, selected); return false; } namespace { void reset_menu_block (bool *menu_block) { *menu_block = false; } } // anonymous namespace void WidgetTreeWrapper::button_press_cb (GdkEventButton *e) { if (e->button != 3) return; int cell_x, cell_y; Gtk::TreeModel::Path path; Gtk::TreeViewColumn *column; Guikachu::Widget *widget = 0; // FIXME: e->x and e->y are doubles if (treeview.get_path_at_pos (e->x, e->y, path, column, cell_x, cell_y)) { if (treeview.get_selection ()->is_selected (path)) { menu_block = true; Glib::signal_idle ().connect ( sigc::bind_return (sigc::bind (sigc::ptr_fun (reset_menu_block), &menu_block), false)); } Gtk::TreeModel::iterator iter = treestore->get_iter (path); widget = (*iter)[col_widget]; } if (widget) widget_menu.emit (e->button, e->time, widget); else form_menu.emit (e->button, e->time); } namespace { template<typename T, typename Container = std::list<T> > class SelectionCollector: public sigc::trackable { public: typedef T element_t; typedef Container container_t; typedef Gtk::TreeModelColumn<element_t> column_t; private: column_t column; container_t items; public: SelectionCollector (const column_t &column_): column (column_) {}; container_t get_items () const { return items; }; void selection_cb (const Gtk::TreeModel::iterator &iter) { items.push_back ((*iter)[column]); }; }; } // anonymous namespace void WidgetTreeWrapper::key_press_cb (GdkEventKey *e) { Guikachu::Widget *widget = 0; Gtk::TreeView::Selection::ListHandle_Path items = treeview.get_selection ()->get_selected_rows (); // It doesn't really matter which widget we select when more than // one are selected if (items.size ()) { const Gtk::TreePath &path = *(items.begin ()); widget = (*treestore->get_iter (path))[col_widget]; } switch (e->keyval) { case GDK_Delete: case GDK_KP_Delete: if (widget) widget_remove.emit (widget); break; case GDK_Menu: if (widget) widget_menu.emit (0, e->time, widget); else form_menu.emit (0, e->time); break; default: break; } }