def font(self, text_style):
        device_font = text_style.fontfacename in LIBERATION_FONT_MAP
        try:
            if device_font:
                face = self.font_map[text_style.fontfacename]
            else:
                face = self.face_map[text_style.fontfacename]
        except KeyError:  # Bad fontfacename field in LRF
            face = self.font_map['Dutch801 Rm BT Roman']

        sz = text_style.fontsize
        wt = text_style.fontweight
        style = text_style.fontstyle
        font = (
            face,
            wt,
            style,
            sz,
        )
        if font in self.cache:
            rfont = self.cache[font]
        else:
            italic = font[2] == QFont.Style.StyleItalic
            rfont = QFont(font[0], font[3], font[1], italic)
            rfont.setPixelSize(font[3])
            rfont.setBold(wt >= 69)
            self.cache[font] = rfont
        qfont = rfont
        if text_style.emplinetype != 'none':
            qfont = QFont(rfont)
            qfont.setOverline(text_style.emplineposition == 'before')
            qfont.setUnderline(text_style.emplineposition == 'after')
        return qfont
Exemple #2
0
class NewsCategory(NewsTreeItem):

    def __init__(self, category, builtin, custom, scheduler_config, parent):
        NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent)
        self.category = category
        self.cdata = get_language(self.category)
        if self.category == _('Scheduled'):
            self.sortq = 0, ''
        elif self.category == _('Custom'):
            self.sortq = 1, ''
        else:
            self.sortq = 2, self.cdata
        self.bold_font = QFont()
        self.bold_font.setBold(True)
        self.bold_font = (self.bold_font)

    def data(self, role):
        if role == Qt.ItemDataRole.DisplayRole:
            return (self.cdata + ' [%d]'%len(self.children))
        elif role == Qt.ItemDataRole.FontRole:
            return self.bold_font
        elif role == Qt.ItemDataRole.ForegroundRole and self.category == _('Scheduled'):
            return QApplication.instance().palette().color(QPalette.ColorRole.Link)
        elif role == Qt.ItemDataRole.UserRole:
            return f'::category::{self.sortq[0]}'
        return None

    def flags(self):
        return Qt.ItemFlag.ItemIsEnabled

    def __eq__(self, other):
        return self.cdata == other.cdata

    def __lt__(self, other):
        return self.sortq < getattr(other, 'sortq', (3, ''))
Exemple #3
0
    def paint_line_numbers(self, ev):
        painter = QPainter(self.line_number_area)
        painter.fillRect(ev.rect(), self.line_number_palette.color(QPalette.ColorRole.Base))

        block = self.firstVisibleBlock()
        num = block.blockNumber()
        top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
        bottom = top + int(self.blockBoundingRect(block).height())
        current = self.textCursor().block().blockNumber()
        painter.setPen(self.line_number_palette.color(QPalette.ColorRole.Text))

        while block.isValid() and top <= ev.rect().bottom():
            if block.isVisible() and bottom >= ev.rect().top():
                if current == num:
                    painter.save()
                    painter.setPen(self.line_number_palette.color(QPalette.ColorRole.BrightText))
                    f = QFont(self.font())
                    f.setBold(True)
                    painter.setFont(f)
                    self.last_current_lnum = (top, bottom - top)
                painter.drawText(0, top, self.line_number_area.width() - 5, self.fontMetrics().height(),
                              Qt.AlignmentFlag.AlignRight, unicode_type(num + 1))
                if current == num:
                    painter.restore()
            block = block.next()
            top = bottom
            bottom = top + int(self.blockBoundingRect(block).height())
            num += 1
