diff --git a/src/calibre/gui2/actions.py b/src/calibre/gui2/actions.py index 4b2e367080..6da04a41be 100644 --- a/src/calibre/gui2/actions.py +++ b/src/calibre/gui2/actions.py @@ -1064,6 +1064,12 @@ class ViewAction(object): # {{{ if fmt_path: self._view_file(fmt_path) + def view_format_by_id(self, id_, format): + fmt_path = self.library_view.model().db.format_abspath(id_, format, + index_is_id=True) + if fmt_path: + self._view_file(fmt_path) + def metadata_view_format(self, fmt): fmt_path = self.library_view.model().db.\ format_abspath(self._metadata_view_id, @@ -1146,6 +1152,9 @@ class ViewAction(object): # {{{ path = self.library_view.model().db.abspath(row.row()) QDesktopServices.openUrl(QUrl.fromLocalFile(path)) + def view_folder_for_id(self, id_): + path = self.library_view.model().db.abspath(id_, index_is_id=True) + QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def view_book(self, triggered): rows = self.current_view().selectionModel().selectedRows() diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 0cd7c1d419..20ddfae0b4 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -121,6 +121,7 @@ class BookInfo(QDialog, Ui_BookInfo): f = f.strip() info[_('Formats')] += '%s, '%(f,f) for key in info.keys(): + if key == 'id': continue txt = info[key] txt = u'
\n'.join(textwrap.wrap(txt, 120)) rows += u'%s:%s'%(key, txt) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 293a522a9e..ff4b2b6ee9 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -21,7 +21,7 @@ from calibre.utils.date import dt_factory, qt_to_dt, isoformat from calibre.ebooks.metadata.meta import set_metadata as _set_metadata from calibre.utils.search_query_parser import SearchQueryParser from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH -from calibre import strftime, isbytestring +from calibre import strftime, isbytestring, prepare_string_for_xml from calibre.constants import filesystem_encoding from calibre.gui2.library import DEFAULT_SORT @@ -300,6 +300,7 @@ class BooksModel(QAbstractTableModel): # {{{ formats = _('None') data[_('Formats')] = formats data[_('Path')] = self.db.abspath(idx) + data['id'] = self.id(idx) comments = self.db.comments(idx) if not comments: comments = _('None') @@ -308,7 +309,9 @@ class BooksModel(QAbstractTableModel): # {{{ if series: sidx = self.db.series_index(idx) sidx = fmt_sidx(sidx, use_roman = self.use_roman_numbers) - data[_('Series')] = _('Book %s of %s.')%(sidx, series) + data[_('Series')] = \ + _('Book %s of %s.')%\ + (sidx, prepare_string_for_xml(series)) return data diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index 06c3e9c85f..377410cf86 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -1,6 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import os, collections + +import os from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \ QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \ @@ -13,6 +14,7 @@ from calibre.gui2.widgets import IMAGE_EXTENSIONS from calibre.gui2.notify import get_notifier from calibre.ebooks import BOOK_EXTENSIONS from calibre.library.comments import comments_to_html +from calibre.gui2.book_details import render_rows class BookInfoDisplay(QWidget): @@ -91,24 +93,27 @@ class BookInfoDisplay(QWidget): class BookDataDisplay(QLabel): - mr = pyqtSignal(int) + mr = pyqtSignal(object) + link_clicked = pyqtSignal(object) def __init__(self): QLabel.__init__(self) self.setText('') self.setWordWrap(True) self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) + self.linkActivated.connect(self.link_activated) + self._link_clicked = False def mouseReleaseEvent(self, ev): - self.mr.emit(1) + QLabel.mouseReleaseEvent(self, ev) + if not self._link_clicked: + self.mr.emit(ev) + self._link_clicked = False - WEIGHTS = collections.defaultdict(lambda : 100) - WEIGHTS[_('Path')] = 0 - WEIGHTS[_('Formats')] = 1 - WEIGHTS[_('Collections')] = 2 - WEIGHTS[_('Series')] = 3 - WEIGHTS[_('Tags')] = 4 - WEIGHTS[_('Comments')] = 5 + def link_activated(self, link): + self._link_clicked = True + link = unicode(link) + self.link_clicked.emit(link) show_book_info = pyqtSignal() @@ -129,6 +134,7 @@ class BookInfoDisplay(QWidget): self._layout.setAlignment(self.cover_display, Qt.AlignTop|Qt.AlignLeft) def mouseReleaseEvent(self, ev): + ev.accept() self.show_book_info.emit() def show_data(self, data): @@ -140,23 +146,11 @@ class BookInfoDisplay(QWidget): rows, comments = [], '' self.book_data.setText('') self.data = data.copy() - keys = data.keys() - keys.sort(cmp=lambda x, y: cmp(self.WEIGHTS[x], self.WEIGHTS[y])) - for key in keys: - txt = data[key] - if not txt or not txt.strip() or txt == 'None': - continue - if isinstance(key, str): - key = key.decode(preferred_encoding, 'replace') - if isinstance(txt, str): - txt = txt.decode(preferred_encoding, 'replace') - if key == _('Comments'): - comments = comments_to_html(txt) - else: - rows.append((key, txt)) + rows = render_rows(self.data) rows = '\n'.join([u'%s:%s'%(k,t) for k, t in rows]) - if comments: + if _('Comments') in self.data: + comments = comments_to_html(self.data[_('Comments')]) comments = 'Comments:'+comments left_pane = u'%s
'%rows right_pane = u'
%s
'%comments @@ -193,6 +187,8 @@ class BookDetailsInterface(object): # These signals must be defined in the class implementing this interface files_dropped = None show_book_info = None + open_containing_folder = None + view_specific_format = None def reset_info(self): raise NotImplementedError() @@ -204,7 +200,8 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface): files_dropped = pyqtSignal(object, object) show_book_info = pyqtSignal() - + open_containing_folder = pyqtSignal(int) + view_specific_format = pyqtSignal(int, object) resized = pyqtSignal(object) @@ -219,11 +216,21 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface): type=Qt.QueuedConnection) self.book_info.files_dropped.connect(self.files_dropped.emit, type=Qt.QueuedConnection) + self.book_info.book_data.link_clicked.connect(self._link_clicked) self.addWidget(self.scroll_area, 100) self.setMinimumHeight(120) self.resized.connect(self.book_info.cover_display.relayout) self.book_info.cover_display.relayout(self.size()) + + def _link_clicked(self, link): + typ, _, val = link.partition(':') + if typ == 'path': + self.open_containing_folder.emit(int(val)) + if typ == 'format': + id_, fmt = val.split(':') + self.view_specific_format.emit(int(id_), fmt) + def resizeEvent(self, ev): self.resized.emit(self.size()) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 682ede1978..99c7856478 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -219,6 +219,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ self.status_bar.initialize(self.system_tray_icon) self.book_details.show_book_info.connect(self.show_book_info) self.book_details.files_dropped.connect(self.files_dropped_on_book) + self.book_details.open_containing_folder.connect(self.view_folder_for_id) + self.book_details.view_specific_format.connect(self.view_format_by_id) ####################### Setup Toolbar ##################### ToolbarMixin.__init__(self)