Ejemplo n.º 1
0
 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)}
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
    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())
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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())
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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())
Ejemplo n.º 11
0
    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()
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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, [])
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
 def refresh(self):
     with open_db() as db:
         new_objs = self._build_objs(db)
     self._updateObjs(new_objs)
Ejemplo n.º 18
0
 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)