diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 811acbe55b..0deef5eb92 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -38,7 +38,7 @@ class ANDROID(USBMS): 0x227]}, # Samsung - 0x04e8 : { 0x681d : [0x0222, 0x0224, 0x0400], + 0x04e8 : { 0x681d : [0x0222, 0x0223, 0x0224, 0x0400], 0x681c : [0x0222, 0x0224, 0x0400], 0x6640 : [0x0100], }, @@ -62,7 +62,8 @@ class ANDROID(USBMS): 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX'] WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', - 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID'] + 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', + 'SCH-I500_CARD'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID'] diff --git a/src/calibre/gui2/actions/similar_books.py b/src/calibre/gui2/actions/similar_books.py index 644cd3160a..b1ee04a4d4 100644 --- a/src/calibre/gui2/actions/similar_books.py +++ b/src/calibre/gui2/actions/similar_books.py @@ -58,6 +58,7 @@ class SimilarBooksAction(InterfaceAction): for a in authors.split(',')] join = ' or ' if search: - self.gui.search.set_search_string(join.join(search)) + self.gui.search.set_search_string(join.join(search), + store_in_history=True) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 7ea4fa679f..0b2065888c 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -316,7 +316,6 @@ class Enumeration(Base): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), QComboBox(parent)] w = self.widgets[1] - print self.col_metadata['display'] vals = self.col_metadata['display']['enum_values'] w.addItem('') for v in vals: @@ -598,7 +597,6 @@ class BulkEnumeration(BulkBase, Enumeration): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), QComboBox(parent)] w = self.widgets[1] - print self.col_metadata['display'] vals = self.col_metadata['display']['enum_values'] w.addItem('Do Not Change') w.addItem('') diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index e30e0e16e1..7c6125d537 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -255,7 +255,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): fm = self.db.field_metadata for f in fm: if (f in ['author_sort'] or - (fm[f]['datatype'] in ['text', 'series'] + (fm[f]['datatype'] in ['text', 'series', 'enumeration'] and fm[f].get('search_terms', None) and f not in ['formats', 'ondevice', 'sort'])): self.all_fields.append(f) diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index d4d2b2678c..ff44080022 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -92,7 +92,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): if c['display'].get('date_format', None): self.date_format_box.setText(c['display'].get('date_format', '')) elif ct == 'composite': - self.composite_box.setText(c['display'].get('enum_values', '')) + self.composite_box.setText(c['display'].get('composite_template', '')) + elif ct == 'enumeration': + self.enum_box.setText(','.join(c['display'].get('enum_values', []))) self.datatype_changed() self.exec_() diff --git a/src/calibre/gui2/preferences/create_custom_column.ui b/src/calibre/gui2/preferences/create_custom_column.ui index 03c191d34e..3e4c6f59ca 100644 --- a/src/calibre/gui2/preferences/create_custom_column.ui +++ b/src/calibre/gui2/preferences/create_custom_column.ui @@ -158,7 +158,7 @@ - <p>Field template. Uses the same syntax as save templates. + Field template. Uses the same syntax as save templates. @@ -202,12 +202,18 @@ Values + + enum_box + + + A comma-separated list of valid values. + 0 diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 1832520cec..0c8eb84a37 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -18,7 +18,7 @@ from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor from calibre.gui2.dialogs.search import SearchDialog from calibre.utils.search_query_parser import saved_searches -class SearchLineEdit(QLineEdit): +class SearchLineEdit(QLineEdit): # {{{ key_pressed = pyqtSignal(object) def keyPressEvent(self, event): @@ -37,17 +37,23 @@ class SearchLineEdit(QLineEdit): def paste(self, *args): self.parent().normalize_state() return QLineEdit.paste(self) +# }}} -class SearchBox2(QComboBox): +class SearchBox2(QComboBox): # {{{ ''' To use this class: * Call initialize() * Connect to the search() and cleared() signals from this widget. - * Connect to the cleared() signal to know when the box content changes - * Connect to focus_to_library signal to be told to manually change focus + * Connect to the changed() signal to know when the box content changes + * Connect to focus_to_library() signal to be told to manually change focus * Call search_done() after every search is complete + * Call set_search_string() to perform a search programmatically + * You can use the current_text property to get the current search text + Be aware that if you are using it in a slow connected to the + changed() signal, if the connection is not queued it will not be + accurate. ''' INTERVAL = 1500 #: Time to wait before emitting search signal @@ -166,49 +172,43 @@ class SearchBox2(QComboBox): self.changed.emit() self.do_search() - def do_search(self, *args): + def _do_search(self, store_in_history=True): text = unicode(self.currentText()).strip() if not text: return self.clear() self.search.emit(text) - idx = self.findText(text, Qt.MatchFixedString) - self.block_signals(True) - if idx < 0: - self.insertItem(0, text) - else: - t = self.itemText(idx) - self.removeItem(idx) - self.insertItem(0, t) + if store_in_history: + idx = self.findText(text, Qt.MatchFixedString) + self.block_signals(True) + if idx < 0: + self.insertItem(0, text) + else: + t = self.itemText(idx) + self.removeItem(idx) + self.insertItem(0, t) self.setCurrentIndex(0) - self.block_signals(False) - config[self.opt_name] = [unicode(self.itemText(i)) for i in - range(self.count())] + self.block_signals(False) + config[self.opt_name] = [unicode(self.itemText(i)) for i in + range(self.count())] + + def do_search(self, *args): + self._do_search() def block_signals(self, yes): self.blockSignals(yes) self.line_edit.blockSignals(yes) - def search_from_tokens(self, tokens, all): - ans = u' '.join([u'%s:%s'%x for x in tokens]) - if not all: - ans = '[' + ans + ']' - self.set_search_string(ans) - - def search_from_tags(self, tags, all): - joiner = ' and ' if all else ' or ' - self.set_search_string(joiner.join(tags)) - - def set_search_string(self, txt): + def set_search_string(self, txt, store_in_history=False): self.setFocus(Qt.OtherFocusReason) if not txt: self.clear() else: self.normalize_state() self.setEditText(txt) - self.search.emit(txt) self.line_edit.end(False) - self.initial_state = False + self.changed.emit() + self._do_search(store_in_history=store_in_history) self.focus_to_library.emit() def search_as_you_type(self, enabled): @@ -217,7 +217,13 @@ class SearchBox2(QComboBox): def in_a_search(self): return self._in_a_search -class SavedSearchBox(QComboBox): + @property + def current_text(self): + return unicode(self.lineEdit().text()) + + # }}} + +class SavedSearchBox(QComboBox): # {{{ ''' To use this class: @@ -329,13 +335,17 @@ class SavedSearchBox(QComboBox): return self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText()))) -class SearchBoxMixin(object): + # }}} + +class SearchBoxMixin(object): # {{{ def __init__(self): self.search.initialize('main_search_history', colorize=True, help_text=_('Search (For Advanced Search click the button to the left)')) self.search.cleared.connect(self.search_box_cleared) - self.search.changed.connect(self.search_box_changed) + # Queued so that search.current_text will be correct + self.search.changed.connect(self.search_box_changed, + type=Qt.QueuedConnection) self.search.focus_to_library.connect(self.focus_to_library) self.clear_button.clicked.connect(self.search.clear_clicked) self.advanced_search_button.clicked[bool].connect(self.do_advanced_search) @@ -364,7 +374,7 @@ class SearchBoxMixin(object): def search_box_changed(self): self.saved_search.clear() - self.tags_view.clear() + self.tags_view.conditional_clear(self.search.current_text) def do_advanced_search(self, *args): d = SearchDialog(self, self.library_view.model().db) @@ -378,7 +388,9 @@ class SearchBoxMixin(object): def focus_to_library(self): self.current_view().setFocus(Qt.OtherFocusReason) -class SavedSearchBoxMixin(object): + # }}} + +class SavedSearchBoxMixin(object): # {{{ def __init__(self): self.saved_search.changed.connect(self.saved_searches_changed) @@ -417,3 +429,6 @@ class SavedSearchBoxMixin(object): if d.result() == d.Accepted: self.saved_searches_changed() self.saved_search.clear() + + # }}} + diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index b841706439..972a1eeba3 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -60,7 +60,7 @@ class TagDelegate(QItemDelegate): # {{{ class TagsView(QTreeView): # {{{ refresh_required = pyqtSignal() - tags_marked = pyqtSignal(object, object) + tags_marked = pyqtSignal(object) user_category_edit = pyqtSignal(object) tag_list_edit = pyqtSignal(object, object) saved_search_edit = pyqtSignal(object) @@ -135,11 +135,21 @@ class TagsView(QTreeView): # {{{ # swallow these to avoid toggling and editing at the same time pass + @property + def search_string(self): + tokens = self._model.tokens() + joiner = ' and ' if self.match_all else ' or ' + return joiner.join(tokens) + def toggle(self, index): modifiers = int(QApplication.keyboardModifiers()) exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT) if self._model.toggle(index, exclusive): - self.tags_marked.emit(self._model.tokens(), self.match_all) + self.tags_marked.emit(self.search_string) + + def conditional_clear(self, search_string): + if search_string != self.search_string: + self.clear() def context_menu_handler(self, action=None, category=None, key=None, index=None): @@ -842,8 +852,7 @@ class TagBrowserMixin(object): # {{{ 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.sort_by) - self.tags_view.tags_marked.connect(self.search.search_from_tags) - self.tags_view.tags_marked.connect(self.saved_search.clear) + self.tags_view.tags_marked.connect(self.search.set_search_string) self.tags_view.tag_list_edit.connect(self.do_tags_list_edit) self.tags_view.user_category_edit.connect(self.do_user_categories_edit) self.tags_view.saved_search_edit.connect(self.do_saved_search_edit) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 6148019906..7b4c66c8b8 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -520,7 +520,7 @@ class ResultCache(SearchQueryParser): # {{{ if len(self.field_metadata[x]['search_terms']): db_col[x] = self.field_metadata[x]['rec_index'] if self.field_metadata[x]['datatype'] not in \ - ['composite', 'text', 'comments', 'series']: + ['composite', 'text', 'comments', 'series', 'enumeration']: exclude_fields.append(db_col[x]) col_datatype[db_col[x]] = self.field_metadata[x]['datatype'] is_multiple_cols[db_col[x]] = self.field_metadata[x]['is_multiple'] @@ -828,7 +828,7 @@ class SortKeyGenerator(object): sidx = record[sidx_fm['rec_index']] val = (val, sidx) - elif dt in ('text', 'comments', 'composite'): + elif dt in ('text', 'comments', 'composite', 'enumeration'): if val is None: val = '' val = val.lower() diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index b477c89fe5..258b18e7ff 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -440,6 +440,10 @@ class CustomColumns(object): val = self.custom_data_adapters[data['datatype']](val, data) if data['normalized']: + if data['datatype'] == 'enumeration' and \ + val not in data['display']['enum_values']: + print 'attempt to set enum to', val + return None if not append or not data['is_multiple']: self.conn.execute('DELETE FROM %s WHERE book=?'%lt, (id_,)) self.conn.execute(