Exemple #4
0
 def paint(self, painter, option, index):
     QStyledItemDelegate.paint(self, painter, option, index)
     result = index.data(Qt.ItemDataRole.UserRole)
     is_hidden, result_before, result_text, result_after, show_leading_dot = self.result_data(
         result)
     if result_text is None:
         return
     painter.save()
     try:
         p = option.palette
         c = QPalette.ColorRole.HighlightedText if option.state & QStyle.StateFlag.State_Selected else QPalette.ColorRole.Text
         group = (QPalette.ColorGroup.Active if option.state
                  & QStyle.StateFlag.State_Active else
                  QPalette.ColorGroup.Inactive)
         c = p.color(group, c)
         painter.setPen(c)
         font = option.font
         if self.emphasize_text:
             emphasis_font = QFont(font)
             emphasis_font.setBold(True)
         else:
             emphasis_font = font
         flags = Qt.AlignmentFlag.AlignTop | Qt.TextFlag.TextSingleLine | Qt.TextFlag.TextIncludeTrailingSpaces
         rect = option.rect.adjusted(
             option.decorationSize.width() + 4 if is_hidden else 0, 0, 0, 0)
         painter.setClipRect(rect)
         before = re.sub(r'\s+', ' ', result_before)
         if show_leading_dot:
             before = '•' + before
         before_width = 0
         if before:
             before_width = painter.boundingRect(rect, flags,
                                                 before).width()
         after = re.sub(r'\s+', ' ', result_after.rstrip())
         after_width = 0
         if after:
             after_width = painter.boundingRect(rect, flags, after).width()
         ellipsis_width = painter.boundingRect(rect, flags, '...').width()
         painter.setFont(emphasis_font)
         text = re.sub(r'\s+', ' ', result_text)
         match_width = painter.boundingRect(rect, flags, text).width()
         if match_width >= rect.width() - 3 * ellipsis_width:
             efm = QFontMetrics(emphasis_font)
             if show_leading_dot:
                 text = '•' + text
             text = efm.elidedText(text, Qt.TextElideMode.ElideRight,
                                   rect.width())
             painter.drawText(rect, flags, text)
         else:
             self.draw_match(painter, flags, before, text, after, rect,
                             before_width, match_width, after_width,
                             ellipsis_width, emphasis_font, font)
     except Exception:
         import traceback
         traceback.print_exc()
     painter.restore()
Exemple #5
0
 def capture_clicked(self, which=1):
     self.capture = which
     for w in 1, 2:
         self.clear_button(w)
     button = getattr(self, 'button%d' % which)
     button.setText(_('Press a key...'))
     button.setFocus(Qt.FocusReason.OtherFocusReason)
     font = QFont()
     font.setBold(True)
     button.setFont(font)
Exemple #6
0
    def process_duplicates(self, db, duplicates):
        ta = _('%(title)s by %(author)s [%(formats)s]')
        bf = QFont(self.dup_list.font())
        bf.setBold(True)
        itf = QFont(self.dup_list.font())
        itf.setItalic(True)

        for mi, cover, formats in duplicates:
            # formats is a list of file paths
            # Grab just the extension and display to the user
            # Based only off the file name, no file type tests are done.
            incoming_formats = ', '.join(
                os.path.splitext(path)[-1].replace('.', '').upper()
                for path in formats)
            item = QTreeWidgetItem([
                ta % dict(title=mi.title,
                          author=mi.format_field('authors')[1],
                          formats=incoming_formats)
            ], 0)
            item.setCheckState(0, Qt.CheckState.Checked)
            item.setFlags(Qt.ItemFlag.ItemIsEnabled
                          | Qt.ItemFlag.ItemIsUserCheckable)
            item.setData(0, Qt.ItemDataRole.FontRole, bf)
            item.setData(0, Qt.ItemDataRole.UserRole, (mi, cover, formats))
            matching_books = db.books_with_same_title(mi)

            def add_child(text):
                c = QTreeWidgetItem([text], 1)
                c.setFlags(Qt.ItemFlag.ItemIsEnabled)
                item.addChild(c)
                return c

            add_child(_('Already in calibre:')).setData(
                0, Qt.ItemDataRole.FontRole, itf)

            author_text = {}
            for book_id in matching_books:
                author_text[book_id] = authors_to_string([
                    a.replace('|', ',') for a in (
                        db.authors(book_id, index_is_id=True) or '').split(',')
                ])

            def key(x):
                return primary_sort_key(str(author_text[x]))

            for book_id in sorted(matching_books, key=key):
                add_child(
                    ta %
                    dict(title=db.title(book_id, index_is_id=True),
                         author=author_text[book_id],
                         formats=db.formats(
                             book_id, index_is_id=True, verify_formats=False)))
            add_child('')

            yield item
