Fix customization of SONY driver plugin, where deleting all collections items causes the settign to disappear. Fix #5751 (Adding/Deleting Search does not refresh Left Pane Correctly) and distiguish between delete confirmation dialogs for deleting from library and device views

This commit is contained in:
Kovid Goyal 2010-06-08 10:56:31 -06:00
commit 085c859327
7 changed files with 83 additions and 39 deletions

View File

@ -143,10 +143,11 @@ class PRS505(USBMS):
if booklists[i] is not None: if booklists[i] is not None:
blists[i] = booklists[i] blists[i] = booklists[i]
opts = self.settings() opts = self.settings()
collections = ['series', 'tags']
if opts.extra_customization: if opts.extra_customization:
collections = [x.strip() for x in collections = [x.strip() for x in
opts.extra_customization.split(',')] opts.extra_customization.split(',')]
else:
collections = []
debug_print('PRS505: collection fields:', collections) debug_print('PRS505: collection fields:', collections)
c.update(blists, collections) c.update(blists, collections)
c.write() c.write()

View File

@ -38,9 +38,10 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
self.opt_read_metadata.setChecked(self.settings.read_metadata) self.opt_read_metadata.setChecked(self.settings.read_metadata)
else: else:
self.opt_read_metadata.hide() self.opt_read_metadata.hide()
if extra_customization_message and settings.extra_customization: if extra_customization_message:
self.extra_customization_label.setText(extra_customization_message) self.extra_customization_label.setText(extra_customization_message)
self.opt_extra_customization.setText(settings.extra_customization) if settings.extra_customization:
self.opt_extra_customization.setText(settings.extra_customization)
else: else:
self.extra_customization_label.setVisible(False) self.extra_customization_label.setVisible(False)
self.opt_extra_customization.setVisible(False) self.opt_extra_customization.setVisible(False)

View File

@ -3,6 +3,7 @@ __copyright__ = '2010, Kovid Goyal <kovid at kovidgoyal.net>'
'''Dialog to create a new custom column''' '''Dialog to create a new custom column'''
import re
from functools import partial from functools import partial
from PyQt4.QtCore import SIGNAL from PyQt4.QtCore import SIGNAL
@ -94,8 +95,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
col = unicode(self.column_name_box.text()).lower() col = unicode(self.column_name_box.text()).lower()
if not col: if not col:
return self.simple_error('', _('No lookup name was provided')) return self.simple_error('', _('No lookup name was provided'))
if not col.isalnum() or not col[0].isalpha(): if re.match('^\w*$', col) is None or not col[0].isalpha():
return self.simple_error('', _('The label must contain only letters and digits, and start with a letter')) return self.simple_error('', _('The label must contain only letters, digits and underscores, and start with a letter'))
col_heading = unicode(self.column_heading_box.text()) col_heading = unicode(self.column_heading_box.text())
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype'] col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
if col_type == '*text': if col_type == '*text':

View File

