mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Direct editing for most categories inthe Tag Browser
This commit is contained in:
commit
0a588b7b0d
@ -1,17 +1,17 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from functools import partial
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
from PyQt4.QtGui import QDialog, QListWidgetItem
|
||||
|
||||
from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor
|
||||
from calibre.gui2 import question_dialog, error_dialog
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
|
||||
class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
|
||||
def tag_cmp(self, x, y):
|
||||
return cmp(x.lower(), y.lower())
|
||||
|
||||
def __init__(self, window, db, tag_to_match):
|
||||
def __init__(self, window, db, tag_to_match, category):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagListEditor.__init__(self)
|
||||
self.setupUi(self)
|
||||
@ -20,9 +20,28 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
self.to_delete = []
|
||||
self.db = db
|
||||
self.all_tags = {}
|
||||
for k,v in db.get_tags_with_ids():
|
||||
self.category = category
|
||||
if category == 'tags':
|
||||
result = db.get_tags_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
elif category == 'series':
|
||||
result = db.get_series_with_ids()
|
||||
compare = (lambda x,y:cmp(title_sort(x).lower(), title_sort(y).lower()))
|
||||
elif category == 'publisher':
|
||||
result = db.get_publishers_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
else: # should be a custom field
|
||||
self.cc_label = None
|
||||
if category in db.field_metadata:
|
||||
self.cc_label = db.field_metadata[category]['label']
|
||||
result = self.db.get_custom_items_with_ids(label=self.cc_label)
|
||||
else:
|
||||
result = []
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
|
||||
for k,v in result:
|
||||
self.all_tags[v] = k
|
||||
for tag in sorted(self.all_tags.keys(), cmp=self.tag_cmp):
|
||||
for tag in sorted(self.all_tags.keys(), cmp=compare):
|
||||
item = QListWidgetItem(tag)
|
||||
item.setData(Qt.UserRole, self.all_tags[tag])
|
||||
self.available_tags.addItem(item)
|
||||
@ -37,13 +56,18 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
self.connect(self.available_tags, SIGNAL('itemChanged(QListWidgetItem *)'), self.finish_editing)
|
||||
|
||||
def finish_editing(self, item):
|
||||
if item.text() != self.item_before_editing.text():
|
||||
if item.text() in self.all_tags.keys() or item.text() in self.to_rename.keys():
|
||||
error_dialog(self, 'Tag already used',
|
||||
'The tag %s is already used.'%(item.text())).exec_()
|
||||
if not item.text():
|
||||
error_dialog(self, _('Item is blank'),
|
||||
_('An item cannot be set to nothing. Delete it instead.')).exec_()
|
||||
item.setText(self.item_before_editing.text())
|
||||
return
|
||||
id,ign = self.item_before_editing.data(Qt.UserRole).toInt()
|
||||
if item.text() != self.item_before_editing.text():
|
||||
if item.text() in self.all_tags.keys() or item.text() in self.to_rename.keys():
|
||||
error_dialog(self, _('Item already used'),
|
||||
_('The item %s is already used.')%(item.text())).exec_()
|
||||
item.setText(self.item_before_editing.text())
|
||||
return
|
||||
(id,ign) = self.item_before_editing.data(Qt.UserRole).toInt()
|
||||
self.to_rename[item.text()] = id
|
||||
|
||||
def rename_tag(self):
|
||||
@ -52,38 +76,53 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
|
||||
def _rename_tag(self, item):
|
||||
if item is None:
|
||||
error_dialog(self, 'No tag selected', 'You must select one tag from the list of Available tags.').exec_()
|
||||
error_dialog(self, _('No item selected'),
|
||||
_('You must select one item from the list of Available items.')).exec_()
|
||||
return
|
||||
self.item_before_editing = item.clone()
|
||||
item.setFlags (item.flags() | Qt.ItemIsEditable);
|
||||
self.available_tags.editItem(item)
|
||||
|
||||
def delete_tags(self, item=None):
|
||||
confirms, deletes = [], []
|
||||
items = self.available_tags.selectedItems() if item is None else [item]
|
||||
if not items:
|
||||
error_dialog(self, 'No tags selected', 'You must select at least one tag from the list of Available tags.').exec_()
|
||||
deletes = self.available_tags.selectedItems() if item is None else [item]
|
||||
if not deletes:
|
||||
error_dialog(self, _('No items selected'),
|
||||
_('You must select at least one items from the list.')).exec_()
|
||||
return
|
||||
ct = ', '.join([unicode(item.text()) for item in deletes])
|
||||
if not question_dialog(self, _('Are your sure?'),
|
||||
'<p>'+_('Are you certain you want to delete the following items?')+'<br>'+ct):
|
||||
return
|
||||
for item in items:
|
||||
if self.db.is_tag_used(unicode(item.text())):
|
||||
confirms.append(item)
|
||||
else:
|
||||
deletes.append(item)
|
||||
if confirms:
|
||||
ct = ', '.join([unicode(item.text()) for item in confirms])
|
||||
if question_dialog(self, _('Are your sure?'),
|
||||
'<p>'+_('The following tags are used by one or more books. '
|
||||
'Are you certain you want to delete them?')+'<br>'+ct):
|
||||
deletes += confirms
|
||||
|
||||
for item in deletes:
|
||||
self.to_delete.append(item)
|
||||
(id,ign) = item.data(Qt.UserRole).toInt()
|
||||
self.to_delete.append(id)
|
||||
self.available_tags.takeItem(self.available_tags.row(item))
|
||||
|
||||
def accept(self):
|
||||
for text in self.to_rename:
|
||||
self.db.rename_tag(self.to_rename[text], unicode(text))
|
||||
for item in self.to_delete:
|
||||
self.db.delete_tag(unicode(item.text()))
|
||||
QDialog.accept(self)
|
||||
rename_func = None
|
||||
if self.category == 'tags':
|
||||
rename_func = self.db.rename_tag
|
||||
delete_func = self.db.delete_tag_using_id
|
||||
elif self.category == 'series':
|
||||
rename_func = self.db.rename_series
|
||||
delete_func = self.db.delete_series_using_id
|
||||
elif self.category == 'publisher':
|
||||
rename_func = self.db.rename_publisher
|
||||
delete_func = self.db.delete_publisher_using_id
|
||||
else:
|
||||
rename_func = partial(self.db.rename_custom_item, label=self.cc_label)
|
||||
delete_func = partial(self.db.delete_custom_item_using_id, label=self.cc_label)
|
||||
|
||||
work_done = False
|
||||
if rename_func:
|
||||
for text in self.to_rename:
|
||||
work_done = True
|
||||
rename_func(id=self.to_rename[text], new_name=unicode(text))
|
||||
for item in self.to_delete:
|
||||
work_done = True
|
||||
delete_func(item)
|
||||
if not work_done:
|
||||
QDialog.reject(self)
|
||||
else:
|
||||
QDialog.accept(self)
|
||||
|
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tag Editor</string>
|
||||
<string>Category Editor</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
@ -25,7 +25,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Tags in use</string>
|
||||
<string>Items in use</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>available_tags</cstring>
|
||||
@ -54,7 +54,7 @@
|
||||
<item>
|
||||
<widget class="QToolButton" name="delete_button">
|
||||
<property name="toolTip">
|
||||
<string>Delete tag from database. This will unapply the tag from all books and then remove it from the database.</string>
|
||||
<string>Delete item from database. This will unapply the item from all books and then remove it from the database.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
@ -74,7 +74,7 @@
|
||||
<item>
|
||||
<widget class="QToolButton" name="rename_button">
|
||||
<property name="toolTip">
|
||||
<string>Rename the tag everywhere it is used.</string>
|
||||
<string>Rename the item in every book where it is used.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
|
@ -17,15 +17,18 @@ from calibre.gui2 import config, NONE
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.library.field_metadata import TagsIcons
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
from calibre.gui2 import error_dialog
|
||||
|
||||
class TagsView(QTreeView): # {{{
|
||||
|
||||
need_refresh = pyqtSignal()
|
||||
refresh_required = pyqtSignal()
|
||||
restriction_set = pyqtSignal(object)
|
||||
tags_marked = pyqtSignal(object, object)
|
||||
user_category_edit = pyqtSignal(object)
|
||||
tag_list_edit = pyqtSignal(object)
|
||||
tag_list_edit = pyqtSignal(object, object)
|
||||
saved_search_edit = pyqtSignal(object)
|
||||
tag_item_renamed = pyqtSignal()
|
||||
search_item_renamed = pyqtSignal()
|
||||
|
||||
def __init__(self, *args):
|
||||
QTreeView.__init__(self, *args)
|
||||
@ -36,7 +39,8 @@ class TagsView(QTreeView): # {{{
|
||||
|
||||
def set_database(self, db, tag_match, popularity, restriction):
|
||||
self.hidden_categories = config['tag_browser_hidden_categories']
|
||||
self._model = TagsModel(db, parent=self, hidden_categories=self.hidden_categories)
|
||||
self._model = TagsModel(db, parent=self,
|
||||
hidden_categories=self.hidden_categories)
|
||||
self.popularity = popularity
|
||||
self.restriction = restriction
|
||||
self.tag_match = tag_match
|
||||
@ -48,12 +52,12 @@ class TagsView(QTreeView): # {{{
|
||||
self.popularity.setChecked(config['sort_by_popularity'])
|
||||
self.popularity.stateChanged.connect(self.sort_changed)
|
||||
self.restriction.activated[str].connect(self.search_restriction_set)
|
||||
self.need_refresh.connect(self.recount, type=Qt.QueuedConnection)
|
||||
self.refresh_required.connect(self.recount, type=Qt.QueuedConnection)
|
||||
db.add_listener(self.database_changed)
|
||||
self.saved_searches_changed(recount=False)
|
||||
|
||||
def database_changed(self, event, ids):
|
||||
self.need_refresh.emit()
|
||||
self.refresh_required.emit()
|
||||
|
||||
@property
|
||||
def match_all(self):
|
||||
@ -80,18 +84,26 @@ class TagsView(QTreeView): # {{{
|
||||
if event.button() == Qt.LeftButton:
|
||||
QTreeView.mouseReleaseEvent(self, event)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
# swallow these to avoid toggling and editing at the same time
|
||||
pass
|
||||
|
||||
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)
|
||||
|
||||
def context_menu_handler(self, action=None, category=None):
|
||||
def context_menu_handler(self, action=None, category=None,
|
||||
key=None, index=None):
|
||||
if not action:
|
||||
return
|
||||
try:
|
||||
if action == 'manage_tags':
|
||||
self.tag_list_edit.emit(category)
|
||||
if action == 'edit_item':
|
||||
self.edit(index)
|
||||
return
|
||||
if action == 'open_editor':
|
||||
self.tag_list_edit.emit(category, key)
|
||||
return
|
||||
if action == 'manage_categories':
|
||||
self.user_category_edit.emit(category)
|
||||
@ -117,29 +129,51 @@ class TagsView(QTreeView): # {{{
|
||||
item = index.internalPointer()
|
||||
tag_name = ''
|
||||
if item.type == TagTreeItem.TAG:
|
||||
tag_item = item
|
||||
tag_name = item.tag.name
|
||||
item = item.parent
|
||||
if item.type == TagTreeItem.CATEGORY:
|
||||
category = unicode(item.name.toString())
|
||||
self.context_menu = QMenu(self)
|
||||
self.context_menu.addAction(_('Hide %s') % category,
|
||||
partial(self.context_menu_handler, action='hide', category=category))
|
||||
key = item.category_key
|
||||
# Verify that we are working with a field that we know something about
|
||||
if key not in self.db.field_metadata:
|
||||
return True
|
||||
|
||||
if self.hidden_categories:
|
||||
self.context_menu = QMenu(self)
|
||||
# If the user right-clicked on an editable item, then offer
|
||||
# the possibility of renaming that item
|
||||
if tag_name and \
|
||||
(key in ['authors', 'tags', 'series', 'publisher', 'search'] or \
|
||||
self.db.field_metadata[key]['is_custom']):
|
||||
self.context_menu.addAction(_('Rename') + " '" + tag_name + "'",
|
||||
partial(self.context_menu_handler, action='edit_item',
|
||||
category=tag_item, index=index))
|
||||
self.context_menu.addSeparator()
|
||||
# Hide/Show/Restore categories
|
||||
self.context_menu.addAction(_('Hide category %s') % category,
|
||||
partial(self.context_menu_handler, action='hide', category=category))
|
||||
if self.hidden_categories:
|
||||
m = self.context_menu.addMenu(_('Show category'))
|
||||
for col in self.hidden_categories:
|
||||
for col in sorted(self.hidden_categories, cmp=lambda x,y: cmp(x.lower(), y.lower())):
|
||||
m.addAction(col,
|
||||
partial(self.context_menu_handler, action='show', category=col))
|
||||
self.context_menu.addSeparator()
|
||||
self.context_menu.addAction(_('Restore defaults'),
|
||||
self.context_menu.addAction(_('Show all categories'),
|
||||
partial(self.context_menu_handler, action='defaults'))
|
||||
|
||||
# Offer specific editors for tags/series/publishers/saved searches
|
||||
self.context_menu.addSeparator()
|
||||
self.context_menu.addAction(_('Manage Tags'),
|
||||
partial(self.context_menu_handler, action='manage_tags',
|
||||
if key in ['tags', 'publisher', 'series'] or \
|
||||
self.db.field_metadata[key]['is_custom']:
|
||||
self.context_menu.addAction(_('Manage ') + category,
|
||||
partial(self.context_menu_handler, action='open_editor',
|
||||
category=tag_name, key=key))
|
||||
elif key == 'search':
|
||||
self.context_menu.addAction(_('Manage Saved Searches'),
|
||||
partial(self.context_menu_handler, action='manage_searches',
|
||||
category=tag_name))
|
||||
|
||||
# Always show the user categories editor
|
||||
self.context_menu.addSeparator()
|
||||
if category in prefs['user_categories'].keys():
|
||||
self.context_menu.addAction(_('Manage User Categories'),
|
||||
partial(self.context_menu_handler, action='manage_categories',
|
||||
@ -149,10 +183,6 @@ class TagsView(QTreeView): # {{{
|
||||
partial(self.context_menu_handler, action='manage_categories',
|
||||
category=None))
|
||||
|
||||
self.context_menu.addAction(_('Manage Saved Searches'),
|
||||
partial(self.context_menu_handler, action='manage_searches',
|
||||
category=tag_name))
|
||||
|
||||
self.context_menu.popup(self.mapToGlobal(point))
|
||||
return True
|
||||
|
||||
@ -203,7 +233,8 @@ class TagTreeItem(object): # {{{
|
||||
TAG = 1
|
||||
ROOT = 2
|
||||
|
||||
def __init__(self, data=None, category_icon=None, icon_map=None, parent=None, tooltip=None):
|
||||
def __init__(self, data=None, category_icon=None, icon_map=None,
|
||||
parent=None, tooltip=None, category_key=None):
|
||||
self.parent = parent
|
||||
self.children = []
|
||||
if self.parent is not None:
|
||||
@ -218,6 +249,7 @@ class TagTreeItem(object): # {{{
|
||||
self.bold_font = QFont()
|
||||
self.bold_font.setBold(True)
|
||||
self.bold_font = QVariant(self.bold_font)
|
||||
self.category_key = category_key
|
||||
elif self.type == self.TAG:
|
||||
icon_map[0] = data.icon
|
||||
self.tag, self.icon_state_map = data, list(map(QVariant, icon_map))
|
||||
@ -263,6 +295,8 @@ class TagTreeItem(object): # {{{
|
||||
return QVariant('%s'%(self.tag.name))
|
||||
else:
|
||||
return QVariant('[%d] %s'%(self.tag.count, self.tag.name))
|
||||
if role == Qt.EditRole:
|
||||
return QVariant(self.tag.name)
|
||||
if role == Qt.DecorationRole:
|
||||
return self.icon_state_map[self.tag.state]
|
||||
if role == Qt.ToolTipRole and self.tag.tooltip is not None:
|
||||
@ -277,7 +311,7 @@ class TagTreeItem(object): # {{{
|
||||
|
||||
class TagsModel(QAbstractItemModel): # {{{
|
||||
|
||||
def __init__(self, db, parent=None, hidden_categories=None):
|
||||
def __init__(self, db, parent, hidden_categories=None):
|
||||
QAbstractItemModel.__init__(self, parent)
|
||||
|
||||
# must do this here because 'QPixmap: Must construct a QApplication
|
||||
@ -297,6 +331,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
|
||||
self.icon_state_map = [None, QIcon(I('plus.svg')), QIcon(I('minus.svg'))]
|
||||
self.db = db
|
||||
self.tags_view = parent
|
||||
self.hidden_categories = hidden_categories
|
||||
self.search_restriction = ''
|
||||
self.ignore_next_search = 0
|
||||
@ -324,7 +359,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
c = TagTreeItem(parent=self.root_item,
|
||||
data=self.categories[i],
|
||||
category_icon=self.category_icon_map[r],
|
||||
tooltip=tt)
|
||||
tooltip=tt, category_key=r)
|
||||
for tag in data[r]:
|
||||
TagTreeItem(parent=c, data=tag, icon_map=self.icon_state_map)
|
||||
|
||||
@ -342,8 +377,12 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
data = self.db.get_categories(sort_on_count=sort, icon_map=self.category_icon_map)
|
||||
|
||||
tb_categories = self.db.field_metadata
|
||||
self.category_items = {}
|
||||
for category in tb_categories:
|
||||
if category in data: # They should always be there, but ...
|
||||
# make a map of sets of names per category for duplicate
|
||||
# checking when editing
|
||||
self.category_items[category] = set([tag.name for tag in data[category]])
|
||||
self.row_map.append(category)
|
||||
self.categories.append(tb_categories[category]['name'])
|
||||
|
||||
@ -382,11 +421,52 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
item = index.internalPointer()
|
||||
return item.data(role)
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
if not index.isValid():
|
||||
return NONE
|
||||
val = unicode(value.toString())
|
||||
if not val:
|
||||
error_dialog(self.tags_view, _('Item is blank'),
|
||||
_('An item cannot be set to nothing. Delete it instead.')).exec_()
|
||||
return False
|
||||
item = index.internalPointer()
|
||||
key = item.parent.category_key
|
||||
# make certain we know about the category
|
||||
if key not in self.db.field_metadata:
|
||||
return
|
||||
if val in self.category_items[key]:
|
||||
error_dialog(self.tags_view, 'Duplicate item',
|
||||
_('The name %s is already used.')%val).exec_()
|
||||
return False
|
||||
oldval = item.tag.name
|
||||
if key == 'search':
|
||||
saved_searches.rename(unicode(item.data(role).toString()), val)
|
||||
self.tags_view.search_item_renamed.emit()
|
||||
else:
|
||||
if key == 'series':
|
||||
self.db.rename_series(item.tag.id, val)
|
||||
elif key == 'publisher':
|
||||
self.db.rename_publisher(item.tag.id, val)
|
||||
elif key == 'tags':
|
||||
self.db.rename_tag(item.tag.id, val)
|
||||
elif key == 'authors':
|
||||
self.db.rename_author(item.tag.id, val)
|
||||
elif self.db.field_metadata[key]['is_custom']:
|
||||
self.db.rename_custom_item(item.tag.id, val,
|
||||
label=self.db.field_metadata[key]['label'])
|
||||
self.tags_view.tag_item_renamed.emit()
|
||||
item.tag.name = val
|
||||
self.dataChanged.emit(index, index)
|
||||
# replace the old value in the duplicate detection map with the new one
|
||||
self.category_items[key].discard(oldval)
|
||||
self.category_items[key].add(val)
|
||||
return True
|
||||
|
||||
def headerData(self, *args):
|
||||
return NONE
|
||||
|
||||
def flags(self, *args):
|
||||
return Qt.ItemIsEnabled|Qt.ItemIsSelectable
|
||||
return Qt.ItemIsEnabled|Qt.ItemIsSelectable|Qt.ItemIsEditable
|
||||
|
||||
def path_for_index(self, index):
|
||||
ans = []
|
||||
|
@ -553,6 +553,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
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)
|
||||
self.tags_view.tag_item_renamed.connect(self.do_tag_item_renamed)
|
||||
self.tags_view.search_item_renamed.connect(self.saved_search.clear_to_help)
|
||||
self.search.search.connect(self.tags_view.model().reinit)
|
||||
for x in (self.location_view.count_changed, self.tags_view.recount,
|
||||
self.restriction_count_changed):
|
||||
@ -660,13 +662,22 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.tags_view.set_new_model()
|
||||
self.tags_view.recount()
|
||||
|
||||
def do_tags_list_edit(self, tag):
|
||||
d = TagListEditor(self, self.library_view.model().db, tag)
|
||||
def do_tags_list_edit(self, tag, category):
|
||||
d = TagListEditor(self, self.library_view.model().db, tag, category)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
# Clean up everything, as information could have changed for many books.
|
||||
self.library_view.model().refresh()
|
||||
self.tags_view.set_new_model()
|
||||
self.tags_view.recount()
|
||||
self.saved_search.clear_to_help()
|
||||
self.search.clear_to_help()
|
||||
|
||||
def do_tag_item_renamed(self):
|
||||
# Clean up library view and search
|
||||
self.library_view.model().refresh()
|
||||
self.saved_search.clear_to_help()
|
||||
self.search.clear_to_help()
|
||||
|
||||
def do_saved_search_edit(self, search):
|
||||
d = SavedSearchEditor(self, search)
|
||||
|
@ -171,6 +171,40 @@ class CustomColumns(object):
|
||||
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
|
||||
return ans
|
||||
|
||||
# convenience methods for tag editing
|
||||
def get_custom_items_with_ids(self, label=None, num=None):
|
||||
if label is not None:
|
||||
data = self.custom_column_label_map[label]
|
||||
if num is not None:
|
||||
data = self.custom_column_num_map[num]
|
||||
table,lt = self.custom_table_names(data['num'])
|
||||
if not data['normalized']:
|
||||
return []
|
||||
ans = self.conn.get('SELECT id, value FROM %s'%table)
|
||||
return ans
|
||||
|
||||
def rename_custom_item(self, id, new_name, label=None, num=None):
|
||||
if id:
|
||||
if label is not None:
|
||||
data = self.custom_column_label_map[label]
|
||||
if num is not None:
|
||||
data = self.custom_column_num_map[num]
|
||||
table,lt = self.custom_table_names(data['num'])
|
||||
self.conn.execute('UPDATE %s SET value=? WHERE id=?'%table, (new_name, id))
|
||||
self.conn.commit()
|
||||
|
||||
def delete_custom_item_using_id(self, id, label=None, num=None):
|
||||
if id:
|
||||
if label is not None:
|
||||
data = self.custom_column_label_map[label]
|
||||
if num is not None:
|
||||
data = self.custom_column_num_map[num]
|
||||
table,lt = self.custom_table_names(data['num'])
|
||||
self.conn.execute('DELETE FROM %s WHERE value=?'%lt, (id,))
|
||||
self.conn.execute('DELETE FROM %s WHERE id=?'%table, (id,))
|
||||
self.conn.commit()
|
||||
# end convenience methods
|
||||
|
||||
def all_custom(self, label=None, num=None):
|
||||
if label is not None:
|
||||
data = self.custom_column_label_map[label]
|
||||
|
@ -731,8 +731,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
icon=icon, tooltip = tooltip)
|
||||
for r in data if item_not_zero_func(r)]
|
||||
if category == 'series':
|
||||
categories[category].sort(cmp=lambda x,y:cmp(title_sort(x.name),
|
||||
title_sort(y.name)))
|
||||
categories[category].sort(cmp=lambda x,y:cmp(title_sort(x.name).lower(),
|
||||
title_sort(y.name).lower()))
|
||||
|
||||
# We delayed computing the standard formats category because it does not
|
||||
# use a view, but is computed dynamically
|
||||
@ -985,20 +985,92 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
if notify:
|
||||
self.notify('metadata', [id])
|
||||
|
||||
# Convenience method for tags_list_editor
|
||||
# Convenience methods for tags_list_editor
|
||||
# Note: we generally do not need to refresh_ids because library_view will
|
||||
# refresh everything.
|
||||
def get_tags_with_ids(self):
|
||||
result = self.conn.get('SELECT * FROM tags')
|
||||
result = self.conn.get('SELECT id,name FROM tags')
|
||||
if not result:
|
||||
return {}
|
||||
r = []
|
||||
for k,v in result:
|
||||
r.append((k,v))
|
||||
return r
|
||||
return []
|
||||
return result
|
||||
|
||||
def rename_tag(self, id, new):
|
||||
self.conn.execute('UPDATE tags SET name=? WHERE id=?', (new, id))
|
||||
def rename_tag(self, id, new_name):
|
||||
if id:
|
||||
self.conn.execute('UPDATE tags SET name=? WHERE id=?', (new_name, id))
|
||||
self.conn.commit()
|
||||
|
||||
def delete_tag_using_id(self, id):
|
||||
if id:
|
||||
self.conn.execute('DELETE FROM books_tags_link WHERE tag=?', (id,))
|
||||
self.conn.execute('DELETE FROM tags WHERE id=?', (id,))
|
||||
self.conn.commit()
|
||||
|
||||
def get_series_with_ids(self):
|
||||
result = self.conn.get('SELECT id,name FROM series')
|
||||
if not result:
|
||||
return []
|
||||
return result
|
||||
|
||||
def rename_series(self, id, new_name):
|
||||
if id:
|
||||
self.conn.execute('UPDATE series SET name=? WHERE id=?', (new_name, id))
|
||||
self.conn.commit()
|
||||
|
||||
def delete_series_using_id(self, id):
|
||||
if id:
|
||||
books = self.conn.get('SELECT book from books_series_link WHERE series=?', (id,))
|
||||
self.conn.execute('DELETE FROM books_series_link WHERE series=?', (id,))
|
||||
self.conn.execute('DELETE FROM series WHERE id=?', (id,))
|
||||
self.conn.commit()
|
||||
for (book_id,) in books:
|
||||
self.conn.execute('UPDATE books SET series_index=1.0 WHERE id=?', (book_id,))
|
||||
|
||||
def get_publishers_with_ids(self):
|
||||
result = self.conn.get('SELECT id,name FROM publishers')
|
||||
if not result:
|
||||
return []
|
||||
return result
|
||||
|
||||
def rename_publisher(self, id, new_name):
|
||||
if id:
|
||||
self.conn.execute('UPDATE publishers SET name=? WHERE id=?', (new_name, id))
|
||||
self.conn.commit()
|
||||
|
||||
def delete_publisher_using_id(self, id):
|
||||
if id:
|
||||
self.conn.execute('DELETE FROM books_publishers_link WHERE publisher=?', (id,))
|
||||
self.conn.execute('DELETE FROM publishers WHERE id=?', (id,))
|
||||
self.conn.commit()
|
||||
|
||||
# There is no editor for author, so we do not need get_authors_with_ids or
|
||||
# delete_author_using_id.
|
||||
def rename_author(self, id, new_name):
|
||||
if id:
|
||||
# Make sure that any commas in new_name are changed to '|'!
|
||||
new_name = new_name.replace(',', '|')
|
||||
self.conn.execute('UPDATE authors SET name=? WHERE id=?', (new_name, id))
|
||||
self.conn.commit()
|
||||
# now must fix up the books
|
||||
books = self.conn.get('SELECT book from books_authors_link WHERE author=?', (id,))
|
||||
for (book_id,) in books:
|
||||
# First, must refresh the cache to see the new authors
|
||||
self.data.refresh_ids(self, [book_id])
|
||||
# now fix the filesystem paths
|
||||
self.set_path(book_id, index_is_id=True)
|
||||
# Next fix the author sort. Reset it to the default
|
||||
authors = self.conn.get('''
|
||||
SELECT authors.name
|
||||
FROM authors, books_authors_link as bl
|
||||
WHERE bl.book = ? and bl.author = authors.id
|
||||
''' , (book_id,))
|
||||
# unpack the double-list structure
|
||||
for i,aut in enumerate(authors):
|
||||
authors[i] = aut[0]
|
||||
ss = authors_to_sort_string(authors)
|
||||
self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', (ss, id))
|
||||
|
||||
# end convenience methods
|
||||
|
||||
def get_tags(self, id):
|
||||
result = self.conn.get(
|
||||
'SELECT name FROM tags WHERE id IN (SELECT tag FROM books_tags_link WHERE book=?)',
|
||||
@ -1083,7 +1155,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.conn.execute('DELETE FROM tags WHERE id=?', (id,))
|
||||
self.conn.commit()
|
||||
|
||||
|
||||
def set_series(self, id, series, notify=True):
|
||||
self.conn.execute('DELETE FROM books_series_link WHERE book=?',(id,))
|
||||
self.conn.execute('DELETE FROM series WHERE (SELECT COUNT(id) FROM books_series_link WHERE series=series.id) < 1')
|
||||
|
@ -52,6 +52,12 @@ class SavedSearchQueries(object):
|
||||
self.queries.pop(self.force_unicode(name), False)
|
||||
prefs[self.opt_name] = self.queries
|
||||
|
||||
def rename(self, old_name, new_name):
|
||||
self.queries[self.force_unicode(new_name)] = \
|
||||
self.queries.get(self.force_unicode(old_name), None)
|
||||
self.queries.pop(self.force_unicode(old_name), False)
|
||||
prefs[self.opt_name] = self.queries
|
||||
|
||||
def names(self):
|
||||
return sorted(self.queries.keys(),
|
||||
cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
|
Loading…
x
Reference in New Issue
Block a user