Merge branch 'master' of gitea.azcona-becker.net:mochapowered/mocha-python

This commit is contained in:
Michael Becker 2024-12-17 00:54:48 -05:00
commit bb25076564
24 changed files with 2754 additions and 0 deletions

5
.gitignore vendored
View File

@ -14,3 +14,8 @@
# Python cache
__pycache__
# Built Mocha Class Libraries / eXecutables
*.mcl
*.mcx

View File

@ -0,0 +1,387 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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 set_filename(self, filename : str):
self._filename = filename
def get_filename(self):
return self._filename
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 = 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._filename = None
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)

View File

@ -0,0 +1,21 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
class BaristaSettings:
def __init__(self):
self.show_entity_definitions = False

View File

@ -0,0 +1,173 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
from gi.repository import Gtk, Gio, GLib
class BaristaToolbar (Gtk.Box):
def __init__(self, parent):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.suvs = [ ]
cfg = parent.application.suv_manager_config
if cfg is not None:
if "suvs" in cfg:
for i in range(0, len(cfg["suvs"])):
suv = cfg["suvs"][i]
suv_id = ""
suv_title = ""
suv_url = ""
running = None
if "id" in suv:
suv_id = suv["id"]
if "title" in suv:
suv_title = suv["title"]
if "url" in suv:
suv_url = suv["url"]
if "external" in suv:
if suv["external"]:
running = True
if running is None:
import subprocess
out = ""
try:
out = subprocess.run(executable="mocha", args=["", "suv", "shell", suv_id,"cat /etc/mocha/suvstart"],capture_output=True,timeout=1).stdout
except subprocess.TimeoutExpired:
pass
if len(out) == 0:
running = False
else:
running = True
self.suvs.append((suv_title, suv_url, running))
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])

View File

@ -0,0 +1,44 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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

View File

