From 078e5b38c9ffca52d830ab4fdceea0ef08225d1a Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 31 Aug 2024 00:47:04 -0400 Subject: [PATCH] preliminary implementation of Barista offline debugging environment for Mocha class libraries --- .gitignore | 5 + .../src/mocha-python/barista/BaristaEditor.py | 380 ++++++++++++++++++ .../mocha-python/barista/BaristaSettings.py | 21 + .../mocha-python/barista/BaristaToolbar.py | 142 +++++++ .../barista/BaseTreeViewFilter.py | 44 ++ .../src/mocha-python/barista/MainWindow.py | 218 ++++++++++ .../mocha-python/barista/TreeViewFilter.py | 89 ++++ .../mocha-python/barista/TreeViewFilter2.py | 128 ++++++ .../src/mocha-python/barista/barista.png | Bin 0 -> 12151 bytes .../src/mocha-python/barista/barista.py | 32 ++ mocha-python/src/mocha-python/barista/mocha | 1 + .../mocha-python/mocha/lib/InstanceCache.py | 1 + .../lib/mcx/dataformats/McxDataFormat.py | 144 +++++++ .../mocha/lib/mcx/dataformats/__init__.py | 1 + .../lib/mcx/objectmodels/McxAttribute.py | 33 ++ .../mocha/lib/mcx/objectmodels/McxInstance.py | 44 ++ .../lib/mcx/objectmodels/McxObjectModel.py | 39 ++ .../lib/mcx/objectmodels/McxRelationship.py | 36 ++ .../mocha/lib/mcx/objectmodels/McxSection.py | 3 + .../mocha/lib/mcx/objectmodels/__init__.py | 22 + 20 files changed, 1383 insertions(+) create mode 100644 mocha-python/src/mocha-python/barista/BaristaEditor.py create mode 100644 mocha-python/src/mocha-python/barista/BaristaSettings.py create mode 100644 mocha-python/src/mocha-python/barista/BaristaToolbar.py create mode 100644 mocha-python/src/mocha-python/barista/BaseTreeViewFilter.py create mode 100644 mocha-python/src/mocha-python/barista/MainWindow.py create mode 100644 mocha-python/src/mocha-python/barista/TreeViewFilter.py create mode 100644 mocha-python/src/mocha-python/barista/TreeViewFilter2.py create mode 100644 mocha-python/src/mocha-python/barista/barista.png create mode 100644 mocha-python/src/mocha-python/barista/barista.py create mode 120000 mocha-python/src/mocha-python/barista/mocha create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/McxDataFormat.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/__init__.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxAttribute.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxInstance.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxObjectModel.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxRelationship.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxSection.py create mode 100644 mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/__init__.py diff --git a/.gitignore b/.gitignore index 940f068..52b7aad 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,8 @@ # Python cache __pycache__ + +# Built Mocha Class Libraries / eXecutables +*.mcl +*.mcx + diff --git a/mocha-python/src/mocha-python/barista/BaristaEditor.py b/mocha-python/src/mocha-python/barista/BaristaEditor.py new file mode 100644 index 0000000..23fb89a --- /dev/null +++ b/mocha-python/src/mocha-python/barista/BaristaEditor.py @@ -0,0 +1,380 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from gi.repository import Gtk, Gdk, Pango + +from mocha.lib.mcx.objectmodels import McxObjectModel, McxInstance, McxAttribute, McxRelationship +from TreeViewFilter2 import TreeViewFilter2 +from BaristaSettings import BaristaSettings + +class BaristaEditor(Gtk.Box): + + def on_search(self, entry : Gtk.SearchEntry): + query = entry.get_text() + self.treeview_filter.update(query) + pass + + def get_selected_item(self): + select = self.tv.get_selection() + model, paths = select.get_selected_rows() + if paths is not None: + if len(paths) > 0: + item = self.treeview_filter.data[paths[0].to_string()] + return item + + return None + + def lookup_selected_inst_instance(self, args): + item = self.get_selected_item() + self.lookup_instance_key(str(item.get_instance_key())) + + def lookup_selected_inst_class(self, args): + item = self.get_selected_item() + self.lookup_instance_key('1$' + str(item.get_class_index())) + + def search_selected_inst_instance(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_instance_global_identifier())) + + def search_selected_inst_class(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_class_global_identifier())) + + def search_selected_attr_instance(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_source_instance().get_instance_global_identifier())) + + def search_selected_attr_attribute(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_attribute_instance().get_instance_global_identifier())) + + def search_selected_rel_instance(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_source_instance().get_instance_global_identifier())) + + def search_selected_rel_relationship(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_relationship_instance().get_instance_global_identifier())) + + def search_selected_rel_target(self, args): + item = self.get_selected_item() + self.txtSearch.set_text(str(item.get_target_instance().get_instance_global_identifier())) + + def lookup_selected_rel_source(self, args): + item = self.get_selected_item() + self.lookup_instance(item.get_source_instance()) + def lookup_selected_rel_relationship(self, args): + item = self.get_selected_item() + self.lookup_instance(item.get_relationship_instance()) + def lookup_selected_rel_target(self, args): + item = self.get_selected_item() + self.lookup_instance(item.get_target_instance()) + + def lookup_instance(self, inst : McxInstance): + gid = str(inst.get_instance_key()) + self.lookup_instance_key(gid) + def lookup_instance_key(self, key : str): + url = "https://" + self.parent.toolbar.get_selected_suv() + "/super/d/inst/" + key + ".htmld" + import subprocess + subprocess.run(["xdg-open", url]) + + def update(self): + self.treeview_filter.settings = self.settings + self.treeview_filter.update() + + def on_context_menu(self, widget : Gtk.TreeView): + item = self.get_selected_item() + if item is not None: + popup = Gtk.Menu() + + if isinstance(item, McxInstance): + + mnu = Gtk.MenuItem() + mnu.set_sensitive(False) + mnu.set_label("Instance: " + str(item.get_instance_global_identifier())) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Look up MADI Instance") + mnu.connect("activate", self.lookup_selected_inst_instance) + mnu.set_use_underline(True) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Look up MADI Parent Class") + mnu.connect("activate", self.lookup_selected_inst_class) + mnu.set_use_underline(True) + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Search Instance GUID") + mnu.set_use_underline(True) + mnu.connect("activate", self.search_selected_inst_instance) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Search Class GUID") + mnu.set_use_underline(True) + mnu.connect("activate", self.search_selected_inst_class) + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Source Instance GUID [" + str(item.get_instance_global_identifier()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Source Instance Key [" + str(item.get_instance_key()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + elif isinstance(item, McxAttribute): + mnu = Gtk.MenuItem() + mnu.set_sensitive(False) + mnu.set_label("Attribute") + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Search Source Instance") + mnu.set_use_underline(True) + mnu.connect("activate", self.search_selected_attr_instance) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Search Attribute Instance") + mnu.set_use_underline(True) + mnu.connect("activate", self.search_selected_attr_attribute) + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Source Instance GUID [" + str(item.get_source_instance().get_instance_global_identifier()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Source Instance Key [" + str(item.get_source_instance().get_instance_key()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Attribute Instance GUID [" + str(item.get_attribute_instance().get_instance_global_identifier()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Attribute Instance Key [" + str(item.get_attribute_instance().get_instance_key()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Copy Value [" + str(item.get_value()) + "]") + mnu.set_use_underline(True) + popup.add(mnu) + + elif isinstance(item, McxRelationship): + mnu = Gtk.MenuItem() + mnu.set_sensitive(False) + mnu.set_label("Relationship") + popup.add(mnu) + + popup.add(Gtk.SeparatorMenuItem()) + + mnu = Gtk.MenuItem() + mnu.set_label("_Source Instance") + mnu.set_submenu(Gtk.Menu()) + + mnu1 = Gtk.MenuItem() + mnu1.set_sensitive(False) + mnu1.set_label(str(item.get_source_instance())) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Look Up MADI Instance") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.lookup_selected_rel_source) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Search") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.search_selected_rel_instance) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance GUID [" + str(item.get_source_instance().get_instance_global_identifier()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance Key [" + str(item.get_source_instance().get_instance_key()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + popup.add(mnu) + + + mnu = Gtk.MenuItem() + mnu.set_label("_Relationship Instance") + mnu.set_submenu(Gtk.Menu()) + + mnu1 = Gtk.MenuItem() + mnu1.set_sensitive(False) + mnu1.set_label(str(item.get_relationship_instance())) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Look Up MADI Instance") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.lookup_selected_rel_relationship) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Search") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.search_selected_rel_relationship) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance GUID [" + str(item.get_relationship_instance().get_instance_global_identifier()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance Key [" + str(item.get_relationship_instance().get_instance_key()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + popup.add(mnu) + + mnu = Gtk.MenuItem() + mnu.set_label("_Target Instance") + mnu.set_submenu(Gtk.Menu()) + + mnu1 = Gtk.MenuItem() + mnu1.set_sensitive(False) + mnu1.set_label(str(item.get_target_instance())) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Look Up MADI Instance") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.lookup_selected_rel_target) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Search") + mnu1.set_use_underline(True) + mnu1.connect("activate", self.search_selected_rel_target) + mnu.get_submenu().add(mnu1) + + mnu.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance GUID [" + str(item.get_target_instance().get_instance_global_identifier()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + mnu1 = Gtk.MenuItem() + mnu1.set_label("_Copy Instance Key [" + str(item.get_target_instance().get_instance_key()) + "]") + mnu1.set_use_underline(True) + mnu.get_submenu().add(mnu1) + + popup.add(mnu) + + popup.show_all() + popup.popup_at_pointer() + + def on_button_press(self, widget : Gtk.TreeView, e : Gdk.EventButton): + if e.button == 3: + # why TF is this not automatic + self.on_context_menu(widget) + + def __init__(self, parent, om : McxObjectModel): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + self.row_items = dict() + self.parent = parent + self.settings = parent.settings + + sc = Gtk.Paned() + + self.txtSearch = Gtk.SearchEntry() + self.txtSearch.connect("search-changed", self.on_search) + self.txtSearch.show() + self.pack_start(self.txtSearch, False, False, 0) + + self.ts = Gtk.TreeStore(bool, Pango.Weight, str, str, str, str) + self.om = om + + self.tv = Gtk.TreeView() + + # self.treeview_filter = TreeViewFilter(self.tv, self.ts, 0, 1) + self.treeview_filter = TreeViewFilter2(om, self.tv, self.ts, 0, 1) + + col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=2, weight=1) + col.set_title("Source") + self.tv.append_column(col) + + col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=3, weight=1) + col.set_title("Parent / Attribute / Relationship") + self.tv.append_column(col) + + col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=4, weight=1) + col.set_title("Value / Target") + self.tv.append_column(col) + + col = Gtk.TreeViewColumn(cell_renderer=Gtk.CellRendererText(), text=5, weight=1) + col.set_title("Effective Date") + self.tv.append_column(col) + + # tv.set_model(ts) + self.tv.connect("popup-menu", self.on_context_menu) + self.tv.connect("button-press-event", self.on_button_press) + self.tv.show() + + tv_wrapper = Gtk.ScrolledWindow() + tv_wrapper.add(self.tv) + tv_wrapper.show() + + sc.add1(tv_wrapper) + sc.show() + + self.treeview_filter.settings = self.settings + self.treeview_filter.update("") + + self.pack_start(sc, True, True, 0) \ No newline at end of file diff --git a/mocha-python/src/mocha-python/barista/BaristaSettings.py b/mocha-python/src/mocha-python/barista/BaristaSettings.py new file mode 100644 index 0000000..9309d31 --- /dev/null +++ b/mocha-python/src/mocha-python/barista/BaristaSettings.py @@ -0,0 +1,21 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +class BaristaSettings: + + def __init__(self): + self.show_entity_definitions = False \ No newline at end of file diff --git a/mocha-python/src/mocha-python/barista/BaristaToolbar.py b/mocha-python/src/mocha-python/barista/BaristaToolbar.py new file mode 100644 index 0000000..1d5a592 --- /dev/null +++ b/mocha-python/src/mocha-python/barista/BaristaToolbar.py @@ -0,0 +1,142 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from gi.repository import Gtk, Gio, GLib + +class BaristaToolbar (Gtk.Box): + + def __init__(self, parent): + super().__init__(orientation=Gtk.Orientation.HORIZONTAL) + + self.suvs = [ + + ( "i-0708947a3b88c3f6.privatesuv.com", "i-0708947a3b88c3f6.privatesuv.com", False ), + ( " SPOT : ", "", False ), + ( "quovii.com", "quovii.com", True ) + + ] + self.suvs_menus = dict() + self.selected_suv = None + + i = 0 + for (k, v, running) in self.suvs: + act = Gio.SimpleAction.new("suv_menu_activate" + str(i)) + act.connect("activate", self.suv_menu_activate) + act.set_enabled(True) + parent.add_action(act) + self.suvs_menus[act] = (k, v, running) + i += 1 + + self.btn = Gtk.MenuButton() + + box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + menu1 = Gio.Menu() + menu2 = Gio.Menu() + + ico_network_server = Gio.ThemedIcon(name="network-server") + sel_suv = None + + i = -1 + for (k, v, running) in self.suvs: + i += 1 + if running: + if self.selected_suv is None: + sel_suv = (k, v, running) + + mnuSuv1 = Gio.MenuItem() + mnuSuv1.set_label(k) + mnuSuv1.set_action_and_target_value("win.suv_menu_activate" + str(i), None) + mnuSuv1.set_icon(ico_network_server) + + menu2.append_item(mnuSuv1) + + menu1.append_section("Running", menu2) + + menu2 = Gio.Menu() + i = -1 + for (k, v, running) in self.suvs: + i += 1 + if not running: + mnuSuv1 = Gio.MenuItem() + mnuSuv1.set_label(k) + mnuSuv1.set_action_and_target_value("win.suv_menu_activate" + str(i), None) + mnuSuv1.set_icon(ico_network_server) + + menu2.append_item(mnuSuv1) + + menu1.append_section("Custom", menu2) + + menu2 = Gio.Menu() + + m_ManageSUVConnections = Gio.MenuItem() + m_ManageSUVConnections.set_label("Manage SUV Connections") + menu2.append_item(m_ManageSUVConnections) + + m_RefreshSUVConnections = Gio.MenuItem() + m_RefreshSUVConnections.set_label("Refresh SUV Connections") + menu2.append_item(m_RefreshSUVConnections) + + menu1.append_section("Manage SUVs", menu2) + + self.btn.set_use_popover(True) + self.btn.set_menu_model(menu1) + + self.btn_label = Gtk.Label(label="Current SUV: SPOT:\t(Running)") + self.btn_label.set_padding(8, 0) + + btnp = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + btnp.pack_start(Gtk.Image.new_from_gicon(ico_network_server, Gtk.IconSize.BUTTON), False, False, 0) + btnp.pack_start(self.btn_label, True, True, 0) + btnp.pack_start(Gtk.Image.new_from_icon_name("go-down", Gtk.IconSize.MENU), False, False, 0) + + self.btn.add(btnp) + self.pack_end(self.btn, False, False, 0) + + # lbl = Gtk.Label("Current SUV: ") + # self.lm = Gtk.ListStore(str) + + # self.cbo = Gtk.ComboBox() + # text = Gtk.CellRendererText() + # self.cbo.pack_start(text, True) + # self.cbo.add_attribute(text, 'text', 0) + # self.cbo.set_model(self.lm) + + # for (k, v, running) in self.suvs: + # row = self.lm.append(None) + # self.lm.set_value(row, 0, k + "\t(Running)" if running else k) + + # self.cbo.set_active(0) + + # self.pack_end(self.cbo, False, False, 0) + # self.pack_end(lbl, False, False, 0) + + if not sel_suv is None: + self.set_selected_suv(sel_suv) + + def get_selected_suv(self) -> str: + if self.selected_suv is None: + return None + + return self.selected_suv[1] + + def set_selected_suv(self, suv): + self.selected_suv = suv + self.btn_label.set_label("Current SUV: " + suv[0] + ("\t(Running)" if suv[2] else "")) + + def suv_menu_activate(self, arg1, arg2): + self.set_selected_suv(self.suvs_menus[arg1]) \ No newline at end of file diff --git a/mocha-python/src/mocha-python/barista/BaseTreeViewFilter.py b/mocha-python/src/mocha-python/barista/BaseTreeViewFilter.py new file mode 100644 index 0000000..6ee3cc0 --- /dev/null +++ b/mocha-python/src/mocha-python/barista/BaseTreeViewFilter.py @@ -0,0 +1,44 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from gi.repository import Gtk + +class BaseTreeViewFilter : + def __init__(self, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int): + self._treeview = treeview + self._treestore = treestore + self.expand_by_default = False + self.show_subtrees_of_matches = False + self.col_visible = col_visible + self.col_weight = col_weight + self.col_start = 2 + + self.last_query = "" + self.last_query_case_sensitive = False + + def update(self, query : str = None, case_sensitive : bool = None): + if query is None: + query = self.last_query + if case_sensitive is None: + case_sensitive = self.last_query_case_sensitive + + self.update_internal(query, case_sensitive) + self.last_query = query + self.last_query_case_sensitive = case_sensitive + + def update_internal(self, query : str, case_sensitive : bool): + pass \ No newline at end of file diff --git a/mocha-python/src/mocha-python/barista/MainWindow.py b/mocha-python/src/mocha-python/barista/MainWindow.py new file mode 100644 index 0000000..969a59b --- /dev/null +++ b/mocha-python/src/mocha-python/barista/MainWindow.py @@ -0,0 +1,218 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from gi.repository import Gtk, Gdk, Pango + +from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat +from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel + +from BaristaEditor import BaristaEditor +from BaristaToolbar import BaristaToolbar +from BaristaSettings import BaristaSettings + +class MainWindow (Gtk.ApplicationWindow): + + def __init__(self): + super().__init__() + + self.settings = BaristaSettings() + self.settings.show_entity_definitions = False + self.current_editor = None + + from os.path import dirname + self.set_default_icon_from_file(dirname(__file__) + '/barista.png') + self.set_icon_name('barista') + + self.set_size_request(1000, 700) + self.set_title('Barista - []') + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + accel_group = Gtk.AccelGroup() + self.add_accel_group(accel_group) + + menu = Gtk.MenuBar() + Gtk.AccelMap.add_entry("/FileOpen", Gdk.KEY_O, Gdk.ModifierType.CONTROL_MASK) + Gtk.AccelMap.add_entry("/FileQuit", Gdk.KEY_Q, Gdk.ModifierType.CONTROL_MASK) + + mnuFile = Gtk.MenuItem() + mnuFile.set_label("_File") + mnuFile.set_use_underline(True) + mnuFile.set_submenu(Gtk.Menu()) + mnuFile.get_submenu().set_accel_group(accel_group) + + mnuFileOpen = Gtk.MenuItem() + mnuFileOpen.set_accel_path("/FileOpen") + mnuFileOpen.connect("activate", self.mnuFileOpen_activate) + mnuFileOpen.set_label("_Open") + mnuFileOpen.set_use_underline(True) + mnuFile.get_submenu().add(mnuFileOpen) + + mnuFileClose = Gtk.MenuItem() + mnuFileClose.connect("activate", self.mnuFileClose_activate) + mnuFileClose.set_label("_Close") + mnuFileClose.set_use_underline(True) + mnuFile.get_submenu().add(mnuFileClose) + + mnuFile.get_submenu().add(Gtk.SeparatorMenuItem()) + + mnuFileExit = Gtk.MenuItem() + mnuFileExit.set_accel_path("/FileQuit") + mnuFileExit.connect("activate", self.mnuFileExit_activate) + mnuFileExit.set_label("E_xit") + mnuFileExit.set_use_underline(True) + mnuFile.get_submenu().add(mnuFileExit) + + menu.add(mnuFile) + + mnuView = Gtk.MenuItem() + mnuView.set_label("_View") + mnuView.set_use_underline(True) + mnuView.set_submenu(Gtk.Menu()) + + mnuViewEntities = Gtk.CheckMenuItem() + mnuViewEntities.connect("activate", self.mnuViewEntities_activate) + mnuViewEntities.set_label("_Entity Definitions") + mnuViewEntities.set_use_underline(True) + mnuView.get_submenu().add(mnuViewEntities) + + menu.add(mnuView) + + mnuHelp = Gtk.MenuItem() + mnuHelp.set_submenu(Gtk.Menu()) + mnuHelp.set_label("_Help") + mnuHelp.set_use_underline(True) + + mnuHelpAbout = Gtk.MenuItem() + mnuHelpAbout.connect("activate", self.mnuHelpAbout_activate) + mnuHelpAbout.set_label("_About") + mnuHelpAbout.set_use_underline(True) + mnuHelp.get_submenu().add(mnuHelpAbout) + + menu.add(mnuHelp) + menu.show() + + box.pack_start(menu, False, False, 0) + + self.toolbar = BaristaToolbar(self) + box.pack_start(self.toolbar, False, False, 0) + + + self.tabs = Gtk.Notebook() + self.tabs.show() + + box.pack_start(self.tabs, True, True, 0) + + box.show() + + self.add(box) + + def create_editor_tab_label(self, filename : str): + from os.path import basename, dirname + + tab_label = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + label = Gtk.Label(label=basename(filename)) + label2 = Gtk.Label(label=dirname(filename)) + label2.set_sensitive(False) + label2.set_ellipsize(Pango.EllipsizeMode.MIDDLE) + + lst = Pango.AttrList() + lst.insert(Pango.attr_size_new_absolute(Pango.SCALE * 10)) + + label2.set_attributes(lst) + tab_label.pack_start(label, True, True, 0) + label.show() + + tab_label.pack_start(label2, True, True, 0) + label2.show() + + tab_label.show() + return tab_label + + def add_editor_tab(self, filename : str): + + f = open(filename, 'rb') + + df = McxDataFormat() + om = McxObjectModel() + df.load(om, f) + + print("barista: creating editor") + box = self.create_editor(om) + + self.tabs.add(box) + box.show() + + self.tabs.set_tab_label(box, self.create_editor_tab_label(filename)) + f.close() + + def create_editor(self, om : McxObjectModel) -> BaristaEditor: + box = BaristaEditor(self, om) + self.current_editor = box + self.current_editor.settings = self.settings + return box + + def mnuFileOpen_activate(self, args): + dlg = Gtk.FileChooserDialog(title="Open File", action=Gtk.FileChooserAction.OPEN) + dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, Gtk.ResponseType.OK) + + filtr = Gtk.FileFilter() + filtr.add_pattern("*.mcx") + filtr.add_pattern("*.mcl") + filtr.set_name("Mocha library or executable") + dlg.add_filter(filtr) + + ret = dlg.run() + filenames = dlg.get_filenames() + + dlg.destroy() + + if ret == Gtk.ResponseType.OK: + for filename in filenames: + self.add_editor_tab (filename) + + def mnuFileClose_activate(self, args): + if self.tabs.get_n_pages() <= 1: + self.close() + else: + pass + + def mnuFileExit_activate(self, args): + self.app.stop() + + def mnuViewEntities_activate(self, widget : Gtk.CheckMenuItem): + if widget.get_active(): + self.settings.show_entity_definitions = True + else: + self.settings.show_entity_definitions = False + + if not self.current_editor is None: + self.current_editor.update() + + def mnuHelpAbout_activate(self, args): + dlg = Gtk.AboutDialog() + + from os.path import dirname + dlg.set_title("About Barista") + dlg.set_program_name("Barista") + dlg.set_version("1.2") + dlg.set_license_type(Gtk.License.GPL_3_0) + dlg.set_authors([ "Michael Becker" ]) + dlg.set_copyright("Copyright ©2024 Mike Becker's Software") + dlg.set_comments("View and manipulate compiled Mocha class library packages") + dlg.run() + dlg.destroy() diff --git a/mocha-python/src/mocha-python/barista/TreeViewFilter.py b/mocha-python/src/mocha-python/barista/TreeViewFilter.py new file mode 100644 index 0000000..df59af5 --- /dev/null +++ b/mocha-python/src/mocha-python/barista/TreeViewFilter.py @@ -0,0 +1,89 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from BaseTreeViewFilter import BaseTreeViewFilter + +from gi.repository import Gtk, Pango + +class TreeViewFilter (BaseTreeViewFilter) : + def __init__(self, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int): + super().__init__(treeview, treestore, col_visible, col_weight) + + self.filter = self._treestore.filter_new() + self.filter.set_visible_column(self.col_visible) + self._treeview.set_model(self.filter) + + def _reset_row(self, model, path, iter, make_visible): + self._treestore.set_value(iter, self.col_weight, Pango.Weight.NORMAL) + self._treestore.set_value(iter, self.col_visible, make_visible) + + def update(self, query : str, case_sensitive : bool = False): + search_query = query.strip() + if not case_sensitive: + search_query = search_query.lower() + + if search_query == "": + self._treeview.freeze_child_notify() + + self._treestore.foreach(self._reset_row, True) + if self.expand_by_default: + self._treeview.expand_all() + # else: + # self._treeview.collapse_all() + + self._treeview.thaw_child_notify() + else: + # self._treestore.foreach(self._reset_row, False) + self._treeview.freeze_child_notify() + + self._treestore.foreach(self._show_matches, search_query, self.show_subtrees_of_matches) + # self._treeview.expand_all() + self._treeview.thaw_child_notify() + + self.filter.refilter() + + def _make_path_visible(self, model, iter): + "Make a row and its ancestors visible" + while iter: + self._treestore.set_value(iter, self.col_visible, True) + iter = model.iter_parent(iter) + + def _make_subtree_visible(self, model, iter): + "Make descendants of a row visible" + for i in range(model.iter_n_children(iter)): + subtree = model.iter_nth_child(iter, i) + if model.get_value(subtree, self.col_visible): + # Subtree already visible + continue + self._treestore.set_value(subtree, self.col_visible, True) + self.make_subtree_visible(model, subtree) + + def _show_matches(self, model, path, iter, search_query, show_subtrees_of_matches): + for i in range(self.col_start, self._treestore.get_n_columns()): + text = str(model.get_value(iter, i)).lower() + if search_query in text: + # Highlight direct match with bold + self._treestore.set_value(iter, self.col_weight, Pango.Weight.BOLD) + self._treestore.set_value(iter, self.col_visible, True) + # Propagate visibility change up + self._make_path_visible(model, iter) + if show_subtrees_of_matches: + # Propagate visibility change down + self._make_subtree_visible(model, iter) + else: + self._treestore.set_value(iter, self.col_weight, Pango.Weight.NORMAL) + self._treestore.set_value(iter, self.col_visible, False) diff --git a/mocha-python/src/mocha-python/barista/TreeViewFilter2.py b/mocha-python/src/mocha-python/barista/TreeViewFilter2.py new file mode 100644 index 0000000..3b85b8f --- /dev/null +++ b/mocha-python/src/mocha-python/barista/TreeViewFilter2.py @@ -0,0 +1,128 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from BaseTreeViewFilter import BaseTreeViewFilter + +from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat +from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel + +from gi.repository import Gtk, Pango + +from BaristaSettings import BaristaSettings + +class TreeViewFilter2 (BaseTreeViewFilter): + + def __init__(self, om : McxObjectModel, treeview : Gtk.TreeView, treestore : Gtk.TreeStore, col_visible : int, col_weight : int): + super().__init__(treeview, treestore, col_visible, col_weight) + treeview.set_model(treestore) + self.om = om + self.data = dict() + self.settings = BaristaSettings() + + def update_internal(self, query : str, case_sensitive : bool = False): + self._treestore.clear() + query = query.strip() + if not case_sensitive: + query = query.lower() + + row = self._treestore.append(None) + self.data[self._treestore.get_path(row).to_string()] = None + self._treestore.set_value(row, 2, 'Instances') + self._treestore.set_value(row, self.col_visible, True) + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + + print("barista: adding instances") + for item in self.om.get_instances(): + if not (query == "" or (query in str(item.get_instance_global_identifier())) or (query in str(item.get_class_global_identifier())) or (query in str(item.get_instance_key()))): + continue + + row2 = self._treestore.append(row) + self.data[self._treestore.get_path(row2).to_string()] = item + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + + gid = str(item.get_instance_global_identifier()) + print("gid in has_entity_value") + if self.settings.show_entity_definitions and self.om.has_entity_value(gid): + gid = self.om.get_entity_key(gid) + + cid = str(item.get_class_global_identifier()) + if self.settings.show_entity_definitions and self.om.has_entity_value(cid): + cid = self.om.get_entity_key(cid) + + self._treestore.set_value(row2, 2, gid + " [" + str(item.get_instance_key()) + "]") + self._treestore.set_value(row2, 3, cid) + + row = self._treestore.append(None) + self.data[self._treestore.get_path(row).to_string()] = None + self._treestore.set_value(row, 2, 'Attributes') + self._treestore.set_value(row, self.col_visible, True) + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + + print("barista: adding attributes") + for item in self.om.get_attributes(): + if not (query == "" or (query in str(item.get_source_instance())) or (query in str(item.get_attribute_instance())) or (query in str(item.get_value()).lower())): + continue + + s_gid = str(item.get_source_instance().get_instance_global_identifier()) if item.get_source_instance() is not None else "" + if self.settings.show_entity_definitions and self.om.has_entity_value(s_gid): + s_gid = self.om.get_entity_key(s_gid) + + a_gid = str(item.get_attribute_instance().get_instance_global_identifier()) if item.get_attribute_instance() is not None else "" + if self.settings.show_entity_definitions and self.om.has_entity_value(a_gid): + a_gid = self.om.get_entity_key(a_gid) + + row2 = self._treestore.append(row) + self.data[self._treestore.get_path(row2).to_string()] = item + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + self._treestore.set_value(row2, 2, s_gid) + self._treestore.set_value(row2, 3, a_gid) + self._treestore.set_value(row2, 4, str(item.get_value())) + + row = self._treestore.append(None) + self.data[self._treestore.get_path(row).to_string()] = None + self._treestore.set_value(row, 2, 'Relationships') + self._treestore.set_value(row, self.col_visible, True) + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + + rels = True + if rels: + if len(query) < 5: + return + print("barista: adding relationships") + for item in self.om.get_relationships(): + if not (query == "" or (query in str(item.get_source_instance())) or (query in str(item.get_relationship_instance())) or (query in str(item.get_target_instance()))): + continue + + s_gid = str(item.get_source_instance().get_instance_global_identifier()) if item.get_source_instance() is not None else "" + if self.settings.show_entity_definitions and self.om.has_entity_value(s_gid): + s_gid = self.om.get_entity_key(s_gid) + + r_gid = str(item.get_relationship_instance().get_instance_global_identifier()) if item.get_relationship_instance() is not None else "" + if self.settings.show_entity_definitions and self.om.has_entity_value(r_gid): + r_gid = self.om.get_entity_key(r_gid) + + t_gid = str(item.get_target_instance().get_instance_global_identifier()) if item.get_target_instance() is not None else "" + if self.settings.show_entity_definitions and self.om.has_entity_value(t_gid): + t_gid = self.om.get_entity_key(t_gid) + + row2 = self._treestore.append(row) + self.data[self._treestore.get_path(row2).to_string()] = item + self._treestore.set_value(row, self.col_weight, Pango.Weight.NORMAL) + self._treestore.set_value(row2, 2, s_gid) + self._treestore.set_value(row2, 3, r_gid) + self._treestore.set_value(row2, 4, t_gid) + diff --git a/mocha-python/src/mocha-python/barista/barista.png b/mocha-python/src/mocha-python/barista/barista.png new file mode 100644 index 0000000000000000000000000000000000000000..637a44e72c489dcd1c2a868d4b50ab5f1fe47e26 GIT binary patch literal 12151 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 a?f%%MjJZxPMEnaJ(AB)GQKDuW`hNggQ1Chc literal 0 HcmV?d00001 diff --git a/mocha-python/src/mocha-python/barista/barista.py b/mocha-python/src/mocha-python/barista/barista.py new file mode 100644 index 0000000..aa2ec05 --- /dev/null +++ b/mocha-python/src/mocha-python/barista/barista.py @@ -0,0 +1,32 @@ +from framework.desktop import DesktopApplication + +import gi +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk, Pango + +from mocha.lib.mcx.dataformats.McxDataFormat import McxDataFormat +from mocha.lib.mcx.objectmodels.McxObjectModel import McxObjectModel + +from MainWindow import MainWindow + +class Program (DesktopApplication): + + def __init__(self): + super().__init__('com.mochapowered.python.barista') + + def startup(self, args): + from gi.repository import GLib + GLib.set_prgname('barista') + + def activate(self, args): + + wnd = MainWindow() + wnd.app = self + self.app.add_window(wnd) + + wnd.show_all() + +if __name__ == '__main__': + + Program().start() \ No newline at end of file diff --git a/mocha-python/src/mocha-python/barista/mocha b/mocha-python/src/mocha-python/barista/mocha new file mode 120000 index 0000000..c1c2eed --- /dev/null +++ b/mocha-python/src/mocha-python/barista/mocha @@ -0,0 +1 @@ +../mocha \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/InstanceCache.py b/mocha-python/src/mocha-python/mocha/lib/InstanceCache.py index 0f15631..7e1b307 100644 --- a/mocha-python/src/mocha-python/mocha/lib/InstanceCache.py +++ b/mocha-python/src/mocha-python/mocha/lib/InstanceCache.py @@ -14,6 +14,7 @@ class InstanceCache: self.dbids_gid = dict() def add_instance(self, inst_key, inst_gid): + print ("registering instance key " + str(inst_key) + " = " + str(inst_gid)) self.inst_guids[inst_key] = inst_gid self.inst_indices[inst_gid] = inst_key diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/McxDataFormat.py b/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/McxDataFormat.py new file mode 100644 index 0000000..3514d84 --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/McxDataFormat.py @@ -0,0 +1,144 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of yaml2mcl. +# +# yaml2mcl is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# yaml2mcl is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with yaml2mcl. If not, see . + +from editor.core import ObjectModel, DataFormat, InvalidDataFormatException, ObjectModelNotSupportedException +from editor.core.io import Reader + +from io import FileIO + +from ..objectmodels import McxObjectModel, McxInstance, McxAttribute, McxRelationship + +class McxDataFormat (DataFormat): + + def __init__(self): + pass + + def get_signature(self) -> str: + return "MCX!" + + def load_internal(self, object_model : ObjectModel, stream : FileIO): + + if not isinstance(object_model, McxObjectModel): + raise ObjectModelNotSupportedException + + r = Reader(stream) + + # SectionFile header + signature = r.read_fixedstring(4) + if signature != self.get_signature(): + raise InvalidDataFormatException + + version = r.read_single() + if version != 2.0: + raise InvalidDataFormatException + + flags = r.read_int32() + section_count = r.read_int32() + guid = r.read_guid() + + sections = [ ] + + for i in range(0, section_count): + section_name = r.read_fixedstring(16).strip(' \0') + offset = r.read_int32() + length = r.read_int32() + length2 = r.read_int32() + itemcount = r.read_int32() + + print(' '+section_name) + sections.append((section_name, offset, length, length2, itemcount)) + + gids = [ ] + insts = dict() + strs = [ ] + + for (section_name, offset, length, length2, itemcount) in sections: + if section_name == 'GUIDTable': + print("mcx: reading GUID table") + r.get_stream().seek(offset) + for i in range(0, itemcount): + gids.append(r.read_guid()) + elif section_name == 'StringTable': + print("mcx: reading string table") + r.get_stream().seek(offset) + for i in range(0, itemcount): + strs.append(r.read_terminatedstring()) + elif section_name == 'Defs': + print("mcx: reading definitions") + r.get_stream().seek(offset) + for i in range(0, itemcount): + key = r.read_terminatedstring() + value = r.read_terminatedstring() + object_model.define_entity(key, value.lower()) + + for (section_name, offset, length, length2, itemcount) in sections: + if section_name == 'Instances': + print("mcx: reading instances") + r.get_stream().seek(offset) + for i in range(0, itemcount): + cgidi = r.read_int32() + cgid = gids[cgidi] + igidi = r.read_int32() + igid = gids[igidi] + ckey = r.read_int32() + ikey = r.read_int32() + + inst = McxInstance(cgid, igid, ckey, ikey) + insts[igid] = inst + object_model.get_instances().append(inst) + + elif section_name == 'Attributes': + print("mcx: reading attributes") + r.get_stream().seek(offset) + for i in range(0, itemcount): + srci = r.read_int32() + src = gids[srci] + + atti = r.read_int32() + att = gids[atti] + + vali = r.read_int32() + val = strs[vali] + + isrc = insts[src] if src in insts else None + iatt = insts[att] if att in insts else None + + object_model.get_attributes().append(McxAttribute(isrc, iatt, val)) + + elif section_name == 'Relationships': + print("mcx: reading relationships") + r.get_stream().seek(offset) + for i in range(0, itemcount): + srci = r.read_int32() + src = gids[srci] + + reli = r.read_int32() + rel = gids[reli] + + tgti = r.read_int32() + tgt = gids[tgti] + + isrc = insts[src] if src in insts else None + irel = insts[rel] if rel in insts else None + itgt = insts[tgt] if tgt in insts else None + + object_model.get_relationships().append(McxRelationship(isrc, irel, itgt)) + + + def save_internal(self, object_model : ObjectModel, stream : FileIO): + + pass \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/__init__.py b/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/__init__.py new file mode 100644 index 0000000..15ed30a --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/dataformats/__init__.py @@ -0,0 +1 @@ +from .McxDataFormat import McxDataFormat diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxAttribute.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxAttribute.py new file mode 100644 index 0000000..a83a08d --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxAttribute.py @@ -0,0 +1,33 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from .McxInstance import McxInstance + +class McxAttribute: + + def __init__(self, source_instance : McxInstance, attribute_instance : McxInstance, value : any): + + self._source_instance = source_instance + self._attribute_instance = attribute_instance + self._value = value + + def get_source_instance(self) -> McxInstance: + return self._source_instance + def get_attribute_instance(self) -> McxInstance: + return self._attribute_instance + def get_value(self): + return self._value \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxInstance.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxInstance.py new file mode 100644 index 0000000..aa2aaa1 --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxInstance.py @@ -0,0 +1,44 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from framework import Guid + +class McxInstance: + + def __init__(self, class_global_identifier : Guid, instance_global_identifier : Guid, class_index : int, instance_index : int): + + self._class_global_identifier = class_global_identifier + self._instance_global_identifier = instance_global_identifier + self._class_index = class_index + self._instance_index = instance_index + + def get_instance_global_identifier(self) -> Guid: + return self._instance_global_identifier + def get_class_global_identifier(self) -> Guid: + return self._class_global_identifier + + def get_class_index(self) -> int: + return self._class_index + + def get_instance_index(self) -> int: + return self._instance_index + + def get_instance_key(self) -> str: + return str(self._class_index) + '$' + str(self._instance_index) + + def __str__(self) -> str: + return str(self.get_instance_global_identifier()) + " [" + str(self.get_class_index()) + "$" + str(self.get_instance_index()) + "]" \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxObjectModel.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxObjectModel.py new file mode 100644 index 0000000..e545214 --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxObjectModel.py @@ -0,0 +1,39 @@ +from .McxInstance import McxInstance +from .McxAttribute import McxAttribute +from .McxRelationship import McxRelationship + +from editor.core import ObjectModel + +class McxObjectModel (ObjectModel): + + def __init__(self): + super().__init__() + self._instances = list[McxInstance]() + self._attributes = list[McxAttribute]() + self._relationships = list[McxRelationship]() + + self.__entityDefinitionsKeys = dict() + self.__entityDefinitionsValues = dict() + + def get_instances(self) -> list[McxInstance]: + return self._instances + + def get_attributes(self) -> list[McxAttribute]: + return self._attributes + + def get_relationships(self) -> list[McxRelationship]: + return self._relationships + + def define_entity(self, key : str, value : str): + self.__entityDefinitionsKeys[value] = key + self.__entityDefinitionsValues[key] = value + + def get_entity_key(self, value : str): + return self.__entityDefinitionsKeys[value] + def get_entity_value(self, key : str): + return self.__entityDefinitionsValues[key] + + def has_entity_key(self, key : str): + return key in self.__entityDefinitionsValues + def has_entity_value(self, value : str): + return value in self.__entityDefinitionsKeys diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxRelationship.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxRelationship.py new file mode 100644 index 0000000..ef54ece --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxRelationship.py @@ -0,0 +1,36 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from .McxInstance import McxInstance + +class McxRelationship: + + def __init__(self, source_instance : McxInstance, relationship_instance : McxInstance, target_instance : McxInstance): + + self._source_instance = source_instance + self._relationship_instance = relationship_instance + self._target_instance = target_instance + + def get_source_instance(self) -> McxInstance: + return self._source_instance + def get_relationship_instance(self) -> McxInstance: + return self._relationship_instance + def get_target_instance(self) -> McxInstance: + return self._target_instance + + def __str__(self) -> str: + return str(self._source_instance) + " : " + str(self._relationship_instance) + " = " + str(self._target_instance) \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxSection.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxSection.py new file mode 100644 index 0000000..029034f --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/McxSection.py @@ -0,0 +1,3 @@ +class McxSection : + + pass \ No newline at end of file diff --git a/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/__init__.py b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/__init__.py new file mode 100644 index 0000000..7e9319a --- /dev/null +++ b/mocha-python/src/mocha-python/mocha/lib/mcx/objectmodels/__init__.py @@ -0,0 +1,22 @@ +# Copyright (C) 2024 Michael Becker +# +# This file is part of mocha-python. +# +# mocha-python is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# mocha-python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with mocha-python. If not, see . + +from .McxAttribute import McxAttribute +from .McxInstance import McxInstance +from .McxObjectModel import McxObjectModel +from .McxRelationship import McxRelationship +from .McxSection import McxSection \ No newline at end of file