diff --git a/Changelog.yaml b/Changelog.yaml
index 12f1b570bc..bcf58ae03d 100644
--- a/Changelog.yaml
+++ b/Changelog.yaml
@@ -4,6 +4,126 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
+- version: 0.7.34
+ date: 2010-12-17
+
+ new features:
+ - title: "Page turn animations in the e-book viewer"
+ type: major
+ description: >
+ "Now when you use the Page Down/Page Up keys or the next/previous page buttons in the viewer, page turning will be animated. The duration of the animation can be controlled in the viewer preferences. Setting it to 0 disables the animation completely."
+
+ - title: "Conversion pipeline: Add an option to set the minimum line height of all elemnts as a percentage of the computed font size. By default, calibre now sets the line height to 120% of the computed font size."
+
+ - title: "Large speedup in startup times and post metadata edit wait for large libraries"
+
+ - title: "Allow changing the font used in the calibre interface via Preferences->Look and feel"
+
+ - title: "Allow editing of the title sort value for a book via the edit metadata dialog"
+
+ - title: "Disable the cover cache. This means that if you are running calibre on an underpowered machine, you might notice some slow down in the cover browser. On the other hand, calibre's memory consumption is reduced."
+
+ - title: "You can now restart calibre in debug mode by clicking the arrow next to the Preferences button. In debug mode, after you quit calibre, a diagnostic log will popup"
+ tickets: [7359]
+
+ - title: "When creating a new calibre library add an option to copy the custom column, saved searches, etc from the current library."
+ tickets: [7643]
+
+ - title: "Add more tweaks to control how the next available series number is calculated."
+ tickets: [7892]
+
+ - title: "Add a tweak to control layout of the custom metadata tab in the edit metadata dialog"
+
+ - title: "Apple driver: Set series number as track number on windows when sending books to iTunes"
+
+ - title: "Drivers for PocketBook 701 and Samsung E65"
+
+ - title: "E-book viewer: Add option to have the mouse wheel flip pages"
+
+ - title: "Add a load_resources method to the InterfaceAction and Plugin classes to facilitate loading of resources from plugin ZIP files"
+
+ - title: "E-book viewer: Add option to not remember position in book when quitting."
+ tickets: [7699]
+
+ - title: "When sorting the book list, keep the current book visible after the sort completes."
+ tickets: [7504]
+
+ - title: "EPUB Output: Add an option to flatten the EPUB file structure, specially for FBReaderJ."
+ tickets: [7788]
+
+ - title: "EPUB Output: Ensure all files inside the generated EPUB have unique filenames, to support broken EPUB readers like Stanza, Aldiko, FBReader and Sigil"
+
+ - title: "FB2 Output: Add support for some 2.1 style tags."
+
+ - title: "Bulk metadata edit: Add options to delete cover/generate default cover."
+ tickets: [7885]
+
+ - title: "Fix a regression in 0.7.33 that broke updating covers in ebook files when saving to disk."
+ tickets: [7886]
+
+ - title: "Don't refresh the Tag browser if it is hidden. Speeds up metadata editing with large libraries, if you hide teh Tag Browser."
+
+ - title: "MOBI Output: Add option to ignore margins in input document"
+ tickets: [7877]
+
+ - title: "Kobo driver: Add support for 1.8.x firmware"
+
+ bug fixes:
+ - title: "Fix various memory leaks introduced in the last couple of releases"
+
+ - title: "EPUB metadata: When rendering first page as the cover, handle embedded svg correctly."
+ tickets: [7909]
+
+ - title: "Disable multiple library support when the CALIBRE_OVERRIDE_DATABASE_PATH env var is set"
+
+ - title: "Content server: Fix bug that could cause saved search based restrictions to not exclude all books"
+ tickets: [7876]
+
+ - title: "Topaz metadata: Read metadata correctly from Topaz files that have MOBI file extensions"
+
+ - title: "MOBI Input: Handle the (rare) MOBI files that do not specify per paragraph text indents correctly."
+ tickets: [7869]
+
+ - title: "MOBI metadata reader: Handle invalid PRC files with spurious image_offset headers"
+
+ - title: "Fix drag/drop of new cover to book detail panel does not update cover browser"
+ tickets: [7890]
+
+ - title: "Do not open the book details dialog when double click on the scrollbars in the book details panel"
+ tickets: [7826]
+
+ - title: "Templates: Fix {tags} not working when no tags are present"
+ tickets: [7888]
+
+ - title: "HTML metadata: Fix regression that broke parsing of some meta tags"
+ tickets: [7851]
+
+ - title: "Preferences: Add tooltips to buddy labels as well."
+ tickets: [7873]
+
+ - title: "Content server: Fix handling of root URL when using --url-prefix"
+
+ - title: "Ensure that the default encoding used by python is never ASCII (needed when running a non frozen version of calibre on linux)"
+
+ improved recipes:
+ - Astronomy Picture of the day
+ - New Scientist
+ - Radikal
+ - Times of India
+ - Economic Times
+ - Zeit Online
+ - Dilbert
+
+ new recipes:
+ - title: "Various Japanes news sources, National Geographic and paper.li"
+ author: "Hiroshi Miura"
+
+ - title: "Science based medicine"
+ author: "BuzzKill"
+
+ - title: "Kompiutierra"
+ author: "Vadim Dyadkin"
+
- version: 0.7.33
date: 2010-12-10
diff --git a/imgsrc/edit_copy.svg b/imgsrc/edit-copy.svg
similarity index 100%
rename from imgsrc/edit_copy.svg
rename to imgsrc/edit-copy.svg
diff --git a/imgsrc/edit-cut.svg b/imgsrc/edit-cut.svg
new file mode 100644
index 0000000000..f078b52e04
--- /dev/null
+++ b/imgsrc/edit-cut.svg
@@ -0,0 +1,831 @@
+
+
+
diff --git a/imgsrc/edit-paste.svg b/imgsrc/edit-paste.svg
new file mode 100644
index 0000000000..d22a8bb7dd
--- /dev/null
+++ b/imgsrc/edit-paste.svg
@@ -0,0 +1,3302 @@
+
+
+
\ No newline at end of file
diff --git a/imgsrc/swap.svg b/imgsrc/swap.svg
deleted file mode 100644
index aa62316b34..0000000000
--- a/imgsrc/swap.svg
+++ /dev/null
@@ -1,722 +0,0 @@
-
-
-
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index 081112444a..a420cd7d44 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -12,13 +12,24 @@ defaults.
# The algorithm used to assign a new book in an existing series a series number.
+# New series numbers assigned using this tweak are always integer values, except
+# if a constant non-integer is specified.
# Possible values are:
-# next - Next available number
+# next - First available integer larger than the largest existing number
+# first_free - First available integer larger than 0
+# next_free - First available integer larger than the smallest existing number
+# last_free - First available integer smaller than the largest existing number
+# Return largest existing + 1 if no free number is found
# const - Assign the number 1 always
+# a number - Assign that number always. The number is not in quotes. Note that
+# 0.0 can be used here.
+# Examples:
+# series_index_auto_increment = 'next'
+# series_index_auto_increment = 'next_free'
+# series_index_auto_increment = 16.5
series_index_auto_increment = 'next'
-
# The algorithm used to copy author to author_sort
# Possible values are:
# invert: use "fn ln" -> "ln, fn" (the original algorithm)
@@ -30,6 +41,20 @@ series_index_auto_increment = 'next'
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
author_sort_copy_method = 'invert'
+# Set which author field to display in the tags pane (the list of authors,
+# series, publishers etc on the left hand side). The choices are author and
+# author_sort. This tweak affects only what is displayed under the authors
+# category in the tags pane and content server. Please note that if you set this
+# to author_sort, it is very possible to see duplicate names in the list because
+# although it is guaranteed that author names are unique, there is no such
+# guarantee for author_sort values. Showing duplicates won't break anything, but
+# it could lead to some confusion. When using 'author_sort', the tooltip will
+# show the author's name.
+# Examples:
+# categories_use_field_for_author_name = 'author'
+# categories_use_field_for_author_name = 'author_sort'
+categories_use_field_for_author_name = 'author'
+
# Set whether boolean custom columns are two- or three-valued.
# Two-values for true booleans
@@ -235,3 +260,9 @@ doubleclick_on_library_view = 'open_viewer'
# Example: locale_for_sorting = 'fr' -- sort using French rules.
# Example: locale_for_sorting = 'nb' -- sort using Norwegian rules.
locale_for_sorting = ''
+
+
+# Set whether to use one or two columns for custom metadata when editing
+# metadata one book at a time. If True, then the fields are laid out using two
+# columns. If False, one column is used.
+metadata_single_use_2_cols_for_custom_fields = True
\ No newline at end of file
diff --git a/resources/images/edit_copy.png b/resources/images/edit-copy.png
similarity index 100%
rename from resources/images/edit_copy.png
rename to resources/images/edit-copy.png
diff --git a/resources/images/edit-cut.png b/resources/images/edit-cut.png
new file mode 100644
index 0000000000..b995283754
Binary files /dev/null and b/resources/images/edit-cut.png differ
diff --git a/resources/images/edit-paste.png b/resources/images/edit-paste.png
new file mode 100644
index 0000000000..b790efec25
Binary files /dev/null and b/resources/images/edit-paste.png differ
diff --git a/resources/images/edit-redo.png b/resources/images/edit-redo.png
new file mode 100644
index 0000000000..8de333fe8c
Binary files /dev/null and b/resources/images/edit-redo.png differ
diff --git a/resources/images/edit-select-all.png b/resources/images/edit-select-all.png
new file mode 100644
index 0000000000..4393bc9dc7
Binary files /dev/null and b/resources/images/edit-select-all.png differ
diff --git a/resources/images/edit-undo.png b/resources/images/edit-undo.png
new file mode 100644
index 0000000000..f6d7e8ba56
Binary files /dev/null and b/resources/images/edit-undo.png differ
diff --git a/resources/images/format-fill-color.png b/resources/images/format-fill-color.png
new file mode 100644
index 0000000000..946bead5da
Binary files /dev/null and b/resources/images/format-fill-color.png differ
diff --git a/resources/images/format-indent-less.png b/resources/images/format-indent-less.png
new file mode 100644
index 0000000000..8662c34871
Binary files /dev/null and b/resources/images/format-indent-less.png differ
diff --git a/resources/images/format-indent-more.png b/resources/images/format-indent-more.png
new file mode 100644
index 0000000000..e1244ef47c
Binary files /dev/null and b/resources/images/format-indent-more.png differ
diff --git a/resources/images/format-justify-center.png b/resources/images/format-justify-center.png
new file mode 100644
index 0000000000..505160a1bb
Binary files /dev/null and b/resources/images/format-justify-center.png differ
diff --git a/resources/images/format-justify-fill.png b/resources/images/format-justify-fill.png
new file mode 100644
index 0000000000..ee34b8272f
Binary files /dev/null and b/resources/images/format-justify-fill.png differ
diff --git a/resources/images/format-justify-left.png b/resources/images/format-justify-left.png
new file mode 100644
index 0000000000..f5af823a82
Binary files /dev/null and b/resources/images/format-justify-left.png differ
diff --git a/resources/images/format-justify-right.png b/resources/images/format-justify-right.png
new file mode 100644
index 0000000000..9a3d8d6ee1
Binary files /dev/null and b/resources/images/format-justify-right.png differ
diff --git a/resources/images/format-list-ordered.png b/resources/images/format-list-ordered.png
new file mode 100644
index 0000000000..c7da85da3f
Binary files /dev/null and b/resources/images/format-list-ordered.png differ
diff --git a/resources/images/format-list-unordered.png b/resources/images/format-list-unordered.png
new file mode 100644
index 0000000000..c959989958
Binary files /dev/null and b/resources/images/format-list-unordered.png differ
diff --git a/resources/images/format-text-color.png b/resources/images/format-text-color.png
new file mode 100644
index 0000000000..2ec27d559d
Binary files /dev/null and b/resources/images/format-text-color.png differ
diff --git a/resources/images/format-text-heading.png b/resources/images/format-text-heading.png
new file mode 100644
index 0000000000..970acb7d60
Binary files /dev/null and b/resources/images/format-text-heading.png differ
diff --git a/resources/images/format-text-subscript.png b/resources/images/format-text-subscript.png
new file mode 100644
index 0000000000..7da8882aea
Binary files /dev/null and b/resources/images/format-text-subscript.png differ
diff --git a/resources/images/format-text-superscript.png b/resources/images/format-text-superscript.png
new file mode 100644
index 0000000000..9dc31ab783
Binary files /dev/null and b/resources/images/format-text-superscript.png differ
diff --git a/resources/images/insert-link.png b/resources/images/insert-link.png
new file mode 100644
index 0000000000..b9e335d320
Binary files /dev/null and b/resources/images/insert-link.png differ
diff --git a/resources/images/swap.png b/resources/images/swap.png
index e5aeb60e22..7f8d40ca1d 100644
Binary files a/resources/images/swap.png and b/resources/images/swap.png differ
diff --git a/resources/recipes/cnd.recipe b/resources/recipes/cnd.recipe
new file mode 100644
index 0000000000..0e8206d07a
--- /dev/null
+++ b/resources/recipes/cnd.recipe
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2010, Derek Liang to allow for readers to generate toc from the document.
'''
def __init__(self, log):
self.log = log
- self.image_hrefs = {}
self.reset_state()
def reset_state(self):
@@ -43,17 +40,25 @@ class FB2MLizer(object):
# in different directories. FB2 images are all in a flat layout so we rename all images
# into a sequential numbering system to ensure there are no collisions between image names.
self.image_hrefs = {}
+ # Mapping of toc items and their
+ self.toc = {}
+ # Used to see whether a new \s*LOGGED IN
' not in raw:
raise Exception('Login failed. Check your username and password')
return br
#TO GET ARTICLE TOC
def johm_get_index(self):
- return self.index_to_soup('http://www3.interscience.wiley.com/journal/111081937/home')
+ return self.index_to_soup('http://onlinelibrary.wiley.com/journal/10.1002/(ISSN)1553-5606/currentissue')
# To parse artice toc
def parse_index(self):
- parse_soup = self.johm_get_index()
+ soup = self.johm_get_index()
+ toc = soup.find(id='issueTocGroups')
+ feeds = []
+ for group in toc.findAll('li', id=re.compile(r'group\d+')):
+ gtitle = group.find(attrs={'class':'subSectionHeading'})
+ if gtitle is None:
+ continue
+ gtitle = self.tag_to_string(gtitle)
+ arts = group.find(attrs={'class':'articles'})
+ if arts is None:
+ continue
+ self.log('Found section:', gtitle)
+ articles = []
+ for art in arts.findAll(attrs={'class':lambda x: x and 'tocArticle'
+ in x}):
+ a = art.find('a', href=True)
+ if a is None:
+ continue
+ url = a.get('href')
+ if url.startswith('/'):
+ url = 'http://onlinelibrary.wiley.com' + url
+ url = url.replace('/abstract', '/full')
+ title = self.tag_to_string(a)
+ a.extract()
+ pm = art.find(attrs={'class':'productMenu'})
+ if pm is not None:
+ pm.extract()
+ desc = self.tag_to_string(art)
+ self.log('\tFound article:', title, 'at', url)
+ articles.append({'title':title, 'url':url, 'description':desc,
+ 'date':''})
+ if articles:
+ feeds.append((gtitle, articles))
- div = parse_soup.find(id='contentCell')
-
- current_section = None
- current_articles = []
- feeds = []
- for x in div.findAll(True):
- if x.name == 'h4':
- # Section heading found
- if current_articles and current_section:
- feeds.append((current_section, current_articles))
- current_section = self.tag_to_string(x)
- current_articles = []
- self.log('\tFound section:', current_section)
- if current_section is not None and x.name == 'strong':
- title = self.tag_to_string(x)
- p = x.parent.parent.find('a', href=lambda x: x and '/HTMLSTART' in x)
- if p is None:
- continue
- url = p.get('href', False)
- if not url or not title:
- continue
- if url.startswith('/'):
- url = 'http://www3.interscience.wiley.com'+url
- url = url.replace('/HTMLSTART', '/main.html,ftx_abs')
- self.log('\t\tFound article:', title)
- self.log('\t\t\t', url)
- #if url.startswith('/'):
- #url = 'http://online.wsj.com'+url
- current_articles.append({'title': title, 'url':url,
- 'description':'', 'date':''})
-
- if current_articles and current_section:
- feeds.append((current_section, current_articles))
-
- return feeds
-
- def preprocess_html(self, soup):
- for img in soup.findAll('img', src=True):
- img['src'] = img['src'].replace('tfig', 'nfig')
- return soup
+ return feeds
diff --git a/resources/recipes/lanacion.recipe b/resources/recipes/lanacion.recipe
index 19f6c1c897..050cb2e79c 100644
--- a/resources/recipes/lanacion.recipe
+++ b/resources/recipes/lanacion.recipe
@@ -78,4 +78,6 @@ class Lanacion(BasicNewsRecipe):
]
def preprocess_html(self, soup):
+ for item in soup.findAll(style=True):
+ del item['style']
return self.adeify_images(soup)
diff --git a/resources/recipes/nejm.recipe b/resources/recipes/nejm.recipe
index c860413926..bc12fbcedf 100644
--- a/resources/recipes/nejm.recipe
+++ b/resources/recipes/nejm.recipe
@@ -4,23 +4,14 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
class NYTimes(BasicNewsRecipe):
title = 'New England Journal of Medicine'
- __author__ = 'Krittika Goyal'
+ __author__ = 'Kovid Goyal'
description = 'Medical news'
timefmt = ' [%d %b, %Y]'
needs_subscription = True
language = 'en'
no_stylesheets = True
- remove_tags_before = dict(name='div', attrs={'align':'center'})
- remove_tags_after = dict(name='ol', attrs={'compact':'COMPACT'})
- remove_tags = [
- dict(name='iframe'),
- #dict(name='div', attrs={'class':'related-articles'}),
- dict(name='div', attrs={'id':['sidebar']}),
- #dict(name='form', attrs={'onsubmit':"return verifySearch(this.w,'Keyword, citation, or author')"}),
- dict(name='table', attrs={'align':'RIGHT'}),
- ]
-
+ keep_only_tags = dict(id='content')
#TO LOGIN
@@ -38,61 +29,50 @@ class NYTimes(BasicNewsRecipe):
#TO GET ARTICLE TOC
def nejm_get_index(self):
- return self.index_to_soup('http://content.nejm.org/current.dtl')
+ return self.index_to_soup('http://content.nejm.org/current.dtl')
# To parse artice toc
def parse_index(self):
- parse_soup = self.nejm_get_index()
+ parse_soup = self.nejm_get_index()
- div = parse_soup.find(id='centerTOC')
+ feeds = []
- current_section = None
- current_articles = []
- feeds = []
- for x in div.findAll(True):
- if x.name == 'img' and '/toc/' in x.get('src', '') and 'uarrow.gif' not in x.get('src', ''):
- # Section heading found
- if current_articles and current_section and 'Week in the' not in current_section:
- feeds.append((current_section, current_articles))
- current_section = x.get('alt')
- current_articles = []
- self.log('\tFound section:', current_section)
- if current_section is not None and x.name == 'strong':
- title = self.tag_to_string(x)
- a = x.parent.find('a', href=lambda x: x and '/full/' in x)
- if a is None:
- continue
- url = a.get('href', False)
- if not url or not title:
- continue
- if url.startswith('/'):
- url = 'http://content.nejm.org'+url
- self.log('\t\tFound article:', title)
- self.log('\t\t\t', url)
- if url.startswith('/'):
- url = 'http://online.wsj.com'+url
- current_articles.append({'title': title, 'url':url,
- 'description':'', 'date':''})
-
- if current_articles and current_section:
- feeds.append((current_section, current_articles))
-
- return feeds
-
- def preprocess_html(self, soup):
- for a in soup.findAll(text=lambda x: x and '[in this window]' in x):
- a = a.findParent('a')
- url = a.get('href', None)
- if not url:
+ div = parse_soup.find(attrs={'class':'tocContent'})
+ for group in div.findAll(attrs={'class':'articleGrouping'}):
+ feed_title = group.find(attrs={'class':'articleType'})
+ if feed_title is None:
continue
- if url.startswith('/'):
- url = 'http://content.nejm.org'+url
- isoup = self.index_to_soup(url)
- img = isoup.find('img', src=lambda x: x and
- x.startswith('/content/'))
- if img is not None:
- img.extract()
- table = a.findParent('table')
- table.replaceWith(img)
- return soup
+ feed_title = self.tag_to_string(feed_title)
+ articles = []
+ self.log('Found section:', feed_title)
+ for art in group.findAll(attrs={'class':lambda x: x and 'articleEntry'
+ in x}):
+ link = art.find(attrs={'class':lambda x:x and 'articleLink' in
+ x})
+ if link is None:
+ continue
+ a = link.find('a', href=True)
+ if a is None:
+ continue
+ url = a.get('href')
+ if url.startswith('/'):
+ url = 'http://www.nejm.org'+url
+ title = self.tag_to_string(a)
+ self.log.info('\tFound article:', title, 'at', url)
+ article = {'title':title, 'url':url, 'date':''}
+ au = art.find(attrs={'class':'articleAuthors'})
+ if au is not None:
+ article['author'] = self.tag_to_string(au)
+ desc = art.find(attrs={'class':'hover_text'})
+ if desc is not None:
+ desc = self.tag_to_string(desc)
+ if 'author' in article:
+ desc = ' by ' + article['author'] + ' ' +desc
+ article['description'] = desc
+ articles.append(article)
+ if articles:
+ feeds.append((feed_title, articles))
+
+ return feeds
+
diff --git a/resources/recipes/wenxuecity-znjy.recipe b/resources/recipes/wenxuecity-znjy.recipe
new file mode 100644
index 0000000000..ecce80222e
--- /dev/null
+++ b/resources/recipes/wenxuecity-znjy.recipe
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2010, Derek Liang
', '
\n\n', text) + text = re.sub(r'(?miu)\s*
', '', text) + text = re.sub(r'(?miu)\s*', '
\n\n', text) + + text = re.sub(r'(?miu)
\u00a0
\n'.encode('utf-8'), res) - if self.options.preprocess_html: - preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None)) + if self.opts.preprocess_html: + preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None)) res = preprocessor(res) f.write(res) self.write_inline_css(inline_class, border_styles) diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index a0f49a7e9a..27973b5f5b 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -12,6 +12,7 @@ from PyQt4.Qt import QMenu, QObject, QTimer from calibre.gui2 import error_dialog from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog from calibre.gui2.dialogs.confirm_delete import confirm +from calibre.gui2.dialogs.confirm_delete_location import confirm_location from calibre.gui2.actions import InterfaceAction single_shot = partial(QTimer.singleShot, 10) @@ -96,10 +97,15 @@ class DeleteAction(InterfaceAction): for action in list(self.delete_menu.actions())[1:]: action.setEnabled(enabled) - def _get_selected_formats(self, msg): + def _get_selected_formats(self, msg, ids): from calibre.gui2.dialogs.select_formats import SelectFormats - fmts = self.gui.library_view.model().db.all_formats() - d = SelectFormats([x.lower() for x in fmts], msg, parent=self.gui) + fmts = set([]) + db = self.gui.library_view.model().db + for x in ids: + fmts_ = db.formats(x, index_is_id=True, verify_formats=False) + if fmts_: + fmts.update(frozenset([x.lower() for x in fmts_.split(',')])) + d = SelectFormats(list(sorted(fmts)), msg, parent=self.gui) if d.exec_() != d.Accepted: return None return d.selected_formats @@ -117,7 +123,7 @@ class DeleteAction(InterfaceAction): if not ids: return fmts = self._get_selected_formats( - _('Choose formats to be deleted')) + _('Choose formats to be deleted'), ids) if not fmts: return for id in ids: @@ -135,7 +141,7 @@ class DeleteAction(InterfaceAction): if not ids: return fmts = self._get_selected_formats( - ''+_('Choose formats not to be deleted')) + '
'+_('Choose formats not to be deleted'), ids) if fmts is None: return for id in ids: @@ -223,7 +229,31 @@ class DeleteAction(InterfaceAction): rows = view.selectionModel().selectedRows() if not rows or len(rows) == 0: return + # Library view is visible. if self.gui.stack.currentIndex() == 0: + # Ask the user if they want to delete the book from the library or device if it is in both. + if self.gui.device_manager.is_device_connected: + on_device = False + on_device_ids = self._get_selected_ids() + for id in on_device_ids: + res = self.gui.book_on_device(id) + if res[0] or res[1] or res[2]: + on_device = True + if on_device: + break + if on_device: + loc = confirm_location('
' + _('Some of the selected books are on the attached device. ' + 'Where do you want the selected files deleted from?'), + self.gui) + if not loc: + return + elif loc == 'dev': + self.remove_matching_books_from_device() + return + elif loc == 'both': + self.remove_matching_books_from_device() + # The following will run if the selected books are not on a connected device. + # The user has selected to delete from the library or the device and library. if not confirm('
'+_('The selected books will be ' 'permanently deleted and the files ' 'removed from your calibre library. Are you sure?') @@ -239,7 +269,7 @@ class DeleteAction(InterfaceAction): else: self.__md = MultiDeleter(self.gui, rows, partial(self.library_ids_deleted, current_row=row)) - + # Device view is visible. else: if not confirm('
'+_('The selected books will be '
'permanently deleted '
diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py
index 8438fa3cfc..d19c97e87b 100644
--- a/src/calibre/gui2/comments_editor.py
+++ b/src/calibre/gui2/comments_editor.py
@@ -5,40 +5,207 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal foo
"
+ elif state == State_TagName:
+ start = pos
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+ if ch.isSpace():
+ pos -= 1
+ state = State_InsideTag
+ break
+ if ch == QChar('>'):
+ state = State_Text
+ break
+ self.setFormat(start, pos - start, self.colors['tag']);
+
+ # anywhere after tag name and before tag closing ('>')
+ elif state == State_InsideTag:
+ start = pos
+
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+
+ if ch == QChar('/'):
+ continue
+
+ if ch == QChar('>'):
+ state = State_Text
+ break
+
+ if not ch.isSpace():
+ pos -= 1
+ state = State_AttributeName
+ break
+
+ # at 's' in e.g.
+ elif state == State_AttributeName:
+ start = pos
+
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+
+ if ch == QChar('='):
+ state = State_AttributeValue
+ break
+
+ if ch in (QChar('>'), QChar('/')):
+ state = State_InsideTag
+ break
+
+ self.setFormat(start, pos - start, self.colors['attrname'])
+
+ # after '=' in e.g.
+ elif state == State_AttributeValue:
+ start = pos
+
+ # find first non-space character
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+
+ # handle opening single quote
+ if ch == QChar("'"):
+ state = State_SingleQuote
+ break
+
+ # handle opening double quote
+ if ch == QChar('"'):
+ state = State_DoubleQuote
+ break
+
+ if not ch.isSpace():
+ break
+
+ if state == State_AttributeValue:
+ # attribute value without quote
+ # just stop at non-space or tag delimiter
+ start = pos
+ while pos < len_:
+ ch = text.at(pos);
+ if ch.isSpace():
+ break
+ if ch in (QChar('>'), QChar('/')):
+ break
+ pos += 1
+ state = State_InsideTag
+ self.setFormat(start, pos - start, self.colors['attrval'])
+
+ # after the opening single quote in an attribute value
+ elif state == State_SingleQuote:
+ start = pos
+
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+ if ch == QChar("'"):
+ break
+
+ state = State_InsideTag
+
+ self.setFormat(start, pos - start, self.colors['attrval'])
+
+ # after the opening double quote in an attribute value
+ elif state == State_DoubleQuote:
+ start = pos
+
+ while pos < len_:
+ ch = text.at(pos)
+ pos += 1
+ if ch == QChar('"'):
+ break
+
+ state = State_InsideTag
+
+ self.setFormat(start, pos - start, self.colors['attrval'])
+
+ else:
+ # State_Text and default
+ while pos < len_:
+ ch = text.at(pos)
+ if ch == QChar('<'):
+ if text.mid(pos, 4) == "