@ -0,0 +1,252 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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
from framework.desktop import DesktopApplication
class MainWindow (Gtk.ApplicationWindow):
def __init__(self, app : DesktopApplication
):
super().__init__()
self.application = app
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)
box.pack_start(self.build_menu(), False, False, 0)
self.toolbar = BaristaToolbar(self)
box.pack_start(self.toolbar, False, False, 0)
self.tabs = Gtk.Notebook()
self.tabs.connect("switch-page", self.tabs_switch_page)
self.tabs.show()
box.pack_start(self.tabs, True, True, 0)
box.show()
self.add(box)
def tabs_switch_page(self, notebook : Gtk.Notebook, page : Gtk.Widget, page_num : int):
import os
filename = page.get_filename()
if filename is None:
return
filetitle = os.path.basename(filename)
self.set_title("Barista - [" + filetitle + "]")
self.current_editor = page
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 open_file(self, filename : str):
self.add_editor_tab (filename)
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)
box.set_filename(filename)
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 build_menu(self):
menu = Gtk.MenuBar()
Gtk.AccelMap.add_entry("<Barista-MainWindow>/FileOpen", Gdk.KEY_O, Gdk.ModifierType.CONTROL_MASK)
Gtk.AccelMap.add_entry("<Barista-MainWindow>/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(self.application.app_accel_group)
mnuFileOpen = Gtk.MenuItem()
mnuFileOpen.set_accel_path("<Barista-MainWindow>/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("<Barista-MainWindow>/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)
return menu
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.open_file(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()
def build_menu2(self):
menu = Gio.Menu()
mnuFile = Gio.MenuItem()
mnuFile.set_label("_File")
menu2 = Gio.Menu()
mnuQuit = Gio.MenuItem()
mnuQuit.set_label("_Quit")
menu2.append_item(mnuQuit)
mnuFile.set_submenu(menu2)
menu.append_item(mnuFile)
return menu

View File

@ -0,0 +1,89 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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)

View File

@ -0,0 +1,128 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,58 @@
from framework.desktop import DesktopApplication
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Gio, 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')
self.app_accel_group = Gtk.AccelGroup()
self.current_window = None
import json, os
from pathlib import Path
fs = str(Path.home()) + "/.config/mocha/barista/suv-manager.json"
if os.path.exists(fs):
self.suv_manager_config = json.load(open(fs))
else:
self.suv_manager_config = None
def startup(self, args):
from gi.repository import GLib
GLib.set_prgname('barista')
def wnd_focus(self, widget, direction_type):
self.current_window = widget
print("wndow focus")
def activate(self, args):
wnd = MainWindow(self)
wnd.connect("focus", self.wnd_focus)
wnd.add_accel_group(self.app_accel_group)
# menu = self.build_menu2()
# self.app.set_menubar(menu)
import sys
files = sys.argv[1:]
for file in files:
wnd.open_file(file)
wnd.app = self
self.app.add_window(wnd)
wnd.show_all()
if __name__ == '__main__':
Program().start()

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Name=Barista
Comment=Edit Mocha compiled library packages
Type=Application
Exec=/usr/bin/barista
Icon=barista
Terminal=false
StartupNotify=true
Categories=TextEditor;Development;GTK;
MimeType=application/x-mocha-mcx

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,618 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg2"
width="273.06668"
height="273.06668"
viewBox="0 0 273.06668 273.06668"
sodipodi:docname="application-x-mocha-executable.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs6">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient869"
id="radialGradient871"
cx="630.7251"
cy="412.48349"
fx="630.7251"
fy="412.48349"
r="416.05017"
gradientTransform="matrix(0.26955401,0,0,0.26944724,54.253379,44.625785)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient869">
<stop
style="stop-color:#ffc061;stop-opacity:1;"
offset="0"
id="stop865" />
<stop
style="stop-color:#804b00;stop-opacity:1;"
offset="1"
id="stop867" />
</linearGradient>
</defs>
<sodipodi:namedview
id="namedview4"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.3975955"
inkscape:cx="105.93947"
inkscape:cy="169.12778"
inkscape:window-width="1840"
inkscape:window-height="1011"
inkscape:window-x="80"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="g8" />
<g
inkscape:groupmode="layer"
inkscape:label="Image"
id="g8">
<image
width="273.06668"
height="273.06668"
preserveAspectRatio="none"
xlink:href="data:image/png;base64,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
"
id="image10"
style="mix-blend-mode:multiply;fill:#17e4cb;fill-opacity:0.9878124;fill-rule:evenodd" />
<path
style="mix-blend-mode:color;fill:#eecf52;fill-opacity:1;fill-rule:evenodd;stroke-width:6.04331;stop-color:#000000"
d="M 132.72269,240.04604 24.097908,130.62215 133.623,19.866513 242.09777,129.5826 Z"
id="path1257" />
<path
style="fill:url(#radialGradient871);fill-opacity:1;stroke-width:0.359405"
d="m 188.37888,255.85766 c -21.47988,-1.45516 -36.50821,-5.47164 -39.03601,-10.4328 -0.81438,-1.59833 -0.54897,-2.87007 0.91313,-4.37556 1.93711,-1.99458 7.88055,-4.67933 12.066,-5.45041 1.10529,-0.20364 1.17239,-0.17653 0.55303,0.22338 -2.87532,1.85654 -3.52476,4.03211 -1.71983,5.76134 2.07424,1.98724 8.43268,4.18275 15.80291,5.45658 11.70857,2.02366 30.92955,2.24706 44.66577,0.51917 6.91828,-0.87027 14.6666,-2.9629 17.82581,-4.8143 3.57662,-2.09605 3.56609,-4.13101 -0.0352,-6.80884 -1.04079,-0.77389 -0.24892,-0.67941 3.12624,0.373 13.97904,4.35876 14.24787,10.50809 0.64751,14.81184 -6.19541,1.96051 -13.05207,3.21149 -23.08947,4.21265 -6.58698,0.65701 -25.21397,0.96467 -31.71984,0.52395 z m 6.07352,-15.97052 c -10.09121,-1.5676 -18.57952,-6.72483 -23.65573,-14.37244 -1.00899,-1.52011 -1.71783,-2.1718 -2.84283,-2.61364 -6.19065,-2.43128 -13.26276,-7.00825 -17.67191,-11.43701 -3.46849,-3.48391 -5.11767,-5.93305 -6.58719,-9.78245 -0.81018,-2.12225 -0.92717,-2.98156 -0.94265,-6.92399 -0.0156,-4.13122 0.0574,-4.648 0.89588,-6.28959 1.0231,-2.00297 3.39146,-3.83822 5.85859,-4.53984 1.63405,-0.4647 7.40215,-0.59454 8.90614,-0.20048 0.90781,0.23786 0.95178,0.17835 1.1888,-1.60845 0.19886,-1.49906 0.51015,-2.11943 1.62074,-3.23002 l 1.37461,-1.37461 h 38.87589 c 42.19673,0 39.7695,-0.11487 41.71546,1.9748 0.85678,0.92007 0.89694,1.15954 0.86461,5.15568 -0.0533,6.59644 -1.64687,15.55862 -4.16399,23.41898 -4.31099,13.46225 -11.94516,23.58447 -21.39989,28.37436 -6.86023,3.47548 -15.6998,4.74377 -24.03653,3.4487 z m 26.09327,-14.4491 c 4.23119,-4.52314 7.39963,-10.16732 9.90323,-17.64137 1.94353,-5.80203 3.74266,-15.7146 3.75121,-20.66795 l 0.003,-1.70717 h -2.33613 -2.33614 v 5.37268 c 0,10.84659 -1.97017,19.84914 -6.16168,28.15541 -1.37175,2.71836 -4.23768,7.01087 -6.00876,8.99975 -1.29416,1.4533 -1.2961,1.4602 -0.19404,0.68959 0.61227,-0.42814 2.13297,-1.86857 3.37936,-3.20094 z m -54.05801,-13.66975 c -0.30407,-2.96792 -2.93666,-13.45462 -3.88314,-15.46819 -0.40219,-0.85557 -1.44251,-2.34672 -2.31185,-3.31363 -2.82956,-3.14713 -6.09007,-4.03591 -8.92192,-2.43201 -3.34177,1.89271 -4.06815,5.68135 -1.91622,9.99456 2.21835,4.44631 11.17659,12.51831 15.0239,13.53757 2.05891,0.54547 2.27686,0.29397 2.00923,-2.3183 z m 33.03279,-39.62949 c -8.45064,-2.23032 -14.03719,-5.9606 -16.39698,-10.94869 -2.6898,-5.6857 -0.25869,-9.58961 12.62323,-20.27056 15.08607,-12.5085 18.21656,-17.32523 15.33254,-23.59142 -2.01159,-4.37063 -6.23744,-8.59654 -12.97551,-12.97572 -3.33553,-2.16783 -10.97002,-6.204842 -13.10763,-6.931137 -0.67449,-0.229177 -1.1409,-0.502117 -1.03647,-0.606545 0.26947,-0.269487 6.55018,1.130074 11.44274,2.549851 19.36152,5.618491 29.32302,14.183571 26.76918,23.016591 -1.69348,5.85734 -7.156,11.55783 -23.36992,24.38807 -6.30378,4.98828 -10.15265,8.75712 -11.09594,10.86531 -1.32269,2.95604 0.14576,6.55865 3.82804,9.39183 2.00366,1.54161 6.6979,3.99311 9.06494,4.73401 1.38527,0.43361 2.50892,1.10448 1.77905,1.06218 -0.18779,-0.0104 -1.47355,-0.31858 -2.85727,-0.68377 z m -1.09221,-5.49619 c -2.39975,-2.76036 -3.04147,-5.65346 -1.82335,-8.22046 0.9325,-1.96506 5.42906,-6.19396 13.15861,-12.3753 13.16377,-10.52709 17.5314,-14.86082 20.03501,-19.87952 1.81163,-3.63153 2.00031,-6.32127 0.65046,-9.2726 -0.52735,-1.15298 -1.32165,-2.50067 -1.76513,-2.99486 -0.44348,-0.49416 -1.04083,-1.21425 -1.32744,-1.60017 -1.15053,-1.54907 1.20299,0.47763 2.7813,2.39508 7.16696,8.70699 3.30705,15.76985 -17.85836,32.6772 -14.51217,11.5926 -16.16407,14.03684 -12.93148,19.13412 1.32059,2.08235 0.83471,2.15448 -0.91962,0.13656 z m -20.99942,-5.35054 c -1.07649,-1.74461 -1.88188,-4.73494 -1.59882,-5.93629 0.89277,-3.78905 5.02116,-8.37781 14.88125,-16.5407 7.14151,-5.91226 10.02579,-8.64378 12.23158,-11.58366 2.8112,-3.74684 3.53869,-6.96654 2.28312,-10.10452 -0.9368,-2.34128 -0.28816,-2.06794 0.99793,0.42052 1.00335,1.94138 1.17979,2.65877 1.18021,4.79849 8.2e-4,3.05128 -0.84492,4.87709 -3.98493,8.60513 -1.92544,2.28603 -4.4275,4.53057 -16.35813,14.67459 -7.99655,6.79906 -10.73273,11.33611 -9.13816,15.15249 0.7464,1.78641 0.46911,2.0749 -0.49405,0.51395 z"
id="path1482"
sodipodi:nodetypes="ssssssssssssssssssssccsssscssssssssscccssssssssssssssssssssssssssssssssssssssssssssscsssss" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 47 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1 @@
../mocha

View File

@ -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

View File

@ -0,0 +1,144 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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

View File

@ -0,0 +1 @@
from .McxDataFormat import McxDataFormat

View File

@ -0,0 +1,33 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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

View File

@ -0,0 +1,44 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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()) + "]"

View File

@ -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

View File

@ -0,0 +1,36 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
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)

View File

@ -0,0 +1,3 @@
class McxSection :
pass

View File

@ -0,0 +1,22 @@
# Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
#
# 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 <https://www.gnu.org/licenses/>.
from .McxAttribute import McxAttribute
from .McxInstance import McxInstance
from .McxObjectModel import McxObjectModel
from .McxRelationship import McxRelationship
from .McxSection import McxSection