KG patches to 0.7.36

This commit is contained in:
GRiker 2011-01-02 11:56:29 -07:00
commit 41f04f34e9
13 changed files with 132 additions and 44 deletions

View File

@ -5,7 +5,7 @@
# Also, each release can have new and improved recipes. # Also, each release can have new and improved recipes.
- version: 0.7.36 - version: 0.7.36
date: 2010-01-01 date: 2011-01-01
new features: new features:
- title: "Tag browser: Add subcategories and search" - title: "Tag browser: Add subcategories and search"

View File

@ -55,16 +55,9 @@ author_sort_copy_method = 'invert'
# categories_use_field_for_author_name = 'author_sort' # categories_use_field_for_author_name = 'author_sort'
categories_use_field_for_author_name = 'author' categories_use_field_for_author_name = 'author'
# Control how the tags pane displays categories containing many items. If the # When partitioning the tags browser, the format of the subcategory label is
# number of items is larger than categories_collapse_more_than, a sub-category # controlled by a template: categories_collapsed_name_template if sorting by
# will be added. If sorting by name, then the subcategories can be organized by # name, categories_collapsed_rating_template if sorting by average rating, and
# first letter (categories_collapse_model = 'first letter') or into equal-sized
# groups (categories_collapse_model = 'partition'). If sorting by average rating
# or by popularity, then 'partition' is always used. The addition of
# subcategories can be disabled by setting categories_collapse_more_than = 0.
# When using partition, the format of the subcategory label is controlled by a
# template: categories_collapsed_name_template if sorting by name,
# categories_collapsed_rating_template if sorting by average rating, and
# categories_collapsed_popularity_template if sorting by popularity. There are # categories_collapsed_popularity_template if sorting by popularity. There are
# two variables available to the template: first and last. The variable 'first' # two variables available to the template: first and last. The variable 'first'
# is the initial item in the subcategory, and the variable 'last' is the final # is the initial item in the subcategory, and the variable 'last' is the final
@ -76,11 +69,10 @@ categories_use_field_for_author_name = 'author'
# avg_rating: the averate rating of all the books referencing this item # avg_rating: the averate rating of all the books referencing this item
# sort: the sort value. For authors, this is the author_sort for that author # sort: the sort value. For authors, this is the author_sort for that author
# category: the category (e.g., authors, series) that the item is in. # category: the category (e.g., authors, series) that the item is in.
categories_collapse_more_than = 50 categories_collapsed_name_template = '{first.sort:shorten(4,'',0)} - {last.sort:shorten(4,'',0)}'
categories_collapsed_name_template = '{first.name:shorten(4,'',0)} - {last.name::shorten(4,'',0)}'
categories_collapsed_rating_template = '{first.avg_rating:4.2f:ifempty(0)} - {last.avg_rating:4.2f:ifempty(0)}' categories_collapsed_rating_template = '{first.avg_rating:4.2f:ifempty(0)} - {last.avg_rating:4.2f:ifempty(0)}'
categories_collapsed_popularity_template = '{first.count:d} - {last.count:d}' categories_collapsed_popularity_template = '{first.count:d} - {last.count:d}'
categories_collapse_model = 'first letter'
# Set whether boolean custom columns are two- or three-valued. # Set whether boolean custom columns are two- or three-valued.
# Two-values for true booleans # Two-values for true booleans

View File

@ -30,6 +30,12 @@ class Drive(str):
typ.order = order typ.order = order
return typ return typ
def drivecmp(a, b):
ans = cmp(getattr(a, 'order', 0), getattr(b, 'order', 0))
if ans == 0:
ans = cmp(a, b)
return ans
class WinPNPScanner(object): class WinPNPScanner(object):
@ -57,7 +63,13 @@ class WinPNPScanner(object):
order = 0 order = 0
match = re.search(r'REV_.*?&(\d+)#', pnp_id) match = re.search(r'REV_.*?&(\d+)#', pnp_id)
if match is None: if match is None:
match = re.search(r'REV_.*?&(\d+)', pnp_id) # Windows XP
# On the Nook Color this is the last digit
#
# USBSTOR\DISK&VEN_B&N&PROD_EBOOK_DISK&REV_0100\7&13EAFDB8&0&2004760017462009&1
# USBSTOR\DISK&VEN_B&N&PROD_EBOOK_DISK&REV_0100\7&13EAFDB8&0&2004760017462009&0
#
match = re.search(r'REV_.*&(\d+)', pnp_id)
if match is not None: if match is not None:
order = int(match.group(1)) order = int(match.group(1))
return order return order

View File

