mirror of
https://gitlab.gnome.org/jpu/cambalache.git
synced 2025-06-25 00:02:51 -04:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
53062b7d3c | ||
|
9aaace82f7 | ||
|
bbb2ce2bf9 | ||
|
dd90c3fc2b | ||
|
8742945f3f | ||
|
06c62349a7 | ||
|
bc6163b1ac | ||
|
ac74e23fde | ||
|
2e29c016f7 | ||
|
b572a7eae3 | ||
|
f96d19ab64 | ||
|
680f9d3243 | ||
|
6fa7f22c9c | ||
|
6ac16ca8c0 | ||
|
e2dbb15a18 | ||
|
c57891a3c8 | ||
|
ccdf5d6ef0 | ||
|
bb39873cd5 | ||
|
9529bfaf76 | ||
|
13db1c20c2 | ||
|
963cee5eec | ||
|
08a73f9c28 | ||
|
02935555bf | ||
|
befb056d2c | ||
|
9cd6e05d5f | ||
|
57a3f3832a | ||
|
8c80fc5d3d |
13
.local.env
13
.local.env
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
SCRIPT=$(readlink -f $0)
|
|
||||||
DIRNAME=$(dirname $SCRIPT)
|
|
||||||
ARCH_TRIPLET=$(cc -dumpmachine)
|
|
||||||
export LIBDIR=$DIRNAME/.local/lib/$ARCH_TRIPLET
|
|
||||||
export LD_LIBRARY_PATH=$LIBDIR:$LIBDIR/cambalache:$LIBDIR/cmb_catalog_gen:$LD_LIBRARY_PATH
|
|
||||||
export GI_TYPELIB_PATH=$LIBDIR/girepository-1.0:$LIBDIR/cambalache:$LIBDIR/cmb_catalog_gen:$GI_TYPELIB_PATH
|
|
||||||
export PKG_CONFIG_PATH=$LIBDIR/pkgconfig:$PKG_CONFIG_PATH
|
|
||||||
export GSETTINGS_SCHEMA_DIR=$DIRNAME/.local/share/glib-2.0/schemas:$GSETTINGS_SCHEMA_DIR
|
|
||||||
export XDG_DATA_DIRS=$DIRNAME/.local/share:$XDG_DATA_DIRS
|
|
||||||
export PYTHONPATH=$DIRNAME/.local/lib/python3/dist-packages:$PYTHONPATH
|
|
||||||
export PATH=$DIRNAME/.local/bin:$PATH
|
|
@ -152,7 +152,7 @@ cambalache under .local directoy and set up all environment variables needed to
|
|||||||
run the app from the source directory. (Follow manual installation to ensure
|
run the app from the source directory. (Follow manual installation to ensure
|
||||||
you have everything needed)
|
you have everything needed)
|
||||||
|
|
||||||
`./run-dev.sh`
|
`./run-dev.py`
|
||||||
|
|
||||||
This is meant for Cambalache development only.
|
This is meant for Cambalache development only.
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@
|
|||||||
{
|
{
|
||||||
"type" : "git",
|
"type" : "git",
|
||||||
"url" : "https://gitlab.gnome.org/jpu/casilda.git",
|
"url" : "https://gitlab.gnome.org/jpu/casilda.git",
|
||||||
"tag" : "0.2.0",
|
"tag" : "0.9.0",
|
||||||
"commit" : "99a0173f21345b85713198c1fa1fbb388d00182f"
|
"commit" : "4f7b1be321cf76832b12bda11fd91897257377e2"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -43,9 +43,12 @@ from cambalache import (
|
|||||||
notification_center,
|
notification_center,
|
||||||
config,
|
config,
|
||||||
utils,
|
utils,
|
||||||
_
|
_,
|
||||||
|
N_
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from cambalache.cmb_blueprint import CmbBlueprintError
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
GObject.type_ensure(CmbGResourceEditor.__gtype__)
|
GObject.type_ensure(CmbGResourceEditor.__gtype__)
|
||||||
@ -64,6 +67,7 @@ class CmbWindow(Adw.ApplicationWindow):
|
|||||||
gtk4_filter = Gtk.Template.Child()
|
gtk4_filter = Gtk.Template.Child()
|
||||||
gtk3_filter = Gtk.Template.Child()
|
gtk3_filter = Gtk.Template.Child()
|
||||||
gtk_builder_filter = Gtk.Template.Child()
|
gtk_builder_filter = Gtk.Template.Child()
|
||||||
|
blueprint_filter = Gtk.Template.Child()
|
||||||
glade_filter = Gtk.Template.Child()
|
glade_filter = Gtk.Template.Child()
|
||||||
css_filter = Gtk.Template.Child()
|
css_filter = Gtk.Template.Child()
|
||||||
gresource_filter = Gtk.Template.Child()
|
gresource_filter = Gtk.Template.Child()
|
||||||
@ -136,7 +140,13 @@ class CmbWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
self.gtk4_import_filters = Gio.ListStore()
|
self.gtk4_import_filters = Gio.ListStore()
|
||||||
|
|
||||||
for filter in [self.gtk4_filter, self.gtk_builder_filter, self.css_filter, self.gresource_filter]:
|
for filter in [
|
||||||
|
self.gtk4_filter,
|
||||||
|
self.gtk_builder_filter,
|
||||||
|
self.blueprint_filter,
|
||||||
|
self.css_filter,
|
||||||
|
self.gresource_filter
|
||||||
|
]:
|
||||||
self.gtk4_import_filters.append(filter)
|
self.gtk4_import_filters.append(filter)
|
||||||
|
|
||||||
self.gtk3_import_filters = Gio.ListStore()
|
self.gtk3_import_filters = Gio.ListStore()
|
||||||
@ -1135,7 +1145,7 @@ class CmbWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
print("IMPORT", path, content_type)
|
print("IMPORT", path, content_type)
|
||||||
|
|
||||||
if content_type in ["application/x-gtk-builder", "application/x-glade"]:
|
if content_type in ["application/x-gtk-builder", "application/x-glade", "text/x-blueprint"]:
|
||||||
self.import_file(file.get_path())
|
self.import_file(file.get_path())
|
||||||
elif content_type == "text/css":
|
elif content_type == "text/css":
|
||||||
self.project.add_css(path)
|
self.project.add_css(path)
|
||||||
@ -1164,7 +1174,22 @@ class CmbWindow(Adw.ApplicationWindow):
|
|||||||
self.project.set_selection([gresource])
|
self.project.set_selection([gresource])
|
||||||
|
|
||||||
def __save(self):
|
def __save(self):
|
||||||
if self.project.save():
|
retval = False
|
||||||
|
|
||||||
|
try :
|
||||||
|
retval = self.project.save()
|
||||||
|
except CmbBlueprintError as e:
|
||||||
|
self.present_message_to_user(
|
||||||
|
_("Error saving project"),
|
||||||
|
secondary_text=N_(
|
||||||
|
"blueprintcompiler encounter the following error:",
|
||||||
|
"blueprintcompiler encounter the following errors:",
|
||||||
|
len(e.errors)
|
||||||
|
),
|
||||||
|
details=[str(e)]
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if retval:
|
||||||
self.__last_saved_index = self.project.history_index
|
self.__last_saved_index = self.project.history_index
|
||||||
self.__update_action_save()
|
self.__update_action_save()
|
||||||
self.emit("project-saved", self.project)
|
self.emit("project-saved", self.project)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<!-- Created with Cambalache 0.95.0 -->
|
<!-- Created with Cambalache 0.97.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<!-- interface-name cmb_window.ui -->
|
<!-- interface-name cmb_window.ui -->
|
||||||
<!-- interface-copyright Juan Pablo Ugarte -->
|
<!-- interface-copyright Juan Pablo Ugarte -->
|
||||||
@ -836,6 +836,9 @@
|
|||||||
<object class="GtkFileFilter" id="glade_filter">
|
<object class="GtkFileFilter" id="glade_filter">
|
||||||
<property name="mime-types">application/x-glade</property>
|
<property name="mime-types">application/x-glade</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkFileFilter" id="blueprint_filter">
|
||||||
|
<property name="mime-types">text/x-blueprint</property>
|
||||||
|
</object>
|
||||||
<object class="GtkFileFilter" id="css_filter">
|
<object class="GtkFileFilter" id="css_filter">
|
||||||
<property name="mime-types">text/css</property>
|
<property name="mime-types">text/css</property>
|
||||||
</object>
|
</object>
|
||||||
@ -844,6 +847,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<object class="GtkFileFilter" id="gtk4_filter">
|
<object class="GtkFileFilter" id="gtk4_filter">
|
||||||
<property name="mime-types">application/x-gtk-builder
|
<property name="mime-types">application/x-gtk-builder
|
||||||
|
text/x-blueprint
|
||||||
text/css</property>
|
text/css</property>
|
||||||
<property name="name">All supported files</property>
|
<property name="name">All supported files</property>
|
||||||
<property name="suffixes">gresource.xml</property>
|
<property name="suffixes">gresource.xml</property>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||||
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
|
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
|
||||||
<!-- Created with Cambalache 0.95.1 -->
|
<!-- Created with Cambalache 0.97.1 -->
|
||||||
<cambalache-project version="0.95.0" target_tk="gtk-4.0">
|
<cambalache-project version="0.96.0" target_tk="gtk-4.0">
|
||||||
<gresources filename="cambalache.gresource.xml" sha256="fdcf4cd517493f548aa4b4fe206ff7762cee9cdda7ec5a85a718b46eb1c4731b"/>
|
<gresources filename="cambalache.gresource.xml" sha256="fdcf4cd517493f548aa4b4fe206ff7762cee9cdda7ec5a85a718b46eb1c4731b"/>
|
||||||
<gresources filename="app/app.gresource.xml" sha256="3684aa78fce08d8e81d0907317214aeb179c5aea091dd0df405476b43e286941"/>
|
<gresources filename="app/app.gresource.xml" sha256="3684aa78fce08d8e81d0907317214aeb179c5aea091dd0df405476b43e286941"/>
|
||||||
<css filename="cambalache.css" priority="400" is_global="1"/>
|
<css filename="cambalache.css" priority="400" is_global="1"/>
|
||||||
@ -221,7 +221,10 @@
|
|||||||
<ui template-class="CmbContextMenu" filename="cmb_context_menu.ui" sha256="81eba3adf715348a5c03ef4cbc151eebd5d9aa8b5a14c5968232f68a61ae573c"/>
|
<ui template-class="CmbContextMenu" filename="cmb_context_menu.ui" sha256="81eba3adf715348a5c03ef4cbc151eebd5d9aa8b5a14c5968232f68a61ae573c"/>
|
||||||
<ui template-class="CmbDBInspector" filename="cmb_db_inspector.ui" sha256="4451cdb08d24bd4a802ea692c0ebb4ef46af13152984c0b435d29bf4eb7dab55"/>
|
<ui template-class="CmbDBInspector" filename="cmb_db_inspector.ui" sha256="4451cdb08d24bd4a802ea692c0ebb4ef46af13152984c0b435d29bf4eb7dab55"/>
|
||||||
<ui filename="app/cmb_shortcuts.ui" sha256="d7ac37fd2430788a9e210ed4bc84dcfeba5609bdcc801afb192bfd900c7a8883"/>
|
<ui filename="app/cmb_shortcuts.ui" sha256="d7ac37fd2430788a9e210ed4bc84dcfeba5609bdcc801afb192bfd900c7a8883"/>
|
||||||
<ui template-class="CmbFileButton" filename="control/cmb_file_button.ui" sha256="f859b4f85d7c80c1fef69b68ebb9129423d9c72fdb38d304132784f7361cbbfd"/>
|
<ui template-class="CmbFileButton" filename="control/cmb_file_button.ui" sha256="f859b4f85d7c80c1fef69b68ebb9129423d9c72fdb38d304132784f7361cbbfd">
|
||||||
|
<property id="dialog-title" type-id="gchararray" disable-inline-object="0" required="0" disabled="0"/>
|
||||||
|
<property id="use-open" type-id="gboolean" disable-inline-object="0" required="0" disabled="0"/>
|
||||||
|
</ui>
|
||||||
<ui template-class="CmbNotificationListView" filename="cmb_notification_list_view.ui" sha256="13622645038ef2aaa154f74cd300f9c0fa0dccf69d45d6c9376f9034e6ee57fb"/>
|
<ui template-class="CmbNotificationListView" filename="cmb_notification_list_view.ui" sha256="13622645038ef2aaa154f74cd300f9c0fa0dccf69d45d6c9376f9034e6ee57fb"/>
|
||||||
<ui template-class="CmbVersionNotificationView" filename="cmb_version_notification_view.ui" sha256="9a3ced46b90eb7e425d1c345853c4e8e908870c61c75475f7e20ce3c9ee8cec6"/>
|
<ui template-class="CmbVersionNotificationView" filename="cmb_version_notification_view.ui" sha256="9a3ced46b90eb7e425d1c345853c4e8e908870c61c75475f7e20ce3c9ee8cec6"/>
|
||||||
<ui template-class="CmbMessageNotificationView" filename="cmb_message_notification_view.ui" sha256="debeffd184e225d82ed29ac590654b8160363e8d5606366dc8acb3ff9840fee3"/>
|
<ui template-class="CmbMessageNotificationView" filename="cmb_message_notification_view.ui" sha256="debeffd184e225d82ed29ac590654b8160363e8d5606366dc8acb3ff9840fee3"/>
|
||||||
@ -253,7 +256,7 @@
|
|||||||
<signal id="placeholder-activated"/>
|
<signal id="placeholder-activated"/>
|
||||||
<signal id="placeholder-selected"/>
|
<signal id="placeholder-selected"/>
|
||||||
</ui>
|
</ui>
|
||||||
<ui template-class="CmbGResourceEditor" filename="cmb_gresource_editor.ui" sha256="46969468ae070bdd315ca4091869d0b8a9bcb24c10cde82208bfd7d36f39fdd0">
|
<ui template-class="CmbGResourceEditor" filename="cmb_gresource_editor.ui" sha256="2050887ef1c45facb6ebff14500214bb035e6808ca61d2a7d661e696d79026ca">
|
||||||
<requires>CmbFileButton</requires>
|
<requires>CmbFileButton</requires>
|
||||||
<requires>CmbEntry</requires>
|
<requires>CmbEntry</requires>
|
||||||
</ui>
|
</ui>
|
||||||
@ -261,13 +264,13 @@
|
|||||||
<requires>CmbFileButton</requires>
|
<requires>CmbFileButton</requires>
|
||||||
<requires>CmbSourceView</requires>
|
<requires>CmbSourceView</requires>
|
||||||
</ui>
|
</ui>
|
||||||
<ui template-class="CmbUIEditor" filename="cmb_ui_editor.ui" sha256="0e4e205a3737fa207406ce74f4d8d3fbb4a477409b8a7b425b3d53c41309b306">
|
<ui template-class="CmbUIEditor" filename="cmb_ui_editor.ui" sha256="70e272e2c6c499a5424c6019154cd8338d9edde1fa111ad592a1c104019bb7ee">
|
||||||
<requires>CmbTextBuffer</requires>
|
<requires>CmbTextBuffer</requires>
|
||||||
<requires>CmbFileButton</requires>
|
<requires>CmbFileButton</requires>
|
||||||
<requires>CmbEntry</requires>
|
<requires>CmbEntry</requires>
|
||||||
<requires>CmbToplevelChooser</requires>
|
<requires>CmbToplevelChooser</requires>
|
||||||
</ui>
|
</ui>
|
||||||
<ui template-class="CmbWindow" filename="app/cmb_window.ui" sha256="20a192093a13209e0add725f15922e4f09689dda08fbf0b3a3eedf5d9adf2efc">
|
<ui template-class="CmbWindow" filename="app/cmb_window.ui" sha256="df07e3e03b88f9b097ad7d65efa15923d339db83b9d4a7c015a312a71f8c9685">
|
||||||
<requires>CmbNotificationListView</requires>
|
<requires>CmbNotificationListView</requires>
|
||||||
<requires>CmbScrolledWindow</requires>
|
<requires>CmbScrolledWindow</requires>
|
||||||
<requires>CmbObjectEditor</requires>
|
<requires>CmbObjectEditor</requires>
|
||||||
|
86
cambalache/cmb_blueprint.py
Normal file
86
cambalache/cmb_blueprint.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#
|
||||||
|
# Blueprint compiler integration functions
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Juan Pablo Ugarte
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation;
|
||||||
|
# version 2.1 of the License.
|
||||||
|
#
|
||||||
|
# library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-only
|
||||||
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
|
try:
|
||||||
|
import blueprintcompiler as bp
|
||||||
|
from blueprintcompiler import parser, tokenizer
|
||||||
|
from blueprintcompiler.decompiler import decompile_string
|
||||||
|
from blueprintcompiler.outputs import XmlOutput
|
||||||
|
except Exception:
|
||||||
|
bp = None
|
||||||
|
|
||||||
|
|
||||||
|
class CmbBlueprintError(Exception):
|
||||||
|
def __init__(self, message, errors=[]):
|
||||||
|
super().__init__(message)
|
||||||
|
self.errors = errors
|
||||||
|
|
||||||
|
|
||||||
|
class CmbBlueprintUnsupportedError(CmbBlueprintError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CmbBlueprintMissingError(CmbBlueprintError):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("blueprintcompiler is not available")
|
||||||
|
|
||||||
|
|
||||||
|
def cmb_blueprint_decompile(data: str) -> str:
|
||||||
|
if bp is None:
|
||||||
|
raise CmbBlueprintMissingError()
|
||||||
|
|
||||||
|
try:
|
||||||
|
retval = decompile_string(data)
|
||||||
|
except bp.decompiler.UnsupportedError as e:
|
||||||
|
raise CmbBlueprintUnsupportedError(str(e))
|
||||||
|
except Exception as e:
|
||||||
|
raise CmbBlueprintError(str(e))
|
||||||
|
|
||||||
|
return retval
|
||||||
|
|
||||||
|
|
||||||
|
def cmb_blueprint_compile(data: str) -> str:
|
||||||
|
if bp is None:
|
||||||
|
raise CmbBlueprintMissingError()
|
||||||
|
|
||||||
|
tokens = tokenizer.tokenize(data)
|
||||||
|
ast, errors, warnings = parser.parse(tokens)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
f = io.StringIO("")
|
||||||
|
errors.pretty_print("temp", data, f)
|
||||||
|
f.seek(0)
|
||||||
|
raise CmbBlueprintError(f.read(), errors=errors)
|
||||||
|
|
||||||
|
if ast is None:
|
||||||
|
raise CmbBlueprintError("AST is None")
|
||||||
|
|
||||||
|
# Ignore warnings
|
||||||
|
|
||||||
|
retval = XmlOutput().emit(ast)
|
||||||
|
return retval.encode()
|
||||||
|
|
@ -82,6 +82,9 @@ class CmbDB(GObject.GObject):
|
|||||||
self.__history_commands = {}
|
self.__history_commands = {}
|
||||||
self.__table_column_mapping = {}
|
self.__table_column_mapping = {}
|
||||||
|
|
||||||
|
self._output_lowercase_boolean = False
|
||||||
|
self._output_use_enum_value = False
|
||||||
|
|
||||||
self.clipboard = []
|
self.clipboard = []
|
||||||
self.clipboard_ids = []
|
self.clipboard_ids = []
|
||||||
|
|
||||||
@ -2023,7 +2026,7 @@ class CmbDB(GObject.GObject):
|
|||||||
self.__unknown_tag(child, root, child.tag)
|
self.__unknown_tag(child, root, child.tag)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
prefix, = self.__node_get(child, "prefix")
|
prefix, = self.__node_get(child, ["prefix"])
|
||||||
|
|
||||||
resource_id = self.add_gresource("gresource", parent_id=gresource_id, gresource_prefix=prefix)
|
resource_id = self.add_gresource("gresource", parent_id=gresource_id, gresource_prefix=prefix)
|
||||||
|
|
||||||
@ -2372,6 +2375,7 @@ class CmbDB(GObject.GObject):
|
|||||||
|
|
||||||
value = None
|
value = None
|
||||||
value_node = None
|
value_node = None
|
||||||
|
pinfo = self.type_info.get(property_type_id, None)
|
||||||
|
|
||||||
is_inline_object = not disable_inline_object and self.target_tk == "gtk-4.0"
|
is_inline_object = not disable_inline_object and self.target_tk == "gtk-4.0"
|
||||||
|
|
||||||
@ -2399,6 +2403,10 @@ class CmbDB(GObject.GObject):
|
|||||||
value = obj_name
|
value = obj_name
|
||||||
elif property_type_id == "GBytes":
|
elif property_type_id == "GBytes":
|
||||||
value = etree.CDATA(val)
|
value = etree.CDATA(val)
|
||||||
|
elif self._output_lowercase_boolean and property_type_id == "gboolean":
|
||||||
|
value = "true" if utils.bool_from_string(val) else "false"
|
||||||
|
elif self._output_use_enum_value and pinfo and pinfo.parent_id == "enum":
|
||||||
|
value = str(pinfo.enum_get_value_as_string(val, use_nick=False))
|
||||||
else:
|
else:
|
||||||
value = val
|
value = val
|
||||||
|
|
||||||
@ -2445,16 +2453,16 @@ class CmbDB(GObject.GObject):
|
|||||||
node = E.signal(name=name, handler=handler)
|
node = E.signal(name=name, handler=handler)
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
utils.xml_node_set(node, "object", data)
|
|
||||||
|
|
||||||
# if object is set, swap defaults to True
|
# if object is set, swap defaults to True
|
||||||
if not swap:
|
if not swap:
|
||||||
utils.xml_node_set(node, "swapped", "no")
|
utils.xml_node_set(node, "swapped", "False")
|
||||||
|
|
||||||
|
utils.xml_node_set(node, "object", data)
|
||||||
elif swap:
|
elif swap:
|
||||||
utils.xml_node_set(node, "swapped", "yes")
|
utils.xml_node_set(node, "swapped", "True")
|
||||||
|
|
||||||
if after:
|
if after:
|
||||||
utils.xml_node_set(node, "after", "yes")
|
utils.xml_node_set(node, "after", "True")
|
||||||
obj.append(node)
|
obj.append(node)
|
||||||
self.__node_add_comment(node, comment)
|
self.__node_add_comment(node, comment)
|
||||||
|
|
||||||
@ -2518,6 +2526,7 @@ class CmbDB(GObject.GObject):
|
|||||||
owner_id,
|
owner_id,
|
||||||
) = row
|
) = row
|
||||||
|
|
||||||
|
pinfo = self.type_info.get(property_type_id, None)
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
# Ignore properties depending on metadata (Gtk4)
|
# Ignore properties depending on metadata (Gtk4)
|
||||||
@ -2537,6 +2546,15 @@ class CmbDB(GObject.GObject):
|
|||||||
if obj_name is None:
|
if obj_name is None:
|
||||||
continue
|
continue
|
||||||
value = obj_name
|
value = obj_name
|
||||||
|
elif self._output_lowercase_boolean and property_type_id == "gboolean":
|
||||||
|
value = "true" if utils.bool_from_string(val) else "false"
|
||||||
|
elif self._output_lowercase_boolean and property_type_id == "CmbBooleanUndefined":
|
||||||
|
if val == "undefined":
|
||||||
|
value = "undefined"
|
||||||
|
else:
|
||||||
|
value = "true" if utils.bool_from_string(val) else "false"
|
||||||
|
elif self._output_use_enum_value and pinfo and pinfo.parent_id == "enum":
|
||||||
|
value = str(pinfo.enum_get_value_as_string(val, use_nick=False))
|
||||||
else:
|
else:
|
||||||
value = val
|
value = val
|
||||||
|
|
||||||
@ -2746,7 +2764,7 @@ class CmbDB(GObject.GObject):
|
|||||||
for child in root:
|
for child in root:
|
||||||
node.append(child)
|
node.append(child)
|
||||||
|
|
||||||
def export_ui(self, ui_id, merengue=False, skip_version_comment=False):
|
def export_ui(self, ui_id, merengue=False):
|
||||||
c = self.conn.cursor()
|
c = self.conn.cursor()
|
||||||
|
|
||||||
c.execute("SELECT translation_domain, comment, template_id, custom_fragment FROM ui WHERE ui_id=?;", (ui_id,))
|
c.execute("SELECT translation_domain, comment, template_id, custom_fragment FROM ui WHERE ui_id=?;", (ui_id,))
|
||||||
@ -2759,7 +2777,6 @@ class CmbDB(GObject.GObject):
|
|||||||
|
|
||||||
node = E.interface()
|
node = E.interface()
|
||||||
|
|
||||||
if not skip_version_comment:
|
|
||||||
node.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
|
node.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
|
||||||
|
|
||||||
utils.xml_node_set(node, "domain", translation_domain)
|
utils.xml_node_set(node, "domain", translation_domain)
|
||||||
@ -3034,6 +3051,20 @@ class CmbDB(GObject.GObject):
|
|||||||
(ui_id, ) if parent_id is None else (ui_id, parent_id)
|
(ui_id, ) if parent_id is None else (ui_id, parent_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_gresource_children_position(self, gresource_id):
|
||||||
|
self.execute(
|
||||||
|
"""
|
||||||
|
UPDATE gresource SET position=new.position - 1
|
||||||
|
FROM (
|
||||||
|
SELECT row_number() OVER (PARTITION BY parent_id ORDER BY position) position, parent_id, gresource_id
|
||||||
|
FROM gresource
|
||||||
|
WHERE parent_id=?
|
||||||
|
) AS new
|
||||||
|
WHERE gresource.parent_id=new.parent_id AND gresource.gresource_id=new.gresource_id;
|
||||||
|
""",
|
||||||
|
(gresource_id, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Function used in SQLite
|
# Function used in SQLite
|
||||||
|
|
||||||
|
@ -36,6 +36,8 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
|||||||
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
|
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
self._last_known = None
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.connect("notify", self.__on_notify)
|
self.connect("notify", self.__on_notify)
|
||||||
@ -47,6 +49,13 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
|||||||
return f"CmbGResource<{self.resource_type}> id={self.gresource_id}"
|
return f"CmbGResource<{self.resource_type}> id={self.gresource_id}"
|
||||||
|
|
||||||
def __on_notify(self, obj, pspec):
|
def __on_notify(self, obj, pspec):
|
||||||
|
resource_type = self.resource_type
|
||||||
|
|
||||||
|
if (resource_type == "gresources" and pspec.name == "gresources-filename") or \
|
||||||
|
(resource_type == "gresource" and pspec.name == "gresource-prefix") or \
|
||||||
|
(resource_type == "file" and pspec.name == "file-filename"):
|
||||||
|
obj.notify("display-name")
|
||||||
|
|
||||||
self.project._gresource_changed(self, pspec.name)
|
self.project._gresource_changed(self, pspec.name)
|
||||||
|
|
||||||
@GObject.Property(type=CmbBaseGResource)
|
@GObject.Property(type=CmbBaseGResource)
|
||||||
@ -84,6 +93,34 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
|||||||
file_filename = self.file_filename
|
file_filename = self.file_filename
|
||||||
return file_filename if file_filename else _("Unnamed file {id}").format(id=self.gresource_id)
|
return file_filename if file_filename else _("Unnamed file {id}").format(id=self.gresource_id)
|
||||||
|
|
||||||
|
# GListModel helpers
|
||||||
|
def _save_last_known_parent_and_position(self):
|
||||||
|
self._last_known = (self.parent, self.position)
|
||||||
|
|
||||||
|
def _update_new_parent(self):
|
||||||
|
parent = self.parent
|
||||||
|
position = self.position
|
||||||
|
|
||||||
|
# Emit GListModel signal to update model
|
||||||
|
if parent:
|
||||||
|
parent.items_changed(position, 0, 1)
|
||||||
|
parent.notify("n-items")
|
||||||
|
|
||||||
|
self._last_known = None
|
||||||
|
|
||||||
|
def _remove_from_old_parent(self):
|
||||||
|
if self._last_known is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
parent, position = self._last_known
|
||||||
|
|
||||||
|
# Emit GListModel signal to update model
|
||||||
|
if parent:
|
||||||
|
parent.items_changed(position, 1, 0)
|
||||||
|
parent.notify("n-items")
|
||||||
|
|
||||||
|
self._last_known = None
|
||||||
|
|
||||||
# GListModel iface
|
# GListModel iface
|
||||||
def do_get_item(self, position):
|
def do_get_item(self, position):
|
||||||
gresource_id = self.gresource_id
|
gresource_id = self.gresource_id
|
||||||
|
@ -221,6 +221,7 @@
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="CmbFileButton" id="file_filename">
|
<object class="CmbFileButton" id="file_filename">
|
||||||
|
<property name="use-open">True</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="column-span">1</property>
|
<property name="column-span">1</property>
|
||||||
|
@ -30,6 +30,7 @@ import http.client
|
|||||||
import time
|
import time
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
from uuid import uuid4
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from .config import VERSION
|
from .config import VERSION
|
||||||
from gi.repository import GObject, GLib, Gio, Gdk, Gtk, Adw, HarfBuzz
|
from gi.repository import GObject, GLib, Gio, Gdk, Gtk, Adw, HarfBuzz
|
||||||
@ -130,7 +131,7 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.retry_interval = 1
|
self.retry_interval = 2
|
||||||
self.user_agent = self.__get_user_agent()
|
self.user_agent = self.__get_user_agent()
|
||||||
self.store = Gio.ListStore(item_type=CmbNotification)
|
self.store = Gio.ListStore(item_type=CmbNotification)
|
||||||
self.settings = Gio.Settings(schema_id="ar.xjuan.Cambalache.notification")
|
self.settings = Gio.Settings(schema_id="ar.xjuan.Cambalache.notification")
|
||||||
@ -156,6 +157,10 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
logger.warning(f"{backend.scheme} is not supported, only HTTPS")
|
logger.warning(f"{backend.scheme} is not supported, only HTTPS")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Ensure we have a UUID
|
||||||
|
if not self.uuid:
|
||||||
|
self.uuid = str(uuid4())
|
||||||
|
|
||||||
logger.info(f"User Agent: {self.user_agent}")
|
logger.info(f"User Agent: {self.user_agent}")
|
||||||
logger.info(f"UUID: {self.uuid}")
|
logger.info(f"UUID: {self.uuid}")
|
||||||
|
|
||||||
@ -248,9 +253,6 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
def __get_notification_idle(self, data):
|
def __get_notification_idle(self, data):
|
||||||
logger.debug(f"Got notification response {json.dumps(data, indent=2, sort_keys=True)}")
|
logger.debug(f"Got notification response {json.dumps(data, indent=2, sort_keys=True)}")
|
||||||
|
|
||||||
if "uuid" in data:
|
|
||||||
self.uuid = data["uuid"]
|
|
||||||
|
|
||||||
if "notification" in data:
|
if "notification" in data:
|
||||||
notification = self.__notification_from_dict(data["notification"])
|
notification = self.__notification_from_dict(data["notification"])
|
||||||
self.store.insert(0, notification)
|
self.store.insert(0, notification)
|
||||||
@ -264,10 +266,10 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
return GLib.SOURCE_REMOVE
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
def __get_notification_thread(self):
|
def __get_notification_thread(self):
|
||||||
headers = {"User-Agent": self.user_agent}
|
headers = {
|
||||||
|
"User-Agent": self.user_agent,
|
||||||
if self.uuid:
|
"x-cambalache-uuid": self.uuid,
|
||||||
headers["x-cambalache-uuid"] = self.uuid
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"GET /notification {headers=}")
|
logger.info(f"GET /notification {headers=}")
|
||||||
@ -277,7 +279,7 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
|
|
||||||
# Reset retry interval
|
# Reset retry interval
|
||||||
self.retry_interval = 1
|
self.retry_interval = 8
|
||||||
|
|
||||||
data = response.read().decode()
|
data = response.read().decode()
|
||||||
|
|
||||||
@ -290,7 +292,7 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
self.retry_interval *= 2
|
self.retry_interval *= 2
|
||||||
self.retry_interval = min(self.retry_interval, 256)
|
self.retry_interval = min(self.retry_interval, 256)
|
||||||
|
|
||||||
logger.warning(f"Request error {e}, retrying in {self.retry_interval}s")
|
logger.info(f"Request error {e}, retrying in {self.retry_interval}s")
|
||||||
GLib.timeout_add_seconds(self.retry_interval, self._get_notification)
|
GLib.timeout_add_seconds(self.retry_interval, self._get_notification)
|
||||||
|
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
@ -318,19 +320,19 @@ class CmbNotificationCenter(GObject.GObject):
|
|||||||
def __poll_vote_idle(self, data):
|
def __poll_vote_idle(self, data):
|
||||||
logger.debug(f"Got vote response {data}")
|
logger.debug(f"Got vote response {data}")
|
||||||
|
|
||||||
uuid = data["uuid"]
|
poll_uuid = data["uuid"]
|
||||||
results = data["results"]
|
results = data["results"]
|
||||||
|
|
||||||
for notification in self.store:
|
for notification in self.store:
|
||||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
|
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
|
||||||
notification.results = CmbPollResult(**results)
|
notification.results = CmbPollResult(**results)
|
||||||
self.__save_notifications()
|
self.__save_notifications()
|
||||||
break
|
break
|
||||||
return GLib.SOURCE_REMOVE
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
def __poll_vote_exception_idle(self, uuid):
|
def __poll_vote_exception_idle(self, poll_uuid):
|
||||||
for notification in self.store:
|
for notification in self.store:
|
||||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
|
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
|
||||||
notification.my_votes = []
|
notification.my_votes = []
|
||||||
break
|
break
|
||||||
return GLib.SOURCE_REMOVE
|
return GLib.SOURCE_REMOVE
|
||||||
|
@ -458,11 +458,17 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
|
|||||||
def remove_data(self, data):
|
def remove_data(self, data):
|
||||||
try:
|
try:
|
||||||
assert data.get_id_string() in self.data_dict
|
assert data.get_id_string() in self.data_dict
|
||||||
|
|
||||||
|
self.project.history_push(
|
||||||
|
_("Remove {key} from {name}").format(key=data.info.key, name=self.display_name_type)
|
||||||
|
)
|
||||||
|
|
||||||
self.project.db.execute(
|
self.project.db.execute(
|
||||||
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
|
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
|
||||||
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
|
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
|
||||||
)
|
)
|
||||||
self.project.db.commit()
|
self.project.db.commit()
|
||||||
|
self.project.history_pop()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"{self} Error removing data {data}: {e}")
|
logger.warning(f"{self} Error removing data {data}: {e}")
|
||||||
return False
|
return False
|
||||||
|
@ -27,7 +27,7 @@ from gi.repository import GObject
|
|||||||
|
|
||||||
from .cmb_objects_base import CmbBaseObjectData
|
from .cmb_objects_base import CmbBaseObjectData
|
||||||
from .cmb_type_info import CmbTypeDataInfo
|
from .cmb_type_info import CmbTypeDataInfo
|
||||||
from cambalache import getLogger
|
from cambalache import getLogger, _
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@ -194,11 +194,17 @@ class CmbObjectData(CmbBaseObjectData):
|
|||||||
def remove_data(self, data):
|
def remove_data(self, data):
|
||||||
try:
|
try:
|
||||||
assert data in self.children
|
assert data in self.children
|
||||||
|
|
||||||
|
self.project.history_push(
|
||||||
|
_("Remove {key} from {name}").format(key=data.info.key, name=self.object.display_name_type)
|
||||||
|
)
|
||||||
|
|
||||||
self.project.db.execute(
|
self.project.db.execute(
|
||||||
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
|
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
|
||||||
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
|
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
|
||||||
)
|
)
|
||||||
self.project.db.commit()
|
self.project.db.commit()
|
||||||
|
self.project.history_pop()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"{self} Error removing data {data}: {e}")
|
logger.warning(f"{self} Error removing data {data}: {e}")
|
||||||
return False
|
return False
|
||||||
|
@ -31,6 +31,12 @@ from .control import cmb_create_editor
|
|||||||
from cambalache import _
|
from cambalache import _
|
||||||
|
|
||||||
|
|
||||||
|
# Everyone knows that debugging is twice as hard as writing a program in the first place.
|
||||||
|
# So if you’re as clever as you can be when you write it, how will you ever debug it?
|
||||||
|
# -- Brian Kernighan, 1974
|
||||||
|
#
|
||||||
|
# TODO: rewrite this!
|
||||||
|
|
||||||
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_object_data_editor.ui")
|
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_object_data_editor.ui")
|
||||||
class CmbObjectDataEditor(Gtk.Box):
|
class CmbObjectDataEditor(Gtk.Box):
|
||||||
__gtype_name__ = "CmbObjectDataEditor"
|
__gtype_name__ = "CmbObjectDataEditor"
|
||||||
@ -69,7 +75,7 @@ class CmbObjectDataEditor(Gtk.Box):
|
|||||||
def __on_remove_clicked(self, button):
|
def __on_remove_clicked(self, button):
|
||||||
if self.info:
|
if self.info:
|
||||||
self.object.remove_data(self.__data)
|
self.object.remove_data(self.__data)
|
||||||
else:
|
elif self.__data:
|
||||||
self.__data.parent.remove_data(self.__data)
|
self.__data.parent.remove_data(self.__data)
|
||||||
|
|
||||||
@GObject.Property(type=GObject.Object)
|
@GObject.Property(type=GObject.Object)
|
||||||
@ -138,7 +144,6 @@ class CmbObjectDataEditor(Gtk.Box):
|
|||||||
self.__update_view()
|
self.__update_view()
|
||||||
|
|
||||||
def __on_data_removed(self, obj, data):
|
def __on_data_removed(self, obj, data):
|
||||||
if self.object and self.info:
|
|
||||||
self.__remove_data_editor(data)
|
self.__remove_data_editor(data)
|
||||||
|
|
||||||
def __ensure_object_data(self, history_message):
|
def __ensure_object_data(self, history_message):
|
||||||
|
@ -260,7 +260,7 @@ It has to be exposed by your application with GtkBuilder expose_object method."
|
|||||||
hexpand=True,
|
hexpand=True,
|
||||||
object=obj,
|
object=obj,
|
||||||
data=data,
|
data=data,
|
||||||
info=None if data else info.data[data_key],
|
info=info.data[data_key],
|
||||||
)
|
)
|
||||||
|
|
||||||
grid.attach(editor, 0, i, 2, 1)
|
grid.attach(editor, 0, i, 2, 1)
|
||||||
|
@ -58,6 +58,9 @@ class CmbPath(CmbBase, Gio.ListModel):
|
|||||||
return self.__path_items.get(directory, None)
|
return self.__path_items.get(directory, None)
|
||||||
|
|
||||||
def add_item(self, item):
|
def add_item(self, item):
|
||||||
|
if item in self.__items:
|
||||||
|
return
|
||||||
|
|
||||||
display_name = item.display_name
|
display_name = item.display_name
|
||||||
is_path = isinstance(item, CmbPath)
|
is_path = isinstance(item, CmbPath)
|
||||||
|
|
||||||
@ -85,9 +88,13 @@ class CmbPath(CmbBase, Gio.ListModel):
|
|||||||
self.notify("display-name")
|
self.notify("display-name")
|
||||||
|
|
||||||
def remove_item(self, item):
|
def remove_item(self, item):
|
||||||
|
if item not in self.__items:
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(item, CmbPath) and item.path in self.__path_items:
|
if isinstance(item, CmbPath) and item.path in self.__path_items:
|
||||||
del self.__path_items[item.path]
|
del self.__path_items[item.path]
|
||||||
|
|
||||||
|
item.path_parent = None
|
||||||
i = self.__items.index(item)
|
i = self.__items.index(item)
|
||||||
self.__items.pop(i)
|
self.__items.pop(i)
|
||||||
self.items_changed(i, 1, 0)
|
self.items_changed(i, 1, 0)
|
||||||
|
@ -27,9 +27,10 @@ import os
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from gi.repository import GObject, Gio
|
from gi.repository import GObject, Gio, GLib
|
||||||
from graphlib import TopologicalSorter, CycleError
|
from graphlib import TopologicalSorter, CycleError
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
@ -49,6 +50,7 @@ from .cmb_layout_property import CmbLayoutProperty
|
|||||||
from .cmb_library_info import CmbLibraryInfo
|
from .cmb_library_info import CmbLibraryInfo
|
||||||
from .cmb_type_info import CmbTypeInfo
|
from .cmb_type_info import CmbTypeInfo
|
||||||
from .cmb_objects_base import CmbSignal
|
from .cmb_objects_base import CmbSignal
|
||||||
|
from .cmb_blueprint import cmb_blueprint_decompile, cmb_blueprint_compile
|
||||||
from .utils import FileHash
|
from .utils import FileHash
|
||||||
from . import constants, utils
|
from . import constants, utils
|
||||||
from cambalache import config, getLogger, _, N_
|
from cambalache import config, getLogger, _, N_
|
||||||
@ -276,7 +278,23 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
return root, relpath, hexdigest
|
return root, relpath, hexdigest
|
||||||
|
|
||||||
return None, None
|
return None, None, None
|
||||||
|
|
||||||
|
def __parse_blp_file(self, filename):
|
||||||
|
fullpath, relpath = self.__get_abs_path(filename)
|
||||||
|
|
||||||
|
with open(fullpath, "rb") as fd:
|
||||||
|
blueprint_decompiled = fd.read()
|
||||||
|
m = hashlib.sha256()
|
||||||
|
m.update(blueprint_decompiled)
|
||||||
|
hexdigest = m.hexdigest()
|
||||||
|
|
||||||
|
blueprint_compiled = cmb_blueprint_compile(blueprint_decompiled.decode())
|
||||||
|
root = etree.fromstring(blueprint_compiled)
|
||||||
|
|
||||||
|
return root, relpath, hexdigest
|
||||||
|
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
def __get_version_comment_from_root(self, root):
|
def __get_version_comment_from_root(self, root):
|
||||||
comment = root.getprevious()
|
comment = root.getprevious()
|
||||||
@ -287,6 +305,9 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
def __load_ui_from_node(self, node):
|
def __load_ui_from_node(self, node):
|
||||||
filename, sha256 = utils.xml_node_get(node, ["filename", "sha256"])
|
filename, sha256 = utils.xml_node_get(node, ["filename", "sha256"])
|
||||||
if filename:
|
if filename:
|
||||||
|
if filename.endswith(".blp"):
|
||||||
|
root, relpath, hexdigest = self.__parse_blp_file(filename)
|
||||||
|
else:
|
||||||
root, relpath, hexdigest = self.__parse_xml_file(filename)
|
root, relpath, hexdigest = self.__parse_xml_file(filename)
|
||||||
|
|
||||||
if sha256 != hexdigest:
|
if sha256 != hexdigest:
|
||||||
@ -525,15 +546,25 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
else:
|
else:
|
||||||
fullpath = filename
|
fullpath = filename
|
||||||
|
|
||||||
dirty = True
|
interface = root.getroot()
|
||||||
|
hexdigest = None
|
||||||
|
blueprint_decompiled = None
|
||||||
|
use_blp = filename.endswith(".blp")
|
||||||
|
|
||||||
|
if use_blp:
|
||||||
|
str_exported = etree.tostring(interface, pretty_print=True, encoding="UTF-8").decode("UTF-8")
|
||||||
|
blueprint_decompiled = cmb_blueprint_decompile(str_exported)
|
||||||
|
|
||||||
# Ensure directory exists
|
# Ensure directory exists
|
||||||
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
|
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
|
||||||
|
|
||||||
original_comment, original_hash = self.__file_state.get(filename, (None, None))
|
original_comment, original_hash = self.__file_state.get(filename, (None, None))
|
||||||
if original_comment is not None:
|
if original_comment is not None:
|
||||||
interface = root.getroot()
|
if use_blp:
|
||||||
|
m = hashlib.sha256()
|
||||||
|
m.update(blueprint_decompiled.encode())
|
||||||
|
hexdigest = m.hexdigest()
|
||||||
|
else:
|
||||||
comment = self.__get_version_comment_from_root(interface)
|
comment = self.__get_version_comment_from_root(interface)
|
||||||
new_comment = comment.text
|
new_comment = comment.text
|
||||||
comment.text = original_comment.text
|
comment.text = original_comment.text
|
||||||
@ -545,9 +576,12 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
hash_file.close()
|
hash_file.close()
|
||||||
|
|
||||||
comment.text = new_comment
|
comment.text = new_comment
|
||||||
dirty = original_hash != hexdigest
|
|
||||||
|
|
||||||
if dirty:
|
if original_hash is None or original_hash != hexdigest:
|
||||||
|
if use_blp:
|
||||||
|
with open(fullpath, "wb") as fd:
|
||||||
|
fd.write(blueprint_decompiled.encode())
|
||||||
|
else:
|
||||||
# Dump xml to file
|
# Dump xml to file
|
||||||
with open(fullpath, "wb") as fd:
|
with open(fullpath, "wb") as fd:
|
||||||
hash_file = FileHash(fd)
|
hash_file = FileHash(fd)
|
||||||
@ -631,8 +665,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
self.__save_xml_and_update_node(ui, root, filename)
|
self.__save_xml_and_update_node(ui, root, filename)
|
||||||
else:
|
else:
|
||||||
# Embed UI content in project as CDATA
|
# Embed UI content in project as CDATA
|
||||||
root = self.db.export_ui(ui_id, skip_version_comment=True)
|
root = self.db.export_ui(ui_id)
|
||||||
self.__save_xml_in_node(ui, root)
|
self.__save_xml_in_node(ui, root.getroot())
|
||||||
|
|
||||||
return ui
|
return ui
|
||||||
|
|
||||||
@ -655,8 +689,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
self.__save_xml_and_update_node(gresources, root, filename)
|
self.__save_xml_and_update_node(gresources, root, filename)
|
||||||
else:
|
else:
|
||||||
# Embed file contents in project as CDATA
|
# Embed file contents in project as CDATA
|
||||||
root = self.db.export_gresource(gresource_id, skip_version_comment=True)
|
root = self.db.export_gresource(gresource_id)
|
||||||
self.__save_xml_in_node(gresources, root)
|
self.__save_xml_in_node(gresources, root.getroot())
|
||||||
|
|
||||||
return gresources
|
return gresources
|
||||||
|
|
||||||
@ -774,7 +808,12 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
# Import file
|
# Import file
|
||||||
self.foreign_keys = False
|
self.foreign_keys = False
|
||||||
|
|
||||||
|
if filename.endswith(".blp"):
|
||||||
|
root, relpath, hexdigest = self.__parse_blp_file(filename)
|
||||||
|
else:
|
||||||
root, relpath, hexdigest = self.__parse_xml_file(filename)
|
root, relpath, hexdigest = self.__parse_xml_file(filename)
|
||||||
|
|
||||||
ui_id = self.db.import_from_node(root, relpath)
|
ui_id = self.db.import_from_node(root, relpath)
|
||||||
self.foreign_keys = True
|
self.foreign_keys = True
|
||||||
|
|
||||||
@ -999,33 +1038,41 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.history_pop()
|
self.history_pop()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
logger.warning("Tried to add GResource", exc_info=True)
|
||||||
return None
|
return None
|
||||||
else:
|
finally:
|
||||||
return self.__add_gresource(True, gresource_id, resource_type)
|
gresource = self.__add_gresource(True, gresource_id, resource_type)
|
||||||
|
gresource._update_new_parent()
|
||||||
|
return gresource
|
||||||
|
|
||||||
def __remove_gresource(self, gresource):
|
def __remove_gresource(self, gresource):
|
||||||
if gresource is None:
|
if gresource is None:
|
||||||
logger.warning("Tried to remove a None GResource", exc_info=True)
|
logger.warning("Tried to remove a None GResource", exc_info=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
print("__remove_gresource", gresource, self.__gresource_id.get(gresource.gresource_id, None))
|
|
||||||
|
|
||||||
self.__gresource_id.pop(gresource.gresource_id, None)
|
|
||||||
|
|
||||||
self.__selection_remove(gresource)
|
self.__selection_remove(gresource)
|
||||||
print("SELECTION", self.__selection)
|
self.__gresource_id.pop(gresource.gresource_id, None)
|
||||||
self.emit("gresource-removed", gresource)
|
self.emit("gresource-removed", gresource)
|
||||||
|
|
||||||
def remove_gresource(self, gresource):
|
def remove_gresource(self, gresource):
|
||||||
try:
|
try:
|
||||||
print("remove_gresource", gresource)
|
parent_id = gresource.parent_id
|
||||||
|
|
||||||
|
gresource._save_last_known_parent_and_position()
|
||||||
self.history_push(_('Remove GResource "{name}"').format(name=gresource.display_name))
|
self.history_push(_('Remove GResource "{name}"').format(name=gresource.display_name))
|
||||||
self.db.execute("DELETE FROM gresource WHERE gresource_id=?;", (gresource.gresource_id,))
|
self.db.execute("DELETE FROM gresource WHERE gresource_id=?;", (gresource.gresource_id,))
|
||||||
|
|
||||||
|
# Update position
|
||||||
|
if parent_id:
|
||||||
|
self.db.update_gresource_children_position(parent_id)
|
||||||
|
|
||||||
self.history_pop()
|
self.history_pop()
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.__remove_gresource(gresource)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error removing gresource {e}", exc_info=True)
|
logger.warning(f"Error removing gresource {e}", exc_info=True)
|
||||||
|
finally:
|
||||||
|
self.__remove_gresource(gresource)
|
||||||
|
gresource._remove_from_old_parent()
|
||||||
|
|
||||||
def get_css_providers(self):
|
def get_css_providers(self):
|
||||||
return list(self.__css_id.values())
|
return list(self.__css_id.values())
|
||||||
@ -1117,7 +1164,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error adding object {obj_name}: {e}")
|
logger.warning(f"Error adding object {obj_name}: {e}")
|
||||||
return None
|
return None
|
||||||
else:
|
finally:
|
||||||
obj = self.__add_object(True, ui_id, object_id, obj_type, name, parent_id, position=position)
|
obj = self.__add_object(True, ui_id, object_id, obj_type, name, parent_id, position=position)
|
||||||
obj._update_new_parent()
|
obj._update_new_parent()
|
||||||
return obj
|
return obj
|
||||||
@ -1283,10 +1330,17 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
obj._property_changed(p)
|
obj._property_changed(p)
|
||||||
|
|
||||||
def __undo_redo_do(self, undo, update_objects=None):
|
def __undo_redo_do(self, undo, update_objects=None):
|
||||||
def get_object_position(c, row):
|
def get_object_position(table, row):
|
||||||
|
if table == "object":
|
||||||
ui_id, parent_id, position = row[0], row[4], row[8]
|
ui_id, parent_id, position = row[0], row[4], row[8]
|
||||||
parent = self.get_object_by_id(ui_id, parent_id)
|
parent = self.get_object_by_id(ui_id, parent_id)
|
||||||
return parent, position
|
return parent, position
|
||||||
|
elif table == "gresource":
|
||||||
|
parent_id, position = row[2], row[3]
|
||||||
|
parent = self.get_gresource_by_id(parent_id)
|
||||||
|
return parent, position
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
c = self.db.cursor()
|
c = self.db.cursor()
|
||||||
|
|
||||||
@ -1303,8 +1357,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
# Undo or Redo command
|
# Undo or Redo command
|
||||||
if command == "INSERT":
|
if command == "INSERT":
|
||||||
if table == "object":
|
if table in ["object", "gresource"]:
|
||||||
parent, position = get_object_position(c, new_values)
|
parent, position = get_object_position(table, new_values)
|
||||||
|
|
||||||
if undo:
|
if undo:
|
||||||
update_objects.append((parent, position, 1, 0))
|
update_objects.append((parent, position, 1, 0))
|
||||||
@ -1318,8 +1372,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
self.__undo_redo_update_insert_delete(c, undo, command, table, columns, table_pk, old_values, new_values)
|
self.__undo_redo_update_insert_delete(c, undo, command, table, columns, table_pk, old_values, new_values)
|
||||||
elif command == "DELETE":
|
elif command == "DELETE":
|
||||||
if table == "object":
|
if table in ["object", "gresource"]:
|
||||||
parent, position = get_object_position(c, old_values)
|
parent, position = get_object_position(table, old_values)
|
||||||
|
|
||||||
if undo:
|
if undo:
|
||||||
update_objects.append((parent, position, 0, 1))
|
update_objects.append((parent, position, 0, 1))
|
||||||
@ -1335,8 +1389,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
elif command == "UPDATE":
|
elif command == "UPDATE":
|
||||||
# parent_id and position have to change together because their are part of a unique index
|
# parent_id and position have to change together because their are part of a unique index
|
||||||
if update_objects is not None and table == "object" and "position" in columns and "parent_id" in columns:
|
if update_objects is not None and table == "object" and "position" in columns and "parent_id" in columns:
|
||||||
old_parent, old_position = get_object_position(c, old_values)
|
old_parent, old_position = get_object_position(table, old_values)
|
||||||
new_parent, new_position = get_object_position(c, new_values)
|
new_parent, new_position = get_object_position(table, new_values)
|
||||||
|
|
||||||
if undo:
|
if undo:
|
||||||
if old_position >= 0:
|
if old_position >= 0:
|
||||||
@ -1348,6 +1402,9 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
update_objects.append((new_parent, new_position, 0, 1))
|
update_objects.append((new_parent, new_position, 0, 1))
|
||||||
if old_position >= 0:
|
if old_position >= 0:
|
||||||
update_objects.append((old_parent, old_position, 1, 0))
|
update_objects.append((old_parent, old_position, 1, 0))
|
||||||
|
elif table == "gresource":
|
||||||
|
# TODO
|
||||||
|
pass
|
||||||
|
|
||||||
if undo:
|
if undo:
|
||||||
self.db.history_update(table, columns, table_pk, old_values)
|
self.db.history_update(table, columns, table_pk, old_values)
|
||||||
@ -1476,12 +1533,27 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
else:
|
else:
|
||||||
obj._remove_data(data)
|
obj._remove_data(data)
|
||||||
else:
|
else:
|
||||||
parent = obj.data_dict.get(f"{row[2]}.{row[6]}", None)
|
owner_id, data_id, id, parent_id = row[2], row[3], row[4], row[6]
|
||||||
|
|
||||||
|
parent = obj.data_dict.get(f"{owner_id}.{parent_id}", None)
|
||||||
|
|
||||||
if parent:
|
if parent:
|
||||||
parent._add_child(row[2], row[3], row[4])
|
parent._add_child(owner_id, data_id, id)
|
||||||
else:
|
else:
|
||||||
obj._add_data(row[2], row[3], row[4])
|
info = self.type_info.get(owner_id)
|
||||||
|
taginfo = None
|
||||||
|
|
||||||
|
if info:
|
||||||
|
r = self.db.execute(
|
||||||
|
"SELECT key FROM type_data WHERE owner_id=? AND data_id=?;",
|
||||||
|
(owner_id, data_id)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
data_key = r[0] if r else None
|
||||||
|
if data_key:
|
||||||
|
taginfo = info.get_data_info(data_key)
|
||||||
|
|
||||||
|
obj._add_data(owner_id, data_id, id, info=taginfo)
|
||||||
elif table == "object_data_arg":
|
elif table == "object_data_arg":
|
||||||
obj = self.get_object_by_id(pk[0], pk[1])
|
obj = self.get_object_by_id(pk[0], pk[1])
|
||||||
if obj:
|
if obj:
|
||||||
@ -2208,6 +2280,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
item.path_parent = None
|
||||||
self.__items.insert(i, item)
|
self.__items.insert(i, item)
|
||||||
self.items_changed(i, 0, 1)
|
self.items_changed(i, 0, 1)
|
||||||
|
|
||||||
@ -2263,24 +2336,46 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
|||||||
|
|
||||||
path_parent = item.path_parent
|
path_parent = item.path_parent
|
||||||
|
|
||||||
|
# Do not do anything if the path is the same
|
||||||
|
if path_parent and path_parent.path and path_parent.path == os.path.dirname(filename):
|
||||||
|
return
|
||||||
|
|
||||||
# Remove item
|
# Remove item
|
||||||
self.__remove_item(item)
|
self.__remove_item(item)
|
||||||
# add it again
|
# add it again
|
||||||
self.__add_item(item, filename)
|
self.__add_item(item, filename)
|
||||||
|
|
||||||
if in_selection:
|
if in_selection:
|
||||||
self.set_selection([item])
|
GLib.idle_add(self.__set_selection_idle, item)
|
||||||
|
|
||||||
# Clear unused paths
|
# Clear unused paths
|
||||||
if path_parent.n_items == 0:
|
if path_parent and path_parent.n_items == 0:
|
||||||
|
GLib.idle_add(self.__clear_unused_paths_idle, path_parent)
|
||||||
|
|
||||||
|
def __set_selection_idle(self, item):
|
||||||
|
self.set_selection([item])
|
||||||
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
|
def __clear_unused_paths_idle(self, path_parent):
|
||||||
|
if path_parent.n_items:
|
||||||
|
return
|
||||||
|
|
||||||
while path_parent is not None:
|
while path_parent is not None:
|
||||||
next_parent = path_parent.path_parent
|
next_parent = path_parent.path_parent
|
||||||
|
|
||||||
if path_parent.n_items <= 1:
|
if path_parent.n_items != 1:
|
||||||
logger.warning(path_parent)
|
break
|
||||||
|
|
||||||
path_parent = next_parent
|
path_parent = next_parent
|
||||||
|
|
||||||
|
if path_parent:
|
||||||
|
if path_parent.path_parent:
|
||||||
|
path_parent.path_parent.remove_item(path_parent)
|
||||||
|
else:
|
||||||
|
self.__remove_item(path_parent)
|
||||||
|
|
||||||
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
def do_ui_added(self, ui):
|
def do_ui_added(self, ui):
|
||||||
self.__add_item(ui, ui.filename)
|
self.__add_item(ui, ui.filename)
|
||||||
self.emit("changed")
|
self.emit("changed")
|
||||||
|
@ -331,7 +331,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def enum_get_value_as_string(self, value):
|
def enum_get_value_as_string(self, value, use_nick=True):
|
||||||
if self.parent_id != "enum":
|
if self.parent_id != "enum":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -340,7 +340,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
|
|||||||
|
|
||||||
# Always use nick as value
|
# Always use nick as value
|
||||||
if value == enum_name or value == enum_nick or value == str(enum_value):
|
if value == enum_name or value == enum_nick or value == str(enum_value):
|
||||||
return enum_nick
|
return enum_nick if use_nick else enum_value
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -65,6 +65,10 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
|
|||||||
def __on_notify(self, obj, pspec):
|
def __on_notify(self, obj, pspec):
|
||||||
self.project._ui_changed(self, pspec.name)
|
self.project._ui_changed(self, pspec.name)
|
||||||
|
|
||||||
|
# Update display name if one of the following properties changed
|
||||||
|
if pspec.name in ["filename", "template-id"]:
|
||||||
|
self.notify("display-name")
|
||||||
|
|
||||||
def list_libraries(self):
|
def list_libraries(self):
|
||||||
retval = {}
|
retval = {}
|
||||||
|
|
||||||
|
@ -23,8 +23,11 @@
|
|||||||
# SPDX-License-Identifier: LGPL-2.1-only
|
# SPDX-License-Identifier: LGPL-2.1-only
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from gi.repository import GObject, Gtk
|
from gi.repository import GObject, Gtk
|
||||||
|
|
||||||
|
from cambalache import _
|
||||||
from .cmb_ui import CmbUI
|
from .cmb_ui import CmbUI
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +36,7 @@ class CmbUIEditor(Gtk.Grid):
|
|||||||
__gtype_name__ = "CmbUIEditor"
|
__gtype_name__ = "CmbUIEditor"
|
||||||
|
|
||||||
filename = Gtk.Template.Child()
|
filename = Gtk.Template.Child()
|
||||||
|
format = Gtk.Template.Child()
|
||||||
template_id = Gtk.Template.Child()
|
template_id = Gtk.Template.Child()
|
||||||
description = Gtk.Template.Child()
|
description = Gtk.Template.Child()
|
||||||
copyright = Gtk.Template.Child()
|
copyright = Gtk.Template.Child()
|
||||||
@ -54,9 +58,6 @@ class CmbUIEditor(Gtk.Grid):
|
|||||||
|
|
||||||
@object.setter
|
@object.setter
|
||||||
def _set_object(self, obj):
|
def _set_object(self, obj):
|
||||||
if obj == self._object:
|
|
||||||
return
|
|
||||||
|
|
||||||
for binding in self._bindings:
|
for binding in self._bindings:
|
||||||
binding.unbind()
|
binding.unbind()
|
||||||
|
|
||||||
@ -79,9 +80,15 @@ class CmbUIEditor(Gtk.Grid):
|
|||||||
self.template_id.object = obj
|
self.template_id.object = obj
|
||||||
self.filename.dirname = obj.project.dirname
|
self.filename.dirname = obj.project.dirname
|
||||||
|
|
||||||
|
# Set some default name
|
||||||
|
self.filename.unnamed_filename = _("unnamed.ui")
|
||||||
|
if not obj.filename and obj.template_id:
|
||||||
|
template = obj.project.get_object_by_id(obj.ui_id, obj.template_id)
|
||||||
|
if template:
|
||||||
|
self.filename.unnamed_filename = f"{template.name}.ui".lower()
|
||||||
|
|
||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
binding = GObject.Object.bind_property(
|
binding = obj.bind_property(
|
||||||
obj,
|
|
||||||
field,
|
field,
|
||||||
getattr(self, field),
|
getattr(self, field),
|
||||||
"cmb-value",
|
"cmb-value",
|
||||||
@ -89,5 +96,42 @@ class CmbUIEditor(Gtk.Grid):
|
|||||||
)
|
)
|
||||||
self._bindings.append(binding)
|
self._bindings.append(binding)
|
||||||
|
|
||||||
|
if obj.project.target_tk == "gtk-4.0":
|
||||||
|
self.filename.mime_types = "application/x-gtk-builder;text/x-blueprint"
|
||||||
|
|
||||||
|
# filename -> format
|
||||||
|
binding = obj.bind_property(
|
||||||
|
"filename",
|
||||||
|
self.format,
|
||||||
|
"selected",
|
||||||
|
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
|
||||||
|
transform_to=self.__filename_to_format,
|
||||||
|
transform_from=self.__format_to_filename,
|
||||||
|
user_data=obj
|
||||||
|
)
|
||||||
|
self._bindings.append(binding)
|
||||||
|
|
||||||
|
self.format.show()
|
||||||
|
self.format.set_sensitive(bool(obj.filename))
|
||||||
|
else:
|
||||||
|
self.filename.mime_types = "application/x-gtk-builder;application/x-glade"
|
||||||
|
self.format.hide()
|
||||||
|
|
||||||
|
def __filename_to_format(self, binding, source_value, ui):
|
||||||
|
if not source_value:
|
||||||
|
self.format.props.sensitive = False
|
||||||
|
return 0
|
||||||
|
self.format.props.sensitive = True
|
||||||
|
|
||||||
|
return 1 if source_value.endswith(".blp") else 0
|
||||||
|
|
||||||
|
def __format_to_filename(self, binding, target_value, ui):
|
||||||
|
if not ui.filename:
|
||||||
|
self.format.props.sensitive = False
|
||||||
|
return None
|
||||||
|
self.format.props.sensitive = True
|
||||||
|
|
||||||
|
return os.path.splitext(ui.filename)[0] + (".blp" if target_value == 1 else ".ui")
|
||||||
|
|
||||||
|
|
||||||
Gtk.WidgetClass.set_css_name(CmbUIEditor, "CmbUIEditor")
|
Gtk.WidgetClass.set_css_name(CmbUIEditor, "CmbUIEditor")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<!-- Created with Cambalache 0.95.0 -->
|
<!-- Created with Cambalache 0.97.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<!-- interface-name cmb_ui_editor.ui -->
|
<!-- interface-name cmb_ui_editor.ui -->
|
||||||
<!-- interface-copyright Juan Pablo Ugarte -->
|
<!-- interface-copyright Juan Pablo Ugarte -->
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<property name="label" translatable="yes">Description:</property>
|
<property name="label" translatable="yes">Description:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">2</property>
|
<property name="row">3</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
<property name="label" translatable="yes">Copyright:</property>
|
<property name="label" translatable="yes">Copyright:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">3</property>
|
<property name="row">4</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<property name="label" translatable="yes">Authors:</property>
|
<property name="label" translatable="yes">Authors:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">4</property>
|
<property name="row">5</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<property name="label" translatable="yes">Domain:</property>
|
<property name="label" translatable="yes">Domain:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">5</property>
|
<property name="row">6</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -78,7 +78,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">5</property>
|
<property name="row">6</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -95,7 +95,7 @@
|
|||||||
<property name="min-content-height">96</property>
|
<property name="min-content-height">96</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">2</property>
|
<property name="row">3</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -112,7 +112,7 @@
|
|||||||
<property name="min-content-height">96</property>
|
<property name="min-content-height">96</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">4</property>
|
<property name="row">5</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -124,7 +124,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">1</property>
|
<property name="row">2</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -134,7 +134,7 @@
|
|||||||
<property name="label" translatable="yes">Template:</property>
|
<property name="label" translatable="yes">Template:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">1</property>
|
<property name="row">2</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -151,7 +151,7 @@
|
|||||||
<property name="min-content-height">96</property>
|
<property name="min-content-height">96</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">3</property>
|
<property name="row">4</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -161,7 +161,7 @@
|
|||||||
<property name="label" translatable="yes">Comment:</property>
|
<property name="label" translatable="yes">Comment:</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">0</property>
|
<property name="column">0</property>
|
||||||
<property name="row">6</property>
|
<property name="row">7</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@ -178,7 +178,34 @@
|
|||||||
<property name="min-content-height">96</property>
|
<property name="min-content-height">96</property>
|
||||||
<layout>
|
<layout>
|
||||||
<property name="column">1</property>
|
<property name="column">1</property>
|
||||||
<property name="row">6</property>
|
<property name="row">7</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Format:</property>
|
||||||
|
<layout>
|
||||||
|
<property name="column">0</property>
|
||||||
|
<property name="row">1</property>
|
||||||
|
</layout>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkDropDown" id="format">
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="model">
|
||||||
|
<object class="GtkStringList">
|
||||||
|
<property name="strings">Gtk Builder
|
||||||
|
Blueprint</property>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
<layout>
|
||||||
|
<property name="column">1</property>
|
||||||
|
<property name="column-span">1</property>
|
||||||
|
<property name="row">1</property>
|
||||||
|
<property name="row-span">1</property>
|
||||||
</layout>
|
</layout>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
@ -49,4 +49,9 @@ class CmbVersionNotificationView(Gtk.Box):
|
|||||||
notification = self.notification
|
notification = self.notification
|
||||||
self.version_label.props.label = _("<b>Version {version} is available</b>").format(version=notification.version)
|
self.version_label.props.label = _("<b>Version {version} is available</b>").format(version=notification.version)
|
||||||
self.release_notes_label.props.label = notification.release_notes
|
self.release_notes_label.props.label = notification.release_notes
|
||||||
|
|
||||||
|
if notification.read_more_url:
|
||||||
self.read_more_button.props.uri = notification.read_more_url
|
self.read_more_button.props.uri = notification.read_more_url
|
||||||
|
self.read_more_button.show()
|
||||||
|
else:
|
||||||
|
self.read_more_button.hide()
|
||||||
|
@ -39,8 +39,9 @@ from . import config
|
|||||||
from .cmb_ui import CmbUI
|
from .cmb_ui import CmbUI
|
||||||
from .cmb_object import CmbObject
|
from .cmb_object import CmbObject
|
||||||
from .cmb_context_menu import CmbContextMenu
|
from .cmb_context_menu import CmbContextMenu
|
||||||
|
from cambalache.cmb_blueprint import cmb_blueprint_decompile
|
||||||
from . import utils
|
from . import utils
|
||||||
from cambalache import getLogger, _
|
from cambalache import getLogger, _, N_
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@ -167,7 +168,6 @@ class CmbMerengueProcess(GObject.Object):
|
|||||||
env = json.loads(os.environ.get("MERENGUE_DEV_ENV", "{}"))
|
env = json.loads(os.environ.get("MERENGUE_DEV_ENV", "{}"))
|
||||||
env = env | {
|
env = env | {
|
||||||
"GDK_BACKEND": "wayland",
|
"GDK_BACKEND": "wayland",
|
||||||
"GSK_RENDERER": "cairo",
|
|
||||||
"WAYLAND_DISPLAY": self.wayland_display,
|
"WAYLAND_DISPLAY": self.wayland_display,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ class CmbView(Gtk.Box):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.__project = None
|
self.__project = None
|
||||||
self.__ui_id = 0
|
self.__ui = None
|
||||||
self.__theme = None
|
self.__theme = None
|
||||||
|
|
||||||
self.menu = self.__create_context_menu()
|
self.menu = self.__create_context_menu()
|
||||||
@ -337,14 +337,34 @@ class CmbView(Gtk.Box):
|
|||||||
return self.__project.db.tostring(ui_id, merengue=merengue)
|
return self.__project.db.tostring(ui_id, merengue=merengue)
|
||||||
|
|
||||||
def __update_view(self):
|
def __update_view(self):
|
||||||
if self.__project and self.__ui_id > 0:
|
if self.__project and self.__ui:
|
||||||
if self.stack.props.visible_child_name == "ui_xml":
|
if self.stack.props.visible_child_name == "ui_xml":
|
||||||
ui = self.__get_ui_xml(self.__ui_id)
|
ui_source = self.__get_ui_xml(self.__ui.ui_id)
|
||||||
self.text_view.buffer.set_text(ui)
|
|
||||||
|
if self.__ui.filename and self.__ui.filename.endswith(".blp"):
|
||||||
|
try:
|
||||||
|
ui_source = cmb_blueprint_decompile(ui_source)
|
||||||
|
self.text_view.lang = "blueprint"
|
||||||
|
except Exception as e:
|
||||||
|
ui_source = _("Error exporting project")
|
||||||
|
ui_source += "\n"
|
||||||
|
ui_source += N_(
|
||||||
|
"blueprintcompiler encounter the following error:",
|
||||||
|
"blueprintcompiler encounter the following errors:",
|
||||||
|
len(e.errors)
|
||||||
|
)
|
||||||
|
ui_source += "\n"
|
||||||
|
ui_source += str(e)
|
||||||
|
self.text_view.lang = ""
|
||||||
|
# TODO: forward error to parent to show to user
|
||||||
|
else:
|
||||||
|
self.text_view.lang = "xml"
|
||||||
|
|
||||||
|
self.text_view.buffer.set_text(ui_source)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.text_view.buffer.set_text("")
|
self.text_view.buffer.set_text("")
|
||||||
self.__ui_id = 0
|
self.__ui = None
|
||||||
|
|
||||||
def __get_ui_dirname(self, ui_id):
|
def __get_ui_dirname(self, ui_id):
|
||||||
dirname = GLib.get_home_dir()
|
dirname = GLib.get_home_dir()
|
||||||
@ -448,19 +468,23 @@ class CmbView(Gtk.Box):
|
|||||||
if len(selection) > 0:
|
if len(selection) > 0:
|
||||||
obj = selection[0]
|
obj = selection[0]
|
||||||
|
|
||||||
if type(obj) not in [CmbUI, CmbObject]:
|
if isinstance(obj, CmbUI):
|
||||||
|
ui = obj
|
||||||
|
elif isinstance(obj, CmbObject):
|
||||||
|
ui = obj.ui
|
||||||
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
ui_id = obj.ui_id
|
ui_id = obj.ui_id
|
||||||
|
|
||||||
if self.__ui_id != ui_id:
|
if self.__ui != ui:
|
||||||
self.__ui_id = ui_id
|
self.__ui = ui
|
||||||
self.__merengue_update_ui(ui_id)
|
self.__merengue_update_ui(ui.ui_id)
|
||||||
|
|
||||||
objects = self.__get_selection_objects(selection, ui_id)
|
objects = self.__get_selection_objects(selection, ui.ui_id)
|
||||||
self.__merengue_command("selection_changed", args={"ui_id": ui_id, "selection": objects})
|
self.__merengue_command("selection_changed", args={"ui_id": ui_id, "selection": objects})
|
||||||
else:
|
else:
|
||||||
self.__ui_id = 0
|
self.__ui = None
|
||||||
self.__merengue_update_ui(0)
|
self.__merengue_update_ui(0)
|
||||||
|
|
||||||
self.__update_view()
|
self.__update_view()
|
||||||
@ -632,7 +656,7 @@ class CmbView(Gtk.Box):
|
|||||||
self.__merengue_last_exit = None
|
self.__merengue_last_exit = None
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__ui_id = 0
|
self.__ui = None
|
||||||
self.__merengue.start()
|
self.__merengue.start()
|
||||||
|
|
||||||
def __command_selection_changed(self, selection):
|
def __command_selection_changed(self, selection):
|
||||||
@ -691,7 +715,7 @@ class CmbView(Gtk.Box):
|
|||||||
|
|
||||||
self.__load_css_providers()
|
self.__load_css_providers()
|
||||||
|
|
||||||
self.__ui_id = 0
|
self.__ui = None
|
||||||
self.__on_project_selection_changed(self.__project)
|
self.__on_project_selection_changed(self.__project)
|
||||||
elif command == "placeholder_selected":
|
elif command == "placeholder_selected":
|
||||||
self.emit(
|
self.emit(
|
||||||
|
@ -35,35 +35,55 @@ class CmbFileButton(Gtk.Button):
|
|||||||
|
|
||||||
dirname = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
|
dirname = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
|
||||||
dialog_title = GObject.Property(type=str, default=_("Select filename"), flags=GObject.ParamFlags.READWRITE)
|
dialog_title = GObject.Property(type=str, default=_("Select filename"), flags=GObject.ParamFlags.READWRITE)
|
||||||
|
accept_label = GObject.Property(type=str, default=_("Select"), flags=GObject.ParamFlags.READWRITE)
|
||||||
|
unnamed_filename = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
|
||||||
|
use_open = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
|
||||||
|
|
||||||
label = Gtk.Template.Child()
|
label = Gtk.Template.Child()
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.__filename = None
|
self.__filename = None
|
||||||
|
self.__filters = None
|
||||||
|
|
||||||
@Gtk.Template.Callback("on_button_clicked")
|
@Gtk.Template.Callback("on_button_clicked")
|
||||||
def __on_button_clicked(self, button):
|
def __on_button_clicked(self, button):
|
||||||
dialog = Gtk.FileDialog(
|
dialog = Gtk.FileDialog(
|
||||||
modal=True,
|
modal=True,
|
||||||
title=self.dialog_title
|
filters=self.__filters,
|
||||||
|
title=self.dialog_title,
|
||||||
|
accept_label=self.accept_label
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.dirname is not None:
|
if self.dirname is not None:
|
||||||
if self.__filename is not None:
|
if self.__filename:
|
||||||
fullpath = os.path.join(self.dirname, self.__filename)
|
fullpath = os.path.join(self.dirname, self.__filename)
|
||||||
dialog.set_initial_file(Gio.File.new_for_path(fullpath))
|
|
||||||
|
file = Gio.File.new_for_path(fullpath)
|
||||||
|
dialog.set_initial_file(file)
|
||||||
|
|
||||||
|
# See which filter matches the file info and use it as default
|
||||||
|
if file.query_exists(None):
|
||||||
|
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, Gio.FileQueryInfoFlags.NONE, None)
|
||||||
|
for filter in self.__filters:
|
||||||
|
if filter.match(info):
|
||||||
|
dialog.set_default_filter(filter)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
dialog.set_initial_folder(Gio.File.new_for_path(self.dirname))
|
dialog.set_initial_folder(Gio.File.new_for_path(self.dirname))
|
||||||
# dialog.set_initial_name("unnamed.ui")
|
if self.unnamed_filename:
|
||||||
|
dialog.set_initial_name(self.unnamed_filename)
|
||||||
|
|
||||||
def dialog_callback(dialog, res):
|
def dialog_callback(dialog, res):
|
||||||
try:
|
try:
|
||||||
file = dialog.save_finish(res)
|
file = dialog.open_finish(res) if self.use_open else dialog.save_finish(res)
|
||||||
self.cmb_value = os.path.relpath(file.get_path(), start=self.dirname)
|
self.cmb_value = os.path.relpath(file.get_path(), start=self.dirname)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if self.use_open:
|
||||||
|
dialog.open(self.get_root(), None, dialog_callback)
|
||||||
|
else:
|
||||||
dialog.save(self.get_root(), None, dialog_callback)
|
dialog.save(self.get_root(), None, dialog_callback)
|
||||||
|
|
||||||
@GObject.Property(type=str)
|
@GObject.Property(type=str)
|
||||||
@ -77,3 +97,18 @@ class CmbFileButton(Gtk.Button):
|
|||||||
|
|
||||||
self.__filename = value if value is not None else ""
|
self.__filename = value if value is not None else ""
|
||||||
self.label.set_label(self.__filename)
|
self.label.set_label(self.__filename)
|
||||||
|
|
||||||
|
@GObject.Property(type=str)
|
||||||
|
def mime_types(self):
|
||||||
|
if self.__filters:
|
||||||
|
return ";".join([f.props.mime_types for f in self.__filters])
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@mime_types.setter
|
||||||
|
def _set_mime_types(self, value):
|
||||||
|
if value:
|
||||||
|
self.__filters = Gio.ListStore()
|
||||||
|
for mime in value.split(';'):
|
||||||
|
self.__filters.append(Gtk.FileFilter(mime_types=[mime]))
|
||||||
|
else:
|
||||||
|
self.__filters = None
|
||||||
|
@ -40,7 +40,8 @@ class CmbSourceView(GtkSource.View):
|
|||||||
|
|
||||||
@GObject.Property(type=str)
|
@GObject.Property(type=str)
|
||||||
def lang(self):
|
def lang(self):
|
||||||
return self.buffer.get_language()
|
language = self.buffer.get_language()
|
||||||
|
return language.get_id() if language else ""
|
||||||
|
|
||||||
@lang.setter
|
@lang.setter
|
||||||
def _set_lang(self, value):
|
def _set_lang(self, value):
|
||||||
|
@ -26,6 +26,7 @@ configure_file(
|
|||||||
install_data([
|
install_data([
|
||||||
'cmb_accessible_editor.py',
|
'cmb_accessible_editor.py',
|
||||||
'cmb_base.py',
|
'cmb_base.py',
|
||||||
|
'cmb_blueprint.py',
|
||||||
'cmb_context_menu.py',
|
'cmb_context_menu.py',
|
||||||
'cmb_css.py',
|
'cmb_css.py',
|
||||||
'cmb_css_editor.py',
|
'cmb_css_editor.py',
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
source .local.env
|
|
||||||
SCRIPT=$(readlink -f $0)
|
SCRIPT=$(readlink -f $0)
|
||||||
DIRNAME=$(dirname $SCRIPT)
|
DIRNAME=$(dirname $SCRIPT)
|
||||||
export GSETTINGS_BACKEND=memory
|
|
||||||
export HOME=$DIRNAME/.local/home
|
|
||||||
mkdir -p $HOME/Projects
|
|
||||||
|
|
||||||
export COVERAGE_PROCESS_START=$DIRNAME/pyproject.toml
|
export COVERAGE_PROCESS_START=$DIRNAME/pyproject.toml
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'cambalache', 'c',
|
'cambalache', 'c',
|
||||||
version: '0.96.0',
|
version: '0.97.3',
|
||||||
meson_version: '>= 1.1.0',
|
meson_version: '>= 1.1.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
@ -29,7 +29,7 @@ privatecmb_catalog_gendir = join_paths(get_option('prefix'), get_option('libdir'
|
|||||||
libxml2_dep = dependency('libxml-2.0', version: '>= 2.9.0')
|
libxml2_dep = dependency('libxml-2.0', version: '>= 2.9.0')
|
||||||
pygobject_dep = dependency('pygobject-3.0', version: '>= 3.52.0')
|
pygobject_dep = dependency('pygobject-3.0', version: '>= 3.52.0')
|
||||||
gtk4_dep = dependency('gtk4', version: '>= 4.18.0')
|
gtk4_dep = dependency('gtk4', version: '>= 4.18.0')
|
||||||
casilda_dep = dependency('casilda-0.1', version: '>= 0.2.0', fallback: ['casilda', 'casilda_dep'])
|
casilda_dep = dependency('casilda-0.1', version: '>= 0.9.0', fallback: ['casilda', 'casilda_dep'])
|
||||||
adw_dep = dependency('libadwaita-1', version: '>= 1.7.0')
|
adw_dep = dependency('libadwaita-1', version: '>= 1.7.0')
|
||||||
gtksource_dep = dependency('gtksourceview-5', version: '>= 5.16.0')
|
gtksource_dep = dependency('gtksourceview-5', version: '>= 5.16.0')
|
||||||
|
|
||||||
|
39
run-dev.py
Executable file
39
run-dev.py
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/python3
|
||||||
|
#
|
||||||
|
# run-dev - Script to run Cambalache from sources
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Juan Pablo Ugarte
|
||||||
|
#
|
||||||
|
# This program 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; version 2 of the License.
|
||||||
|
#
|
||||||
|
# This program 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 this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import locale
|
||||||
|
|
||||||
|
from tools.cmb_init_dev import cmb_init_dev
|
||||||
|
|
||||||
|
# Compile deps and install things in .local
|
||||||
|
cmb_init_dev()
|
||||||
|
|
||||||
|
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
|
||||||
|
locale.bindtextdomain("cambalache", os.path.join(basedir, ".local", "share", "locale"))
|
||||||
|
locale.textdomain("cambalache")
|
||||||
|
|
||||||
|
from cambalache.app import CmbApplication # noqa E402
|
||||||
|
|
||||||
|
CmbApplication().run(sys.argv)
|
16
run-dev.sh
16
run-dev.sh
@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
source .local.env
|
|
||||||
|
|
||||||
if python3 $DIRNAME/tools/cmb_init_dev.py; then
|
|
||||||
python3 - $@ << EOF
|
|
||||||
import sys
|
|
||||||
import locale
|
|
||||||
locale.bindtextdomain("cambalache", "$DIRNAME/.local/share/locale")
|
|
||||||
locale.textdomain("cambalache")
|
|
||||||
from cambalache.app import CmbApplication
|
|
||||||
CmbApplication().run(sys.argv)
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
echo Could not initialize dev environment
|
|
||||||
fi
|
|
10
run-tests.sh
10
run-tests.sh
@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
source .local.env
|
|
||||||
SCRIPT=$(readlink -f $0)
|
|
||||||
DIRNAME=$(dirname $SCRIPT)
|
|
||||||
export GSETTINGS_BACKEND=memory
|
|
||||||
export HOME=$DIRNAME/.local/home
|
|
||||||
mkdir -p $HOME/Projects
|
|
||||||
|
|
||||||
pytest $@
|
|
@ -1,5 +1,5 @@
|
|||||||
[wrap-git]
|
[wrap-git]
|
||||||
directory = casilda
|
directory = casilda
|
||||||
url = https://gitlab.gnome.org/jpu/casilda.git
|
url = https://gitlab.gnome.org/jpu/casilda.git
|
||||||
revision = 0.2.0
|
revision = 0.9.0
|
||||||
depth = 1
|
depth = 1
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
|
import os
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
|
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
|
||||||
|
|
||||||
|
# Ensure home directory
|
||||||
|
homedir = os.path.join(basedir, ".local", "home")
|
||||||
|
os.makedirs(os.path.join(homedir, "Projects"), exist_ok=True)
|
||||||
|
|
||||||
|
os.environ["GSETTINGS_BACKEND"] = "memory"
|
||||||
|
os.environ["HOME"] = homedir
|
||||||
|
|
||||||
gi.require_version("Gtk", "4.0")
|
gi.require_version("Gtk", "4.0")
|
||||||
from gi.repository import Gtk # noqa E402
|
from gi.repository import Gtk # noqa E402
|
||||||
from tools.cmb_init_dev import cmb_init_dev # noqa E402
|
from tools.cmb_init_dev import cmb_init_dev # noqa E402
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
<signal name="activate-default" handler="on_activate_default"/>
|
<signal name="activate-default" handler="on_activate_default"/>
|
||||||
<signal name="activate-default" handler="on_activate_default2"/>
|
<signal name="activate-default" handler="on_activate_default2"/>
|
||||||
<signal name="add" handler="on_add"/>
|
<signal name="add" handler="on_add"/>
|
||||||
<signal name="focus" handler="on_focus" swapped="yes"/>
|
<signal name="focus" handler="on_focus" swapped="True"/>
|
||||||
<signal name="focus-in-event" handler="on_focus_in_event" after="yes"/>
|
<signal name="focus-in-event" handler="on_focus_in_event" after="True"/>
|
||||||
<signal name="focus-out-event" handler="on_focus_out_event" swapped="yes" after="yes"/>
|
<signal name="focus-out-event" handler="on_focus_out_event" swapped="True" after="True"/>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkDialog" id="dialog1"/>
|
<object class="GtkDialog" id="dialog1"/>
|
||||||
</interface>
|
</interface>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="label">
|
<object class="GtkLabel" id="label">
|
||||||
<accessibility>
|
<accessibility>
|
||||||
<property name="help-text">help text</property>
|
<property name="description">help text</property>
|
||||||
<property name="label">a label</property>
|
<property name="label">a label</property>
|
||||||
<relation name="described-by">a11y3</relation>
|
<relation name="described-by">a11y3</relation>
|
||||||
<relation name="details">a11y2</relation>
|
<relation name="details">a11y2</relation>
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkButton">
|
<object class="GtkButton">
|
||||||
<signal name="activate" handler="on_button_activate"/>
|
<signal name="activate" handler="on_button_activate"/>
|
||||||
<signal name="clicked" handler="on_button_clicked" swapped="yes"/>
|
<signal name="clicked" handler="on_button_clicked" swapped="True"/>
|
||||||
<signal name="clicked" handler="on_button_clicked2" after="yes"/>
|
<signal name="clicked" handler="on_button_clicked2" after="True"/>
|
||||||
<signal name="clicked" handler="on_button_clicked3" swapped="yes" after="yes"/>
|
<signal name="clicked" handler="on_button_clicked3" swapped="True" after="True"/>
|
||||||
<signal name="clicked" handler="on_button_clicked4" object="win1"/>
|
<signal name="clicked" handler="on_button_clicked4" object="win1"/>
|
||||||
<signal name="clicked" handler="on_button_clicked5" object="win1" swapped="no"/>
|
<signal name="clicked" handler="on_button_clicked5" swapped="False" object="win1"/>
|
||||||
<signal name="notify::label" handler="on_notify"/>
|
<signal name="notify::label" handler="on_notify"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<interface>
|
<interface>
|
||||||
<!-- interface-name string_list.ui -->
|
<!-- interface-name string_list.ui -->
|
||||||
<requires lib="gtk" version="4.8"/>
|
<requires lib="gtk" version="4.10"/>
|
||||||
<object class="GtkStringList" id="list1">
|
<object class="GtkStringList" id="list1">
|
||||||
<property name="strings">a
|
<property name="strings">a
|
||||||
b
|
b
|
||||||
|
109
tests/test_cmb_blueprint.py
Normal file
109
tests/test_cmb_blueprint.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/pytest
|
||||||
|
|
||||||
|
"""
|
||||||
|
import .ui files into cambalache and compare it to blueprint compiler output
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
from cambalache import CmbProject
|
||||||
|
from cambalache.cmb_blueprint import cmb_blueprint_decompile, cmb_blueprint_compile, CmbBlueprintUnsupportedError
|
||||||
|
|
||||||
|
|
||||||
|
def tostring(ui):
|
||||||
|
db = ui.project.db
|
||||||
|
|
||||||
|
# Internal API to ensure Cambalache outputs the same as blueprint compiler
|
||||||
|
db._output_lowercase_boolean = True
|
||||||
|
db._output_use_enum_value = True
|
||||||
|
|
||||||
|
tree = db.export_ui(ui.ui_id)
|
||||||
|
|
||||||
|
if tree is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# Remove all XML comments since they are not supported by blueprint
|
||||||
|
for node in root.xpath("//comment()"):
|
||||||
|
parent = node.getparent()
|
||||||
|
if parent is not None:
|
||||||
|
parent.remove(node)
|
||||||
|
|
||||||
|
for node in root.iterfind("requires"):
|
||||||
|
lib = node.get("lib", None)
|
||||||
|
if lib != "gtk":
|
||||||
|
root.remove(node)
|
||||||
|
|
||||||
|
return etree.tostring(root, pretty_print=True, encoding="UTF-8").decode("UTF-8")
|
||||||
|
|
||||||
|
|
||||||
|
def get_exported_and_blueprint(filename):
|
||||||
|
"""
|
||||||
|
import .ui file and compare it with the exported version
|
||||||
|
"""
|
||||||
|
path = os.path.join(os.path.dirname(__file__), "gtk-4.0", filename)
|
||||||
|
project = CmbProject(target_tk="gtk-4.0")
|
||||||
|
ui, msgs, detail_msg = project.import_file(path)
|
||||||
|
|
||||||
|
assert (ui)
|
||||||
|
assert (msgs is None)
|
||||||
|
assert (detail_msg is None)
|
||||||
|
|
||||||
|
# Export Cambalache UI
|
||||||
|
str_exported = tostring(ui)
|
||||||
|
|
||||||
|
# Decompile and recompile UI to Blueprint
|
||||||
|
blueprint_decompiled = cmb_blueprint_decompile(str_exported)
|
||||||
|
blueprint_compiled = cmb_blueprint_compile(blueprint_decompiled)
|
||||||
|
assert blueprint_compiled is not None
|
||||||
|
|
||||||
|
# Remove blueprint DO NOT EDIT comment
|
||||||
|
root = etree.fromstring(blueprint_compiled)
|
||||||
|
blueprint_compiled = etree.tostring(root, pretty_print=True, encoding="UTF-8").decode("UTF-8")
|
||||||
|
|
||||||
|
return str_exported, blueprint_compiled
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("filename", [
|
||||||
|
"window.ui",
|
||||||
|
"children.ui",
|
||||||
|
"layout.ui",
|
||||||
|
"signals.ui",
|
||||||
|
"template.ui",
|
||||||
|
"inline_object.ui",
|
||||||
|
"stack_page.ui",
|
||||||
|
"comboboxtext.ui",
|
||||||
|
"style.ui",
|
||||||
|
"filefilter.ui",
|
||||||
|
"menu.ui",
|
||||||
|
|
||||||
|
# help-text is not an accessibility property
|
||||||
|
"accessibility.ui",
|
||||||
|
|
||||||
|
# bind-flags missing
|
||||||
|
# "bindings.ui",
|
||||||
|
|
||||||
|
# Translation comment missing
|
||||||
|
# "string_list.ui",
|
||||||
|
])
|
||||||
|
def test_assert_exported_and_compiled(filename):
|
||||||
|
"""
|
||||||
|
import .ui file and compare it with the exported version
|
||||||
|
"""
|
||||||
|
str_exported, blueprint_compiled = get_exported_and_blueprint(filename)
|
||||||
|
|
||||||
|
# Compare blueprint generated string with cambalache
|
||||||
|
assert blueprint_compiled.strip() == str_exported.strip()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("filename", [
|
||||||
|
"liststore.ui",
|
||||||
|
"treestore.ui",
|
||||||
|
"label.ui",
|
||||||
|
])
|
||||||
|
def test_assert_exported_and_compiled_unsupported(filename):
|
||||||
|
with pytest.raises(CmbBlueprintUnsupportedError):
|
||||||
|
str_exported, blueprint_compiled = get_exported_and_blueprint(filename)
|
||||||
|
|
@ -13,7 +13,7 @@ from . import utils as test_utils
|
|||||||
|
|
||||||
DAY = 3600 * 24
|
DAY = 3600 * 24
|
||||||
now = utils.utcnow()
|
now = utils.utcnow()
|
||||||
CMB_UUID = str(uuid4())
|
CMB_UUID = notification_center.uuid
|
||||||
POLL_UUID = str(uuid4())
|
POLL_UUID = str(uuid4())
|
||||||
|
|
||||||
POLL_NOTIFICATION_BASE = {
|
POLL_NOTIFICATION_BASE = {
|
||||||
@ -44,16 +44,14 @@ def wait_for_all_threads():
|
|||||||
|
|
||||||
|
|
||||||
def test_cmb_notification_disabled():
|
def test_cmb_notification_disabled():
|
||||||
assert not notification_center.uuid
|
|
||||||
assert notification_center.enabled is False
|
assert notification_center.enabled is False
|
||||||
assert len(notification_center.store) == 0
|
assert len(notification_center.store) == 0
|
||||||
notification_center.enabled = True
|
notification_center.enabled = True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("response, headers, n", [
|
@pytest.mark.parametrize("response, n", [
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"uuid": CMB_UUID,
|
|
||||||
"notification": {
|
"notification": {
|
||||||
"type": "version",
|
"type": "version",
|
||||||
"start_date": now - DAY,
|
"start_date": now - DAY,
|
||||||
@ -63,14 +61,10 @@ def test_cmb_notification_disabled():
|
|||||||
"read_more_url": "http://localhost"
|
"read_more_url": "http://localhost"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'User-Agent': notification_center.user_agent
|
|
||||||
},
|
|
||||||
1
|
1
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"uuid": CMB_UUID,
|
|
||||||
"notification": {
|
"notification": {
|
||||||
"type": "message",
|
"type": "message",
|
||||||
"start_date": now - DAY,
|
"start_date": now - DAY,
|
||||||
@ -79,26 +73,16 @@ def test_cmb_notification_disabled():
|
|||||||
"message": "This is a message notification"
|
"message": "This is a message notification"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'User-Agent': notification_center.user_agent,
|
|
||||||
'x-cambalache-uuid': CMB_UUID
|
|
||||||
},
|
|
||||||
2
|
2
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"uuid": CMB_UUID,
|
|
||||||
"notification": POLL_NOTIFICATION_BASE
|
"notification": POLL_NOTIFICATION_BASE
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'User-Agent': notification_center.user_agent,
|
|
||||||
'x-cambalache-uuid': CMB_UUID
|
|
||||||
},
|
|
||||||
3
|
3
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"uuid": CMB_UUID,
|
|
||||||
"notification": {
|
"notification": {
|
||||||
**POLL_NOTIFICATION_BASE,
|
**POLL_NOTIFICATION_BASE,
|
||||||
"results": {
|
"results": {
|
||||||
@ -107,14 +91,10 @@ def test_cmb_notification_disabled():
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'User-Agent': notification_center.user_agent,
|
|
||||||
'x-cambalache-uuid': CMB_UUID
|
|
||||||
},
|
|
||||||
4
|
4
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
def test_cmb_notification_get(mocker, response, headers, n):
|
def test_cmb_notification_get(mocker, response, n):
|
||||||
wait_for_all_threads()
|
wait_for_all_threads()
|
||||||
|
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
@ -137,7 +117,14 @@ def test_cmb_notification_get(mocker, response, headers, n):
|
|||||||
|
|
||||||
test_utils.process_all_pending_gtk_events()
|
test_utils.process_all_pending_gtk_events()
|
||||||
|
|
||||||
request_mock.assert_called_with("GET", "/notification", headers=headers)
|
request_mock.assert_called_with(
|
||||||
|
"GET",
|
||||||
|
"/notification",
|
||||||
|
headers={
|
||||||
|
'User-Agent': notification_center.user_agent,
|
||||||
|
'x-cambalache-uuid': CMB_UUID
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
assert notification_center.uuid == CMB_UUID
|
assert notification_center.uuid == CMB_UUID
|
||||||
on_new_notification.assert_called()
|
on_new_notification.assert_called()
|
||||||
|
@ -4,11 +4,52 @@
|
|||||||
import .ui files into cambalache and export to compare results
|
import .ui files into cambalache and export to compare results
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
from cambalache import CmbProject, config
|
from cambalache import CmbProject, config
|
||||||
|
|
||||||
|
|
||||||
def assert_original_and_exported(target_tk, filename):
|
@pytest.mark.parametrize("target_tk,filename", [
|
||||||
|
("gtk+-3.0", "window.ui"),
|
||||||
|
("gtk+-3.0", "children.ui"),
|
||||||
|
("gtk+-3.0", "packing.ui"),
|
||||||
|
("gtk+-3.0", "signals.ui"),
|
||||||
|
("gtk+-3.0", "template.ui"),
|
||||||
|
("gtk+-3.0", "comboboxtext.ui"),
|
||||||
|
("gtk+-3.0", "dialog.ui"),
|
||||||
|
("gtk+-3.0", "label.ui"),
|
||||||
|
("gtk+-3.0", "levelbar.ui"),
|
||||||
|
("gtk+-3.0", "liststore.ui"),
|
||||||
|
("gtk+-3.0", "scale.ui"),
|
||||||
|
("gtk+-3.0", "sizegroup.ui"),
|
||||||
|
("gtk+-3.0", "style.ui"),
|
||||||
|
("gtk+-3.0", "treestore.ui"),
|
||||||
|
("gtk+-3.0", "filefilter.ui"),
|
||||||
|
("gtk+-3.0", "custom_fragment.ui"),
|
||||||
|
("gtk+-3.0", "bindings.ui"),
|
||||||
|
("gtk+-3.0", "menu.ui"),
|
||||||
|
("gtk+-3.0", "accessibility.ui"),
|
||||||
|
("gtk-4.0", "window.ui"),
|
||||||
|
("gtk-4.0", "children.ui"),
|
||||||
|
("gtk-4.0", "layout.ui"),
|
||||||
|
("gtk-4.0", "signals.ui"),
|
||||||
|
("gtk-4.0", "template.ui"),
|
||||||
|
("gtk-4.0", "inline_object.ui"),
|
||||||
|
("gtk-4.0", "stack_page.ui"),
|
||||||
|
("gtk-4.0", "liststore.ui"),
|
||||||
|
("gtk-4.0", "treestore.ui"),
|
||||||
|
("gtk-4.0", "comboboxtext.ui"),
|
||||||
|
("gtk-4.0", "style.ui"),
|
||||||
|
("gtk-4.0", "label.ui"),
|
||||||
|
("gtk-4.0", "filefilter.ui"),
|
||||||
|
("gtk-4.0", "custom_fragment.ui"),
|
||||||
|
("gtk-4.0", "bindings.ui"),
|
||||||
|
("gtk-4.0", "menu.ui"),
|
||||||
|
("gtk-4.0", "string_list.ui"),
|
||||||
|
("gtk-4.0", "accessibility.ui"),
|
||||||
|
("gtk-4.0", "ui_comments.ui")
|
||||||
|
])
|
||||||
|
def test_(target_tk, filename):
|
||||||
"""
|
"""
|
||||||
import .ui file and compare it with the exported version
|
import .ui file and compare it with the exported version
|
||||||
"""
|
"""
|
||||||
@ -29,160 +70,3 @@ def assert_original_and_exported(target_tk, filename):
|
|||||||
|
|
||||||
assert str_exported == str_original
|
assert str_exported == str_original
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Gtk+ 3.0 Tests
|
|
||||||
#
|
|
||||||
def test_gtk3_window():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "window.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_children():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "children.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_packing():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "packing.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_signals():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "signals.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_template():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "template.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_comboboxtext():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "comboboxtext.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_dialog():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "dialog.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_label():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "label.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_levelbar():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "levelbar.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_liststore():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "liststore.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_scale():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "scale.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_sizegroup():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "sizegroup.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_style():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "style.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_treestore():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "treestore.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_filefilter():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "filefilter.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_custom_fragment():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "custom_fragment.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_bindings():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "bindings.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_menu():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "menu.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk3_accessibility():
|
|
||||||
assert_original_and_exported("gtk+-3.0", "accessibility.ui")
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Gtk 4.0 Tests
|
|
||||||
#
|
|
||||||
def test_gtk4_window():
|
|
||||||
assert_original_and_exported("gtk-4.0", "window.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_children():
|
|
||||||
assert_original_and_exported("gtk-4.0", "children.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_layout():
|
|
||||||
assert_original_and_exported("gtk-4.0", "layout.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_signals():
|
|
||||||
assert_original_and_exported("gtk-4.0", "signals.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_template():
|
|
||||||
assert_original_and_exported("gtk-4.0", "template.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_inline_object():
|
|
||||||
assert_original_and_exported("gtk-4.0", "inline_object.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_stack_page():
|
|
||||||
assert_original_and_exported("gtk-4.0", "stack_page.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_liststore():
|
|
||||||
assert_original_and_exported("gtk-4.0", "liststore.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_treestore():
|
|
||||||
assert_original_and_exported("gtk-4.0", "treestore.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_comboboxtext():
|
|
||||||
assert_original_and_exported("gtk-4.0", "comboboxtext.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_style():
|
|
||||||
assert_original_and_exported("gtk-4.0", "style.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_label():
|
|
||||||
assert_original_and_exported("gtk-4.0", "label.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_filefilter():
|
|
||||||
assert_original_and_exported("gtk-4.0", "filefilter.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_custom_fragment():
|
|
||||||
assert_original_and_exported("gtk-4.0", "custom_fragment.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_bindings():
|
|
||||||
assert_original_and_exported("gtk-4.0", "bindings.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_menu():
|
|
||||||
assert_original_and_exported("gtk-4.0", "menu.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_string_list():
|
|
||||||
assert_original_and_exported("gtk-4.0", "string_list.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_accessibility():
|
|
||||||
assert_original_and_exported("gtk-4.0", "accessibility.ui")
|
|
||||||
|
|
||||||
|
|
||||||
def test_gtk4_ui_comments():
|
|
||||||
assert_original_and_exported("gtk-4.0", "ui_comments.ui")
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Cambalache UI Maker developer mode
|
# Cambalache UI Maker developer mode
|
||||||
#
|
#
|
||||||
# Copyright (C) 2021-2024 Juan Pablo Ugarte
|
# Copyright (C) 2021-2025 Juan Pablo Ugarte
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as
|
# it under the terms of the GNU General Public License as
|
||||||
@ -22,18 +22,46 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import gi
|
||||||
import sys
|
import sys
|
||||||
import stat
|
import stat
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
|
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
|
||||||
sys.path.insert(1, basedir)
|
|
||||||
|
|
||||||
cambalachedir = os.path.join(basedir, "cambalache")
|
|
||||||
localdir = os.path.join(basedir, ".local")
|
localdir = os.path.join(basedir, ".local")
|
||||||
|
locallibdir = os.path.join(localdir, "lib", sys.implementation._multiarch)
|
||||||
|
cambalachedir = os.path.join(basedir, "cambalache")
|
||||||
localpkgdatadir = os.path.join(localdir, "share", "cambalache")
|
localpkgdatadir = os.path.join(localdir, "share", "cambalache")
|
||||||
catalogsdir = os.path.join(localpkgdatadir, "catalogs")
|
catalogsdir = os.path.join(localpkgdatadir, "catalogs")
|
||||||
|
localbindir = os.path.join(localdir, "bin")
|
||||||
|
|
||||||
|
repository = gi.Repository.get_default()
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = [locallibdir, f"{locallibdir}/cambalache", f"{locallibdir}/cmb_catalog_gen"]
|
||||||
|
for path in LD_LIBRARY_PATH:
|
||||||
|
repository.prepend_library_path(path)
|
||||||
|
|
||||||
|
GI_TYPELIB_PATH = [f"{locallibdir}/girepository-1.0", f"{locallibdir}/cambalache", f"{locallibdir}/cmb_catalog_gen"]
|
||||||
|
for path in GI_TYPELIB_PATH:
|
||||||
|
repository.prepend_search_path(path)
|
||||||
|
|
||||||
|
for var, value in [
|
||||||
|
("LD_LIBRARY_PATH", ":".join(LD_LIBRARY_PATH)),
|
||||||
|
("GI_TYPELIB_PATH", ":".join(GI_TYPELIB_PATH)),
|
||||||
|
("PKG_CONFIG_PATH", os.path.join(locallibdir, "pkgconfig")),
|
||||||
|
("GSETTINGS_SCHEMA_DIR", os.path.join(localdir, "share", "glib-2.0", "schemas")),
|
||||||
|
("XDG_DATA_DIRS", os.path.join(localdir, "share")),
|
||||||
|
("PYTHONPATH", os.path.join(localdir, "lib", "python3", "dist-packages"))
|
||||||
|
]:
|
||||||
|
if var in os.environ:
|
||||||
|
old_value = os.environ[var]
|
||||||
|
os.environ[var] = f"{value}:{old_value}"
|
||||||
|
else:
|
||||||
|
os.environ[var] = value
|
||||||
|
|
||||||
|
sys.path.insert(1, basedir)
|
||||||
|
sys.path.insert(1, localbindir)
|
||||||
|
|
||||||
from gi.repository import GLib # noqa: E402
|
from gi.repository import GLib # noqa: E402
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user