From 899a13defc2acff9c03d14215741f982e2c23d00 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 07:58:22 -0600 Subject: [PATCH 01/17] Content server: In OPDS feeds use the new author sort information when generating feeds by author --- src/calibre/library/server/opds.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/calibre/library/server/opds.py b/src/calibre/library/server/opds.py index d396d73af2..7b8d609dda 100644 --- a/src/calibre/library/server/opds.py +++ b/src/calibre/library/server/opds.py @@ -99,17 +99,20 @@ def html_to_lxml(raw): raw = etree.tostring(root, encoding=None) return etree.fromstring(raw) -def CATALOG_ENTRY(item, base_href, version, updated): +def CATALOG_ENTRY(item, base_href, version, updated, ignore_count=False): id_ = 'calibre:category:'+item.name iid = 'N' + item.name if item.id is not None: iid = 'I' + str(item.id) link = NAVLINK(href = base_href + '/' + hexlify(iid)) + count = _('%d books')%item.count + if ignore_count: + count = '' return E.entry( TITLE(item.name), ID(id_), UPDATED(updated), - E.content(_('%d books')%item.count, type='text'), + E.content(count, type='text'), link ) @@ -265,8 +268,12 @@ class CategoryFeed(NavFeed): def __init__(self, items, which, id_, updated, version, offsets, page_url, up_url): NavFeed.__init__(self, id_, updated, version, offsets, page_url, up_url) base_href = self.base_href + '/category/' + hexlify(which) + ignore_count = False + if which == 'search': + ignore_count = True for item in items: - self.root.append(CATALOG_ENTRY(item, base_href, version, updated)) + self.root.append(CATALOG_ENTRY(item, base_href, version, updated, + ignore_count=ignore_count)) class CategoryGroupFeed(NavFeed): @@ -393,7 +400,7 @@ class OPDSServer(object): owhich = hexlify('N'+which) up_url = url_for('opdsnavcatalog', version, which=owhich) items = categories[category] - items = [x for x in items if x.name.startswith(which)] + items = [x for x in items if getattr(x, 'sort', x.name).startswith(which)] if not items: raise cherrypy.HTTPError(404, 'No items in group %r:%r'%(category, which)) @@ -458,11 +465,11 @@ class OPDSServer(object): def __init__(self, text, count): self.text, self.count = text, count - starts = set([x.name[0] for x in items]) + starts = set([getattr(x, 'sort', x.name)[0] for x in items]) category_groups = OrderedDict() for x in sorted(starts, cmp=lambda x,y:cmp(x.lower(), y.lower())): category_groups[x] = len([y for y in items if - y.name.startswith(x)]) + getattr(y, 'sort', y.name).startswith(x)]) items = [Group(x, y) for x, y in category_groups.items()] max_items = self.opts.max_opds_items offsets = OPDSOffsets(offset, max_items, len(items)) From 64f0678559dd86c6ce6057d28ef3b6ebb12f4216 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 08:29:23 -0600 Subject: [PATCH 02/17] Better bookmarks icon --- resources/images/bookmarks.svg | 712 +++++++++++---------------------- 1 file changed, 239 insertions(+), 473 deletions(-) diff --git a/resources/images/bookmarks.svg b/resources/images/bookmarks.svg index 2fcd844283..6964853702 100644 --- a/resources/images/bookmarks.svg +++ b/resources/images/bookmarks.svg @@ -1,7 +1,6 @@ + sodipodi:docbase="/home/dobey/Projects/gnome-icon-theme/scalable/apps" + sodipodi:docname="accessories-dictionary.svg" + inkscape:export-filename="/home/ulisse/Desktop/accessories-dictionary.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> - - - - - - - - - - - - - - - - + id="linearGradient2309"> + id="stop2311" /> + id="stop2313" /> + id="linearGradient2301"> + id="stop2303" /> + id="stop2305" /> + inkscape:collect="always" + id="linearGradient2286"> + id="stop2288" /> - - + id="stop2290" /> + inkscape:collect="always" + id="linearGradient2276"> + id="stop2278" /> + id="stop2280" /> + inkscape:collect="always" + id="linearGradient2258"> + id="stop2260" /> + id="stop2262" /> + + + + + + + + + style="stop-color:#babdb6" /> + style="stop-color:#d3d7cf;stop-opacity:0;" /> + inkscape:collect="always" + id="linearGradient2184"> + id="stop2186" /> + id="stop2188" /> + gradientTransform="matrix(-1,0,0,1,48,0)" /> + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:grid-points="true" + gridspacingx="0.5px" + gridspacingy="0.5px" + gridempspacing="2" + inkscape:window-width="872" + inkscape:window-height="694" + inkscape:window-x="0" + inkscape:window-y="25" + fill="#75507b" /> @@ -458,133 +249,108 @@ image/svg+xml + + + Ulisse Perusin + + + Dictionary + + + dictionary + translation + + + + + + + + + + + - - - + style="opacity:0.50196078;color:#000000;fill:url(#radialGradient2292);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:17.85;stroke-opacity:1;visibility:visible;display:block;overflow:visible" + id="path2284" + sodipodi:cx="24" + sodipodi:cy="36.75" + sodipodi:rx="22.5" + sodipodi:ry="6.75" + d="M 46.5 36.75 A 22.5 6.75 0 1 1 1.5,36.75 A 22.5 6.75 0 1 1 46.5 36.75 z" + transform="matrix(1.066667,0,0,0.962963,-1.600001,1.111111)" /> + style="color:#000000;fill:#523856;fill-opacity:1;fill-rule:nonzero;stroke:#3e263b;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:17.85;stroke-opacity:1;visibility:visible;display:block;overflow:visible" + d="M 4.5,11.5 L 43.5,11.5 L 47.5,38.5 L 29,38.5 L 28,37.5 C 26,39 22,39 20,37.5 L 19,38.5 L 0.5,38.5 L 4.5,11.5 z " + id="rect1304" + sodipodi:nodetypes="ccccccccc" /> + sodipodi:type="inkscape:offset" + inkscape:radius="-0.91809106" + inkscape:original="M 4.5 11.5 L 0.5 38.5 L 19 38.5 L 20 37.5 C 22 39 26 39 28 37.5 L 29 38.5 L 47.5 38.5 L 43.5 11.5 L 4.5 11.5 z " + xlink:href="#rect1304" + style="opacity:0.13333333;color:#000000;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:17.85;stroke-opacity:1;visibility:visible;display:block;overflow:visible" + id="path2274" + inkscape:href="#rect1304" + d="M 5.28125,12.40625 L 1.5625,37.59375 L 18.59375,37.59375 L 19.34375,36.84375 C 19.667151,36.507336 20.191452,36.467006 20.5625,36.75 C 21.327469,37.323727 22.653015,37.71875 24,37.71875 C 25.346985,37.71875 26.672531,37.323727 27.4375,36.75 C 27.808548,36.467006 28.332849,36.507336 28.65625,36.84375 L 29.40625,37.59375 L 46.4375,37.59375 L 42.71875,12.40625 L 5.28125,12.40625 z " /> + style="fill:url(#linearGradient2282);fill-opacity:1.0;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 2,36.5 C 7.6666667,36.5 16,35 19,36.5 C 22,34 26,34 29,36.5 C 32,35 41,36.5 46,36.5 L 45.5,34 C 38.5,31.5 29,28.5 24,33 C 19,28.5 9.5,31.5 2.5,34 L 2,36.5 z " + id="path2180" + sodipodi:nodetypes="cccccccc" /> + sodipodi:type="inkscape:offset" + inkscape:radius="-1.0582203" + inkscape:original="M 14 30.875 C 10.125 31.375 6 32.75 2.5 34 L 2 36.5 C 7.6666667 36.5 16 35 19 36.5 C 22 34 26 34 29 36.5 C 32 35 41 36.5 46 36.5 L 45.5 34 C 38.5 31.5 29 28.5 24 33 C 21.5 30.75 17.875 30.375 14 30.875 z " + xlink:href="#path2180" + style="opacity:0.30196078;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2315);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2266" + inkscape:href="#path2180" + d="M 14.375,31.9375 C 10.963293,32.392394 7.260823,33.622273 3.90625,34.8125 L 3.8125,35.34375 C 6.2979599,35.262594 9.0476285,35.037732 11.6875,34.875 C 14.462294,34.703951 16.881256,34.711661 18.78125,35.40625 C 20.133116,34.409774 21.661646,33.894157 23.21875,33.75 C 21.042747,31.830616 17.941674,31.461944 14.375,31.9375 z M 28.625,31.9375 C 27.145571,32.213473 25.86037,32.798142 24.78125,33.75 C 26.338354,33.894157 27.866884,34.409774 29.21875,35.40625 C 31.163554,34.697135 33.704549,34.703523 36.5625,34.875 C 39.261382,35.036933 41.920385,35.260963 44.1875,35.34375 L 44.09375,34.8125 C 40.739177,33.622273 37.036707,32.392394 33.625,31.9375 C 31.827105,31.697781 30.128781,31.656984 28.625,31.9375 z " /> + style="fill:url(#linearGradient2245);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2247);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 2.5,34 C 9,31.5 20,29 24,33 C 28,29 39,31.5 45.5,34 L 42.5,10.5 C 37,8 27.5,6 24,9 C 20,6 12,8 5.5,10.5 L 2.5,34 z " + id="path2182" + sodipodi:nodetypes="ccccccc" /> - - - - - - - + style="color:#000000;fill:url(#linearGradient2211);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:17.85;stroke-opacity:1;visibility:visible;display:block;overflow:visible" + d="M 24,9.5 C 25.221264,8.803878 26.327771,7.9069322 28,8 L 29,30.5 C 27.5,30 25.5,31.5 24,32.5 L 24,9.5 z " + id="path2195" + sodipodi:nodetypes="ccccc" /> + + From 9455a93d78310e226c462fb0d1057038c353696c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 15 Jun 2010 16:03:43 +0100 Subject: [PATCH 03/17] Added: 1) The addition of tooltips to the Tag Browser displaying the average rating. 2) Also, change the sort by popularity to a combobox to allow sorting by average rating. --- src/calibre/gui2/__init__.py | 4 ++-- src/calibre/gui2/tag_view.py | 41 +++++++++++++++++++------------- src/calibre/library/database2.py | 34 ++++++++++++++++---------- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 306bbc77e6..1056f6ced6 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -43,8 +43,8 @@ def _config(): help=_('Notify when a new version is available')) c.add_opt('use_roman_numerals_for_series_number', default=True, help=_('Use Roman numerals for series number')) - c.add_opt('sort_by_popularity', default=False, - help=_('Sort tags list by popularity')) + c.add_opt('sort_tags_by', default='name', + help=_('Sort tags list by name, popularity, or rating')) c.add_opt('cover_flow_queue_length', default=6, help=_('Number of covers to show in the cover browsing mode')) c.add_opt('LRF_conversion_defaults', default=[], diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 9cc90ca83f..c3c5e8954d 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -81,8 +81,12 @@ class TagDelegate(QItemDelegate): r.setLeft(r.left() + ((width+left_offset*2)*factor) + 3) painter.restore() # Paint the text - painter.drawText(r, Qt.AlignLeft|Qt.AlignVCenter, - QString('[%d] %s'%(item.tag.count, item.tag.name))) + if item.tag.count == 0: + painter.drawText(r, Qt.AlignLeft|Qt.AlignVCenter, + QString('%s'%(item.tag.name))) + else: + painter.drawText(r, Qt.AlignLeft|Qt.AlignVCenter, + QString('[%d] %s'%(item.tag.count, item.tag.name))) class TagsView(QTreeView): # {{{ @@ -107,12 +111,12 @@ class TagsView(QTreeView): # {{{ self.setHeaderHidden(True) self.setItemDelegate(TagDelegate(self)) - def set_database(self, db, tag_match, popularity): + 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) - self.popularity = popularity + self.sort_by = sort_by self.tag_match = tag_match self.db = db self.search_restriction = None @@ -120,8 +124,9 @@ class TagsView(QTreeView): # {{{ self.setContextMenuPolicy(Qt.CustomContextMenu) self.clicked.connect(self.toggle) self.customContextMenuRequested.connect(self.show_context_menu) - self.popularity.setChecked(config['sort_by_popularity']) - self.popularity.stateChanged.connect(self.sort_changed) + pop = config['sort_tags_by'] + self.sort_by.setCurrentIndex(self.db.CATEGORY_SORTS.index(pop)) + self.sort_by.currentIndexChanged.connect(self.sort_changed) self.refresh_required.connect(self.recount, type=Qt.QueuedConnection) db.add_listener(self.database_changed) @@ -132,8 +137,8 @@ class TagsView(QTreeView): # {{{ def match_all(self): return self.tag_match and self.tag_match.currentIndex() > 0 - def sort_changed(self, state): - config.set('sort_by_popularity', state == Qt.Checked) + def sort_changed(self, pop): + config.set('sort_tags_by', self.db.CATEGORY_SORTS[pop]) self.recount() def set_search_restriction(self, s): @@ -420,7 +425,7 @@ class TagsModel(QAbstractItemModel): # {{{ self.row_map = [] # get_node_tree cannot return None here, because row_map is empty - data = self.get_node_tree(config['sort_by_popularity']) + data = self.get_node_tree(config['sort_tags_by']) self.root_item = TagTreeItem() for i, r in enumerate(self.row_map): if self.hidden_categories and self.categories[i] in self.hidden_categories: @@ -464,11 +469,11 @@ class TagsModel(QAbstractItemModel): # {{{ # Now get the categories if self.search_restriction: - data = self.db.get_categories(sort_on_count=sort, + data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map, ids=self.db.search('', return_matches=True)) else: - data = self.db.get_categories(sort_on_count=sort, icon_map=self.category_icon_map) + data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map) tb_categories = self.db.field_metadata for category in tb_categories: @@ -482,7 +487,7 @@ class TagsModel(QAbstractItemModel): # {{{ return data def refresh(self): - data = self.get_node_tree(config['sort_by_popularity']) # get category data + data = self.get_node_tree(config['sort_tags_by']) # get category data if data is None: return False row_index = -1 @@ -691,7 +696,7 @@ class TagBrowserMixin(object): # {{{ def __init__(self, db): self.library_view.model().count_changed_signal.connect(self.tags_view.recount) self.tags_view.set_database(self.library_view.model().db, - self.tag_match, self.popularity) + self.tag_match, self.sort_by) self.tags_view.tags_marked.connect(self.search.search_from_tags) self.tags_view.tags_marked.connect(self.saved_search.clear_to_help) self.tags_view.tag_list_edit.connect(self.do_tags_list_edit) @@ -752,9 +757,13 @@ class TagBrowserWidget(QWidget): # {{{ parent.tags_view = TagsView(parent) self._layout.addWidget(parent.tags_view) - parent.popularity = QCheckBox(parent) - parent.popularity.setText(_('Sort by &popularity')) - self._layout.addWidget(parent.popularity) + parent.sort_by = QComboBox(parent) + # Must be in the same order as db2.CATEGORY_SORTS + for x in (_('Sort by name'), _('Sort by popularity'), + _('Sort by average rating')): + parent.sort_by.addItem(x) + parent.sort_by.setCurrentIndex(0) + self._layout.addWidget(parent.sort_by) parent.tag_match = QComboBox(parent) for x in (_('Match any'), _('Match all')): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 2fb22a27f4..321a5fce17 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -64,6 +64,10 @@ class Tag(object): self.state = state self.avg_rating = avg/2.0 if avg is not None else 0 self.sort = sort + if self.avg_rating > 0: + if tooltip: + tooltip = tooltip + ': ' + tooltip = _('%sAverage rating is %3.1f')%(tooltip, self.avg_rating) self.tooltip = tooltip self.icon = icon @@ -687,7 +691,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): tn=field['table'], col=field['link_column']), (id_,)) return set(x[0] for x in ans) - def get_categories(self, sort_on_count=False, ids=None, icon_map=None): + CATEGORY_SORTS = ('name', 'popularity', 'rating') + + def get_categories(self, sort='name', ids=None, icon_map=None): self.books_list_filter.change([] if not ids else ids) categories = {} @@ -711,10 +717,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): else: query = '''SELECT id, {0}, count, avg_rating, sort FROM tag_browser_filtered_{1}'''.format(cn, tn) - if sort_on_count: - query += ' ORDER BY count DESC' - else: + if sort == 'popularity': + query += ' ORDER BY count DESC, sort ASC' + elif sort == 'name': query += ' ORDER BY sort ASC' + else: + query += ' ORDER BY avg_rating DESC, sort ASC' data = self.conn.get(query) # icon_map is not None if get_categories is to store an icon and @@ -770,11 +778,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if count > 0: categories['formats'].append(Tag(fmt, count=count, icon=icon)) - if sort_on_count: - categories['formats'].sort(cmp=lambda x,y:cmp(x.count, y.count), - reverse=True) - else: - categories['formats'].sort(cmp=lambda x,y:cmp(x.name, y.name)) + if sort == 'popularity': + categories['formats'].sort(key=lambda x: x.count, reverse=True) + else: # no ratings exist to sort on + categories['formats'].sort(key = lambda x:x.name) #### Now do the user-defined categories. #### user_categories = prefs['user_categories'] @@ -799,12 +806,15 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # 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_on_count: + if sort == 'popularity': categories[cat_name] = \ - sorted(items, cmp=(lambda x, y: cmp(y.count, x.count))) + sorted(items, key=lambda x: x.count, reverse=True) + elif sort == 'name': + categories[cat_name] = \ + sorted(items, key=lambda x: x.sort.lower()) else: categories[cat_name] = \ - sorted(items, cmp=(lambda x, y: cmp(x.name.lower(), y.name.lower()))) + sorted(items, key=lambda x:x.avg_rating, reverse=True) #### Finally, the saved searches category #### items = [] From bb8bc9cea5a2bb725240adf54a5f2847b6701877 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 09:07:24 -0600 Subject: [PATCH 04/17] Display average rating by partial coloring of category icon --- resources/default_tweaks.py | 7 +- resources/images/series.svg | 2133 ++++++------ resources/images/user_profile.svg | 4988 ++--------------------------- src/calibre/gui2/tag_view.py | 59 +- 4 files changed, 1346 insertions(+), 5841 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 2075391da4..aaeb992151 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -72,9 +72,4 @@ gui_pubdate_display_format = 'MMM yyyy' # without changing anything is sufficient to change the sort. title_series_sorting = 'library_order' -# How to render average rating in the tag browser. -# There are two rendering methods available. The first is to show a partial -# star, and the second is to show a partially filled rectangle. The first is -# better looking, but uses more screen space than the second. -# Values are 'star' or 'rectangle' -render_avg_rating_using='star' + diff --git a/resources/images/series.svg b/resources/images/series.svg index c26d1ef7a2..f2eb87b709 100644 --- a/resources/images/series.svg +++ b/resources/images/series.svg @@ -1,1096 +1,1071 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + id="linearGradient6642"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + 2007-06-23 + + + Lapo Calamandrei + + + + + address + book + contact + + + + + + + Andreas Nilsson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/user_profile.svg b/resources/images/user_profile.svg index 5a51783d7c..2fc0eea150 100644 --- a/resources/images/user_profile.svg +++ b/resources/images/user_profile.svg @@ -1,4750 +1,312 @@ + + version="1.1"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:grid-bbox="true" + inkscape:document-units="px" + fill="#9db029" + stroke="#727e0a" + inkscape:window-width="1330" + inkscape:window-height="815" + inkscape:window-x="202" + inkscape:window-y="68" + inkscape:window-maximized="0" /> + id="metadata4"> image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + user + person + + + + + + + + + + + - - + inkscape:label="cipek" + inkscape:groupmode="layer" + style="display:inline"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="opacity:1;color:#000000;fill:url(#linearGradient1372);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M 13.365469,24.850231 L 19.043607,24.850231 L 15.731361,21.774572 L 15.021593,22.720929 L 14.311825,22.011162 L 13.365469,24.850231 z " + id="path4173" /> - + sodipodi:nodetypes="cccc" + id="path4370" + d="M 19.882923,32.490544 C 21.530768,31.712992 22.297815,29.810737 22.297815,29.810737 C 21.014177,24.39981 16.976336,20.652646 16.976336,20.652646 C 16.976336,20.652646 20.274824,29.141269 19.882923,32.490544 z " + style="opacity:0.22784807;color:#000000;fill:url(#linearGradient1366);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> + id="layer2" + inkscape:label="dalsi cipek" + style="display:inline"> + + + + + + + + + diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 9cc90ca83f..70d626bc6e 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -13,11 +13,10 @@ from functools import partial from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QCheckBox, \ QFont, QSize, QIcon, QPoint, QVBoxLayout, QComboBox, \ QAbstractItemModel, QVariant, QModelIndex, QMenu, \ - QPushButton, QWidget, QItemDelegate, QString, QPen, \ - QColor, QLinearGradient, QBrush + QPushButton, QWidget, QItemDelegate, QString from calibre.gui2 import config, NONE -from calibre.utils.config import prefs, tweaks +from calibre.utils.config import prefs from calibre.library.field_metadata import TagsIcons from calibre.utils.search_query_parser import saved_searches from calibre.gui2 import error_dialog @@ -38,51 +37,25 @@ class TagDelegate(QItemDelegate): QItemDelegate.paint(self, painter, option, index) return r = option.rect - # Paint the decoration icon icon = self._parent.model().data(index, Qt.DecorationRole).toPyObject() - icon.paint(painter, r, Qt.AlignLeft) + painter.save() + if item.tag.state != 0 or not config['show_avg_rating']: + icon.paint(painter, r, Qt.AlignLeft) + else: + icon.paint(painter, r, Qt.AlignLeft, mode=QIcon.Disabled) + rating = item.tag.avg_rating + if rating is None: + rating = 5.0 + painter.setClipRect(r.left(), r.bottom()-int(r.height()*(rating/5.0)), + r.width(), r.height()) + icon.paint(painter, r, Qt.AlignLeft) + painter.setClipRect(r) - # Paint the rating, if any. The decoration icon is assumed to be square, - # filling the row top to bottom. The three is arbitrary, there to - # provide a little space between the icon and what follows - r.setLeft(r.left()+r.height()+3) - rating = item.tag.avg_rating - if config['show_avg_rating'] and item.tag.avg_rating is not None: - painter.save() - if tweaks['render_avg_rating_using'] == 'star': - painter.setClipRect(r.left(), r.top(), - int(r.height()*(rating/5.0)), r.height()) - self.icon.paint(painter, r, Qt.AlignLeft | Qt.AlignVCenter) - r.setLeft(r.left() + r.height()) - else: - painter.translate(r.left(), r.top()) - # Compute factor so sizes can be expressed in percentages of the - # box defined by the row height - factor = r.height()/100. - width = 20 - height = 80 - left_offset = 5 - top_offset = 10 - if r > 0.0: - color = QColor(100, 100, 255) #medium blue, less glare - pen = QPen(color, 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) - painter.setPen(pen) - painter.scale(factor, factor) - painter.drawRect(left_offset, top_offset, width, height) - fill_height = height*(rating/5.0) - gradient = QLinearGradient(0, 0, 0, 100) - gradient.setColorAt(0.0, color) - gradient.setColorAt(1.0, color) - painter.setBrush(QBrush(gradient)) - painter.drawRect(left_offset, top_offset+(height-fill_height), - width, fill_height) - # The '3' is arbitrary, there because we need a little space - # between the rectangle and the text. - r.setLeft(r.left() + ((width+left_offset*2)*factor) + 3) - painter.restore() # Paint the text + r.setLeft(r.left()+r.height()+3) painter.drawText(r, Qt.AlignLeft|Qt.AlignVCenter, QString('[%d] %s'%(item.tag.count, item.tag.name))) + painter.restore() class TagsView(QTreeView): # {{{ From 82bb29424f5db4accc58c0f04cb1bb6ac3d2335a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 09:13:24 -0600 Subject: [PATCH 05/17] Cleanup Tag browser text display --- src/calibre/gui2/tag_view.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 70d626bc6e..fc20d98279 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -13,7 +13,7 @@ from functools import partial from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QCheckBox, \ QFont, QSize, QIcon, QPoint, QVBoxLayout, QComboBox, \ QAbstractItemModel, QVariant, QModelIndex, QMenu, \ - QPushButton, QWidget, QItemDelegate, QString + QPushButton, QWidget, QItemDelegate from calibre.gui2 import config, NONE from calibre.utils.config import prefs @@ -37,15 +37,15 @@ class TagDelegate(QItemDelegate): QItemDelegate.paint(self, painter, option, index) return r = option.rect - icon = self._parent.model().data(index, Qt.DecorationRole).toPyObject() + model = self._parent.model() + icon = model.data(index, Qt.DecorationRole).toPyObject() painter.save() - if item.tag.state != 0 or not config['show_avg_rating']: + if item.tag.state != 0 or not config['show_avg_rating'] or \ + item.tag.avg_rating is None: icon.paint(painter, r, Qt.AlignLeft) else: icon.paint(painter, r, Qt.AlignLeft, mode=QIcon.Disabled) rating = item.tag.avg_rating - if rating is None: - rating = 5.0 painter.setClipRect(r.left(), r.bottom()-int(r.height()*(rating/5.0)), r.width(), r.height()) icon.paint(painter, r, Qt.AlignLeft) @@ -54,7 +54,7 @@ class TagDelegate(QItemDelegate): # Paint the text r.setLeft(r.left()+r.height()+3) painter.drawText(r, Qt.AlignLeft|Qt.AlignVCenter, - QString('[%d] %s'%(item.tag.count, item.tag.name))) + model.data(index, Qt.DisplayRole).toString()) painter.restore() class TagsView(QTreeView): # {{{ @@ -345,11 +345,7 @@ class TagTreeItem(object): # {{{ if self.tag.count == 0: return QVariant('%s'%(self.tag.name)) else: - if self.tag.avg_rating is None: - return QVariant('[%d] %s'%(self.tag.count, self.tag.name)) - else: - return QVariant('[%d][%3.1f] %s'%(self.tag.count, - self.tag.avg_rating, self.tag.name)) + return QVariant('[%d] %s'%(self.tag.count, self.tag.name)) if role == Qt.EditRole: return QVariant(self.tag.name) if role == Qt.DecorationRole: From 7445bae81fa03579c74c8537b78011624e476a8f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 09:29:29 -0600 Subject: [PATCH 06/17] ... --- src/calibre/gui2/tag_view.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index fc20d98279..ce37b44f79 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -26,18 +26,13 @@ from calibre.gui2.dialogs.edit_authors_dialog import EditAuthorsDialog class TagDelegate(QItemDelegate): - def __init__(self, parent): - QItemDelegate.__init__(self, parent) - self._parent = parent - self.icon = QIcon(I('star.png')) - def paint(self, painter, option, index): item = index.internalPointer() if item.type != TagTreeItem.TAG: QItemDelegate.paint(self, painter, option, index) return r = option.rect - model = self._parent.model() + model = self.parent().model() icon = model.data(index, Qt.DecorationRole).toPyObject() painter.save() if item.tag.state != 0 or not config['show_avg_rating'] or \ From 1efe9d8d1d1a50de04b2d471e385d256618374ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 09:51:11 -0600 Subject: [PATCH 07/17] Better rendering of avg rating indication --- src/calibre/gui2/tag_view.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index ce37b44f79..5e5393fdc4 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -39,7 +39,9 @@ class TagDelegate(QItemDelegate): item.tag.avg_rating is None: icon.paint(painter, r, Qt.AlignLeft) else: - icon.paint(painter, r, Qt.AlignLeft, mode=QIcon.Disabled) + painter.setOpacity(0.3) + icon.paint(painter, r, Qt.AlignLeft) + painter.setOpacity(1) rating = item.tag.avg_rating painter.setClipRect(r.left(), r.bottom()-int(r.height()*(rating/5.0)), r.width(), r.height()) From 4b50654ef2ec7e28dae0426d9a07d7c745e067d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 10:26:10 -0600 Subject: [PATCH 08/17] Fix #5845 (Updated recipe for Danas) --- resources/recipes/danas.recipe | 56 +++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/resources/recipes/danas.recipe b/resources/recipes/danas.recipe index d82928e323..159553370a 100644 --- a/resources/recipes/danas.recipe +++ b/resources/recipes/danas.recipe @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- __license__ = 'GPL v3' __copyright__ = '2008-2010, Darko Miletic ' ''' @@ -23,7 +22,14 @@ class Danas(BasicNewsRecipe): language = 'sr' publication_type = 'newspaper' remove_empty_feeds = True - extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .article_description,body,.lokacija{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif} .nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif} .antrfileText{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em; margin-bottom: 0; margin-top: 0} h2,.datum,.lokacija,.autor{font-size: small} .antrfileNaslov{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em; font-weight:bold; margin-bottom: 0; margin-top: 0} img{margin-bottom: 0.8em} ' + extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} + @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + .article_description,body,.lokacija{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif} + .nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif} + .antrfileText{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em; + margin-bottom: 0; margin-top: 0} h2,.datum,.lokacija,.autor{font-size: small} + .antrfileNaslov{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em; + font-weight:bold; margin-bottom: 0; margin-top: 0} img{margin-bottom: 0.8em} """ conversion_options = { 'comment' : description @@ -42,19 +48,32 @@ class Danas(BasicNewsRecipe): ] feeds = [ - (u'Politika' , u'http://www.danas.rs/rss/rss.asp?column_id=27') - ,(u'Hronika' , u'http://www.danas.rs/rss/rss.asp?column_id=2' ) - ,(u'Dru\xc5\xa1tvo', u'http://www.danas.rs/rss/rss.asp?column_id=24') - ,(u'Dijalog' , u'http://www.danas.rs/rss/rss.asp?column_id=1' ) - ,(u'Ekonomija', u'http://www.danas.rs/rss/rss.asp?column_id=6' ) - ,(u'Svet' , u'http://www.danas.rs/rss/rss.asp?column_id=25') - ,(u'Srbija' , u'http://www.danas.rs/rss/rss.asp?column_id=28') - ,(u'Kultura' , u'http://www.danas.rs/rss/rss.asp?column_id=5' ) - ,(u'Sport' , u'http://www.danas.rs/rss/rss.asp?column_id=13') - ,(u'Scena' , u'http://www.danas.rs/rss/rss.asp?column_id=42') - ,(u'Feljton' , u'http://www.danas.rs/rss/rss.asp?column_id=19') - ,(u'Periskop' , u'http://www.danas.rs/rss/rss.asp?column_id=4' ) - ,(u'Famozno' , u'http://www.danas.rs/rss/rss.asp?column_id=47') + (u'Politika' , u'http://www.danas.rs/rss/rss.asp?column_id=27') + ,(u'Hronika' , u'http://www.danas.rs/rss/rss.asp?column_id=2' ) + ,(u'Drustvo' , u'http://www.danas.rs/rss/rss.asp?column_id=24') + ,(u'Dijalog' , u'http://www.danas.rs/rss/rss.asp?column_id=1' ) + ,(u'Ekonomija' , u'http://www.danas.rs/rss/rss.asp?column_id=6' ) + ,(u'Svet' , u'http://www.danas.rs/rss/rss.asp?column_id=25') + ,(u'Srbija' , u'http://www.danas.rs/rss/rss.asp?column_id=28') + ,(u'Kultura' , u'http://www.danas.rs/rss/rss.asp?column_id=5' ) + ,(u'Sport' , u'http://www.danas.rs/rss/rss.asp?column_id=13') + ,(u'Scena' , u'http://www.danas.rs/rss/rss.asp?column_id=42') + ,(u'Feljton' , u'http://www.danas.rs/rss/rss.asp?column_id=19') + ,(u'Periskop' , u'http://www.danas.rs/rss/rss.asp?column_id=4' ) + ,(u'Famozno' , u'http://www.danas.rs/rss/rss.asp?column_id=47') + ,(u'Sluzbena beleska' , u'http://www.danas.rs/rss/rss.asp?column_id=48') + ,(u'Suocavanja' , u'http://www.danas.rs/rss/rss.asp?column_id=49') + ,(u'Moj Izbor' , u'http://www.danas.rs/rss/rss.asp?column_id=50') + ,(u'Direktno' , u'http://www.danas.rs/rss/rss.asp?column_id=51') + ,(u'I tome slicno' , u'http://www.danas.rs/rss/rss.asp?column_id=52') + ,(u'No longer and not yet', u'http://www.danas.rs/rss/rss.asp?column_id=53') + ,(u'Resetovanje' , u'http://www.danas.rs/rss/rss.asp?column_id=54') + ,(u'Iza scene' , u'http://www.danas.rs/rss/rss.asp?column_id=60') + ,(u'Drustvoslovlje' , u'http://www.danas.rs/rss/rss.asp?column_id=55') + ,(u'Zvaka u pepeljari' , u'http://www.danas.rs/rss/rss.asp?column_id=56') + ,(u'Vostani Serbie' , u'http://www.danas.rs/rss/rss.asp?column_id=57') + ,(u'Med&Jad-a' , u'http://www.danas.rs/rss/rss.asp?column_id=58') + ,(u'Svetlosti pozornice' , u'http://www.danas.rs/rss/rss.asp?column_id=59') ] def preprocess_html(self, soup): @@ -65,3 +84,10 @@ class Danas(BasicNewsRecipe): def print_version(self, url): return url + '&action=print' + def get_cover_url(self): + cover_url = None + soup = self.index_to_soup('http://www.danas.rs/') + for citem in soup.findAll('img'): + if citem['src'].endswith('naslovna.jpg'): + return 'http://www.danas.rs' + citem['src'] + return cover_url From 60219fe54cd83e3ce53ebd21ca985b6aa62f044b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 10:37:27 -0600 Subject: [PATCH 09/17] Add author_sort_map to MetaInformation --- src/calibre/ebooks/metadata/__init__.py | 7 ++++++- src/calibre/ebooks/metadata/book/__init__.py | 2 ++ src/calibre/ebooks/metadata/book/base.py | 1 + src/calibre/library/database2.py | 6 +++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index c633e5149b..8caca1f261 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -223,6 +223,7 @@ class MetaInformation(object): 'isbn', 'tags', 'cover_data', 'application_id', 'guide', 'manifest', 'spine', 'toc', 'cover', 'language', 'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', + 'author_sort_map', 'pubdate', 'rights', 'publication_type', 'uuid'): if hasattr(mi, attr): setattr(ans, attr, getattr(mi, attr)) @@ -244,6 +245,7 @@ class MetaInformation(object): self.tags = getattr(mi, 'tags', []) #: mi.cover_data = (ext, data) self.cover_data = getattr(mi, 'cover_data', (None, None)) + self.author_sort_map = getattr(mi, 'author_sort_map', {}) for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'language', @@ -258,7 +260,7 @@ class MetaInformation(object): 'series', 'series_index', 'tags', 'rating', 'isbn', 'language', 'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover', 'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', - 'rights', 'publication_type', 'uuid' + 'rights', 'publication_type', 'uuid', 'author_sort_map' ): prints(x, getattr(self, x, 'None')) @@ -288,6 +290,9 @@ class MetaInformation(object): self.tags += mi.tags self.tags = list(set(self.tags)) + if mi.author_sort_map: + self.author_sort_map.update(mi.author_sort_map) + if getattr(mi, 'cover_data', False): other_cover = mi.cover_data[-1] self_cover = self.cover_data[-1] if self.cover_data else '' diff --git a/src/calibre/ebooks/metadata/book/__init__.py b/src/calibre/ebooks/metadata/book/__init__.py index 8483c2bddb..c3b95f1188 100644 --- a/src/calibre/ebooks/metadata/book/__init__.py +++ b/src/calibre/ebooks/metadata/book/__init__.py @@ -35,6 +35,8 @@ PUBLICATION_METADATA_FIELDS = frozenset([ 'title_sort', # Ordered list of authors. Must never be None, can be [_('Unknown')] 'authors', + # Map of sort strings for each author + 'author_sort_map', # Pseudo field that can be set, but if not set is auto generated # from authors and languages 'author_sort', diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index ad5dd17ace..ba34f04f95 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -16,6 +16,7 @@ NULL_VALUES = { 'classifiers' : {}, 'languages' : [], 'device_collections': [], + 'author_sort_map': {}, 'authors' : [_('Unknown')], 'title' : _('Unknown'), } diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 321a5fce17..c7830187df 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -433,7 +433,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if aum: aum = [a.strip().replace('|', ',') for a in aum.split(',')] mi = MetaInformation(self.title(idx, index_is_id=index_is_id), aum) mi.author_sort = self.author_sort(idx, index_is_id=index_is_id) - mi.authors_sort_strings = self.authors_sort_strings(idx, index_is_id) + if mi.authors: + mi.author_sort_map = {} + for name, sort in zip(mi.authors, self.authors_sort_strings(idx, + index_is_id)): + mi.author_sort_map[name] = sort mi.comments = self.comments(idx, index_is_id=index_is_id) mi.publisher = self.publisher(idx, index_is_id=index_is_id) mi.timestamp = self.timestamp(idx, index_is_id=index_is_id) From 6d80e2093cee088427e2d5fd50099f2fdc0bacb4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 11:03:19 -0600 Subject: [PATCH 10/17] Thai Rath and The Nation (Thailand) by Anat Ruangrassamee --- resources/recipes/thairath.recipe | 58 ++++++++++++++++++++++++ resources/recipes/the_nation_thai.recipe | 44 ++++++++++++++++++ src/calibre/utils/localization.py | 1 + 3 files changed, 103 insertions(+) create mode 100644 resources/recipes/thairath.recipe create mode 100644 resources/recipes/the_nation_thai.recipe diff --git a/resources/recipes/thairath.recipe b/resources/recipes/thairath.recipe new file mode 100644 index 0000000000..6ebb84f3a5 --- /dev/null +++ b/resources/recipes/thairath.recipe @@ -0,0 +1,58 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1271637235(BasicNewsRecipe): + + title = u'Thairath' + __author__ = 'Anat R.' + language = 'th' + + oldest_article = 7 + + max_articles_per_feed = 100 + no_stylesheets = True + + remove_javascript = True + + use_embedded_content = False + feeds = [(u'News', +u'http://www.thairath.co.th/rss/news.xml'), (u'Politics', +u'http://www.thairath.co.th/rss/pol.xml'), (u'Economy', +u'http://www.thairath.co.th/rss/eco.xml'), (u'International', +u'http://www.thairath.co.th/rss/oversea.xml'), (u'Sports', +u'http://www.thairath.co.th/rss/sport.xml'), (u'Life', +u'http://www.thairath.co.th/rss/life.xml'), (u'Education', +u'http://www.thairath.co.th/rss/edu.xml'), (u'Tech', +u'http://www.thairath.co..th/rss/tech.xml'), (u'Entertainment', +u'http://www.thairath.co.th/rss/ent.xml')] + keep_only_tags = [] + + keep_only_tags.append(dict(name = 'h1', attrs = {'id' : 'title'})) + + keep_only_tags.append(dict(name = 'ul', attrs = {'class' : +'detail-info'})) + + keep_only_tags.append(dict(name = 'img', attrs = {'class' : +'detail-image'})) + + keep_only_tags.append(dict(name = 'div', attrs = {'class' : +'entry'})) + remove_tags = [] + remove_tags.append(dict(name = 'div', attrs = {'id': +'menu-holder'})) + + remove_tags.append(dict(name = 'div', attrs = {'class': +'addthis_toolbox addthis_default_style'})) + + remove_tags.append(dict(name = 'div', attrs = {'class': 'box top-item'})) + + remove_tags.append(dict(name = 'div', attrs = {'class': 'column-200 column-margin-430'})) + + remove_tags.append(dict(name = 'div', attrs = {'id': +'detail-related'})) + + remove_tags.append(dict(name = 'div', attrs = {'id': 'related'})) + + remove_tags.append(dict(name = 'id', attrs = {'class': 'footer'})) + + remove_tags.append(dict(name = "ul",attrs = +{'id':'banner-highlights-images'})) diff --git a/resources/recipes/the_nation_thai.recipe b/resources/recipes/the_nation_thai.recipe new file mode 100644 index 0000000000..a33a16e0a5 --- /dev/null +++ b/resources/recipes/the_nation_thai.recipe @@ -0,0 +1,44 @@ + +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1271596863(BasicNewsRecipe): + + title = u'The Nation' + __author__ = 'Anat R.' + language = 'en_TH' + + oldest_article = 7 + + max_articles_per_feed = 100 + no_stylesheets = True + + remove_javascript = True + + use_embedded_content = False + feeds = [(u'Topstory', +u'http://www.nationmultimedia.com/home/rss/topstories.rss'), +(u'National', u'http://www.nationmultimedia.com/home/rss/national.rss'), + (u'Politics', +u'http://www.nationmultimedia.com/home/rss/politics.rss'), (u'Business', + u'http://www.nationmultimedia.com/home/rss/business.rss'), +(u'Regional', u'http://www.nationmultimedia.com/home/rss/regional.rss'), + (u'Sports', u'http://www.nationmultimedia.com/home/rss/sport.rss'), +(u'Travel', u'http://www.nationmultimedia.com/home/rss/travel.rss'), +(u'Life', u'http://www.nationmultimedia.com/home/rss/life.rss')] + keep_only_tags = [] + + keep_only_tags.append(dict(name = 'div', attrs = {'class' : +'pd10'})) + remove_tags = [] + + remove_tags.append(dict(name = 'div', attrs = {'class': +'WrapperHeaderCol2-2'})) + + remove_tags.append(dict(name = 'div', attrs = {'class': +'LayoutMenu2'})) + + remove_tags.append(dict(name = 'div', attrs = {'class': +'TextHeaderRight'})) + + remove_tags.append(dict(name = "ul",attrs = {'id':'toolZoom'})) + diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py index 026547ee2e..e60a3233c6 100644 --- a/src/calibre/utils/localization.py +++ b/src/calibre/utils/localization.py @@ -103,6 +103,7 @@ _extra_lang_codes = { 'en_TH' : _('English (Thailand)'), 'en_CY' : _('English (Cyprus)'), 'en_PK' : _('English (Pakistan)'), + 'en_IL' : _('English (Israel)'), 'en_SG' : _('English (Singapore)'), 'en_YE' : _('English (Yemen)'), 'en_IE' : _('English (Ireland)'), From 9e31b075da56b77217eda794ce39b5415f4ccf41 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 11:06:21 -0600 Subject: [PATCH 11/17] Click to open in book details on device view now opens the actual file instead of the folder containing it --- src/calibre/gui2/book_details.py | 5 +---- src/calibre/gui2/status.py | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 75c045d011..8cf726420c 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -258,8 +258,7 @@ class BookDetails(QWidget): id_, fmt = val.split(':') self.view_specific_format.emit(int(id_), fmt) elif typ == 'devpath': - path = os.path.dirname(val) - QDesktopServices.openUrl(QUrl.fromLocalFile(path)) + QDesktopServices.openUrl(QUrl.fromLocalFile(val)) def mouseReleaseEvent(self, ev): @@ -275,8 +274,6 @@ class BookDetails(QWidget): self.setToolTip('