@ -11,7 +11,7 @@ intended to be subclassed with the relevant parts implemented for a particular
device. This class handles device detection. device. This class handles device detection.
''' '''
import os, subprocess, time, re, sys, glob, operator import os, subprocess, time, re, sys, glob
from itertools import repeat from itertools import repeat
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
@ -225,7 +225,7 @@ class Device(DeviceConfig, DevicePlugin):
return False return False
def open_windows(self): def open_windows(self):
from calibre.devices.scanner import win_pnp_drives from calibre.devices.scanner import win_pnp_drives, drivecmp
time.sleep(5) time.sleep(5)
drives = {} drives = {}
@ -263,7 +263,7 @@ class Device(DeviceConfig, DevicePlugin):
if self.WINDOWS_MAIN_MEM in (self.WINDOWS_CARD_A_MEM, if self.WINDOWS_MAIN_MEM in (self.WINDOWS_CARD_A_MEM,
self.WINDOWS_CARD_B_MEM) or \ self.WINDOWS_CARD_B_MEM) or \
self.WINDOWS_CARD_A_MEM == self.WINDOWS_CARD_B_MEM: self.WINDOWS_CARD_A_MEM == self.WINDOWS_CARD_B_MEM:
letters = sorted(drives.values(), key=operator.attrgetter('order')) letters = sorted(drives.values(), cmp=drivecmp)
drives = {} drives = {}
for which, letter in zip(['main', 'carda', 'cardb'], letters): for which, letter in zip(['main', 'carda', 'cardb'], letters):
drives[which] = letter drives[which] = letter

View File

@ -175,7 +175,7 @@ class PDFWriter(QObject): # {{{
if self.cover_data is None: if self.cover_data is None:
return return
item_path = os.path.join(self.tmp_path, 'cover.pdf') item_path = os.path.join(self.tmp_path, 'cover.pdf')
printer = self.get_printer() printer = get_pdf_printer(self.opts)
printer.setOutputFileName(item_path) printer.setOutputFileName(item_path)
self.combine_queue.insert(0, item_path) self.combine_queue.insert(0, item_path)
p = QPixmap() p = QPixmap()

View File

@ -53,6 +53,8 @@ gprefs.defaults['toolbar_icon_size'] = 'medium'
gprefs.defaults['toolbar_text'] = 'auto' gprefs.defaults['toolbar_text'] = 'auto'
gprefs.defaults['show_child_bar'] = False gprefs.defaults['show_child_bar'] = False
gprefs.defaults['font'] = None gprefs.defaults['font'] = None
gprefs.defaults['tags_browser_partition_method'] = 'first letter'
gprefs.defaults['tags_browser_collapse_at'] = 50
# }}} # }}}

View File

@ -23,7 +23,7 @@ class BookInfo(QDialog, Ui_BookInfo):
self.cover_pixmap = None self.cover_pixmap = None
self.comments.sizeHint = self.comments_size_hint self.comments.sizeHint = self.comments_size_hint
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks) self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
self.comments.linkClicked(self.link_clicked) self.comments.linkClicked.connect(self.link_clicked)
self.view_func = view_func self.view_func = view_func

View File

@ -386,7 +386,12 @@ class BooksView(QTableView): # {{{
old_state = self.get_default_state() old_state = self.get_default_state()
if tweaks['sort_columns_at_startup'] is not None: if tweaks['sort_columns_at_startup'] is not None:
old_state['sort_history'] = tweaks['sort_columns_at_startup'] sh = []
for c,d in tweaks['sort_columns_at_startup']:
if not isinstance(d, bool):
d = True if d == 0 else False
sh.append((c, d))
old_state['sort_history'] = sh
self.apply_state(old_state) self.apply_state(old_state)

View File

@ -303,7 +303,7 @@ def run_gui(opts, args, actions, listener, app, gui_debug=None):
runner.main.system_tray_icon.hide() runner.main.system_tray_icon.hide()
except: except:
pass pass
if runner.main.gui_debug is not None: if getattr(runner.main, 'gui_debug', None) is not None:
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0] e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
import subprocess import subprocess
creationflags = 0 creationflags = 0

View File

@ -57,6 +57,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
(_('Never'), 'never')] (_('Never'), 'never')]
r('toolbar_text', gprefs, choices=choices) r('toolbar_text', gprefs, choices=choices)
choices = [(_('Disabled'), 'disabled'), (_('By first letter'), 'first letter'),
(_('Partitioned'), 'partition')]
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
self.current_font = None self.current_font = None
self.change_font_button.clicked.connect(self.change_font) self.change_font_button.clicked.connect(self.change_font)
@ -113,6 +118,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def refresh_gui(self, gui): def refresh_gui(self, gui):
gui.search.search_as_you_type(config['search_as_you_type']) gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display() self.update_font_display()
gui.tags_view.reread_collapse_parameters()
if __name__ == '__main__': if __name__ == '__main__':
app = QApplication([]) app = QApplication([])

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>670</width> <width>670</width>
<height>385</height> <height>420</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -141,7 +141,37 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2"> <item row="7" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tags browser: partitioning method:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_partition_method</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="opt_tags_browser_partition_method"/>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tags browser - collapse when more items than:</string>
</property>
<property name="buddy">
<cstring>opt_tags_browser_collapse_at</cstring>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
<item row="15" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
<string>&amp;Toolbar</string> <string>&amp;Toolbar</string>
@ -183,7 +213,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="16" column="0">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
@ -204,14 +234,14 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="1"> <item row="16" column="1">
<widget class="QPushButton" name="change_font_button"> <widget class="QPushButton" name="change_font_button">
<property name="text"> <property name="text">
<string>Change &amp;font (needs restart)</string> <string>Change &amp;font (needs restart)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0" colspan="2"> <item row="17" column="0" colspan="2">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>

