def setup_ui(self): self.l = l = QVBoxLayout(self) self.setLayout(l) self.gb = gb = QGroupBox(_('&Images in book'), self) self.v = v = QVBoxLayout(gb) gb.setLayout(v), gb.setFlat(True) self.names, self.names_filter = create_filterable_names_list( sorted(self.image_names, key=sort_key), filter_text=_('Filter the list of images'), parent=self) self.names.doubleClicked.connect( self.double_clicked, type=Qt.ConnectionType.QueuedConnection) self.cover_view = CoverView(self) l.addWidget(self.names_filter) v.addWidget(self.names) self.splitter = s = QSplitter(self) l.addWidget(s) s.addWidget(gb) s.addWidget(self.cover_view) self.h = h = QHBoxLayout() self.preserve = p = QCheckBox(_('Preserve aspect ratio')) p.setToolTip( textwrap.fill( _('If enabled the cover image you select will be embedded' ' into the book in such a way that when viewed, its aspect' ' ratio (ratio of width to height) will be preserved.' ' This will mean blank spaces around the image if the screen' ' the book is being viewed on has an aspect ratio different' ' to the image.'))) p.setChecked(tprefs['add_cover_preserve_aspect_ratio']) p.setVisible(self.container.book_type != 'azw3') def on_state_change(s): tprefs.set('add_cover_preserve_aspect_ratio', s == Qt.CheckState.Checked) p.stateChanged.connect(on_state_change) self.info_label = il = QLabel('\xa0') h.addWidget(p), h.addStretch(1), h.addWidget(il) l.addLayout(h) l.addWidget(self.bb) b = self.bb.addButton(_('Import &image'), QDialogButtonBox.ButtonRole.ActionRole) b.clicked.connect(self.import_image) b.setIcon(QIcon(I('document_open.png'))) self.names.setFocus(Qt.FocusReason.OtherFocusReason) self.names.selectionModel().currentChanged.connect( self.current_image_changed) cname = get_raster_cover_name(self.container) if cname: row = self.names.model().find_name(cname) if row > -1: self.names.setCurrentIndex(self.names.model().index(row))
def setup_ui(self): self.l = l = QVBoxLayout(self) self.setLayout(l) self.gb = gb = QGroupBox(_('&Images in book'), self) self.v = v = QVBoxLayout(gb) gb.setLayout(v), gb.setFlat(True) self.names, self.names_filter = create_filterable_names_list( sorted(self.image_names, key=sort_key), filter_text=_('Filter the list of images'), parent=self) self.names.doubleClicked.connect(self.double_clicked, type=Qt.QueuedConnection) self.cover_view = CoverView(self) l.addWidget(self.names_filter) v.addWidget(self.names) self.splitter = s = QSplitter(self) l.addWidget(s) s.addWidget(gb) s.addWidget(self.cover_view) self.h = h = QHBoxLayout() self.preserve = p = QCheckBox(_('Preserve aspect ratio')) p.setToolTip(textwrap.fill(_('If enabled the cover image you select will be embedded' ' into the book in such a way that when viewed, its aspect' ' ratio (ratio of width to height) will be preserved.' ' This will mean blank spaces around the image if the screen' ' the book is being viewed on has an aspect ratio different' ' to the image.'))) p.setChecked(tprefs['add_cover_preserve_aspect_ratio']) p.setVisible(self.container.book_type != 'azw3') def on_state_change(s): tprefs.set('add_cover_preserve_aspect_ratio', s == Qt.Checked) p.stateChanged.connect(on_state_change) self.info_label = il = QLabel('\xa0') h.addWidget(p), h.addStretch(1), h.addWidget(il) l.addLayout(h) l.addWidget(self.bb) b = self.bb.addButton(_('Import &image'), self.bb.ActionRole) b.clicked.connect(self.import_image) b.setIcon(QIcon(I('document_open.png'))) self.names.setFocus(Qt.OtherFocusReason) self.names.selectionModel().currentChanged.connect(self.current_image_changed) cname = get_raster_cover_name(self.container) if cname: row = self.names.model().find_name(cname) if row > -1: self.names.setCurrentIndex(self.names.model().index(row))
def build(self, container, preserve_state=True): if preserve_state: state = self.get_state() self.clear() self.root = self.invisibleRootItem() self.root.setFlags(Qt.ItemIsDragEnabled) self.categories = {} for category, text in ( ('text', _('Text')), ('styles', _('Styles')), ('images', _('Images')), ('fonts', _('Fonts')), ('misc', _('Miscellaneous')), ): self.categories[category] = i = QTreeWidgetItem(self.root, 0) i.setText(0, text) i.setData(0, Qt.DecorationRole, self.top_level_pixmap_cache[category]) f = i.font(0) f.setBold(True) i.setFont(0, f) i.setData(0, NAME_ROLE, category) flags = Qt.ItemIsEnabled if category == 'text': flags |= Qt.ItemIsDropEnabled i.setFlags(flags) processed, seen = {}, {} cover_page_name = get_cover_page_name(container) cover_image_name = get_raster_cover_name(container) manifested_names = set() for names in container.manifest_type_map.itervalues(): manifested_names |= set(names) def get_category(name, mt): category = 'misc' if mt.startswith('image/'): category = 'images' elif mt in OEB_FONTS: category = 'fonts' elif mt in OEB_STYLES: category = 'styles' elif mt in OEB_DOCS: category = 'text' ext = name.rpartition('.')[-1].lower() if ext in {'ttf', 'otf', 'woff'}: # Probably wrong mimetype in the OPF category = 'fonts' return category def set_display_name(name, item): if name in processed: # We have an exact duplicate (can happen if there are # duplicates in the spine) item.setText(0, processed[name].text(0)) item.setText(1, processed[name].text(1)) return parts = name.split('/') text = parts[-1] while text in seen and parts: text = parts.pop() + '/' + text seen[text] = item item.setText(0, text) item.setText(1, hexlify(sort_key(text))) def render_emblems(item, emblems): emblems = tuple(emblems) if not emblems: return icon = self.rendered_emblem_cache.get(emblems, None) if icon is None: pixmaps = [] for emblem in emblems: pm = self.emblem_cache.get(emblem, None) if pm is None: pm = self.emblem_cache[emblem] = QPixmap( I(emblem)).scaled( self.iconSize(), transformMode=Qt.SmoothTransformation) pixmaps.append(pm) num = len(pixmaps) w, h = pixmaps[0].width(), pixmaps[0].height() if num == 1: icon = self.rendered_emblem_cache[emblems] = QIcon( pixmaps[0]) else: canvas = QPixmap((num * w) + ((num - 1) * 2), h) canvas.fill(Qt.transparent) painter = QPainter(canvas) for i, pm in enumerate(pixmaps): painter.drawPixmap(i * (w + 2), 0, pm) painter.end() icon = self.rendered_emblem_cache[emblems] = canvas item.setData(0, Qt.DecorationRole, icon) cannot_be_renamed = container.names_that_must_not_be_changed ncx_mime = guess_type('a.ncx') def create_item(name, linear=None): imt = container.mime_map.get(name, guess_type(name)) icat = get_category(name, imt) category = 'text' if linear is not None else ({ 'text': 'misc' }.get(icat, icat)) item = QTreeWidgetItem( self.categories['text' if linear is not None else category], 1) flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable if category == 'text': flags |= Qt.ItemIsDragEnabled if name not in cannot_be_renamed: flags |= Qt.ItemIsEditable item.setFlags(flags) item.setStatusTip(0, _('Full path: ') + name) item.setData(0, NAME_ROLE, name) item.setData(0, CATEGORY_ROLE, category) item.setData(0, LINEAR_ROLE, bool(linear)) item.setData(0, MIME_ROLE, imt) set_display_name(name, item) tooltips = [] emblems = [] if name in {cover_page_name, cover_image_name}: emblems.append('default_cover.png') tooltips.append( _('This file is the cover %s for this book') % (_('image') if name == cover_image_name else _('page'))) if name in container.opf_name: emblems.append('metadata.png') tooltips.append( _('This file contains all the metadata and book structure information' )) if imt == ncx_mime: emblems.append('toc.png') tooltips.append( _('This file contains the metadata table of contents')) if name not in manifested_names and not container.ok_to_be_unmanifested( name): emblems.append('dialog_question.png') tooltips.append( _('This file is not listed in the book manifest')) if linear is False: emblems.append('arrow-down.png') tooltips.append( _('This file is marked as non-linear in the spine\nDrag it to the top to make it linear' )) if linear is None and icat == 'text': # Text item outside spine emblems.append('dialog_warning.png') tooltips.append( _('This file is a text file that is not referenced in the spine' )) if category == 'text' and name in processed: # Duplicate entry in spine emblems.append('dialog_error.png') tooltips.append( _('This file occurs more than once in the spine')) render_emblems(item, emblems) if tooltips: item.setData(0, Qt.ToolTipRole, '\n'.join(tooltips)) return item for name, linear in container.spine_names: processed[name] = create_item(name, linear=linear) for name in container.name_path_map: if name in processed: continue processed[name] = create_item(name) for name, c in self.categories.iteritems(): c.setExpanded(True) if name != 'text': c.sortChildren(1, Qt.AscendingOrder) if preserve_state: self.set_state(state) if self.current_edited_name: item = self.item_from_name(self.current_edited_name) if item is not None: self.mark_item_as_current(item)
def check_links(container): links_map = defaultdict(set) xml_types = {guess_type('a.opf'), guess_type('a.ncx')} errors = [] a = errors.append def fl(x): x = repr(x) if x.startswith('u'): x = x[1:] return x for name, mt in container.mime_map.iteritems(): if mt in OEB_DOCS or mt in OEB_STYLES or mt in xml_types: for href, lnum, col in container.iterlinks(name): if not href: a(EmptyLink(_('The link is empty'), name, lnum, col)) try: tname = container.href_to_name(href, name) except ValueError: tname = None # Absolute paths to files on another drive in windows cause this if tname is not None: if container.exists(tname): if tname in container.mime_map: links_map[name].add(tname) else: # Filesystem says the file exists, but it is not in # the mime_map, so either there is a case mismatch # or the link is a directory apath = container.name_to_abspath(tname) if os.path.isdir(apath): a( BadLink( _('The linked resource %s is a directory' ) % fl(href), name, lnum, col)) else: a( CaseMismatch( href, actual_case_for_name(container, tname), name, lnum, col)) else: cname = corrected_case_for_name(container, tname) if cname is not None: a(CaseMismatch(href, cname, name, lnum, col)) else: a( DanglingLink( _('The linked resource %s does not exist') % fl(href), tname, name, lnum, col)) else: purl = urlparse(href) if purl.scheme == 'file': a( FileLink( _('The link %s is a file:// URL') % fl(href), name, lnum, col)) elif purl.path and purl.path.startswith( '/') and purl.scheme in {'', 'file'}: a( LocalLink( _('The link %s points to a file outside the book' ) % fl(href), name, lnum, col)) elif purl.path and purl.scheme in { '', 'file' } and ':' in urlunquote(purl.path): a( InvalidCharInLink( _('The link %s contains a : character, this will cause errors on Windows computers' ) % fl(href), name, lnum, col)) spine_docs = {name for name, linear in container.spine_names} spine_styles = { tname for name in spine_docs for tname in links_map[name] if container.mime_map.get(tname, None) in OEB_STYLES } num = -1 while len(spine_styles) > num: # Handle import rules in stylesheets num = len(spine_styles) spine_styles |= { tname for name in spine_styles for tname in links_map[name] if container.mime_map.get(tname, None) in OEB_STYLES } seen = set(OEB_DOCS) | set(OEB_STYLES) spine_resources = { tname for name in spine_docs | spine_styles for tname in links_map[name] if container.mime_map[tname] not in seen } unreferenced = set() cover_name = container.guide_type_map.get('cover', None) nav_items = frozenset(container.manifest_items_with_property('nav')) for name, mt in container.mime_map.iteritems(): if mt in OEB_STYLES and name not in spine_styles: a(UnreferencedResource(name)) elif mt in OEB_DOCS and name not in spine_docs and name not in nav_items: a(UnreferencedDoc(name)) elif (mt in OEB_FONTS or mt.partition('/')[0] in { 'image', 'audio', 'video' }) and name not in spine_resources and name != cover_name: if mt.partition('/')[ 0] == 'image' and name == get_raster_cover_name(container): continue a(UnreferencedResource(name)) else: continue unreferenced.add(name) manifest_names = set(container.manifest_id_map.itervalues()) for name in container.mime_map: if name not in manifest_names and not container.ok_to_be_unmanifested( name): a(Unmanifested(name, unreferenced=name in unreferenced)) if name == 'META-INF/calibre_bookmarks.txt': a(Bookmarks(name)) return errors
def build(self, container, preserve_state=True): if container is None: return if preserve_state: state = self.get_state() self.clear() self.root = self.invisibleRootItem() self.root.setFlags(Qt.ItemIsDragEnabled) self.categories = {} for category, text, __ in CATEGORIES: self.categories[category] = i = QTreeWidgetItem(self.root, 0) i.setText(0, text) i.setData(0, Qt.DecorationRole, self.top_level_pixmap_cache[category]) f = i.font(0) f.setBold(True) i.setFont(0, f) i.setData(0, NAME_ROLE, category) flags = Qt.ItemIsEnabled if category == 'text': flags |= Qt.ItemIsDropEnabled i.setFlags(flags) processed, seen = {}, {} cover_page_name = get_cover_page_name(container) cover_image_name = get_raster_cover_name(container) manifested_names = set() for names in container.manifest_type_map.itervalues(): manifested_names |= set(names) def get_category(name, mt): category = 'misc' if mt.startswith('image/'): category = 'images' elif mt in OEB_FONTS: category = 'fonts' elif mt in OEB_STYLES: category = 'styles' elif mt in OEB_DOCS: category = 'text' ext = name.rpartition('.')[-1].lower() if ext in {'ttf', 'otf', 'woff'}: # Probably wrong mimetype in the OPF category = 'fonts' return category def set_display_name(name, item): if tprefs['file_list_shows_full_pathname']: text = name else: if name in processed: # We have an exact duplicate (can happen if there are # duplicates in the spine) item.setText(0, processed[name].text(0)) item.setText(1, processed[name].text(1)) return parts = name.split('/') text = parts.pop() while text in seen and parts: text = parts.pop() + '/' + text seen[text] = item item.setText(0, text) item.setText(1, hexlify(sort_key(text))) def render_emblems(item, emblems): emblems = tuple(emblems) if not emblems: return icon = self.rendered_emblem_cache.get(emblems, None) if icon is None: pixmaps = [] for emblem in emblems: pm = self.emblem_cache.get(emblem, None) if pm is None: pm = self.emblem_cache[emblem] = QIcon(I(emblem)).pixmap(self.iconSize()) pixmaps.append(pm) num = len(pixmaps) w, h = pixmaps[0].width(), pixmaps[0].height() if num == 1: icon = self.rendered_emblem_cache[emblems] = QIcon(pixmaps[0]) else: canvas = QPixmap((num * w) + ((num-1)*2), h) canvas.setDevicePixelRatio(pixmaps[0].devicePixelRatio()) canvas.fill(Qt.transparent) painter = QPainter(canvas) for i, pm in enumerate(pixmaps): painter.drawPixmap(int(i * (w + 2)/canvas.devicePixelRatio()), 0, pm) painter.end() icon = self.rendered_emblem_cache[emblems] = canvas item.setData(0, Qt.DecorationRole, icon) cannot_be_renamed = container.names_that_must_not_be_changed ncx_mime = guess_type('a.ncx') nav_items = frozenset(container.manifest_items_with_property('nav')) def create_item(name, linear=None): imt = container.mime_map.get(name, guess_type(name)) icat = get_category(name, imt) category = 'text' if linear is not None else ({'text':'misc'}.get(icat, icat)) item = QTreeWidgetItem(self.categories['text' if linear is not None else category], 1) flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable if category == 'text': flags |= Qt.ItemIsDragEnabled if name not in cannot_be_renamed: flags |= Qt.ItemIsEditable item.setFlags(flags) item.setStatusTip(0, _('Full path: ') + name) item.setData(0, NAME_ROLE, name) item.setData(0, CATEGORY_ROLE, category) item.setData(0, LINEAR_ROLE, bool(linear)) item.setData(0, MIME_ROLE, imt) set_display_name(name, item) tooltips = [] emblems = [] if name in {cover_page_name, cover_image_name}: emblems.append('default_cover.png') tooltips.append(_('This file is the cover %s for this book') % (_('image') if name == cover_image_name else _('page'))) if name in container.opf_name: emblems.append('metadata.png') tooltips.append(_('This file contains all the metadata and book structure information')) if imt == ncx_mime or name in nav_items: emblems.append('toc.png') tooltips.append(_('This file contains the metadata table of contents')) if name not in manifested_names and not container.ok_to_be_unmanifested(name): emblems.append('dialog_question.png') tooltips.append(_('This file is not listed in the book manifest')) if linear is False: emblems.append('arrow-down.png') tooltips.append(_('This file is marked as non-linear in the spine\nDrag it to the top to make it linear')) if linear is None and icat == 'text': # Text item outside spine emblems.append('dialog_warning.png') tooltips.append(_('This file is a text file that is not referenced in the spine')) if category == 'text' and name in processed: # Duplicate entry in spine emblems.append('dialog_error.png') tooltips.append(_('This file occurs more than once in the spine')) render_emblems(item, emblems) if tooltips: item.setData(0, Qt.ToolTipRole, '\n'.join(tooltips)) return item for name, linear in container.spine_names: processed[name] = create_item(name, linear=linear) for name in container.name_path_map: if name in processed: continue processed[name] = create_item(name) for name, c in self.categories.iteritems(): c.setExpanded(True) if name != 'text': c.sortChildren(1, Qt.AscendingOrder) if preserve_state: self.set_state(state) if self.current_edited_name: item = self.item_from_name(self.current_edited_name) if item is not None: self.mark_item_as_current(item)
def check_links(container): links_map = defaultdict(set) xml_types = {guess_type('a.opf'), guess_type('a.ncx')} errors = [] a = errors.append def fl(x): x = repr(x) if x.startswith('u'): x = x[1:] return x for name, mt in iteritems(container.mime_map): if mt in OEB_DOCS or mt in OEB_STYLES or mt in xml_types: for href, lnum, col in container.iterlinks(name): if not href: a(EmptyLink(_('The link is empty'), name, lnum, col)) try: tname = container.href_to_name(href, name) except ValueError: tname = None # Absolute paths to files on another drive in windows cause this if tname is not None: if container.exists(tname): if tname in container.mime_map: links_map[name].add(tname) else: # Filesystem says the file exists, but it is not in # the mime_map, so either there is a case mismatch # or the link is a directory apath = container.name_to_abspath(tname) if os.path.isdir(apath): a(BadLink(_('The linked resource %s is a directory') % fl(href), name, lnum, col)) else: a(CaseMismatch(href, actual_case_for_name(container, tname), name, lnum, col)) else: cname = corrected_case_for_name(container, tname) if cname is not None: a(CaseMismatch(href, cname, name, lnum, col)) else: a(DanglingLink(_('The linked resource %s does not exist') % fl(href), tname, name, lnum, col)) else: purl = urlparse(href) if purl.scheme == 'file': a(FileLink(_('The link %s is a file:// URL') % fl(href), name, lnum, col)) elif purl.path and purl.path.startswith('/') and purl.scheme in {'', 'file'}: a(LocalLink(_('The link %s points to a file outside the book') % fl(href), name, lnum, col)) elif purl.path and purl.scheme in {'', 'file'} and ':' in urlunquote(purl.path): a(InvalidCharInLink(_('The link %s contains a : character, this will cause errors on Windows computers') % fl(href), name, lnum, col)) spine_docs = {name for name, linear in container.spine_names} spine_styles = {tname for name in spine_docs for tname in links_map[name] if container.mime_map.get(tname, None) in OEB_STYLES} num = -1 while len(spine_styles) > num: # Handle import rules in stylesheets num = len(spine_styles) spine_styles |= {tname for name in spine_styles for tname in links_map[name] if container.mime_map.get(tname, None) in OEB_STYLES} seen = set(OEB_DOCS) | set(OEB_STYLES) spine_resources = {tname for name in spine_docs | spine_styles for tname in links_map[name] if container.mime_map[tname] not in seen} unreferenced = set() cover_name = container.guide_type_map.get('cover', None) nav_items = frozenset(container.manifest_items_with_property('nav')) for name, mt in iteritems(container.mime_map): if mt in OEB_STYLES and name not in spine_styles: a(UnreferencedResource(name)) elif mt in OEB_DOCS and name not in spine_docs and name not in nav_items: a(UnreferencedDoc(name)) elif (mt in OEB_FONTS or mt.partition('/')[0] in {'image', 'audio', 'video'}) and name not in spine_resources and name != cover_name: if mt.partition('/')[0] == 'image' and name == get_raster_cover_name(container): continue a(UnreferencedResource(name)) else: continue unreferenced.add(name) manifest_names = set(itervalues(container.manifest_id_map)) for name in container.mime_map: if name not in manifest_names and not container.ok_to_be_unmanifested(name): a(Unmanifested(name, unreferenced=name in unreferenced)) if name == 'META-INF/calibre_bookmarks.txt': a(Bookmarks(name)) return errors
def build(self, container, preserve_state=True): if preserve_state: state = self.get_state() self.clear() self.root = self.invisibleRootItem() self.root.setFlags(Qt.ItemIsDragEnabled) self.categories = {} for category, text in ( ("text", _("Text")), ("styles", _("Styles")), ("images", _("Images")), ("fonts", _("Fonts")), ("misc", _("Miscellaneous")), ): self.categories[category] = i = QTreeWidgetItem(self.root, 0) i.setText(0, text) i.setData(0, Qt.DecorationRole, self.top_level_pixmap_cache[category]) f = i.font(0) f.setBold(True) i.setFont(0, f) i.setData(0, NAME_ROLE, category) flags = Qt.ItemIsEnabled if category == "text": flags |= Qt.ItemIsDropEnabled i.setFlags(flags) processed, seen = {}, {} cover_page_name = get_cover_page_name(container) cover_image_name = get_raster_cover_name(container) manifested_names = set() for names in container.manifest_type_map.itervalues(): manifested_names |= set(names) font_types = {guess_type("a." + x) for x in ("ttf", "otf", "woff")} def get_category(name, mt): category = "misc" if mt.startswith("image/"): category = "images" elif mt in font_types: category = "fonts" elif mt in OEB_STYLES: category = "styles" elif mt in OEB_DOCS: category = "text" ext = name.rpartition(".")[-1].lower() if ext in {"ttf", "otf", "woff"}: # Probably wrong mimetype in the OPF category = "fonts" return category def set_display_name(name, item): if name in processed: # We have an exact duplicate (can happen if there are # duplicates in the spine) item.setText(0, processed[name].text(0)) item.setText(1, processed[name].text(1)) return parts = name.split("/") text = parts[-1] while text in seen and parts: text = parts.pop() + "/" + text seen[text] = item item.setText(0, text) item.setText(1, hexlify(sort_key(text))) def render_emblems(item, emblems): emblems = tuple(emblems) if not emblems: return icon = self.rendered_emblem_cache.get(emblems, None) if icon is None: pixmaps = [] for emblem in emblems: pm = self.emblem_cache.get(emblem, None) if pm is None: pm = self.emblem_cache[emblem] = QPixmap(I(emblem)).scaled( self.iconSize(), transformMode=Qt.SmoothTransformation ) pixmaps.append(pm) num = len(pixmaps) w, h = pixmaps[0].width(), pixmaps[0].height() if num == 1: icon = self.rendered_emblem_cache[emblems] = QIcon(pixmaps[0]) else: canvas = QPixmap((num * w) + ((num - 1) * 2), h) canvas.fill(Qt.transparent) painter = QPainter(canvas) for i, pm in enumerate(pixmaps): painter.drawPixmap(i * (w + 2), 0, pm) painter.end() icon = self.rendered_emblem_cache[emblems] = canvas item.setData(0, Qt.DecorationRole, icon) ok_to_be_unmanifested = container.names_that_need_not_be_manifested cannot_be_renamed = container.names_that_must_not_be_changed def create_item(name, linear=None): imt = container.mime_map.get(name, guess_type(name)) icat = get_category(name, imt) category = "text" if linear is not None else ({"text": "misc"}.get(icat, icat)) item = QTreeWidgetItem(self.categories["text" if linear is not None else category], 1) flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable if category == "text": flags |= Qt.ItemIsDragEnabled if name not in cannot_be_renamed: flags |= Qt.ItemIsEditable item.setFlags(flags) item.setStatusTip(0, _("Full path: ") + name) item.setData(0, NAME_ROLE, name) item.setData(0, CATEGORY_ROLE, category) item.setData(0, LINEAR_ROLE, bool(linear)) item.setData(0, MIME_ROLE, imt) set_display_name(name, item) # TODO: Add appropriate tooltips based on the emblems emblems = [] if name in {cover_page_name, cover_image_name}: emblems.append("default_cover.png") if name not in manifested_names and name not in ok_to_be_unmanifested: emblems.append("dialog_question.png") if linear is False: emblems.append("arrow-down.png") if linear is None and icat == "text": # Text item outside spine emblems.append("dialog_warning.png") if category == "text" and name in processed: # Duplicate entry in spine emblems.append("dialog_warning.png") render_emblems(item, emblems) return item for name, linear in container.spine_names: processed[name] = create_item(name, linear=linear) all_files = list(container.manifest_type_map.iteritems()) all_files.append((guess_type("a.opf"), [container.opf_name])) for name in container.name_path_map: if name in processed: continue processed[name] = create_item(name) for name, c in self.categories.iteritems(): c.setExpanded(True) if name != "text": c.sortChildren(1, Qt.AscendingOrder) if preserve_state: self.set_state(state)
def build(self, container, preserve_state=True): if preserve_state: state = self.get_state() self.clear() self.root = self.invisibleRootItem() self.root.setFlags(Qt.ItemIsDragEnabled) self.categories = {} for category, text in ( ('text', _('Text')), ('styles', _('Styles')), ('images', _('Images')), ('fonts', _('Fonts')), ('misc', _('Miscellaneous')), ): self.categories[category] = i = QTreeWidgetItem(self.root, 0) i.setText(0, text) i.setData(0, Qt.DecorationRole, self.top_level_pixmap_cache[category]) f = i.font(0) f.setBold(True) i.setFont(0, f) i.setData(0, NAME_ROLE, category) flags = Qt.ItemIsEnabled if category == 'text': flags |= Qt.ItemIsDropEnabled i.setFlags(flags) processed, seen = {}, {} cover_page_name = get_cover_page_name(container) cover_image_name = get_raster_cover_name(container) manifested_names = set() for names in container.manifest_type_map.itervalues(): manifested_names |= set(names) font_types = {guess_type('a.'+x) for x in ('ttf', 'otf', 'woff')} def get_category(mt): category = 'misc' if mt.startswith('image/'): category = 'images' elif mt in font_types: category = 'fonts' elif mt in OEB_STYLES: category = 'styles' elif mt in OEB_DOCS: category = 'text' return category def set_display_name(name, item): if name in processed: # We have an exact duplicate (can happen if there are # duplicates in the spine) item.setText(0, processed[name].text(0)) return parts = name.split('/') text = parts[-1] while text in seen and parts: text = parts.pop() + '/' + text seen[text] = item item.setText(0, text) def render_emblems(item, emblems): emblems = tuple(emblems) if not emblems: return icon = self.rendered_emblem_cache.get(emblems, None) if icon is None: pixmaps = [] for emblem in emblems: pm = self.emblem_cache.get(emblem, None) if pm is None: pm = self.emblem_cache[emblem] = QPixmap( I(emblem)).scaled(self.iconSize(), transformMode=Qt.SmoothTransformation) pixmaps.append(pm) num = len(pixmaps) w, h = pixmaps[0].width(), pixmaps[0].height() if num == 1: icon = self.rendered_emblem_cache[emblems] = QIcon(pixmaps[0]) else: canvas = QPixmap((num * w) + ((num-1)*2), h) canvas.fill(Qt.transparent) painter = QPainter(canvas) for i, pm in enumerate(pixmaps): painter.drawPixmap(i * (w + 2), 0, pm) painter.end() icon = self.rendered_emblem_cache[emblems] = canvas item.setData(0, Qt.DecorationRole, icon) ok_to_be_unmanifested = container.names_that_need_not_be_manifested def create_item(name, linear=None): imt = container.mime_map.get(name, guess_type(name)) icat = get_category(imt) category = 'text' if linear is not None else ({'text':'misc'}.get(icat, icat)) item = QTreeWidgetItem(self.categories['text' if linear is not None else category], 1) flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable if category == 'text': flags |= Qt.ItemIsDragEnabled item.setFlags(flags) item.setStatusTip(0, _('Full path: ') + name) item.setData(0, NAME_ROLE, name) item.setData(0, CATEGORY_ROLE, category) set_display_name(name, item) # TODO: Add appropriate tooltips based on the emblems emblems = [] if name in {cover_page_name, cover_image_name}: emblems.append('default_cover.png') if name not in manifested_names and name not in ok_to_be_unmanifested: emblems.append('dialog_question.png') if linear is False: emblems.append('arrow-down.png') if linear is None and icat == 'text': # Text item outside spine emblems.append('dialog_warning.png') if category == 'text' and name in processed: # Duplicate entry in spine emblems.append('dialog_warning.png') render_emblems(item, emblems) return item for name, linear in container.spine_names: processed[name] = create_item(name, linear=linear) all_files = list(container.manifest_type_map.iteritems()) all_files.append((guess_type('a.opf'), [container.opf_name])) for name in container.name_path_map: if name in processed: continue processed[name] = create_item(name) for c in self.categories.itervalues(): c.setExpanded(True) if preserve_state: self.set_state(state)