mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from main branch
This commit is contained in:
commit
2c054f7505
@ -19,6 +19,65 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - title:
|
||||||
|
|
||||||
|
|
||||||
|
- version: 0.7.54
|
||||||
|
date: 2011-04-08
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "New output format, HTMLZ which is a single HTML file with its associated images/stylesheets in a zipped up file"
|
||||||
|
description: "Useful when you want to convert your ebook into a single HTML file for easy editing. Note that this output plugin is still new and needs testing"
|
||||||
|
|
||||||
|
- title: "When dealing with ZIP/RAR archives, use the file header rather than the file extension to detrmine the file type, when possible. This fixes the common case of CBZ files being actually cbr files and vice versa"
|
||||||
|
|
||||||
|
- title: "Support for the Motorola Atrix"
|
||||||
|
|
||||||
|
- title: "Allow the icons in the toolbar to be turned off completely via Preferences->Look & Feel"
|
||||||
|
|
||||||
|
- title: "When downloading metadata use the gzip transfer encoding when possible for a speedup."
|
||||||
|
tickets: [749304]
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Conversion pipeline: Workaround for bug in lxml that causes a massive mem leak on windows and OS X when the input document contains non ASCII CSS selectors."
|
||||||
|
tickets: [754555]
|
||||||
|
|
||||||
|
- title: "Conversion pipeline: Handle inline <style> tags that put all the actual CSS inside an XML comment."
|
||||||
|
tickets: [750063]
|
||||||
|
|
||||||
|
- title: "The 'Choose Library' button now shows its popup menu when you already have more than one library instead of the dialog to create a new library"
|
||||||
|
tickets: [754154]
|
||||||
|
|
||||||
|
- title: "Apply all content server setting when clicking the Start Server button in Preferences->Sharing over the net"
|
||||||
|
tickets: [753122]
|
||||||
|
|
||||||
|
- title: "Fix content server breaking if its restriction is set to a saved search that was deleted"
|
||||||
|
tickets: [751950]
|
||||||
|
|
||||||
|
- title: "Fix detection of PocketBook with 2.0.6 firmware on windows"
|
||||||
|
tickets: [750336]
|
||||||
|
|
||||||
|
- title: "ODT Input: Fix handling of the <text:s> element."
|
||||||
|
tickets: [749655]
|
||||||
|
|
||||||
|
- title: "MOBI Output: Don't use self closed tags"
|
||||||
|
|
||||||
|
- title: "Fix book details popup becoming too tall if there is a lot of metadata"
|
||||||
|
|
||||||
|
- title: "Fix new PDF engine crashing on PDF files with embedded fonts with null names"
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Kommersant
|
||||||
|
- Perfil
|
||||||
|
- Times of India
|
||||||
|
- IHT
|
||||||
|
- Guardian
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Al Ahram"
|
||||||
|
authors: Hassan Williamson
|
||||||
|
|
||||||
|
- title: "F-Secure and developpez.com"
|
||||||
|
authors: louhike
|
||||||
|
|
||||||
- version: 0.7.53
|
- version: 0.7.53
|
||||||
date: 2011-04-01
|
date: 2011-04-01
|
||||||
|
|
||||||
|
62
recipes/al_ahram.recipe
Normal file
62
recipes/al_ahram.recipe
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Hassan Williamson <haz at hazrpg.co.uk>'
|
||||||
|
'''
|
||||||
|
ahram.org.eg
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AlAhram(BasicNewsRecipe):
|
||||||
|
title = 'Al-Ahram'
|
||||||
|
__author__ = 'Hassan Williamson'
|
||||||
|
description = 'News from Egypt in Arabic.'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
#delay = 1
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf8'
|
||||||
|
publisher = 'Al-Ahram'
|
||||||
|
category = 'News'
|
||||||
|
language = 'ar'
|
||||||
|
publication_type = 'newsportal'
|
||||||
|
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif; direction: rtl; } .txtTitle{ font-weight: bold; } '
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'class':['bbcolright']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':['bbnav', 'bbsp']}),
|
||||||
|
dict(name='div', attrs={'id':['AddThisButton']})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_attributes = [
|
||||||
|
'width','height'
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'الأولى', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=25'),
|
||||||
|
(u'مصر', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=27'),
|
||||||
|
(u'المحافظات', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=29'),
|
||||||
|
(u'الوطن العربي', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=31'),
|
||||||
|
(u'العالم', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=26'),
|
||||||
|
(u'تقارير المراسلين', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=2'),
|
||||||
|
(u'تحقيقات', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=3'),
|
||||||
|
(u'قضايا واراء', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=4'),
|
||||||
|
(u'اقتصاد', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=5'),
|
||||||
|
(u'رياضة', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=6'),
|
||||||
|
(u'حوادث', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=38'),
|
||||||
|
(u'دنيا الثقافة', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=7'),
|
||||||
|
(u'المراة والطفل', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=8'),
|
||||||
|
(u'يوم جديد', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=9'),
|
||||||
|
(u'الكتاب', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=10'),
|
||||||
|
(u'الاعمدة', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=11'),
|
||||||
|
(u'أراء حرة', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=59'),
|
||||||
|
(u'ملفات الاهرام', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=12'),
|
||||||
|
(u'بريد الاهرام', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=15'),
|
||||||
|
(u'الاخيرة', 'http://www.ahram.org.eg/RssXml.aspx?CategoryID=16'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@ -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.53'
|
__version__ = '0.7.54'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re, importlib
|
import re, importlib
|
||||||
|
@ -97,6 +97,10 @@ class CSSSelector(etree.XPath):
|
|||||||
|
|
||||||
def __init__(self, css, namespaces=XPNSMAP):
|
def __init__(self, css, namespaces=XPNSMAP):
|
||||||
css = self.MIN_SPACE_RE.sub(r'\1', css)
|
css = self.MIN_SPACE_RE.sub(r'\1', css)
|
||||||
|
if isinstance(css, unicode):
|
||||||
|
# Workaround for bug in lxml on windows/OS X that causes a massive
|
||||||
|
# memory leak with non ASCII selectors
|
||||||
|
css = css.encode('ascii', 'ignore').decode('ascii')
|
||||||
try:
|
try:
|
||||||
path = css_to_xpath(css)
|
path = css_to_xpath(css)
|
||||||
except UnicodeEncodeError: # Bug in css_to_xpath
|
except UnicodeEncodeError: # Bug in css_to_xpath
|
||||||
|
@ -23,6 +23,10 @@ from calibre.utils.date import UNDEFINED_DATE
|
|||||||
# Setup gprefs {{{
|
# Setup gprefs {{{
|
||||||
gprefs = JSONConfig('gui')
|
gprefs = JSONConfig('gui')
|
||||||
|
|
||||||
|
gprefs.defaults['action-layout-menubar'] = ()
|
||||||
|
|
||||||
|
gprefs.defaults['action-layout-menubar-device'] = ()
|
||||||
|
|
||||||
gprefs.defaults['action-layout-toolbar'] = (
|
gprefs.defaults['action-layout-toolbar'] = (
|
||||||
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
|
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
|
||||||
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
|
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
|
||||||
|
@ -75,7 +75,7 @@ class InterfaceAction(QObject):
|
|||||||
dont_remove_from = frozenset([])
|
dont_remove_from = frozenset([])
|
||||||
|
|
||||||
all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu',
|
all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu',
|
||||||
'context-menu-device', 'toolbar-child'])
|
'context-menu-device', 'toolbar-child', 'menubar', 'menubar-device'])
|
||||||
|
|
||||||
#: Type of action
|
#: Type of action
|
||||||
#: 'current' means acts on the current view
|
#: 'current' means acts on the current view
|
||||||
@ -145,11 +145,10 @@ class InterfaceAction(QObject):
|
|||||||
ans[candidate] = zf.read(candidate)
|
ans[candidate] = zf.read(candidate)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
'''
|
'''
|
||||||
Setup this plugin. Only called once during initialization. self.gui is
|
Setup this plugin. Only called once during initialization. self.gui is
|
||||||
available. The action secified by :attr:`action_spec` is available as
|
available. The action specified by :attr:`action_spec` is available as
|
||||||
``self.qaction``.
|
``self.qaction``.
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
@ -12,7 +12,7 @@ class AddToLibraryAction(InterfaceAction):
|
|||||||
name = 'Add To Library'
|
name = 'Add To Library'
|
||||||
action_spec = (_('Add books to library'), 'add_book.png',
|
action_spec = (_('Add books to library'), 'add_book.png',
|
||||||
_('Add books to your calibre library from the connected device'), None)
|
_('Add books to your calibre library from the connected device'), None)
|
||||||
dont_add_to = frozenset(['toolbar', 'context-menu', 'toolbar-child'])
|
dont_add_to = frozenset(['menubar', 'toolbar', 'context-menu', 'toolbar-child'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -18,6 +18,7 @@ class FetchAnnotationsAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Fetch Annotations'
|
name = 'Fetch Annotations'
|
||||||
action_spec = (_('Fetch annotations (experimental)'), None, None, None)
|
action_spec = (_('Fetch annotations (experimental)'), None, None, None)
|
||||||
|
dont_add_to = frozenset(['menubar', 'toolbar', 'context-menu', 'toolbar-child'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -18,7 +18,7 @@ class GenerateCatalogAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Generate Catalog'
|
name = 'Generate Catalog'
|
||||||
action_spec = (_('Create a catalog of the books in your calibre library'), None, None, None)
|
action_spec = (_('Create a catalog of the books in your calibre library'), None, None, None)
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def generate_catalog(self):
|
def generate_catalog(self):
|
||||||
rows = self.gui.library_view.selectionModel().selectedRows()
|
rows = self.gui.library_view.selectionModel().selectedRows()
|
||||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import os, shutil
|
import os, shutil
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QMenu, Qt, QInputDialog
|
from PyQt4.Qt import QMenu, Qt, QInputDialog, QToolButton
|
||||||
|
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.constants import filesystem_encoding
|
from calibre.constants import filesystem_encoding
|
||||||
@ -80,7 +80,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
name = 'Choose Library'
|
name = 'Choose Library'
|
||||||
action_spec = (_('%d books'), 'lt.png',
|
action_spec = (_('%d books'), 'lt.png',
|
||||||
_('Choose calibre library to work with'), None)
|
_('Choose calibre library to work with'), None)
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.count_changed(0)
|
self.count_changed(0)
|
||||||
@ -88,6 +88,9 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
|
|
||||||
self.stats = LibraryUsageStats()
|
self.stats = LibraryUsageStats()
|
||||||
|
self.popup_type = (QToolButton.InstantPopup if len(self.stats.stats) > 1 else
|
||||||
|
QToolButton.MenuButtonPopup)
|
||||||
|
|
||||||
self.create_action(spec=(_('Switch/create library...'), 'lt.png', None,
|
self.create_action(spec=(_('Switch/create library...'), 'lt.png', None,
|
||||||
None), attr='action_choose')
|
None), attr='action_choose')
|
||||||
self.action_choose.triggered.connect(self.choose_library,
|
self.action_choose.triggered.connect(self.choose_library,
|
||||||
@ -123,6 +126,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
self.choose_menu.addAction(ac)
|
self.choose_menu.addAction(ac)
|
||||||
|
|
||||||
|
|
||||||
self.rename_separator = self.choose_menu.addSeparator()
|
self.rename_separator = self.choose_menu.addSeparator()
|
||||||
|
|
||||||
self.maintenance_menu = QMenu(_('Library Maintenance'))
|
self.maintenance_menu = QMenu(_('Library Maintenance'))
|
||||||
@ -172,6 +176,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
return
|
return
|
||||||
db = self.gui.library_view.model().db
|
db = self.gui.library_view.model().db
|
||||||
locations = list(self.stats.locations(db))
|
locations = list(self.stats.locations(db))
|
||||||
|
|
||||||
for ac in self.switch_actions:
|
for ac in self.switch_actions:
|
||||||
ac.setVisible(False)
|
ac.setVisible(False)
|
||||||
self.quick_menu.clear()
|
self.quick_menu.clear()
|
||||||
@ -205,7 +210,6 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
rename_actions, delete_actions, qs_actions,
|
rename_actions, delete_actions, qs_actions,
|
||||||
self.action_choose)
|
self.action_choose)
|
||||||
|
|
||||||
|
|
||||||
def location_selected(self, loc):
|
def location_selected(self, loc):
|
||||||
enabled = loc == 'library'
|
enabled = loc == 'library'
|
||||||
self.qaction.setEnabled(enabled)
|
self.qaction.setEnabled(enabled)
|
||||||
|
@ -20,7 +20,7 @@ class ConvertAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Convert Books'
|
name = 'Convert Books'
|
||||||
action_spec = (_('Convert books'), 'convert.png', None, _('C'))
|
action_spec = (_('Convert books'), 'convert.png', None, _('C'))
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -24,7 +24,7 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
|
|
||||||
config_email = pyqtSignal()
|
config_email = pyqtSignal()
|
||||||
toggle_server = pyqtSignal()
|
toggle_server = pyqtSignal()
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QMenu.__init__(self, parent)
|
QMenu.__init__(self, parent)
|
||||||
@ -121,8 +121,7 @@ class SendToDeviceAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Send To Device'
|
name = 'Send To Device'
|
||||||
action_spec = (_('Send to device'), 'sync.png', None, _('D'))
|
action_spec = (_('Send to device'), 'sync.png', None, _('D'))
|
||||||
dont_remove_from = frozenset(['toolbar-device'])
|
dont_add_to = frozenset(['menubar', 'toolbar', 'context-menu', 'toolbar-child'])
|
||||||
dont_add_to = frozenset(['toolbar', 'context-menu', 'toolbar-child'])
|
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.qaction.triggered.connect(self.do_sync)
|
self.qaction.triggered.connect(self.do_sync)
|
||||||
|
@ -12,7 +12,7 @@ class EditCollectionsAction(InterfaceAction):
|
|||||||
name = 'Edit Collections'
|
name = 'Edit Collections'
|
||||||
action_spec = (_('Manage collections'), None,
|
action_spec = (_('Manage collections'), None,
|
||||||
_('Manage the collections on this device'), None)
|
_('Manage the collections on this device'), None)
|
||||||
dont_add_to = frozenset(['toolbar', 'context-menu', 'toolbar-child'])
|
dont_add_to = frozenset(['menubar', 'toolbar', 'context-menu', 'toolbar-child'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -141,15 +141,18 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
list(range(self.gui.library_view.model().rowCount(QModelIndex())))
|
list(range(self.gui.library_view.model().rowCount(QModelIndex())))
|
||||||
current_row = row_list.index(cr)
|
current_row = row_list.index(cr)
|
||||||
|
|
||||||
if test_eight_code:
|
func = (self.do_edit_metadata if test_eight_code else
|
||||||
changed = self.do_edit_metadata(row_list, current_row)
|
self.do_edit_metadata_old)
|
||||||
else:
|
changed, rows_to_refresh = func(row_list, current_row)
|
||||||
changed = self.do_edit_metadata_old(row_list, current_row)
|
|
||||||
|
m = self.gui.library_view.model()
|
||||||
|
|
||||||
|
if rows_to_refresh:
|
||||||
|
m.refresh_rows(rows_to_refresh)
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
self.gui.library_view.model().refresh_ids(list(changed))
|
m.refresh_ids(list(changed))
|
||||||
current = self.gui.library_view.currentIndex()
|
current = self.gui.library_view.currentIndex()
|
||||||
m = self.gui.library_view.model()
|
|
||||||
if self.gui.cover_flow:
|
if self.gui.cover_flow:
|
||||||
self.gui.cover_flow.dataChanged()
|
self.gui.cover_flow.dataChanged()
|
||||||
m.current_changed(current, previous)
|
m.current_changed(current, previous)
|
||||||
@ -183,6 +186,7 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
current_row += d.row_delta
|
current_row += d.row_delta
|
||||||
self.gui.library_view.set_current_row(current_row)
|
self.gui.library_view.set_current_row(current_row)
|
||||||
self.gui.library_view.scroll_to_row(current_row)
|
self.gui.library_view.scroll_to_row(current_row)
|
||||||
|
return changed, set()
|
||||||
|
|
||||||
def do_edit_metadata(self, row_list, current_row):
|
def do_edit_metadata(self, row_list, current_row):
|
||||||
from calibre.gui2.metadata.single import edit_metadata
|
from calibre.gui2.metadata.single import edit_metadata
|
||||||
@ -190,7 +194,7 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
changed, rows_to_refresh = edit_metadata(db, row_list, current_row,
|
changed, rows_to_refresh = edit_metadata(db, row_list, current_row,
|
||||||
parent=self.gui, view_slot=self.view_format_callback,
|
parent=self.gui, view_slot=self.view_format_callback,
|
||||||
set_current_callback=self.set_current_callback)
|
set_current_callback=self.set_current_callback)
|
||||||
return changed
|
return changed, rows_to_refresh
|
||||||
|
|
||||||
def set_current_callback(self, id_):
|
def set_current_callback(self, id_):
|
||||||
db = self.gui.library_view.model().db
|
db = self.gui.library_view.model().db
|
||||||
|
@ -11,7 +11,7 @@ class NextMatchAction(InterfaceAction):
|
|||||||
name = 'Move to next highlighted book'
|
name = 'Move to next highlighted book'
|
||||||
action_spec = (_('Move to next match'), 'arrow-down.png',
|
action_spec = (_('Move to next match'), 'arrow-down.png',
|
||||||
_('Move to next highlighted match'), [_('N'), _('F3')])
|
_('Move to next highlighted match'), [_('N'), _('F3')])
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -13,7 +13,7 @@ class OpenFolderAction(InterfaceAction):
|
|||||||
name = 'Open Folder'
|
name = 'Open Folder'
|
||||||
action_spec = (_('Open containing folder'), 'document_open.png', None,
|
action_spec = (_('Open containing folder'), 'document_open.png', None,
|
||||||
_('O'))
|
_('O'))
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -16,7 +16,6 @@ class PreferencesAction(InterfaceAction):
|
|||||||
|
|
||||||
name = 'Preferences'
|
name = 'Preferences'
|
||||||
action_spec = (_('Preferences'), 'config.png', None, _('Ctrl+P'))
|
action_spec = (_('Preferences'), 'config.png', None, _('Ctrl+P'))
|
||||||
dont_remove_from = frozenset(['toolbar'])
|
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
pm = QMenu()
|
pm = QMenu()
|
||||||
|
@ -15,7 +15,7 @@ class ShowBookDetailsAction(InterfaceAction):
|
|||||||
name = 'Show Book Details'
|
name = 'Show Book Details'
|
||||||
action_spec = (_('Show book details'), 'dialog_information.png', None,
|
action_spec = (_('Show book details'), 'dialog_information.png', None,
|
||||||
_('I'))
|
_('I'))
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -15,7 +15,7 @@ class TweakEpubAction(InterfaceAction):
|
|||||||
action_spec = (_('Tweak ePub'), 'trim.png',
|
action_spec = (_('Tweak ePub'), 'trim.png',
|
||||||
_('Make small changes to ePub format books'),
|
_('Make small changes to ePub format books'),
|
||||||
_('T'))
|
_('T'))
|
||||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||||
action_type = 'current'
|
action_type = 'current'
|
||||||
|
|
||||||
def genesis(self):
|
def genesis(self):
|
||||||
|
@ -62,7 +62,7 @@ class Bool(Base):
|
|||||||
w = self.widgets[1]
|
w = self.widgets[1]
|
||||||
items = [_('Yes'), _('No'), _('Undefined')]
|
items = [_('Yes'), _('No'), _('Undefined')]
|
||||||
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
|
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not self.db.prefs.get('bools_are_tristate'):
|
||||||
items = items[:-1]
|
items = items[:-1]
|
||||||
icons = icons[:-1]
|
icons = icons[:-1]
|
||||||
for icon, text in zip(icons, items):
|
for icon, text in zip(icons, items):
|
||||||
@ -70,7 +70,7 @@ class Bool(Base):
|
|||||||
|
|
||||||
def setter(self, val):
|
def setter(self, val):
|
||||||
val = {None: 2, False: 1, True: 0}[val]
|
val = {None: 2, False: 1, True: 0}[val]
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no' and val == 2:
|
if not self.db.prefs.get('bools_are_tristate') and val == 2:
|
||||||
val = 1
|
val = 1
|
||||||
self.widgets[1].setCurrentIndex(val)
|
self.widgets[1].setCurrentIndex(val)
|
||||||
|
|
||||||
@ -549,7 +549,7 @@ class BulkBool(BulkBase, Bool):
|
|||||||
value = None
|
value = None
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None:
|
if not self.db.prefs.get('bools_are_tristate') and val is None:
|
||||||
val = False
|
val = False
|
||||||
if value is not None and value != val:
|
if value is not None and value != val:
|
||||||
return None
|
return None
|
||||||
@ -559,7 +559,7 @@ class BulkBool(BulkBase, Bool):
|
|||||||
def setup_ui(self, parent):
|
def setup_ui(self, parent):
|
||||||
self.make_widgets(parent, QComboBox)
|
self.make_widgets(parent, QComboBox)
|
||||||
items = [_('Yes'), _('No')]
|
items = [_('Yes'), _('No')]
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not self.db.prefs.get('bools_are_tristate'):
|
||||||
items.append('')
|
items.append('')
|
||||||
else:
|
else:
|
||||||
items.append(_('Undefined'))
|
items.append(_('Undefined'))
|
||||||
@ -571,7 +571,7 @@ class BulkBool(BulkBase, Bool):
|
|||||||
|
|
||||||
def getter(self):
|
def getter(self):
|
||||||
val = self.main_widget.currentIndex()
|
val = self.main_widget.currentIndex()
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not self.db.prefs.get('bools_are_tristate'):
|
||||||
return {2: False, 1: False, 0: True}[val]
|
return {2: False, 1: False, 0: True}[val]
|
||||||
else:
|
else:
|
||||||
return {2: None, 1: False, 0: True}[val]
|
return {2: None, 1: False, 0: True}[val]
|
||||||
@ -586,13 +586,13 @@ class BulkBool(BulkBase, Bool):
|
|||||||
return
|
return
|
||||||
val = self.gui_val
|
val = self.gui_val
|
||||||
val = self.normalize_ui_val(val)
|
val = self.normalize_ui_val(val)
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None:
|
if not self.db.prefs.get('bools_are_tristate') and val is None:
|
||||||
val = False
|
val = False
|
||||||
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
|
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
|
||||||
|
|
||||||
def a_c_checkbox_changed(self):
|
def a_c_checkbox_changed(self):
|
||||||
if not self.ignore_change_signals:
|
if not self.ignore_change_signals:
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no' and \
|
if not self.db.prefs.get('bools_are_tristate') and \
|
||||||
self.main_widget.currentIndex() == 2:
|
self.main_widget.currentIndex() == 2:
|
||||||
self.a_c_checkbox.setChecked(False)
|
self.a_c_checkbox.setChecked(False)
|
||||||
else:
|
else:
|
||||||
|
@ -7,12 +7,12 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
|
from PyQt4.Qt import (QIcon, Qt, QWidget, QToolBar, QSize,
|
||||||
pyqtSignal, QToolButton, QMenu, \
|
pyqtSignal, QToolButton, QMenu, QMenuBar, QAction,
|
||||||
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
|
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup)
|
||||||
|
|
||||||
|
|
||||||
from calibre.constants import __appname__
|
from calibre.constants import __appname__, isosx
|
||||||
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
|
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
|
||||||
from calibre.gui2.throbber import ThrobbingButton
|
from calibre.gui2.throbber import ThrobbingButton
|
||||||
from calibre.gui2 import gprefs
|
from calibre.gui2 import gprefs
|
||||||
@ -238,6 +238,80 @@ class Spacer(QWidget): # {{{
|
|||||||
self.l.addStretch(10)
|
self.l.addStretch(10)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class MenuAction(QAction):
|
||||||
|
|
||||||
|
def __init__(self, clone, parent):
|
||||||
|
QAction.__init__(self, clone.text(), parent)
|
||||||
|
self.clone = clone
|
||||||
|
clone.changed.connect(self.clone_changed)
|
||||||
|
|
||||||
|
def clone_changed(self):
|
||||||
|
self.setText(self.clone.text())
|
||||||
|
|
||||||
|
|
||||||
|
class MenuBar(QMenuBar): # {{{
|
||||||
|
|
||||||
|
def __init__(self, location_manager, parent):
|
||||||
|
QMenuBar.__init__(self, parent)
|
||||||
|
self.gui = parent
|
||||||
|
self.setNativeMenuBar(True)
|
||||||
|
|
||||||
|
self.location_manager = location_manager
|
||||||
|
self.location_manager.locations_changed.connect(self.build_bar)
|
||||||
|
self.added_actions = []
|
||||||
|
|
||||||
|
self.donate_action = QAction(_('Donate'), self)
|
||||||
|
self.donate_menu = QMenu()
|
||||||
|
self.donate_menu.addAction(self.gui.donate_action)
|
||||||
|
self.donate_action.setMenu(self.donate_menu)
|
||||||
|
self.build_bar()
|
||||||
|
|
||||||
|
def build_bar(self, changed_action=None):
|
||||||
|
showing_device = self.location_manager.has_device
|
||||||
|
actions = '-device' if showing_device else ''
|
||||||
|
actions = gprefs['action-layout-menubar'+actions]
|
||||||
|
|
||||||
|
show_main = len(actions) > 0
|
||||||
|
self.setVisible(show_main)
|
||||||
|
|
||||||
|
for ac in self.added_actions:
|
||||||
|
m = ac.menu()
|
||||||
|
if m is not None:
|
||||||
|
m.setVisible(False)
|
||||||
|
|
||||||
|
self.clear()
|
||||||
|
self.added_actions = []
|
||||||
|
self.action_map = {}
|
||||||
|
|
||||||
|
for what in actions:
|
||||||
|
if what is None:
|
||||||
|
continue
|
||||||
|
elif what == 'Location Manager':
|
||||||
|
for ac in self.location_manager.available_actions:
|
||||||
|
ac = self.build_menu(ac)
|
||||||
|
self.addAction(ac)
|
||||||
|
self.added_actions.append(ac)
|
||||||
|
elif what == 'Donate':
|
||||||
|
self.addAction(self.donate_action)
|
||||||
|
elif what in self.gui.iactions:
|
||||||
|
action = self.gui.iactions[what]
|
||||||
|
ac = self.build_menu(action.qaction)
|
||||||
|
self.addAction(ac)
|
||||||
|
self.added_actions.append(ac)
|
||||||
|
|
||||||
|
def build_menu(self, action):
|
||||||
|
m = action.menu()
|
||||||
|
ac = MenuAction(action, self)
|
||||||
|
if m is None:
|
||||||
|
m = QMenu()
|
||||||
|
m.addAction(action)
|
||||||
|
ac.setMenu(m)
|
||||||
|
return ac
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class ToolBar(QToolBar): # {{{
|
class ToolBar(QToolBar): # {{{
|
||||||
|
|
||||||
def __init__(self, donate, location_manager, child_bar, parent):
|
def __init__(self, donate, location_manager, child_bar, parent):
|
||||||
@ -284,6 +358,8 @@ class ToolBar(QToolBar): # {{{
|
|||||||
mactions = gprefs['action-layout-toolbar'+mactions]
|
mactions = gprefs['action-layout-toolbar'+mactions]
|
||||||
cactions = gprefs['action-layout-toolbar-child']
|
cactions = gprefs['action-layout-toolbar-child']
|
||||||
|
|
||||||
|
show_main = len(mactions) > 0
|
||||||
|
self.setVisible(show_main)
|
||||||
show_child = len(cactions) > 0
|
show_child = len(cactions) > 0
|
||||||
self.child_bar.setVisible(show_child)
|
self.child_bar.setVisible(show_child)
|
||||||
|
|
||||||
@ -306,6 +382,11 @@ class ToolBar(QToolBar): # {{{
|
|||||||
bar.added_actions.append(ac)
|
bar.added_actions.append(ac)
|
||||||
bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
|
bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
|
||||||
elif what == 'Donate':
|
elif what == 'Donate':
|
||||||
|
if isosx:
|
||||||
|
bar.addAction(self.gui.donate_action)
|
||||||
|
ch = self.setup_tool_button(bar, self.gui.donate_action)
|
||||||
|
ch.setText(_('Donate'))
|
||||||
|
else:
|
||||||
self.d_widget = QWidget()
|
self.d_widget = QWidget()
|
||||||
self.d_widget.setLayout(QVBoxLayout())
|
self.d_widget.setLayout(QVBoxLayout())
|
||||||
self.d_widget.layout().addWidget(self.donate_button)
|
self.d_widget.layout().addWidget(self.donate_button)
|
||||||
@ -325,10 +406,13 @@ class ToolBar(QToolBar): # {{{
|
|||||||
ch.setAutoRaise(True)
|
ch.setAutoRaise(True)
|
||||||
if ac.menu() is not None and menu_mode is not None:
|
if ac.menu() is not None and menu_mode is not None:
|
||||||
ch.setPopupMode(menu_mode)
|
ch.setPopupMode(menu_mode)
|
||||||
|
return ch
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
QToolBar.resizeEvent(self, ev)
|
QToolBar.resizeEvent(self, ev)
|
||||||
style = Qt.ToolButtonTextUnderIcon
|
style = Qt.ToolButtonTextUnderIcon
|
||||||
|
s = gprefs['toolbar_icon_size']
|
||||||
|
if s != 'off':
|
||||||
p = gprefs['toolbar_text']
|
p = gprefs['toolbar_text']
|
||||||
if p == 'never':
|
if p == 'never':
|
||||||
style = Qt.ToolButtonIconOnly
|
style = Qt.ToolButtonIconOnly
|
||||||
@ -421,6 +505,9 @@ class MainWindowMixin(object): # {{{
|
|||||||
self.location_manager, self.child_bar, self)
|
self.location_manager, self.child_bar, self)
|
||||||
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
|
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
|
||||||
self.addToolBar(Qt.BottomToolBarArea, self.child_bar)
|
self.addToolBar(Qt.BottomToolBarArea, self.child_bar)
|
||||||
|
self.menu_bar = MenuBar(self.location_manager, self)
|
||||||
|
self.setMenuBar(self.menu_bar)
|
||||||
|
self.setUnifiedTitleAndToolBarOnMac(True)
|
||||||
|
|
||||||
l = self.centralwidget.layout()
|
l = self.centralwidget.layout()
|
||||||
l.addWidget(self.search_bar)
|
l.addWidget(self.search_bar)
|
||||||
|
@ -353,7 +353,7 @@ class CcBoolDelegate(QStyledItemDelegate): # {{{
|
|||||||
editor = DelegateCB(parent)
|
editor = DelegateCB(parent)
|
||||||
items = [_('Y'), _('N'), ' ']
|
items = [_('Y'), _('N'), ' ']
|
||||||
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
|
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not index.model().db.prefs.get('bools_are_tristate'):
|
||||||
items = items[:-1]
|
items = items[:-1]
|
||||||
icons = icons[:-1]
|
icons = icons[:-1]
|
||||||
for icon, text in zip(icons, items):
|
for icon, text in zip(icons, items):
|
||||||
@ -367,7 +367,7 @@ class CcBoolDelegate(QStyledItemDelegate): # {{{
|
|||||||
def setEditorData(self, editor, index):
|
def setEditorData(self, editor, index):
|
||||||
m = index.model()
|
m = index.model()
|
||||||
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
|
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not m.db.prefs.get('bools_are_tristate'):
|
||||||
val = 1 if not val else 0
|
val = 1 if not val else 0
|
||||||
else:
|
else:
|
||||||
val = 2 if val is None else 1 if not val else 0
|
val = 2 if val is None else 1 if not val else 0
|
||||||
|
@ -700,7 +700,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.dc_decorator[col] = functools.partial(
|
self.dc_decorator[col] = functools.partial(
|
||||||
bool_type_decorator, idx=idx,
|
bool_type_decorator, idx=idx,
|
||||||
bool_cols_are_tristate=
|
bool_cols_are_tristate=
|
||||||
tweaks['bool_custom_columns_are_tristate'] != 'no')
|
self.db.prefs.get('bools_are_tristate'))
|
||||||
elif datatype in ('int', 'float'):
|
elif datatype in ('int', 'float'):
|
||||||
self.dc[col] = functools.partial(number_type, idx=idx)
|
self.dc[col] = functools.partial(number_type, idx=idx)
|
||||||
elif datatype == 'datetime':
|
elif datatype == 'datetime':
|
||||||
@ -710,7 +710,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.dc_decorator[col] = functools.partial(
|
self.dc_decorator[col] = functools.partial(
|
||||||
bool_type_decorator, idx=idx,
|
bool_type_decorator, idx=idx,
|
||||||
bool_cols_are_tristate=
|
bool_cols_are_tristate=
|
||||||
tweaks['bool_custom_columns_are_tristate'] != 'no')
|
self.db.prefs.get('bools_are_tristate'))
|
||||||
elif datatype == 'rating':
|
elif datatype == 'rating':
|
||||||
self.dc[col] = functools.partial(rating_type, idx=idx)
|
self.dc[col] = functools.partial(rating_type, idx=idx)
|
||||||
elif datatype == 'series':
|
elif datatype == 'series':
|
||||||
|
@ -521,7 +521,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class MetadataSingleDialogAlt(MetadataSingleDialogBase): # {{{
|
class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
|
||||||
|
|
||||||
cc_two_column = False
|
cc_two_column = False
|
||||||
one_line_comments_toolbar = True
|
one_line_comments_toolbar = True
|
||||||
@ -654,10 +654,14 @@ class MetadataSingleDialogAlt(MetadataSingleDialogBase): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1}
|
||||||
|
|
||||||
def edit_metadata(db, row_list, current_row, parent=None, view_slot=None,
|
def edit_metadata(db, row_list, current_row, parent=None, view_slot=None,
|
||||||
set_current_callback=None):
|
set_current_callback=None):
|
||||||
d = MetadataSingleDialog(db, parent)
|
cls = db.prefs.get('edit_metadata_single_layout', '')
|
||||||
|
if cls not in editors:
|
||||||
|
cls = 'default'
|
||||||
|
d = editors[cls](db, parent)
|
||||||
d.start(row_list, current_row, view_slot=view_slot,
|
d.start(row_list, current_row, view_slot=view_slot,
|
||||||
set_current_callback=set_current_callback)
|
set_current_callback=set_current_callback)
|
||||||
return d.changed, d.rows_to_refresh
|
return d.changed, d.rows_to_refresh
|
||||||
|
@ -8,6 +8,7 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from threading import Thread, Event
|
from threading import Thread, Event
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt,
|
from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt,
|
||||||
QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox,
|
QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox,
|
||||||
@ -22,7 +23,7 @@ from calibre.ebooks.metadata.sources.identify import identify
|
|||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.gui2 import error_dialog, NONE
|
from calibre.gui2 import error_dialog, NONE
|
||||||
from calibre.utils.date import utcnow, fromordinal, format_date
|
from calibre.utils.date import utcnow, fromordinal, format_date
|
||||||
|
from calibre.library.comments import comments_to_html
|
||||||
|
|
||||||
class RichTextDelegate(QStyledItemDelegate): # {{{
|
class RichTextDelegate(QStyledItemDelegate): # {{{
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ class RichTextDelegate(QStyledItemDelegate): # {{{
|
|||||||
painter.restore()
|
painter.restore()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class ResultsModel(QAbstractTableModel):
|
class ResultsModel(QAbstractTableModel): # {{{
|
||||||
|
|
||||||
COLUMNS = (
|
COLUMNS = (
|
||||||
'#', _('Title'), _('Published'), _('Has cover'), _('Has summary')
|
'#', _('Title'), _('Published'), _('Has cover'), _('Has summary')
|
||||||
@ -71,15 +72,12 @@ class ResultsModel(QAbstractTableModel):
|
|||||||
return len(self.COLUMNS)
|
return len(self.COLUMNS)
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if role != Qt.DisplayRole:
|
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
||||||
return NONE
|
|
||||||
if orientation == Qt.Horizontal:
|
|
||||||
try:
|
try:
|
||||||
return QVariant(self.COLUMNS[section])
|
return QVariant(self.COLUMNS[section])
|
||||||
except:
|
except:
|
||||||
return NONE
|
return NONE
|
||||||
else:
|
return NONE
|
||||||
return QVariant(unicode(section+1))
|
|
||||||
|
|
||||||
def data_as_text(self, book, col):
|
def data_as_text(self, book, col):
|
||||||
if col == 0:
|
if col == 0:
|
||||||
@ -110,10 +108,33 @@ class ResultsModel(QAbstractTableModel):
|
|||||||
return self.yes_icon
|
return self.yes_icon
|
||||||
if col == 4 and book.comments:
|
if col == 4 and book.comments:
|
||||||
return self.yes_icon
|
return self.yes_icon
|
||||||
|
elif role == Qt.UserRole:
|
||||||
|
return book
|
||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
|
def sort(self, col, order=Qt.AscendingOrder):
|
||||||
|
key = lambda x: x
|
||||||
|
if col == 0:
|
||||||
|
key = attrgetter('gui_rank')
|
||||||
|
elif col == 1:
|
||||||
|
key = attrgetter('title')
|
||||||
|
elif col == 2:
|
||||||
|
key = attrgetter('authors')
|
||||||
|
elif col == 3:
|
||||||
|
key = attrgetter('has_cached_cover_url')
|
||||||
|
elif key == 4:
|
||||||
|
key = lambda x: bool(x.comments)
|
||||||
|
|
||||||
|
self.results.sort(key=key, reverse=order==Qt.AscendingOrder)
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class ResultsView(QTableView): # {{{
|
class ResultsView(QTableView): # {{{
|
||||||
|
|
||||||
|
show_details_signal = pyqtSignal(object)
|
||||||
|
book_selected = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
self.rt_delegate = RichTextDelegate(self)
|
self.rt_delegate = RichTextDelegate(self)
|
||||||
@ -121,6 +142,9 @@ class ResultsView(QTableView): # {{{
|
|||||||
self.setAlternatingRowColors(True)
|
self.setAlternatingRowColors(True)
|
||||||
self.setSelectionBehavior(self.SelectRows)
|
self.setSelectionBehavior(self.SelectRows)
|
||||||
self.setIconSize(QSize(24, 24))
|
self.setIconSize(QSize(24, 24))
|
||||||
|
self.clicked.connect(self.show_details)
|
||||||
|
self.doubleClicked.connect(self.select_index)
|
||||||
|
self.setSortingEnabled(True)
|
||||||
|
|
||||||
def show_results(self, results):
|
def show_results(self, results):
|
||||||
self._model = ResultsModel(results, self)
|
self._model = ResultsModel(results, self)
|
||||||
@ -129,6 +153,38 @@ class ResultsView(QTableView): # {{{
|
|||||||
self.setItemDelegateForColumn(i, self.rt_delegate)
|
self.setItemDelegateForColumn(i, self.rt_delegate)
|
||||||
self.resizeRowsToContents()
|
self.resizeRowsToContents()
|
||||||
self.resizeColumnsToContents()
|
self.resizeColumnsToContents()
|
||||||
|
self.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
def currentChanged(self, current, previous):
|
||||||
|
ret = QTableView.currentChanged(self, current, previous)
|
||||||
|
self.show_details(current)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def show_details(self, index):
|
||||||
|
book = self.model().data(index, Qt.UserRole)
|
||||||
|
parts = [
|
||||||
|
'<center>',
|
||||||
|
'<h2>%s</h2>'%book.title,
|
||||||
|
'<div><i>%s</i></div>'%authors_to_string(book.authors),
|
||||||
|
]
|
||||||
|
if not book.is_null('rating'):
|
||||||
|
parts.append('<div>%s</div>'%('\u2605'*int(book.rating)))
|
||||||
|
parts.append('</center>')
|
||||||
|
if book.tags:
|
||||||
|
parts.append('<div>%s</div><div>\u00a0</div>'%', '.join(book.tags))
|
||||||
|
if book.comments:
|
||||||
|
parts.append(comments_to_html(book.comments))
|
||||||
|
|
||||||
|
self.show_details_signal.emit(''.join(parts))
|
||||||
|
|
||||||
|
def select_index(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
index = self.model().index(0, 0)
|
||||||
|
book = self.model().data(index, Qt.UserRole)
|
||||||
|
self.book_selected.emit(book)
|
||||||
|
|
||||||
|
def get_result(self):
|
||||||
|
self.select_index(self.currentIndex())
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -227,6 +283,8 @@ class IdentifyWorker(Thread): # {{{
|
|||||||
class IdentifyWidget(QWidget): # {{{
|
class IdentifyWidget(QWidget): # {{{
|
||||||
|
|
||||||
rejected = pyqtSignal()
|
rejected = pyqtSignal()
|
||||||
|
results_found = pyqtSignal()
|
||||||
|
book_selected = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, log, parent=None):
|
def __init__(self, log, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -244,11 +302,15 @@ class IdentifyWidget(QWidget): # {{{
|
|||||||
l.addWidget(self.top, 0, 0)
|
l.addWidget(self.top, 0, 0)
|
||||||
|
|
||||||
self.results_view = ResultsView(self)
|
self.results_view = ResultsView(self)
|
||||||
|
self.results_view.book_selected.connect(self.book_selected.emit)
|
||||||
|
self.get_result = self.results_view.get_result
|
||||||
l.addWidget(self.results_view, 1, 0)
|
l.addWidget(self.results_view, 1, 0)
|
||||||
|
|
||||||
self.comments_view = Comments(self)
|
self.comments_view = Comments(self)
|
||||||
l.addWidget(self.comments_view, 1, 1)
|
l.addWidget(self.comments_view, 1, 1)
|
||||||
|
|
||||||
|
self.results_view.show_details_signal.connect(self.comments_view.show_data)
|
||||||
|
|
||||||
self.query = QLabel('download starting...')
|
self.query = QLabel('download starting...')
|
||||||
f = self.query.font()
|
f = self.query.font()
|
||||||
f.setPointSize(f.pointSize()-2)
|
f.setPointSize(f.pointSize()-2)
|
||||||
@ -324,6 +386,13 @@ class IdentifyWidget(QWidget): # {{{
|
|||||||
|
|
||||||
self.results_view.show_results(self.worker.results)
|
self.results_view.show_results(self.worker.results)
|
||||||
|
|
||||||
|
self.comments_view.show_data('''
|
||||||
|
<div style="margin-bottom:2ex">Found <b>%d</b> results</div>
|
||||||
|
<div>To see <b>details</b>, click on any result</div>''' %
|
||||||
|
len(self.worker.results))
|
||||||
|
|
||||||
|
self.results_found.emit()
|
||||||
|
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.abort.set()
|
self.abort.set()
|
||||||
@ -343,23 +412,46 @@ class FullFetch(QDialog): # {{{
|
|||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.addWidget(self.stack)
|
l.addWidget(self.stack)
|
||||||
|
|
||||||
self.bb = QDialogButtonBox(QDialogButtonBox.Cancel)
|
self.bb = QDialogButtonBox(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
|
||||||
l.addWidget(self.bb)
|
l.addWidget(self.bb)
|
||||||
self.bb.rejected.connect(self.reject)
|
self.bb.rejected.connect(self.reject)
|
||||||
|
self.next_button = self.bb.addButton(_('Next'), self.bb.AcceptRole)
|
||||||
|
self.next_button.setDefault(True)
|
||||||
|
self.next_button.setEnabled(False)
|
||||||
|
self.next_button.clicked.connect(self.next_clicked)
|
||||||
|
self.ok_button = self.bb.button(self.bb.Ok)
|
||||||
|
self.ok_button.setVisible(False)
|
||||||
|
self.ok_button.clicked.connect(self.ok_clicked)
|
||||||
|
|
||||||
self.identify_widget = IdentifyWidget(log, self)
|
self.identify_widget = IdentifyWidget(log, self)
|
||||||
self.identify_widget.rejected.connect(self.reject)
|
self.identify_widget.rejected.connect(self.reject)
|
||||||
|
self.identify_widget.results_found.connect(self.identify_results_found)
|
||||||
|
self.identify_widget.book_selected.connect(self.book_selected)
|
||||||
self.stack.addWidget(self.identify_widget)
|
self.stack.addWidget(self.identify_widget)
|
||||||
self.resize(850, 500)
|
self.resize(850, 500)
|
||||||
|
|
||||||
|
def book_selected(self, book):
|
||||||
|
print (book)
|
||||||
|
self.next_button.setVisible(False)
|
||||||
|
self.ok_button.setVisible(True)
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
# Prevent pressing Enter from closing the dialog
|
# Prevent the usual dialog accept mechanisms from working
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
self.identify_widget.cancel()
|
self.identify_widget.cancel()
|
||||||
return QDialog.reject(self)
|
return QDialog.reject(self)
|
||||||
|
|
||||||
|
def identify_results_found(self):
|
||||||
|
self.next_button.setEnabled(True)
|
||||||
|
|
||||||
|
def next_clicked(self, *args):
|
||||||
|
self.identify_widget.get_result()
|
||||||
|
|
||||||
|
def ok_clicked(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
def start(self, title=None, authors=None, identifiers={}):
|
def start(self, title=None, authors=None, identifiers={}):
|
||||||
self.identify_widget.start(title=title, authors=authors,
|
self.identify_widget.start(title=title, authors=authors,
|
||||||
identifiers=identifiers)
|
identifiers=identifiers)
|
||||||
|
@ -13,6 +13,7 @@ from calibre.gui2.preferences import ConfigWidgetBase, test_widget
|
|||||||
from calibre.gui2.preferences.columns_ui import Ui_Form
|
from calibre.gui2.preferences.columns_ui import Ui_Form
|
||||||
from calibre.gui2.preferences.create_custom_column import CreateCustomColumn
|
from calibre.gui2.preferences.create_custom_column import CreateCustomColumn
|
||||||
from calibre.gui2 import error_dialog, question_dialog, ALL_COLUMNS
|
from calibre.gui2 import error_dialog, question_dialog, ALL_COLUMNS
|
||||||
|
from calibre.utils.config import test_eight_code
|
||||||
|
|
||||||
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||||
|
|
||||||
@ -33,6 +34,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
signal = getattr(self.opt_columns, 'item'+signal)
|
signal = getattr(self.opt_columns, 'item'+signal)
|
||||||
signal.connect(self.columns_changed)
|
signal.connect(self.columns_changed)
|
||||||
|
|
||||||
|
if test_eight_code:
|
||||||
|
r = self.register
|
||||||
|
choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')]
|
||||||
|
r('edit_metadata_single_layout', db.prefs, choices=choices)
|
||||||
|
r('bools_are_tristate', db.prefs, restart_required=True)
|
||||||
|
else:
|
||||||
|
self.items_in_v_eight.setVisible(False)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
ConfigWidgetBase.initialize(self)
|
ConfigWidgetBase.initialize(self)
|
||||||
self.init_columns()
|
self.init_columns()
|
||||||
@ -169,6 +178,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
must_restart = True
|
must_restart = True
|
||||||
return must_restart
|
return must_restart
|
||||||
|
|
||||||
|
def refresh_gui(self, gui):
|
||||||
|
gui.library_view.reset()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from PyQt4.Qt import QApplication
|
from PyQt4.Qt import QApplication
|
||||||
|
@ -197,6 +197,67 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<layout class="QVBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="items_in_v_eight">
|
||||||
|
<property name="title">
|
||||||
|
<string>Related Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit metadata layout:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_edit_metadata_single_layout</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_edit_metadata_single_layout">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Choose a different layout for the Edit Metadata dialog. Alternate layouts make it easier to edit custom columns.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Boolean columns are tristate:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_bools_are_tristate</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_bools_are_tristate">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>If checked, boolean columns values can be Yes, No, and Unknown.
|
||||||
|
If not checked, the values can be Yes and No.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<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>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -360,6 +360,7 @@ class Preferences(QMainWindow):
|
|||||||
self.gui.create_device_menu()
|
self.gui.create_device_menu()
|
||||||
self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
|
self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
|
||||||
self.gui.tool_bar.build_bar()
|
self.gui.tool_bar.build_bar()
|
||||||
|
self.gui.menu_bar.build_bar()
|
||||||
self.gui.build_context_menus()
|
self.gui.build_context_menus()
|
||||||
self.gui.tool_bar.apply_settings()
|
self.gui.tool_bar.apply_settings()
|
||||||
|
|
||||||
|
@ -34,9 +34,12 @@ class BaseModel(QAbstractListModel):
|
|||||||
if name == 'Location Manager':
|
if name == 'Location Manager':
|
||||||
return FakeAction(name, None,
|
return FakeAction(name, None,
|
||||||
_('Switch between library and device views'),
|
_('Switch between library and device views'),
|
||||||
dont_remove_from=set(['toolbar-device']))
|
dont_add_to=frozenset(['menubar', 'toolbar',
|
||||||
|
'toolbar-child', 'context-menu',
|
||||||
|
'context-menu-device']))
|
||||||
if name is None:
|
if name is None:
|
||||||
return FakeAction('--- '+_('Separator')+' ---', None)
|
return FakeAction('--- '+_('Separator')+' ---', None,
|
||||||
|
dont_add_to=frozenset(['menubar', 'menubar-device']))
|
||||||
try:
|
try:
|
||||||
return gui.iactions[name]
|
return gui.iactions[name]
|
||||||
except:
|
except:
|
||||||
@ -89,7 +92,7 @@ class AllModel(BaseModel):
|
|||||||
self._data = self.get_all_actions(current)
|
self._data = self.get_all_actions(current)
|
||||||
|
|
||||||
def get_all_actions(self, current):
|
def get_all_actions(self, current):
|
||||||
all = list(self.gui.iactions.keys()) + ['Donate']
|
all = list(self.gui.iactions.keys()) + ['Donate', 'Location Manager']
|
||||||
all = [x for x in all if x not in current] + [None]
|
all = [x for x in all if x not in current] + [None]
|
||||||
all = [self.name_to_action(x, self.gui) for x in all]
|
all = [self.name_to_action(x, self.gui) for x in all]
|
||||||
all = [x for x in all if self.key not in x.dont_add_to]
|
all = [x for x in all if self.key not in x.dont_add_to]
|
||||||
@ -208,12 +211,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
|
|
||||||
LOCATIONS = [
|
LOCATIONS = [
|
||||||
('toolbar', _('The main toolbar')),
|
('toolbar', _('The main toolbar')),
|
||||||
('toolbar-child', _('The optional second toolbar')),
|
|
||||||
('toolbar-device', _('The main toolbar when a device is connected')),
|
('toolbar-device', _('The main toolbar when a device is connected')),
|
||||||
|
('toolbar-child', _('The optional second toolbar')),
|
||||||
|
('menubar', _('The menubar')),
|
||||||
|
('menubar-device', _('The menubar when a device is connected')),
|
||||||
('context-menu', _('The context menu for the books in the '
|
('context-menu', _('The context menu for the books in the '
|
||||||
'calibre library')),
|
'calibre library')),
|
||||||
('context-menu-device', _('The context menu for the books on '
|
('context-menu-device', _('The context menu for the books on '
|
||||||
'the device'))
|
'the device')),
|
||||||
]
|
]
|
||||||
|
|
||||||
def genesis(self, gui):
|
def genesis(self, gui):
|
||||||
@ -284,6 +289,21 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
|
# Ensure preferences are showing in either the toolbar or
|
||||||
|
# the menubar.
|
||||||
|
pref_in_toolbar = lm_in_toolbar = False
|
||||||
|
cm = self.models['toolbar']
|
||||||
|
for x in cm[1]._data:
|
||||||
|
if x.name == 'Preferences':
|
||||||
|
pref_in_toolbar = True
|
||||||
|
if x.name == 'Location Manager':
|
||||||
|
lm_in_toolbar = True
|
||||||
|
if not pref_in_toolbar:
|
||||||
|
self.models['menubar'][1].add(['Preferences'])
|
||||||
|
if not lm_in_toolbar:
|
||||||
|
self.models['menubar-device'][1].add(['Location Manager'])
|
||||||
|
|
||||||
|
# Save data.
|
||||||
for am, cm in self.models.values():
|
for am, cm in self.models.values():
|
||||||
cm.commit()
|
cm.commit()
|
||||||
return False
|
return False
|
||||||
|
@ -153,6 +153,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
|
|
||||||
for ac in self.iactions.values():
|
for ac in self.iactions.values():
|
||||||
ac.do_genesis()
|
ac.do_genesis()
|
||||||
|
self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self)
|
||||||
MainWindowMixin.__init__(self, db)
|
MainWindowMixin.__init__(self, db)
|
||||||
|
|
||||||
# Jobs Button {{{
|
# Jobs Button {{{
|
||||||
@ -186,8 +187,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.system_tray_menu = QMenu(self)
|
self.system_tray_menu = QMenu(self)
|
||||||
self.restore_action = self.system_tray_menu.addAction(
|
self.restore_action = self.system_tray_menu.addAction(
|
||||||
QIcon(I('page.png')), _('&Restore'))
|
QIcon(I('page.png')), _('&Restore'))
|
||||||
self.donate_action = self.system_tray_menu.addAction(
|
self.system_tray_menu.addAction(self.donate_action)
|
||||||
QIcon(I('donate.png')), _('&Donate to support calibre'))
|
|
||||||
self.donate_button.setDefaultAction(self.donate_action)
|
self.donate_button.setDefaultAction(self.donate_action)
|
||||||
self.donate_button.setStatusTip(self.donate_button.toolTip())
|
self.donate_button.setStatusTip(self.donate_button.toolTip())
|
||||||
self.eject_action = self.system_tray_menu.addAction(
|
self.eject_action = self.system_tray_menu.addAction(
|
||||||
|
@ -547,7 +547,7 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
return matchkind, query
|
return matchkind, query
|
||||||
|
|
||||||
def get_bool_matches(self, location, query, candidates):
|
def get_bool_matches(self, location, query, candidates):
|
||||||
bools_are_tristate = tweaks['bool_custom_columns_are_tristate'] != 'no'
|
bools_are_tristate = not self.db_prefs.get('bools_are_tristate')
|
||||||
loc = self.field_metadata[location]['rec_index']
|
loc = self.field_metadata[location]['rec_index']
|
||||||
matches = set()
|
matches = set()
|
||||||
query = icu_lower(query)
|
query = icu_lower(query)
|
||||||
@ -947,7 +947,7 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
if not fields:
|
if not fields:
|
||||||
fields = [('timestamp', False)]
|
fields = [('timestamp', False)]
|
||||||
|
|
||||||
keyg = SortKeyGenerator(fields, self.field_metadata, self._data)
|
keyg = SortKeyGenerator(fields, self.field_metadata, self._data, self.db_prefs)
|
||||||
self._map.sort(key=keyg)
|
self._map.sort(key=keyg)
|
||||||
|
|
||||||
tmap = list(itertools.repeat(False, len(self._data)))
|
tmap = list(itertools.repeat(False, len(self._data)))
|
||||||
@ -970,9 +970,10 @@ class SortKey(object):
|
|||||||
|
|
||||||
class SortKeyGenerator(object):
|
class SortKeyGenerator(object):
|
||||||
|
|
||||||
def __init__(self, fields, field_metadata, data):
|
def __init__(self, fields, field_metadata, data, db_prefs):
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
self.field_metadata = field_metadata
|
self.field_metadata = field_metadata
|
||||||
|
self.db_prefs = db_prefs
|
||||||
self.orders = [1 if x[1] else -1 for x in fields]
|
self.orders = [1 if x[1] else -1 for x in fields]
|
||||||
self.entries = [(x[0], field_metadata[x[0]]) for x in fields]
|
self.entries = [(x[0], field_metadata[x[0]]) for x in fields]
|
||||||
self.library_order = tweaks['title_series_sorting'] == 'library_order'
|
self.library_order = tweaks['title_series_sorting'] == 'library_order'
|
||||||
@ -1032,7 +1033,7 @@ class SortKeyGenerator(object):
|
|||||||
val = self.string_sort_key(val)
|
val = self.string_sort_key(val)
|
||||||
|
|
||||||
elif dt == 'bool':
|
elif dt == 'bool':
|
||||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
if not self.db_prefs.get('bools_are_tristate'):
|
||||||
val = {True: 1, False: 2, None: 2}.get(val, 2)
|
val = {True: 1, False: 2, None: 2}.get(val, 2)
|
||||||
else:
|
else:
|
||||||
val = {True: 1, False: 2, None: 3}.get(val, 3)
|
val = {True: 1, False: 2, None: 3}.get(val, 3)
|
||||||
|
@ -40,7 +40,7 @@ from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
|||||||
from calibre.utils.magick.draw import save_cover_data_to
|
from calibre.utils.magick.draw import save_cover_data_to
|
||||||
from calibre.utils.recycle_bin import delete_file, delete_tree
|
from calibre.utils.recycle_bin import delete_file, delete_tree
|
||||||
from calibre.utils.formatter_functions import load_user_template_functions
|
from calibre.utils.formatter_functions import load_user_template_functions
|
||||||
|
from calibre.utils.config import test_eight_code
|
||||||
|
|
||||||
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||||
|
|
||||||
@ -213,6 +213,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
defs = self.prefs.defaults
|
defs = self.prefs.defaults
|
||||||
defs['gui_restriction'] = defs['cs_restriction'] = ''
|
defs['gui_restriction'] = defs['cs_restriction'] = ''
|
||||||
defs['categories_using_hierarchy'] = []
|
defs['categories_using_hierarchy'] = []
|
||||||
|
defs['edit_metadata_single_layout'] = 'default'
|
||||||
|
|
||||||
|
defs['bools_are_tristate'] = \
|
||||||
|
tweaks.get('bool_custom_columns_are_tristate', 'yes') == 'yes'
|
||||||
|
if self.prefs.get('bools_are_tristate') is None or not test_eight_code:
|
||||||
|
self.prefs.set('bools_are_tristate', defs['bools_are_tristate'])
|
||||||
|
|
||||||
# Migrate saved search and user categories to db preference scheme
|
# Migrate saved search and user categories to db preference scheme
|
||||||
def migrate_preference(key, default):
|
def migrate_preference(key, default):
|
||||||
|
@ -17,8 +17,8 @@ from calibre.utils.magick.draw import save_cover_data_to, Image, \
|
|||||||
|
|
||||||
class CSSortKeyGenerator(SortKeyGenerator):
|
class CSSortKeyGenerator(SortKeyGenerator):
|
||||||
|
|
||||||
def __init__(self, fields, fm):
|
def __init__(self, fields, fm, db_prefs):
|
||||||
SortKeyGenerator.__init__(self, fields, fm, None)
|
SortKeyGenerator.__init__(self, fields, fm, None, db_prefs)
|
||||||
|
|
||||||
def __call__(self, record):
|
def __call__(self, record):
|
||||||
return self.itervals(record).next()
|
return self.itervals(record).next()
|
||||||
@ -56,7 +56,8 @@ class ContentServer(object):
|
|||||||
field = self.db.data.sanitize_sort_field_name(field)
|
field = self.db.data.sanitize_sort_field_name(field)
|
||||||
if field not in self.db.field_metadata.sortable_field_keys():
|
if field not in self.db.field_metadata.sortable_field_keys():
|
||||||
raise cherrypy.HTTPError(400, '%s is not a valid sort field'%field)
|
raise cherrypy.HTTPError(400, '%s is not a valid sort field'%field)
|
||||||
keyg = CSSortKeyGenerator([(field, order)], self.db.field_metadata)
|
keyg = CSSortKeyGenerator([(field, order)], self.db.field_metadata,
|
||||||
|
self.db.prefs)
|
||||||
items.sort(key=keyg, reverse=not order)
|
items.sort(key=keyg, reverse=not order)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
@ -197,7 +197,7 @@ Once you've located the zip file of your plugin you can then directly update it
|
|||||||
|
|
||||||
zip -R /path/to/plugin/zip/file.zip *
|
zip -R /path/to/plugin/zip/file.zip *
|
||||||
|
|
||||||
This will automatically update all changed files. It relies on the freely available zip command line tool.
|
This will update all changed files. It relies on the freely available zip command line tool. Note that you should quit calibre before running this command.
|
||||||
|
|
||||||
More plugin examples
|
More plugin examples
|
||||||
----------------------
|
----------------------
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -785,6 +785,9 @@ def write_tweaks(raw):
|
|||||||
|
|
||||||
tweaks = read_tweaks()
|
tweaks = read_tweaks()
|
||||||
test_eight_code = tweaks.get('test_eight_code', False)
|
test_eight_code = tweaks.get('test_eight_code', False)
|
||||||
|
# test_eight_code notes
|
||||||
|
# Change documentation of bool columns are tristate to indicate that it can be
|
||||||
|
# overridden on a per library basis via Preferences->Custom columns
|
||||||
|
|
||||||
def migrate():
|
def migrate():
|
||||||
if hasattr(os, 'geteuid') and os.geteuid() == 0:
|
if hasattr(os, 'geteuid') and os.geteuid() == 0:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user