diff --git a/resources/images/marked-text.png b/resources/images/marked-text.png deleted file mode 100644 index d60fc1f384..0000000000 Binary files a/resources/images/marked-text.png and /dev/null differ diff --git a/resources/pin-template.svg b/resources/pin-template.svg new file mode 100644 index 0000000000..0a950c6f92 --- /dev/null +++ b/resources/pin-template.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Kovid Goyal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index fd1287c15b..18ad1c4eb9 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -93,6 +93,7 @@ class View: 'au_map': self.get_author_data, 'ondevice': self.get_ondevice, 'marked': self.get_marked, + 'all_marked_labels': self.all_marked_labels, 'series_sort':self.get_series_sort, }.get(col, self._get) if isinstance(col, numbers.Integral): @@ -221,6 +222,9 @@ class View: id_ = idx if index_is_id else self.index_to_id(idx) return self.marked_ids.get(id_, default_value) + def all_marked_labels(self): + return set(self.marked_ids.values()) - {'true'} + def get_author_data(self, idx, index_is_id=True, default_value=None): id_ = idx if index_is_id else self.index_to_id(idx) with self.cache.safe_read_lock: diff --git a/src/calibre/gui2/actions/mark_books.py b/src/calibre/gui2/actions/mark_books.py index fc7de5f0d2..7fd9aa6330 100644 --- a/src/calibre/gui2/actions/mark_books.py +++ b/src/calibre/gui2/actions/mark_books.py @@ -122,7 +122,7 @@ class MarkBooksAction(InterfaceAction): self.menu = m = self.qaction.menu() m.aboutToShow.connect(self.about_to_show_menu) ma = partial(self.create_menu_action, m) - self.show_marked_action = a = ma('mark_with_text', _('Mark books with text label'), icon='marked-text.png') + self.show_marked_action = a = ma('mark_with_text', _('Mark books with text label'), icon='marked.png') a.triggered.connect(self.mark_with_text) self.show_marked_action = a = ma('show-marked', _('Show marked books'), icon='search.png', shortcut='Shift+Ctrl+M') a.triggered.connect(self.show_marked) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 325f11926c..561c8180bd 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -23,7 +23,7 @@ from calibre import ( fit_image, force_unicode, human_readable, isbytestring, prepare_string_for_xml, strftime ) -from calibre.constants import DEBUG, config_dir, filesystem_encoding +from calibre.constants import DEBUG, config_dir, dark_link_color, filesystem_encoding from calibre.db.search import CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH, _match from calibre.ebooks.metadata import authors_to_string, fmt_sidx, string_to_authors from calibre.ebooks.metadata.book.formatter import SafeFormat @@ -52,6 +52,17 @@ ALIGNMENT_MAP = {'left': Qt.AlignmentFlag.AlignLeft, 'right': Qt.AlignmentFlag.A _default_image = None +def render_pin(color='green', save_to=None): + svg = P('pin-template.svg', data=True).replace(b'fill:#f39509', ('fill:' + color).encode('utf-8')) + pm = QPixmap() + dpr = QApplication.instance().devicePixelRatio() + pm.setDevicePixelRatio(dpr) + pm.loadFromData(svg, 'svg') + if save_to: + pm.save(save_to) + return pm + + def default_image(): global _default_image if _default_image is None: @@ -232,7 +243,6 @@ class BooksModel(QAbstractTableModel): # {{{ # remember that the cover grid view needs a larger version of the icon, # anyway) self.marked_icon = QIcon(I('marked.png')) - self.marked_text_icon = QIcon(I('marked-text.png')) self.bool_blank_icon_as_icon = QIcon(self.bool_blank_icon) self.row_decoration = None self.device_connected = False @@ -241,8 +251,33 @@ class BooksModel(QAbstractTableModel): # {{{ self.current_highlighted_idx = None self.highlight_only = False self.row_height = 0 + self.marked_text_icons = {} self.read_config() + def marked_text_icon_for(self, label): + import random + ans = self.marked_text_icons.get(label) + if ans is not None: + return ans[1] + used_labels = self.db.data.all_marked_labels() + for qlabel in tuple(self.marked_text_icons): + if qlabel not in used_labels: + del self.marked_text_icons[qlabel] + used_colors = {x[0] for x in self.marked_text_icons.values()} + if QApplication.instance().is_dark_theme: + all_colors = {dark_link_color, 'lightgreen', 'red', 'maroon', 'cyan', 'pink'} + else: + all_colors = {'blue', 'green', 'red', 'maroon', 'cyan', 'pink'} + for c in all_colors - used_colors: + color = c + break + else: + color = random.choice(sorted(all_colors)) + pm = render_pin(color) + ans = QIcon(pm) + self.marked_text_icons[label] = color, ans + return ans + def _clear_caches(self): self.color_cache = defaultdict(dict) self.icon_cache = defaultdict(dict) @@ -1075,7 +1110,7 @@ class BooksModel(QAbstractTableModel): # {{{ try: m = self.db.data.get_marked(self.db.data.index_to_id(section)) if m: - i = self.marked_icon if m == 'true' else self.marked_text_icon + i = self.marked_icon if m == 'true' else self.marked_text_icon_for(m) else: i = self.row_decoration return i