def refresh(self): if not self.query_text: return with open_db() as db: objs = self._build_objs(db) self._updateObjs(objs) self.indexes = {obj['id']: n for n, obj in enumerate(objs)}
def setFromDraft(self, draft_id): with open_db() as db: msg = db.find_message(draft_id) with open(msg.get_filename(), 'rb') as fp: pymessage = email.message_from_binary_file( fp, policy=email.policy.default) self.msg = pymessage self.draft_id = draft_id self.subjectEdit.setText(self.msg['Subject']) self.rcptEdit.set_recipients( to=getaddresses(self.msg.get_all('To', [])), cc=getaddresses(self.msg.get_all('Cc', []))) body = pymessage.get_body(('plain', )) if body is not None: body = body.get_content() self.messageEdit.setPlainText(body) for attachment in pymessage.iter_attachments(): self.attachmentsList.addItem(attachment.get_filename() or 'untitled.attachment') self.attachmentList.show()
def _get_reply_manually(self, reply_to, reply_all=False): d = {} with open_db() as db: msg = db.find_message(reply_to) subject = msg.get_header('subject') if not re.match('re:', subject, re.I): subject = 'Re: %s' % subject d['Subject'] = subject d['In-reply-to'] = '<%s>' % reply_to # TODO add 'References' # TODO add 'From' d['To'] = msg.get_header('reply-to') or msg.get_header('from') if reply_all: ccs = [ msg.get_header('from'), msg.get_header('to'), msg.get_header('cc'), ] # TODO remove oneself d['Cc'] = ', '.join(filter(None, ccs)) return d
def setReply(self, reply_to, reply_all): assert reply_to info = self._get_reply_from_cli(reply_to, reply_all) for header in ('In-reply-to', 'References'): if header in info: self.msg[header] = info[header] self.subjectEdit.setText(info['Subject']) self.rcptEdit.set_recipients(to=getaddresses([info.get('To', '')]), cc=getaddresses([info.get('Cc', '')])) with open_db() as db: msg = db.find_message(reply_to) with open(msg.get_filename(), 'rb') as fp: pymessage = email.message_from_binary_file( fp, policy=email.policy.default) body = pymessage.get_body(('plain', )) if body is not None: body = body.get_content() parser = Parser() parsed = parser.parse(body) for block in parsed: indent_recursive(block) self.messageEdit.setPlainText(to_text(parsed))
def __init__(self, thread_id, *args, **kwargs): super(ThreadWidget, self).__init__(*args, **kwargs) self.setupUi(self) self.splitter.setStretchFactor(1, 1) self.setupToolbar() mdl = ThreadMessagesModel(thread_id) self.messagesTree.setModel(mdl) self.messagesTree.expandAll() self.messagesView.setThread(thread_id) self.messagesView.resumeDraft.connect(self.triggeredResumeDraft) self.messagesTree.messageActivated.connect( self.messagesView.showMessage) self.messagesTree.messagesSelectionChanged.connect( self.messagesView.selectMessageChanged) self.messagesTree.messagesSelectionChanged.connect( self.messagesView.scrollToSelected) self.messagesTree.messagesSelectionChanged.connect( self.updateToolbarState) self.messagesView.expanded.connect(self.messagesTree.selectMessage) with open_db() as db: thread = get_thread_by_id(db, thread_id) self.setWindowTitle(self.tr('Thread: %s') % thread.get_subject())
def __init__(self, *args, **kwargs): super(ThreadsWidget, self).__init__(*args, **kwargs) self.setupUi(self) self.threadsView.activated.connect(self._openThread) with open_db() as db: self.tagsView.setModel(TagsListModel(db)) self.tagsView.tagActivated.connect(self.tagActivated) self.threadsView.setModel(ThreadListModel()) self.threadsView.setItemDelegate(ThreadUnreadDelegate()) self.threadsView.setItemDelegateForColumn(0, ThreadSubjectDelegate()) self.threadsView.setDragEnabled(True) self.threadsView.selectionModel().selectionChanged.connect( self.updateToolbarState) for col, sz in enumerate( CONFIG.get('ui', 'threads_view', 'columns', default=[])): self.threadsView.setColumnWidth(col, sz) self.threadsView.header().sectionResized.connect(self._saveColumnSizes) self.searchLine.returnPressed.connect(self.doSearch) self.searchButton.clicked.connect(self.doSearch) self.splitter.setStretchFactor(1, 1) self.setupToolbar()
def openTagEditor(self): with open_db() as db: union_tags = set() common_tags = None for thread in self._iter_selected_threads(db): for msg in iter_thread_messages(thread): msg_tags = set(msg.get_tags()) union_tags |= msg_tags if common_tags is None: common_tags = msg_tags else: common_tags &= msg_tags union_tags -= common_tags w = TagEditor(parent=self) w.setCheckedTags(common_tags, union_tags) if not w.exec_(): return checked, partially = w.checkedTags() with open_db_rw() as db: for thread in self._iter_selected_threads(db): for msg in iter_thread_messages(thread): msg_tags = set(msg.get_tags()) to_remove = msg_tags - (checked | partially ) # unchecked tags to_add = checked - msg_tags # newly checked tags LOGGER.debug('changing tags for message %r: +(%s) -(%s)', msg.get_message_id(), to_add, to_remove) msg.freeze() for tag in to_remove: msg.remove_tag(tag) for tag in to_add: msg.add_tag(tag) msg.thaw() msg.tags_to_maildir_flags() LOGGER.debug('changed tags for message %r', msg.get_message_id()) for tag in to_remove: WATCHER.tagMailRemoved.emit(tag, msg.get_message_id()) for tag in to_add: WATCHER.tagMailAdded.emit(tag, msg.get_message_id())
def on_addButton_clicked(self): with open_db() as db: all_tags = set(db.get_all_tags()) cfg_tags = set( self.tagsList.item(i).text() for i in range(self.tagsList.count())) choose_tags = sorted(all_tags - cfg_tags) tag, ok = QInputDialog.getItem(self, self.tr('Tag'), self.tr('Choose a tag to configure'), choose_tags) if not ok: return item = QListWidgetItem(tag, self.tagsList) self._refreshItem(item)
def setThread(self, thread_id): with open_db() as db: thread = get_thread_by_id(db, thread_id) tree = build_thread_tree(thread) message_list = flatten_depth_first(tree) self.setWindowTitle(self.tr('Thread: %s') % thread.get_subject()) subjectLabel = QLabel() subjectLabel.setTextFormat(Qt.PlainText) subjectLabel.setText(self.tr('Subject: %s') % thread.get_subject()) subjectLabel.setLineWidth(1) subjectLabel.setFrameShape(QFrame.Box) self.layout().addWidget(subjectLabel) self.buildUi(message_list)
def openTagEditor(self): selected = self._getSelectedMessages() assert selected w = TagEditor(parent=self) with open_db() as db: msgs = [db.find_message(msg_id) for msg_id in selected] common_tags = reduce(set.__and__, (set(msg.get_tags()) for msg in msgs), set(msgs[0].get_tags())) union_tags = reduce(set.__or__, (set(msg.get_tags()) for msg in msgs), set()) union_tags -= common_tags w.setCheckedTags(common_tags, union_tags) if not w.exec_(): return checked, partially = w.checkedTags() with open_db_rw() as db: msgs = [db.find_message(sel) for sel in selected] for msg in msgs: msg_tags = set(msg.get_tags()) to_remove = msg_tags - (checked | partially) # unchecked tags to_add = checked - msg_tags # newly checked tags msg.freeze() for tag in to_remove: msg.remove_tag(tag) for tag in to_add: msg.add_tag(tag) msg.thaw() msg.tags_to_maildir_flags() for tag in to_remove: WATCHER.tagMailRemoved.emit(tag, msg.get_message_id()) for tag in to_add: WATCHER.tagMailAdded.emit(tag, msg.get_message_id())
def __init__(self, *args, **kwargs): super(TagChooser, self).__init__(*args, **kwargs) self.setupUi(self) with open_db() as db: self.tags_model = CheckableTagsListModel(db, parent=self) self.filter_model = QSortFilterProxyModel(self) self.filter_model.setSourceModel(self.tags_model) self.tagsList.setModel(self.filter_model) self.filterEdit.textEdited.connect(self.tags_model.setNewTagItem) self.filterEdit.textEdited.connect( self.filter_model.setFilterFixedString) self.filterEdit.textEdited.connect(self.selectAtLeastOne) self.filterEdit.installEventFilter(self) self.selectAtLeastOne()
def __init__(self, thread_id, *args, **kwargs): super(ThreadMessagesModel, self).__init__(*args, **kwargs) EXCERPT_BUILDER.builtExcerpt.connect(self._builtExcerpt) with open_db() as db: thread = get_thread_by_id(db, thread_id) tree = build_thread_tree(thread) objs = { obj.get_message_id(): self._dict_from_obj(obj) for obj in flatten_depth_first(tree) } tree = { msg.get_message_id() if msg else None: [sub.get_message_id() for sub in tree[msg]] for msg in tree } self._setTree(tree, objs)
def refreshOneMail(self, _, msgid): with open_db() as db: query = db.create_query(f'id:{msgid}') if hasattr(query, 'set_omit_excluded'): # FIXME remove condition when function is integrated in notmuch bindings query.set_omit_excluded(query.EXCLUDE.FALSE) thread = next(iter(query.search_threads())) tid = thread.get_thread_id() try: row = self.indexes[tid] except KeyError: return new_obj = self._thread_to_dict(thread) self.objs[row].clear() self.objs[row].update(new_obj) qidx1 = self.index(row, 0) qidx2 = qidx1.siblingAtColumn(self.columnCount() - 1) self.dataChanged.emit(qidx1, qidx2, [])
def _prepareTagMenu(self): # TODO much improvement, colored tags, filter tags, sub-tags menu = self.sender() menu.clear() msg_ids = self._getSelectedMessages() with open_db() as db: msgs = [db.find_message(msg_id) for msg_id in msg_ids] msg_tags = reduce(set.__or__, (set(msg.get_tags()) for msg in msgs), set()) db_tags = set(db.get_all_tags()) db_tags -= UNTOUCHABLE_TAGS for tag in sorted(db_tags): action = menu.addAction(tag) action.setCheckable(True) action.setData(tag) if tag in msg_tags: action.setChecked(True) action.triggered.connect(self._setMessagesTag)
def _toggleMessageWidget(self, qmsg): with open_db() as db: message = db.find_message(qmsg.message_id) collapsed = isinstance(qmsg, PlainMessageWidget) if collapsed: new = CollapsedMessageWidget(message, parent=self) # new.toggle.connect(self._selectInTree) else: new = PlainMessageWidget(message, parent=self) new.resumeDraft.connect(self._resumeDraft) new.toggle.connect(self._toggleMessage) new.setLineWidth(qmsg.lineWidth()) self.widgets[message.get_message_id()] = new self.layout().replaceWidget(qmsg, new) qmsg.deleteLater() if not collapsed: self.expanded.emit(message.get_message_id()) return new
def refresh(self): objs = [] with open_db() as db: sub_iter = chain( self.path.joinpath('new').iterdir(), self.path.joinpath('cur').iterdir(), ) for msg_path in sub_iter: msg = db.find_message_by_filename(str(msg_path)) if msg is None: continue objs.append({ 'date': msg.get_date(), 'tags': frozenset(msg.get_tags()), 'subject': msg.get_header('subject'), 'sender': msg.get_header('From'), 'message_id': msg.get_message_id(), 'thread_id': msg.get_thread_id(), 'path': msg_path, }) self._setObjs(objs)
def refresh(self): with open_db() as db: new_objs = self._build_objs(db) self._updateObjs(new_objs)
def doSearch(self): query_text = self.searchLine.text() with open_db() as db: self.threadsView.model().setQuery(db, query_text) self.setWindowTitle(self.tr('Query: %s') % query_text)