View File

@ -17,7 +17,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QFont, QSize, \
QShortcut, QKeySequence, SIGNAL QShortcut, QKeySequence, SIGNAL
from calibre.ebooks.metadata import title_sort from calibre.ebooks.metadata import title_sort
from calibre.gui2 import config, NONE from calibre.gui2 import config, NONE, gprefs
from calibre.library.field_metadata import TagsIcons, category_icon_map from calibre.library.field_metadata import TagsIcons, category_icon_map
from calibre.utils.config import tweaks from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key, upper, lower, strcmp from calibre.utils.icu import sort_key, upper, lower, strcmp
@ -94,6 +94,10 @@ class TagsView(QTreeView): # {{{
self.setDropIndicatorShown(True) self.setDropIndicatorShown(True)
self.setAutoExpandDelay(500) self.setAutoExpandDelay(500)
self.pane_is_visible = False self.pane_is_visible = False
if gprefs['tags_browser_collapse_at'] == 0:
self.collapse_model = 'disable'
else:
self.collapse_model = gprefs['tags_browser_partition_method']
def set_pane_is_visible(self, to_what): def set_pane_is_visible(self, to_what):
pv = self.pane_is_visible pv = self.pane_is_visible
@ -101,12 +105,20 @@ class TagsView(QTreeView): # {{{
if to_what and not pv: if to_what and not pv:
self.recount() self.recount()
def reread_collapse_parameters(self):
if gprefs['tags_browser_collapse_at'] == 0:
self.collapse_model = 'disable'
else:
self.collapse_model = gprefs['tags_browser_partition_method']
self.set_new_model(self._model.get_filter_categories_by())
def set_database(self, db, tag_match, sort_by): def set_database(self, db, tag_match, sort_by):
self.hidden_categories = config['tag_browser_hidden_categories'] self.hidden_categories = config['tag_browser_hidden_categories']
self._model = TagsModel(db, parent=self, self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=None, search_restriction=None,
drag_drop_finished=self.drag_drop_finished) drag_drop_finished=self.drag_drop_finished,
collapse_model=self.collapse_model)
self.pane_is_visible = True # because TagsModel.init did a recount self.pane_is_visible = True # because TagsModel.init did a recount
self.sort_by = sort_by self.sort_by = sort_by
self.tag_match = tag_match self.tag_match = tag_match
@ -194,6 +206,12 @@ class TagsView(QTreeView): # {{{
self.hidden_categories.add(category) self.hidden_categories.add(category)
elif action == 'show': elif action == 'show':
self.hidden_categories.discard(category) self.hidden_categories.discard(category)
elif action == 'categorization':
changed = self.collapse_model != category
self.collapse_model = category
if changed:
self.set_new_model(self._model.get_filter_categories_by())
gprefs['tags_browser_partition_method'] = category
elif action == 'defaults': elif action == 'defaults':
self.hidden_categories.clear() self.hidden_categories.clear()
config.set('tag_browser_hidden_categories', self.hidden_categories) config.set('tag_browser_hidden_categories', self.hidden_categories)
@ -216,6 +234,8 @@ class TagsView(QTreeView): # {{{
item = item.parent item = item.parent
if item.type == TagTreeItem.CATEGORY: if item.type == TagTreeItem.CATEGORY:
while item.parent != self._model.root_item:
item = item.parent
category = unicode(item.name.toString()) category = unicode(item.name.toString())
key = item.category_key key = item.category_key
# Verify that we are working with a field that we know something about # Verify that we are working with a field that we know something about
@ -277,6 +297,23 @@ class TagsView(QTreeView): # {{{
self.context_menu.addAction(_('Show all categories'), self.context_menu.addAction(_('Show all categories'),
partial(self.context_menu_handler, action='defaults')) partial(self.context_menu_handler, action='defaults'))
m = self.context_menu.addMenu(_('Change sub-categorization scheme'))
da = m.addAction('Disable',
partial(self.context_menu_handler, action='categorization', category='disable'))
fla = m.addAction('By first letter',
partial(self.context_menu_handler, action='categorization', category='first letter'))
pa = m.addAction('Partition',
partial(self.context_menu_handler, action='categorization', category='partition'))
if self.collapse_model == 'disable':
da.setCheckable(True)
da.setChecked(True)
elif self.collapse_model == 'first letter':
fla.setCheckable(True)
fla.setChecked(True)
else:
pa.setCheckable(True)
pa.setChecked(True)
if not self.context_menu.isEmpty(): if not self.context_menu.isEmpty():
self.context_menu.popup(self.mapToGlobal(point)) self.context_menu.popup(self.mapToGlobal(point))
return True return True
@ -338,7 +375,8 @@ class TagsView(QTreeView): # {{{
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=self.search_restriction, search_restriction=self.search_restriction,
drag_drop_finished=self.drag_drop_finished, drag_drop_finished=self.drag_drop_finished,
filter_categories_by=filter_categories_by) filter_categories_by=filter_categories_by,
collapse_model=self.collapse_model)
self.setModel(self._model) self.setModel(self._model)
except: except:
# The DB must be gone. Set the model to None and hope that someone # The DB must be gone. Set the model to None and hope that someone
@ -467,7 +505,7 @@ class TagsModel(QAbstractItemModel): # {{{
def __init__(self, db, parent, hidden_categories=None, def __init__(self, db, parent, hidden_categories=None,
search_restriction=None, drag_drop_finished=None, search_restriction=None, drag_drop_finished=None,
filter_categories_by=None): filter_categories_by=None, collapse_model='disable'):
QAbstractItemModel.__init__(self, parent) QAbstractItemModel.__init__(self, parent)
# must do this here because 'QPixmap: Must construct a QApplication # must do this here because 'QPixmap: Must construct a QApplication
@ -488,6 +526,7 @@ class TagsModel(QAbstractItemModel): # {{{
self.search_restriction = search_restriction self.search_restriction = search_restriction
self.row_map = [] self.row_map = []
self.filter_categories_by = filter_categories_by self.filter_categories_by = filter_categories_by
self.collapse_model = collapse_model
# get_node_tree cannot return None here, because row_map is empty # get_node_tree cannot return None here, because row_map is empty
data = self.get_node_tree(config['sort_tags_by']) data = self.get_node_tree(config['sort_tags_by'])
@ -678,16 +717,19 @@ class TagsModel(QAbstractItemModel): # {{{
if data is None: if data is None:
return False return False
row_index = -1 row_index = -1
collapse = tweaks['categories_collapse_more_than'] collapse = gprefs['tags_browser_collapse_at']
collapse_model = tweaks['categories_collapse_model'] collapse_model = self.collapse_model
if sort_by == 'name': if collapse == 0:
collapse_template = tweaks['categories_collapsed_name_template'] collapse_model = 'disable'
elif sort_by == 'rating': elif collapse_model != 'disable':
collapse_model = 'partition' if sort_by == 'name':
collapse_template = tweaks['categories_collapsed_rating_template'] collapse_template = tweaks['categories_collapsed_name_template']
else: elif sort_by == 'rating':
collapse_model = 'partition' collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_popularity_template'] collapse_template = tweaks['categories_collapsed_rating_template']
else:
collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_popularity_template']
collapse_letter = None collapse_letter = None
for i, r in enumerate(self.row_map): for i, r in enumerate(self.row_map):
@ -722,7 +764,7 @@ class TagsModel(QAbstractItemModel): # {{{
tag.avg_rating = None tag.avg_rating = None
tag.state = state_map.get(tag.name, 0) tag.state = state_map.get(tag.name, 0)
if collapse > 0 and cat_len > collapse: if collapse_model != 'disable' and cat_len > collapse:
if collapse_model == 'partition': if collapse_model == 'partition':
if (idx % collapse) == 0: if (idx % collapse) == 0:
d = {'first': tag} d = {'first': tag}
@ -738,7 +780,7 @@ class TagsModel(QAbstractItemModel): # {{{
category_key=category_node.category_key) category_key=category_node.category_key)
else: else:
if upper(tag.sort[0]) != collapse_letter: if upper(tag.sort[0]) != collapse_letter:
collapse_letter = upper(tag.name[0]) collapse_letter = upper(tag.sort[0])
sub_cat = TagTreeItem(parent=category, sub_cat = TagTreeItem(parent=category,
data = collapse_letter, data = collapse_letter,
category_icon = category_node.icon, category_icon = category_node.icon,

View File

@ -1930,7 +1930,6 @@ class EPUB_MOBI(CatalogPlugin):
# Loop through booksByAuthor # Loop through booksByAuthor
book_count = 0 book_count = 0
current_author = '' current_author = ''
previous_author = ''
current_letter = '' current_letter = ''
current_series = None current_series = None
for book in self.booksByAuthor: for book in self.booksByAuthor: