1117 lines
33 KiB
C++
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);
|
|
}
|
|
}
|