diff --git a/Changelog.yaml b/Changelog.yaml index 3bbb9e83a1..567f437d53 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -23,7 +23,8 @@ date: 2012-03-23 new features: - - title: "E-book viewer: A whole new full screen mode, with no toolbars to distract from the text and the ability to set the width of the column of text via Preferences in the ebook viewer." + - title: "E-book viewer: A whole new full screen mode." + description: "The new mode has no toolbars to distract from the text and the ability to set the width of the column of text via Preferences in the ebook viewer. Click the Fullscreen button on the toolbar in the viewer to enter fullscreen mode (or press the F11 or Ctrl+Shit+F keys)" type: major tickets: [959830] diff --git a/src/calibre/ebooks/mobi/debug/headers.py b/src/calibre/ebooks/mobi/debug/headers.py index 2cc7954559..b367be2e4a 100644 --- a/src/calibre/ebooks/mobi/debug/headers.py +++ b/src/calibre/ebooks/mobi/debug/headers.py @@ -328,7 +328,7 @@ class MOBIHeader(object): # {{{ (self.sect_idx, self.skel_idx, self.datp_idx, self.oth_idx ) = struct.unpack_from(b'>4L', self.raw, 248) self.unknown9 = self.raw[264:self.length] - if self.meta_orth_indx != self.sect_idx: + if self.meta_orth_indx not in {NULL_INDEX, self.sect_idx}: raise ValueError('KF8 header has different Meta orth and ' 'section indices') diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py index 1324a3cbd4..2659339190 100644 --- a/src/calibre/gui2/comments_editor.py +++ b/src/calibre/gui2/comments_editor.py @@ -9,10 +9,10 @@ import re, os from lxml import html -from PyQt4.Qt import QApplication, QFontInfo, QSize, QWidget, QPlainTextEdit, \ - QToolBar, QVBoxLayout, QAction, QIcon, Qt, QTabWidget, QUrl, \ - QSyntaxHighlighter, QColor, QChar, QColorDialog, QMenu, QInputDialog, \ - QHBoxLayout +from PyQt4.Qt import (QApplication, QFontInfo, QSize, QWidget, QPlainTextEdit, + QToolBar, QVBoxLayout, QAction, QIcon, Qt, QTabWidget, QUrl, + QSyntaxHighlighter, QColor, QChar, QColorDialog, QMenu, QInputDialog, + QHBoxLayout, QKeySequence) from PyQt4.QtWebKit import QWebView, QWebPage from calibre.ebooks.chardet import xml_to_unicode @@ -32,6 +32,7 @@ class PageAction(QAction): # {{{ type=Qt.QueuedConnection) self.page_action.changed.connect(self.update_state, type=Qt.QueuedConnection) + self.update_state() @property def page_action(self): @@ -66,6 +67,12 @@ class EditorWidget(QWebView): # {{{ self.comments_pat = re.compile(r'', re.DOTALL) + extra_shortcuts = { + 'ToggleBold': 'Bold', + 'ToggleItalic': 'Italic', + 'ToggleUnderline': 'Underline', + } + for wac, name, icon, text, checkable in [ ('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True), ('ToggleItalic', 'italic', 'format-text-italic', _('Italic'), @@ -106,6 +113,9 @@ class EditorWidget(QWebView): # {{{ ]: ac = PageAction(wac, icon, text, checkable, self) setattr(self, 'action_'+name, ac) + ss = extra_shortcuts.get(wac, None) + if ss: + ac.setShortcut(QKeySequence(getattr(QKeySequence, ss))) self.action_color = QAction(QIcon(I('format-text-color')), _('Foreground color'), self) diff --git a/src/calibre/gui2/complete.py b/src/calibre/gui2/complete.py index bb9e0cb4c9..b5c1fc8b3e 100644 --- a/src/calibre/gui2/complete.py +++ b/src/calibre/gui2/complete.py @@ -6,8 +6,8 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import QLineEdit, QAbstractListModel, Qt, \ - QApplication, QCompleter +from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt, + QApplication, QCompleter, QMetaObject) from calibre.utils.icu import sort_key, lower from calibre.gui2 import NONE @@ -182,14 +182,27 @@ class MultiCompleteComboBox(EnComboBox): def set_add_separator(self, what): self.lineEdit().set_add_separator(what) - + def show_initial_value(self, what): + ''' + Show an initial value. Handle the case of the initial value being blank + correctly (on Qt 4.8.0 having a blank value causes the first value from + the completer to be shown, when the event loop runs). + ''' + what = unicode(what) + le = self.lineEdit() + if not what.strip(): + QMetaObject.invokeMethod(self, 'clearEditText', + Qt.QueuedConnection) + else: + self.setEditText(what) + le.selectAll() if __name__ == '__main__': from PyQt4.Qt import QDialog, QVBoxLayout app = QApplication([]) d = QDialog() d.setLayout(QVBoxLayout()) - le = MultiCompleteLineEdit(d) + le = MultiCompleteComboBox(d) d.layout().addWidget(le) le.all_items = ['one', 'otwo', 'othree', 'ooone', 'ootwo', 'oothree'] d.exec_() diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 81d25c1f5e..60b8e3445d 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -128,8 +128,7 @@ class TextDelegate(QStyledItemDelegate): # {{{ for item in sorted(complete_items, key=sort_key): editor.addItem(item) ct = index.data(Qt.DisplayRole).toString() - editor.setEditText(ct) - editor.lineEdit().selectAll() + editor.show_initial_value(ct) else: editor = EnLineEdit(parent) return editor @@ -170,8 +169,7 @@ class CompleteDelegate(QStyledItemDelegate): # {{{ for item in sorted(all_items, key=sort_key): editor.addItem(item) ct = index.data(Qt.DisplayRole).toString() - editor.setEditText(ct) - editor.lineEdit().selectAll() + editor.show_initial_value(ct) else: editor = EnLineEdit(parent) return editor @@ -190,8 +188,7 @@ class LanguagesDelegate(QStyledItemDelegate): # {{{ editor = LanguagesEdit(parent=parent) editor.init_langs(index.model().db) ct = index.data(Qt.DisplayRole).toString() - editor.setEditText(ct) - editor.lineEdit().selectAll() + editor.show_initial_value(ct) return editor def setModelData(self, editor, model, index): diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 095b57af60..eabde31015 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -882,6 +882,11 @@ class FullFetch(QDialog): # {{{ self.covers_widget.chosen.connect(self.ok_clicked) self.stack.addWidget(self.covers_widget) + # Workaround for Qt 4.8.0 bug that causes the frame of the window to go + # off the top of the screen if a max height is not set for the + # QWebView. Seems to only happen on windows, but keep it for all + # platforms just in case. + self.identify_widget.comments_view.setMaximumHeight(500) self.resize(850, 550) self.finished.connect(self.cleanup) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 11f2c61427..b03de237c1 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -709,7 +709,7 @@ class DocumentView(QWebView): # {{{ if self.manager is not None: self.manager.load_started() self.loading_url = QUrl.fromLocalFile(path) - html = re.sub(r'<\s*title\s*/\s*>', '', html, flags=re.IGNORECASE) + html = re.sub(ur'<\s*title\s*/\s*>', u'', html, flags=re.IGNORECASE) if has_svg: self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path)) else: diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 7ea5ceb5b4..da1b1e27c6 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -204,7 +204,8 @@ class DevNull(object): pass NULL = DevNull() -def do_add(db, paths, one_book_per_directory, recurse, add_duplicates): +def do_add(db, paths, one_book_per_directory, recurse, add_duplicates, otitle, + oauthors, oisbn, otags, oseries, oseries_index): orig = sys.stdout #sys.stdout = NULL try: @@ -231,6 +232,11 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates): mi.title = os.path.splitext(os.path.basename(book))[0] if not mi.authors: mi.authors = [_('Unknown')] + for x in ('title', 'authors', 'isbn', 'tags', 'series'): + val = locals()[x] + if val: setattr(mi, x[1:], val) + if oseries: + mi.series_index = oseries_index formats.append(format) metadata.append(mi) @@ -302,39 +308,56 @@ the directory related options below. parser.add_option('-e', '--empty', action='store_true', default=False, help=_('Add an empty book (a book with no formats)')) parser.add_option('-t', '--title', default=None, - help=_('Set the title of the added empty book')) + help=_('Set the title of the added book(s)')) parser.add_option('-a', '--authors', default=None, - help=_('Set the authors of the added empty book')) + help=_('Set the authors of the added book(s)')) parser.add_option('-i', '--isbn', default=None, - help=_('Set the ISBN of the added empty book')) + help=_('Set the ISBN of the added book(s)')) + parser.add_option('-T', '--tags', default=None, + help=_('Set the tags of the added book(s)')) + parser.add_option('-s', '--series', default=None, + help=_('Set the series of the added book(s)')) + parser.add_option('-S', '--series-index', default=1.0, type=float, + help=_('Set the series number of the added book(s)')) + return parser -def do_add_empty(db, title, authors, isbn): - from calibre.ebooks.metadata import MetaInformation, string_to_authors +def do_add_empty(db, title, authors, isbn, tags, series, series_index): + from calibre.ebooks.metadata import MetaInformation mi = MetaInformation(None) if title is not None: mi.title = title if authors: - mi.authors = string_to_authors(authors) + mi.authors = authors if isbn: mi.isbn = isbn + if tags: + mi.tags = tags + if series: + mi.series, mi.series_index = series, series_index db.import_book(mi, []) write_dirtied(db) send_message() def command_add(args, dbpath): + from calibre.ebooks.metadata import string_to_authors parser = add_option_parser() opts, args = parser.parse_args(sys.argv[:1] + args) + aut = string_to_authors(opts.authors) if opts.authors else [] + tags = [x.strip() for x in opts.tags.split(',')] if opts.tags else [] if opts.empty: - do_add_empty(get_db(dbpath, opts), opts.title, opts.authors, opts.isbn) + do_add_empty(get_db(dbpath, opts), opts.title, aut, opts.isbn, tags, + opts.series, opts.series_index) return 0 if len(args) < 2: parser.print_help() print print >>sys.stderr, _('You must specify at least one file to add') return 1 - do_add(get_db(dbpath, opts), args[1:], opts.one_book_per_directory, opts.recurse, opts.duplicates) + do_add(get_db(dbpath, opts), args[1:], opts.one_book_per_directory, + opts.recurse, opts.duplicates, opts.title, opts.author, opts.isbn, + tags, opts.series, opts.series_index) return 0 def do_remove(db, ids):