Exemple #7
0
 def __init__(self, toc=None):
     QStandardItemModel.__init__(self)
     self.current_query = {'text': '', 'index': -1, 'items': ()}
     self.all_items = depth_first = []
     normal_font = QApplication.instance().font()
     emphasis_font = QFont(normal_font)
     emphasis_font.setBold(True), emphasis_font.setItalic(True)
     if toc:
         for t in toc['children']:
             self.appendRow(
                 TOCItem(t, 0, depth_first, normal_font, emphasis_font))
     self.node_id_map = {x.node_id: x for x in self.all_items}
Exemple #8
0
 def data(self, index, role):
     try:
         widget = self.widgets[index.row()]
     except:
         return None
     if role == Qt.ItemDataRole.DisplayRole:
         return (widget.config_title())
     if role == Qt.ItemDataRole.DecorationRole:
         return (widget.config_icon())
     if role == Qt.ItemDataRole.FontRole:
         f = QFont()
         f.setBold(True)
         return (f)
     return None
Exemple #9
0
def generate_masthead(title,
                      output_path=None,
                      width=600,
                      height=60,
                      as_qimage=False,
                      font_family=None):
    init_environment()
    font_family = font_family or cprefs[
        'title_font_family'] or 'Liberation Serif'
    img = QImage(width, height, QImage.Format.Format_ARGB32)
    img.fill(Qt.GlobalColor.white)
    p = QPainter(img)
    p.setRenderHints(QPainter.RenderHint.Antialiasing
                     | QPainter.RenderHint.TextAntialiasing)
    f = QFont(font_family)
    f.setStyleStrategy(QFont.StyleStrategy.PreferAntialias)
    f.setPixelSize((height * 3) // 4), f.setBold(True)
    p.setFont(f)
    p.drawText(img.rect(),
               Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter,
               sanitize(title))
    p.end()
    if as_qimage:
        return img
    data = pixmap_to_data(img)
    if output_path is None:
        return data
    with open(output_path, 'wb') as f:
        f.write(data)
Exemple #10
0
class Category(QWidget):  # {{{

    plugin_activated = pyqtSignal(object)

    def __init__(self, name, plugins, gui_name, parent=None):
        QWidget.__init__(self, parent)
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        self.label = QLabel(gui_name)
        self.sep = QFrame(self)
        self.bf = QFont()
        self.bf.setBold(True)
        self.label.setFont(self.bf)
        self.sep.setFrameShape(QFrame.Shape.HLine)
        self._layout.addWidget(self.label)
        self._layout.addWidget(self.sep)

        self.plugins = plugins

        self.bar = QToolBar(self)
        self.bar.setStyleSheet('QToolBar { border: none; background: none }')
        lh = QApplication.instance().line_height
        self.bar.setIconSize(QSize(2 * lh, 2 * lh))
        self.bar.setMovable(False)
        self.bar.setFloatable(False)
        self.bar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon)
        self._layout.addWidget(self.bar)
        self.actions = []
        for p in plugins:
            target = partial(self.triggered, p)
            ac = self.bar.addAction(QIcon(p.icon),
                                    p.gui_name.replace('&', '&&'), target)
            ac.setToolTip(textwrap.fill(p.description))
            ac.setWhatsThis(textwrap.fill(p.description))
            ac.setStatusTip(p.description)
            self.actions.append(ac)
            w = self.bar.widgetForAction(ac)
            w.setCursor(Qt.CursorShape.PointingHandCursor)
            if hasattr(w, 'setAutoRaise'):
                w.setAutoRaise(True)
            w.setMinimumWidth(100)

    def triggered(self, plugin, *args):
        self.plugin_activated.emit(plugin)
