mirror of
https://gitlab.gnome.org/jpu/cambalache.git
synced 2025-06-25 00:02:51 -04:00
Compare commits
No commits in common. "main" and "0.96.0" have entirely different histories.
13
.local.env
Executable file
13
.local.env
Executable file
@ -0,0 +1,13 @@
|
||||
#!/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
|
||||
you have everything needed)
|
||||
|
||||
`./run-dev.py`
|
||||
`./run-dev.sh`
|
||||
|
||||
This is meant for Cambalache development only.
|
||||
|
||||
|
@ -83,8 +83,8 @@
|
||||
{
|
||||
"type" : "git",
|
||||
"url" : "https://gitlab.gnome.org/jpu/casilda.git",
|
||||
"tag" : "0.9.0",
|
||||
"commit" : "4f7b1be321cf76832b12bda11fd91897257377e2"
|
||||
"tag" : "0.2.0",
|
||||
"commit" : "99a0173f21345b85713198c1fa1fbb388d00182f"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -43,12 +43,9 @@ from cambalache import (
|
||||
notification_center,
|
||||
config,
|
||||
utils,
|
||||
_,
|
||||
N_
|
||||
_
|
||||
)
|
||||
|
||||
from cambalache.cmb_blueprint import CmbBlueprintError
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
GObject.type_ensure(CmbGResourceEditor.__gtype__)
|
||||
@ -67,7 +64,6 @@ class CmbWindow(Adw.ApplicationWindow):
|
||||
gtk4_filter = Gtk.Template.Child()
|
||||
gtk3_filter = Gtk.Template.Child()
|
||||
gtk_builder_filter = Gtk.Template.Child()
|
||||
blueprint_filter = Gtk.Template.Child()
|
||||
glade_filter = Gtk.Template.Child()
|
||||
css_filter = Gtk.Template.Child()
|
||||
gresource_filter = Gtk.Template.Child()
|
||||
@ -140,13 +136,7 @@ class CmbWindow(Adw.ApplicationWindow):
|
||||
|
||||
self.gtk4_import_filters = Gio.ListStore()
|
||||
|
||||
for filter in [
|
||||
self.gtk4_filter,
|
||||
self.gtk_builder_filter,
|
||||
self.blueprint_filter,
|
||||
self.css_filter,
|
||||
self.gresource_filter
|
||||
]:
|
||||
for filter in [self.gtk4_filter, self.gtk_builder_filter, self.css_filter, self.gresource_filter]:
|
||||
self.gtk4_import_filters.append(filter)
|
||||
|
||||
self.gtk3_import_filters = Gio.ListStore()
|
||||
@ -1145,7 +1135,7 @@ class CmbWindow(Adw.ApplicationWindow):
|
||||
|
||||
print("IMPORT", path, content_type)
|
||||
|
||||
if content_type in ["application/x-gtk-builder", "application/x-glade", "text/x-blueprint"]:
|
||||
if content_type in ["application/x-gtk-builder", "application/x-glade"]:
|
||||
self.import_file(file.get_path())
|
||||
elif content_type == "text/css":
|
||||
self.project.add_css(path)
|
||||
@ -1174,25 +1164,10 @@ class CmbWindow(Adw.ApplicationWindow):
|
||||
self.project.set_selection([gresource])
|
||||
|
||||
def __save(self):
|
||||
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.__update_action_save()
|
||||
self.emit("project-saved", self.project)
|
||||
if self.project.save():
|
||||
self.__last_saved_index = self.project.history_index
|
||||
self.__update_action_save()
|
||||
self.emit("project-saved", self.project)
|
||||
|
||||
def save_project(self):
|
||||
if self.project is None:
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Created with Cambalache 0.97.1 -->
|
||||
<!-- Created with Cambalache 0.95.0 -->
|
||||
<interface>
|
||||
<!-- interface-name cmb_window.ui -->
|
||||
<!-- interface-copyright Juan Pablo Ugarte -->
|
||||
@ -836,9 +836,6 @@
|
||||
<object class="GtkFileFilter" id="glade_filter">
|
||||
<property name="mime-types">application/x-glade</property>
|
||||
</object>
|
||||
<object class="GtkFileFilter" id="blueprint_filter">
|
||||
<property name="mime-types">text/x-blueprint</property>
|
||||
</object>
|
||||
<object class="GtkFileFilter" id="css_filter">
|
||||
<property name="mime-types">text/css</property>
|
||||
</object>
|
||||
@ -847,7 +844,6 @@
|
||||
</object>
|
||||
<object class="GtkFileFilter" id="gtk4_filter">
|
||||
<property name="mime-types">application/x-gtk-builder
|
||||
text/x-blueprint
|
||||
text/css</property>
|
||||
<property name="name">All supported files</property>
|
||||
<property name="suffixes">gresource.xml</property>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
|
||||
<!-- Created with Cambalache 0.97.1 -->
|
||||
<cambalache-project version="0.96.0" target_tk="gtk-4.0">
|
||||
<!-- Created with Cambalache 0.95.1 -->
|
||||
<cambalache-project version="0.95.0" target_tk="gtk-4.0">
|
||||
<gresources filename="cambalache.gresource.xml" sha256="fdcf4cd517493f548aa4b4fe206ff7762cee9cdda7ec5a85a718b46eb1c4731b"/>
|
||||
<gresources filename="app/app.gresource.xml" sha256="3684aa78fce08d8e81d0907317214aeb179c5aea091dd0df405476b43e286941"/>
|
||||
<css filename="cambalache.css" priority="400" is_global="1"/>
|
||||
@ -221,10 +221,7 @@
|
||||
<ui template-class="CmbContextMenu" filename="cmb_context_menu.ui" sha256="81eba3adf715348a5c03ef4cbc151eebd5d9aa8b5a14c5968232f68a61ae573c"/>
|
||||
<ui template-class="CmbDBInspector" filename="cmb_db_inspector.ui" sha256="4451cdb08d24bd4a802ea692c0ebb4ef46af13152984c0b435d29bf4eb7dab55"/>
|
||||
<ui filename="app/cmb_shortcuts.ui" sha256="d7ac37fd2430788a9e210ed4bc84dcfeba5609bdcc801afb192bfd900c7a8883"/>
|
||||
<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="CmbFileButton" filename="control/cmb_file_button.ui" sha256="f859b4f85d7c80c1fef69b68ebb9129423d9c72fdb38d304132784f7361cbbfd"/>
|
||||
<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="CmbMessageNotificationView" filename="cmb_message_notification_view.ui" sha256="debeffd184e225d82ed29ac590654b8160363e8d5606366dc8acb3ff9840fee3"/>
|
||||
@ -256,7 +253,7 @@
|
||||
<signal id="placeholder-activated"/>
|
||||
<signal id="placeholder-selected"/>
|
||||
</ui>
|
||||
<ui template-class="CmbGResourceEditor" filename="cmb_gresource_editor.ui" sha256="2050887ef1c45facb6ebff14500214bb035e6808ca61d2a7d661e696d79026ca">
|
||||
<ui template-class="CmbGResourceEditor" filename="cmb_gresource_editor.ui" sha256="46969468ae070bdd315ca4091869d0b8a9bcb24c10cde82208bfd7d36f39fdd0">
|
||||
<requires>CmbFileButton</requires>
|
||||
<requires>CmbEntry</requires>
|
||||
</ui>
|
||||
@ -264,13 +261,13 @@
|
||||
<requires>CmbFileButton</requires>
|
||||
<requires>CmbSourceView</requires>
|
||||
</ui>
|
||||
<ui template-class="CmbUIEditor" filename="cmb_ui_editor.ui" sha256="70e272e2c6c499a5424c6019154cd8338d9edde1fa111ad592a1c104019bb7ee">
|
||||
<ui template-class="CmbUIEditor" filename="cmb_ui_editor.ui" sha256="0e4e205a3737fa207406ce74f4d8d3fbb4a477409b8a7b425b3d53c41309b306">
|
||||
<requires>CmbTextBuffer</requires>
|
||||
<requires>CmbFileButton</requires>
|
||||
<requires>CmbEntry</requires>
|
||||
<requires>CmbToplevelChooser</requires>
|
||||
</ui>
|
||||
<ui template-class="CmbWindow" filename="app/cmb_window.ui" sha256="df07e3e03b88f9b097ad7d65efa15923d339db83b9d4a7c015a312a71f8c9685">
|
||||
<ui template-class="CmbWindow" filename="app/cmb_window.ui" sha256="20a192093a13209e0add725f15922e4f09689dda08fbf0b3a3eedf5d9adf2efc">
|
||||
<requires>CmbNotificationListView</requires>
|
||||
<requires>CmbScrolledWindow</requires>
|
||||
<requires>CmbObjectEditor</requires>
|
||||
|
@ -1,86 +0,0 @@
|
||||
#
|
||||
# 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,9 +82,6 @@ class CmbDB(GObject.GObject):
|
||||
self.__history_commands = {}
|
||||
self.__table_column_mapping = {}
|
||||
|
||||
self._output_lowercase_boolean = False
|
||||
self._output_use_enum_value = False
|
||||
|
||||
self.clipboard = []
|
||||
self.clipboard_ids = []
|
||||
|
||||
@ -2026,7 +2023,7 @@ class CmbDB(GObject.GObject):
|
||||
self.__unknown_tag(child, root, child.tag)
|
||||
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)
|
||||
|
||||
@ -2375,7 +2372,6 @@ class CmbDB(GObject.GObject):
|
||||
|
||||
value = 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"
|
||||
|
||||
@ -2403,10 +2399,6 @@ class CmbDB(GObject.GObject):
|
||||
value = obj_name
|
||||
elif property_type_id == "GBytes":
|
||||
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:
|
||||
value = val
|
||||
|
||||
@ -2453,16 +2445,16 @@ class CmbDB(GObject.GObject):
|
||||
node = E.signal(name=name, handler=handler)
|
||||
|
||||
if data:
|
||||
utils.xml_node_set(node, "object", data)
|
||||
|
||||
# if object is set, swap defaults to True
|
||||
if not swap:
|
||||
utils.xml_node_set(node, "swapped", "False")
|
||||
|
||||
utils.xml_node_set(node, "object", data)
|
||||
utils.xml_node_set(node, "swapped", "no")
|
||||
elif swap:
|
||||
utils.xml_node_set(node, "swapped", "True")
|
||||
utils.xml_node_set(node, "swapped", "yes")
|
||||
|
||||
if after:
|
||||
utils.xml_node_set(node, "after", "True")
|
||||
utils.xml_node_set(node, "after", "yes")
|
||||
obj.append(node)
|
||||
self.__node_add_comment(node, comment)
|
||||
|
||||
@ -2526,7 +2518,6 @@ class CmbDB(GObject.GObject):
|
||||
owner_id,
|
||||
) = row
|
||||
|
||||
pinfo = self.type_info.get(property_type_id, None)
|
||||
value = None
|
||||
|
||||
# Ignore properties depending on metadata (Gtk4)
|
||||
@ -2546,15 +2537,6 @@ class CmbDB(GObject.GObject):
|
||||
if obj_name is None:
|
||||
continue
|
||||
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:
|
||||
value = val
|
||||
|
||||
@ -2764,7 +2746,7 @@ class CmbDB(GObject.GObject):
|
||||
for child in root:
|
||||
node.append(child)
|
||||
|
||||
def export_ui(self, ui_id, merengue=False):
|
||||
def export_ui(self, ui_id, merengue=False, skip_version_comment=False):
|
||||
c = self.conn.cursor()
|
||||
|
||||
c.execute("SELECT translation_domain, comment, template_id, custom_fragment FROM ui WHERE ui_id=?;", (ui_id,))
|
||||
@ -2777,7 +2759,8 @@ class CmbDB(GObject.GObject):
|
||||
|
||||
node = E.interface()
|
||||
|
||||
node.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
|
||||
if not skip_version_comment:
|
||||
node.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
|
||||
|
||||
utils.xml_node_set(node, "domain", translation_domain)
|
||||
|
||||
@ -3051,20 +3034,6 @@ class CmbDB(GObject.GObject):
|
||||
(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
|
||||
|
||||
|
@ -36,8 +36,6 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
||||
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._last_known = None
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.connect("notify", self.__on_notify)
|
||||
@ -49,13 +47,6 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
||||
return f"CmbGResource<{self.resource_type}> id={self.gresource_id}"
|
||||
|
||||
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)
|
||||
|
||||
@GObject.Property(type=CmbBaseGResource)
|
||||
@ -93,34 +84,6 @@ class CmbGResource(CmbBaseGResource, Gio.ListModel):
|
||||
file_filename = self.file_filename
|
||||
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
|
||||
def do_get_item(self, position):
|
||||
gresource_id = self.gresource_id
|
||||
|
@ -221,7 +221,6 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="CmbFileButton" id="file_filename">
|
||||
<property name="use-open">True</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="column-span">1</property>
|
||||
|
@ -30,7 +30,6 @@ import http.client
|
||||
import time
|
||||
import platform
|
||||
|
||||
from uuid import uuid4
|
||||
from urllib.parse import urlparse
|
||||
from .config import VERSION
|
||||
from gi.repository import GObject, GLib, Gio, Gdk, Gtk, Adw, HarfBuzz
|
||||
@ -131,7 +130,7 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.retry_interval = 2
|
||||
self.retry_interval = 1
|
||||
self.user_agent = self.__get_user_agent()
|
||||
self.store = Gio.ListStore(item_type=CmbNotification)
|
||||
self.settings = Gio.Settings(schema_id="ar.xjuan.Cambalache.notification")
|
||||
@ -157,10 +156,6 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
logger.warning(f"{backend.scheme} is not supported, only HTTPS")
|
||||
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"UUID: {self.uuid}")
|
||||
|
||||
@ -253,6 +248,9 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
def __get_notification_idle(self, data):
|
||||
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:
|
||||
notification = self.__notification_from_dict(data["notification"])
|
||||
self.store.insert(0, notification)
|
||||
@ -266,10 +264,10 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
return GLib.SOURCE_REMOVE
|
||||
|
||||
def __get_notification_thread(self):
|
||||
headers = {
|
||||
"User-Agent": self.user_agent,
|
||||
"x-cambalache-uuid": self.uuid,
|
||||
}
|
||||
headers = {"User-Agent": self.user_agent}
|
||||
|
||||
if self.uuid:
|
||||
headers["x-cambalache-uuid"] = self.uuid
|
||||
|
||||
try:
|
||||
logger.info(f"GET /notification {headers=}")
|
||||
@ -279,7 +277,7 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
assert response.status == 200
|
||||
|
||||
# Reset retry interval
|
||||
self.retry_interval = 8
|
||||
self.retry_interval = 1
|
||||
|
||||
data = response.read().decode()
|
||||
|
||||
@ -292,7 +290,7 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
self.retry_interval *= 2
|
||||
self.retry_interval = min(self.retry_interval, 256)
|
||||
|
||||
logger.info(f"Request error {e}, retrying in {self.retry_interval}s")
|
||||
logger.warning(f"Request error {e}, retrying in {self.retry_interval}s")
|
||||
GLib.timeout_add_seconds(self.retry_interval, self._get_notification)
|
||||
|
||||
self.connection.close()
|
||||
@ -320,19 +318,19 @@ class CmbNotificationCenter(GObject.GObject):
|
||||
def __poll_vote_idle(self, data):
|
||||
logger.debug(f"Got vote response {data}")
|
||||
|
||||
poll_uuid = data["uuid"]
|
||||
uuid = data["uuid"]
|
||||
results = data["results"]
|
||||
|
||||
for notification in self.store:
|
||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
|
||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
|
||||
notification.results = CmbPollResult(**results)
|
||||
self.__save_notifications()
|
||||
break
|
||||
return GLib.SOURCE_REMOVE
|
||||
|
||||
def __poll_vote_exception_idle(self, poll_uuid):
|
||||
def __poll_vote_exception_idle(self, uuid):
|
||||
for notification in self.store:
|
||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
|
||||
if isinstance(notification, CmbPollNotification) and notification.poll.id == uuid:
|
||||
notification.my_votes = []
|
||||
break
|
||||
return GLib.SOURCE_REMOVE
|
||||
|
@ -458,17 +458,11 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
|
||||
def remove_data(self, data):
|
||||
try:
|
||||
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(
|
||||
"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.project.db.commit()
|
||||
self.project.history_pop()
|
||||
except Exception as e:
|
||||
logger.warning(f"{self} Error removing data {data}: {e}")
|
||||
return False
|
||||
|
@ -27,7 +27,7 @@ from gi.repository import GObject
|
||||
|
||||
from .cmb_objects_base import CmbBaseObjectData
|
||||
from .cmb_type_info import CmbTypeDataInfo
|
||||
from cambalache import getLogger, _
|
||||
from cambalache import getLogger
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
@ -194,17 +194,11 @@ class CmbObjectData(CmbBaseObjectData):
|
||||
def remove_data(self, data):
|
||||
try:
|
||||
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(
|
||||
"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.project.db.commit()
|
||||
self.project.history_pop()
|
||||
except Exception as e:
|
||||
logger.warning(f"{self} Error removing data {data}: {e}")
|
||||
return False
|
||||
|
@ -31,12 +31,6 @@ from .control import cmb_create_editor
|
||||
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")
|
||||
class CmbObjectDataEditor(Gtk.Box):
|
||||
__gtype_name__ = "CmbObjectDataEditor"
|
||||
@ -75,7 +69,7 @@ class CmbObjectDataEditor(Gtk.Box):
|
||||
def __on_remove_clicked(self, button):
|
||||
if self.info:
|
||||
self.object.remove_data(self.__data)
|
||||
elif self.__data:
|
||||
else:
|
||||
self.__data.parent.remove_data(self.__data)
|
||||
|
||||
@GObject.Property(type=GObject.Object)
|
||||
@ -144,7 +138,8 @@ class CmbObjectDataEditor(Gtk.Box):
|
||||
self.__update_view()
|
||||
|
||||
def __on_data_removed(self, obj, data):
|
||||
self.__remove_data_editor(data)
|
||||
if self.object and self.info:
|
||||
self.__remove_data_editor(data)
|
||||
|
||||
def __ensure_object_data(self, history_message):
|
||||
if self.data:
|
||||
|
@ -260,7 +260,7 @@ It has to be exposed by your application with GtkBuilder expose_object method."
|
||||
hexpand=True,
|
||||
object=obj,
|
||||
data=data,
|
||||
info=info.data[data_key],
|
||||
info=None if data else info.data[data_key],
|
||||
)
|
||||
|
||||
grid.attach(editor, 0, i, 2, 1)
|
||||
|
@ -58,9 +58,6 @@ class CmbPath(CmbBase, Gio.ListModel):
|
||||
return self.__path_items.get(directory, None)
|
||||
|
||||
def add_item(self, item):
|
||||
if item in self.__items:
|
||||
return
|
||||
|
||||
display_name = item.display_name
|
||||
is_path = isinstance(item, CmbPath)
|
||||
|
||||
@ -88,13 +85,9 @@ class CmbPath(CmbBase, Gio.ListModel):
|
||||
self.notify("display-name")
|
||||
|
||||
def remove_item(self, item):
|
||||
if item not in self.__items:
|
||||
return
|
||||
|
||||
if isinstance(item, CmbPath) and item.path in self.__path_items:
|
||||
del self.__path_items[item.path]
|
||||
|
||||
item.path_parent = None
|
||||
i = self.__items.index(item)
|
||||
self.__items.pop(i)
|
||||
self.items_changed(i, 1, 0)
|
||||
|
@ -27,10 +27,9 @@ import os
|
||||
import json
|
||||
import time
|
||||
import sqlite3
|
||||
import hashlib
|
||||
|
||||
from pathlib import Path
|
||||
from gi.repository import GObject, Gio, GLib
|
||||
from gi.repository import GObject, Gio
|
||||
from graphlib import TopologicalSorter, CycleError
|
||||
|
||||
from lxml import etree
|
||||
@ -50,7 +49,6 @@ from .cmb_layout_property import CmbLayoutProperty
|
||||
from .cmb_library_info import CmbLibraryInfo
|
||||
from .cmb_type_info import CmbTypeInfo
|
||||
from .cmb_objects_base import CmbSignal
|
||||
from .cmb_blueprint import cmb_blueprint_decompile, cmb_blueprint_compile
|
||||
from .utils import FileHash
|
||||
from . import constants, utils
|
||||
from cambalache import config, getLogger, _, N_
|
||||
@ -278,23 +276,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
return root, relpath, hexdigest
|
||||
|
||||
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
|
||||
return None, None
|
||||
|
||||
def __get_version_comment_from_root(self, root):
|
||||
comment = root.getprevious()
|
||||
@ -305,10 +287,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
def __load_ui_from_node(self, node):
|
||||
filename, sha256 = utils.xml_node_get(node, ["filename", "sha256"])
|
||||
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:
|
||||
logger.warning(f"{filename} hash mismatch, file was modified")
|
||||
@ -546,49 +525,36 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
else:
|
||||
fullpath = filename
|
||||
|
||||
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)
|
||||
dirty = True
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
|
||||
|
||||
original_comment, original_hash = self.__file_state.get(filename, (None, None))
|
||||
if original_comment is not None:
|
||||
if use_blp:
|
||||
m = hashlib.sha256()
|
||||
m.update(blueprint_decompiled.encode())
|
||||
hexdigest = m.hexdigest()
|
||||
else:
|
||||
comment = self.__get_version_comment_from_root(interface)
|
||||
new_comment = comment.text
|
||||
comment.text = original_comment.text
|
||||
interface = root.getroot()
|
||||
|
||||
# Calculate hash
|
||||
hash_file = FileHash()
|
||||
comment = self.__get_version_comment_from_root(interface)
|
||||
new_comment = comment.text
|
||||
comment.text = original_comment.text
|
||||
|
||||
# Calculate hash
|
||||
hash_file = FileHash()
|
||||
root.write(hash_file, pretty_print=True, xml_declaration=True, encoding="UTF-8")
|
||||
hexdigest = hash_file.hexdigest()
|
||||
hash_file.close()
|
||||
|
||||
comment.text = new_comment
|
||||
dirty = original_hash != hexdigest
|
||||
|
||||
if dirty:
|
||||
# Dump xml to file
|
||||
with open(fullpath, "wb") as fd:
|
||||
hash_file = FileHash(fd)
|
||||
root.write(hash_file, pretty_print=True, xml_declaration=True, encoding="UTF-8")
|
||||
hexdigest = hash_file.hexdigest()
|
||||
hash_file.close()
|
||||
|
||||
comment.text = new_comment
|
||||
|
||||
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
|
||||
with open(fullpath, "wb") as fd:
|
||||
hash_file = FileHash(fd)
|
||||
root.write(hash_file, pretty_print=True, xml_declaration=True, encoding="UTF-8")
|
||||
hexdigest = hash_file.hexdigest()
|
||||
hash_file.close()
|
||||
|
||||
# Store filename and hash in node
|
||||
utils.xml_node_set(node, "filename", filename)
|
||||
utils.xml_node_set(node, "sha256", hexdigest)
|
||||
@ -665,8 +631,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
self.__save_xml_and_update_node(ui, root, filename)
|
||||
else:
|
||||
# Embed UI content in project as CDATA
|
||||
root = self.db.export_ui(ui_id)
|
||||
self.__save_xml_in_node(ui, root.getroot())
|
||||
root = self.db.export_ui(ui_id, skip_version_comment=True)
|
||||
self.__save_xml_in_node(ui, root)
|
||||
|
||||
return ui
|
||||
|
||||
@ -689,8 +655,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
self.__save_xml_and_update_node(gresources, root, filename)
|
||||
else:
|
||||
# Embed file contents in project as CDATA
|
||||
root = self.db.export_gresource(gresource_id)
|
||||
self.__save_xml_in_node(gresources, root.getroot())
|
||||
root = self.db.export_gresource(gresource_id, skip_version_comment=True)
|
||||
self.__save_xml_in_node(gresources, root)
|
||||
|
||||
return gresources
|
||||
|
||||
@ -808,12 +774,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
# Import file
|
||||
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)
|
||||
self.foreign_keys = True
|
||||
|
||||
@ -1038,41 +999,33 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
self.db.commit()
|
||||
self.history_pop()
|
||||
except Exception:
|
||||
logger.warning("Tried to add GResource", exc_info=True)
|
||||
return None
|
||||
finally:
|
||||
gresource = self.__add_gresource(True, gresource_id, resource_type)
|
||||
gresource._update_new_parent()
|
||||
return gresource
|
||||
else:
|
||||
return self.__add_gresource(True, gresource_id, resource_type)
|
||||
|
||||
def __remove_gresource(self, gresource):
|
||||
if gresource is None:
|
||||
logger.warning("Tried to remove a None GResource", exc_info=True)
|
||||
return
|
||||
|
||||
self.__selection_remove(gresource)
|
||||
print("__remove_gresource", gresource, self.__gresource_id.get(gresource.gresource_id, None))
|
||||
|
||||
self.__gresource_id.pop(gresource.gresource_id, None)
|
||||
|
||||
self.__selection_remove(gresource)
|
||||
print("SELECTION", self.__selection)
|
||||
self.emit("gresource-removed", gresource)
|
||||
|
||||
def remove_gresource(self, gresource):
|
||||
try:
|
||||
parent_id = gresource.parent_id
|
||||
|
||||
gresource._save_last_known_parent_and_position()
|
||||
print("remove_gresource", gresource)
|
||||
self.history_push(_('Remove GResource "{name}"').format(name=gresource.display_name))
|
||||
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.db.commit()
|
||||
self.__remove_gresource(gresource)
|
||||
except Exception as e:
|
||||
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):
|
||||
return list(self.__css_id.values())
|
||||
@ -1164,7 +1117,7 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
except Exception as e:
|
||||
logger.warning(f"Error adding object {obj_name}: {e}")
|
||||
return None
|
||||
finally:
|
||||
else:
|
||||
obj = self.__add_object(True, ui_id, object_id, obj_type, name, parent_id, position=position)
|
||||
obj._update_new_parent()
|
||||
return obj
|
||||
@ -1330,17 +1283,10 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
obj._property_changed(p)
|
||||
|
||||
def __undo_redo_do(self, undo, update_objects=None):
|
||||
def get_object_position(table, row):
|
||||
if table == "object":
|
||||
ui_id, parent_id, position = row[0], row[4], row[8]
|
||||
parent = self.get_object_by_id(ui_id, parent_id)
|
||||
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
|
||||
def get_object_position(c, row):
|
||||
ui_id, parent_id, position = row[0], row[4], row[8]
|
||||
parent = self.get_object_by_id(ui_id, parent_id)
|
||||
return parent, position
|
||||
|
||||
c = self.db.cursor()
|
||||
|
||||
@ -1357,8 +1303,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
# Undo or Redo command
|
||||
if command == "INSERT":
|
||||
if table in ["object", "gresource"]:
|
||||
parent, position = get_object_position(table, new_values)
|
||||
if table == "object":
|
||||
parent, position = get_object_position(c, new_values)
|
||||
|
||||
if undo:
|
||||
update_objects.append((parent, position, 1, 0))
|
||||
@ -1372,8 +1318,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
self.__undo_redo_update_insert_delete(c, undo, command, table, columns, table_pk, old_values, new_values)
|
||||
elif command == "DELETE":
|
||||
if table in ["object", "gresource"]:
|
||||
parent, position = get_object_position(table, old_values)
|
||||
if table == "object":
|
||||
parent, position = get_object_position(c, old_values)
|
||||
|
||||
if undo:
|
||||
update_objects.append((parent, position, 0, 1))
|
||||
@ -1389,8 +1335,8 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
elif command == "UPDATE":
|
||||
# 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:
|
||||
old_parent, old_position = get_object_position(table, old_values)
|
||||
new_parent, new_position = get_object_position(table, new_values)
|
||||
old_parent, old_position = get_object_position(c, old_values)
|
||||
new_parent, new_position = get_object_position(c, new_values)
|
||||
|
||||
if undo:
|
||||
if old_position >= 0:
|
||||
@ -1402,9 +1348,6 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
update_objects.append((new_parent, new_position, 0, 1))
|
||||
if old_position >= 0:
|
||||
update_objects.append((old_parent, old_position, 1, 0))
|
||||
elif table == "gresource":
|
||||
# TODO
|
||||
pass
|
||||
|
||||
if undo:
|
||||
self.db.history_update(table, columns, table_pk, old_values)
|
||||
@ -1533,27 +1476,12 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
else:
|
||||
obj._remove_data(data)
|
||||
else:
|
||||
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)
|
||||
parent = obj.data_dict.get(f"{row[2]}.{row[6]}", None)
|
||||
|
||||
if parent:
|
||||
parent._add_child(owner_id, data_id, id)
|
||||
parent._add_child(row[2], row[3], row[4])
|
||||
else:
|
||||
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)
|
||||
obj._add_data(row[2], row[3], row[4])
|
||||
elif table == "object_data_arg":
|
||||
obj = self.get_object_by_id(pk[0], pk[1])
|
||||
if obj:
|
||||
@ -2280,7 +2208,6 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
i += 1
|
||||
|
||||
item.path_parent = None
|
||||
self.__items.insert(i, item)
|
||||
self.items_changed(i, 0, 1)
|
||||
|
||||
@ -2336,45 +2263,23 @@ class CmbProject(GObject.Object, Gio.ListModel):
|
||||
|
||||
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
|
||||
self.__remove_item(item)
|
||||
# add it again
|
||||
self.__add_item(item, filename)
|
||||
|
||||
if in_selection:
|
||||
GLib.idle_add(self.__set_selection_idle, item)
|
||||
self.set_selection([item])
|
||||
|
||||
# Clear unused paths
|
||||
if path_parent and path_parent.n_items == 0:
|
||||
GLib.idle_add(self.__clear_unused_paths_idle, path_parent)
|
||||
if path_parent.n_items == 0:
|
||||
while path_parent is not None:
|
||||
next_parent = path_parent.path_parent
|
||||
|
||||
def __set_selection_idle(self, item):
|
||||
self.set_selection([item])
|
||||
return GLib.SOURCE_REMOVE
|
||||
if path_parent.n_items <= 1:
|
||||
logger.warning(path_parent)
|
||||
|
||||
def __clear_unused_paths_idle(self, path_parent):
|
||||
if path_parent.n_items:
|
||||
return
|
||||
|
||||
while path_parent is not None:
|
||||
next_parent = path_parent.path_parent
|
||||
|
||||
if path_parent.n_items != 1:
|
||||
break
|
||||
|
||||
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
|
||||
path_parent = next_parent
|
||||
|
||||
def do_ui_added(self, ui):
|
||||
self.__add_item(ui, ui.filename)
|
||||
|
@ -331,7 +331,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
|
||||
|
||||
return False
|
||||
|
||||
def enum_get_value_as_string(self, value, use_nick=True):
|
||||
def enum_get_value_as_string(self, value):
|
||||
if self.parent_id != "enum":
|
||||
return None
|
||||
|
||||
@ -340,7 +340,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
|
||||
|
||||
# Always use nick as value
|
||||
if value == enum_name or value == enum_nick or value == str(enum_value):
|
||||
return enum_nick if use_nick else enum_value
|
||||
return enum_nick
|
||||
|
||||
return None
|
||||
|
||||
|
@ -65,10 +65,6 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
|
||||
def __on_notify(self, obj, pspec):
|
||||
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):
|
||||
retval = {}
|
||||
|
||||
|
@ -23,11 +23,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-only
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from gi.repository import GObject, Gtk
|
||||
|
||||
from cambalache import _
|
||||
from .cmb_ui import CmbUI
|
||||
|
||||
|
||||
@ -36,7 +33,6 @@ class CmbUIEditor(Gtk.Grid):
|
||||
__gtype_name__ = "CmbUIEditor"
|
||||
|
||||
filename = Gtk.Template.Child()
|
||||
format = Gtk.Template.Child()
|
||||
template_id = Gtk.Template.Child()
|
||||
description = Gtk.Template.Child()
|
||||
copyright = Gtk.Template.Child()
|
||||
@ -58,6 +54,9 @@ class CmbUIEditor(Gtk.Grid):
|
||||
|
||||
@object.setter
|
||||
def _set_object(self, obj):
|
||||
if obj == self._object:
|
||||
return
|
||||
|
||||
for binding in self._bindings:
|
||||
binding.unbind()
|
||||
|
||||
@ -80,15 +79,9 @@ class CmbUIEditor(Gtk.Grid):
|
||||
self.template_id.object = obj
|
||||
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:
|
||||
binding = obj.bind_property(
|
||||
binding = GObject.Object.bind_property(
|
||||
obj,
|
||||
field,
|
||||
getattr(self, field),
|
||||
"cmb-value",
|
||||
@ -96,42 +89,5 @@ class CmbUIEditor(Gtk.Grid):
|
||||
)
|
||||
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")
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Created with Cambalache 0.97.1 -->
|
||||
<!-- Created with Cambalache 0.95.0 -->
|
||||
<interface>
|
||||
<!-- interface-name cmb_ui_editor.ui -->
|
||||
<!-- interface-copyright Juan Pablo Ugarte -->
|
||||
@ -27,7 +27,7 @@
|
||||
<property name="label" translatable="yes">Description:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">3</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -37,7 +37,7 @@
|
||||
<property name="label" translatable="yes">Copyright:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">4</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -47,7 +47,7 @@
|
||||
<property name="label" translatable="yes">Authors:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">5</property>
|
||||
<property name="row">4</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -57,7 +57,7 @@
|
||||
<property name="label" translatable="yes">Domain:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">6</property>
|
||||
<property name="row">5</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -78,7 +78,7 @@
|
||||
<property name="visible">True</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">6</property>
|
||||
<property name="row">5</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -95,7 +95,7 @@
|
||||
<property name="min-content-height">96</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">3</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -112,7 +112,7 @@
|
||||
<property name="min-content-height">96</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">5</property>
|
||||
<property name="row">4</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -124,7 +124,7 @@
|
||||
<property name="visible">True</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">2</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -134,7 +134,7 @@
|
||||
<property name="label" translatable="yes">Template:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">2</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -151,7 +151,7 @@
|
||||
<property name="min-content-height">96</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">4</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -161,7 +161,7 @@
|
||||
<property name="label" translatable="yes">Comment:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">7</property>
|
||||
<property name="row">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
@ -178,34 +178,7 @@
|
||||
<property name="min-content-height">96</property>
|
||||
<layout>
|
||||
<property name="column">1</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>
|
||||
<property name="row">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -49,9 +49,4 @@ class CmbVersionNotificationView(Gtk.Box):
|
||||
notification = self.notification
|
||||
self.version_label.props.label = _("<b>Version {version} is available</b>").format(version=notification.version)
|
||||
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.show()
|
||||
else:
|
||||
self.read_more_button.hide()
|
||||
self.read_more_button.props.uri = notification.read_more_url
|
||||
|
@ -39,9 +39,8 @@ from . import config
|
||||
from .cmb_ui import CmbUI
|
||||
from .cmb_object import CmbObject
|
||||
from .cmb_context_menu import CmbContextMenu
|
||||
from cambalache.cmb_blueprint import cmb_blueprint_decompile
|
||||
from . import utils
|
||||
from cambalache import getLogger, _, N_
|
||||
from cambalache import getLogger, _
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
@ -168,6 +167,7 @@ class CmbMerengueProcess(GObject.Object):
|
||||
env = json.loads(os.environ.get("MERENGUE_DEV_ENV", "{}"))
|
||||
env = env | {
|
||||
"GDK_BACKEND": "wayland",
|
||||
"GSK_RENDERER": "cairo",
|
||||
"WAYLAND_DISPLAY": self.wayland_display,
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ class CmbView(Gtk.Box):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.__project = None
|
||||
self.__ui = None
|
||||
self.__ui_id = 0
|
||||
self.__theme = None
|
||||
|
||||
self.menu = self.__create_context_menu()
|
||||
@ -337,34 +337,14 @@ class CmbView(Gtk.Box):
|
||||
return self.__project.db.tostring(ui_id, merengue=merengue)
|
||||
|
||||
def __update_view(self):
|
||||
if self.__project and self.__ui:
|
||||
if self.__project and self.__ui_id > 0:
|
||||
if self.stack.props.visible_child_name == "ui_xml":
|
||||
ui_source = self.__get_ui_xml(self.__ui.ui_id)
|
||||
|
||||
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)
|
||||
ui = self.__get_ui_xml(self.__ui_id)
|
||||
self.text_view.buffer.set_text(ui)
|
||||
return
|
||||
|
||||
self.text_view.buffer.set_text("")
|
||||
self.__ui = None
|
||||
self.__ui_id = 0
|
||||
|
||||
def __get_ui_dirname(self, ui_id):
|
||||
dirname = GLib.get_home_dir()
|
||||
@ -468,23 +448,19 @@ class CmbView(Gtk.Box):
|
||||
if len(selection) > 0:
|
||||
obj = selection[0]
|
||||
|
||||
if isinstance(obj, CmbUI):
|
||||
ui = obj
|
||||
elif isinstance(obj, CmbObject):
|
||||
ui = obj.ui
|
||||
else:
|
||||
if type(obj) not in [CmbUI, CmbObject]:
|
||||
return
|
||||
|
||||
ui_id = obj.ui_id
|
||||
|
||||
if self.__ui != ui:
|
||||
self.__ui = ui
|
||||
self.__merengue_update_ui(ui.ui_id)
|
||||
if self.__ui_id != ui_id:
|
||||
self.__ui_id = ui_id
|
||||
self.__merengue_update_ui(ui_id)
|
||||
|
||||
objects = self.__get_selection_objects(selection, ui.ui_id)
|
||||
objects = self.__get_selection_objects(selection, ui_id)
|
||||
self.__merengue_command("selection_changed", args={"ui_id": ui_id, "selection": objects})
|
||||
else:
|
||||
self.__ui = None
|
||||
self.__ui_id = 0
|
||||
self.__merengue_update_ui(0)
|
||||
|
||||
self.__update_view()
|
||||
@ -656,7 +632,7 @@ class CmbView(Gtk.Box):
|
||||
self.__merengue_last_exit = None
|
||||
return
|
||||
|
||||
self.__ui = None
|
||||
self.__ui_id = 0
|
||||
self.__merengue.start()
|
||||
|
||||
def __command_selection_changed(self, selection):
|
||||
@ -715,7 +691,7 @@ class CmbView(Gtk.Box):
|
||||
|
||||
self.__load_css_providers()
|
||||
|
||||
self.__ui = None
|
||||
self.__ui_id = 0
|
||||
self.__on_project_selection_changed(self.__project)
|
||||
elif command == "placeholder_selected":
|
||||
self.emit(
|
||||
|
@ -35,56 +35,36 @@ class CmbFileButton(Gtk.Button):
|
||||
|
||||
dirname = GObject.Property(type=str, 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()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.__filename = None
|
||||
self.__filters = None
|
||||
|
||||
@Gtk.Template.Callback("on_button_clicked")
|
||||
def __on_button_clicked(self, button):
|
||||
dialog = Gtk.FileDialog(
|
||||
modal=True,
|
||||
filters=self.__filters,
|
||||
title=self.dialog_title,
|
||||
accept_label=self.accept_label
|
||||
title=self.dialog_title
|
||||
)
|
||||
|
||||
if self.dirname is not None:
|
||||
if self.__filename:
|
||||
if self.__filename is not None:
|
||||
fullpath = os.path.join(self.dirname, self.__filename)
|
||||
|
||||
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
|
||||
dialog.set_initial_file(Gio.File.new_for_path(fullpath))
|
||||
else:
|
||||
dialog.set_initial_folder(Gio.File.new_for_path(self.dirname))
|
||||
if self.unnamed_filename:
|
||||
dialog.set_initial_name(self.unnamed_filename)
|
||||
# dialog.set_initial_name("unnamed.ui")
|
||||
|
||||
def dialog_callback(dialog, res):
|
||||
try:
|
||||
file = dialog.open_finish(res) if self.use_open else dialog.save_finish(res)
|
||||
file = dialog.save_finish(res)
|
||||
self.cmb_value = os.path.relpath(file.get_path(), start=self.dirname)
|
||||
except Exception:
|
||||
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)
|
||||
def cmb_value(self):
|
||||
@ -97,18 +77,3 @@ class CmbFileButton(Gtk.Button):
|
||||
|
||||
self.__filename = value if value is not None else ""
|
||||
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,8 +40,7 @@ class CmbSourceView(GtkSource.View):
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def lang(self):
|
||||
language = self.buffer.get_language()
|
||||
return language.get_id() if language else ""
|
||||
return self.buffer.get_language()
|
||||
|
||||
@lang.setter
|
||||
def _set_lang(self, value):
|
||||
|
@ -26,7 +26,6 @@ configure_file(
|
||||
install_data([
|
||||
'cmb_accessible_editor.py',
|
||||
'cmb_base.py',
|
||||
'cmb_blueprint.py',
|
||||
'cmb_context_menu.py',
|
||||
'cmb_css.py',
|
||||
'cmb_css_editor.py',
|
||||
|
@ -1,6 +1,6 @@
|
||||
project(
|
||||
'cambalache', 'c',
|
||||
version: '0.97.3',
|
||||
version: '0.96.0',
|
||||
meson_version: '>= 1.1.0',
|
||||
default_options: [
|
||||
'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')
|
||||
pygobject_dep = dependency('pygobject-3.0', version: '>= 3.52.0')
|
||||
gtk4_dep = dependency('gtk4', version: '>= 4.18.0')
|
||||
casilda_dep = dependency('casilda-0.1', version: '>= 0.9.0', fallback: ['casilda', 'casilda_dep'])
|
||||
casilda_dep = dependency('casilda-0.1', version: '>= 0.2.0', fallback: ['casilda', 'casilda_dep'])
|
||||
adw_dep = dependency('libadwaita-1', version: '>= 1.7.0')
|
||||
gtksource_dep = dependency('gtksourceview-5', version: '>= 5.16.0')
|
||||
|
||||
|
39
run-dev.py
39
run-dev.py
@ -1,39 +0,0 @@
|
||||
#!/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
Executable file
16
run-dev.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/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
|
@ -1,7 +1,11 @@
|
||||
#!/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
|
||||
|
||||
export COVERAGE_PROCESS_START=$DIRNAME/pyproject.toml
|
||||
|
10
run-tests.sh
Executable file
10
run-tests.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/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]
|
||||
directory = casilda
|
||||
url = https://gitlab.gnome.org/jpu/casilda.git
|
||||
revision = 0.9.0
|
||||
revision = 0.2.0
|
||||
depth = 1
|
||||
|
@ -1,15 +1,5 @@
|
||||
import os
|
||||
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")
|
||||
from gi.repository import Gtk # 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_default2"/>
|
||||
<signal name="add" handler="on_add"/>
|
||||
<signal name="focus" handler="on_focus" swapped="True"/>
|
||||
<signal name="focus-in-event" handler="on_focus_in_event" after="True"/>
|
||||
<signal name="focus-out-event" handler="on_focus_out_event" swapped="True" after="True"/>
|
||||
<signal name="focus" handler="on_focus" swapped="yes"/>
|
||||
<signal name="focus-in-event" handler="on_focus_in_event" after="yes"/>
|
||||
<signal name="focus-out-event" handler="on_focus_out_event" swapped="yes" after="yes"/>
|
||||
</object>
|
||||
<object class="GtkDialog" id="dialog1"/>
|
||||
</interface>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<accessibility>
|
||||
<property name="description">help text</property>
|
||||
<property name="help-text">help text</property>
|
||||
<property name="label">a label</property>
|
||||
<relation name="described-by">a11y3</relation>
|
||||
<relation name="details">a11y2</relation>
|
||||
|
@ -9,11 +9,11 @@
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<signal name="activate" handler="on_button_activate"/>
|
||||
<signal name="clicked" handler="on_button_clicked" swapped="True"/>
|
||||
<signal name="clicked" handler="on_button_clicked2" after="True"/>
|
||||
<signal name="clicked" handler="on_button_clicked3" swapped="True" after="True"/>
|
||||
<signal name="clicked" handler="on_button_clicked" swapped="yes"/>
|
||||
<signal name="clicked" handler="on_button_clicked2" after="yes"/>
|
||||
<signal name="clicked" handler="on_button_clicked3" swapped="yes" after="yes"/>
|
||||
<signal name="clicked" handler="on_button_clicked4" object="win1"/>
|
||||
<signal name="clicked" handler="on_button_clicked5" swapped="False" object="win1"/>
|
||||
<signal name="clicked" handler="on_button_clicked5" object="win1" swapped="no"/>
|
||||
<signal name="notify::label" handler="on_notify"/>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<interface>
|
||||
<!-- interface-name string_list.ui -->
|
||||
<requires lib="gtk" version="4.10"/>
|
||||
<requires lib="gtk" version="4.8"/>
|
||||
<object class="GtkStringList" id="list1">
|
||||
<property name="strings">a
|
||||
b
|
||||
|
@ -1,109 +0,0 @@
|
||||
#!/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
|
||||
now = utils.utcnow()
|
||||
CMB_UUID = notification_center.uuid
|
||||
CMB_UUID = str(uuid4())
|
||||
POLL_UUID = str(uuid4())
|
||||
|
||||
POLL_NOTIFICATION_BASE = {
|
||||
@ -44,14 +44,16 @@ def wait_for_all_threads():
|
||||
|
||||
|
||||
def test_cmb_notification_disabled():
|
||||
assert not notification_center.uuid
|
||||
assert notification_center.enabled is False
|
||||
assert len(notification_center.store) == 0
|
||||
notification_center.enabled = True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("response, n", [
|
||||
@pytest.mark.parametrize("response, headers, n", [
|
||||
(
|
||||
{
|
||||
"uuid": CMB_UUID,
|
||||
"notification": {
|
||||
"type": "version",
|
||||
"start_date": now - DAY,
|
||||
@ -61,10 +63,14 @@ def test_cmb_notification_disabled():
|
||||
"read_more_url": "http://localhost"
|
||||
}
|
||||
},
|
||||
{
|
||||
'User-Agent': notification_center.user_agent
|
||||
},
|
||||
1
|
||||
),
|
||||
(
|
||||
{
|
||||
"uuid": CMB_UUID,
|
||||
"notification": {
|
||||
"type": "message",
|
||||
"start_date": now - DAY,
|
||||
@ -73,16 +79,26 @@ def test_cmb_notification_disabled():
|
||||
"message": "This is a message notification"
|
||||
}
|
||||
},
|
||||
{
|
||||
'User-Agent': notification_center.user_agent,
|
||||
'x-cambalache-uuid': CMB_UUID
|
||||
},
|
||||
2
|
||||
),
|
||||
(
|
||||
{
|
||||
"uuid": CMB_UUID,
|
||||
"notification": POLL_NOTIFICATION_BASE
|
||||
},
|
||||
{
|
||||
'User-Agent': notification_center.user_agent,
|
||||
'x-cambalache-uuid': CMB_UUID
|
||||
},
|
||||
3
|
||||
),
|
||||
(
|
||||
{
|
||||
"uuid": CMB_UUID,
|
||||
"notification": {
|
||||
**POLL_NOTIFICATION_BASE,
|
||||
"results": {
|
||||
@ -91,10 +107,14 @@ def test_cmb_notification_disabled():
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'User-Agent': notification_center.user_agent,
|
||||
'x-cambalache-uuid': CMB_UUID
|
||||
},
|
||||
4
|
||||
)
|
||||
])
|
||||
def test_cmb_notification_get(mocker, response, n):
|
||||
def test_cmb_notification_get(mocker, response, headers, n):
|
||||
wait_for_all_threads()
|
||||
|
||||
mocker.patch(
|
||||
@ -117,14 +137,7 @@ def test_cmb_notification_get(mocker, response, n):
|
||||
|
||||
test_utils.process_all_pending_gtk_events()
|
||||
|
||||
request_mock.assert_called_with(
|
||||
"GET",
|
||||
"/notification",
|
||||
headers={
|
||||
'User-Agent': notification_center.user_agent,
|
||||
'x-cambalache-uuid': CMB_UUID
|
||||
}
|
||||
)
|
||||
request_mock.assert_called_with("GET", "/notification", headers=headers)
|
||||
|
||||
assert notification_center.uuid == CMB_UUID
|
||||
on_new_notification.assert_called()
|
||||
|
@ -4,52 +4,11 @@
|
||||
import .ui files into cambalache and export to compare results
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from cambalache import CmbProject, config
|
||||
|
||||
|
||||
@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):
|
||||
def assert_original_and_exported(target_tk, filename):
|
||||
"""
|
||||
import .ui file and compare it with the exported version
|
||||
"""
|
||||
@ -70,3 +29,160 @@ def test_(target_tk, filename):
|
||||
|
||||
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
|
||||
#
|
||||
# Copyright (C) 2021-2025 Juan Pablo Ugarte
|
||||
# Copyright (C) 2021-2024 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
|
||||
@ -22,46 +22,18 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import gi
|
||||
import sys
|
||||
import stat
|
||||
import signal
|
||||
import subprocess
|
||||
|
||||
basedir = os.path.join(os.path.split(os.path.dirname(__file__))[0])
|
||||
localdir = os.path.join(basedir, ".local")
|
||||
locallibdir = os.path.join(localdir, "lib", sys.implementation._multiarch)
|
||||
sys.path.insert(1, basedir)
|
||||
|
||||
cambalachedir = os.path.join(basedir, "cambalache")
|
||||
localdir = os.path.join(basedir, ".local")
|
||||
localpkgdatadir = os.path.join(localdir, "share", "cambalache")
|
||||
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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user