mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
merge from trunk
This commit is contained in:
commit
052b0044e2
@ -48,7 +48,7 @@ class LeMonde(BasicNewsRecipe):
|
||||
if alink.string is not None:
|
||||
tstr = alink.string
|
||||
alink.replaceWith(tstr)
|
||||
return soup
|
||||
return self.adeify_images(soup)
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'([0-9])%'), lambda m: m.group(1) + ' %'),
|
||||
|
@ -118,6 +118,7 @@ sort_columns_at_startup = None
|
||||
# timestamp default if not set: dd MMM yyyy
|
||||
gui_pubdate_display_format = 'MMM yyyy'
|
||||
gui_timestamp_display_format = 'dd MMM yyyy'
|
||||
gui_last_modified_display_format = 'dd MMM yyyy'
|
||||
|
||||
#: Control sorting of titles and series in the library display
|
||||
# Control title and series sorting in the library view. If set to
|
||||
|
@ -69,7 +69,24 @@ nmake -f ms\ntdll.mak install
|
||||
Qt
|
||||
--------
|
||||
|
||||
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
||||
Extract Qt sourcecode to C:\Qt\4.x.x.
|
||||
|
||||
Qt uses its own routine to locate and load "system libraries" including the openssl libraries needed for "Get Books". This means that we have to apply the following patch to have Qt load the openssl libraries bundled with calibre:
|
||||
|
||||
|
||||
--- src/corelib/plugin/qsystemlibrary.cpp 2011-02-22 05:04:00.000000000 -0700
|
||||
+++ src/corelib/plugin/qsystemlibrary.cpp 2011-04-25 20:53:13.635247466 -0600
|
||||
@@ -110,7 +110,7 @@ HINSTANCE QSystemLibrary::load(const wch
|
||||
|
||||
#if !defined(QT_BOOTSTRAPPED)
|
||||
if (!onlySystemDirectory)
|
||||
- searchOrder << QFileInfo(qAppFileName()).path();
|
||||
+ searchOrder << (QFileInfo(qAppFileName()).path().replace(QLatin1Char('/'), QLatin1Char('\\')) + QString::fromLatin1("\\DLLs\\"));
|
||||
#endif
|
||||
searchOrder << qSystemDirectory();
|
||||
|
||||
|
||||
Now, run configure and make::
|
||||
|
||||
configure -opensource -release -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
|
||||
|
||||
|
@ -11,7 +11,10 @@
|
||||
SummaryCodepage='1252' />
|
||||
|
||||
<Media Id="1" Cabinet="{app}.cab" CompressionLevel="{compression}" EmbedCab="yes" />
|
||||
|
||||
<!-- The following line is needed because of the patch to QtCore4.dll. You can remove this line
|
||||
after you update Qt beyond 4.7.2. 'emus' means re-install even if version is the same not just if it is older. -->
|
||||
<Property Id='REINSTALLMODE' Value='emus'/>
|
||||
|
||||
<Upgrade Id="{upgrade_code}">
|
||||
<UpgradeVersion Maximum="{version}"
|
||||
IncludeMaximum="yes"
|
||||
|
@ -347,9 +347,10 @@ class UploadUserManual(Command): # {{{
|
||||
with NamedTemporaryFile(suffix='.zip') as f:
|
||||
os.fchmod(f.fileno(),
|
||||
stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH|stat.S_IWRITE)
|
||||
with CurrentDir(self.d(path)):
|
||||
with CurrentDir(path):
|
||||
with ZipFile(f, 'w') as zf:
|
||||
for x in os.listdir('.'):
|
||||
if x.endswith('.swp'): continue
|
||||
zf.write(x)
|
||||
if os.path.isdir(x):
|
||||
for y in os.listdir(x):
|
||||
|
@ -388,7 +388,11 @@ class CurrentDir(object):
|
||||
return self.cwd
|
||||
|
||||
def __exit__(self, *args):
|
||||
os.chdir(self.cwd)
|
||||
try:
|
||||
os.chdir(self.cwd)
|
||||
except:
|
||||
# The previous CWD no longer exists
|
||||
pass
|
||||
|
||||
|
||||
class StreamReadWrapper(object):
|
||||
|
@ -460,7 +460,7 @@ class ITUNES(DriverBase):
|
||||
|
||||
cached_books[this_book.path] = {
|
||||
'title':book.Name,
|
||||
'author':book.artist().split(' & '),
|
||||
'author':book.Artist.split(' & '),
|
||||
'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
|
||||
'uuid': book.Composer,
|
||||
'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
|
||||
|
@ -402,7 +402,7 @@ class HTMLPreProcessor(object):
|
||||
(re.compile(r'((?<=</a>)\s*file:/{2,4}[A-Z].*<br>|file:////?[A-Z].*<br>(?=\s*<hr>))', re.IGNORECASE), lambda match: ''),
|
||||
|
||||
# Center separator lines
|
||||
(re.compile(u'<br>\s*(?P<break>([*#•✦=]+\s*)+)\s*<br>'), lambda match: '<p>\n<p style="text-align:center">' + match.group(1) + '</p>'),
|
||||
(re.compile(u'<br>\s*(?P<break>([*#•✦=] *){3,})\s*<br>'), lambda match: '<p>\n<p style="text-align:center">' + match.group('break') + '</p>'),
|
||||
|
||||
# Remove page links
|
||||
(re.compile(r'<a name=\d+></a>', re.IGNORECASE), lambda match: ''),
|
||||
|
@ -120,7 +120,11 @@ class Metadata(object):
|
||||
_('TEMPLATE ERROR'),
|
||||
self).strip()
|
||||
return val
|
||||
|
||||
if field.startswith('#') and field.endswith('_index'):
|
||||
try:
|
||||
return self.get_extra(field[:-6])
|
||||
except:
|
||||
pass
|
||||
raise AttributeError(
|
||||
'Metadata object has no attribute named: '+ repr(field))
|
||||
|
||||
@ -170,11 +174,6 @@ class Metadata(object):
|
||||
try:
|
||||
return self.__getattribute__(field)
|
||||
except AttributeError:
|
||||
if field.startswith('#') and field.endswith('_index'):
|
||||
try:
|
||||
return self.get_extra(field[:-6])
|
||||
except:
|
||||
pass
|
||||
return default
|
||||
|
||||
def get_extra(self, field, default=None):
|
||||
@ -631,7 +630,7 @@ class Metadata(object):
|
||||
res = format_date(res, fmeta['display'].get('date_format','dd MMM yyyy'))
|
||||
elif datatype == 'rating':
|
||||
res = res/2.0
|
||||
elif key in ('book_size', 'size'):
|
||||
elif key == 'size':
|
||||
res = human_readable(res)
|
||||
return (name, unicode(res), orig_res, fmeta)
|
||||
|
||||
|
@ -400,7 +400,8 @@ class MetadataUpdater(object):
|
||||
if getattr(self, 'exth', None) is None:
|
||||
raise MobiError('No existing EXTH record. Cannot update metadata.')
|
||||
|
||||
self.record0[92:96] = iana2mobi(mi.language)
|
||||
if not mi.is_null('language'):
|
||||
self.record0[92:96] = iana2mobi(mi.language)
|
||||
self.create_exth(exth=exth, new_title=mi.title)
|
||||
|
||||
# Fetch updated timestamp, cover_record, thumbnail_record
|
||||
|
@ -301,7 +301,7 @@ class Amazon(Source):
|
||||
if asin is None:
|
||||
asin = identifiers.get('asin', None)
|
||||
if asin:
|
||||
return 'http://amzn.com/%s'%asin
|
||||
return ('amazon', asin, 'http://amzn.com/%s'%asin)
|
||||
# }}}
|
||||
|
||||
def create_query(self, log, title=None, authors=None, identifiers={}): # {{{
|
||||
|
@ -56,7 +56,8 @@ class InternalMetadataCompareKeyGen(object):
|
||||
|
||||
'''
|
||||
Generate a sort key for comparison of the relevance of Metadata objects,
|
||||
given a search query.
|
||||
given a search query. This is used only to compare results from the same
|
||||
metadata source, not across different sources.
|
||||
|
||||
The sort key ensures that an ascending order sort is a sort by order of
|
||||
decreasing relevance.
|
||||
@ -374,7 +375,11 @@ class Source(Plugin):
|
||||
|
||||
def get_book_url(self, identifiers):
|
||||
'''
|
||||
Return the URL for the book identified by identifiers at this source.
|
||||
Return a 3-tuple or None. The 3-tuple is of the form:
|
||||
(identifier_type, identifier_value, URL).
|
||||
The URL is the URL for the book identified by identifiers at this
|
||||
source. identifier_type, identifier_value specify the identifier
|
||||
corresponding to the URL.
|
||||
This URL must be browseable to by a human using a browser. It is meant
|
||||
to provide a clickable link for the user to easily visit the books page
|
||||
at this source.
|
||||
|
@ -173,7 +173,7 @@ class GoogleBooks(Source):
|
||||
def get_book_url(self, identifiers): # {{{
|
||||
goog = identifiers.get('google', None)
|
||||
if goog is not None:
|
||||
return 'http://books.google.com/books?id=%s'%goog
|
||||
return ('google', goog, 'http://books.google.com/books?id=%s'%goog)
|
||||
# }}}
|
||||
|
||||
def create_query(self, log, title=None, authors=None, identifiers={}): # {{{
|
||||
|
@ -435,18 +435,30 @@ def identify(log, abort, # {{{
|
||||
# }}}
|
||||
|
||||
def urls_from_identifiers(identifiers): # {{{
|
||||
identifiers = dict([(k.lower(), v) for k, v in identifiers.iteritems()])
|
||||
ans = []
|
||||
for plugin in all_metadata_plugins():
|
||||
try:
|
||||
url = plugin.get_book_url(identifiers)
|
||||
if url is not None:
|
||||
ans.append((plugin.name, url))
|
||||
id_type, id_val, url = plugin.get_book_url(identifiers)
|
||||
ans.append((plugin.name, id_type, id_val, url))
|
||||
except:
|
||||
pass
|
||||
isbn = identifiers.get('isbn', None)
|
||||
if isbn:
|
||||
ans.append((isbn,
|
||||
'http://www.worldcat.org/search?q=bn%%3A%s&qt=advanced'%isbn))
|
||||
ans.append((isbn, 'isbn', isbn,
|
||||
'http://www.worldcat.org/isbn/'+isbn))
|
||||
doi = identifiers.get('doi', None)
|
||||
if doi:
|
||||
ans.append(('DOI', 'doi', doi,
|
||||
'http://dx.doi.org/'+doi))
|
||||
arxiv = identifiers.get('arxiv', None)
|
||||
if arxiv:
|
||||
ans.append(('arXiv', 'arxiv', arxiv,
|
||||
'http://arxiv.org/abs/'+arxiv))
|
||||
oclc = identifiers.get('oclc', None)
|
||||
if oclc:
|
||||
ans.append(('OCLC', 'oclc', oclc,
|
||||
'http://www.worldcat.org/oclc/'+oclc))
|
||||
return ans
|
||||
# }}}
|
||||
|
||||
|
@ -272,7 +272,7 @@ class OverDrive(Source):
|
||||
creators = creators.split(', ')
|
||||
|
||||
# if an exact match in a preferred format occurs
|
||||
if ((author and creators[0] == author[0]) or (not author and not creators)) and od_title.lower() == title.lower() and int(formatid) in [1, 50, 410, 900] and thumbimage:
|
||||
if ((author and creators and creators[0] == author[0]) or (not author and not creators)) and od_title.lower() == title.lower() and int(formatid) in [1, 50, 410, 900] and thumbimage:
|
||||
return self.format_results(reserveid, od_title, subtitle, series, publisher,
|
||||
creators, thumbimage, worldcatlink, formatid)
|
||||
else:
|
||||
@ -298,7 +298,7 @@ class OverDrive(Source):
|
||||
close_matches.insert(0, self.format_results(reserveid, od_title, subtitle, series, publisher, creators, thumbimage, worldcatlink, formatid))
|
||||
else:
|
||||
close_matches.append(self.format_results(reserveid, od_title, subtitle, series, publisher, creators, thumbimage, worldcatlink, formatid))
|
||||
|
||||
|
||||
elif close_title_match and close_author_match and int(formatid) in [1, 50, 410, 900]:
|
||||
close_matches.append(self.format_results(reserveid, od_title, subtitle, series, publisher, creators, thumbimage, worldcatlink, formatid))
|
||||
|
||||
|
@ -222,7 +222,7 @@ class SaveWorker(Thread):
|
||||
if isbytestring(fpath):
|
||||
fpath = fpath.decode(filesystem_encoding)
|
||||
formats[fmt.lower()] = fpath
|
||||
data[i] = [opf, cpath, formats]
|
||||
data[i] = [opf, cpath, formats, mi.last_modified.isoformat()]
|
||||
return data
|
||||
|
||||
def run(self):
|
||||
|
@ -16,6 +16,7 @@ from calibre import CurrentDir
|
||||
from calibre.ebooks.pdb.formatreader import FormatReader
|
||||
from calibre.ptempfile import TemporaryFile
|
||||
from calibre.utils.magick import Image, create_canvas
|
||||
from calibre.ebooks.compression.palmdoc import decompress_doc
|
||||
|
||||
DATATYPE_PHTML = 0
|
||||
DATATYPE_PHTML_COMPRESSED = 1
|
||||
|
@ -739,12 +739,6 @@ def build_forms(srcdir, info=None):
|
||||
dat = dat.replace('from QtWebKit.QWebView import QWebView',
|
||||
'from PyQt4 import QtWebKit\nfrom PyQt4.QtWebKit import QWebView')
|
||||
|
||||
if form.endswith('viewer%smain.ui'%os.sep):
|
||||
info('\t\tPromoting WebView')
|
||||
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
|
||||
dat = dat.replace('self.view = QWebView(', 'self.view = DocumentView(')
|
||||
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
|
||||
|
||||
open(compiled_form, 'wb').write(dat)
|
||||
|
||||
_df = os.environ.get('CALIBRE_DEVELOP_FROM', None)
|
||||
|
@ -17,13 +17,13 @@ from calibre.gui2.dnd import (dnd_has_image, dnd_get_image, dnd_get_files,
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.ebooks.metadata.book.base import (field_metadata, Metadata)
|
||||
from calibre.ebooks.metadata import fmt_sidx
|
||||
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
|
||||
from calibre.constants import filesystem_encoding
|
||||
from calibre.library.comments import comments_to_html
|
||||
from calibre.gui2 import (config, open_local_file, open_url, pixmap_to_data,
|
||||
gprefs)
|
||||
from calibre.utils.icu import sort_key
|
||||
|
||||
|
||||
def render_html(mi, css, vertical, widget, all_fields=False): # {{{
|
||||
table = render_data(mi, all_fields=all_fields,
|
||||
use_roman_numbers=config['use_roman_numerals_for_series_number'])
|
||||
@ -114,17 +114,20 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
|
||||
u', '.join(fmts))))
|
||||
elif field == 'identifiers':
|
||||
pass # TODO
|
||||
urls = urls_from_identifiers(mi.identifiers)
|
||||
links = [u'<a href="%s" title="%s:%s">%s</a>' % (url, id_typ, id_val, name)
|
||||
for name, id_typ, id_val, url in urls]
|
||||
links = u', '.join(links)
|
||||
if links:
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(
|
||||
_('Ids')+':', links)))
|
||||
else:
|
||||
val = mi.format_field(field)[-1]
|
||||
if val is None:
|
||||
continue
|
||||
val = prepare_string_for_xml(val)
|
||||
if metadata['datatype'] == 'series':
|
||||
if metadata['is_custom']:
|
||||
sidx = mi.get_extra(field)
|
||||
else:
|
||||
sidx = getattr(mi, field+'_index')
|
||||
sidx = mi.get(field+'_index')
|
||||
if sidx is None:
|
||||
sidx = 1.0
|
||||
val = _('Book %s of <span class="series_name">%s</span>')%(fmt_sidx(sidx,
|
||||
@ -292,6 +295,8 @@ class BookInfo(QWebView):
|
||||
|
||||
def link_activated(self, link):
|
||||
self._link_clicked = True
|
||||
if unicode(link.scheme()) in ('http', 'https'):
|
||||
return open_url(link)
|
||||
link = unicode(link.toString())
|
||||
self.link_clicked.emit(link)
|
||||
|
||||
|
@ -22,6 +22,12 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../../../resources/images.qrc">:/images/dialog_warning.png</pixmap>
|
||||
</property>
|
||||
@ -46,6 +52,10 @@
|
||||
<property name="text">
|
||||
<string>Library</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/library.png</normaloff>:/images/library.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -53,6 +63,10 @@
|
||||
<property name="text">
|
||||
<string>Device</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/reader.png</normaloff>:/images/reader.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -60,6 +74,10 @@
|
||||
<property name="text">
|
||||
<string>Library and Device</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -214,7 +214,6 @@ class SearchBar(QWidget): # {{{
|
||||
x.setIcon(QIcon(I("search_add_saved.png")))
|
||||
x.setObjectName("save_search_button")
|
||||
l.addWidget(x)
|
||||
x.setToolTip(_("Save current search under the name shown in the box"))
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -97,18 +97,25 @@ class RatingDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
class DateDelegate(QStyledItemDelegate): # {{{
|
||||
|
||||
def __init__(self, parent, tweak_name='gui_timestamp_display_format',
|
||||
default_format='dd MMM yyyy', editor_format='dd MMM yyyy'):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
self.tweak_name = tweak_name
|
||||
self.default_format = default_format
|
||||
self.editor_format = editor_format
|
||||
|
||||
def displayText(self, val, locale):
|
||||
d = val.toDate()
|
||||
if d <= UNDEFINED_QDATE:
|
||||
return ''
|
||||
format = tweaks['gui_timestamp_display_format']
|
||||
format = tweaks[self.tweak_name]
|
||||
if format is None:
|
||||
format = 'dd MMM yyyy'
|
||||
format = self.default_format
|
||||
return format_date(d.toPyDate(), format)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
qde = QStyledItemDelegate.createEditor(self, parent, option, index)
|
||||
qde.setDisplayFormat('dd MMM yyyy')
|
||||
qde.setDisplayFormat(self.editor_format)
|
||||
qde.setMinimumDate(UNDEFINED_QDATE)
|
||||
qde.setSpecialValueText(_('Undefined'))
|
||||
qde.setCalendarPopup(True)
|
||||
|
@ -70,6 +70,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
'publisher' : _("Publisher"),
|
||||
'tags' : _("Tags"),
|
||||
'series' : _("Series"),
|
||||
'last_modified' : _('Modified'),
|
||||
}
|
||||
|
||||
def __init__(self, parent=None, buffer=40):
|
||||
@ -620,6 +621,8 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
idx=self.db.field_metadata['timestamp']['rec_index']),
|
||||
'pubdate' : functools.partial(datetime_type,
|
||||
idx=self.db.field_metadata['pubdate']['rec_index']),
|
||||
'last_modified': functools.partial(datetime_type,
|
||||
idx=self.db.field_metadata['last_modified']['rec_index']),
|
||||
'rating' : functools.partial(rating_type,
|
||||
idx=self.db.field_metadata['rating']['rec_index']),
|
||||
'publisher': functools.partial(text_type,
|
||||
|
@ -76,6 +76,8 @@ class BooksView(QTableView): # {{{
|
||||
self.rating_delegate = RatingDelegate(self)
|
||||
self.timestamp_delegate = DateDelegate(self)
|
||||
self.pubdate_delegate = PubDateDelegate(self)
|
||||
self.last_modified_delegate = DateDelegate(self,
|
||||
tweak_name='gui_last_modified_display_format')
|
||||
self.tags_delegate = CompleteDelegate(self, ',', 'all_tags')
|
||||
self.authors_delegate = CompleteDelegate(self, '&', 'all_author_names', True)
|
||||
self.cc_names_delegate = CompleteDelegate(self, '&', 'all_custom', True)
|
||||
@ -296,6 +298,7 @@ class BooksView(QTableView): # {{{
|
||||
state = {}
|
||||
state['hidden_columns'] = [cm[i] for i in range(h.count())
|
||||
if h.isSectionHidden(i) and cm[i] != 'ondevice']
|
||||
state['last_modified_injected'] = True
|
||||
state['sort_history'] = \
|
||||
self.cleanup_sort_history(self.model().sort_history)
|
||||
state['column_positions'] = {}
|
||||
@ -380,7 +383,7 @@ class BooksView(QTableView): # {{{
|
||||
|
||||
def get_default_state(self):
|
||||
old_state = {
|
||||
'hidden_columns': [],
|
||||
'hidden_columns': ['last_modified'],
|
||||
'sort_history':[DEFAULT_SORT],
|
||||
'column_positions': {},
|
||||
'column_sizes': {},
|
||||
@ -388,6 +391,7 @@ class BooksView(QTableView): # {{{
|
||||
'size':'center',
|
||||
'timestamp':'center',
|
||||
'pubdate':'center'},
|
||||
'last_modified_injected': True,
|
||||
}
|
||||
h = self.column_header
|
||||
cm = self.column_map
|
||||
@ -398,7 +402,7 @@ class BooksView(QTableView): # {{{
|
||||
old_state['column_sizes'][name] = \
|
||||
min(350, max(self.sizeHintForColumn(i),
|
||||
h.sectionSizeHint(i)))
|
||||
if name == 'timestamp':
|
||||
if name in ('timestamp', 'last_modified'):
|
||||
old_state['column_sizes'][name] += 12
|
||||
return old_state
|
||||
|
||||
@ -418,6 +422,13 @@ class BooksView(QTableView): # {{{
|
||||
pass
|
||||
if ans is not None:
|
||||
db.prefs[name] = ans
|
||||
else:
|
||||
if not ans.get('last_modified_injected', False):
|
||||
ans['last_modified_injected'] = True
|
||||
hc = ans.get('hidden_columns', [])
|
||||
if 'last_modified' not in hc:
|
||||
hc.append('last_modified')
|
||||
db.prefs[name] = ans
|
||||
return ans
|
||||
|
||||
|
||||
@ -459,7 +470,8 @@ class BooksView(QTableView): # {{{
|
||||
def database_changed(self, db):
|
||||
for i in range(self.model().columnCount(None)):
|
||||
if self.itemDelegateForColumn(i) in (self.rating_delegate,
|
||||
self.timestamp_delegate, self.pubdate_delegate):
|
||||
self.timestamp_delegate, self.pubdate_delegate,
|
||||
self.last_modified_delegate):
|
||||
self.setItemDelegateForColumn(i, self.itemDelegate())
|
||||
|
||||
cm = self.column_map
|
||||
|
@ -18,11 +18,11 @@ from calibre.gui2.widgets import EnLineEdit, FormatList, ImageView
|
||||
from calibre.gui2.complete import MultiCompleteLineEdit, MultiCompleteComboBox
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.config import tweaks, prefs
|
||||
from calibre.ebooks.metadata import title_sort, authors_to_string, \
|
||||
string_to_authors, check_isbn
|
||||
from calibre.ebooks.metadata import (title_sort, authors_to_string,
|
||||
string_to_authors, check_isbn)
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.gui2 import file_icon_provider, UNDEFINED_QDATE, UNDEFINED_DATE, \
|
||||
choose_files, error_dialog, choose_images, question_dialog
|
||||
from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATE, UNDEFINED_DATE,
|
||||
choose_files, error_dialog, choose_images, question_dialog)
|
||||
from calibre.utils.date import local_tz, qt_to_dt
|
||||
from calibre import strftime
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
@ -280,11 +280,16 @@ class AuthorSortEdit(EnLineEdit):
|
||||
aus = self.current_val
|
||||
meth = tweaks['author_sort_copy_method']
|
||||
if aus:
|
||||
ln, _, rest = aus.partition(',')
|
||||
if rest:
|
||||
if meth in ('invert', 'nocomma', 'comma'):
|
||||
aus = rest.strip() + ' ' + ln.strip()
|
||||
self.authors_edit.current_val = [aus]
|
||||
ans = []
|
||||
for one in [a.strip() for a in aus.split('&')]:
|
||||
if not one:
|
||||
continue
|
||||
ln, _, rest = one.partition(',')
|
||||
if rest:
|
||||
if meth in ('invert', 'nocomma', 'comma'):
|
||||
one = rest.strip() + ' ' + ln.strip()
|
||||
ans.append(one)
|
||||
self.authors_edit.current_val = ans
|
||||
|
||||
def auto_generate(self, *args):
|
||||
au = unicode(self.authors_edit.text())
|
||||
@ -805,6 +810,7 @@ class CommentsEdit(Editor): # {{{
|
||||
else:
|
||||
val = comments_to_html(val)
|
||||
self.html = val
|
||||
self.wyswyg_dirtied()
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
def initialize(self, db, id_):
|
||||
@ -936,7 +942,11 @@ class IdentifiersEdit(QLineEdit): # {{{
|
||||
ans = {}
|
||||
for x in parts:
|
||||
c = x.split(':')
|
||||
if len(c) == 2:
|
||||
if len(c) > 1:
|
||||
if c[0] == 'isbn':
|
||||
v = check_isbn(c[1])
|
||||
if v is not None:
|
||||
c[1] = v
|
||||
ans[c[0]] = c[1]
|
||||
return ans
|
||||
def fset(self, val):
|
||||
@ -947,6 +957,11 @@ class IdentifiersEdit(QLineEdit): # {{{
|
||||
if x == 'isbn':
|
||||
x = '00isbn'
|
||||
return x
|
||||
for k in list(val):
|
||||
if k == 'isbn':
|
||||
v = check_isbn(k)
|
||||
if v is not None:
|
||||
val[k] = v
|
||||
ids = sorted(val.iteritems(), key=keygen)
|
||||
txt = ', '.join(['%s:%s'%(k, v) for k, v in ids])
|
||||
self.setText(txt.strip())
|
||||
@ -954,8 +969,8 @@ class IdentifiersEdit(QLineEdit): # {{{
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
def initialize(self, db, id_):
|
||||
self.current_val = db.get_identifiers(id_, index_is_id=True)
|
||||
self.original_val = self.current_val
|
||||
self.original_val = db.get_identifiers(id_, index_is_id=True)
|
||||
self.current_val = self.original_val
|
||||
|
||||
def commit(self, db, id_):
|
||||
if self.original_val != self.current_val:
|
||||
|
@ -41,8 +41,11 @@ class FieldsModel(FM): # {{{
|
||||
self.reset()
|
||||
|
||||
def commit(self):
|
||||
val = [k for k, v in self.overrides.iteritems() if v == Qt.Unchecked]
|
||||
self.prefs['ignore_fields'] = val
|
||||
ignored_fields = set([x for x in self.prefs['ignore_fields'] if x not in
|
||||
self.overrides])
|
||||
changed = set([k for k, v in self.overrides.iteritems() if v ==
|
||||
Qt.Unchecked])
|
||||
self.prefs['ignore_fields'] = list(ignored_fields.union(changed))
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -253,7 +253,7 @@ class ResultsView(QTableView): # {{{
|
||||
parts.append('</center>')
|
||||
if book.identifiers:
|
||||
urls = urls_from_identifiers(book.identifiers)
|
||||
ids = ['<a href="%s">%s</a>'%(url, name) for name, url in urls]
|
||||
ids = ['<a href="%s">%s</a>'%(url, name) for name, ign, ign, url in urls]
|
||||
if ids:
|
||||
parts.append('<div><b>%s:</b> %s</div><br>'%(_('See at'), ', '.join(ids)))
|
||||
if book.tags:
|
||||
|
@ -63,7 +63,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
self.shortcuts.linkActivated.connect(self.shortcut_activated)
|
||||
text = '<p>'+_('Quick create:')
|
||||
for col, name in [('isbn', _('ISBN')), ('formats', _('Formats')),
|
||||
('last_modified', _('Modified Date')), ('yesno', _('Yes/No')),
|
||||
('yesno', _('Yes/No')),
|
||||
('tags', _('Tags')), ('series', _('Series')), ('rating',
|
||||
_('Rating')), ('people', _("People's names"))]:
|
||||
text += ' <a href="col:%s">%s</a>,'%(col, name)
|
||||
@ -150,7 +150,6 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
'tags': _('My Tags'),
|
||||
'series': _('My Series'),
|
||||
'rating': _('My Rating'),
|
||||
'last_modified':_('Modified Date'),
|
||||
'people': _('People')}[which])
|
||||
self.is_names.setChecked(which == 'people')
|
||||
if self.composite_box.isVisible():
|
||||
@ -158,9 +157,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
{
|
||||
'isbn': '{identifiers:select(isbn)}',
|
||||
'formats': '{formats}',
|
||||
'last_modified':'''{last_modified:'format_date($, "dd MMM yyyy")'}'''
|
||||
}[which])
|
||||
self.composite_sort_by.setCurrentIndex(2 if which == 'last_modified' else 0)
|
||||
self.composite_sort_by.setCurrentIndex(0)
|
||||
|
||||
def datatype_changed(self, *args):
|
||||
try:
|
||||
|
@ -235,6 +235,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
gui.library_view.refresh_book_details()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
from calibre.gui2 import Application
|
||||
app = Application([])
|
||||
test_widget('Interface', 'Look & Feel')
|
||||
|
||||
|
@ -7,28 +7,27 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>717</width>
|
||||
<height>444</height>
|
||||
<height>390</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<widget class="QWidget" name="page">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>699</width>
|
||||
<height>306</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Main interface</string>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/lt.png</normaloff>:/images/lt.png</iconset>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<attribute name="title">
|
||||
<string>Main Interface</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_9">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
@ -75,6 +74,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="opt_systray_icon">
|
||||
<property name="text">
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="opt_disable_animations">
|
||||
<property name="toolTip">
|
||||
@ -85,6 +91,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="opt_disable_tray_notification">
|
||||
<property name="text">
|
||||
<string>Disable &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="opt_show_splash_screen">
|
||||
<property name="text">
|
||||
@ -97,12 +110,12 @@
|
||||
<property name="title">
|
||||
<string>&Toolbar</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="opt_toolbar_icon_size"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>&Icon size:</string>
|
||||
</property>
|
||||
@ -115,7 +128,7 @@
|
||||
<widget class="QComboBox" name="opt_toolbar_text"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Show &text under icons:</string>
|
||||
</property>
|
||||
@ -127,20 +140,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="4" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
@ -161,135 +161,15 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="change_font_button">
|
||||
<property name="text">
|
||||
<string>Change &font (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="opt_systray_icon">
|
||||
<property name="text">
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="opt_disable_tray_notification">
|
||||
<property name="text">
|
||||
<string>Disable &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>649</width>
|
||||
<height>96</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Tag Browser</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_show_avg_rating">
|
||||
<property name="text">
|
||||
<string>Show &average ratings in the tags browser</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Tags browser category &partitioning method:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_tags_browser_partition_method</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="opt_tags_browser_partition_method">
|
||||
<property name="toolTip">
|
||||
<string>Choose how tag browser subcategories are displayed when
|
||||
there are more items than the limit. Select by first
|
||||
letter to see an A, B, C list. Choose partitioned to
|
||||
have a list of fixed-sized groups. Set to disabled
|
||||
if you never want subcategories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Collapse when more items than:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_tags_browser_collapse_at</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
|
||||
<property name="toolTip">
|
||||
<string>If a Tag Browser category has more than this number of items, it is divided
|
||||
up into sub-categories. If the partition method is set to disable, this value is ignored.</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_81">
|
||||
<property name="text">
|
||||
<string>Categories with &hierarchical items:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_categories_using_hierarchy</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="MultiCompleteLineEdit" name="opt_categories_using_hierarchy">
|
||||
<property name="toolTip">
|
||||
<string>A comma-separated list of columns in which items containing
|
||||
periods are displayed in the tag browser trees. For example, if
|
||||
this box contains 'tags' then tags of the form 'Mystery.English'
|
||||
and 'Mystery.Thriller' will be displayed with English and Thriller
|
||||
both under 'Mystery'. If 'tags' is not in this box,
|
||||
then the tags will be displayed each on their own line.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
@ -303,77 +183,15 @@ then the tags will be displayed each on their own line.</string>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>429</width>
|
||||
<height>63</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Cover Browser</string>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/book.png</normaloff>:/images/book.png</iconset>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_separate_cover_flow">
|
||||
<property name="text">
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Number of covers to show in browse mode (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_cover_flow_queue_length</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>699</width>
|
||||
<height>306</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<attribute name="title">
|
||||
<string>Book Details</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="opt_use_roman_numerals_for_series_number">
|
||||
<property name="text">
|
||||
<string>Use &Roman numerals for series</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="gridLayout_12">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
@ -425,6 +243,16 @@ then the tags will be displayed each on their own line.</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="opt_use_roman_numerals_for_series_number">
|
||||
<property name="text">
|
||||
<string>Use &Roman numerals for series</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
@ -437,6 +265,148 @@ then the tags will be displayed each on their own line.</string>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/tags.png</normaloff>:/images/tags.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Tag Browser</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_10">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Tags browser category &partitioning method:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_tags_browser_partition_method</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="opt_tags_browser_partition_method">
|
||||
<property name="toolTip">
|
||||
<string>Choose how tag browser subcategories are displayed when
|
||||
there are more items than the limit. Select by first
|
||||
letter to see an A, B, C list. Choose partitioned to
|
||||
have a list of fixed-sized groups. Set to disabled
|
||||
if you never want subcategories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>&Collapse when more items than:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_tags_browser_collapse_at</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
|
||||
<property name="toolTip">
|
||||
<string>If a Tag Browser category has more than this number of items, it is divided
|
||||
up into sub-categories. If the partition method is set to disable, this value is ignored.</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="5">
|
||||
<widget class="QCheckBox" name="opt_show_avg_rating">
|
||||
<property name="text">
|
||||
<string>Show &average ratings in the tags browser</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_81">
|
||||
<property name="text">
|
||||
<string>Categories with &hierarchical items:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_categories_using_hierarchy</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="5">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>690</width>
|
||||
<height>252</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="3">
|
||||
<widget class="MultiCompleteLineEdit" name="opt_categories_using_hierarchy">
|
||||
<property name="toolTip">
|
||||
<string>A comma-separated list of columns in which items containing
|
||||
periods are displayed in the tag browser trees. For example, if
|
||||
this box contains 'tags' then tags of the form 'Mystery.English'
|
||||
and 'Mystery.Thriller' will be displayed with English and Thriller
|
||||
both under 'Mystery'. If 'tags' is not in this box,
|
||||
then the tags will be displayed each on their own line.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/cover_flow.png</normaloff>:/images/cover_flow.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Cover Browser</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_11">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_separate_cover_flow">
|
||||
<property name="text">
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Number of covers to show in browse mode (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_cover_flow_queue_length</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>690</width>
|
||||
<height>283</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -89,7 +89,7 @@ class Category(QWidget): # {{{
|
||||
self.bar = QToolBar(self)
|
||||
self.bar.setStyleSheet(
|
||||
'QToolBar { border: none; background: none }')
|
||||
self.bar.setIconSize(QSize(48, 48))
|
||||
self.bar.setIconSize(QSize(32, 32))
|
||||
self.bar.setMovable(False)
|
||||
self.bar.setFloatable(False)
|
||||
self.bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
|
@ -209,8 +209,11 @@ class FieldsModel(QAbstractListModel): # {{{
|
||||
return ret
|
||||
|
||||
def commit(self):
|
||||
val = [k for k, v in self.overrides.iteritems() if v == Qt.Unchecked]
|
||||
msprefs['ignore_fields'] = val
|
||||
ignored_fields = set([x for x in msprefs['ignore_fields'] if x not in
|
||||
self.overrides])
|
||||
changed = set([k for k, v in self.overrides.iteritems() if v ==
|
||||
Qt.Unchecked])
|
||||
msprefs['ignore_fields'] = list(ignored_fields.union(changed))
|
||||
|
||||
|
||||
# }}}
|
||||
|
@ -7,12 +7,15 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from functools import partial
|
||||
|
||||
|
||||
from PyQt4.Qt import QComboBox, Qt, QLineEdit, QStringList, pyqtSlot, QDialog, \
|
||||
pyqtSignal, QCompleter, QAction, QKeySequence, QTimer, \
|
||||
QString, QIcon
|
||||
QString, QIcon, QMenu
|
||||
|
||||
from calibre.gui2 import config
|
||||
from calibre.gui2 import config, error_dialog
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
|
||||
from calibre.gui2.dialogs.search import SearchDialog
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
@ -330,6 +333,24 @@ class SavedSearchBox(QComboBox): # {{{
|
||||
self.saved_search_selected (name)
|
||||
self.changed.emit()
|
||||
|
||||
def delete_current_search(self):
|
||||
idx = self.currentIndex()
|
||||
if idx <= 0:
|
||||
error_dialog(self, _('Delete current search'),
|
||||
_('No search is selected'), show=True)
|
||||
return
|
||||
if not confirm('<p>'+_('The selected search will be '
|
||||
'<b>permanently deleted</b>. Are you sure?')
|
||||
+'</p>', 'saved_search_delete', self):
|
||||
return
|
||||
ss = saved_searches().lookup(unicode(self.currentText()))
|
||||
if ss is None:
|
||||
return
|
||||
saved_searches().delete(unicode(self.currentText()))
|
||||
self.clear()
|
||||
self.search_box.clear()
|
||||
self.changed.emit()
|
||||
|
||||
# SIGNALed from the main UI
|
||||
def copy_search_button_clicked (self):
|
||||
idx = self.currentIndex();
|
||||
@ -428,6 +449,22 @@ class SavedSearchBoxMixin(object): # {{{
|
||||
for x in ('copy', 'save'):
|
||||
b = getattr(self, x+'_search_button')
|
||||
b.setStatusTip(b.toolTip())
|
||||
self.save_search_button.setToolTip('<p>' +
|
||||
_("Save current search under the name shown in the box. "
|
||||
"Press and hold for a pop-up options menu.") + '</p>')
|
||||
self.save_search_button.setMenu(QMenu())
|
||||
self.save_search_button.menu().addAction(
|
||||
QIcon(I('plus.png')),
|
||||
_('Create saved search'),
|
||||
self.saved_search.save_search_button_clicked)
|
||||
self.save_search_button.menu().addAction(
|
||||
QIcon(I('trash.png')),
|
||||
_('Delete saved search'),
|
||||
self.saved_search.delete_current_search)
|
||||
self.save_search_button.menu().addAction(
|
||||
QIcon(I('search.png')),
|
||||
_('Manage saved searches'),
|
||||
partial(self.do_saved_search_edit, None))
|
||||
|
||||
def saved_searches_changed(self, set_restriction=None, recount=True):
|
||||
p = sorted(saved_searches().names(), key=sort_key)
|
||||
|
@ -33,24 +33,21 @@
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QWebView" name="view"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QScrollBar" name="vertical_scrollbar">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QScrollBar" name="horizontal_scrollbar">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QFrame" name="dictionary_box">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
@ -91,6 +88,9 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="DocumentView" name="view" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
@ -108,7 +108,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>Qt::LeftToolBarArea</enum>
|
||||
<enum>LeftToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
@ -136,7 +136,7 @@
|
||||
</widget>
|
||||
<widget class="QToolBar" name="tool_bar2">
|
||||
<attribute name="toolBarArea">
|
||||
<enum>Qt::TopToolBarArea</enum>
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
@ -316,6 +316,12 @@
|
||||
<extends>QWidget</extends>
|
||||
<header>QtWebKit/QWebView</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>DocumentView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>calibre/gui2/viewer/documentview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
|
@ -406,11 +406,9 @@ class ResultCache(SearchQueryParser): # {{{
|
||||
if val_func is None:
|
||||
loc = self.field_metadata[location]['rec_index']
|
||||
val_func = lambda item, loc=loc: item[loc]
|
||||
dt = self.field_metadata[location]['datatype']
|
||||
|
||||
q = ''
|
||||
val_func = lambda item, loc=loc: item[loc]
|
||||
cast = adjust = lambda x: x
|
||||
dt = self.field_metadata[location]['datatype']
|
||||
|
||||
if query == 'false':
|
||||
if dt == 'rating' or location == 'cover':
|
||||
|
@ -368,7 +368,8 @@ class FieldMetadata(dict):
|
||||
'date_format': tweaks['gui_timestamp_display_format']}
|
||||
self._tb_cats['pubdate']['display'] = {
|
||||
'date_format': tweaks['gui_pubdate_display_format']}
|
||||
self._tb_cats['last_modified']['display'] = {'date_format': 'iso'}
|
||||
self._tb_cats['last_modified']['display'] = {
|
||||
'date_format': tweaks['gui_last_modified_display_format']}
|
||||
self.custom_field_prefix = '#'
|
||||
self.get = self._tb_cats.get
|
||||
|
||||
|
@ -17,6 +17,7 @@ from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||
from calibre.constants import preferred_encoding
|
||||
from calibre.ebooks.metadata import fmt_sidx
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
from calibre.utils.date import parse_date
|
||||
from calibre import strftime, prints, sanitize_file_name_unicode
|
||||
|
||||
plugboard_any_device_value = 'any device'
|
||||
@ -42,6 +43,8 @@ FORMAT_ARG_DESCS = dict(
|
||||
publisher=_('The publisher'),
|
||||
timestamp=_('The date'),
|
||||
pubdate=_('The published date'),
|
||||
last_modified=_('The date when the metadata for this book record'
|
||||
' was last modified'),
|
||||
id=_('The calibre internal id')
|
||||
)
|
||||
|
||||
@ -191,6 +194,9 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250,
|
||||
format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple())
|
||||
if hasattr(mi.pubdate, 'timetuple'):
|
||||
format_args['pubdate'] = strftime(timefmt, mi.pubdate.timetuple())
|
||||
if hasattr(mi, 'last_modified') and hasattr(mi.last_modified, 'timetuple'):
|
||||
format_args['last_modified'] = strftime(timefmt, mi.last_modified.timetuple())
|
||||
|
||||
format_args['id'] = str(id)
|
||||
# Now format the custom fields
|
||||
custom_metadata = mi.get_all_user_metadata(make_copy=False)
|
||||
@ -373,10 +379,14 @@ def save_serialized_to_disk(ids, data, plugboards, root, opts, callback):
|
||||
root, opts, length = _sanitize_args(root, opts)
|
||||
failures = []
|
||||
for x in ids:
|
||||
opf, cover, format_map = data[x]
|
||||
opf, cover, format_map, last_modified = data[x]
|
||||
if isinstance(opf, unicode):
|
||||
opf = opf.encode('utf-8')
|
||||
mi = OPF(cStringIO.StringIO(opf)).to_book_metadata()
|
||||
try:
|
||||
mi.last_modified = parse_date(last_modified)
|
||||
except:
|
||||
pass
|
||||
tb = ''
|
||||
try:
|
||||
failed, id, title = do_save_book_to_disk(x, mi, cover, plugboards,
|
||||
|
@ -20,13 +20,14 @@ What formats does |app| support conversion to/from?
|
||||
|app| supports the conversion of many input formats to many output formats.
|
||||
It can convert every input format in the following list, to every output format.
|
||||
|
||||
*Input Formats:* CBZ, CBR, CBC, CHM, EPUB, FB2, HTML, HTMLZ, LIT, LRF, MOBI, ODT, PDF, PRC**, PDB***, PML, RB, RTF, SNB, TCR, TXT, TXTZ
|
||||
*Input Formats:* CBZ, CBR, CBC, CHM, EPUB, FB2, HTML, HTMLZ, LIT, LRF, MOBI, ODT, PDF, PRC, PDB, PML, RB, RTF, SNB, TCR, TXT, TXTZ
|
||||
|
||||
*Output Formats:* EPUB, FB2, OEB, LIT, LRF, MOBI, HTMLZ, PDB, PML, RB, PDF, SNB, TCR, TXT, TXTZ
|
||||
|
||||
** PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers
|
||||
.. note ::
|
||||
|
||||
*** PDB is also a generic format. |app| supports eReder, Plucker, PML and zTxt PDB files.
|
||||
PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers.
|
||||
PDB is also a generic format. |app| supports eReder, Plucker, PML and zTxt PDB files.
|
||||
|
||||
.. _best-source-formats:
|
||||
|
||||
|
@ -365,6 +365,8 @@ Dates and numeric fields support the relational operators ``=`` (equals), ``>``
|
||||
Rating fields are considered to be numeric. For example, the search ``rating:>=3`` will find all books rated 3
|
||||
or higher.
|
||||
|
||||
You can search for the number of items in multiple-valued fields such as tags). These searches begin with the character ``#``, then use the same syntax as numeric fields. For example, to find all books with more than 4 tags, use ``tags:#>4``. To find all books with exactly 10 tags, use ``tags:#=10``.
|
||||
|
||||
Series indices are searchable. For the standard series, the search name is 'series_index'. For
|
||||
custom series columns, use the column search name followed by _index. For example, to search the indices for a
|
||||
custom series column named ``#my_series``, you would use the search name ``#my_series_index``.
|
||||
|
@ -65,17 +65,14 @@ Catalog plugins
|
||||
Metadata download plugins
|
||||
--------------------------
|
||||
|
||||
.. module:: calibre.ebooks.metadata.fetch
|
||||
.. module:: calibre.ebooks.metadata.sources.base
|
||||
|
||||
.. autoclass:: MetadataSource
|
||||
.. autoclass:: Source
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. autoclass:: calibre.ebooks.metadata.covers.CoverDownload
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
.. autoclass:: InternalMetadataCompareKeyGen
|
||||
|
||||
Conversion plugins
|
||||
--------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user