mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
1) Add 'managers' for series and publisher.
2) Option for manager now appears only if the right-click is on/in that category 3) Avoid the cleanup/repaints if no work was done
This commit is contained in:
parent
5a9d7a0425
commit
55c0f6f289
@ -6,12 +6,11 @@ from PyQt4.QtGui import QDialog, QListWidgetItem
|
|||||||
from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor
|
from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor
|
||||||
from calibre.gui2 import question_dialog, error_dialog
|
from calibre.gui2 import question_dialog, error_dialog
|
||||||
|
|
||||||
|
from calibre.ebooks.metadata import title_sort
|
||||||
|
|
||||||
class TagListEditor(QDialog, Ui_TagListEditor):
|
class TagListEditor(QDialog, Ui_TagListEditor):
|
||||||
|
|
||||||
def tag_cmp(self, x, y):
|
def __init__(self, window, db, tag_to_match, category):
|
||||||
return cmp(x.lower(), y.lower())
|
|
||||||
|
|
||||||
def __init__(self, window, db, tag_to_match):
|
|
||||||
QDialog.__init__(self, window)
|
QDialog.__init__(self, window)
|
||||||
Ui_TagListEditor.__init__(self)
|
Ui_TagListEditor.__init__(self)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -20,9 +19,20 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
|||||||
self.to_delete = []
|
self.to_delete = []
|
||||||
self.db = db
|
self.db = db
|
||||||
self.all_tags = {}
|
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 == 'publishers':
|
||||||
|
result = db.get_publishers_with_ids()
|
||||||
|
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||||
|
|
||||||
|
for k,v in result:
|
||||||
self.all_tags[v] = k
|
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 = QListWidgetItem(tag)
|
||||||
item.setData(Qt.UserRole, self.all_tags[tag])
|
item.setData(Qt.UserRole, self.all_tags[tag])
|
||||||
self.available_tags.addItem(item)
|
self.available_tags.addItem(item)
|
||||||
@ -38,17 +48,17 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
|||||||
|
|
||||||
def finish_editing(self, item):
|
def finish_editing(self, item):
|
||||||
if not item.text():
|
if not item.text():
|
||||||
error_dialog(self, 'Tag is blank',
|
error_dialog(self, 'Item is blank',
|
||||||
'A tag cannot be set to nothing. Delete it instead.'%(item.text())).exec_()
|
'An item cannot be set to nothing. Delete it instead.'%(item.text())).exec_()
|
||||||
item.setText(self.item_before_editing.text())
|
item.setText(self.item_before_editing.text())
|
||||||
return
|
return
|
||||||
if item.text() != self.item_before_editing.text():
|
if item.text() != self.item_before_editing.text():
|
||||||
if item.text() in self.all_tags.keys() or item.text() in self.to_rename.keys():
|
if item.text() in self.all_tags.keys() or item.text() in self.to_rename.keys():
|
||||||
error_dialog(self, 'Tag already used',
|
error_dialog(self, 'Item already used',
|
||||||
'The tag %s is already used.'%(item.text())).exec_()
|
'The item %s is already used.'%(item.text())).exec_()
|
||||||
item.setText(self.item_before_editing.text())
|
item.setText(self.item_before_editing.text())
|
||||||
return
|
return
|
||||||
(id,_) = self.item_before_editing.data(Qt.UserRole).toInt()
|
(id,ign) = self.item_before_editing.data(Qt.UserRole).toInt()
|
||||||
self.to_rename[item.text()] = id
|
self.to_rename[item.text()] = id
|
||||||
|
|
||||||
def rename_tag(self):
|
def rename_tag(self):
|
||||||
@ -57,39 +67,49 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
|||||||
|
|
||||||
def _rename_tag(self, item):
|
def _rename_tag(self, item):
|
||||||
if item is None:
|
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
|
return
|
||||||
self.item_before_editing = item.clone()
|
self.item_before_editing = item.clone()
|
||||||
item.setFlags (item.flags() | Qt.ItemIsEditable);
|
item.setFlags (item.flags() | Qt.ItemIsEditable);
|
||||||
self.available_tags.editItem(item)
|
self.available_tags.editItem(item)
|
||||||
|
|
||||||
def delete_tags(self, item=None):
|
def delete_tags(self, item=None):
|
||||||
confirms, deletes = [], []
|
deletes = self.available_tags.selectedItems() if item is None else [item]
|
||||||
items = self.available_tags.selectedItems() if item is None else [item]
|
if not deletes:
|
||||||
if not items:
|
error_dialog(self, 'No items selected', 'You must select at least one items from the list.').exec_()
|
||||||
error_dialog(self, 'No tags selected', 'You must select at least one tag from the list of Available tags.').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
|
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:
|
for item in deletes:
|
||||||
(id,_) = item.data(Qt.UserRole).toInt()
|
(id,ign) = item.data(Qt.UserRole).toInt()
|
||||||
self.to_delete.append(id)
|
self.to_delete.append(id)
|
||||||
self.available_tags.takeItem(self.available_tags.row(item))
|
self.available_tags.takeItem(self.available_tags.row(item))
|
||||||
|
|
||||||
def accept(self):
|
def 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 == 'publishers':
|
||||||
|
rename_func = self.db.rename_publisher
|
||||||
|
delete_func = self.db.delete_publisher_using_id
|
||||||
|
|
||||||
|
work_done = False
|
||||||
|
if rename_func:
|
||||||
for text in self.to_rename:
|
for text in self.to_rename:
|
||||||
self.db.rename_tag(id=self.to_rename[text], new_name=unicode(text))
|
work_done = True
|
||||||
|
rename_func(id=self.to_rename[text], new_name=unicode(text))
|
||||||
for item in self.to_delete:
|
for item in self.to_delete:
|
||||||
self.db.delete_tag_using_id(item)
|
work_done = True
|
||||||
|
delete_func(item)
|
||||||
|
if not work_done:
|
||||||
|
QDialog.reject(self)
|
||||||
|
else:
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Tag Editor</string>
|
<string>Category Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset>
|
<iconset>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Tags in use</string>
|
<string>Items in use</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>available_tags</cstring>
|
<cstring>available_tags</cstring>
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="delete_button">
|
<widget class="QToolButton" name="delete_button">
|
||||||
<property name="toolTip">
|
<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>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
@ -74,7 +74,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="rename_button">
|
<widget class="QToolButton" name="rename_button">
|
||||||
<property name="toolTip">
|
<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>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
|
@ -24,7 +24,7 @@ class TagsView(QTreeView): # {{{
|
|||||||
restriction_set = pyqtSignal(object)
|
restriction_set = pyqtSignal(object)
|
||||||
tags_marked = pyqtSignal(object, object)
|
tags_marked = pyqtSignal(object, object)
|
||||||
user_category_edit = pyqtSignal(object)
|
user_category_edit = pyqtSignal(object)
|
||||||
tag_list_edit = pyqtSignal(object)
|
tag_list_edit = pyqtSignal(object, object)
|
||||||
saved_search_edit = pyqtSignal(object)
|
saved_search_edit = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
@ -91,7 +91,13 @@ class TagsView(QTreeView): # {{{
|
|||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
if action == 'manage_tags':
|
if action == 'manage_tags':
|
||||||
self.tag_list_edit.emit(category)
|
self.tag_list_edit.emit(category, 'tags')
|
||||||
|
return
|
||||||
|
if action == 'manage_series':
|
||||||
|
self.tag_list_edit.emit(category, 'series')
|
||||||
|
return
|
||||||
|
if action == 'manage_publishers':
|
||||||
|
self.tag_list_edit.emit(category, 'publishers')
|
||||||
return
|
return
|
||||||
if action == 'manage_categories':
|
if action == 'manage_categories':
|
||||||
self.user_category_edit.emit(category)
|
self.user_category_edit.emit(category)
|
||||||
@ -136,10 +142,24 @@ class TagsView(QTreeView): # {{{
|
|||||||
partial(self.context_menu_handler, action='defaults'))
|
partial(self.context_menu_handler, action='defaults'))
|
||||||
|
|
||||||
self.context_menu.addSeparator()
|
self.context_menu.addSeparator()
|
||||||
|
if category == _('Tags'):
|
||||||
self.context_menu.addAction(_('Manage Tags'),
|
self.context_menu.addAction(_('Manage Tags'),
|
||||||
partial(self.context_menu_handler, action='manage_tags',
|
partial(self.context_menu_handler, action='manage_tags',
|
||||||
category=tag_name))
|
category=tag_name))
|
||||||
|
elif category == _('Searches'):
|
||||||
|
self.context_menu.addAction(_('Manage Saved Searches'),
|
||||||
|
partial(self.context_menu_handler, action='manage_searches',
|
||||||
|
category=tag_name))
|
||||||
|
elif category == _('Publishers'):
|
||||||
|
self.context_menu.addAction(_('Manage Publishers'),
|
||||||
|
partial(self.context_menu_handler, action='manage_publishers',
|
||||||
|
category=tag_name))
|
||||||
|
elif category == _('Series'):
|
||||||
|
self.context_menu.addAction(_('Manage Series'),
|
||||||
|
partial(self.context_menu_handler, action='manage_series',
|
||||||
|
category=tag_name))
|
||||||
|
|
||||||
|
self.context_menu.addSeparator()
|
||||||
if category in prefs['user_categories'].keys():
|
if category in prefs['user_categories'].keys():
|
||||||
self.context_menu.addAction(_('Manage User Categories'),
|
self.context_menu.addAction(_('Manage User Categories'),
|
||||||
partial(self.context_menu_handler, action='manage_categories',
|
partial(self.context_menu_handler, action='manage_categories',
|
||||||
@ -149,10 +169,6 @@ class TagsView(QTreeView): # {{{
|
|||||||
partial(self.context_menu_handler, action='manage_categories',
|
partial(self.context_menu_handler, action='manage_categories',
|
||||||
category=None))
|
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))
|
self.context_menu.popup(self.mapToGlobal(point))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -660,13 +660,16 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.tags_view.set_new_model()
|
self.tags_view.set_new_model()
|
||||||
self.tags_view.recount()
|
self.tags_view.recount()
|
||||||
|
|
||||||
def do_tags_list_edit(self, tag):
|
def do_tags_list_edit(self, tag, category):
|
||||||
d = TagListEditor(self, self.library_view.model().db, tag)
|
d = TagListEditor(self, self.library_view.model().db, tag, category)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
if d.result() == d.Accepted:
|
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.set_new_model()
|
||||||
self.tags_view.recount()
|
self.tags_view.recount()
|
||||||
self.library_view.model().refresh()
|
self.saved_search.clear_to_help()
|
||||||
|
self.search.clear_to_help()
|
||||||
|
|
||||||
def do_saved_search_edit(self, search):
|
def do_saved_search_edit(self, search):
|
||||||
d = SavedSearchEditor(self, search)
|
d = SavedSearchEditor(self, search)
|
||||||
|
@ -731,8 +731,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
icon=icon, tooltip = tooltip)
|
icon=icon, tooltip = tooltip)
|
||||||
for r in data if item_not_zero_func(r)]
|
for r in data if item_not_zero_func(r)]
|
||||||
if category == 'series':
|
if category == 'series':
|
||||||
categories[category].sort(cmp=lambda x,y:cmp(title_sort(x.name),
|
categories[category].sort(cmp=lambda x,y:cmp(title_sort(x.name).lower(),
|
||||||
title_sort(y.name)))
|
title_sort(y.name).lower()))
|
||||||
|
|
||||||
# We delayed computing the standard formats category because it does not
|
# We delayed computing the standard formats category because it does not
|
||||||
# use a view, but is computed dynamically
|
# use a view, but is computed dynamically
|
||||||
@ -985,7 +985,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
if notify:
|
if notify:
|
||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
# Convenience method for tags_list_editor
|
# Convenience methods for tags_list_editor
|
||||||
def get_tags_with_ids(self):
|
def get_tags_with_ids(self):
|
||||||
result = self.conn.get('SELECT id,name FROM tags')
|
result = self.conn.get('SELECT id,name FROM tags')
|
||||||
if not result:
|
if not result:
|
||||||
@ -996,6 +996,49 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.conn.execute('UPDATE tags SET name=? WHERE id=?', (new_name, id))
|
self.conn.execute('UPDATE tags SET name=? WHERE id=?', (new_name, id))
|
||||||
self.conn.commit()
|
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):
|
||||||
|
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,))
|
||||||
|
for (book_id,) in books:
|
||||||
|
self.conn.execute('UPDATE books SET series_index=1.0 WHERE id=?', (book_id,))
|
||||||
|
self.conn.execute('DELETE FROM books_series_link WHERE series=?', (id,))
|
||||||
|
self.conn.execute('DELETE FROM series WHERE id=?', (id,))
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
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):
|
||||||
|
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()
|
||||||
|
|
||||||
|
# end convenience methods
|
||||||
|
|
||||||
def get_tags(self, id):
|
def get_tags(self, id):
|
||||||
result = self.conn.get(
|
result = self.conn.get(
|
||||||
'SELECT name FROM tags WHERE id IN (SELECT tag FROM books_tags_link WHERE book=?)',
|
'SELECT name FROM tags WHERE id IN (SELECT tag FROM books_tags_link WHERE book=?)',
|
||||||
@ -1080,12 +1123,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.conn.execute('DELETE FROM tags WHERE id=?', (id,))
|
self.conn.execute('DELETE FROM tags WHERE id=?', (id,))
|
||||||
self.conn.commit()
|
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 set_series(self, id, series, notify=True):
|
def set_series(self, id, series, notify=True):
|
||||||
self.conn.execute('DELETE FROM books_series_link WHERE book=?',(id,))
|
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')
|
self.conn.execute('DELETE FROM series WHERE (SELECT COUNT(id) FROM books_series_link WHERE series=series.id) < 1')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user