Exemple #11
0
    def paint_line_numbers(self, ev):
        painter = QPainter(self.line_number_area)
        painter.fillRect(
            ev.rect(), self.line_number_palette.color(QPalette.ColorRole.Base))

        block = self.firstVisibleBlock()
        num = block.blockNumber()
        top = int(
            self.blockBoundingGeometry(block).translated(
                self.contentOffset()).top())
        bottom = top + int(self.blockBoundingRect(block).height())
        current = self.textCursor().block().blockNumber()
        painter.setPen(self.line_number_palette.color(QPalette.ColorRole.Text))

        while block.isValid() and top <= ev.rect().bottom():
            if block.isVisible() and bottom >= ev.rect().top():
                set_bold = False
                set_italic = False
                if current == num:
                    set_bold = True
                if num + 1 in self.clicked_line_numbers:
                    set_italic = True
                painter.save()
                if set_bold or set_italic:
                    f = QFont(self.font())
                    if set_bold:
                        f.setBold(set_bold)
                        painter.setPen(
                            self.line_number_palette.color(
                                QPalette.ColorRole.BrightText))
                    f.setItalic(set_italic)
                    painter.setFont(f)
                else:
                    painter.setFont(self.font())
                painter.drawText(0, top,
                                 self.line_number_area.width() - 5,
                                 self.fontMetrics().height(),
                                 Qt.AlignmentFlag.AlignRight, str(num + 1))
                painter.restore()
            block = block.next()
            top = bottom
            bottom = top + int(self.blockBoundingRect(block).height())
            num += 1
Exemple #12
0
 def data(self, index, role):
     row = index.row()
     try:
         tweak = self.tweaks[row]
     except:
         return None
     if role == Qt.ItemDataRole.DisplayRole:
         return textwrap.fill(tweak.name, 40)
     if role == Qt.ItemDataRole.FontRole and tweak.is_customized:
         ans = QFont()
         ans.setBold(True)
         return ans
     if role == Qt.ItemDataRole.ToolTipRole:
         tt = _('This tweak has its default value')
         if tweak.is_customized:
             tt = '<p>' + _('This tweak has been customized')
             tt += '<pre>'
             for varn, val in iteritems(tweak.custom_values):
                 tt += '%s = %r\n\n' % (varn, val)
         return textwrap.fill(tt)
     if role == Qt.ItemDataRole.UserRole:
         return tweak
     return None
