KG updates

This commit is contained in:
GRiker 2011-01-24 06:01:53 -07:00
commit a10327e0bc
11 changed files with 281 additions and 46 deletions

View File

@ -0,0 +1,36 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1295088390(BasicNewsRecipe):
title = u'Everett Herald'
language = 'en'
__author__ = '77ja65'
oldest_article = 4
max_articles_per_feed = 50
no_stylesheets = True
masthead_url = 'http://heraldnet.com/images/hnet/jQueryComponents/jQueryNavigation/heraldnet_logo.png'
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [(u'Local News',
u'http://heraldnet.com/section/RSS02&mime=xml'),
(u'Sports', u'http://heraldnet.com/section/RSS04&mime=xml'),
(u'Entertainment',
u'http://heraldnet.com/section/RSS07&mime=xml'),
(u'Life', u'http://heraldnet.com/section/RSS03&mime=xml'),
(u'Breaking News',
u'http://heraldnet.com/section/RSS34&mime=xml'),
(u'Seahawks', u'http://heraldnet.com/section/RSS22&mime=xml'),
(u'HeraldNet', u'http://heraldnet.com/section/RSS01&mime=xml'),
(u'Inside Everett',
u'http://heraldnet.com/section/RSS26&mime=xml')
]
def print_version(self, url):
return url + "&template=PrinterFriendly"
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-
weight:bold;font-size:large;}
h2{font-family:Arial,Helvetica,sans-serif; font-
weight:normal;font-size:small;}
'''

View File

@ -137,17 +137,17 @@ class HeuristicProcessor(object):
]
ITALICIZE_STYLE_PATS = [
r'(?msu)(?<=\s)_(?P<words>\S[^_]{0,40}?\S)?_(?=\s)',
r'(?msu)(?<=\s)/(?P<words>\S[^/]{0,40}?\S)?/(?=\s)',
r'(?msu)(?<=\s)~~(?P<words>\S[^~]{0,40}?\S)?~~(?=\s)',
r'(?msu)(?<=\s)\*(?P<words>\S[^\*]{0,40}?\S)?\*(?=\s)',
r'(?msu)(?<=\s)~(?P<words>\S[^~]{0,40}?\S)?~(?=\s)',
r'(?msu)(?<=\s)_/(?P<words>\S[^/_]{0,40}?\S)?/_(?=\s)',
r'(?msu)(?<=\s)_\*(?P<words>\S[^\*_]{0,40}?\S)?\*_(?=\s)',
r'(?msu)(?<=\s)\*/(?P<words>\S[^/\*]{0,40}?\S)?/\*(?=\s)',
r'(?msu)(?<=\s)_\*/(?P<words>\S[^\*_]{0,40}?\S)?/\*_(?=\s)',
r'(?msu)(?<=\s)/:(?P<words>\S[^:/]{0,40}?\S)?:/(?=\s)',
r'(?msu)(?<=\s)\|:(?P<words>\S[^:\|]{0,40}?\S)?:\|(?=\s)',
r'(?msu)(?<=\s)_(?P<words>\S[^_]{0,40}?\S)?_(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)/(?P<words>\S[^/]{0,40}?\S)?/(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)~~(?P<words>\S[^~]{0,40}?\S)?~~(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)\*(?P<words>\S[^\*]{0,40}?\S)?\*(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)~(?P<words>\S[^~]{0,40}?\S)?~(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)_/(?P<words>\S[^/_]{0,40}?\S)?/_(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)_\*(?P<words>\S[^\*_]{0,40}?\S)?\*_(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)\*/(?P<words>\S[^/\*]{0,40}?\S)?/\*(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)_\*/(?P<words>\S[^\*_]{0,40}?\S)?/\*_(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)/:(?P<words>\S[^:/]{0,40}?\S)?:/(?=[\s\.,\!\?])',
r'(?msu)(?<=\s)\|:(?P<words>\S[^:\|]{0,40}?\S)?:\|(?=[\s\.,\!\?])',
]
for word in ITALICIZE_WORDS:

View File

@ -166,6 +166,8 @@ class RegexEdit(QWidget, Ui_Edit):
def builder(self):
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self)
if bld.cancelled:
return
if bld.exec_() == bld.Accepted:
self.edit.setText(bld.regex.text())

View File

@ -13,7 +13,7 @@ from calibre.customize.ui import available_input_formats, available_output_forma
device_plugins
from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import UserFeedback, OpenFeedback
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog
from calibre.utils.ipc.job import BaseJob
from calibre.devices.scanner import DeviceScanner
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
@ -826,8 +826,24 @@ class DeviceMixin(object): # {{{
fmt = None
if specific:
d = ChooseFormatDialog(self, _('Choose format to send to device'),
self.device_manager.device.settings().format_map)
formats = []
aval_out_formats = available_output_formats()
format_count = {}
for row in rows:
fmts = self.library_view.model().db.formats(row.row())
if fmts:
for f in fmts.split(','):
f = f.lower()
if format_count.has_key(f):
format_count[f] += 1
else:
format_count[f] = 1
for f in self.device_manager.device.settings().format_map:
if f in format_count.keys():
formats.append((f, _('%i of %i Books' % (format_count[f], len(rows))), True if f in aval_out_formats else False))
elif f in aval_out_formats:
formats.append((f, _('0 of %i Books' % len(rows)), True))
d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats)
if d.exec_() != QDialog.Accepted:
return
if d.format():

View File

@ -0,0 +1,53 @@
__license__ = 'GPL v3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
from PyQt4.Qt import QDialog, QTreeWidgetItem, QIcon, SIGNAL
from calibre.gui2 import file_icon_provider
from calibre.gui2.dialogs.choose_format_device_ui import Ui_ChooseFormatDeviceDialog
class ChooseFormatDeviceDialog(QDialog, Ui_ChooseFormatDeviceDialog):
def __init__(self, window, msg, formats):
'''
formats is a list of tuples: [(format, exists, convertible)].
format: Lower case format identifier. E.G. mobi
exists: String representing the number of books that
exist in the format.
convertible: True if the format is a convertible format.
formats should be ordered in the device's preferred format ordering.
'''
QDialog.__init__(self, window)
Ui_ChooseFormatDeviceDialog.__init__(self)
self.setupUi(self)
self.connect(self.formats, SIGNAL('activated(QModelIndex)'),
self.activated_slot)
self.msg.setText(msg)
for i, (format, exists, convertible) in enumerate(formats):
t_item = QTreeWidgetItem()
t_item.setIcon(0, file_icon_provider().icon_from_ext(format.lower()))
t_item.setText(0, format.upper())
t_item.setText(1, exists)
if convertible:
t_item.setIcon(2, QIcon(I('ok.png')))
self.formats.addTopLevelItem(t_item)
if i == 0:
self.formats.setCurrentItem(t_item)
t_item.setSelected(True)
self.formats.resizeColumnToContents(2)
self.formats.resizeColumnToContents(1)
self.formats.resizeColumnToContents(0)
self.formats.header().resizeSection(0, self.formats.header().sectionSize(0) * 2)
self._format = None
def activated_slot(self, *args):
self.accept()
def format(self):
return self._format
def accept(self):
self._format = unicode(self.formats.currentItem().text(0))
return QDialog.accept(self)

View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChooseFormatDeviceDialog</class>
<widget class="QDialog" name="ChooseFormatDeviceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>507</width>
<height>377</height>
</rect>
</property>
<property name="windowTitle">
<string>Choose Format</string>
</property>
<property name="windowIcon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/mimetypes/unknown.png</normaloff>:/images/mimetypes/unknown.png</iconset>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="msg">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="formats">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Format</string>
</property>
</column>
<column>
<property name="text">
<string>Existing</string>
</property>
<property name="textAlignment">
<set>AlignLeft|AlignVCenter</set>
</property>
</column>
<column>
<property name="text">
<string>Convertible</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../../resources/images.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ChooseFormatDeviceDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ChooseFormatDeviceDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,8 +6,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import re, os
from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \
pyqtSignal, QDialogButtonBox
from PyQt4 import QtGui
pyqtSignal, QDialogButtonBox, QDate, QLineEdit
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
from calibre.gui2.dialogs.tag_editor import TagEditor
@ -302,6 +301,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
self.pubdate.setSpecialValueText(_('Undefined'))
self.clear_pubdate_button.clicked.connect(self.clear_pubdate)
self.pubdate.dateChanged.connect(self.do_apply_pubdate)
self.adddate.setDate(QDate.currentDate())
self.adddate.setMinimumDate(UNDEFINED_QDATE)
self.adddate.setSpecialValueText(_('Undefined'))
self.clear_adddate_button.clicked.connect(self.clear_adddate)
@ -365,16 +365,16 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
offset = 10
self.s_r_number_of_books = min(10, len(self.ids))
for i in range(1,self.s_r_number_of_books+1):
w = QtGui.QLabel(self.tabWidgetPage3)
w = QLabel(self.tabWidgetPage3)
w.setText(_('Book %d:')%i)
self.testgrid.addWidget(w, i+offset, 0, 1, 1)
w = QtGui.QLineEdit(self.tabWidgetPage3)
w = QLineEdit(self.tabWidgetPage3)
w.setReadOnly(True)
name = 'book_%d_text'%i
setattr(self, name, w)
self.book_1_text.setObjectName(name)
self.testgrid.addWidget(w, i+offset, 1, 1, 1)
w = QtGui.QLineEdit(self.tabWidgetPage3)
w = QLineEdit(self.tabWidgetPage3)
w.setReadOnly(True)
name = 'book_%d_result'%i
setattr(self, name, w)

View File

@ -515,8 +515,8 @@ class TagsModel(QAbstractItemModel): # {{{
QAbstractItemModel.__init__(self, parent)
# must do this here because 'QPixmap: Must construct a QApplication
# before a QPaintDevice'. The ':' in front avoids polluting either the
# user-defined categories (':' at end) or columns namespaces (no ':').
# before a QPaintDevice'. The ':' at the end avoids polluting either of
# the other namespaces (alpha, '#', or '@')
iconmap = {}
for key in category_icon_map:
iconmap[key] = QIcon(I(category_icon_map[key]))
@ -690,7 +690,7 @@ class TagsModel(QAbstractItemModel): # {{{
tb_cats = self.db.field_metadata
for user_cat in sorted(self.db.prefs.get('user_categories', {}).keys(),
key=sort_key):
cat_name = user_cat+':' # add the ':' to avoid name collision
cat_name = '@' + user_cat # add the '@' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
if len(saved_searches().names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
@ -997,7 +997,7 @@ class TagsModel(QAbstractItemModel): # {{{
if self.hidden_categories and self.categories[i] in self.hidden_categories:
continue
row_index += 1
if key.endswith(':'):
if key.startswith('@'):
# User category, so skip it. The tag will be marked in its real category
continue
category_item = self.root_item.children[row_index]
@ -1016,7 +1016,7 @@ class TagsModel(QAbstractItemModel): # {{{
ans.append('%s%s:"=%s"'%(prefix, category, tag.name))
return ans
def find_node(self, key, txt, start_path):
def find_item_node(self, key, txt, start_path):
'''
Search for an item (a node) in the tags browser list that matches both
the key (exact case-insensitive match) and txt (contains case-
@ -1070,6 +1070,22 @@ class TagsModel(QAbstractItemModel): # {{{
break
return self.path_found
def find_category_node(self, key):
'''
Search for an category node (a top-level node) in the tags browser list
that matches the key (exact case-insensitive match). Returns the path to
the node. Paths are as in find_item_node.
'''
if not key:
return None
for i in xrange(self.rowCount(QModelIndex())):
idx = self.index(i, 0, QModelIndex())
ckey = idx.internalPointer().category_key
if strcmp(ckey, key) == 0:
return self.path_for_index(idx)
return None
def show_item_at_path(self, path, box=False):
'''
Scroll the browser and open categories to show the item referenced by
@ -1355,15 +1371,15 @@ class TagBrowserWidget(QWidget): # {{{
self.search_button.setFocus(True)
self.item_search.lineEdit().blockSignals(False)
colon = txt.find(':')
key = None
colon = txt.rfind(':') if len(txt) > 2 else 0
if colon > 0:
key = self.parent.library_view.model().db.\
field_metadata.search_term_to_field_key(txt[:colon])
txt = txt[colon+1:]
self.current_find_position = model.find_node(key, txt,
self.current_find_position)
self.current_find_position = \
model.find_item_node(key, txt, self.current_find_position)
if self.current_find_position:
model.show_item_at_path(self.current_find_position, box=True)
elif self.item_search.text():

View File

@ -319,7 +319,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.field_metadata.remove_dynamic_categories()
tb_cats = self.field_metadata
for user_cat in sorted(self.prefs.get('user_categories', {}).keys(), key=sort_key):
cat_name = user_cat+':' # add the ':' to avoid name collision
cat_name = '@' + user_cat # add the '@' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
if len(saved_searches().names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
@ -1243,7 +1243,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if category in icon_map:
icon = icon_map[label]
else:
icon = icon_map[':custom']
icon = icon_map['custom:']
icon_map[category] = icon
datatype = cat['datatype']
@ -1339,20 +1339,19 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if label in taglist and name in taglist[label]:
items.append(taglist[label][name])
# else: do nothing, to not include nodes w zero counts
if len(items):
cat_name = user_cat+':' # add the ':' to avoid name collision
# Not a problem if we accumulate entries in the icon map
if icon_map is not None:
icon_map[cat_name] = icon_map[':user']
if sort == 'popularity':
categories[cat_name] = \
sorted(items, key=lambda x: x.count, reverse=True)
elif sort == 'name':
categories[cat_name] = \
sorted(items, key=lambda x: sort_key(x.sort))
else:
categories[cat_name] = \
sorted(items, key=lambda x:x.avg_rating, reverse=True)
cat_name = '@' + user_cat # add the '@' to avoid name collision
# Not a problem if we accumulate entries in the icon map
if icon_map is not None:
icon_map[cat_name] = icon_map['user:']
if sort == 'popularity':
categories[cat_name] = \
sorted(items, key=lambda x: x.count, reverse=True)
elif sort == 'name':
categories[cat_name] = \
sorted(items, key=lambda x: sort_key(x.sort))
else:
categories[cat_name] = \
sorted(items, key=lambda x:x.avg_rating, reverse=True)
#### Finally, the saved searches category ####
items = []

View File

@ -16,7 +16,7 @@ class TagsIcons(dict):
'''
category_icons = ['authors', 'series', 'formats', 'publisher', 'rating',
'news', 'tags', ':custom', ':user', 'search',]
'news', 'tags', 'custom:', 'user:', 'search',]
def __init__(self, icon_dict):
for a in self.category_icons:
if a not in icon_dict:
@ -31,8 +31,8 @@ category_icon_map = {
'rating' : 'rating.png',
'news' : 'news.png',
'tags' : 'tags.png',
':custom' : 'column.png',
':user' : 'drawer.png',
'custom:' : 'column.png',
'user:' : 'drawer.png',
'search' : 'search.png'
}

View File

@ -478,6 +478,8 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
- Focus the search bar
* - :kbd:`Shift+Ctrl+F`
- Open the advanced search dialog
* - :kbd:`Esc`
- Clear the current search
* - :kbd:`N or F3`
- Find the next book that matches the current search (only works if the highlight checkbox next to the search bar is checked)
* - :kbd:`Shift+N or Shift+F3`