mirror of
https://gitlab.gnome.org/jpu/cambalache.git
synced 2025-06-25 00:02:51 -04:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ae11d6485a | ||
|
a69df48d17 | ||
|
090ef7f633 | ||
|
4cce14ea3f | ||
|
7ebfa1999c | ||
|
d81f8ee7fd | ||
|
aef72f38cc | ||
|
3527266b4a | ||
|
d8f59a9144 | ||
|
c8a3f36641 |
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
|
|
@ -9,6 +9,11 @@ Cambalache used even/odd minor numbers to differentiate between stable and
|
|||||||
development releases.
|
development releases.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.96.1
|
||||||
|
|
||||||
|
- Fix/improve GResource list model update
|
||||||
|
- Fix removing custom class data like styles
|
||||||
|
|
||||||
## 0.96.0
|
## 0.96.0
|
||||||
|
|
||||||
- Add GResource support
|
- Add GResource support
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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"/>
|
||||||
|
@ -2023,7 +2023,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)
|
||||||
|
|
||||||
@ -3034,6 +3034,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 = 2
|
||||||
|
|
||||||
data = response.read().decode()
|
data = response.read().decode()
|
||||||
|
|
||||||
@ -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
|
||||||
|
@ -70,7 +70,10 @@ class CmbObjectDataEditor(Gtk.Box):
|
|||||||
if self.info:
|
if self.info:
|
||||||
self.object.remove_data(self.__data)
|
self.object.remove_data(self.__data)
|
||||||
else:
|
else:
|
||||||
self.__data.parent.remove_data(self.__data)
|
if self.__data.parent:
|
||||||
|
self.__data.parent.remove_data(self.__data)
|
||||||
|
else:
|
||||||
|
self.__data.object.remove_data(self.__data)
|
||||||
|
|
||||||
@GObject.Property(type=GObject.Object)
|
@GObject.Property(type=GObject.Object)
|
||||||
def object(self):
|
def object(self):
|
||||||
@ -133,13 +136,11 @@ class CmbObjectDataEditor(Gtk.Box):
|
|||||||
self.__update_arg(key)
|
self.__update_arg(key)
|
||||||
|
|
||||||
def __on_data_added(self, obj, data):
|
def __on_data_added(self, obj, data):
|
||||||
if self.info and self.data is None and self.info == data.info:
|
self.data = data
|
||||||
self.data = data
|
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):
|
||||||
if self.data:
|
if self.data:
|
||||||
|
@ -999,33 +999,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 +1125,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 +1291,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):
|
||||||
ui_id, parent_id, position = row[0], row[4], row[8]
|
if table == "object":
|
||||||
parent = self.get_object_by_id(ui_id, parent_id)
|
ui_id, parent_id, position = row[0], row[4], row[8]
|
||||||
return parent, position
|
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
|
||||||
|
|
||||||
c = self.db.cursor()
|
c = self.db.cursor()
|
||||||
|
|
||||||
@ -1303,8 +1318,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 +1333,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 +1350,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 +1363,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 +1494,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:
|
||||||
|
@ -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
|
||||||
self.read_more_button.props.uri = notification.read_more_url
|
|
||||||
|
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()
|
||||||
|
@ -35,6 +35,7 @@ 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)
|
||||||
|
use_open = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
|
||||||
|
|
||||||
label = Gtk.Template.Child()
|
label = Gtk.Template.Child()
|
||||||
|
|
||||||
@ -59,12 +60,15 @@ class CmbFileButton(Gtk.Button):
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
dialog.save(self.get_root(), None, dialog_callback)
|
if self.use_open:
|
||||||
|
dialog.open(self.get_root(), None, dialog_callback)
|
||||||
|
else:
|
||||||
|
dialog.save(self.get_root(), None, dialog_callback)
|
||||||
|
|
||||||
@GObject.Property(type=str)
|
@GObject.Property(type=str)
|
||||||
def cmb_value(self):
|
def cmb_value(self):
|
||||||
|
@ -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
|
||||||
|
|
@ -14,6 +14,15 @@
|
|||||||
</p>
|
</p>
|
||||||
</description>
|
</description>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2025-05-16" version="0.96.1">
|
||||||
|
<description>
|
||||||
|
<p>GResource First Bugfix Release!</p>
|
||||||
|
<ul>
|
||||||
|
<li>Fix/improve GResource list model update</li>
|
||||||
|
<li>Fix removing custom class data like styles</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release date="2025-04-20" version="0.96.0">
|
<release date="2025-04-20" version="0.96.0">
|
||||||
<description>
|
<description>
|
||||||
<p>GResource Release!</p>
|
<p>GResource Release!</p>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'cambalache', 'c',
|
'cambalache', 'c',
|
||||||
version: '0.96.0',
|
version: '0.96.1',
|
||||||
meson_version: '>= 1.1.0',
|
meson_version: '>= 1.1.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
|
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,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
|
||||||
|
@ -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()
|
||||||
|
@ -28,12 +28,29 @@ 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")
|
||||||
|
|
||||||
|
for var, value in [
|
||||||
|
("LD_LIBRARY_PATH", f"{locallibdir}:{locallibdir}/cambalache:{locallibdir}/cmb_catalog_gen"),
|
||||||
|
("GI_TYPELIB_PATH", f"{locallibdir}/girepository-1.0:{locallibdir}/cambalache:{locallibdir}/cmb_catalog_gen"),
|
||||||
|
("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