From 339181a62112992db53a4b831f052ee46091ddfb Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 3 Oct 2010 16:08:21 -0700 Subject: [PATCH 1/6] GwR wip apple driver --- src/calibre/devices/apple/driver.py | 47 ++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 2559d0149e..341d2840cc 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -17,7 +17,7 @@ from calibre.ebooks.metadata import authors_to_string, MetaInformation from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.epub import set_metadata from calibre.library.server.utils import strftime -from calibre.utils.config import config_dir +from calibre.utils.config import config_dir, prefs from calibre.utils.date import isoformat, now, parse_date from calibre.utils.localization import get_lang from calibre.utils.logging import Log @@ -164,6 +164,7 @@ class ITUNES(DriverBase): # Properties cached_books = {} cache_dir = os.path.join(config_dir, 'caches', 'itunes') + calibre_library_path = prefs['library_path'] archive_path = os.path.join(cache_dir, "thumbs.zip") description_prefix = "added by calibre" ejected = False @@ -173,6 +174,7 @@ class ITUNES(DriverBase): log = Log() manual_sync_mode = False path_template = 'iTunes/%s - %s.%s' + plugboard = None problem_titles = [] problem_msg = None report_progress = None @@ -814,6 +816,15 @@ class ITUNES(DriverBase): ''' self.report_progress = report_progress + def set_plugboard(self, pb): + # This method is called with the plugboard that matches the above + # format and the current device name. + if DEBUG: + self.log.info("ITUNES.set_plugboard()") + if pb is not None: + self.log.info(' using plugboard %s' % pb) + self.plugboard = pb + def sync_booklists(self, booklists, end_session=True): ''' Update metadata on device. @@ -825,6 +836,10 @@ class ITUNES(DriverBase): if DEBUG: self.log.info("ITUNES.sync_booklists()") + # booklists[0] should contain enough info to call + # self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book) + # from here using the plugboard data as the metadata + if self.update_needed: if DEBUG: self.log.info(' calling _update_device') @@ -860,7 +875,7 @@ class ITUNES(DriverBase): return (capacity,-1,-1) def upload_books(self, files, names, on_card=None, end_session=True, - metadata=None): + metadata=None, plugboards=None): ''' Upload a list of books to the device. If a file already exists on the device, it should be replaced. @@ -891,6 +906,7 @@ class ITUNES(DriverBase): self.log.info("ITUNES.upload_books()") self._dump_files(files, header='upload_books()',indent=2) self._dump_update_list(header='upload_books()',indent=2) + self.log.info(" plugboards: %s" % plugboards) if isosx: for (i,file) in enumerate(files): @@ -977,6 +993,13 @@ class ITUNES(DriverBase): self._dump_cached_books(header="after upload_books()",indent=2) return (new_booklist, [], []) + def use_plugboard_ext(self): + ''' Declare which plugboard extensions we care about ''' + if DEBUG: + self.log.info("ITUNES.use_plugboard_ext()") + ext = 'epub' + return ext + # Private methods def _add_device_book(self,fpath, metadata): @@ -1256,7 +1279,10 @@ class ITUNES(DriverBase): self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) finally: - zfw.close() + try: + zfw.close() + except: + pass else: if DEBUG: self.log.info(" no cover defined in metadata for '%s'" % metadata.title) @@ -1273,10 +1299,10 @@ class ITUNES(DriverBase): this_book.db_id = None this_book.device_collections = [] this_book.format = format - this_book.library_id = lb_added + this_book.library_id = lb_added # ??? GR this_book.path = path this_book.thumbnail = thumb - this_book.iTunes_id = lb_added + this_book.iTunes_id = lb_added # ??? GR this_book.uuid = metadata.uuid if isosx: @@ -2213,6 +2239,7 @@ class ITUNES(DriverBase): (self.iTunes.name(), self.iTunes.version(), self.initial_status, self.version[0],self.version[1],self.version[2])) self.log.info(" iTunes_media: %s" % self.iTunes_media) + self.log.info(" calibre_library_path: %s" % self.calibre_library_path) if iswindows: ''' @@ -2266,6 +2293,7 @@ class ITUNES(DriverBase): (self.iTunes.Windows[0].name, self.iTunes.Version, self.initial_status, self.version[0],self.version[1],self.version[2])) self.log.info(" iTunes_media: %s" % self.iTunes_media) + self.log.info(" calibre_library_path: %s" % self.calibre_library_path) def _purge_orphans(self,library_books, cached_books): ''' @@ -2368,7 +2396,8 @@ class ITUNES(DriverBase): ''' iTunes does not delete books from storage when removing from database We only want to delete stored copies if the file is stored in iTunes - We don't want to delete files stored outside of iTunes + We don't want to delete files stored outside of iTunes. + Also confirm that storage_path does not point into calibre's storage. ''' if DEBUG: self.log.info(" ITUNES._remove_from_iTunes():") @@ -2376,7 +2405,8 @@ class ITUNES(DriverBase): if isosx: try: storage_path = os.path.split(cached_book['lib_book'].location().path) - if cached_book['lib_book'].location().path.startswith(self.iTunes_media): + if cached_book['lib_book'].location().path.startswith(self.iTunes_media) and \ + not storage_path[0].startswith(self.calibre_library_path): title_storage_path = storage_path[0] if DEBUG: self.log.info(" removing title_storage_path: %s" % title_storage_path) @@ -2427,7 +2457,8 @@ class ITUNES(DriverBase): path = book.Location if book: - if self.iTunes_media and path.startswith(self.iTunes_media): + if self.iTunes_media and path.startswith(self.iTunes_media) and \ + not path.startswith(self.calibre_library_path): storage_path = os.path.split(path) if DEBUG: self.log.info(" removing '%s' at %s" % From d8f64b400e875faa9f74288b4e1eb0957c1365d2 Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 5 Oct 2010 03:53:38 -0700 Subject: [PATCH 2/6] GwR wip apple driver updates --- src/calibre/devices/apple/driver.py | 163 +++++++++++++--------- src/calibre/ebooks/metadata/book/base.py | 5 + src/calibre/gui2/device.py | 5 + src/calibre/gui2/preferences/plugboard.ui | 9 +- 4 files changed, 111 insertions(+), 71 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 341d2840cc..a6fd413539 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -68,6 +68,8 @@ class ITUNES(DriverBase): Delete: delete_books() remove_books_from_metadata() + use_plugboard_ext() + set_plugboard() sync_booklists() card_prefix() free_space() @@ -76,6 +78,8 @@ class ITUNES(DriverBase): set_progress_reporter() upload_books() add_books_to_metadata() + use_plugboard_ext() + set_plugboard() set_progress_reporter() sync_booklists() card_prefix() @@ -817,12 +821,12 @@ class ITUNES(DriverBase): self.report_progress = report_progress def set_plugboard(self, pb): - # This method is called with the plugboard that matches the above - # format and the current device name. + # This method is called with the plugboard that matches the format + # declared in use_plugboard_ext and a device name of ITUNES if DEBUG: self.log.info("ITUNES.set_plugboard()") - if pb is not None: self.log.info(' using plugboard %s' % pb) + if pb is not None: self.plugboard = pb def sync_booklists(self, booklists, end_session=True): @@ -836,10 +840,6 @@ class ITUNES(DriverBase): if DEBUG: self.log.info("ITUNES.sync_booklists()") - # booklists[0] should contain enough info to call - # self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book) - # from here using the plugboard data as the metadata - if self.update_needed: if DEBUG: self.log.info(' calling _update_device') @@ -875,7 +875,7 @@ class ITUNES(DriverBase): return (capacity,-1,-1) def upload_books(self, files, names, on_card=None, end_session=True, - metadata=None, plugboards=None): + metadata=None): ''' Upload a list of books to the device. If a file already exists on the device, it should be replaced. @@ -906,7 +906,6 @@ class ITUNES(DriverBase): self.log.info("ITUNES.upload_books()") self._dump_files(files, header='upload_books()',indent=2) self._dump_update_list(header='upload_books()',indent=2) - self.log.info(" plugboards: %s" % plugboards) if isosx: for (i,file) in enumerate(files): @@ -994,10 +993,10 @@ class ITUNES(DriverBase): return (new_booklist, [], []) def use_plugboard_ext(self): - ''' Declare which plugboard extensions we care about ''' - if DEBUG: - self.log.info("ITUNES.use_plugboard_ext()") + ''' Declare which plugboard extension we care about ''' ext = 'epub' + if DEBUG: + self.log.info("ITUNES.use_plugboard_ext(): declaring %s" % ext) return ext @@ -2601,75 +2600,84 @@ class ITUNES(DriverBase): if DEBUG: self.log.info(" ITUNES._update_iTunes_metadata()") - strip_tags = re.compile(r'<[^<]*?/?>') + STRIP_TAGS = re.compile(r'<[^<]*?/?>') + + # Update metadata from plugboard + # If self.plugboard is None (no transforms), original metadata is returned intact + metadata_x = self._xform_metadata_via_plugboard(metadata) if isosx: if lb_added: - lb_added.album.set(metadata.title) - lb_added.artist.set(authors_to_string(metadata.authors)) - lb_added.composer.set(metadata.uuid) + lb_added.album.set(metadata_x.title) + lb_added.artist.set(authors_to_string(metadata_x.authors)) + lb_added.composer.set(metadata_x.uuid) lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.enabled.set(True) - lb_added.sort_artist.set(metadata.author_sort.title()) + lb_added.sort_artist.set(metadata_x.author_sort.title()) lb_added.sort_name.set(this_book.title_sorter) if this_book.format == 'pdf': lb_added.name.set(metadata.title) + elif this_book.format == 'epub': + lb_added.name.set(metadata_x.title) + if db_added: - db_added.album.set(metadata.title) - db_added.artist.set(authors_to_string(metadata.authors)) - db_added.composer.set(metadata.uuid) + db_added.album.set(metadata_x.title) + db_added.artist.set(authors_to_string(metadata_x.authors)) + db_added.composer.set(metadata_x.uuid) db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.enabled.set(True) - db_added.sort_artist.set(metadata.author_sort.title()) + db_added.sort_artist.set(metadata_x.author_sort.title()) db_added.sort_name.set(this_book.title_sorter) if this_book.format == 'pdf': db_added.name.set(metadata.title) + elif this_book.format == 'epub': + db_added.name.set(metadata_x.title) - if metadata.comments: + if metadata_x.comments: if lb_added: - lb_added.comment.set(strip_tags.sub('',metadata.comments)) + lb_added.comment.set(STRIP_TAGS.sub('',metadata_x.comments)) if db_added: - db_added.comment.set(strip_tags.sub('',metadata.comments)) + db_added.comment.set(STRIP_TAGS.sub('',metadata_x.comments)) - if metadata.rating: + if metadata_x.rating: if lb_added: - lb_added.rating.set(metadata.rating*10) + lb_added.rating.set(metadata_x.rating*10) # iBooks currently doesn't allow setting rating ... ? try: if db_added: - db_added.rating.set(metadata.rating*10) + db_added.rating.set(metadata_x.rating*10) except: pass # Set genre from series if available, else first alpha tag # Otherwise iTunes grabs the first dc:subject from the opf metadata - if metadata.series and self.settings().read_metadata: + if metadata_x.series and self.settings().read_metadata: if DEBUG: self.log.info(" using Series name as Genre") # Format the index as a sort key - index = metadata.series_index + index = metadata_x.series_index integer = int(index) fraction = index-integer series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0')) if lb_added: - lb_added.sort_name.set("%s %s" % (metadata.series, series_index)) - lb_added.genre.set(metadata.series) - lb_added.episode_ID.set(metadata.series) - lb_added.episode_number.set(metadata.series_index) + lb_added.sort_name.set("%s %s" % (metadata_x.series, series_index)) + lb_added.genre.set(metadata_x.series) + lb_added.episode_ID.set(metadata_x.series) + lb_added.episode_number.set(metadata_x.series_index) if db_added: - db_added.sort_name.set("%s %s" % (metadata.series, series_index)) - db_added.genre.set(metadata.series) - db_added.episode_ID.set(metadata.series) - db_added.episode_number.set(metadata.series_index) + db_added.sort_name.set("%s %s" % (metadata_x.series, series_index)) + db_added.genre.set(metadata_x.series) + db_added.episode_ID.set(metadata_x.series) + db_added.episode_number.set(metadata_x.series_index) - elif metadata.tags: + elif metadata_x.tags: if DEBUG: self.log.info(" %susing Tag as Genre" % "no Series name available, " if self.settings().read_metadata else '') - for tag in metadata.tags: + for tag in metadata_x.tags: if self._is_alpha(tag[0]): if lb_added: lb_added.genre.set(tag) @@ -2679,40 +2687,44 @@ class ITUNES(DriverBase): elif iswindows: if lb_added: - lb_added.Album = metadata.title - lb_added.Artist = authors_to_string(metadata.authors) - lb_added.Composer = metadata.uuid + lb_added.Album = metadata_x.title + lb_added.Artist = authors_to_string(metadata_x.authors) + lb_added.Composer = metadata_x.uuid lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.Enabled = True - lb_added.SortArtist = (metadata.author_sort.title()) + lb_added.SortArtist = (metadata_x.author_sort.title()) lb_added.SortName = (this_book.title_sorter) if this_book.format == 'pdf': lb_added.Name = metadata.title + elif this_book.format == 'epub': + lb_added.Name = metadata_x.title if db_added: - db_added.Album = metadata.title - db_added.Artist = authors_to_string(metadata.authors) - db_added.Composer = metadata.uuid + db_added.Album = metadata_x.title + db_added.Artist = authors_to_string(metadata_x.authors) + db_added.Composer = metadata_x.uuid db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.Enabled = True - db_added.SortArtist = (metadata.author_sort.title()) + db_added.SortArtist = (metadata_x.author_sort.title()) db_added.SortName = (this_book.title_sorter) if this_book.format == 'pdf': db_added.Name = metadata.title + elif this_book.format == 'epub': + db_added.Name = metadata_x.title - if metadata.comments: + if metadata_x.comments: if lb_added: - lb_added.Comment = (strip_tags.sub('',metadata.comments)) + lb_added.Comment = (STRIP_TAGS.sub('',metadata_x.comments)) if db_added: - db_added.Comment = (strip_tags.sub('',metadata.comments)) + db_added.Comment = (STRIP_TAGS.sub('',metadata_x.comments)) - if metadata.rating: + if metadata_x.rating: if lb_added: - lb_added.AlbumRating = (metadata.rating*10) + lb_added.AlbumRating = (metadata_x.rating*10) # iBooks currently doesn't allow setting rating ... ? try: if db_added: - db_added.AlbumRating = (metadata.rating*10) + db_added.AlbumRating = (metadata_x.rating*10) except: if DEBUG: self.log.warning(" iTunes automation interface reported an error" @@ -2722,36 +2734,36 @@ class ITUNES(DriverBase): # Otherwise iBooks uses first from opf # iTunes balks on setting EpisodeNumber, but it sticks (9.1.1.12) - if metadata.series and self.settings().read_metadata: + if metadata_x.series and self.settings().read_metadata: if DEBUG: self.log.info(" using Series name as Genre") # Format the index as a sort key - index = metadata.series_index + index = metadata_x.series_index integer = int(index) fraction = index-integer series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0')) if lb_added: - lb_added.SortName = "%s %s" % (metadata.series, series_index) - lb_added.Genre = metadata.series - lb_added.EpisodeID = metadata.series + lb_added.SortName = "%s %s" % (metadata_x.series, series_index) + lb_added.Genre = metadata_x.series + lb_added.EpisodeID = metadata_x.series try: - lb_added.EpisodeNumber = metadata.series_index + lb_added.EpisodeNumber = metadata_x.series_index except: pass if db_added: - db_added.SortName = "%s %s" % (metadata.series, series_index) - db_added.Genre = metadata.series - db_added.EpisodeID = metadata.series + db_added.SortName = "%s %s" % (metadata_x.series, series_index) + db_added.Genre = metadata_x.series + db_added.EpisodeID = metadata_x.series try: - db_added.EpisodeNumber = metadata.series_index + db_added.EpisodeNumber = metadata_x.series_index except: if DEBUG: self.log.warning(" iTunes automation interface reported an error" " setting EpisodeNumber on iDevice") - elif metadata.tags: + elif metadata_x.tags: if DEBUG: self.log.info(" using Tag as Genre") - for tag in metadata.tags: + for tag in metadata_x.tags: if self._is_alpha(tag[0]): if lb_added: lb_added.Genre = tag @@ -2759,6 +2771,25 @@ class ITUNES(DriverBase): db_added.Genre = tag break + def _xform_metadata_via_plugboard(self, book): + ''' Transform book metadata from plugboard templates ''' + if DEBUG: + self.log.info("ITUNES._update_metadata_from_plugboard()") + + if self.plugboard is not None: + newmi = book.deepcopy_metadata() + newmi.template_to_attribute(book, self.plugboard) + if DEBUG: + if book.title != newmi.title: + self.log.info(" .title (original): %s" % book.title) + self.log.info(" .title (templated): %s" % newmi.title) + else: + self.log.info(" .title (no change): %s" % book.title) + else: + newmi = book + return newmi + + class ITUNES_ASYNC(ITUNES): ''' This subclass allows the user to interact directly with iTunes via a menu option @@ -3043,7 +3074,7 @@ class BookList(list): class Book(Metadata): ''' A simple class describing a book in the iTunes Books Library. - - See ebooks.metadata.__init__ for all fields + See ebooks.metadata.book.base ''' def __init__(self,title,author): diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index b8288e210c..e69c7dc09e 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -148,6 +148,11 @@ class Metadata(object): object.__setattr__(m, '_data', copy.deepcopy(object.__getattribute__(self, '_data'))) return m + def deepcopy_metadata(self): + m = Metadata(None) + object.__setattr__(m, '_data', copy.deepcopy(object.__getattribute__(self, '_data'))) + return m + def get(self, field, default=None): try: return self.__getattribute__(field) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index d06b77b4e2..b28a6ce63c 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -345,6 +345,11 @@ class DeviceManager(Thread): # {{{ def _upload_books(self, files, names, on_card=None, metadata=None, plugboards=None): '''Upload books to device: ''' + if hasattr(self.connected_device, 'use_plugboard_ext') and \ + callable(self.connected_device.use_plugboard_ext): + ext = self.connected_device.use_plugboard_ext() + if ext is not None: + self.connected_device.set_plugboard(self.find_plugboard(ext, plugboards)) if metadata and files and len(metadata) == len(files): for f, mi in zip(files, metadata): if isinstance(f, unicode): diff --git a/src/calibre/gui2/preferences/plugboard.ui b/src/calibre/gui2/preferences/plugboard.ui index 8249584678..9f14978ca8 100644 --- a/src/calibre/gui2/preferences/plugboard.ui +++ b/src/calibre/gui2/preferences/plugboard.ui @@ -6,8 +6,8 @@ 0 0 - 707 - 340 + 931 + 389 @@ -23,7 +23,7 @@ Use this dialog to define a 'plugboard' for a format (or all formats) and a devi Often templates will contain simple references to composite columns, but this is not necessary. You can use any template in a source box that you can use elsewhere in calibre. -One possible use for a plugboard is to alter the title to contain series informaton. Another would be to change the author sort, something that mobi users might do to force it to use the ';' that the kindle requires. A third would be to specify the language. +One possible use for a plugboard is to alter the title to contain series information. Another would be to change the author sort, something that mobi users might do to force it to use the ';' that the kindle requires. A third would be to specify the language. Qt::PlainText @@ -41,8 +41,7 @@ One possible use for a plugboard is to alter the title to contain series informa - - + From b7577601803bd38f46ea39de64657b7471bdef0a Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 6 Oct 2010 20:29:42 +0100 Subject: [PATCH 3/6] Add set_plugboards to interface.py --- src/calibre/devices/interface.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 2307bf94d6..29ba5020c7 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -411,6 +411,22 @@ class DevicePlugin(Plugin): ''' raise NotImplementedError() + def set_plugboards(self, plugboards, pb_func): + ''' + provide the driver the current set of plugboards and a function to + select a specific plugboard. This method is called immediately before + add_books and sync_booklists. + + pb_func is a callable with the following signature: + def pb_func(device_name, format, plugboards) + You give it the current device name (either the class name or + DEVICE_PLUGBOARD_NAME), the format you are interested in (a 'real' + format or 'device_db'), and the plugboards (you were given those by + set_plugboards, the same place you got this method). + + Return value: None or a single plugboard instance. + ''' + pass class BookList(list): ''' From 2dab246d4df0ddf6f28c0f2d9dedab3b0fb607ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 6 Oct 2010 16:21:29 -0600 Subject: [PATCH 4/6] Implement #7075 (possibility to move a book to another library) --- src/calibre/gui2/actions/copy_to_library.py | 19 +++++++++++++++++-- src/calibre/gui2/actions/delete.py | 21 +++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/actions/copy_to_library.py b/src/calibre/gui2/actions/copy_to_library.py index 6b7654f644..513026f757 100644 --- a/src/calibre/gui2/actions/copy_to_library.py +++ b/src/calibre/gui2/actions/copy_to_library.py @@ -21,6 +21,7 @@ class Worker(Thread): def __init__(self, ids, db, loc, progress, done): Thread.__init__(self) self.ids = ids + self.processed = set([]) self.db = db self.loc = loc self.error = None @@ -71,6 +72,7 @@ class Worker(Thread): co = self.db.conversion_options(x, 'PIPE') if co is not None: newdb.set_conversion_options(x, 'PIPE', co) + self.processed.add(x) class CopyToLibraryAction(InterfaceAction): @@ -107,9 +109,13 @@ class CopyToLibraryAction(InterfaceAction): for name, loc in locations: self.menu.addAction(name, partial(self.copy_to_library, loc)) + self.menu.addAction(name + ' ' + _('(delete after copy)'), + partial(self.copy_to_library, loc, delete_after=True)) + self.menu.addSeparator() + self.qaction.setVisible(bool(locations)) - def copy_to_library(self, loc): + def copy_to_library(self, loc, delete_after=False): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot copy'), @@ -140,7 +146,16 @@ class CopyToLibraryAction(InterfaceAction): else: self.gui.status_bar.show_message(_('Copied %d books to %s') % (len(ids), loc), 2000) - + if delete_after and self.worker.processed: + v = self.gui.library_view + ci = v.currentIndex() + row = None + if ci.isValid(): + row = ci.row() + + v.model().delete_books_by_id(self.worker.processed) + self.gui.iactions['Remove Books'].library_ids_deleted( + self.worker.processed, row) diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index 406860e4ec..a541590fd1 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -149,6 +149,18 @@ class DeleteAction(InterfaceAction): self.gui.library_view.model().current_changed(self.gui.library_view.currentIndex(), self.gui.library_view.currentIndex()) + + def library_ids_deleted(self, ids_deleted, current_row=None): + view = self.gui.library_view + for v in (self.gui.memory_view, self.gui.card_a_view, self.gui.card_b_view): + if v is None: + continue + v.model().clear_ondevice(ids_deleted) + if current_row is not None: + ci = view.model().index(current_row, 0) + if ci.isValid(): + view.set_current_row(current_row) + def delete_books(self, *args): ''' Delete selected books from device or library. @@ -168,14 +180,7 @@ class DeleteAction(InterfaceAction): if ci.isValid(): row = ci.row() ids_deleted = view.model().delete_books(rows) - for v in (self.gui.memory_view, self.gui.card_a_view, self.gui.card_b_view): - if v is None: - continue - v.model().clear_ondevice(ids_deleted) - if row is not None: - ci = view.model().index(row, 0) - if ci.isValid(): - view.set_current_row(row) + self.library_ids_deleted(ids_deleted, row) else: if not confirm('

