diff --git a/resources/recipes/hbr.recipe b/resources/recipes/hbr.recipe index 3d1e8ccfac..cd7dcd2061 100644 --- a/resources/recipes/hbr.recipe +++ b/resources/recipes/hbr.recipe @@ -33,9 +33,9 @@ class HBR(BasicNewsRecipe): def get_browser(self): br = BasicNewsRecipe.get_browser(self) br.open(self.LOGIN_URL) - br.select_form(name='signInForm') - br['signInForm:username'] = self.username - br['signInForm:password'] = self.password + br.select_form(name='signin-form') + br['signin-form:username'] = self.username + br['signin-form:password'] = self.password raw = br.submit().read() if 'My Account' not in raw: raise Exception('Failed to login, are you sure your username and password are correct?') diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index a2be629449..762a05d193 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -424,7 +424,7 @@ class KOBO(USBMS): paths[source_id] = os.path.join(prefix, *(path.split('/'))) return paths - def update_device_database_collections(self, booklists, collections_attributes): + def update_device_database_collections(self, booklists, collections_attributes, oncard): # debug_print('Starting update_device_database_collections', collections_attributes) # Force collections_attributes to be 'tags' as no other is currently supported @@ -433,23 +433,31 @@ class KOBO(USBMS): collections = booklists.get_collections(collections_attributes) # debug_print('Collections', collections) + + # Create a connection to the sqlite database + # Needs to be outside books collection as in the case of removing + # the last book from the collection the list of books is empty + # and the removal of the last book would not occur + connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') + cursor = connection.cursor() + + # Reset Im_Reading list in the database + if oncard == 'carda': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\'' + elif oncard != 'carda' and oncard != 'cardb': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\'' + + try: + cursor.execute (query) + except: + debug_print('Database Exception: Unable to reset Im_Reading list') + raise + else: +# debug_print('Commit: Reset Im_Reading list') + connection.commit() + for category, books in collections.items(): if category == 'Im_Reading': - # Create a connection to the sqlite database - connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') - cursor = connection.cursor() - - # Reset Im_Reading list in the database - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null' - try: - cursor.execute (query) - except: - debug_print('Database Exception: Unable to reset Im_Reading list') - raise - else: -# debug_print('Commit: Reset Im_Reading list') - connection.commit() - for book in books: # debug_print('Title:', book.title, 'lpath:', book.path) book.device_collections = ['Im_Reading'] @@ -471,8 +479,8 @@ class KOBO(USBMS): connection.commit() # debug_print('Database: Commit create Im_Reading list') - cursor.close() - connection.close() + cursor.close() + connection.close() # debug_print('Finished update_device_database_collections', collections_attributes) @@ -494,12 +502,16 @@ class KOBO(USBMS): #debug_print('KOBO: collection fields:', collections) for i, blist in blists.items(): - self.update_device_database_collections(blist, collections) + if i == 0: + oncard = 'main' + else: + oncard = 'carda' + self.update_device_database_collections(blist, collections, oncard) USBMS.sync_booklists(self, booklists, end_session=end_session) #debug_print('KOBO: finished sync_booklists') def rebuild_collections(self, booklist, oncard): collections_attributes = [] - self.update_device_database_collections(booklist, collections_attributes) + self.update_device_database_collections(booklist, collections_attributes, oncard) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 03a0047927..e87a8021f9 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -216,8 +216,8 @@ class HTMLPreProcessor(object): (re.compile(u'¸\s*()*\s*C', re.UNICODE), lambda match: u'Ç'), # ˛ - (re.compile(u'˛\s*()*\s*a', re.UNICODE), lambda match: u'ą'), - (re.compile(u'˛\s*()*\s*A', re.UNICODE), lambda match: u'Ą'), + (re.compile(u'\s*˛\s*()*\s*a', re.UNICODE), lambda match: u'ą'), + (re.compile(u'\s*˛\s*()*\s*A', re.UNICODE), lambda match: u'Ą'), (re.compile(u'˛\s*()*\s*e', re.UNICODE), lambda match: u'ę'), (re.compile(u'˛\s*()*\s*E', re.UNICODE), lambda match: u'Ę'), diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index ac04652efa..bd9728989b 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -173,6 +173,8 @@ class EditMetadataAction(InterfaceAction): ''' rows = [r.row() for r in \ self.gui.library_view.selectionModel().selectedRows()] + m = self.gui.library_view.model() + ids = [m.id(r) for r in rows] if not rows or len(rows) == 0: d = error_dialog(self.gui, _('Cannot edit metadata'), _('No books selected')) @@ -191,6 +193,7 @@ class EditMetadataAction(InterfaceAction): self.gui.tags_view.recount() if self.gui.cover_flow: self.gui.cover_flow.dataChanged() + self.gui.library_view.select_rows(ids) # Merge books {{{ def merge_books(self, safe_merge=False): diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index b20cd7594f..ae3141db56 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -707,6 +707,10 @@ class DeviceMixin(object): # {{{ ''' Called when a device is connected to the computer. ''' + # This can happen as this function is called in a queued connection and + # the user could have yanked the device in the meantime + if connected and not self.device_manager.is_device_connected: + connected = False self.set_device_menu_items_state(connected) if connected: self.device_manager.get_device_information(\ diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index b0ba049a7f..2e5c7838ca 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -220,6 +220,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): if val: val.sort(cmp=lambda x,y: cmp(x.lower(), y.lower())) val = val[0] + if txt == 'authors': + val = val.replace('|', ',') else: val = '' else: @@ -303,6 +305,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): # The standard tags and authors values want to be lists. # All custom columns are to be strings val = fm['is_multiple'].join(val) + elif field == 'authors': + val = [v.replace('|', ',') for v in val] else: val = apply_pattern(val) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 18d6760ea7..aca7b0cb75 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -328,6 +328,9 @@ Future conversion of these books will use the default settings. true + + true + diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index d07eac7670..26dbda6ca4 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -751,20 +751,22 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): try: if self.formats_changed: self.sync_formats() - title = unicode(self.title.text()) + title = unicode(self.title.text()).strip() self.db.set_title(self.id, title, notify=False) - au = unicode(self.authors.text()) + au = unicode(self.authors.text()).strip() if au: self.db.set_authors(self.id, string_to_authors(au), notify=False) - aus = unicode(self.author_sort.text()) + aus = unicode(self.author_sort.text()).strip() if aus: self.db.set_author_sort(self.id, aus, notify=False, commit=False) self.db.set_isbn(self.id, - re.sub(r'[^0-9a-zA-Z]', '', unicode(self.isbn.text())), + re.sub(r'[^0-9a-zA-Z]', '', + unicode(self.isbn.text()).strip()), notify=False, commit=False) self.db.set_rating(self.id, 2*self.rating.value(), notify=False, commit=False) - self.db.set_publisher(self.id, unicode(self.publisher.currentText()), + self.db.set_publisher(self.id, + unicode(self.publisher.currentText()).strip(), notify=False, commit=False) self.db.set_tags(self.id, [x.strip() for x in unicode(self.tags.text()).split(',')], notify=False, commit=False) @@ -773,7 +775,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): commit=False) self.db.set_series_index(self.id, self.series_index.value(), notify=False, commit=False) - self.db.set_comment(self.id, unicode(self.comments.toPlainText()), + self.db.set_comment(self.id, + unicode(self.comments.toPlainText()).strip(), notify=False, commit=False) d = self.pubdate.date() d = qt_to_dt(d) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index d67d286aeb..724454dccf 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -479,6 +479,36 @@ class BooksView(QTableView): # {{{ sm = self.selectionModel() sm.select(index, sm.ClearAndSelect|sm.Rows) + def select_rows(self, identifiers, using_ids=True, change_current=True, + scroll=True): + ''' + Select rows identified by identifiers. identifiers can be a set of ids, + row numbers or QModelIndexes. + ''' + selmode = self.selectionMode() + self.setSelectionMode(QAbstractItemView.MultiSelection) + try: + rows = set([x.row() if hasattr(x, 'row') else x for x in + identifiers]) + if using_ids: + rows = set([]) + identifiers = set(identifiers) + m = self.model() + for row in range(m.rowCount(QModelIndex())): + if m.id(row) in identifiers: + rows.add(row) + if rows: + row = list(sorted(rows))[0] + if change_current: + self.set_current_row(row, select=False) + if scroll: + self.scroll_to_row(row) + self.clearSelection() + for r in rows: + self.selectRow(r) + finally: + self.setSelectionMode(selmode) + def close(self): self._model.close() diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index f40eed1fcd..f8d50d1cd2 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -38,6 +38,7 @@ from calibre.gui2.init import LibraryViewMixin, LayoutMixin from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin from calibre.gui2.tag_view import TagBrowserMixin +from calibre.utils.ordered_dict import OrderedDict class Listener(Thread): # {{{ @@ -97,7 +98,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ MainWindow.__init__(self, opts, parent) self.opts = opts self.device_connected = None - acmap = {} + acmap = OrderedDict() for action in interface_actions(): mod, cls = action.actual_plugin.split(':') ac = getattr(__import__(mod, fromlist=['1'], level=0), cls)(self, diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index ecb467b4c2..95794a8c1d 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -56,7 +56,7 @@ class ContentServer(object): def sort(self, items, field, order): field = self.db.data.sanitize_sort_field_name(field) - if field not in ('title', 'authors', 'rating', 'timestamp', 'tags', 'size', 'series'): + if field not in self.db.field_metadata.field_keys(): raise cherrypy.HTTPError(400, '%s is not a valid sort field'%field) keyg = CSSortKeyGenerator([(field, order)], self.db.field_metadata) items.sort(key=keyg, reverse=not order) diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 781048666b..beea30acb2 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -81,7 +81,7 @@ Device Integration What devices does |app| support? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/3/DX/DXG, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. +At the moment |app| has full support for the SONY PRS line, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle line, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. How can I help get my device supported in |app|? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~