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.
- version: 0.7.36
date: 2010-01-01
date: 2011-01-01
new features:
- title: "Tag browser: Add subcategories and search"

View file

@ -55,16 +55,9 @@
# categories_use_field_for_author_name = 'author_sort'
categories_use_field_for_author_name = 'author'
# Control how the tags pane displays categories containing many items. If the
# number of items is larger than categories_collapse_more_than, a sub-category
# will be added. If sorting by name, then the subcategories can be organized by
# 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
# When partitioning the tags browser, 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
# 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
@ -76,11 +69,10 @@
# 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
# category: the category (e.g., authors, series) that the item is in.
categories_collapse_more_than = 50
categories_collapsed_name_template = '{first.name:shorten(4,'',0)} - {last.name::shorten(4,'',0)}'
categories_collapsed_name_template = '{first.sort:shorten(4,'',0)} - {last.sort:shorten(4,'',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_collapse_model = 'first letter'
# Set whether boolean custom columns are two- or three-valued.
# Two-values for true booleans

View file

@ -30,6 +30,12 @@ def __new__(self, val, order=0):
typ.order = order
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):
@ -57,7 +63,13 @@ def drive_order(self, pnp_id):
order = 0
match = re.search(r'REV_.*?&(\d+)#', pnp_id)
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:
order = int(match.group(1))
return order

View file

@ -11,7 +11,7 @@
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 calibre.devices.interface import DevicePlugin
@ -225,7 +225,7 @@ def can_handle_windows(self, device_id, debug=False):
return False
def open_windows(self):
from calibre.devices.scanner import win_pnp_drives
from calibre.devices.scanner import win_pnp_drives, drivecmp
time.sleep(5)
drives = {}
@ -263,7 +263,7 @@ def open_windows(self):
if self.WINDOWS_MAIN_MEM in (self.WINDOWS_CARD_A_MEM,
self.WINDOWS_CARD_B_MEM) or \
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 = {}
for which, letter in zip(['main', 'carda', 'cardb'], letters):
drives[which] = letter

View file

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

View file

@ -53,6 +53,8 @@
gprefs.defaults['toolbar_text'] = 'auto'
gprefs.defaults['show_child_bar'] = False
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 @@ def __init__(self, parent, view, row, view_func):
self.cover_pixmap = None
self.comments.sizeHint = self.comments_size_hint
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

View file

@ -386,7 +386,12 @@ def restore_state(self):
old_state = self.get_default_state()
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)

View file

@ -303,7 +303,7 @@ def run_gui(opts, args, actions, listener, app, gui_debug=None):
runner.main.system_tray_icon.hide()
except:
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]
import subprocess
creationflags = 0

View file

@ -57,6 +57,11 @@ def genesis(self, gui):
(_('Never'), 'never')]
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.change_font_button.clicked.connect(self.change_font)
@ -113,6 +118,7 @@ def commit(self, *args):
def refresh_gui(self, gui):
gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
if __name__ == '__main__':
app = QApplication([])

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>670</width>
<height>385</height>
<height>420</height>
</rect>
</property>
<property name="windowTitle">
@ -141,7 +141,37 @@
</property>
</widget>
</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">
<property name="title">
<string>&amp;Toolbar</string>
@ -183,7 +213,7 @@
</layout>
</widget>
</item>
<item row="8" column="0">
<item row="16" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
@ -204,14 +234,14 @@
</item>
</layout>
</item>
<item row="8" column="1">
<item row="16" column="1">
<widget class="QPushButton" name="change_font_button">
<property name="text">
<string>Change &amp;font (needs restart)</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="17" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>

View file

@ -17,7 +17,7 @@
QShortcut, QKeySequence, SIGNAL
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.utils.config import tweaks
from calibre.utils.icu import sort_key, upper, lower, strcmp
@ -94,6 +94,10 @@ def __init__(self, parent=None):
self.setDropIndicatorShown(True)
self.setAutoExpandDelay(500)
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):
pv = self.pane_is_visible
@ -101,12 +105,20 @@ def set_pane_is_visible(self, to_what):
if to_what and not pv:
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):
self.hidden_categories = config['tag_browser_hidden_categories']
self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories,
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.sort_by = sort_by
self.tag_match = tag_match
@ -194,6 +206,12 @@ def context_menu_handler(self, action=None, category=None,
self.hidden_categories.add(category)
elif action == 'show':
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':
self.hidden_categories.clear()
config.set('tag_browser_hidden_categories', self.hidden_categories)
@ -216,6 +234,8 @@ def show_context_menu(self, point):
item = item.parent
if item.type == TagTreeItem.CATEGORY:
while item.parent != self._model.root_item:
item = item.parent
category = unicode(item.name.toString())
key = item.category_key
# Verify that we are working with a field that we know something about
@ -277,6 +297,23 @@ def show_context_menu(self, point):
self.context_menu.addAction(_('Show all categories'),
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():
self.context_menu.popup(self.mapToGlobal(point))
return True
@ -338,7 +375,8 @@ def set_new_model(self, filter_categories_by=None):
hidden_categories=self.hidden_categories,
search_restriction=self.search_restriction,
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)
except:
# 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,
search_restriction=None, drag_drop_finished=None,
filter_categories_by=None):
filter_categories_by=None, collapse_model='disable'):
QAbstractItemModel.__init__(self, parent)
# must do this here because 'QPixmap: Must construct a QApplication
@ -488,6 +526,7 @@ def __init__(self, db, parent, hidden_categories=None,
self.search_restriction = search_restriction
self.row_map = []
self.filter_categories_by = filter_categories_by
self.collapse_model = collapse_model
# get_node_tree cannot return None here, because row_map is empty
data = self.get_node_tree(config['sort_tags_by'])
@ -678,16 +717,19 @@ def refresh(self, data=None):
if data is None:
return False
row_index = -1
collapse = tweaks['categories_collapse_more_than']
collapse_model = tweaks['categories_collapse_model']
if sort_by == 'name':
collapse_template = tweaks['categories_collapsed_name_template']
elif sort_by == 'rating':
collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_rating_template']
else:
collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_popularity_template']
collapse = gprefs['tags_browser_collapse_at']
collapse_model = self.collapse_model
if collapse == 0:
collapse_model = 'disable'
elif collapse_model != 'disable':
if sort_by == 'name':
collapse_template = tweaks['categories_collapsed_name_template']
elif sort_by == 'rating':
collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_rating_template']
else:
collapse_model = 'partition'
collapse_template = tweaks['categories_collapsed_popularity_template']
collapse_letter = None
for i, r in enumerate(self.row_map):
@ -722,7 +764,7 @@ def refresh(self, data=None):
tag.avg_rating = None
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 (idx % collapse) == 0:
d = {'first': tag}
@ -738,7 +780,7 @@ def refresh(self, data=None):
category_key=category_node.category_key)
else:
if upper(tag.sort[0]) != collapse_letter:
collapse_letter = upper(tag.name[0])
collapse_letter = upper(tag.sort[0])
sub_cat = TagTreeItem(parent=category,
data = collapse_letter,
category_icon = category_node.icon,

View file

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