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(