mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge
This commit is contained in:
commit
649bae6933
129
Changelog.yaml
129
Changelog.yaml
@ -4,12 +4,137 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
#- version: ?.?.?
|
||||||
|
# date: 2011-??-??
|
||||||
|
#
|
||||||
|
# new features:
|
||||||
|
# - title:
|
||||||
|
#
|
||||||
|
# bug fixes:
|
||||||
|
# - title:
|
||||||
|
#
|
||||||
|
# improved recipes:
|
||||||
|
# -
|
||||||
|
#
|
||||||
|
# new recipes:
|
||||||
|
# - title:
|
||||||
|
|
||||||
|
|
||||||
|
- version: 0.7.43
|
||||||
|
date: 2011-01-28
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Ask for confirmation when stopping running jobs"
|
||||||
|
tickets: [3101]
|
||||||
|
|
||||||
|
- title: "Combine the database integrity check and library check into a single menu item. Also nicer implementation of the db integrity check."
|
||||||
|
|
||||||
|
- title: "BiBTeX Catalog: Add option to include file paths in the catalog."
|
||||||
|
tickets: [8589]
|
||||||
|
|
||||||
|
- title: "Create 'generic' output profiles and generic devices in the welcome wizard"
|
||||||
|
|
||||||
|
- title: "Bulk metadata edit: Custom column widgets all have an apply checkbox next to them."
|
||||||
|
|
||||||
|
- title: "Only use LibraryThing to download metadata if the user provides a library thing username and password. Since LT doesn't like web scraping"
|
||||||
|
|
||||||
|
- title: "Allow renaming of user categories in the manage categories dialog. Also allow searching for books in a category from the Tag Browser by right clicking ona a category"
|
||||||
|
|
||||||
|
- title: "Folder device plugin: Add option to disable the use of sub folders"
|
||||||
|
|
||||||
|
- title: "Allow saving/loading of search and replace expressions in the bulk metadata edit dialog."
|
||||||
|
|
||||||
|
- title: "Remeber previously used regular expression in the add books preferences dialog"
|
||||||
|
|
||||||
|
- title: "Search and replace wizard: Cache the previously used input document."
|
||||||
|
|
||||||
|
- title: "Pressing Esc clears the current search in the main book list"
|
||||||
|
|
||||||
|
- title: "Preselct right formats when using send specific format to device"
|
||||||
|
tickets: [7834]
|
||||||
|
|
||||||
|
- title: "Regex wizard gets find next and previous match buttons"
|
||||||
|
tickets: [4486]
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Do not allow customization of user interface plugins until calibre is restarted"
|
||||||
|
tickets: [8621]
|
||||||
|
|
||||||
|
- title: "EPUB Output: When using preserve cover aspect ratio, use the actual image sizes in the SVG template as otherwise ADE doesn't fully preserve the aspect ratio"
|
||||||
|
|
||||||
|
- title: "Fix completion on a word with a trailing space causing the first letter to be duplicated in the edit metadata dialog"
|
||||||
|
|
||||||
|
- title: "PML Input: PML x and Xn tags don't indent properly in TOC. Also handle invalid T markup and retain soft scene breaks"
|
||||||
|
tickets: [6194, 8565]
|
||||||
|
|
||||||
|
- title: "TXT Input: Retain whitespace at the beginning of lines. Don't preserve spaces in heuristic processing. Detect and retain soft scene breaks."
|
||||||
|
|
||||||
|
- title: "Fix Adding empty book - cover browser doesn't update"
|
||||||
|
tickets: [8557]
|
||||||
|
|
||||||
|
- title: "When generating author sort string ignore trailing Inc."
|
||||||
|
tickets: [8539]
|
||||||
|
|
||||||
|
- title: "When converting HTML/ZIP files do not leave temporary files that are only deleted on application shutdown."
|
||||||
|
tickets: [8597]
|
||||||
|
|
||||||
|
- title: "Don't crash if the prefs stored in the db are corrupted"
|
||||||
|
|
||||||
|
- title: "Catalog generation: Do not use inline-block CSS as apparently Adobe Digital Editions cannot handle it."
|
||||||
|
tickets: [8566]
|
||||||
|
|
||||||
|
- title: "Fix extra spaces being inserted into TOC title when reading TOC from OPF guide element."
|
||||||
|
tickets: [8569]
|
||||||
|
|
||||||
|
- title: "Remember window size for bulk metadata edit and catalog generation dialogs"
|
||||||
|
tickets: [8525]
|
||||||
|
|
||||||
|
- title: "Heuristics, italicize common cases: Enhance pattern matching to match punctuation after pattern."
|
||||||
|
|
||||||
|
- title: "Fix regression in converting HTML files that have ASCII-encoded non unicode characters inside their <style> tags. Apparently Word generates these."
|
||||||
|
tickets: [8494]
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Calgary Herald
|
||||||
|
- The Economist
|
||||||
|
- New Yorker
|
||||||
|
- Heise
|
||||||
|
- HNA
|
||||||
|
- ZDNet
|
||||||
|
- NRC Handelsblad
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "SPIN Magazine"
|
||||||
|
author: Quistopher
|
||||||
|
|
||||||
|
- title: "Caps n Babes"
|
||||||
|
author: skyhawker
|
||||||
|
|
||||||
|
- title: "Leduc"
|
||||||
|
author: Brian Hahn
|
||||||
|
|
||||||
|
- title: "David Bravo's Blog, La Nueva Espana, 20 Minutos and La Tribuna de Talavera"
|
||||||
|
author: Luis Hernandez
|
||||||
|
|
||||||
|
- title: "Sinfest"
|
||||||
|
author: nadid
|
||||||
|
|
||||||
|
- title: "Various Czech news sources"
|
||||||
|
author: FunThomas
|
||||||
|
|
||||||
|
- title: "tportal.h"
|
||||||
|
author: Darko Miletic
|
||||||
|
|
||||||
|
- title: "Everett Herald"
|
||||||
|
author: "77jag5"
|
||||||
|
|
||||||
|
- title: "Roger Ebert"
|
||||||
|
author: Shane Erstad
|
||||||
|
|
||||||
- version: 0.7.42
|
- version: 0.7.42
|
||||||
date: 2011-01-21
|
date: 2011-01-21
|
||||||
|
|
||||||
new features:
|
new features:
|
||||||
- title: "0.7.42 is a re-release of 0.7.41, because conversion to MOBI was broken in 0.7.41"
|
|
||||||
|
|
||||||
- title: "Conversions: Replace the remove header/footer options with a more geenric search replace option, that allows you to not only remove but also replace text"
|
- title: "Conversions: Replace the remove header/footer options with a more geenric search replace option, that allows you to not only remove but also replace text"
|
||||||
|
|
||||||
- title: "Conversion: The preprocess html option has now become a new 'Heuristic Processing' option which allows you to control exactly which heuristics are used"
|
- title: "Conversion: The preprocess html option has now become a new 'Heuristic Processing' option which allows you to control exactly which heuristics are used"
|
||||||
|
@ -26,7 +26,7 @@ class BBC(BasicNewsRecipe):
|
|||||||
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('Interviews', 'http://www.avclub.com/feed/interview/'),
|
('TV', 'http://www.avclub.com/feed/tv/'),
|
||||||
('AV Club Daily', 'http://www.avclub.com/feed/daily'),
|
('AV Club Daily', 'http://www.avclub.com/feed/daily'),
|
||||||
('Film', 'http://www.avclub.com/feed/film/'),
|
('Film', 'http://www.avclub.com/feed/film/'),
|
||||||
('Music', 'http://www.avclub.com/feed/music/'),
|
('Music', 'http://www.avclub.com/feed/music/'),
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.42'
|
__version__ = '0.7.43'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -19,7 +19,8 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
VENDOR_ID = {
|
VENDOR_ID = {
|
||||||
# HTC
|
# HTC
|
||||||
0x0bb4 : { 0x0c02 : [0x100, 0x0227, 0x0226], 0x0c01 : [0x100, 0x0227], 0x0ff9
|
0x0bb4 : { 0x0c02 : [0x100, 0x0227, 0x0226], 0x0c01 : [0x100,
|
||||||
|
0x0227, 0x0226], 0x0ff9
|
||||||
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
|
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
|
||||||
0xc92 : [0x100], 0xc97: [0x226], 0xc99 : [0x0100]},
|
0xc92 : [0x100], 0xc97: [0x226], 0xc99 : [0x0100]},
|
||||||
|
|
||||||
|
@ -141,8 +141,8 @@ class CoverManager(object):
|
|||||||
if width is None or height is None:
|
if width is None or height is None:
|
||||||
self.log.warning('Failed to read cover dimensions')
|
self.log.warning('Failed to read cover dimensions')
|
||||||
width, height = 600, 800
|
width, height = 600, 800
|
||||||
if self.preserve_aspect_ratio:
|
#if self.preserve_aspect_ratio:
|
||||||
width, height = 600, 800
|
# width, height = 600, 800
|
||||||
self.svg_template = self.svg_template.replace('__viewbox__',
|
self.svg_template = self.svg_template.replace('__viewbox__',
|
||||||
'0 0 %d %d'%(width, height))
|
'0 0 %d %d'%(width, height))
|
||||||
self.svg_template = self.svg_template.replace('__width__',
|
self.svg_template = self.svg_template.replace('__width__',
|
||||||
|
@ -47,7 +47,7 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
OptionRecommendation(name='preserve_cover_aspect_ratio',
|
OptionRecommendation(name='preserve_cover_aspect_ratio',
|
||||||
recommended_value=False,
|
recommended_value=False,
|
||||||
help=_('Preserve the aspect ratio of the cover, instead'
|
help=_('Preserve the aspect ratio of the cover, instead'
|
||||||
' of stretching it to fill the ull first page of the'
|
' of stretching it to fill the full first page of the'
|
||||||
' generated pdf.')
|
' generated pdf.')
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
@ -197,14 +197,10 @@ def error_dialog(parent, title, msg, det_msg='', show=False,
|
|||||||
return d.exec_()
|
return d.exec_()
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def question_dialog(parent, title, msg, det_msg='', show_copy_button=False,
|
def question_dialog(parent, title, msg, det_msg='', show_copy_button=False):
|
||||||
buttons=None, yes_button=None):
|
|
||||||
from calibre.gui2.dialogs.message_box import MessageBox
|
from calibre.gui2.dialogs.message_box import MessageBox
|
||||||
d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
|
d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
|
||||||
show_copy_button=show_copy_button)
|
show_copy_button=show_copy_button)
|
||||||
if buttons is not None:
|
|
||||||
d.bb.setStandardButtons(buttons)
|
|
||||||
|
|
||||||
return d.exec_() == d.Accepted
|
return d.exec_() == d.Accepted
|
||||||
|
|
||||||
def info_dialog(parent, title, msg, det_msg='', show=False,
|
def info_dialog(parent, title, msg, det_msg='', show=False,
|
||||||
|
@ -295,8 +295,12 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
return error_dialog(self.gui, _('Failed'),
|
return error_dialog(self.gui, _('Failed'),
|
||||||
_('Database integrity check failed, click Show details'
|
_('Database integrity check failed, click Show details'
|
||||||
' for details.'), show=True, det_msg=d.error[1])
|
' for details.'), show=True, det_msg=d.error[1])
|
||||||
|
|
||||||
d = CheckLibraryDialog(self.gui, m.db)
|
d = CheckLibraryDialog(self.gui, m.db)
|
||||||
d.exec_()
|
if not d.do_exec():
|
||||||
|
info_dialog(self.gui, _('No problems found'),
|
||||||
|
_('The files in your library match the information '
|
||||||
|
'in the database.'), show=True)
|
||||||
|
|
||||||
def switch_requested(self, location):
|
def switch_requested(self, location):
|
||||||
if not self.change_library_allowed():
|
if not self.change_library_allowed():
|
||||||
|
@ -7,7 +7,7 @@ import os, traceback, Queue, time, cStringIO, re, sys
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \
|
from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \
|
||||||
Qt, pyqtSignal, QDialog, QMessageBox
|
Qt, pyqtSignal, QDialog
|
||||||
|
|
||||||
from calibre.customize.ui import available_input_formats, available_output_formats, \
|
from calibre.customize.ui import available_input_formats, available_output_formats, \
|
||||||
device_plugins
|
device_plugins
|
||||||
@ -609,10 +609,8 @@ class DeviceMixin(object): # {{{
|
|||||||
autos = u'\n'.join(map(unicode, map(force_unicode, autos)))
|
autos = u'\n'.join(map(unicode, map(force_unicode, autos)))
|
||||||
return self.ask_a_yes_no_question(
|
return self.ask_a_yes_no_question(
|
||||||
_('No suitable formats'), msg,
|
_('No suitable formats'), msg,
|
||||||
buttons=QMessageBox.Yes|QMessageBox.Cancel,
|
|
||||||
ans_when_user_unavailable=True,
|
ans_when_user_unavailable=True,
|
||||||
det_msg=autos,
|
det_msg=autos
|
||||||
show_copy_button=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_default_thumbnail(self, height):
|
def set_default_thumbnail(self, height):
|
||||||
|
@ -139,7 +139,7 @@ class CheckLibraryDialog(QDialog):
|
|||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
self.setWindowTitle(_('Check Library'))
|
self.setWindowTitle(_('Check Library -- Problems Found'))
|
||||||
|
|
||||||
self._layout = QVBoxLayout(self)
|
self._layout = QVBoxLayout(self)
|
||||||
self.setLayout(self._layout)
|
self.setLayout(self._layout)
|
||||||
@ -148,7 +148,7 @@ class CheckLibraryDialog(QDialog):
|
|||||||
self.log.itemChanged.connect(self.item_changed)
|
self.log.itemChanged.connect(self.item_changed)
|
||||||
self._layout.addWidget(self.log)
|
self._layout.addWidget(self.log)
|
||||||
|
|
||||||
self.check_button = QPushButton(_('&Run the check'))
|
self.check_button = QPushButton(_('&Run the check again'))
|
||||||
self.check_button.setDefault(False)
|
self.check_button.setDefault(False)
|
||||||
self.check_button.clicked.connect(self.run_the_check)
|
self.check_button.clicked.connect(self.run_the_check)
|
||||||
self.copy_button = QPushButton(_('Copy &to clipboard'))
|
self.copy_button = QPushButton(_('Copy &to clipboard'))
|
||||||
@ -196,8 +196,17 @@ class CheckLibraryDialog(QDialog):
|
|||||||
self.resize(750, 500)
|
self.resize(750, 500)
|
||||||
self.bbox.setEnabled(True)
|
self.bbox.setEnabled(True)
|
||||||
|
|
||||||
|
def do_exec(self):
|
||||||
self.run_the_check()
|
self.run_the_check()
|
||||||
|
|
||||||
|
probs = 0
|
||||||
|
for c in self.problem_count:
|
||||||
|
probs += self.problem_count[c]
|
||||||
|
if probs == 0:
|
||||||
|
return False
|
||||||
|
self.exec_()
|
||||||
|
return True
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
self.db.prefs['check_library_ignore_extensions'] = \
|
self.db.prefs['check_library_ignore_extensions'] = \
|
||||||
unicode(self.ext_ignores.text())
|
unicode(self.ext_ignores.text())
|
||||||
@ -219,7 +228,10 @@ class CheckLibraryDialog(QDialog):
|
|||||||
attr, h, checkable, fixable = check
|
attr, h, checkable, fixable = check
|
||||||
list = getattr(checker, attr, None)
|
list = getattr(checker, attr, None)
|
||||||
if list is None:
|
if list is None:
|
||||||
|
self.problem_count[attr] = 0
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
self.problem_count[attr] = len(list)
|
||||||
|
|
||||||
tl = Item()
|
tl = Item()
|
||||||
tl.setText(0, h)
|
tl.setText(0, h)
|
||||||
@ -250,6 +262,7 @@ class CheckLibraryDialog(QDialog):
|
|||||||
t.setHeaderLabels([_('Name'), _('Path from library')])
|
t.setHeaderLabels([_('Name'), _('Path from library')])
|
||||||
self.all_items = []
|
self.all_items = []
|
||||||
self.top_level_items = {}
|
self.top_level_items = {}
|
||||||
|
self.problem_count = {}
|
||||||
for check in CHECKS:
|
for check in CHECKS:
|
||||||
builder(t, checker, check)
|
builder(t, checker, check)
|
||||||
|
|
||||||
|
@ -21,10 +21,10 @@ class Dialog(QDialog, Ui_Dialog):
|
|||||||
self.again.stateChanged.connect(self.toggle)
|
self.again.stateChanged.connect(self.toggle)
|
||||||
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
|
||||||
def toggle(self, *args):
|
def toggle(self, *args):
|
||||||
dynamic[_config_name(self.name)] = self.again.isChecked()
|
dynamic[_config_name(self.name)] = self.again.isChecked()
|
||||||
|
|
||||||
|
|
||||||
def confirm(msg, name, parent=None, pixmap='dialog_warning.png'):
|
def confirm(msg, name, parent=None, pixmap='dialog_warning.png'):
|
||||||
if not dynamic.get(_config_name(name), True):
|
if not dynamic.get(_config_name(name), True):
|
||||||
return True
|
return True
|
||||||
|
@ -92,7 +92,10 @@ class MessageBox(QDialog, Ui_Dialog):
|
|||||||
def showEvent(self, ev):
|
def showEvent(self, ev):
|
||||||
ret = QDialog.showEvent(self, ev)
|
ret = QDialog.showEvent(self, ev)
|
||||||
if self.is_question:
|
if self.is_question:
|
||||||
|
try:
|
||||||
self.bb.button(self.bb.Yes).setFocus(Qt.OtherFocusReason)
|
self.bb.button(self.bb.Yes).setFocus(Qt.OtherFocusReason)
|
||||||
|
except:
|
||||||
|
pass# Buttons were changed
|
||||||
else:
|
else:
|
||||||
self.bb.button(self.bb.Ok).setFocus(Qt.OtherFocusReason)
|
self.bb.button(self.bb.Ok).setFocus(Qt.OtherFocusReason)
|
||||||
return ret
|
return ret
|
||||||
|
@ -7,7 +7,7 @@ import re, os
|
|||||||
|
|
||||||
from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \
|
from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \
|
||||||
pyqtSignal, QDialogButtonBox, QInputDialog, QLineEdit, \
|
pyqtSignal, QDialogButtonBox, QInputDialog, QLineEdit, \
|
||||||
QMessageBox, QDate
|
QDate
|
||||||
|
|
||||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
@ -15,7 +15,8 @@ from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
|||||||
from calibre.ebooks.metadata.book.base import composite_formatter
|
from calibre.ebooks.metadata.book.base import composite_formatter
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||||
from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE, gprefs
|
from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE, \
|
||||||
|
gprefs, question_dialog
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.utils.config import dynamic, JSONConfig
|
from calibre.utils.config import dynamic, JSONConfig
|
||||||
from calibre.utils.titlecase import titlecase
|
from calibre.utils.titlecase import titlecase
|
||||||
@ -888,12 +889,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
if self.query_field.currentIndex() == 0:
|
if self.query_field.currentIndex() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
ret = QMessageBox.question(self, _("Delete saved search/replace"),
|
if not question_dialog(self, _("Delete saved search/replace"),
|
||||||
_("The selected saved search/replace will be deleted. "
|
_("The selected saved search/replace will be deleted. "
|
||||||
"Are you sure?"),
|
"Are you sure?")):
|
||||||
QMessageBox.Ok, QMessageBox.Cancel)
|
|
||||||
|
|
||||||
if ret == QMessageBox.Cancel:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
item_id = self.query_field.currentIndex()
|
item_id = self.query_field.currentIndex()
|
||||||
@ -917,11 +915,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
new = True
|
new = True
|
||||||
name = unicode(name)
|
name = unicode(name)
|
||||||
if name in self.queries.keys():
|
if name in self.queries.keys():
|
||||||
ret = QMessageBox.question(self, _("Save search/replace"),
|
if not question_dialog(self, _("Save search/replace"),
|
||||||
_("That saved search/replace already exists and will be overwritten. "
|
_("That saved search/replace already exists and will be overwritten. "
|
||||||
"Are you sure?"),
|
"Are you sure?")):
|
||||||
QMessageBox.Ok, QMessageBox.Cancel)
|
|
||||||
if ret == QMessageBox.Cancel:
|
|
||||||
return
|
return
|
||||||
new = False
|
new = False
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from functools import partial
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt4.Qt import SIGNAL, QObject, Qt, QTimer, QDate, \
|
from PyQt4.Qt import SIGNAL, QObject, Qt, QTimer, QDate, \
|
||||||
QPixmap, QListWidgetItem, QDialog, pyqtSignal, QMessageBox, QIcon, \
|
QPixmap, QListWidgetItem, QDialog, pyqtSignal, QIcon, \
|
||||||
QPushButton
|
QPushButton
|
||||||
|
|
||||||
from calibre.gui2 import error_dialog, file_icon_provider, dynamic, \
|
from calibre.gui2 import error_dialog, file_icon_provider, dynamic, \
|
||||||
@ -209,7 +209,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
title = unicode(self.title.text()).strip()
|
title = unicode(self.title.text()).strip()
|
||||||
author = unicode(self.authors.text()).strip()
|
author = unicode(self.authors.text()).strip()
|
||||||
if author.endswith('&'):
|
if author.endswith('&'):
|
||||||
author = author[:-1]
|
author = author[:-1].strip()
|
||||||
if not title or not author:
|
if not title or not author:
|
||||||
return error_dialog(self, _('Specify title and author'),
|
return error_dialog(self, _('Specify title and author'),
|
||||||
_('You must specify a title and author before generating '
|
_('You must specify a title and author before generating '
|
||||||
@ -770,9 +770,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
if question_dialog(self, _('Tags changed'),
|
if question_dialog(self, _('Tags changed'),
|
||||||
_('You have changed the tags. In order to use the tags'
|
_('You have changed the tags. In order to use the tags'
|
||||||
' editor, you must either discard or apply these '
|
' editor, you must either discard or apply these '
|
||||||
'changes'), show_copy_button=False,
|
'changes. Apply changes?'), show_copy_button=False):
|
||||||
buttons=QMessageBox.Apply|QMessageBox.Discard,
|
|
||||||
yes_button=QMessageBox.Apply):
|
|
||||||
self.apply_tags(commit=True, notify=True)
|
self.apply_tags(commit=True, notify=True)
|
||||||
self.original_tags = unicode(self.tags.text())
|
self.original_tags = unicode(self.tags.text())
|
||||||
else:
|
else:
|
||||||
|
@ -19,7 +19,7 @@ from PyQt4.Qt import QAbstractTableModel, QVariant, QModelIndex, Qt, \
|
|||||||
|
|
||||||
from calibre.utils.ipc.server import Server
|
from calibre.utils.ipc.server import Server
|
||||||
from calibre.utils.ipc.job import ParallelJob
|
from calibre.utils.ipc.job import ParallelJob
|
||||||
from calibre.gui2 import Dispatcher, error_dialog, NONE, config, gprefs
|
from calibre.gui2 import Dispatcher, error_dialog, question_dialog, NONE, config, gprefs
|
||||||
from calibre.gui2.device import DeviceJob
|
from calibre.gui2.device import DeviceJob
|
||||||
from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog
|
from calibre.gui2.dialogs.jobs_ui import Ui_JobsDialog
|
||||||
from calibre import __appname__
|
from calibre import __appname__
|
||||||
@ -380,8 +380,8 @@ class JobsDialog(QDialog, Ui_JobsDialog):
|
|||||||
self.model = model
|
self.model = model
|
||||||
self.setWindowModality(Qt.NonModal)
|
self.setWindowModality(Qt.NonModal)
|
||||||
self.setWindowTitle(__appname__ + _(' - Jobs'))
|
self.setWindowTitle(__appname__ + _(' - Jobs'))
|
||||||
self.kill_button.clicked.connect(self.kill_job)
|
|
||||||
self.details_button.clicked.connect(self.show_details)
|
self.details_button.clicked.connect(self.show_details)
|
||||||
|
self.kill_button.clicked.connect(self.kill_job)
|
||||||
self.stop_all_jobs_button.clicked.connect(self.kill_all_jobs)
|
self.stop_all_jobs_button.clicked.connect(self.kill_all_jobs)
|
||||||
self.pb_delegate = ProgressBarDelegate(self)
|
self.pb_delegate = ProgressBarDelegate(self)
|
||||||
self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate)
|
self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate)
|
||||||
@ -415,18 +415,19 @@ class JobsDialog(QDialog, Ui_JobsDialog):
|
|||||||
d.exec_()
|
d.exec_()
|
||||||
d.timer.stop()
|
d.timer.stop()
|
||||||
|
|
||||||
def kill_job(self, *args):
|
|
||||||
for index in self.jobs_view.selectedIndexes():
|
|
||||||
row = index.row()
|
|
||||||
self.model.kill_job(row, self)
|
|
||||||
return
|
|
||||||
|
|
||||||
def show_details(self, *args):
|
def show_details(self, *args):
|
||||||
for index in self.jobs_view.selectedIndexes():
|
for index in self.jobs_view.selectedIndexes():
|
||||||
self.show_job_details(index)
|
self.show_job_details(index)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def kill_job(self, *args):
|
||||||
|
if question_dialog(self, _('Are you sure?'), _('Do you really want to stop the selected job?')):
|
||||||
|
for index in self.jobs_view.selectedIndexes():
|
||||||
|
row = index.row()
|
||||||
|
self.model.kill_job(row, self)
|
||||||
|
|
||||||
def kill_all_jobs(self, *args):
|
def kill_all_jobs(self, *args):
|
||||||
|
if question_dialog(self, _('Are you sure?'), _('Do you really want to stop all non-device jobs?')):
|
||||||
self.model.kill_all_jobs()
|
self.model.kill_all_jobs()
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def closeEvent(self, e):
|
||||||
|
@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import sys, os, time, socket, traceback
|
import sys, os, time, socket, traceback
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QCoreApplication, QIcon, QMessageBox, QObject, QTimer, \
|
from PyQt4.Qt import QCoreApplication, QIcon, QObject, QTimer, \
|
||||||
QThread, pyqtSignal, Qt, QProgressDialog, QString, QPixmap, \
|
QThread, pyqtSignal, Qt, QProgressDialog, QString, QPixmap, \
|
||||||
QSplashScreen, QApplication
|
QSplashScreen, QApplication
|
||||||
|
|
||||||
@ -319,9 +319,6 @@ def run_gui(opts, args, actions, listener, app, gui_debug=None):
|
|||||||
|
|
||||||
def cant_start(msg=_('If you are sure it is not running')+', ',
|
def cant_start(msg=_('If you are sure it is not running')+', ',
|
||||||
what=None):
|
what=None):
|
||||||
d = QMessageBox(QMessageBox.Critical, _('Cannot Start ')+__appname__,
|
|
||||||
'<p>'+(_('%s is already running.')%__appname__)+'</p>',
|
|
||||||
QMessageBox.Ok)
|
|
||||||
base = '<p>%s</p><p>%s %s'
|
base = '<p>%s</p><p>%s %s'
|
||||||
where = __appname__ + ' '+_('may be running in the system tray, in the')+' '
|
where = __appname__ + ' '+_('may be running in the system tray, in the')+' '
|
||||||
if isosx:
|
if isosx:
|
||||||
@ -334,8 +331,10 @@ def cant_start(msg=_('If you are sure it is not running')+', ',
|
|||||||
else:
|
else:
|
||||||
what = _('try deleting the file')+': '+ADDRESS
|
what = _('try deleting the file')+': '+ADDRESS
|
||||||
|
|
||||||
d.setInformativeText(base%(where, msg, what))
|
info = base%(where, msg, what)
|
||||||
d.exec_()
|
error_dialog(None, _('Cannot Start ')+__appname__,
|
||||||
|
'<p>'+(_('%s is already running.')%__appname__)+'</p>'+info, show=True)
|
||||||
|
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
def communicate(args):
|
def communicate(args):
|
||||||
|
@ -10,7 +10,7 @@ import textwrap, re, os
|
|||||||
from PyQt4.Qt import Qt, QDateEdit, QDate, \
|
from PyQt4.Qt import Qt, QDateEdit, QDate, \
|
||||||
QIcon, QToolButton, QWidget, QLabel, QGridLayout, \
|
QIcon, QToolButton, QWidget, QLabel, QGridLayout, \
|
||||||
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, \
|
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, \
|
||||||
QPushButton, QSpinBox, QMessageBox, QLineEdit
|
QPushButton, QSpinBox, QLineEdit
|
||||||
|
|
||||||
from calibre.gui2.widgets import EnLineEdit, CompleteComboBox, \
|
from calibre.gui2.widgets import EnLineEdit, CompleteComboBox, \
|
||||||
EnComboBox, FormatList, ImageView, CompleteLineEdit
|
EnComboBox, FormatList, ImageView, CompleteLineEdit
|
||||||
@ -848,9 +848,7 @@ class TagsEdit(CompleteLineEdit): # {{{
|
|||||||
if question_dialog(self, _('Tags changed'),
|
if question_dialog(self, _('Tags changed'),
|
||||||
_('You have changed the tags. In order to use the tags'
|
_('You have changed the tags. In order to use the tags'
|
||||||
' editor, you must either discard or apply these '
|
' editor, you must either discard or apply these '
|
||||||
'changes'), show_copy_button=False,
|
'changes. Apply changes?'), show_copy_button=False):
|
||||||
buttons=QMessageBox.Apply|QMessageBox.Discard,
|
|
||||||
yes_button=QMessageBox.Apply):
|
|
||||||
self.commit(db, id_)
|
self.commit(db, id_)
|
||||||
db.commit()
|
db.commit()
|
||||||
self.original_val = self.current_val
|
self.original_val = self.current_val
|
||||||
|
@ -188,6 +188,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.plugin_view.PositionAtCenter)
|
self.plugin_view.PositionAtCenter)
|
||||||
self.plugin_view.scrollTo(idx,
|
self.plugin_view.scrollTo(idx,
|
||||||
self.plugin_view.PositionAtCenter)
|
self.plugin_view.PositionAtCenter)
|
||||||
|
self.plugin_view.selectionModel().select(idx,
|
||||||
|
self.plugin_view.selectionModel().ClearAndSelect)
|
||||||
|
self.plugin_view.setCurrentIndex(idx)
|
||||||
else:
|
else:
|
||||||
error_dialog(self, _('No valid plugin path'),
|
error_dialog(self, _('No valid plugin path'),
|
||||||
_('%s is not a valid plugin path')%path).exec_()
|
_('%s is not a valid plugin path')%path).exec_()
|
||||||
@ -220,10 +223,16 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
_('Plugin: %s does not need customization')%plugin.name).exec_()
|
_('Plugin: %s does not need customization')%plugin.name).exec_()
|
||||||
return
|
return
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
|
from calibre.customize import InterfaceActionBase
|
||||||
|
if isinstance(plugin, InterfaceActionBase) and not getattr(plugin,
|
||||||
|
'actual_iaction_plugin_loaded', False):
|
||||||
|
return error_dialog(self, _('Must restart'),
|
||||||
|
_('You must restart calibre before you can'
|
||||||
|
' configure the <b>%s</b> plugin')%plugin.name, show=True)
|
||||||
if plugin.do_user_config():
|
if plugin.do_user_config():
|
||||||
self._plugin_model.refresh_plugin(plugin)
|
self._plugin_model.refresh_plugin(plugin)
|
||||||
elif op == 'remove':
|
elif op == 'remove':
|
||||||
msg = _('Plugin {0} successfully removed').format(plugin.name)
|
msg = _('Plugin <b>{0}</b> successfully removed').format(plugin.name)
|
||||||
if remove_plugin(plugin):
|
if remove_plugin(plugin):
|
||||||
self._plugin_model.populate()
|
self._plugin_model.populate()
|
||||||
self._plugin_model.reset()
|
self._plugin_model.reset()
|
||||||
|
@ -12,11 +12,9 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import collections, os, sys, textwrap, time
|
import collections, os, sys, textwrap, time
|
||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from PyQt4.Qt import Qt, SIGNAL, QTimer, \
|
from PyQt4.Qt import Qt, SIGNAL, QTimer, QHelpEvent, QAction, \
|
||||||
QPixmap, QMenu, QIcon, pyqtSignal, \
|
QMenu, QIcon, pyqtSignal, \
|
||||||
QDialog, \
|
QDialog, QSystemTrayIcon, QApplication, QKeySequence
|
||||||
QSystemTrayIcon, QApplication, QKeySequence, \
|
|
||||||
QMessageBox, QHelpEvent, QAction
|
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import __appname__, isosx
|
from calibre.constants import __appname__, isosx
|
||||||
@ -101,28 +99,40 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.device_connected = None
|
self.device_connected = None
|
||||||
self.gui_debug = gui_debug
|
self.gui_debug = gui_debug
|
||||||
acmap = OrderedDict()
|
self.iactions = OrderedDict()
|
||||||
for action in interface_actions():
|
for action in interface_actions():
|
||||||
if opts.ignore_plugins and action.plugin_path is not None:
|
if opts.ignore_plugins and action.plugin_path is not None:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
ac = action.load_actual_plugin(self)
|
ac = self.init_iaction(action)
|
||||||
except:
|
except:
|
||||||
# Ignore errors in loading user supplied plugins
|
# Ignore errors in loading user supplied plugins
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
if ac.plugin_path is None:
|
if action.plugin_path is None:
|
||||||
raise
|
raise
|
||||||
|
continue
|
||||||
|
|
||||||
ac.plugin_path = action.plugin_path
|
ac.plugin_path = action.plugin_path
|
||||||
ac.interface_action_base_plugin = action
|
ac.interface_action_base_plugin = action
|
||||||
|
|
||||||
|
self.add_iaction(ac)
|
||||||
|
|
||||||
|
def init_iaction(self, action):
|
||||||
|
ac = action.load_actual_plugin(self)
|
||||||
|
ac.plugin_path = action.plugin_path
|
||||||
|
ac.interface_action_base_plugin = action
|
||||||
|
action.actual_iaction_plugin_loaded = True
|
||||||
|
return ac
|
||||||
|
|
||||||
|
def add_iaction(self, ac):
|
||||||
|
acmap = self.iactions
|
||||||
if ac.name in acmap:
|
if ac.name in acmap:
|
||||||
if ac.priority >= acmap[ac.name].priority:
|
if ac.priority >= acmap[ac.name].priority:
|
||||||
acmap[ac.name] = ac
|
acmap[ac.name] = ac
|
||||||
else:
|
else:
|
||||||
acmap[ac.name] = ac
|
acmap[ac.name] = ac
|
||||||
|
|
||||||
self.iactions = acmap
|
|
||||||
|
|
||||||
def initialize(self, library_path, db, listener, actions, show_gui=True):
|
def initialize(self, library_path, db, listener, actions, show_gui=True):
|
||||||
opts = self.opts
|
opts = self.opts
|
||||||
@ -345,11 +355,12 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
def is_minimized_to_tray(self):
|
def is_minimized_to_tray(self):
|
||||||
return getattr(self, '__systray_minimized', False)
|
return getattr(self, '__systray_minimized', False)
|
||||||
|
|
||||||
def ask_a_yes_no_question(self, title, msg, **kwargs):
|
def ask_a_yes_no_question(self, title, msg, det_msg='',
|
||||||
awu = kwargs.pop('ans_when_user_unavailable', True)
|
show_copy_button=False, ans_when_user_unavailable=True):
|
||||||
if self.is_minimized_to_tray:
|
if self.is_minimized_to_tray:
|
||||||
return awu
|
return ans_when_user_unavailable
|
||||||
return question_dialog(self, title, msg, **kwargs)
|
return question_dialog(self, title, msg, det_msg=det_msg,
|
||||||
|
show_copy_button=show_copy_button)
|
||||||
|
|
||||||
def hide_windows(self):
|
def hide_windows(self):
|
||||||
for window in QApplication.topLevelWidgets():
|
for window in QApplication.topLevelWidgets():
|
||||||
@ -589,11 +600,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
Quitting may cause corruption on the device.<br>
|
Quitting may cause corruption on the device.<br>
|
||||||
Are you sure you want to quit?''')+'</p>'
|
Are you sure you want to quit?''')+'</p>'
|
||||||
|
|
||||||
d = QMessageBox(QMessageBox.Warning, _('WARNING: Active jobs'), msg,
|
if not question_dialog(self, _('Active jobs'), msg):
|
||||||
QMessageBox.Yes|QMessageBox.No, self)
|
|
||||||
d.setIconPixmap(QPixmap(I('dialog_warning.png')))
|
|
||||||
d.setDefaultButton(QMessageBox.No)
|
|
||||||
if d.exec_() != QMessageBox.Yes:
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -53,8 +53,10 @@ class CSV_XML(CatalogPlugin): # {{{
|
|||||||
'database. Should be a comma-separated list of fields.\n'
|
'database. Should be a comma-separated list of fields.\n'
|
||||||
'Available fields: %s,\n'
|
'Available fields: %s,\n'
|
||||||
'plus user-created custom fields.\n'
|
'plus user-created custom fields.\n'
|
||||||
|
'Example: %s=title,authors,tags\n'
|
||||||
"Default: '%%default'\n"
|
"Default: '%%default'\n"
|
||||||
"Applies to: CSV, XML output formats")%', '.join(FIELDS)),
|
"Applies to: CSV, XML output formats")%(', '.join(FIELDS),
|
||||||
|
'--fields')),
|
||||||
|
|
||||||
Option('--sort-by',
|
Option('--sort-by',
|
||||||
default = 'id',
|
default = 'id',
|
||||||
@ -230,8 +232,10 @@ class BIBTEX(CatalogPlugin): # {{{
|
|||||||
help = _('The fields to output when cataloging books in the '
|
help = _('The fields to output when cataloging books in the '
|
||||||
'database. Should be a comma-separated list of fields.\n'
|
'database. Should be a comma-separated list of fields.\n'
|
||||||
'Available fields: %s.\n'
|
'Available fields: %s.\n'
|
||||||
|
'Example: %s=title,authors,tags\n'
|
||||||
"Default: '%%default'\n"
|
"Default: '%%default'\n"
|
||||||
"Applies to: BIBTEX output format")%', '.join(FIELDS)),
|
"Applies to: BIBTEX output format")%(', '.join(FIELDS),
|
||||||
|
'--fields')),
|
||||||
|
|
||||||
Option('--sort-by',
|
Option('--sort-by',
|
||||||
default = 'id',
|
default = 'id',
|
||||||
|
@ -107,6 +107,7 @@ My device is not being detected by |app|?
|
|||||||
Follow these steps to find the problem:
|
Follow these steps to find the problem:
|
||||||
|
|
||||||
* Make sure that you are connecting only a single device to your computer at a time. Do not have another |app| supported device like an iPhone/iPad etc. at the same time.
|
* Make sure that you are connecting only a single device to your computer at a time. Do not have another |app| supported device like an iPhone/iPad etc. at the same time.
|
||||||
|
* If you are connecting an Apple iDevice (iPad, iPod Touch, iPhone), use the 'Connect to iTunes' method in the 'Getting started' instructions in `Calibre + Apple iDevices: Start here <http://www.mobileread.com/forums/showthread.php?t=118559>`_.
|
||||||
* Make sure you are running the latest version of |app|. The latest version can always be downloaded from `the calibre website <http://calibre-ebook.com/download>`_.
|
* Make sure you are running the latest version of |app|. The latest version can always be downloaded from `the calibre website <http://calibre-ebook.com/download>`_.
|
||||||
* Ensure your operating system is seeing the device. That is, the device should be mounted as a disk that you can access using Windows explorer or whatever the file management program on your computer is.
|
* Ensure your operating system is seeing the device. That is, the device should be mounted as a disk that you can access using Windows explorer or whatever the file management program on your computer is.
|
||||||
* In calibre, go to Preferences->Plugins->Device Interface plugin and make sure the plugin for your device is enabled, the plugin icon next to it should be green when it is enabled.
|
* In calibre, go to Preferences->Plugins->Device Interface plugin and make sure the plugin for your device is enabled, the plugin icon next to it should be green when it is enabled.
|
||||||
@ -224,13 +225,12 @@ Replace ``192.168.1.2`` with the local IP address of the computer running |app|.
|
|||||||
You wills ee a list of books in Safari, just click on the epub link for whichever book you want to read, Safari will then prompt you to open it with iBooks.
|
You wills ee a list of books in Safari, just click on the epub link for whichever book you want to read, Safari will then prompt you to open it with iBooks.
|
||||||
|
|
||||||
|
|
||||||
With the USB cable
|
With the USB cable + iTunes
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
As of |app| version 0.7.0, you can plug your iDevice into the computer using its charging cable, and |app| will detect it and show you a list of books on the device. You can then use the *Send to device button* to send books directly to iBooks on the device. Note that you must have at least iOS 4 installed on your iPhone/iTouch for this to work.
|
Use the 'Connect to iTunes' method in the 'Getting started' instructions in `Calibre + Apple iDevices: Start here <http://www.mobileread.com/forums/showthread.php?t=118559>`_.
|
||||||
|
|
||||||
This method only works on Windows XP and higher and OS X 10.5 and higher. Linux is not supported (iTunes is not available in linux) and OS X 10.4 is not supported.
|
This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux is not supported (iTunes is not available in linux) and OS X 10.4 is not supported.
|
||||||
For more details on how this works, see `this forum post <http://www.mobileread.com/forums/showpost.php?p=944079&postcount=1>`_.
|
|
||||||
|
|
||||||
How do I use |app| with my Android phone?
|
How do I use |app| with my Android phone?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user