diff --git a/resources/templates/book_details.css b/resources/templates/book_details.css index 5059a8f4c3..5fc2691850 100644 --- a/resources/templates/book_details.css +++ b/resources/templates/book_details.css @@ -2,6 +2,11 @@ a { text-decoration: none; color: blue } + +a:hover { + color: red +} + .comments { margin-top: 0; padding-top: 0; diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index 1546644f95..7488df4609 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -156,17 +156,17 @@ class HeuristicProcessor(object): ] ITALICIZE_STYLE_PATS = [ - r'(?msu)(?<=[\s>])_(?P[^_]+)_', - r'(?msu)(?<=[\s>])/(?P[^/\*>]+)/', - r'(?msu)(?<=[\s>])~~(?P[^~]+)~~', - r'(?msu)(?<=[\s>])\*(?P[^\*]+)\*', - r'(?msu)(?<=[\s>])~(?P[^~]+)~', - r'(?msu)(?<=[\s>])_/(?P[^/_]+)/_', - r'(?msu)(?<=[\s>])_\*(?P[^\*_]+)\*_', - r'(?msu)(?<=[\s>])\*/(?P[^/\*]+)/\*', - r'(?msu)(?<=[\s>])_\*/(?P[^\*_]+)/\*_', - r'(?msu)(?<=[\s>])/:(?P[^:/]+):/', - r'(?msu)(?<=[\s>])\|:(?P[^:\|]+):\|', + ur'(?msu)(?<=[\s>"“\'‘])_(?P[^_]+)_', + ur'(?msu)(?<=[\s>"“\'‘])/(?P[^/\*>]+)/', + ur'(?msu)(?<=[\s>"“\'‘])~~(?P[^~]+)~~', + ur'(?msu)(?<=[\s>"“\'‘])\*(?P[^\*]+)\*', + ur'(?msu)(?<=[\s>"“\'‘])~(?P[^~]+)~', + ur'(?msu)(?<=[\s>"“\'‘])_/(?P[^/_]+)/_', + ur'(?msu)(?<=[\s>"“\'‘])_\*(?P[^\*_]+)\*_', + ur'(?msu)(?<=[\s>"“\'‘])\*/(?P[^/\*]+)/\*', + ur'(?msu)(?<=[\s>"“\'‘])_\*/(?P[^\*_]+)/\*_', + ur'(?msu)(?<=[\s>"“\'‘])/:(?P[^:/]+):/', + ur'(?msu)(?<=[\s>"“\'‘])\|:(?P[^:\|]+):\|', ] for word in ITALICIZE_WORDS: @@ -518,13 +518,13 @@ class HeuristicProcessor(object): if re.findall('(<|>)', replacement_break): if re.match('^\d+).*', '\g', replacement_break)) - replacement_break = re.sub('(?i)(width=\d+\%?|width:\s*\d+(\%|px|pt|em)?;?)', '', replacement_break) - divpercent = (100 - width) / 2 - hr_open = re.sub('45', str(divpercent), hr_open) - scene_break = hr_open+replacement_break+'' + width = int(re.sub('.*?width(:|=)(?P\d+).*', '\g', replacement_break)) + replacement_break = re.sub('(?i)(width=\d+\%?|width:\s*\d+(\%|px|pt|em)?;?)', '', replacement_break) + divpercent = (100 - width) / 2 + hr_open = re.sub('45', str(divpercent), hr_open) + scene_break = hr_open+replacement_break+'' else: - scene_break = hr_open+'
' + scene_break = hr_open+'
' elif re.match('^' else: @@ -584,10 +584,10 @@ class HeuristicProcessor(object): #print "styles for this line are: "+str(styles) split_styles = [] for style in styles: - #print "style is: "+str(style) - newstyle = style.split(':') - #print "newstyle is: "+str(newstyle) - split_styles.append(newstyle) + #print "style is: "+str(style) + newstyle = style.split(':') + #print "newstyle is: "+str(newstyle) + split_styles.append(newstyle) styles = split_styles for style, setting in styles: if style == 'text-align' and setting != 'left': diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 5619ef7806..7927517b22 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -563,10 +563,12 @@ class Metadata(object): def format_tags(self): return u', '.join([unicode(t) for t in sorted(self.tags, key=sort_key)]) - def format_rating(self, v = None): + def format_rating(self, v=None, divide_by=1.0): if v is None: - return unicode(self.rating/2) - return unicode(v/2) + if self.rating is not None: + return unicode(self.rating/divide_by) + return u'None' + return unicode(v/divide_by) def format_field(self, key, series_with_index=True): ''' diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index ee18d8e9ca..eadfa55549 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -40,6 +40,11 @@ path_to_ebook to the database. parser.add_option('--ignore-plugins', default=False, action='store_true', help=_('Ignore custom plugins, useful if you installed a plugin' ' that is preventing calibre from starting')) + parser.add_option('-s', '--shutdown-running-calibre', default=False, + action='store_true', + help=_('Cause a running calibre instance, if any, to be' + ' shutdown. Note that if there are running jobs, they ' + 'will be silently aborted, so use with care.')) return parser def init_qt(args): @@ -339,7 +344,7 @@ def cant_start(msg=_('If you are sure it is not running')+', ', raise SystemExit(1) -def communicate(args): +def communicate(opts, args): t = RC() t.start() time.sleep(3) @@ -348,9 +353,12 @@ def communicate(args): cant_start(what=_('try deleting the file')+': '+f) raise SystemExit(1) - if len(args) > 1: - args[1] = os.path.abspath(args[1]) - t.conn.send('launched:'+repr(args)) + if opts.shutdown_running_calibre: + t.conn.send('shutdown:') + else: + if len(args) > 1: + args[1] = os.path.abspath(args[1]) + t.conn.send('launched:'+repr(args)) t.conn.close() raise SystemExit(0) @@ -365,6 +373,8 @@ def main(args=sys.argv): from calibre.utils.lock import singleinstance from multiprocessing.connection import Listener si = singleinstance('calibre GUI') + if si and opts.shutdown_running_calibre: + return 0 if si: try: listener = Listener(address=ADDRESS) @@ -390,10 +400,10 @@ def main(args=sys.argv): else: # On windows only singleinstance can be trusted otherinstance = True if iswindows else False - if not otherinstance: + if not otherinstance and not opts.shutdown_running_calibre: return run_gui(opts, args, actions, listener, app, gui_debug=gui_debug) - communicate(args) + communicate(opts, args) return 0 diff --git a/src/calibre/gui2/threaded_jobs.py b/src/calibre/gui2/threaded_jobs.py index ad295503a0..9e16f88a1a 100644 --- a/src/calibre/gui2/threaded_jobs.py +++ b/src/calibre/gui2/threaded_jobs.py @@ -100,7 +100,8 @@ class ThreadedJob(BaseJob): try: self.consolidate_log() except: - self.log.exception('Log consolidation failed') + if self.log is not None: + self.log.exception('Log consolidation failed') # No need to keep references to these around anymore self.func = self.args = self.kwargs = self.notifications = None @@ -112,7 +113,7 @@ class ThreadedJob(BaseJob): self.start_time = time.time() self.duration = 0.0001 else: - self.duration = time.time() - self.start_time() + self.duration = time.time() - self.start_time self.abort.set() self.log('Aborted job:', self.description) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 4885f7b2db..435b9ebe78 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -446,6 +446,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.library_view.model().refresh() self.library_view.model().research() self.tags_view.recount() + elif msg.startswith('shutdown:'): + self.quit(confirm_quit=False) else: print msg @@ -599,8 +601,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ dynamic.set('sort_history', self.library_view.model().sort_history) self.save_layout_state() - def quit(self, checked=True, restart=False, debug_on_restart=False): - if not self.confirm_quit(): + def quit(self, checked=True, restart=False, debug_on_restart=False, + confirm_quit=True): + if confirm_quit and not self.confirm_quit(): return try: self.shutdown() diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 8eb2e8d788..b89e9c36f8 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -189,7 +189,7 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, else: template = re.sub(r'\{series_index[^}]*?\}', '', template) if mi.rating is not None: - format_args['rating'] = mi.format_rating() + format_args['rating'] = mi.format_rating(divide_by=2.0) if hasattr(mi.timestamp, 'timetuple'): format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple()) if hasattr(mi.pubdate, 'timetuple'): @@ -212,7 +212,8 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, elif cm['datatype'] == 'bool': format_args[key] = _('yes') if format_args[key] else _('no') elif cm['datatype'] == 'rating': - format_args[key] = mi.format_rating(format_args[key]) + format_args[key] = mi.format_rating(format_args[key], + divide_by=2.0) elif cm['datatype'] in ['int', 'float']: if format_args[key] != 0: format_args[key] = unicode(format_args[key]) diff --git a/src/calibre/manual/creating_plugins.rst b/src/calibre/manual/creating_plugins.rst index 3b6b9611af..4a69cc8753 100644 --- a/src/calibre/manual/creating_plugins.rst +++ b/src/calibre/manual/creating_plugins.rst @@ -195,9 +195,10 @@ It can get tiresome to keep re-adding a plugin to calibre to test small changes. Once you've located the zip file of your plugin you can then directly update it with your changes instead of re-adding it each time. To do so from the command line, in the directory that contains your plugin source code, use:: - zip -R /path/to/plugin/zip/file.zip * + calibre -s; sleep 4s; zip -R /path/to/plugin/zip/file.zip *; calibre -This will update all changed files. It relies on the freely available zip command line tool. Note that you should quit calibre before running this command. +This will shutdown a running calibre. Wait for the shutdown to complete, then update your plugin files and relaunch calibre. +It relies on the freely available zip command line tool. More plugin examples ----------------------