@ -63,8 +63,7 @@ class TagsView(QTreeView): # {{{
def sort_changed(self, state): def sort_changed(self, state):
config.set('sort_by_popularity', state == Qt.Checked) config.set('sort_by_popularity', state == Qt.Checked)
self.model().refresh() self.recount()
# self.search_restriction_set()
def set_search_restriction(self, s): def set_search_restriction(self, s):
if s: if s:
@ -197,7 +196,9 @@ class TagsView(QTreeView): # {{{
ci = self.indexAt(QPoint(10, 10)) ci = self.indexAt(QPoint(10, 10))
path = self.model().path_for_index(ci) if self.is_visible(ci) else None path = self.model().path_for_index(ci) if self.is_visible(ci) else None
try: try:
self.model().refresh() if not self.model().refresh(): # categories changed!
self.set_new_model()
path = None
except: #Database connection could be closed if an integrity check is happening except: #Database connection could be closed if an integrity check is happening
pass pass
if path: if path:
@ -210,10 +211,16 @@ class TagsView(QTreeView): # {{{
# gone, or if columns have been hidden or restored, we must rebuild the # gone, or if columns have been hidden or restored, we must rebuild the
# model. Reason: it is much easier than reconstructing the browser tree. # model. Reason: it is much easier than reconstructing the browser tree.
def set_new_model(self): def set_new_model(self):
self._model = TagsModel(self.db, parent=self, try:
hidden_categories=self.hidden_categories, self._model = TagsModel(self.db, parent=self,
search_restriction=self.search_restriction) hidden_categories=self.hidden_categories,
self.setModel(self._model) search_restriction=self.search_restriction)
self.setModel(self._model)
except:
# The DB must be gone. Set the model to None and hope that someone
# will call set_database later. I don't know if this in fact works
self._model = None
self.setModel(None)
# }}} # }}}
class TagTreeItem(object): # {{{ class TagTreeItem(object): # {{{
@ -323,18 +330,9 @@ class TagsModel(QAbstractItemModel): # {{{
self.tags_view = parent self.tags_view = parent
self.hidden_categories = hidden_categories self.hidden_categories = hidden_categories
self.search_restriction = search_restriction self.search_restriction = search_restriction
self.row_map = []
# Reconstruct the user categories, putting them into metadata # get_node_tree cannot return None here, because row_map is empty
tb_cats = self.db.field_metadata
for k in tb_cats.keys():
if tb_cats[k]['kind'] in ['user', 'search']:
del tb_cats[k]
for user_cat in sorted(prefs['user_categories'].keys()):
cat_name = user_cat+':' # add the ':' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
if len(saved_searches.names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
data = self.get_node_tree(config['sort_by_popularity']) data = self.get_node_tree(config['sort_by_popularity'])
self.root_item = TagTreeItem() self.root_item = TagTreeItem()
for i, r in enumerate(self.row_map): for i, r in enumerate(self.row_map):
@ -355,9 +353,22 @@ class TagsModel(QAbstractItemModel): # {{{
self.search_restriction = s self.search_restriction = s
def get_node_tree(self, sort): def get_node_tree(self, sort):
old_row_map = self.row_map[:]
self.row_map = [] self.row_map = []
self.categories = [] self.categories = []
# Reconstruct the user categories, putting them into metadata
tb_cats = self.db.field_metadata
for k in tb_cats.keys():
if tb_cats[k]['kind'] in ['user', 'search']:
del tb_cats[k]
for user_cat in sorted(prefs['user_categories'].keys()):
cat_name = user_cat+':' # add the ':' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
if len(saved_searches.names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
# Now get the categories
if self.search_restriction: if self.search_restriction:
data = self.db.get_categories(sort_on_count=sort, data = self.db.get_categories(sort_on_count=sort,
icon_map=self.category_icon_map, icon_map=self.category_icon_map,
@ -367,13 +378,19 @@ class TagsModel(QAbstractItemModel): # {{{
tb_categories = self.db.field_metadata tb_categories = self.db.field_metadata
for category in tb_categories: for category in tb_categories:
if category in data: # They should always be there, but ... if category in data: # The search category can come and go
self.row_map.append(category) self.row_map.append(category)
self.categories.append(tb_categories[category]['name']) self.categories.append(tb_categories[category]['name'])
if len(old_row_map) != 0 and len(old_row_map) != len(self.row_map):
# A category has been added or removed. We must force a rebuild of
# the model
return None
return data return data
def refresh(self): def refresh(self):
data = self.get_node_tree(config['sort_by_popularity']) # get category data data = self.get_node_tree(config['sort_by_popularity']) # get category data
if data is None:
return False
row_index = -1 row_index = -1
for i, r in enumerate(self.row_map): for i, r in enumerate(self.row_map):
if self.hidden_categories and self.categories[i] in self.hidden_categories: if self.hidden_categories and self.categories[i] in self.hidden_categories:
@ -395,6 +412,7 @@ class TagsModel(QAbstractItemModel): # {{{
tag.state = state_map.get(tag.name, 0) tag.state = state_map.get(tag.name, 0)
t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_state_map) t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_state_map)
self.endInsertRows() self.endInsertRows()
return True
def columnCount(self, parent): def columnCount(self, parent):
return 1 return 1
@ -408,6 +426,8 @@ class TagsModel(QAbstractItemModel): # {{{
def setData(self, index, value, role=Qt.EditRole): def setData(self, index, value, role=Qt.EditRole):
if not index.isValid(): if not index.isValid():
return NONE return NONE
# set up to position at the category label
path = self.path_for_index(self.parent(index))
val = unicode(value.toString()) val = unicode(value.toString())
if not val: if not val:
error_dialog(self.tags_view, _('Item is blank'), error_dialog(self.tags_view, _('Item is blank'),
@ -439,7 +459,12 @@ class TagsModel(QAbstractItemModel): # {{{
label=self.db.field_metadata[key]['label']) label=self.db.field_metadata[key]['label'])
self.tags_view.tag_item_renamed.emit() self.tags_view.tag_item_renamed.emit()
item.tag.name = val item.tag.name = val
self.refresh() self.refresh() # Should work, because no categories can have disappeared
if path:
idx = self.index_for_path(path)
if idx.isValid():
self.tags_view.setCurrentIndex(idx)
self.tags_view.scrollTo(idx, QTreeView.PositionAtCenter)
return True return True
def headerData(self, *args): def headerData(self, *args):

View File

@ -854,7 +854,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
r = unicode(r) r = unicode(r)
if r is not None and r != '': if r is not None and r != '':
self.restriction_in_effect = True self.restriction_in_effect = True
restriction = "search:%s"%(r) restriction = 'search:"%s"'%(r)
else: else:
self.restriction_in_effect = False self.restriction_in_effect = False
restriction = '' restriction = ''
@ -1557,7 +1557,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if not confirm('<p>'+_('The selected books will be ' if not confirm('<p>'+_('The selected books will be '
'<b>permanently deleted</b> ' '<b>permanently deleted</b> '
'from your device. Are you sure?') 'from your device. Are you sure?')
+'</p>', 'library_delete_books', self): +'</p>', 'device_delete_books', self):
return return
if self.stack.currentIndex() == 1: if self.stack.currentIndex() == 1:
view = self.memory_view view = self.memory_view

View File

@ -192,7 +192,7 @@ class CustomColumns(object):
# check if item exists # check if item exists
new_id = self.conn.get( new_id = self.conn.get(
'SELECT id FROM %s WHERE value=?'%table, (new_name,), all=False) 'SELECT id FROM %s WHERE value=?'%table, (new_name,), all=False)
if new_id is None: if new_id is None or old_id == new_id:
self.conn.execute('UPDATE %s SET value=? WHERE id=?'%table, (new_name, old_id)) self.conn.execute('UPDATE %s SET value=? WHERE id=?'%table, (new_name, old_id))
else: else:
# New id exists. If the column is_multiple, then process like # New id exists. If the column is_multiple, then process like

View File

@ -1003,8 +1003,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
new_id = self.conn.get( new_id = self.conn.get(
'''SELECT id from tags '''SELECT id from tags
WHERE name=?''', (new_name,), all=False) WHERE name=?''', (new_name,), all=False)
if new_id is None: if new_id is None or old_id == new_id:
# easy case. Simply rename the tag # easy cases. Simply rename the tag. Do it even if equal, in case
# there is a change of case
self.conn.execute('''UPDATE tags SET name=? self.conn.execute('''UPDATE tags SET name=?
WHERE id=?''', (new_name, old_id)) WHERE id=?''', (new_name, old_id))
else: else:
@ -1041,7 +1042,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
new_id = self.conn.get( new_id = self.conn.get(
'''SELECT id from series '''SELECT id from series
WHERE name=?''', (new_name,), all=False) WHERE name=?''', (new_name,), all=False)
if new_id is None: if new_id is None or old_id == new_id:
self.conn.execute('UPDATE series SET name=? WHERE id=?', self.conn.execute('UPDATE series SET name=? WHERE id=?',
(new_name, old_id)) (new_name, old_id))
else: else:
@ -1086,7 +1087,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
new_id = self.conn.get( new_id = self.conn.get(
'''SELECT id from publishers '''SELECT id from publishers
WHERE name=?''', (new_name,), all=False) WHERE name=?''', (new_name,), all=False)
if new_id is None: if new_id is None or old_id == new_id:
# New name doesn't exist. Simply change the old name # New name doesn't exist. Simply change the old name
self.conn.execute('UPDATE publishers SET name=? WHERE id=?', \ self.conn.execute('UPDATE publishers SET name=? WHERE id=?', \
(new_name, old_id)) (new_name, old_id))
@ -1113,22 +1114,34 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
new_name = new_name.replace(',', '|') new_name = new_name.replace(',', '|')
# Get the list of books we must fix up, one way or the other # Get the list of books we must fix up, one way or the other
books = self.conn.get('SELECT book from books_authors_link WHERE author=?', (old_id,)) # Save the list so we can use it twice
bks = self.conn.get('SELECT book from books_authors_link WHERE author=?', (old_id,))
books = []
for (book_id,) in bks:
books.append(book_id)
# check if the new author already exists # check if the new author already exists
new_id = self.conn.get('SELECT id from authors WHERE name=?', new_id = self.conn.get('SELECT id from authors WHERE name=?',
(new_name,), all=False) (new_name,), all=False)
if new_id is None: if new_id is None or old_id == new_id:
# No name clash. Go ahead and update the author's name # No name clash. Go ahead and update the author's name
self.conn.execute('UPDATE authors SET name=? WHERE id=?', self.conn.execute('UPDATE authors SET name=? WHERE id=?',
(new_name, old_id)) (new_name, old_id))
else: else:
# First check for the degenerate case -- changing a value to itself.
# Update it in case there is a change of case, but do nothing else
if old_id == new_id:
self.conn.execute('UPDATE authors SET name=? WHERE id=?',
(new_name, old_id))
self.conn.commit()
return
# Author exists. To fix this, we must replace all the authors # Author exists. To fix this, we must replace all the authors
# instead of replacing the one. Reason: db integrity checks can stop # instead of replacing the one. Reason: db integrity checks can stop
# the rename process, which would leave everything half-done. We # the rename process, which would leave everything half-done. We
# can't do it the same way as tags (delete and add) because author # can't do it the same way as tags (delete and add) because author
# order is important. # order is important.
for (book_id,) in books:
for book_id in books:
# Get the existing list of authors # Get the existing list of authors
authors = self.conn.get(''' authors = self.conn.get('''
SELECT author from books_authors_link SELECT author from books_authors_link
@ -1139,7 +1152,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# with the new one while we are at it # with the new one while we are at it
for i,aut in enumerate(authors): for i,aut in enumerate(authors):
authors[i] = aut[0] if aut[0] != old_id else new_id authors[i] = aut[0] if aut[0] != old_id else new_id
# Delete the existing authors list # Delete the existing authors list
self.conn.execute('''DELETE FROM books_authors_link self.conn.execute('''DELETE FROM books_authors_link
WHERE book=?''',(book_id,)) WHERE book=?''',(book_id,))
@ -1154,11 +1166,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# metadata. Ignore it. # metadata. Ignore it.
pass pass
# Now delete the old author from the DB # Now delete the old author from the DB
bks = self.conn.get('SELECT book FROM books_authors_link WHERE author=?', (old_id,))
self.conn.execute('DELETE FROM authors WHERE id=?', (old_id,)) self.conn.execute('DELETE FROM authors WHERE id=?', (old_id,))
self.conn.commit() self.conn.commit()
# the authors are now changed, either by changing the author's name # the authors are now changed, either by changing the author's name
# or replacing the author in the list. Now must fix up the books. # or replacing the author in the list. Now must fix up the books.
for (book_id,) in books: for book_id in books:
# First, must refresh the cache to see the new authors # First, must refresh the cache to see the new authors
self.data.refresh_ids(self, [book_id]) self.data.refresh_ids(self, [book_id])
# now fix the filesystem paths # now fix the filesystem paths
@ -1168,14 +1181,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
SELECT authors.name SELECT authors.name
FROM authors, books_authors_link as bl FROM authors, books_authors_link as bl
WHERE bl.book = ? and bl.author = authors.id WHERE bl.book = ? and bl.author = authors.id
ORDER BY bl.id
''' , (book_id,)) ''' , (book_id,))
# unpack the double-list structure # unpack the double-list structure
for i,aut in enumerate(authors): for i,aut in enumerate(authors):
authors[i] = aut[0] authors[i] = aut[0]
ss = authors_to_sort_string(authors) ss = authors_to_sort_string(authors)
# Change the '|'s to ','
ss = ss.replace('|', ',')
self.conn.execute('''UPDATE books self.conn.execute('''UPDATE books
SET author_sort=? SET author_sort=?
WHERE id=?''', (ss, old_id)) WHERE id=?''', (ss, book_id))
self.conn.commit() self.conn.commit()
# the caller will do a general refresh, so we don't need to # the caller will do a general refresh, so we don't need to
# do one here # do one here