From 76a4b2e0e23d9437c0750c0175b8d84967c79143 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sat, 27 Dec 2025 15:26:43 -0600 Subject: [PATCH] Enhanced rename functionality to target selection bounds f present; WIP cbindings for memreaper effort --- .../core/widgets/dialogs/rename_widget.py | 48 +++++++++++++++++-- .../core/widgets/files_view/tab_mixin.py | 8 ++-- src/solarfm/core/widgets/icon_grid_widget.py | 34 ++++++++----- src/solarfm/utils/cbindings/gtkmemreaper.py | 20 ++++++++ .../python_package_works/gtkmemreaper.c | 21 ++++---- .../cbindings/python_package_works/setup.py | 2 +- 6 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 src/solarfm/utils/cbindings/gtkmemreaper.py diff --git a/src/solarfm/core/widgets/dialogs/rename_widget.py b/src/solarfm/core/widgets/dialogs/rename_widget.py index 2e3ae1a..dfb69b4 100644 --- a/src/solarfm/core/widgets/dialogs/rename_widget.py +++ b/src/solarfm/core/widgets/dialogs/rename_widget.py @@ -64,16 +64,56 @@ class RenameWidget: def set_to_title_case(self, widget, eve=None): - self._rename_fname.set_text( self._rename_fname.get_text().title() ) + bounds = self._rename_fname.get_selection_bounds() + fname = self._rename_fname.get_text() + + if bounds: + start, end = bounds + replace = fname[start:end] + fname = fname.replace(replace, replace.title()) + else: + fname = fname.title() + + self._rename_fname.set_text(fname) def set_to_upper_case(self, widget, eve=None): - self._rename_fname.set_text( self._rename_fname.get_text().upper() ) + bounds = self._rename_fname.get_selection_bounds() + fname = self._rename_fname.get_text() + + if bounds: + start, end = bounds + replace = fname[start:end] + fname = fname.replace(replace, replace.upper()) + else: + fname = fname.upper() + + self._rename_fname.set_text(fname) def set_to_lower_case(self, widget, eve=None): - self._rename_fname.set_text( self._rename_fname.get_text().lower() ) + bounds = self._rename_fname.get_selection_bounds() + fname = self._rename_fname.get_text() + + if bounds: + start, end = bounds + replace = fname[start:end] + fname = fname.replace(replace, replace.lower()) + else: + fname = fname.lower() + + self._rename_fname.set_text(fname) def set_to_invert_case(self, widget, eve=None): - self._rename_fname.set_text( self._rename_fname.get_text().swapcase() ) + bounds = self._rename_fname.get_selection_bounds() + fname = self._rename_fname.get_text() + + if bounds: + start, end = bounds + replace = fname[start:end] + fname = fname.replace(replace, replace.swapcase()) + else: + fname = fname.swapcase() + + self._rename_fname.set_text(fname) def hide_rename_file_menu(self, widget=None, eve=None): self._rename_file_menu.hide() diff --git a/src/solarfm/core/widgets/files_view/tab_mixin.py b/src/solarfm/core/widgets/files_view/tab_mixin.py index 91241d8..f67eb05 100644 --- a/src/solarfm/core/widgets/files_view/tab_mixin.py +++ b/src/solarfm/core/widgets/files_view/tab_mixin.py @@ -100,13 +100,15 @@ class TabMixin(GridMixin): icon_grid.unparent() scroll.unparent() - Gtk.main_iteration_do(False) - gc.collect() - logger.debug(f"Reference count for tab_box is: {tab_box.__grefcount__}") logger.debug(f"Reference count for icon_grid is: {icon_grid.__grefcount__}") logger.debug(f"Reference count for scroll is: {scroll.__grefcount__}") + del tab_box + del icon_grid + del scroll + + gc.collect() if not settings_manager.is_trace_debug(): self.fm_controller.save_state() diff --git a/src/solarfm/core/widgets/icon_grid_widget.py b/src/solarfm/core/widgets/icon_grid_widget.py index 8263656..537da8e 100644 --- a/src/solarfm/core/widgets/icon_grid_widget.py +++ b/src/solarfm/core/widgets/icon_grid_widget.py @@ -9,8 +9,8 @@ from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GdkPixbuf - # Application imports +from utils.cbindings import gtkmemreaper @@ -48,19 +48,23 @@ class IconGridWidget(Gtk.IconView): def _setup_signals(self): ... - def _setup_additional_signals(self, grid_icon_single_click, - grid_icon_double_click, - grid_set_selected_items, - grid_on_drag_set, - grid_on_drag_data_received, - grid_on_drag_motion): + def _setup_additional_signals(self, + grid_icon_single_click, + grid_icon_double_click, + grid_set_selected_items, + grid_on_drag_set, + grid_on_drag_data_received, + grid_on_drag_motion + ): - self._handler_ids.append(self.connect("button_release_event", grid_icon_single_click)) - self._handler_ids.append(self.connect("item-activated", grid_icon_double_click)) - self._handler_ids.append(self.connect("selection-changed", grid_set_selected_items)) - self._handler_ids.append(self.connect("drag-data-get", grid_on_drag_set)) - self._handler_ids.append(self.connect("drag-data-received", grid_on_drag_data_received)) - self._handler_ids.append(self.connect("drag-motion", grid_on_drag_motion)) + self._handler_ids = [ + self.connect("button_release_event", grid_icon_single_click), + self.connect("item-activated", grid_icon_double_click), + self.connect("selection-changed", grid_set_selected_items), + self.connect("drag-data-get", grid_on_drag_set), + self.connect("drag-data-received", grid_on_drag_data_received), + self.connect("drag-motion", grid_on_drag_motion) + ] def _load_widgets(self): self.clear_and_set_new_store() @@ -113,10 +117,14 @@ class IconGridWidget(Gtk.IconView): if icon: logger.debug(f"Reference count for icon is: {icon.__grefcount__}") icon.run_dispose() + # icon_ptr = int(hash(icon)) # WARNING: not stable across runs + # gtkmemreaper.free_pixbuf(icon_ptr) del icon store.clear() store.run_dispose() + # store_ptr = int(hash(store)) # WARNING: not stable across runs + # gtkmemreaper.free_list_store(store) del store gc.collect() diff --git a/src/solarfm/utils/cbindings/gtkmemreaper.py b/src/solarfm/utils/cbindings/gtkmemreaper.py new file mode 100644 index 0000000..da1201c --- /dev/null +++ b/src/solarfm/utils/cbindings/gtkmemreaper.py @@ -0,0 +1,20 @@ +# Python imports +import pkg_resources +import importlib.util + +# Lib imports + +# Application imports + + + +def __bootstrap__(): + __file__ = pkg_resources.resource_filename(__name__, 'gtkmemreaper.cpython-313-x86_64-linux-gnu.so') + spec = importlib.util.spec_from_file_location( + __name__, + __file__ + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + +__bootstrap__() diff --git a/src/solarfm/utils/cbindings/python_package_works/gtkmemreaper.c b/src/solarfm/utils/cbindings/python_package_works/gtkmemreaper.c index 9fda6e0..e4d2c7f 100644 --- a/src/solarfm/utils/cbindings/python_package_works/gtkmemreaper.c +++ b/src/solarfm/utils/cbindings/python_package_works/gtkmemreaper.c @@ -1,42 +1,45 @@ #include -#include +#include #include #include #include // static PyObject* free_pixbuf(PyObject* self, PyObject* args) { -static void free_pixbuf(PyObject* self, PyObject* args) { +static PyObject* free_pixbuf(PyObject* self, PyObject* args) { PyObject *py_pixbuf; if (!PyArg_ParseTuple(args, "O", &py_pixbuf)) { - return NULL; + Py_RETURN_NONE; } GdkPixbuf *pixbuf = (GdkPixbuf *) PyLong_AsVoidPtr(py_pixbuf); if (!GDK_IS_PIXBUF(pixbuf)) { PyErr_SetString(PyExc_TypeError, "Invalid GdkPixbuf pointer."); - return NULL; + Py_RETURN_NONE; } - g_free(pixbuf); - // return PyBytes_FromStringAndSize((const char *) cairo_data, buffer_size); + g_object_unref(pixbuf); + g_assert_null(pixbuf); + Py_RETURN_NONE; } -static void free_list_store(PyObject* self, PyObject* args) { +static PyObject* free_list_store(PyObject* self, PyObject* args) { PyObject *py_list_store; if (!PyArg_ParseTuple(args, "O", &py_list_store)) { - return NULL; + Py_RETURN_NONE; } GtkListStore *list_store = (GtkListStore *) PyLong_AsVoidPtr(py_list_store); if (!GTK_IS_LIST_STORE(list_store)) { PyErr_SetString(PyExc_TypeError, "Invalid Gtk.ListStore pointer."); - return NULL; + Py_RETURN_NONE; } g_object_unref(list_store); + g_assert_null(list_store); + Py_RETURN_NONE; } diff --git a/src/solarfm/utils/cbindings/python_package_works/setup.py b/src/solarfm/utils/cbindings/python_package_works/setup.py index 77d881d..677b53d 100644 --- a/src/solarfm/utils/cbindings/python_package_works/setup.py +++ b/src/solarfm/utils/cbindings/python_package_works/setup.py @@ -8,7 +8,7 @@ from subprocess import check_output -pkg_config_args = ["gdk-pixbuf-2.0", "cairo", "gtk"] +pkg_config_args = ["gdk-pixbuf-2.0", "cairo", "gtk+-3.0"] def get_pkgconfig_flags(flag_type): return check_output(["pkg-config", flag_type] + pkg_config_args).decode().split()