'+_('Click to open Book Details window') + '

' + _('Path') + ': ' + data.get(_('Path'), '')) - - def reset_info(self): self.show_data({}) diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index 90426f8021..9aa9b8262c 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -239,8 +239,7 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface): id_, fmt = val.split(':') self.view_specific_format.emit(int(id_), fmt) elif typ == 'devpath': - path = os.path.dirname(val) - QDesktopServices.openUrl(QUrl.fromLocalFile(path)) + QDesktopServices.openUrl(QUrl.fromLocalFile(val)) def resizeEvent(self, ev): From 6db976e5aaf973ffa72b6a6a0dac5fe6748a6258 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 11:16:09 -0600 Subject: [PATCH 12/17] Fix #5843 (Book covers not displaying when in device view) --- src/calibre/gui2/book_details.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 8cf726420c..a397ab903d 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -109,7 +109,7 @@ class CoverView(QWidget): # {{{ def show_data(self, data): self.animation.stop() - if data.get('id', None) == self.data.get('id', None): + if data.get('id', True) == self.data.get('id', False): return self.data = {'id':data.get('id', None)} if data.has_key('cover'): From 41c5a609204840fa0b6d008c8d39ebf48ab969cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 16:27:52 -0600 Subject: [PATCH 13/17] OS X/linux driver for PocketBook 301 --- src/calibre/customize/builtins.py | 3 ++- src/calibre/devices/eb600/driver.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index ca93990420..ec895cb8a4 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -436,7 +436,7 @@ from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.cybook.driver import CYBOOK from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \ POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \ - BOOQ, ELONEX + BOOQ, ELONEX, POCKETBOOK301 from calibre.devices.iliad.driver import ILIAD from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 from calibre.devices.jetbook.driver import JETBOOK @@ -507,6 +507,7 @@ plugins += [ JETBOOK, SHINEBOOK, POCKETBOOK360, + POCKETBOOK301, KINDLE, KINDLE2, KINDLE_DX, diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 307531c357..9b7a21a3bb 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -201,4 +201,21 @@ class ELONEX(EB600): def can_handle(cls, dev, debug=False): return dev[3] == 'Elonex' and dev[4] == 'eBook' +class POCKETBOOK301(USBMS): + + name = 'PocketBook 301 Device Interface' + description = _('Communicate with the PocketBook 301 reader.') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm', 'txt'] + + SUPPORTS_SUB_DIRS = True + + MAIN_MEMORY_VOLUME_LABEL = 'PocketBook 301 Main Memory' + STORAGE_CARD_VOLUME_LABEL = 'PocketBook 301 Storage Card' + + VENDOR_ID = [0x1] + PRODUCT_ID = [0x301] + BCD = [0x132] + From e26630370648e94d9b74b5f61bdde4b9551b4edf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 17:23:55 -0600 Subject: [PATCH 14/17] Separate status bar and book details in narrow alyout as well --- resources/images/user_profile.svg | 4 +- src/calibre/gui2/book_details.py | 69 ++++-- src/calibre/gui2/dialogs/config/config.ui | 6 +- src/calibre/gui2/init.py | 54 +++-- src/calibre/gui2/status.py | 253 ---------------------- src/calibre/gui2/ui.py | 10 +- 6 files changed, 96 insertions(+), 300 deletions(-) delete mode 100644 src/calibre/gui2/status.py diff --git a/resources/images/user_profile.svg b/resources/images/user_profile.svg index 2fc0eea150..c809eeaeb1 100644 --- a/resources/images/user_profile.svg +++ b/resources/images/user_profile.svg @@ -33,7 +33,7 @@ %s:%s'%(k,t) for k, t in rows]) - if _('Comments') in data and data[_('Comments')]: - comments = comments_to_html(data[_('Comments')]) - rows += u'%s'%comments + if self.vertical: + if _('Comments') in data and data[_('Comments')]: + comments = comments_to_html(data[_('Comments')]) + rows += u'%s'%comments + self.label.setText(u'%s
'%rows) + else: + comments = '' + if _('Comments') in data: + comments = comments_to_html(data[_('Comments')]) + left_pane = u'%s
'%rows + right_pane = u'