'+_('The selected books will be ' 'permanently deleted ' From e3f6049cf65d261cdcb9d5a0895374707f7082f5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 6 Oct 2010 17:33:26 -0600 Subject: [PATCH 5/6] Fix another regression in the isbndb plugin --- src/calibre/ebooks/metadata/isbndb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/metadata/isbndb.py b/src/calibre/ebooks/metadata/isbndb.py index 6416dcdc39..07a054eeaa 100644 --- a/src/calibre/ebooks/metadata/isbndb.py +++ b/src/calibre/ebooks/metadata/isbndb.py @@ -83,7 +83,7 @@ class ISBNDBMetadata(Metadata): summ = tostring(book.find('summary')) if summ: - self.comments = 'SUMMARY:\n'+summ.string + self.comments = 'SUMMARY:\n'+summ def build_isbn(base_url, opts): From 217ea7a9a22ec243b1d19d332b478f357a77417d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 6 Oct 2010 20:31:40 -0600 Subject: [PATCH 6/6] Implement #7082 (Enhance viewer "Remember Last Used Window Size") --- src/calibre/gui2/viewer/main.py | 38 ++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 79f4c29998..26fd2cadc9 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -166,6 +166,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def __init__(self, pathtoebook=None, debug_javascript=False): MainWindow.__init__(self, None) self.setupUi(self) + self.show_toc_on_open = False + self.current_book_has_toc = False self.base_window_title = unicode(self.windowTitle()) self.iterator = None self.current_page = None @@ -214,11 +216,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.action_metadata.setCheckable(True) self.action_metadata.setShortcut(Qt.CTRL+Qt.Key_I) self.action_table_of_contents.setCheckable(True) + self.toc.setMinimumWidth(80) self.action_reference_mode.setCheckable(True) self.connect(self.action_reference_mode, SIGNAL('triggered(bool)'), lambda x: self.view.reference_mode(x)) self.connect(self.action_metadata, SIGNAL('triggered(bool)'), lambda x:self.metadata.setVisible(x)) - self.connect(self.action_table_of_contents, SIGNAL('triggered(bool)'), lambda x:self.toc.setVisible(x)) + self.connect(self.action_table_of_contents, SIGNAL('toggled(bool)'), lambda x:self.toc.setVisible(x)) self.connect(self.action_copy, SIGNAL('triggered(bool)'), self.copy) self.connect(self.action_font_size_larger, SIGNAL('triggered(bool)'), self.font_size_larger) @@ -259,7 +262,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): f = functools.partial(self.load_ebook, pathtoebook) QTimer.singleShot(50, f) self.view.setMinimumSize(100, 100) - self.splitter.setSizes([1, 300]) self.toc.setCursor(Qt.PointingHandCursor) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) @@ -285,6 +287,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def save_state(self): state = str(self.saveState(self.STATE_VERSION)) dynamic['viewer_toolbar_state'] = state + dynamic.set('viewer_window_geometry', self.saveGeometry()) + if self.current_book_has_toc: + dynamic.set('viewer_toc_isvisible', bool(self.toc.isVisible())) + if self.toc.isVisible(): + dynamic.set('viewer_splitter_state', + bytearray(self.splitter.saveState())) def restore_state(self): state = dynamic.get('viewer_toolbar_state', None) @@ -609,10 +617,15 @@ class EbookViewer(MainWindow, Ui_EbookViewer): title = self.iterator.opf.title if not title: title = os.path.splitext(os.path.basename(pathtoebook))[0] - self.action_table_of_contents.setDisabled(not self.iterator.toc) if self.iterator.toc: self.toc_model = TOC(self.iterator.toc) self.toc.setModel(self.toc_model) + if self.show_toc_on_open: + self.action_table_of_contents.setChecked(True) + else: + self.action_table_of_contents.setChecked(False) + self.action_table_of_contents.setDisabled(not self.iterator.toc) + self.current_book_has_toc = bool(self.iterator.toc) self.current_title = title self.setWindowTitle(self.base_window_title+' - '+title) self.pos.setMaximum(sum(self.iterator.pages)) @@ -656,22 +669,21 @@ class EbookViewer(MainWindow, Ui_EbookViewer): return self def __exit__(self, *args): - self.write_settings() if self.iterator is not None: self.save_current_position() self.iterator.__exit__(*args) - def write_settings(self): - dynamic.set('viewer_window_geometry', self.saveGeometry()) - def read_settings(self): c = config().parse() - wg = dynamic['viewer_window_geometry'] - if wg is not None and c.remember_window_size: - self.restoreGeometry(wg) - - - + self.splitter.setSizes([1, 300]) + if c.remember_window_size: + wg = dynamic.get('viewer_window_geometry', None) + if wg is not None: + self.restoreGeometry(wg) + ss = dynamic.get('viewer_splitter_state', None) + if ss is not None: + self.splitter.restoreState(ss) + self.show_toc_on_open = dynamic.get('viewer_toc_isvisible', False) def config(defaults=None): desc = _('Options to control the ebook viewer')