sirEdit/src/gui/overview.cpp

1117 lines
33 KiB
C++

#include <sirEdit/data/tools.hpp>
#include <sirEdit/data/serialize.hpp>
#include <gtkmm.h>
#include <unordered_set>
#include <sirEdit/main.hpp>
using namespace sirEdit::data;
//
// Dependencie graph
//
class ImageRender : public Gtk::Widget
{
private:
Cairo::RefPtr<Cairo::ImageSurface> __surface;
Glib::RefPtr<Gdk::Window> __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<Cairo::Context>& 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<Glib::ustring> data_name;
Gtk::TreeModelColumn<Glib::ustring> data_sort_name;
Gtk::TreeModelColumn<bool> data_used;
Gtk::TreeModelColumn<bool> data_active;
Gtk::TreeModelColumn<Tool*> 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<Glib::ustring> data_name;
Gtk::TreeModelColumn<Glib::ustring> data_sort_name;
Gtk::TreeModelColumn<bool> data_used;
Gtk::TreeModelColumn<bool> data_active;
Gtk::TreeModelColumn<Type*> 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<Glib::ustring> data_name;
Gtk::TreeModelColumn<Glib::ustring> data_sort_name;
Gtk::TreeModelColumn<bool> data_used;
Gtk::TreeModelColumn<bool> data_active;
Gtk::TreeModelColumn<Field*> data_field;
Gtk::TreeModelColumn<bool> 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<Gtk::TreeStore> tool_store;
Gtk::ScrolledWindow type_scroll;
Gtk::TreeView type_view;
Glib::RefPtr<Gtk::TreeStore> type_store;
Gtk::ScrolledWindow field_scroll;
Gtk::TreeView field_view;
Glib::RefPtr<Gtk::TreeStore> 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<class SOURCE>
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<const Type*> __cache_used_type;
std::unordered_set<const Type*> __cache_selected_type;
std::unordered_set<const Tool*> __cache_selected_tools;
std::unordered_set<const Tool*> __cache_used_tools;
std::unordered_set<const Field*> __cache_used_field;
std::unordered_set<const Field*> __cache_selected_fields;
const Type* __current_type = nullptr;
std::unordered_set<const Type*> getActiveTypes() {
std::unordered_set<const Type*> 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<const Field*> getActiveFields() {
std::unordered_set<const Field*> 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<Tool*>(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<bool FIRST>
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<FIRST>(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<FIRST>(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*>(type);
for(auto& i : type->getSubTypes())
if(getSuper(*i) == type)
this->__genTypeItem<false>(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<true>(*(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<bool USED_CHECK = false, class MODEL, class TOGGLE_CALLBACK>
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<const Tool*>(tmp[toolModel.data_tool]));
if(tmpInter == this->__cache_used_tools.end()) {
this->__cache_used_tools.insert(static_cast<const Tool*>(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<const Type*>(tmp[typeModel.data_type]));
if(tmpIter == this->__cache_used_type.end()) {
this->__cache_used_type.insert(static_cast<const Type*>(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<const Field*>(tmp[fieldModel.data_field]));
if(tmpIter == this->__cache_used_field.end()) {
this->__cache_used_field.insert(static_cast<const Field*>(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<true>(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);
}
}