#include #include #include #include #include using namespace sirEdit::data; // // Dependencie graph // class ImageRender : public Gtk::Widget { private: Cairo::RefPtr __surface; Glib::RefPtr __refGdkWindow; public: ImageRender() { this->set_has_window(true); } void loadImage(std::string path) { this->__surface = Cairo::ImageSurface::create_from_png(path); this->queue_resize(); this->queue_draw(); } protected: // // Size // Gtk::SizeRequestMode get_request_mode_vfunc() const override { return Gtk::SizeRequestMode::SIZE_REQUEST_CONSTANT_SIZE; } void get_preferred_width_vfunc(int& minimum_width, int& natural_width) const override { if(this->__surface) minimum_width = this->__surface->get_width(); else minimum_width = 0; natural_width = minimum_width; } void get_preferred_height_for_width_vfunc(int width, int& minimum_height, int& natural_height) const override { this->get_preferred_height_vfunc(minimum_height, natural_height); } void get_preferred_height_vfunc(int& minimum_height, int& natural_height) const override { if(this->__surface) minimum_height = this->__surface->get_height(); else minimum_height = 0; natural_height = minimum_height; } void get_preferred_width_for_height_vfunc(int height, int& minimum_width, int& natural_width) const override { this->get_preferred_width_vfunc(minimum_width, natural_width); } void on_realize() override { this->set_realized(); //if(!this->__refGdkWindow) { //Create the GdkWindow GdkWindowAttr attributes; memset(&attributes, 0, sizeof(attributes)); Gtk::Allocation allocation = get_allocation(); //Set initial position and size of the Gdk::Window attributes.x = allocation.get_x(); attributes.y = allocation.get_y(); attributes.width = allocation.get_width(); attributes.height = allocation.get_height(); attributes.event_mask = this->get_events() | Gdk::EXPOSURE_MASK; attributes.window_type = GDK_WINDOW_CHILD; attributes.wclass = GDK_INPUT_OUTPUT; this->__refGdkWindow = Gdk::Window::create(this->get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y); set_window(this->__refGdkWindow); //make the widget receive expose events this->__refGdkWindow->set_user_data(this->gobj()); } } void on_unrealize() override { this->set_realized(); } void on_size_allocate(Gtk::Allocation& allocation) override { this->set_allocation(allocation); } // // Draw // bool on_draw(const Cairo::RefPtr& cr) override { if(this->__surface) { cr->save(); cr->set_source(this->__surface, 0, 0); cr->rectangle(0, 0, this->__surface->get_width(), this->__surface->get_height()); cr->fill(); cr->restore(); } return true; } }; class DependencieGraph : public Gtk::VBox { private: ImageRender __renderer; Gtk::ScrolledWindow __scrollable; Gtk::Button __update; public: DependencieGraph() { // Layout this->__update = Gtk::Button("Update"); this->__scrollable.add(this->__renderer); this->pack_start(this->__update, false, true); this->pack_end(this->__scrollable, true, true); // Button this->__update.signal_clicked().connect([this]() -> void { this->__renderer.loadImage("test.png"); // TODO: Calculate and draw correct diagram }); } }; // // Overview // class ToolModel : public Gtk::TreeModel::ColumnRecord { public: Gtk::TreeModelColumn data_name; Gtk::TreeModelColumn data_sort_name; Gtk::TreeModelColumn data_used; Gtk::TreeModelColumn data_active; Gtk::TreeModelColumn data_tool; ToolModel() { this->add(data_used); this->add(data_active); this->add(data_name); this->add(data_sort_name); this->add(data_tool); } }; static ToolModel toolModel; class TypeModel : public Gtk::TreeModel::ColumnRecord { public: Gtk::TreeModelColumn data_name; Gtk::TreeModelColumn data_sort_name; Gtk::TreeModelColumn data_used; Gtk::TreeModelColumn data_active; Gtk::TreeModelColumn data_type; TypeModel() { this->add(data_used); this->add(data_active); this->add(data_name); this->add(data_sort_name); this->add(data_type); } }; static TypeModel typeModel; class FieldModel : public Gtk::TreeModel::ColumnRecord { public: Gtk::TreeModelColumn data_name; Gtk::TreeModelColumn data_sort_name; Gtk::TreeModelColumn data_used; Gtk::TreeModelColumn data_active; Gtk::TreeModelColumn data_field; Gtk::TreeModelColumn data_isUsable; FieldModel() { this->add(data_used); this->add(data_active); this->add(data_name); this->add(data_sort_name); this->add(data_field); this->add(data_isUsable); } }; static FieldModel fieldModel; class Overview : public Gtk::VBox { private: Transactions& transactions; // Stack Gtk::Stack stack; Gtk::StackSwitcher stack_switcher; // Type overview Gtk::HPaned tool_paned; Gtk::HPaned type_paned; Gtk::HPaned field_paned; Gtk::ToggleButton hide_inactive_tool = Gtk::ToggleButton("Hide inactive"); Gtk::ToggleButton hide_unused_tool = Gtk::ToggleButton("Hide unused"); Gtk::ToggleButton hide_inactive_type = Gtk::ToggleButton("Hide inactive"); Gtk::ToggleButton hide_unused_type = Gtk::ToggleButton("Hide unused"); Gtk::ToggleButton hide_inactive_field = Gtk::ToggleButton("Hide inactive"); Gtk::ToggleButton hide_unused_field = Gtk::ToggleButton("Hide unused"); Gtk::ScrolledWindow tool_scroll; Gtk::TreeView tool_view; Glib::RefPtr tool_store; Gtk::ScrolledWindow type_scroll; Gtk::TreeView type_view; Glib::RefPtr type_store; Gtk::ScrolledWindow field_scroll; Gtk::TreeView field_view; Glib::RefPtr field_store; Gtk::VBox sidebar; DependencieGraph graph; // // Render sidebar // inline void clearSidebar() { auto tmp = this->sidebar.get_children(); for(auto& i : tmp) this->sidebar.remove(*i); } inline Gtk::Label* createLabel(const std::string& text) { Gtk::Label* result = Gtk::manage(new Gtk::Label(text)); result->set_xalign(0); result->set_line_wrap(true); return result; }; inline void renderToolSidebar(const Tool& tool) { // Create base structure Gtk::VBox* typesSet; Gtk::VBox* fieldsSet; Gtk::VBox* typeSet; { this->clearSidebar(); this->sidebar.pack_start(*(createLabel(std::string("Name: ") + tool.getName())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Description:\n") + tool.getDescription())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Command-Line:\n") + tool.getCommand())), false, false); typesSet = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*typesSet, false, false); fieldsSet = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*fieldsSet, false, false); typeSet = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*typeSet, false, false); } // Add types { // TODO: Sort bool first = true; for(auto& i : tool.getStatesTypes()) if(std::get<1>(i.second) >= TYPE_STATE::UNUSED) { if(first) { first = false; typesSet->pack_start(*(createLabel(std::string(" "))), false, false); typesSet->pack_start(*(createLabel(std::string("Types:"))), false, false); } const char* state; switch(std::get<1>(i.second)) { case TYPE_STATE::UNUSED: state = "UNUSED"; break; case TYPE_STATE::READ: state = "READ"; break; case TYPE_STATE::WRITE: state = "WRITE"; break; case TYPE_STATE::DELETE: state = "DELETE"; break; default: throw; // That should never happen! } typesSet->pack_start(*(createLabel(i.first->getName() + " : " + state)), false, false); } } // Add fileds { // TODO: Sort bool first = true; for(auto& i : tool.getStatesFields()) for(auto& j : i.second) if(j.second > FIELD_STATE::NO) { if(first) { first = false; typeSet->pack_start(*(createLabel(std::string(" "))), false, false); typeSet->pack_start(*(createLabel(std::string("Fields:"))), false, false); } const char* state; switch(j.second) { case FIELD_STATE::UNUSED: state = "UNUSED"; break; case FIELD_STATE::READ: state = "READ"; break; case FIELD_STATE::WRITE: state = "WRITE"; break; case FIELD_STATE::CREATE: state = "CREATE"; break; default: throw; // That should never happen } } } this->sidebar.show_all(); } template inline void renderSidebarHints(const SOURCE& source, Gtk::VBox& target) { // Hints { bool first = true; for(auto& i : source.getHints()) { if(first) { first = false; target.pack_start(*(createLabel(std::string(" "))), false, false); target.pack_start(*(createLabel(std::string("Hints:"))), false, false); } std::string tmp = i.first; if(i.second.size() > 0) { tmp += "("; bool first2 = true; for(auto& j : i.second) { if(first2) first2 = false; else tmp += ", "; tmp += j; } tmp += ")"; } target.pack_start(*(createLabel(tmp)), false, false); } } // Restrictions { bool first = true; for(auto& i : source.getRestrictions()) { if(first) { first = false; target.pack_start(*(createLabel(std::string(" "))), false, false); target.pack_start(*(createLabel(std::string("Restrictions:"))), false, false); } std::string tmp = i.first; if(i.second.size() > 0) { tmp += "("; bool first2 = true; for(auto& j : i.second) { if(first2) first2 = false; else tmp += ", "; tmp += j; } tmp += ")"; } target.pack_start(*(createLabel(tmp)), false, false); } } } inline void renderTypeSidebar(const Type& type) { // Base structure Gtk::VBox* hints; Gtk::VBox* types; { this->clearSidebar(); this->sidebar.pack_start(*(createLabel(std::string("Name: ") + type.getName())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Description:\n") + type.getComment())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Kind: ") + type.getMetaTypeName())), false, false); hints = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*hints, false, false); types = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*types, false, false); } // Generate hints renderSidebarHints(type, *hints); // Used in tools { // TODO: Sort bool first = true; for(auto& i : this->transactions.getData().getTools()) { TYPE_STATE fs = i->getTypeTransitiveState(type); if(fs > TYPE_STATE::NO) { if(first) { first = false; types->pack_start(*(createLabel(std::string(" "))), false, false); types->pack_start(*(createLabel(std::string("Tools:"))), false, false); } const char* state; switch(fs) { case TYPE_STATE::UNUSED: state = "UNUSED"; break; case TYPE_STATE::READ: state = "READ"; break; case TYPE_STATE::WRITE: state = "WRITE"; break; case TYPE_STATE::DELETE: state = "DELETE"; break; default: throw; // That should never happen! } types->pack_start(*(createLabel(i->getName() + " : " + state)), false, false); } } } // Finalize this->sidebar.show_all(); } inline void renderFieldSidebar(const Field& field) { // Base structure Gtk::VBox* hints; Gtk::VBox* types; { this->clearSidebar(); this->sidebar.pack_start(*(createLabel(std::string("Name: ") + field.getName())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Description:\n") + field.getComment())), false, false); this->sidebar.pack_start(*(createLabel(std::string("Type: ") + field.printType())), false, false); hints = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*hints, false, false); types = Gtk::manage(new Gtk::VBox()); this->sidebar.pack_start(*types, false, false); } // Generate hints renderSidebarHints(field, *hints); // Used in tools { // TODO: Sort bool first = true; for(auto& i : this->transactions.getData().getTools()) { FIELD_STATE fs = i->getFieldTransitiveState(field); if(fs > FIELD_STATE::NO) { if(first) { first = false; types->pack_start(*(createLabel(std::string(" "))), false, false); types->pack_start(*(createLabel(std::string("Tools:"))), false, false); } const char* state; switch(fs) { case FIELD_STATE::UNUSED: state = "UNUSED"; break; case FIELD_STATE::READ: state = "READ"; break; case FIELD_STATE::WRITE: state = "WRITE"; break; case FIELD_STATE::CREATE: state = "CREATE"; break; default: throw; // That should never happen! } types->pack_start(*(createLabel(i->getName() + " : " + state)), false, false); } } } // Finalize this->sidebar.show_all(); } // // Cache // std::unordered_set __cache_used_type; std::unordered_set __cache_selected_type; std::unordered_set __cache_selected_tools; std::unordered_set __cache_used_tools; std::unordered_set __cache_used_field; std::unordered_set __cache_selected_fields; const Type* __current_type = nullptr; std::unordered_set getActiveTypes() { std::unordered_set result; for(auto& i : this->type_view.get_selection()->get_selected_rows()) { const Type* tmp = (*(this->type_store->get_iter(i)))[typeModel.data_type]; if(tmp != nullptr) result.insert(tmp); } return std::move(result); } std::unordered_set getActiveFields() { std::unordered_set result; for(auto& i : this->field_view.get_selection()->get_selected_rows()) { const Field* tmp = (*(this->field_store->get_iter(i)))[fieldModel.data_field]; if(tmp != nullptr) result.insert(tmp); } return std::move(result); } void genCacheTools() { this->__cache_selected_tools = {}; for(auto& i : this->tool_view.get_selection()->get_selected_rows()) { const Tool* tmp = (*(this->tool_store->get_iter(i)))[toolModel.data_tool]; if(tmp != nullptr) this->__cache_selected_tools.insert(tmp); } } void genCacheTypes() { this->__cache_selected_type = std::move(this->getActiveTypes()); } void genCacheFields() { this->__cache_selected_fields = std::move(this->getActiveFields()); } // // Helpers // bool blockChange = false; bool isToolActive(const Tool* tool) { // Check marked tools if(this->__cache_selected_tools.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_tools) { if(i == tool) { found = true; break; } } if(!found) return false; } // Check type if(this->__cache_selected_type.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_type) { if(tool->getTypeTransitiveState(*i) >= TYPE_STATE::READ) { found = true; break; } } if(!found) return false; } // Check field if(this->__cache_selected_fields.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_fields) { auto tmp = tool->getStatesFields().find(i); if(tmp != tool->getStatesFields().end()) { for(auto& j : tmp->second) if(j.second >= FIELD_STATE::READ) { found = true; break; } if(found) break; } } if(!found) return false; } return true; } bool isTypeActive(const Type* type) { // Check selected types if(this->__cache_selected_type.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_type) { if(i == type) { found = true; break; } } if(!found) return false; } // Check tools if(this->__cache_selected_tools.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_tools) { if(i->getTypeTransitiveState(*type) >= TYPE_STATE::READ) { found = true; break; } } if(!found) return false; } // Check field if(this->__cache_selected_fields.size() != 0) { bool found = false; for(auto& i : this->__cache_selected_fields) { for(auto& j : this->transactions.getData().getTools()) if(j->getFieldSetState(*i, *type) >= FIELD_STATE::READ) { found = true; break; } if(found) break; } if(!found) return false; } return true; } bool isFieldActive(const Field* field) { // Check tools if(this->__cache_selected_tools.size() > 0) { bool found = false; for(auto& i : this->__cache_selected_tools) { for(auto& j : i->getStatesFields()) { // TODO: Optimize if(j.first == field) for(auto& k : j.second) if(k.second >= FIELD_STATE::READ) { found = true; break; } if(found) break; } if(found) break; } if(!found) return false; } // Check types if(this->__cache_selected_type.size() > 0) { bool found = false; for(auto& i : this->transactions.getData().getTools()) { for(auto& j : i->getStatesFields()) { // TODO: Optimize if(j.first == field) for(auto& k : this->__cache_selected_type) { auto tmp = j.second.find(k); if(tmp != j.second.end()) if(tmp->second >= FIELD_STATE::READ) { found = true; break; } } if(found) break; } if(found) break; } if(!found) return false; } // Check fields if(this->__cache_selected_fields.size() > 0) { bool found = false; for(auto& i : this->__cache_selected_fields) if(i == field) { found = true; break; } if(!found) return false; } return true; } void updateToolData() { this->tool_view.get_selection()->unselect_all(); this->tool_store->clear(); this->tool_store->set_sort_column(toolModel.data_sort_name, Gtk::SortType::SORT_ASCENDING); // Empty selection { auto tmp = *(this->tool_store->append()); tmp[toolModel.data_name] = "-- NO SELECTION --"; tmp[toolModel.data_sort_name] = "a"; tmp[toolModel.data_active] = false; tmp[toolModel.data_used] = false; tmp[toolModel.data_tool] = nullptr; } // Add Tools for(auto& i : this->transactions.getData().getTools()) { // Checks if(this->hide_unused_tool.property_active().get_value() && this->__cache_used_tools.find(i) == this->__cache_used_tools.end()) continue; if(this->hide_inactive_tool.property_active().get_value() && !this->isToolActive(i)) continue; // Insert auto tmp = *(this->tool_store->append()); tmp[toolModel.data_name] = i->getName(); tmp[toolModel.data_sort_name] = "b_" + i->getName(); tmp[toolModel.data_active] = this->isToolActive(i); tmp[toolModel.data_used] = this->__cache_used_tools.find(i) != this->__cache_used_tools.end(); tmp[toolModel.data_tool] = const_cast(i); } // Select tools for(auto& i : this->tool_store->children()) if(this->__cache_selected_tools.find((*i)[toolModel.data_tool]) != this->__cache_selected_tools.end()) this->tool_view.get_selection()->select(i); } template void __genTypeItem(Gtk::TreeStore& tree, Gtk::TreeStore::Row& row, const Type* type) { // Checks if(this->hide_unused_type.property_active().get_value() && this->__cache_used_type.find(type) == this->__cache_used_type.end()) { for(auto& i : type->getSubTypes()) if(getSuper(*i) == type) this->__genTypeItem(tree, row, i); return; } if(this->hide_inactive_type.property_active().get_value() && !this->isTypeActive(type)) { for(auto& i : type->getSubTypes()) if(getSuper(*i) == type) this->__genTypeItem(tree, row, i); return; } // Insert Gtk::TreeStore::Row own; Gtk::TreeStore::iterator ownIter; { if constexpr(FIRST) ownIter = tree.append(); else ownIter = tree.append(row.children()); } own = *ownIter; own[typeModel.data_name] = type->getName() + " : " + type->getMetaTypeName(); own[typeModel.data_sort_name] = "b_" + type->getName(); own[typeModel.data_active] = this->isTypeActive(type); own[typeModel.data_used] = this->__cache_used_type.find(type) != this->__cache_used_type.end(); own[typeModel.data_type] = const_cast(type); for(auto& i : type->getSubTypes()) if(getSuper(*i) == type) this->__genTypeItem(tree, own, i); } void selectTypes(Gtk::TreeStore::iterator iter) { // Check entry if(this->__cache_selected_type.find((*iter)[typeModel.data_type]) != this->__cache_selected_type.end()) this->type_view.get_selection()->select(iter); // Update children for(auto& i : iter->children()) this->selectTypes(i); } void updateTypeData() { // Get scroll bar double x = this->type_scroll.get_hadjustment()->get_value(); double y = this->type_scroll.get_vadjustment()->get_value(); // Update entries this->type_view.get_selection()->unselect_all(); this->type_store->clear(); // TODO: Just clear when required. this->type_store->set_sort_column(typeModel.data_sort_name, Gtk::SortType::SORT_ASCENDING); //Add empty { auto tmp = *(this->type_store->append()); tmp[typeModel.data_name] = "-- NO SELECTION --"; tmp[typeModel.data_sort_name] = "a"; tmp[typeModel.data_active] = false; tmp[typeModel.data_used] = false; tmp[typeModel.data_type] = nullptr; } { Gtk::TreeStore::Row tmp; for(auto& i : this->transactions.getData().getBaseTypes()) this->__genTypeItem(*(this->type_store.get()), tmp, i); this->type_view.expand_all(); for(auto& i : this->type_store->children()) this->selectTypes(i); } // Set scroll bar this->type_scroll.get_hadjustment()->set_value(x); this->type_scroll.get_vadjustment()->set_value(y); } Gtk::TreeStore::Path __genFieldData(const Type* type, bool required, Gtk::TreeStore::Path* insertInto) { // Find local fields to show auto fields = getFields(*type); // Generate class iter const Type* parent = getSuper(*type); Gtk::TreeStore::iterator classIter; if(insertInto != nullptr) classIter = this->field_store->append(this->field_store->get_iter(*insertInto)->children()); // TODO: Check if is empty else if(parent != nullptr) { Gtk::TreeStore::Path parentPath = this->__genFieldData(parent, required | fields.size() > 0, nullptr); if(!required & fields.size() == 0) return {}; classIter = this->field_store->append(this->field_store->get_iter(parentPath)->children()); } else { if(!required & fields.size() == 0) return {}; classIter = this->field_store->append(); } // Set class info { auto tmp = *classIter; tmp[fieldModel.data_isUsable] = false; tmp[fieldModel.data_active] = false; tmp[fieldModel.data_used] = false; tmp[fieldModel.data_field] = nullptr; tmp[fieldModel.data_name] = type->getName() + " : " + type->getMetaTypeName(); tmp[fieldModel.data_sort_name] = "b_" + type->getName(); } // Add interfaces for(auto& i : getInterfaces(*type)) { Gtk::TreeStore::Path tmp = this->field_store->get_path(classIter); this->__genFieldData(i, false, &tmp); } // Show data for(auto& i : fields) { auto tmpIter = this->field_store->append(classIter->children()); auto tmp = *tmpIter; tmp[fieldModel.data_active] = this->isFieldActive(&i); tmp[fieldModel.data_isUsable] = true; tmp[fieldModel.data_field] = &i; tmp[fieldModel.data_name] = i.getName() + " : " + i.printType(); tmp[fieldModel.data_used] = this->__cache_used_field.find(&i) != this->__cache_used_field.end(); tmp[fieldModel.data_sort_name] = "c_" + i.getName(); } return this->field_store->get_path(classIter); } void selectFields(Gtk::TreeStore::iterator iter) { // Update this { const Field* field = (*iter)[fieldModel.data_field]; if(this->__cache_selected_fields.find(field) != this->__cache_selected_fields.end()) this->field_view.get_selection()->select(iter); } // Update children for(auto& i : iter->children()) this->selectFields(i); } void updateFieldData() { // Get scrollbar double x = this->field_scroll.get_hadjustment()->get_value(); double y = this->field_scroll.get_vadjustment()->get_value(); // TODO: Check filter this->field_view.get_selection()->unselect_all(); this->field_store->clear(); this->field_store->set_sort_column(fieldModel.data_sort_name, Gtk::SortType::SORT_ASCENDING); { // Add fiedls if(this->__current_type != nullptr) { // Empty slot { auto tmp = *(this->field_store->append()); tmp[fieldModel.data_active] = false; tmp[fieldModel.data_isUsable] = true; tmp[fieldModel.data_field] = nullptr; tmp[fieldModel.data_name] = "-- NO SELECTION --"; tmp[fieldModel.data_used] = false; tmp[fieldModel.data_sort_name] = "a"; } this->__genFieldData(this->__current_type, false, nullptr); } this->field_view.expand_all(); // Select for(auto& i : this->field_store->children()) this->selectFields(i); } // Set scrollbar this->field_scroll.get_hadjustment()->set_value(x); this->field_scroll.get_vadjustment()->set_value(y); } // // Generators // template static void genTreeView(Gtk::TreeView& tree_view, MODEL& tree_model, const TOGGLE_CALLBACK& toggle_callback) { auto tmp_used = Gtk::manage(new Gtk::CellRendererToggle()); tree_view.append_column("used", *tmp_used); tree_view.get_column(0)->add_attribute(tmp_used->property_active(), tree_model.data_used); tmp_used->signal_toggled().connect(toggle_callback); if constexpr(USED_CHECK) tree_view.get_column(0)->add_attribute(tmp_used->property_activatable(), tree_model.data_isUsable); auto tmp_active = Gtk::manage(new Gtk::CellRendererToggle()); tmp_active->set_activatable(false); tree_view.append_column("active", *tmp_active); tree_view.get_column(1)->add_attribute(tmp_active->property_active(), tree_model.data_active); tree_view.append_column("name", tree_model.data_name); tree_view.set_search_column(2); tree_view.set_expander_column(*(tree_view.get_column(2))); tree_view.expand_all(); tree_view.get_selection()->set_mode(Gtk::SelectionMode::SELECTION_MULTIPLE); } // // Events // void toggle_tool_used(const Glib::ustring& index) { auto tmp = *(this->tool_store->get_iter(index)); auto tmpInter = this->__cache_used_tools.find(static_cast(tmp[toolModel.data_tool])); if(tmpInter == this->__cache_used_tools.end()) { this->__cache_used_tools.insert(static_cast(tmp[toolModel.data_tool])); tmp[toolModel.data_used] = true; } else { this->__cache_used_tools.erase(tmpInter); tmp[toolModel.data_used] = false; } this->update_all(); } void toggle_type_used(const Glib::ustring& index) { auto tmp = *(this->type_store->get_iter(index)); auto tmpIter = this->__cache_used_type.find(static_cast(tmp[typeModel.data_type])); if(tmpIter == this->__cache_used_type.end()) { this->__cache_used_type.insert(static_cast(tmp[typeModel.data_type])); tmp[typeModel.data_used] = true; } else { this->__cache_used_type.erase(tmpIter); tmp[typeModel.data_used] = false; } this->update_all(); } void toggle_field_used(const Glib::ustring& index) { auto tmp = *(this->field_store->get_iter(index)); auto tmpIter = this->__cache_used_field.find(static_cast(tmp[fieldModel.data_field])); if(tmpIter == this->__cache_used_field.end()) { this->__cache_used_field.insert(static_cast(tmp[fieldModel.data_field])); tmp[fieldModel.data_used] = true; } else { this->__cache_used_field.erase(tmpIter); tmp[fieldModel.data_used] = false; } this->update_all(); } void update_all() { if(blockChange) return; blockChange = true; // Update views this->genCacheTools(); this->genCacheTypes(); this->genCacheFields(); this->updateToolData(); this->updateTypeData(); this->updateFieldData(); blockChange = false; } public: Overview(Transactions& transactions) : Gtk::VBox(), transactions(transactions) { // Init Stack this->stack.add(this->tool_paned, "Tool Overview", "Tool Overview"); this->stack.add(this->graph, "Tool Relations", "Tool Relations"); this->stack_switcher.set_stack(this->stack); { Gtk::Alignment* tmp = Gtk::manage(new Gtk::Alignment()); tmp->set(0.5, 0.5, 0, 0); tmp->add(this->stack_switcher); this->pack_start(*tmp, false, true); } this->pack_start(this->stack, true, true); // Init layout this->tool_paned.pack2(this->type_paned); { Gtk::VBox* tmp = Gtk::manage(new Gtk::VBox()); tmp->pack_start(this->hide_inactive_tool, false, true); this->hide_inactive_tool.signal_toggled().connect([this]() -> void { this->update_all(); }); tmp->pack_start(this->hide_unused_tool, false, true); this->hide_unused_tool.signal_toggled().connect([this]() -> void { this->update_all(); }); this->tool_scroll.add(this->tool_view); tmp->pack_start(this->tool_scroll, true, true); this->tool_paned.pack1(*tmp); } this->type_paned.pack2(this->field_paned); { Gtk::VBox* tmp = Gtk::manage(new Gtk::VBox()); tmp->pack_start(this->hide_inactive_type, false, true); this->hide_inactive_type.signal_toggled().connect([this]() -> void { this->update_all(); }); tmp->pack_start(this->hide_unused_type, false, true); this->hide_unused_type.signal_toggled().connect([this]() -> void { this->update_all(); }); this->type_scroll.add(this->type_view); tmp->pack_start(this->type_scroll, true, true); this->type_paned.pack1(*tmp); } { Gtk::VBox* tmp = Gtk::manage(new Gtk::VBox()); tmp->pack_start(this->hide_inactive_field, false, true); this->hide_inactive_field.signal_toggled().connect([this]() -> void { this->update_all(); }); tmp->pack_start(this->hide_unused_field, false, true); this->hide_unused_field.signal_toggled().connect([this]() -> void { this->update_all(); }); this->field_scroll.add(this->field_view); tmp->pack_start(this->field_scroll, true, true); this->field_paned.pack1(*tmp); } { Gtk::ScrolledWindow* tmp = Gtk::manage(new Gtk::ScrolledWindow()); tmp->add(this->sidebar); this->field_paned.pack2(*tmp); } // Trees this->tool_store = Gtk::TreeStore::create(toolModel); this->tool_view.set_model(this->tool_store); genTreeView(this->tool_view, toolModel, [this](Glib::ustring index) -> void { this->toggle_tool_used(index); }); this->tool_view.get_selection()->signal_changed().connect([this]() -> void { this->update_all(); }); this->tool_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) -> void { const Tool* tool = (*(this->tool_store->get_iter(path)))[toolModel.data_tool]; if(tool != nullptr) this->renderToolSidebar(*tool); }); this->type_store = Gtk::TreeStore::create(typeModel); this->type_view.set_model(this->type_store); genTreeView(this->type_view, typeModel, [this](Glib::ustring index) -> void { this->toggle_type_used(index); }); this->type_view.get_selection()->signal_changed().connect([this]() -> void { this->update_all(); }); this->type_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) -> void { const Type* type = (*(this->type_store->get_iter(path)))[typeModel.data_type]; if(type != nullptr) { this->__current_type = type; this->update_all(); this->renderTypeSidebar(*type); } }); this->field_store = Gtk::TreeStore::create(fieldModel); this->field_view.set_model(this->field_store); genTreeView(this->field_view, fieldModel, [this](Glib::ustring index) -> void { this->toggle_field_used(index); }); this->field_view.get_selection()->signal_changed().connect([this]() -> void { this->update_all(); }); this->field_view.signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) -> void { const Field* field = (*(this->field_store->get_iter(path)))[fieldModel.data_field]; if(field != nullptr) this->renderFieldSidebar(*field); }); // Content this->update_all(); // Change notification this->transactions.addChangeCallback([this]() -> void { this->update_all(); }); } }; namespace sirEdit::gui { Gtk::Widget* createOverview(Transactions& transactions) { return new Overview(transactions); } }