%s
'%comments + self.label.setText(u'
%s%s
' + % (left_pane, right_pane)) - self.label.setText(u'%s
'%rows) -class BookDetails(QWidget): +# }}} + +class BookDetails(QWidget): # {{{ resized = pyqtSignal(object) show_book_info = pyqtSignal() @@ -234,20 +255,26 @@ class BookDetails(QWidget): # }}} - def __init__(self, parent=None): + def __init__(self, vertical, parent=None): QWidget.__init__(self, parent) + self.setAcceptDrops(True) self._layout = QVBoxLayout() - + if not vertical: + self._layout.setDirection(self._layout.LeftToRight) self.setLayout(self._layout) - self.cover_view = CoverView(self) + + self.cover_view = CoverView(vertical, self) self.cover_view.relayout(self.size()) self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection) - self._layout.addWidget(self.cover_view, alignment=Qt.AlignHCenter) - self.book_info = BookInfo(self) + self._layout.addWidget(self.cover_view) + self.book_info = BookInfo(vertical, self) self._layout.addWidget(self.book_info) self.book_info.link_clicked.connect(self._link_clicked) self.book_info.mr.connect(self.mouseReleaseEvent) - self.setMinimumSize(QSize(190, 200)) + if vertical: + self.setMinimumSize(QSize(190, 200)) + else: + self.setMinimumSize(120, 120) self.setCursor(Qt.PointingHandCursor) def _link_clicked(self, link): @@ -277,5 +304,5 @@ class BookDetails(QWidget): def reset_info(self): self.show_data({}) - +# }}} diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index ba92c0d301..efda00fc97 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -7,7 +7,7 @@ 0 0 - 884 + 1000 730 @@ -89,7 +89,7 @@ 0 0 - 604 + 720 679 @@ -370,7 +370,7 @@ - + Show &average ratings in the tags browser diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 1277cb06c7..f1b989ffd8 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -8,17 +8,18 @@ __docformat__ = 'restructuredtext en' import functools from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon, QStackedWidget, \ - QWidget, QHBoxLayout, QToolBar, QSize, QSizePolicy + QWidget, QHBoxLayout, QToolBar, QSize, QSizePolicy, QStatusBar from calibre.utils.config import prefs from calibre.ebooks import BOOK_EXTENSIONS -from calibre.constants import isosx, __appname__ +from calibre.constants import isosx, __appname__, preferred_encoding from calibre.gui2 import config, is_widescreen from calibre.gui2.library.views import BooksView, DeviceBooksView from calibre.gui2.widgets import Splitter from calibre.gui2.tag_view import TagBrowserWidget -from calibre.gui2.status import StatusBar, HStatusBar from calibre.gui2.book_details import BookDetails +from calibre.gui2.notify import get_notifier + _keep_refs = [] @@ -355,6 +356,27 @@ class SideBar(QToolBar): # {{{ # }}} +class StatusBar(QStatusBar): # {{{ + + def initialize(self, systray=None): + self.systray = systray + self.notifier = get_notifier(systray) + + def show_message(self, msg, timeout=0): + QStatusBar.showMessage(self, msg, timeout) + if self.notifier is not None and not config['disable_tray_notification']: + if isosx and isinstance(msg, unicode): + try: + msg = msg.encode(preferred_encoding) + except UnicodeEncodeError: + msg = msg.encode('utf-8') + self.notifier(msg) + + def clear_message(self): + QStatusBar.clearMessage(self) + +# }}} + class LayoutMixin(object): # {{{ def __init__(self): @@ -362,7 +384,7 @@ class LayoutMixin(object): # {{{ self.setWindowTitle(__appname__) if config['gui_layout'] == 'narrow': - self.status_bar = self.book_details = StatusBar(self) + self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), @@ -375,31 +397,37 @@ class LayoutMixin(object): # {{{ for x in ('bd', 'tb', 'cb')], self.jobs_button, parent=self) l.addWidget(self.sidebar) self.bd_splitter.addWidget(self._layout_mem[0]) - self.bd_splitter.addWidget(self.status_bar) + self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) else: - self.status_bar = HStatusBar(self) - self.setStatusBar(self.status_bar) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) - self.book_details = BookDetails(self) + self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) - for x in ('cb', 'tb', 'bd'): - button = getattr(self, x+'_splitter').button - button.setIconSize(QSize(22, 22)) - self.status_bar.addPermanentWidget(button) - self.status_bar.addPermanentWidget(self.jobs_button) + self.status_bar = StatusBar(self) + self.setStatusBar(self.status_bar) + for x in ('cb', 'tb', 'bd'): + button = getattr(self, x+'_splitter').button + button.setIconSize(QSize(22, 22)) + self.status_bar.addPermanentWidget(button) + self.status_bar.addPermanentWidget(self.jobs_button) def finalize_layout(self): + self.status_bar.initialize(self.system_tray_icon) + self.book_details.show_book_info.connect(self.show_book_info) + self.book_details.files_dropped.connect(self.files_dropped_on_book) + self.book_details.open_containing_folder.connect(self.view_folder_for_id) + self.book_details.view_specific_format.connect(self.view_format_by_id) + m = self.library_view.model() if m.rowCount(None) > 0: self.library_view.set_current_row(0) diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py deleted file mode 100644 index 9aa9b8262c..0000000000 --- a/src/calibre/gui2/status.py +++ /dev/null @@ -1,253 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' - -import os - -from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \ - QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \ - QPropertyAnimation, QEasingCurve, QDesktopServices, QUrl - - -from calibre import fit_image, preferred_encoding, isosx -from calibre.gui2 import config -from calibre.gui2.widgets import IMAGE_EXTENSIONS -from calibre.gui2.notify import get_notifier -from calibre.ebooks import BOOK_EXTENSIONS -from calibre.library.comments import comments_to_html -from calibre.gui2.book_details import render_rows - -class BookInfoDisplay(QWidget): - - DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS - files_dropped = pyqtSignal(object, object) - - @classmethod - def paths_from_event(cls, event): - ''' - Accept a drop event and return a list of paths that can be read from - and represent files with extensions. - ''' - if event.mimeData().hasFormat('text/uri-list'): - urls = [unicode(u.toLocalFile()) for u in event.mimeData().urls()] - urls = [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)] - return [u for u in urls if os.path.splitext(u)[1][1:].lower() in cls.DROPABBLE_EXTENSIONS] - - def dragEnterEvent(self, event): - if int(event.possibleActions() & Qt.CopyAction) + \ - int(event.possibleActions() & Qt.MoveAction) == 0: - return - paths = self.paths_from_event(event) - if paths: - event.acceptProposedAction() - - def dropEvent(self, event): - paths = self.paths_from_event(event) - event.setDropAction(Qt.CopyAction) - self.files_dropped.emit(event, paths) - - def dragMoveEvent(self, event): - event.acceptProposedAction() - - - class BookCoverDisplay(QLabel): # {{{ - - def __init__(self, coverpath=I('book.svg')): - QLabel.__init__(self) - self.animation = QPropertyAnimation(self, 'size', self) - self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) - self.animation.setDuration(1000) - self.animation.setStartValue(QSize(0, 0)) - self.setMaximumWidth(81) - self.setMaximumHeight(108) - self.default_pixmap = QPixmap(coverpath) - self.setScaledContents(True) - self.statusbar_height = 120 - self.setPixmap(self.default_pixmap) - - def do_layout(self): - self.animation.stop() - pixmap = self.pixmap() - pwidth, pheight = pixmap.width(), pixmap.height() - width, height = fit_image(pwidth, pheight, - pwidth, self.statusbar_height-20)[1:] - self.setMaximumHeight(height) - try: - aspect_ratio = pwidth/float(pheight) - except ZeroDivisionError: - aspect_ratio = 1 - self.setMaximumWidth(int(aspect_ratio*self.maximumHeight())) - self.animation.setEndValue(self.maximumSize()) - - def setPixmap(self, pixmap): - QLabel.setPixmap(self, pixmap) - self.do_layout() - self.animation.start() - - def sizeHint(self): - return QSize(self.maximumWidth(), self.maximumHeight()) - - def relayout(self, statusbar_size): - self.statusbar_height = statusbar_size.height() - self.do_layout() - - # }}} - - class BookDataDisplay(QLabel): - - mr = pyqtSignal(object) - link_clicked = pyqtSignal(object) - - def __init__(self): - QLabel.__init__(self) - self.setText('') - self.setWordWrap(True) - self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) - self.linkActivated.connect(self.link_activated) - self._link_clicked = False - - def mouseReleaseEvent(self, ev): - QLabel.mouseReleaseEvent(self, ev) - if not self._link_clicked: - self.mr.emit(ev) - self._link_clicked = False - - def link_activated(self, link): - self._link_clicked = True - link = unicode(link) - self.link_clicked.emit(link) - - show_book_info = pyqtSignal() - - def __init__(self, clear_message): - QWidget.__init__(self) - self.setCursor(Qt.PointingHandCursor) - self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) - self._layout = QHBoxLayout() - self.setLayout(self._layout) - self.clear_message = clear_message - self.cover_display = BookInfoDisplay.BookCoverDisplay() - self._layout.addWidget(self.cover_display) - self.book_data = BookInfoDisplay.BookDataDisplay() - self.book_data.mr.connect(self.mouseReleaseEvent) - self._layout.addWidget(self.book_data) - self.data = {} - self.setVisible(False) - self._layout.setAlignment(self.cover_display, Qt.AlignTop|Qt.AlignLeft) - - def mouseReleaseEvent(self, ev): - ev.accept() - self.show_book_info.emit() - - def show_data(self, data): - if data.has_key('cover'): - self.cover_display.setPixmap(QPixmap.fromImage(data.pop('cover'))) - else: - self.cover_display.setPixmap(self.cover_display.default_pixmap) - - rows, comments = [], '' - self.book_data.setText('') - self.data = data.copy() - rows = render_rows(self.data) - rows = '\n'.join([u'%s:%s'%(k,t) for - k, t in rows]) - if _('Comments') in self.data: - comments = comments_to_html(self.data[_('Comments')]) - comments = ('%s:'%_('Comments'))+comments - left_pane = u'%s
'%rows - right_pane = u'
%s
'%comments - self.book_data.setText(u'
%s%s
' - % (left_pane, right_pane)) - - self.clear_message() - self.book_data.updateGeometry() - self.updateGeometry() - self.setVisible(True) - self.setToolTip('

