diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index b27c9b4ab4..72b305fd78 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -5,24 +5,68 @@ __docformat__ = 'restructuredtext en' ''' ''' -import textwrap +import textwrap, os -from PyQt4.QtCore import QCoreApplication -from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon +from PyQt4.QtCore import QCoreApplication, SIGNAL, QModelIndex, QUrl +from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon, QDesktopServices from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo class BookInfo(QDialog, Ui_BookInfo): - def __init__(self, parent, info): + def __init__(self, parent, view, row): QDialog.__init__(self, parent) Ui_BookInfo.__init__(self) self.setupUi(self) - - self.setWindowTitle(info[_('Title')]) desktop = QCoreApplication.instance().desktop() screen_height = desktop.availableGeometry().height() - 100 self.resize(self.size().width(), screen_height) + + + self.view = view + self.current_row = None + self.refresh(row) + self.connect(self.view.selectionModel(), SIGNAL('currentChanged(QModelIndex,QModelIndex)'), self.slave) + self.connect(self.next_button, SIGNAL('clicked()'), self.next) + self.connect(self.previous_button, SIGNAL('clicked()'), self.previous) + self.connect(self.text, SIGNAL('linkActivated(QString)'), self.open_book_path) + + def slave(self, current, previous): + row = current.row() + self.refresh(row) + + def open_book_path(self, path): + if os.sep in unicode(path): + QDesktopServices.openUrl(QUrl('file:'+path)) + else: + format = unicode(path) + path = self.view.model().db.format_abspath(self.current_row, format) + if path is not None: + QDesktopServices.openUrl(QUrl('file:'+path)) + + + def next(self): + row = self.view.currentIndex().row() + ni = self.view.model().index(row+1, 0) + if ni.isValid(): + self.view.setCurrentIndex(ni) + + def previous(self): + row = self.view.currentIndex().row() + ni = self.view.model().index(row-1, 0) + if ni.isValid(): + self.view.setCurrentIndex(ni) + + def refresh(self, row): + if isinstance(row, QModelIndex): + row = row.row() + if row == self.current_row: + return + self.previous_button.setEnabled(False if row == 0 else True) + self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True) + self.current_row = row + info = self.view.model().get_book_info(row) + self.setWindowTitle(info[_('Title')]) self.title.setText(''+info.pop(_('Title'))) self.comments.setText(info.pop(_('Comments'), '')) @@ -37,6 +81,15 @@ class BookInfo(QDialog, Ui_BookInfo): rows = u'' self.text.setText('') self.data = info + if _('Path') in info.keys(): + p = info[_('Path')] + info[_('Path')] = '%s'%(p, p) + if _('Formats') in info.keys(): + formats = info[_('Formats')].split(',') + info[_('Formats')] = '' + for f in formats: + f = f.strip() + info[_('Formats')] += '%s, '%(f,f) for key in info.keys(): txt = info[key] txt = u'
\n'.join(textwrap.wrap(txt, 120)) diff --git a/src/calibre/gui2/dialogs/book_info.ui b/src/calibre/gui2/dialogs/book_info.ui index d79dbfe5c7..28b27b99b4 100644 --- a/src/calibre/gui2/dialogs/book_info.ui +++ b/src/calibre/gui2/dialogs/book_info.ui @@ -6,14 +6,14 @@ 0 0 917 - 780 + 783 Dialog - - + + TextLabel @@ -23,51 +23,68 @@ - - - - - 0 - 0 - - - - Qt::Horizontal - - - - + + + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Comments + + + + + + + + + + - + - TextLabel + &Previous - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true + + + :/images/previous.svg:/images/previous.svg - - - Comments + + + &Next + + + + :/images/next.svg:/images/next.svg - - - - - - - + + - + + + diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 5309455b99..c298147497 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -230,6 +230,7 @@ class BooksModel(QAbstractTableModel): else: formats = _('None') data[_('Formats')] = formats + data[_('Path')] = self.db.abspath(idx) comments = self.db.comments(idx) if not comments: comments = _('None') @@ -265,6 +266,8 @@ class BooksModel(QAbstractTableModel): return data def get_book_info(self, index): + if isinstance(index, int): + index = self.index(index, 0) data = self.current_changed(index, None, False) row = index.row() data[_('Title')] = self.db.title(row) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 6204bc40ee..711136b6bd 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -1185,8 +1185,7 @@ in which you want to store your books files. Any existing books will be automati return index = self.library_view.currentIndex() if index.isValid(): - info = self.library_view.model().get_book_info(index) - BookInfo(self, info).show() + BookInfo(self, self.library_view, index).show() ############################################################################ diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index d627330f1f..e141812033 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -73,7 +73,7 @@ class BookInfoDisplay(QFrame): rows = u'' self.book_data.setText('') - self.data = data + self.data = data.copy() for key in data.keys(): txt = data[key] #txt = '
\n'.join(textwrap.wrap(txt, 120)) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index d3a231a87c..dbfe0c7592 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -418,9 +418,16 @@ class LibraryDatabase2(LibraryDatabase): def path(self, index, index_is_id=False): 'Return the relative path to the directory containing this books files as a unicode string.' - id = index if index_is_id else self.id() + id = index if index_is_id else self.id(index) path = self.conn.execute('SELECT path FROM books WHERE id=?', (id,)).fetchone()[0].replace('/', os.sep) return path + + def abspath(self, index, index_is_id=False): + 'Return the absolute path to the directory containing this books files as a unicode string.' + path = os.path.join(self.library_path, self.path(index, index_is_id=index_is_id)) + if not os.path.exists(path): + os.makedirs(path) + return path def construct_path_name(self, id): @@ -550,13 +557,8 @@ class LibraryDatabase2(LibraryDatabase): return ','.join(ans) - def format(self, index, format, index_is_id=False, as_file=False, mode='r+b'): - ''' - Return the ebook format as a bytestring or `None` if the format doesn't exist, - or we don't have permission to write to the ebook file. - - `as_file`: If True the ebook format is returned as a file object opened in `mode` - ''' + def format_abspath(self, index, format, index_is_id=False): + 'Return absolute path to the ebook file of format `format`' id = index if index_is_id else self.id(index) path = os.path.join(self.library_path, self.path(id, index_is_id=True)) name = self.conn.execute('SELECT name FROM data WHERE book=? AND format=?', (id, format)).fetchone()[0] @@ -564,8 +566,19 @@ class LibraryDatabase2(LibraryDatabase): format = ('.' + format.lower()) if format else '' path = os.path.join(path, name+format) if os.access(path, os.R_OK|os.W_OK): - f = open(path, mode) - return f if as_file else f.read() + return path + + def format(self, index, format, index_is_id=False, as_file=False, mode='r+b'): + ''' + Return the ebook format as a bytestring or `None` if the format doesn't exist, + or we don't have permission to write to the ebook file. + + `as_file`: If True the ebook format is returned as a file object opened in `mode` + ''' + path = self.format_abspath(index, format, index_is_id=index_is_id) + if path is not None: + f = open(path, mode) + return f if as_file else f.read() self.remove_format(id, format, index_is_id=True) def add_format(self, index, format, stream, index_is_id=False, path=None): @@ -578,6 +591,9 @@ class LibraryDatabase2(LibraryDatabase): name = self.construct_file_name(id) ext = ('.' + format.lower()) if format else '' dest = os.path.join(path, name+ext) + pdir = os.path.dirname(dest) + if not os.path.exists(pdir): + os.makedirs(pdir) with open(dest, 'wb') as f: shutil.copyfileobj(stream, f) stream.seek(0, 2)