From 76cdaa08cf3eed904afe79c9b67490515309cd1f Mon Sep 17 00:00:00 2001 From: Dale Sedivec Date: Mon, 6 Jul 2020 18:36:55 -0500 Subject: [PATCH 1/2] Update for changed API in Catalina --- quotefix/fixer.py | 65 ++++++++++++++++++++++++++++++++++++++++++++--- setup.py | 4 +++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/quotefix/fixer.py b/quotefix/fixer.py index 389603a..c903213 100644 --- a/quotefix/fixer.py +++ b/quotefix/fixer.py @@ -3,7 +3,7 @@ from quotefix.utils import swizzle from quotefix.attribution import CustomizedAttribution from quotefix.messagetypes import * -from objc import Category, lookUpClass +from objc import Category, lookUpClass, registerMetaDataForSelector from logger import logger import re, traceback, objc @@ -42,7 +42,25 @@ def sendEvent(self, original, event): ) original(self, event) -def fix(self): +def call_fix_later(self): + try: + logger.debug("entered fix_future") + backend = self.backEnd() + future = backend.messageFuture() + future.addSuccessBlock_(lambda message: fix(self, message)) + logger.debug("success block added") + except Exception: + logger.critical(traceback.format_exc()) + if self.app.is_debugging: + NSRunAlertPanel( + 'QuoteFix caught an exception', + 'The QuoteFix plug-in caught an exception in call_fix_later:\n\n' + + traceback.format_exc() + + '\nPlease contact the developer quoting the contents of this alert.', + None, None, None + ) + +def fix(self, message=None): try: # if toggle key is active, temporarily switch the active state is_active = self.app.toggle_key_active ^ self.app.is_active @@ -82,12 +100,18 @@ def fix(self): if attributor: try: + if message is None: + # Must be pre-Catalina, where we can get the + # message directly without having to go through + # the future. + assert not HAS_MESSAGE_FUTURE + message = backend.message() for original in objc.getInstanceVariable(backend, '_originalMessages'): attributor( app = self.app, editor = self, dom = htmldom, - reply = backend.message(), + reply = message, inreplyto = original, ) backend.setHasChanges_(False) @@ -384,7 +408,40 @@ def show(self, original): elif not self.move_above_new_signature(htmldom, view): view.moveToEndOfDocument_(self) - ComposeViewController.fix = fix + # As of Catalina, you can't get the message directly from the + # backend. Rather, you have to get the EFFuture instance (see + # private EmailFoundation.framework), and tell it to call you when + # the message is ready.) + ComposeBackEnd = lookUpClass("ComposeBackEnd") + HAS_MESSAGE_FUTURE = bool( + ComposeBackEnd.instancesRespondToSelector_("messageFuture") + ) + + if HAS_MESSAGE_FUTURE: + # Must tell PyObjC that [EFFuture addSuccessBlock:] takes a + # block that itself takes an object, which is presumably the + # result of the future. + registerMetaDataForSelector( + b"EFFuture", + b"addSuccessBlock:", + dict( + arguments={ + 2: { + "callable": { + "retval": {"type": b"v"}, + "arguments": { + "0": {"type": b"^v"}, + "1": {"type": b"@"}, + }, + }, + }, + }, + ), + ) + ComposeViewController.fix = call_fix_later + else: + ComposeViewController.fix = fix + ComposeViewController.remove_attachment_placeholders = remove_attachment_placeholders ComposeViewController.remove_quotes = remove_quotes ComposeViewController.make_selectable_quotes = make_selectable_quotes diff --git a/setup.py b/setup.py index 314ee58..fda1132 100644 --- a/setup.py +++ b/setup.py @@ -119,6 +119,10 @@ # Mail 11.0 (3445.1.3) High Sierra beta 'C86CD990-4660-4E36-8CDA-7454DEB2E199' ], + 'Supported10.15PluginCompatibilityUUIDs': [ + # Version 13.4 (3608.80.23.2.2) Catalina 10.15.5 + '6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053', + ], # settings for Sparkle 'SUFeedURL' : 'https://raw.github.com/robertklep/quotefixformac/master/updates/appcast.xml', 'SUEnableAutomaticChecks' : False, From 0b14ec48e770c55a96a7a55f2b246cbc1bb42e68 Mon Sep 17 00:00:00 2001 From: Dale Sedivec Date: Tue, 4 Aug 2020 02:22:55 -0500 Subject: [PATCH 2/2] Add a couple docstrings --- quotefix/fixer.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/quotefix/fixer.py b/quotefix/fixer.py index c903213..21603da 100644 --- a/quotefix/fixer.py +++ b/quotefix/fixer.py @@ -43,6 +43,13 @@ def sendEvent(self, original, event): original(self, event) def call_fix_later(self): + """Tell the ComposeBackEnd to call fix() later, when the message is ready. + + This became necessary starting around Catalina, when you could no + longer ask the ComposeBackEnd to immediately give you the message + instance. + + """ try: logger.debug("entered fix_future") backend = self.backEnd() @@ -61,6 +68,17 @@ def call_fix_later(self): ) def fix(self, message=None): + """Run all of the relevant fixes on a new message. + + This function is attached as a method on DocumentEditor and/or + ComposeViewController. + + If message is None then we get the message from the + ComposeBackEnd. Doing this became impossible around Catalina, + where instead we ask the ComposeBackEnd to call this function + later with the message: see call_fix_later elsewhere in this file. + + """ try: # if toggle key is active, temporarily switch the active state is_active = self.app.toggle_key_active ^ self.app.is_active