'+_('Click to open Book Details window') + - '

' + _('Path') + ': ' + data.get(_('Path'), '')) - - - -class StatusBarInterface(object): - - def initialize(self, systray=None): - self.systray = systray - self.notifier = get_notifier(systray) - - def show_message(self, msg, timeout=0): - QStatusBar.showMessage(self, msg, timeout) - if self.notifier is not None and not config['disable_tray_notification']: - if isosx and isinstance(msg, unicode): - try: - msg = msg.encode(preferred_encoding) - except UnicodeEncodeError: - msg = msg.encode('utf-8') - self.notifier(msg) - - def clear_message(self): - QStatusBar.clearMessage(self) - -class BookDetailsInterface(object): - - # These signals must be defined in the class implementing this interface - files_dropped = None - show_book_info = None - open_containing_folder = None - view_specific_format = None - - def reset_info(self): - raise NotImplementedError() - - def show_data(self, data): - raise NotImplementedError() - -class HStatusBar(QStatusBar, StatusBarInterface): - pass - -class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface): - - files_dropped = pyqtSignal(object, object) - show_book_info = pyqtSignal() - open_containing_folder = pyqtSignal(int) - view_specific_format = pyqtSignal(int, object) - - resized = pyqtSignal(object) - - def initialize(self, systray=None): - StatusBarInterface.initialize(self, systray=systray) - self.book_info = BookInfoDisplay(self.clear_message) - self.book_info.setAcceptDrops(True) - self.scroll_area = QScrollArea() - self.scroll_area.setWidget(self.book_info) - self.scroll_area.setWidgetResizable(True) - self.book_info.show_book_info.connect(self.show_book_info.emit, - type=Qt.QueuedConnection) - self.book_info.files_dropped.connect(self.files_dropped.emit, - type=Qt.QueuedConnection) - self.book_info.book_data.link_clicked.connect(self._link_clicked) - self.addWidget(self.scroll_area, 100) - self.setMinimumHeight(120) - self.resized.connect(self.book_info.cover_display.relayout) - self.book_info.cover_display.relayout(self.size()) - - - def _link_clicked(self, link): - typ, _, val = link.partition(':') - if typ == 'path': - self.open_containing_folder.emit(int(val)) - elif typ == 'format': - id_, fmt = val.split(':') - self.view_specific_format.emit(int(id_), fmt) - elif typ == 'devpath': - QDesktopServices.openUrl(QUrl.fromLocalFile(val)) - - - def resizeEvent(self, ev): - self.resized.emit(self.size()) - - def reset_info(self): - self.book_info.show_data({}) - - def show_data(self, data): - self.book_info.show_data(data) - diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 2226520cf2..e9046b7439 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -104,6 +104,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ def __init__(self, opts, parent=None): MainWindow.__init__(self, opts, parent) self.opts = opts + self.setUnifiedTitleAndToolBarOnMac(True) def initialize(self, library_path, db, listener, actions): opts = self.opts @@ -126,8 +127,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) - self.jobs_button = JobsButton(horizontal=config['gui_layout'] != - 'narrow') + self.jobs_button = JobsButton(horizontal=True) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} @@ -216,12 +216,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ self.vanity.setText(self.vanity_template%dict(version=' ', device=' ')) self.device_info = ' ' UpdateMixin.__init__(self, opts) - ####################### Status Bar ##################### - self.status_bar.initialize(self.system_tray_icon) - self.book_details.show_book_info.connect(self.show_book_info) - self.book_details.files_dropped.connect(self.files_dropped_on_book) - self.book_details.open_containing_folder.connect(self.view_folder_for_id) - self.book_details.view_specific_format.connect(self.view_format_by_id) ####################### Setup Toolbar ##################### ToolbarMixin.__init__(self) From 957bac503cceca7e7c9072508129bbdf48cff447 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 17:56:33 -0600 Subject: [PATCH 15/17] ... --- src/calibre/gui2/init.py | 44 ++++++++-------------------------------- src/calibre/gui2/ui.py | 1 - 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index f1b989ffd8..0a82d3b75b 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import functools from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon, QStackedWidget, \ - QWidget, QHBoxLayout, QToolBar, QSize, QSizePolicy, QStatusBar + QSize, QSizePolicy, QStatusBar from calibre.utils.config import prefs from calibre.ebooks import BOOK_EXTENSIONS @@ -333,29 +333,6 @@ class Stack(QStackedWidget): # {{{ # }}} -class SideBar(QToolBar): # {{{ - - - def __init__(self, splitters, jobs_button, parent=None): - QToolBar.__init__(self, _('Side bar'), parent) - self.setOrientation(Qt.Vertical) - self.setMovable(False) - self.setFloatable(False) - self.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.setIconSize(QSize(48, 48)) - self.spacer = QWidget(self) - self.spacer.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) - for s in splitters: - self.addWidget(s.button) - self.addWidget(self.spacer) - self.addWidget(jobs_button) - - for ch in self.children(): - if isinstance(ch, QToolButton): - ch.setCursor(Qt.PointingHandCursor) - -# }}} - class StatusBar(QStatusBar): # {{{ def initialize(self, systray=None): @@ -383,24 +360,18 @@ class LayoutMixin(object): # {{{ self.setupUi(self) self.setWindowTitle(__appname__) - if config['gui_layout'] == 'narrow': + if config['gui_layout'] == 'narrow': # narrow {{{ self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), orientation=Qt.Vertical, parent=self, side_index=1) - self._layout_mem = [QWidget(self), QHBoxLayout()] - self._layout_mem[0].setLayout(self._layout_mem[1]) - l = self._layout_mem[1] - l.addWidget(self.stack) - self.sidebar = SideBar([getattr(self, x+'_splitter') - for x in ('bd', 'tb', 'cb')], self.jobs_button, parent=self) - l.addWidget(self.sidebar) - self.bd_splitter.addWidget(self._layout_mem[0]) + self.bd_splitter.addWidget(self.stack) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) - else: + # }}} + else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1) @@ -412,14 +383,15 @@ class LayoutMixin(object): # {{{ self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) + # }}} self.status_bar = StatusBar(self) - self.setStatusBar(self.status_bar) for x in ('cb', 'tb', 'bd'): button = getattr(self, x+'_splitter').button - button.setIconSize(QSize(22, 22)) + button.setIconSize(QSize(24, 24)) self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) + self.setStatusBar(self.status_bar) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index e9046b7439..aa2d94a637 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -104,7 +104,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ def __init__(self, opts, parent=None): MainWindow.__init__(self, opts, parent) self.opts = opts - self.setUnifiedTitleAndToolBarOnMac(True) def initialize(self, library_path, db, listener, actions): opts = self.opts From f79ae5139723ba03438d1ed73531be54d6da39dd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 19:23:00 -0600 Subject: [PATCH 16/17] Improved icons --- resources/images/devices/folder.svg | 1190 ++++--- resources/images/user_profile.svg | 4604 +++++++++++++++++++++++++-- 2 files changed, 5002 insertions(+), 792 deletions(-) diff --git a/resources/images/devices/folder.svg b/resources/images/devices/folder.svg index 74c1d628e4..e0d6f6b8be 100644 --- a/resources/images/devices/folder.svg +++ b/resources/images/devices/folder.svg @@ -9,544 +9,700 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - x="0.0000000" - y="0.0000000" - width="48.000000px" - height="48.000000px" - id="svg1" + width="128" + height="128" + id="svg2811" sodipodi:version="0.32" - inkscape:version="0.44" - sodipodi:docname="folder.svg" - sodipodi:docbase="/home/lapo/Icone/Crux/crux-icon-theme/scalable/places" - inkscape:export-filename="/home/lapo/Icone/Crux/folderx-daritaliare.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> + inkscape:version="0.45.1" + version="1.0" + sodipodi:docname="folder-downloads.svgz" + inkscape:output_extension="org.inkscape.output.svgz.inkscape" + inkscape:export-filename="folder-downloads.png" + inkscape:export-xdpi="11.25" + inkscape:export-ydpi="11.25" + sodipodi:docbase="/home/david/oxygen/trunk/scalable/places"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="metadata2816"> image/svg+xml - Folder - - - Lapo Calamandrei - - - 2006-06-26 - - - - - folder - directory - storage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="layer1"> + - + id="g17" + style="opacity:0.6;filter:url(#filter2807)" + transform="matrix(1.0033404,0,0,1,-8.2374684,8)"> - + d="M 132,96 C 132,98.2 128.4,100 124,100 L 20,100 C 15.6,100 12,98.2 12,96 C 12,93.8 15.6,92 20,92 L 124,92 C 128.4,92 132,93.8 132,96 z" + id="path19" /> + + + + + + id="g2450" + inkscape:label="Livello 1" + transform="translate(-2.4797995e-7,16)"> - - - - - - - - - + d="M 88,103.99999 C 88,106.20914 77.254827,108 63.999997,108 C 50.745166,108 40,106.20914 40,103.99999 C 40,101.79086 50.745166,100 63.999997,100 C 77.254827,100 88,101.79086 88,103.99999 L 88,103.99999 z " + style="opacity:0.3;fill:url(#radialGradient4545);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" + id="path4543" /> + transform="matrix(1,0,0,0.9756098,-72.426501,80.585366)" + id="g9589"> + style="fill:url(#linearGradient9614)" + d="M 122.40803,-58 C 121.11995,-58 120.07225,-56.801009 120.07225,-55.327765 C 120.07225,-55.327765 120.07225,-20.243475 120.07225,-17.752469 C 118.07565,-17.752469 105.76289,-17.752469 105.76289,-17.752469 C 104.82364,-17.752469 103.98018,-17.113701 103.61327,-16.125567 C 103.61266,-16.122801 103.4265,-15.080231 103.4265,-15.080231 C 103.4265,-14.384954 103.65845,-13.726198 104.08019,-13.22662 L 134.74308,23.18138 C 135.18048,23.70094 135.7944,24 136.42579,24 C 137.05778,24 137.6705,23.70094 138.1085,23.18138 L 168.772,-13.22662 C 169.42327,-14.000449 169.60703,-15.136741 169.23953,-16.125567 C 168.87081,-17.115074 168.02735,-17.751777 167.08929,-17.751777 C 167.08929,-17.751777 154.77654,-17.751777 152.77994,-17.751777 C 152.77994,-20.243475 152.77994,-55.327076 152.77994,-55.327076 C 152.77933,-56.801009 151.73164,-58 150.44355,-58 L 122.40803,-58 z " + id="path49" /> + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + style="opacity:0.5;fill:#ffffff" + enable-background="new " + d="M 152.77933,-15.331057 C 154.77534,-15.331057 167.08869,-15.331057 167.08869,-15.331057 C 168.02794,-15.331057 168.87021,-14.692971 169.23832,-13.704155 C 169.2594,-13.646273 169.27387,-13.585625 169.29193,-13.527062 C 169.48051,-14.144468 169.47147,-14.830788 169.23832,-15.457162 C 168.87021,-16.445977 168.02614,-17.084064 167.08869,-17.084064 C 167.08869,-17.084064 154.77534,-17.084064 152.77933,-17.084064 C 152.77933,-16.201364 152.77933,-15.589458 152.77933,-15.331057 z " + id="path74" /> + + + + + + + + diff --git a/resources/images/user_profile.svg b/resources/images/user_profile.svg index c809eeaeb1..0aecc0c1f7 100644 --- a/resources/images/user_profile.svg +++ b/resources/images/user_profile.svg @@ -1,6 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:version="0.46" + version="1.0" + sodipodi:docname="im-user.svgz" + inkscape:output_extension="org.inkscape.output.svgz.inkscape" + sodipodi:docbase="/home/pinheiro/pics/oxygen/scalable/mimetypes" + inkscape:export-filename="/home/pinheiro/pics/oxygen/scalable/actions/im-user.png" + inkscape:export-xdpi="180" + inkscape:export-ydpi="180"> + inkscape:current-layer="layer1" + width="128px" + height="128px" + showgrid="false" + inkscape:grid-points="true" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1016" + inkscape:window-height="692" + inkscape:window-x="20" + inkscape:window-y="356"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="metadata2611"> image/svg+xml - - - - Jakub Steiner - - - http://jimmac.musichall.cz - - - user - person - - - - - - - - - - - - - - - - - - - - - + inkscape:label="Livello 1"> + style="fill:#493a3a;fill-opacity:1;stroke:none;stroke-width:3.1559999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3766)" + id="path3716" + sodipodi:cx="63.865829" + sodipodi:cy="108.4109" + sodipodi:rx="41.861637" + sodipodi:ry="13.953878" + d="M 105.72747,108.4109 A 41.861637,13.953878 0 1 1 22.004192,108.4109 A 41.861637,13.953878 0 1 1 105.72747,108.4109 z" + transform="matrix(0.8610583,0,0,1.1808092,8.9931858,-19.441249)" /> + + + + + + + + + style="fill:url(#radialGradient4322);fill-opacity:1;stroke:none;stroke-width:3.40000010000000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path2563" + sodipodi:cx="58.041332" + sodipodi:cy="37.27911" + sodipodi:rx="29.958668" + sodipodi:ry="29.958668" + d="M 88,37.27911 A 29.958668,29.958668 0 1 1 28.082664,37.27911 A 29.958668,29.958668 0 1 1 88,37.27911 z" + transform="matrix(0.844028,0,0,0.844028,14.927656,-4.6758122)" /> + + + + + + From 1fe90c2d537bd119bacd5520b6049475d60a18bc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jun 2010 20:27:18 -0600 Subject: [PATCH 17/17] EPUB Output: Remove workaround for Adobe Digital Editions' faulty rendering of links in html. calibre no longer forces links to be blue and underlined --- src/calibre/ebooks/epub/output.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index ee779aaefa..8708b98d97 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -385,14 +385,6 @@ class EPUBOutput(OutputFormatPlugin): if val and not pval: rule.style.setProperty('padding-left', val) - if stylesheet is not None: - stylesheet.data.add('a { color: inherit; text-decoration: inherit; ' - 'cursor: default; }') - stylesheet.data.add('a[href] { color: blue; ' - 'text-decoration: underline; cursor:pointer; }') - else: - self.oeb.log.warn('No stylesheet found') - # }}} def workaround_sony_quirks(self): # {{{