From ccfdecb334ccdf68796f9e939b0012e26aa2ebd6 Mon Sep 17 00:00:00 2001 From: Matt Lewis Date: Sat, 25 May 2024 18:42:44 +0200 Subject: [PATCH] GUI. Changed cut/copy/paste behaviour when the clipboard library is not present: Put iCal object on clipboard as text. When pasting, try to interpret data as iCal object, and only use as text if that doesn't work. --- docs/Development.md | 14 +++++++----- docs/known_issues.md | 5 ----- pygenda/pygenda_gui.py | 51 +++++++++++++++++++++++++----------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/docs/Development.md b/docs/Development.md index 86814a1..d544d6c 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -17,11 +17,15 @@ clipboard library manually, as in the section below.) Building clipboard library -------------------------- -This is a small C library required for cutting/copying entries. Built -automatically with `./setup.py install`. Tested on Gemian, postmarketOS -and Slackware, and probably works on other Linux distributions. It might -need tweaking for Windows/MacOS/BSD/other. To build by hand and copy to -the correct location in the source tree: +This is a small C library to improve cutting/copying behaviour. +Specifically it allows Pygenda entries to be copied+pasted as text +into applications that expect text, and as calendar entries into +applications that expect clendar entries. Built automatically with +`./setup.py install`. Tested on Gemian, postmarketOS and Slackware, +and probably works on other Linux distributions. It might need +tweaking for Windows/MacOS/BSD/other. + +To build by hand and copy to the correct location in the source tree: cd csrc cmake . diff --git a/docs/known_issues.md b/docs/known_issues.md index 1e11bbc..9a42c4c 100644 --- a/docs/known_issues.md +++ b/docs/known_issues.md @@ -128,11 +128,6 @@ Medium * An undo/redo function would be useful -* Copying/cutting entries doesn't work on all platforms. This functionality - relies on a small C library, due to limits of Python GObject module. - Builds on my devices and on Gemini. Needs looking at for other Linux - distributions and Windows/MacOS. - * Softkeys should be user-customisable * Date/time widgets are not easy to click (tap) on since most of the area diff --git a/pygenda/pygenda_gui.py b/pygenda/pygenda_gui.py index 3dcf709..ea3cabd 100644 --- a/pygenda/pygenda_gui.py +++ b/pygenda/pygenda_gui.py @@ -439,7 +439,7 @@ def _init_clipboard(cls) -> None: libclip_file = '{:s}/libpygenda_clipboard.so'.format(ospath.dirname(__file__)) cls._lib_clip = ctypes.CDLL(libclip_file) except: - print('Warning: Failed to load clipboard library', file=stderr) + print('Notice: Clipboard library not found, using fallback', file=stderr) @classmethod @@ -877,22 +877,17 @@ def cut_request(cls, *args) -> bool: # Handler to implement "cut" from GUI, e.g. cut clicked in menu en = cls.views[cls._view_idx].get_cursor_entry() if en and 'SUMMARY' in en: - if cls._lib_clip is None: - # Don't do fallback - might lead to unexpected data loss - print('Warning: No clipboard library, cut not available', file=stderr) - elif 'RRULE' in en: # repeating entry + if 'RRULE' in en: # repeating entry # Need to think about how to implement this from UI side. # Problem: Does user expect single occurrence to be cut, or all? # Maybe bring up dialog "Cut single occurrence, or all repeats?" # Then, do we do the came for Copying repeating entries? # How do we adapt repeats when moved to a different date? print('Warning: Cutting repeating entries not implemented', file=stderr) - else: - txtbuf = bytes(en['SUMMARY'], 'utf-8') - calbuf = en.to_ical() - cls._lib_clip.set_cb(ctypes.create_string_buffer(txtbuf),ctypes.create_string_buffer(calbuf)) - Calendar.delete_entry(en) - cls.view_redraw(en_changes=True) + return True + cls._put_entry_on_clipboard(en) + Calendar.delete_entry(en) + cls.view_redraw(en_changes=True) return True # don't propagate event @@ -901,18 +896,21 @@ def copy_request(cls, *args) -> bool: # Handler to implement "copy" from GUI, e.g. copy clicked in menu en = cls.views[cls._view_idx].get_cursor_entry() if en and 'SUMMARY' in en: - if cls._lib_clip is None: - print('Warning: No clipboard library, fallback to text copy', file=stderr) - cb = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) - txt = en['SUMMARY'] - cb.set_text(txt, -1) - else: - txtbuf = bytes(en['SUMMARY'], 'utf-8') - calbuf = en.to_ical() - cls._lib_clip.set_cb(ctypes.create_string_buffer(txtbuf),ctypes.create_string_buffer(calbuf)) + cls._put_entry_on_clipboard(en) return True # don't propagate event + @classmethod + def _put_entry_on_clipboard(cls, en:Union[iEvent,iTodo]) -> None: + calbuf = en.to_ical() + if cls._lib_clip is None: + cb = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + cb.set_text(calbuf.decode('utf-8'), -1) + else: + txtbuf = bytes(en['SUMMARY'], 'utf-8') + cls._lib_clip.set_cb(ctypes.create_string_buffer(txtbuf),ctypes.create_string_buffer(calbuf)) + + @classmethod def paste_request(cls, *args) -> bool: # Handler to implement "paste" from GUI, e.g. paste clicked in menu @@ -929,6 +927,19 @@ def paste_request(cls, *args) -> bool: # Fallback: request plain text from clipboard txt = cb.wait_for_text() if txt: # might be None + if cls._lib_clip is None: + # "Text" might be ical data, let's check... + try: + ical = iCalendar.from_ical(txt) + en = ical.walk()[0] + cls.views[cls._view_idx].paste_entry(en) + cls.view_redraw(en_changes=True) + return True + except: + pass + # We got text. Either clipboard library is present, or + # there was an exception trying to treat it as ical data. + # So, let's just treat it as plain text... txt = cls._sanitise_pasted_text(txt) # Type of entry created depends on View, so call View paste fn if txt: