class MailApp(Category(MailApp)): @classmethod def registerQuoteFixApplication(cls, app): cls.app = app @swizzle(MailApp, 'sendEvent:') def sendEvent(self, original, event): if not hasattr(self, 'app'): original(self, event) return # Keep track of an active Opt key if event.type() == NSFlagsChanged: flags = event.modifierFlags() self.app.toggle_key_active = ( flags & NSAlternateKeyMask) and not (flags & NSControlKeyMask) # Handle reply/reply-all (XXX: won't work if you have assigned a different shortcut key to these actions) if self.app.toggle_key_active and event.type( ) == NSKeyDown and event.charactersIgnoringModifiers().lower() == 'r': # Strip the Opt-key from the event event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( event.type(), event.locationInWindow(), event.modifierFlags() & ~NSAlternateKeyMask, event.timestamp(), event.windowNumber(), event.context(), event.characters(), event.charactersIgnoringModifiers(), event.isARepeat(), event.keyCode()) original(self, event)
class ComposeViewController(Category(ComposeViewController)): @classmethod def registerQuoteFixApplication(cls, app): cls.app = app @swizzle(ComposeViewController, 'finishLoadingEditor', '_finishLoadingEditor') def finishLoadingEditor(self, original): logger.debug('[ComposeViewController finishLoadingEditor]') original(self) self.fix() # Don't let any changes made during quotefixing trigger the 'Save # to Drafts' alert. self.setHasUserMadeChanges_(False) self.backEnd().setHasChanges_(False) @swizzle(ComposeViewController, 'show') def show(self, original): logger.debug('[ComposeViewController show]') original(self) # If toggle key is active, temporarily switch the active state is_active = self.app.toggle_key_active ^ self.app.is_active if not is_active or not self.app.is_quotefixing: return # When the compose view should be shown, we assume any animations # are done and we can position the cursor. view = self.composeWebView() htmldom = view.mainFrame().DOMDocument() if self.app.move_cursor_to_top: view.moveToBeginningOfDocument_(self) elif not self.move_above_new_signature(htmldom, view): view.moveToEndOfDocument_(self)
class NSTreeController(Category(ak.NSTreeController)): """Category to make ak.NSTreeController more useable Based on extension by Wil Shipley http://www.wilshipley.com/blog/2006/04/pimp-my-code-part-10-whining-about.html See also: http://jonathandann.wordpress.com/2008/04/06/using-nstreecontroller/ http://www.cocoabuilder.com/archive/message/cocoa/2008/5/18/207078 """ def setSelectedObject_(self, obj): self.setSelectedObjects_([obj]) def setSelectedObjects_(self, objects): paths = [self.indexPathForObject_(obj) for obj in objects] self.setSelectionIndexPaths_(paths) def objectAtArrangedIndexPath_(self, path): return self.arrangedObjects().objectAtIndexPath_(path) def nodeAtArrangedIndexPath_(self, path): return self.arrangedObjects().nodeAtIndexPath_(path) def nodeForObject_(self, obj): return self.nodeAtArrangedIndexPath_(self.indexPathForObject_(obj)) def indexPathForObject_(self, obj): return self._indexPathFromIndexPath_inChildren_toObject_( None, self.content(), obj) def _indexPathFromIndexPath_inChildren_toObject_(self, basePath, children, obj): for childIndex, child in enumerate(children): lkp = self.leafKeyPath() if lkp and child.valueForKeyPath_(lkp): childsChildren = [] else: ckp = self.countKeyPath() if ckp: childCount = child.valueForKeyPath_(ckp).unsignedIntValue() if ckp and not childCount: childsChildren = [] else: childsChildren = child.valueForKeyPath_( self.childrenKeyPath()) if obj is child or childsChildren: if basePath is None: path = fn.NSIndexPath.indexPathWithIndex_(childIndex) else: path = basePath.indexPathByAddingIndex_(childIndex) if obj is child: return path if childsChildren: path = self._indexPathFromIndexPath_inChildren_toObject_( path, childsChildren, obj) if path is not None: return path return None
class DocumentEditor(Category(DocumentEditor)): @classmethod def registerQuoteFixApplication(cls, app): cls.app = app @swizzle(DocumentEditor, 'finishLoadingEditor') def finishLoadingEditor(self, original): logger.debug('DocumentEditor finishLoadingEditor') original(self) self.fix()
class MessageHeaders(Category(MessageHeaders)): @classmethod def registerQuoteFixApplication(cls, app): cls.app = app @swizzle(MessageHeaders, 'htmlStringShowingHeaderDetailLevel:useBold:useGray:') def htmlStringShowingHeaderDetailLevel_useBold_useGray_( self, original, level, bold, gray): if self.app.use_custom_forwarding_attribution and self.app.remove_apple_mail_forward_attribution: return '' return original(self, level, bold, gray)
class NSUserDefaults(Category(lookUpClass('NSUserDefaults'))): @property def bool(self): return DictProxy(self, 'bool') @property def string(self): return DictProxy(self, 'string') @property def object(self): return DictProxy(self, 'object') @property def int(self): return DictProxy(self, 'int')
class NSOutlineView(Category(ak.NSOutlineView)): """Category to improve usability of ak.NSOutlineView Originally based on extension by Wil Shipley http://www.wilshipley.com/blog/2006/04/pimp-my-code-part-10-whining-about.html See also: http://jonathandann.wordpress.com/2008/04/06/using-nstreecontroller/ http://www.cocoabuilder.com/archive/message/cocoa/2008/5/18/207078 """ def realItemForOpaqueItem_(self, item): return representedObject(item) def iterVisibleObjects(self): """Iterate (row, visible object) pairs""" for row in range(self.numberOfRows()): item = self.itemAtRow_(row) yield row, representedObject(item)
class DocumentEditor(Category(DocumentEditor)): @classmethod def registerMailTrackApplication(cls, app): cls.app = app @swizzle(DocumentEditor, 'finishLoadingEditor') def finishLoadingEditor(self, original): logger.debug('DocumentEditor finishLoadingEditor') # execute original finishLoadingEditor() original(self) try: # if toggle key is active, temporarily switch the active state is_active = self.app.toggle_key_active ^ self.app.is_active # check if we can proceed if not is_active: logger.debug( "MailTrack is not active, so no MailTracking for you!") return # grab composeView instance (this is the WebView which contains the # message editor) and check for the right conditions try: view = objc.getInstanceVariable(self, 'composeWebView') except: # was renamed in Lion view = objc.getInstanceVariable(self, '_composeWebView') # grab some other variables we need to perform our business backend = self.backEnd() htmldom = view.mainFrame().DOMDocument() htmlroot = htmldom.documentElement() messageType = self.messageType() # XXX: hack alert! if message type is DRAFT, but we can determine this # is actually a Send Again action, adjust the message type. origmsg = backend.originalMessage() if origmsg and messageType == DRAFT: # get the message viewer for this message viewer = MessageViewer.existingViewerShowingMessage_(origmsg) if not viewer: # XXX: this happens with conversation view active, not sure if this is stable enough though messageType = SENDAGAIN elif viewer: # get the mailbox for the viewer mailboxes = viewer.selectedMailboxes() # get the Drafts mailbox draftmailbox = viewer.draftsMailbox() # check if they're the same; if not, it's a Send-Again if draftmailbox not in mailboxes: messageType = SENDAGAIN # send original HTML to menu for debugging self.app.html = htmlroot.innerHTML() if not self.app.is_mailtracking: logger.debug( 'mailtracking turned off in preferences, skipping that part' ) elif messageType not in self.app.message_types_to_track: logger.debug('message type "%s" not in %s, not tracking' % (messageType, self.app.message_types_to_track)) else: # move cursor to end of document view.moveToEndOfDocument_(self) # perform some general cleanups logger.debug('calling cleanup_layout()') if self.cleanup_layout(htmlroot, backend): backend.setHasChanges_(False) # move cursor to end of document if self.app.move_cursor_to_top: view.moveToBeginningOfDocument_(self) # move to beginning of line logger.debug('calling view.moveToBeginningOfLine()') view.moveToBeginningOfLine_(self) # done logger.debug('MailTracking done') except Exception: logger.critical(traceback.format_exc()) if self.app.is_debugging: NSRunAlertPanel( 'MailTrack caught an exception', 'The MailTrack plug-in caught an exception:\n\n' + traceback.format_exc() + '\nPlease contact the developer quoting the contents of this alert.', None, None, None)