Exemple #13
0
    def paint_line_numbers(self, ev):
        painter = QPainter(self.line_number_area)
        painter.fillRect(ev.rect(), self.line_number_palette.color(QPalette.ColorRole.Base))

        block = self.firstVisibleBlock()
        num = block.blockNumber()
        top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
        bottom = top + int(self.blockBoundingRect(block).height())
        painter.setPen(self.line_number_palette.color(QPalette.ColorRole.Text))
        change_starts = {x[0] for x in self.changes}

        while block.isValid() and top <= ev.rect().bottom():
            r = ev.rect()
            if block.isVisible() and bottom >= r.top():
                text = unicode_type(self.line_number_map.get(num, ''))
                is_start = text != '-' and num in change_starts
                if is_start:
                    painter.save()
                    f = QFont(self.font())
                    f.setBold(True)
                    painter.setFont(f)
                    painter.setPen(self.line_number_palette.color(QPalette.ColorRole.BrightText))
                if text == '-':
                    painter.drawLine(r.left() + 2, (top + bottom)//2, r.right() - 2, (top + bottom)//2)
                else:
                    if self.right:
                        painter.drawText(r.left() + 3, top, r.right(), self.fontMetrics().height(),
                                Qt.AlignmentFlag.AlignLeft, text)
                    else:
                        painter.drawText(r.left() + 2, top, r.right() - 5, self.fontMetrics().height(),
                                Qt.AlignmentFlag.AlignRight, text)
                if is_start:
                    painter.restore()
            block = block.next()
            top = bottom
            bottom = top + int(self.blockBoundingRect(block).height())
            num += 1
Exemple #14
0
 def mark_item_as_current(self, item):
     font = QFont(self.font())
     font.setItalic(True)
     font.setBold(True)
     item.setData(0, Qt.ItemDataRole.FontRole, font)
Exemple #15
0
class CompareSingle(QWidget):
    def __init__(self,
                 field_metadata,
                 parent=None,
                 revert_tooltip=None,
                 datetime_fmt='MMMM yyyy',
                 blank_as_equal=True,
                 fields=('title', 'authors', 'series', 'tags', 'rating',
                         'publisher', 'pubdate', 'identifiers', 'languages',
                         'comments', 'cover'),
                 db=None):
        QWidget.__init__(self, parent)
        self.l = l = QGridLayout()
        # l.setContentsMargins(0, 0, 0, 0)
        self.setLayout(l)
        revert_tooltip = revert_tooltip or _('Revert %s')
        self.current_mi = None
        self.changed_font = QFont(QApplication.font())
        self.changed_font.setBold(True)
        self.changed_font.setItalic(True)
        self.blank_as_equal = blank_as_equal

        self.widgets = OrderedDict()
        row = 0

        for field in fields:
            m = field_metadata[field]
            dt = m['datatype']
            extra = None
            if 'series' in {field, dt}:
                cls = SeriesEdit
            elif field == 'identifiers':
                cls = IdentifiersEdit
            elif field == 'languages':
                cls = LanguagesEdit
            elif 'comments' in {field, dt}:
                cls = CommentsEdit
            elif 'rating' in {field, dt}:
                cls = RatingsEdit
            elif dt == 'datetime':
                extra = datetime_fmt
                cls = DateEdit
            elif field == 'cover':
                cls = CoverView
            elif dt in {'text', 'enum'}:
                cls = LineEdit
            else:
                continue
            neww = cls(field, True, self, m, extra)
            neww.setObjectName(field)
            connect_lambda(
                neww.changed, self,
                lambda self: self.changed(self.sender().objectName()))
            if isinstance(neww, EditWithComplete):
                try:
                    neww.update_items_cache(db.new_api.all_field_names(field))
                except ValueError:
                    pass  # A one-one field like title
            if isinstance(neww, SeriesEdit):
                neww.set_db(db.new_api)
            oldw = cls(field, False, self, m, extra)
            newl = QLabel('&%s:' % m['name'])
            newl.setBuddy(neww)
            button = RightClickButton(self)
            button.setIcon(QIcon(I('back.png')))
            button.setObjectName(field)
            connect_lambda(
                button.clicked, self,
                lambda self: self.revert(self.sender().objectName()))
            button.setToolTip(revert_tooltip % m['name'])
            if field == 'identifiers':
                button.m = m = QMenu(button)
                button.setMenu(m)
                button.setPopupMode(
                    QToolButton.ToolButtonPopupMode.DelayedPopup)
                m.addAction(button.toolTip()).triggered.connect(button.click)
                m.actions()[0].setIcon(button.icon())
                m.addAction(_('Merge identifiers')).triggered.connect(
                    self.merge_identifiers)
                m.actions()[1].setIcon(QIcon(I('merge.png')))
            elif field == 'tags':
                button.m = m = QMenu(button)
                button.setMenu(m)
                button.setPopupMode(
                    QToolButton.ToolButtonPopupMode.DelayedPopup)
                m.addAction(button.toolTip()).triggered.connect(button.click)
                m.actions()[0].setIcon(button.icon())
                m.addAction(_('Merge tags')).triggered.connect(self.merge_tags)
                m.actions()[1].setIcon(QIcon(I('merge.png')))

            self.widgets[field] = Widgets(neww, oldw, newl, button)
            for i, w in enumerate((newl, neww, button, oldw)):
                c = i if i < 2 else i + 1
                if w is oldw:
                    c += 1
                l.addWidget(w, row, c)
            row += 1

        if 'comments' in self.widgets and not gprefs.get(
                'diff_widget_show_comments_controls', True):
            self.widgets['comments'].new.hide_toolbars()

    def save_comments_controls_state(self):
        if 'comments' in self.widgets:
            vis = self.widgets['comments'].new.toolbars_visible
            if vis != gprefs.get('diff_widget_show_comments_controls', True):
                gprefs.set('diff_widget_show_comments_controls', vis)

    def changed(self, field):
        w = self.widgets[field]
        if not w.new.same_as(w.old) and (not self.blank_as_equal
                                         or not w.new.is_blank):
            w.label.setFont(self.changed_font)
        else:
            w.label.setFont(QApplication.font())

    def revert(self, field):
        widgets = self.widgets[field]
        neww, oldw = widgets[:2]
        if hasattr(neww, 'set_undoable'):
            neww.set_undoable(oldw.current_val)
        else:
            neww.current_val = oldw.current_val

    def merge_identifiers(self):
        widgets = self.widgets['identifiers']
        neww, oldw = widgets[:2]
        val = neww.as_dict
        val.update(oldw.as_dict)
        neww.as_dict = val

    def merge_tags(self):
        widgets = self.widgets['tags']
        neww, oldw = widgets[:2]
        val = oldw.value
        lval = {icu_lower(x) for x in val}
        extra = [x for x in neww.value if icu_lower(x) not in lval]
        if extra:
            neww.value = val + extra

    def __call__(self, oldmi, newmi):
        self.current_mi = newmi
        self.initial_vals = {}
        for field, widgets in iteritems(self.widgets):
            widgets.old.from_mi(oldmi)
            widgets.new.from_mi(newmi)
            self.initial_vals[field] = widgets.new.current_val

    def apply_changes(self):
        changed = False
        for field, widgets in iteritems(self.widgets):
            val = widgets.new.current_val
            if val != self.initial_vals[field]:
                widgets.new.to_mi(self.current_mi)
                changed = True
        return changed
Exemple #16
0
class EmailAccounts(QAbstractTableModel):  # {{{
    def __init__(self, accounts, subjects, aliases={}, tags={}):
        QAbstractTableModel.__init__(self)
        self.accounts = accounts
        self.subjects = subjects
        self.aliases = aliases
        self.tags = tags
        self.sorted_on = (0, True)
        self.account_order = list(self.accounts)
        self.do_sort()
        self.headers = [
            _('Email'),
            _('Formats'),
            _('Subject'),
            _('Auto send'),
            _('Alias'),
            _('Auto send only tags')
        ]
        self.default_font = QFont()
        self.default_font.setBold(True)
        self.default_font = (self.default_font)
        self.tooltips = [None] + list(
            map(textwrap.fill, [
                _('Formats to email. The first matching format will be sent.'),
                _('Subject of the email to use when sending. When left blank '
                  'the title will be used for the subject. Also, the same '
                  'templates used for "Save to disk" such as {title} and '
                  '{author_sort} can be used here.'), '<p>' +
                _('If checked, downloaded news will be automatically '
                  'mailed to this email address '
                  '(provided it is in one of the listed formats and has not been filtered by tags).'
                  ),
                _('Friendly name to use for this email address'),
                _('If specified, only news with one of these tags will be sent to'
                  ' this email address. All news downloads have their title as a'
                  ' tag, so you can use this to easily control which news downloads'
                  ' are sent to this email address.')
            ]))

    def do_sort(self):
        col = self.sorted_on[0]
        if col == 0:

            def key(account_key):
                return numeric_sort_key(account_key)
        elif col == 1:

            def key(account_key):
                return numeric_sort_key(self.accounts[account_key][0] or '')
        elif col == 2:

            def key(account_key):
                return numeric_sort_key(self.subjects.get(account_key) or '')
        elif col == 3:

            def key(account_key):
                return numeric_sort_key(
                    as_unicode(self.accounts[account_key][0]) or '')
        elif col == 4:

            def key(account_key):
                return numeric_sort_key(self.aliases.get(account_key) or '')
        elif col == 5:

            def key(account_key):
                return numeric_sort_key(self.tags.get(account_key) or '')

        self.account_order.sort(key=key, reverse=not self.sorted_on[1])

    def sort(self, column, order=Qt.SortOrder.AscendingOrder):
        nsort = (column, order == Qt.SortOrder.AscendingOrder)
        if nsort != self.sorted_on:
            self.sorted_on = nsort
            self.beginResetModel()
            try:
                self.do_sort()
            finally:
                self.endResetModel()

    def rowCount(self, *args):
        return len(self.account_order)

    def columnCount(self, *args):
        return len(self.headers)

    def headerData(self, section, orientation, role):
        if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
            return self.headers[section]
        return None

    def data(self, index, role):
        row, col = index.row(), index.column()
        if row < 0 or row >= self.rowCount():
            return None
        account = self.account_order[row]
        if account not in self.accounts:
            return None
        if role == Qt.ItemDataRole.UserRole:
            return (account, self.accounts[account])
        if role == Qt.ItemDataRole.ToolTipRole:
            return self.tooltips[col]
        if role in [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole]:
            if col == 0:
                return (account)
            if col == 1:
                return ', '.join(
                    x.strip()
                    for x in (self.accounts[account][0] or '').split(','))
            if col == 2:
                return (self.subjects.get(account, ''))
            if col == 4:
                return (self.aliases.get(account, ''))
            if col == 5:
                return (self.tags.get(account, ''))
        if role == Qt.ItemDataRole.FontRole and self.accounts[account][2]:
            return self.default_font
        if role == Qt.ItemDataRole.CheckStateRole and col == 3:
            return (Qt.CheckState.Checked
                    if self.accounts[account][1] else Qt.CheckState.Unchecked)
        return None

    def flags(self, index):
        if index.column() == 3:
            return QAbstractTableModel.flags(
                self, index) | Qt.ItemFlag.ItemIsUserCheckable
        else:
            return QAbstractTableModel.flags(
                self, index) | Qt.ItemFlag.ItemIsEditable

    def setData(self, index, value, role):
        if not index.isValid():
            return False
        row, col = index.row(), index.column()
        account = self.account_order[row]
        if col == 3:
            self.accounts[account][1] ^= True
        elif col == 2:
            self.subjects[account] = as_unicode(value or '')
        elif col == 4:
            self.aliases.pop(account, None)
            aval = as_unicode(value or '').strip()
            if aval:
                self.aliases[account] = aval
        elif col == 5:
            self.tags.pop(account, None)
            aval = as_unicode(value or '').strip()
            if aval:
                self.tags[account] = aval
        elif col == 1:
            self.accounts[account][0] = re.sub(
                ',+', ',', re.sub(r'\s+', ',',
                                  as_unicode(value or '').upper()))
        elif col == 0:
            na = as_unicode(value or '')
            from email.utils import parseaddr
            addr = parseaddr(na)[-1]
            if not addr or '@' not in na:
                return False
            self.accounts[na] = self.accounts.pop(account)
            self.account_order[row] = na
            if '@kindle.com' in addr:
                self.accounts[na][0] = 'AZW, MOBI, TPZ, PRC, AZW1'

        self.dataChanged.emit(self.index(index.row(), 0),
                              self.index(index.row(), 3))
        return True

    def make_default(self, index):
        if index.isValid():
            self.beginResetModel()
            row = index.row()
            for x in self.accounts.values():
                x[2] = False
            self.accounts[self.account_order[row]][2] = True
            self.endResetModel()

    def add(self):
        x = _('new email address')
        y = x
        c = 0
        while y in self.accounts:
            c += 1
            y = x + str(c)
        auto_send = len(self.accounts) < 1
        self.beginResetModel()
        self.accounts[y] = [
            'MOBI, EPUB', auto_send,
            len(self.account_order) == 0
        ]
        self.account_order = list(self.accounts)
        self.do_sort()
        self.endResetModel()
        return self.index(self.account_order.index(y), 0)

    def remove_rows(self, *rows):
        for row in sorted(rows, reverse=True):
            try:
                account = self.account_order[row]
            except Exception:
                continue
            self.accounts.pop(account)
        self.account_order = sorted(self.accounts)
        has_default = False
        for account in self.account_order:
            if self.accounts[account][2]:
                has_default = True
                break
        if not has_default and self.account_order:
            self.accounts[self.account_order[0]][2] = True

        self.beginResetModel()
        self.endResetModel()
        self.do_sort()

    def remove(self, index):
        if index.isValid():
            self.remove(index.row())