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''%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)