diff --git a/src/libprs500/gui2/dialogs/fetch_metadata.py b/src/libprs500/gui2/dialogs/fetch_metadata.py
index 3e6be81679..97dd6f8e65 100644
--- a/src/libprs500/gui2/dialogs/fetch_metadata.py
+++ b/src/libprs500/gui2/dialogs/fetch_metadata.py
@@ -142,7 +142,6 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
def selected_book(self):
try:
- print self.matches.currentIndex().row()
return self.matches.model().matches[self.matches.currentIndex().row()]
except:
return None
diff --git a/src/libprs500/gui2/dialogs/metadata_single.py b/src/libprs500/gui2/dialogs/metadata_single.py
index 1b8635047a..c9d7cd88b7 100644
--- a/src/libprs500/gui2/dialogs/metadata_single.py
+++ b/src/libprs500/gui2/dialogs/metadata_single.py
@@ -26,6 +26,7 @@ from libprs500.gui2 import qstring_to_unicode, error_dialog, file_icon_provider,
choose_files, pixmap_to_data, choose_images
from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
from libprs500.gui2.dialogs.fetch_metadata import FetchMetadata
+from libprs500.gui2.dialogs.tag_editor import TagEditor
from libprs500.ebooks.BeautifulSoup import BeautifulSoup
from libprs500.ebooks import BOOK_EXTENSIONS
@@ -145,6 +146,8 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
QObject.connect(self.fetch_cover_button, SIGNAL('clicked()'),
self.fetch_cover)
+ QObject.connect(self.tag_editor_button, SIGNAL('clicked()'),
+ self.edit_tags)
self.title.setText(db.title(row))
isbn = db.isbn(self.id)
@@ -202,6 +205,13 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.exec_()
+ def edit_tags(self):
+ d = TagEditor(self, self.db, self.row)
+ d.exec_()
+ if d.result() == QDialog.Accepted:
+ tag_string = ', '.join(d.tags)
+ self.tags.setText(tag_string)
+
def fetch_cover(self):
isbn = qstring_to_unicode(self.isbn.text())
if isbn:
diff --git a/src/libprs500/gui2/dialogs/metadata_single.ui b/src/libprs500/gui2/dialogs/metadata_single.ui
index e696b7ad73..af91a3a8bc 100644
--- a/src/libprs500/gui2/dialogs/metadata_single.ui
+++ b/src/libprs500/gui2/dialogs/metadata_single.ui
@@ -155,11 +155,28 @@
-
-
-
- Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.
-
-
+
+
-
+
+
+ Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.
+
+
+
+ -
+
+
+ Open Tag Editor
+
+
+ Open Tag Editor
+
+
+ :/images/chapters.svg
+
+
+
+
-
@@ -218,9 +235,6 @@
- -
-
-
-
@@ -234,6 +248,9 @@
+ -
+
+
diff --git a/src/libprs500/gui2/dialogs/tag_editor.py b/src/libprs500/gui2/dialogs/tag_editor.py
new file mode 100644
index 0000000000..26ca84d3c1
--- /dev/null
+++ b/src/libprs500/gui2/dialogs/tag_editor.py
@@ -0,0 +1,93 @@
+## Copyright (C) 2008 Kovid Goyal kovid@kovidgoyal.net
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with this program; if not, write to the Free Software Foundation, Inc.,
+## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from PyQt4.QtCore import SIGNAL, Qt
+from PyQt4.QtGui import QDialog
+
+from libprs500.gui2.dialogs.tag_editor_ui import Ui_TagEditor
+from libprs500.gui2 import qstring_to_unicode
+
+class TagEditor(QDialog, Ui_TagEditor):
+
+ def __init__(self, window, db, index):
+ QDialog.__init__(self, window)
+ Ui_TagEditor.__init__(self)
+ self.setupUi(self)
+
+ self.db = db
+ self.index = index
+ tags = self.db.tags(self.index)
+ if tags:
+ tags = [tag.lower().strip() for tag in tags.split(',') if tag.strip()]
+ tags.sort()
+ for tag in tags:
+ self.applied_tags.addItem(tag)
+ else:
+ tags = []
+
+ self.tags = tags
+
+ all_tags = [tag.lower() for tag in self.db.all_tags()]
+ all_tags = list(set(all_tags))
+ all_tags.sort()
+ for tag in all_tags:
+ if tag not in tags:
+ self.available_tags.addItem(tag)
+
+ self.connect(self.apply_button, SIGNAL('clicked()'), self.apply_tags)
+ self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_tags)
+ self.connect(self.add_tag_button, SIGNAL('clicked()'), self.add_tag)
+ self.connect(self.add_tag_input, SIGNAL('returnPressed()'), self.add_tag)
+
+ def apply_tags(self):
+ for item in self.available_tags.selectedItems():
+ tag = qstring_to_unicode(item.text())
+ self.tags.append(tag)
+ self.available_tags.takeItem(self.available_tags.row(item))
+
+ self.tags.sort()
+ self.applied_tags.clear()
+ for tag in self.tags:
+ self.applied_tags.addItem(tag)
+
+
+
+ def unapply_tags(self):
+ for item in self.applied_tags.selectedItems():
+ tag = qstring_to_unicode(item.text())
+ self.tags.remove(tag)
+ self.available_tags.addItem(tag)
+
+ self.tags.sort()
+ self.applied_tags.clear()
+ for tag in self.tags:
+ self.applied_tags.addItem(tag)
+
+ self.available_tags.sortItems()
+
+ def add_tag(self):
+ tags = qstring_to_unicode(self.add_tag_input.text()).lower().split(',')
+ for tag in tags:
+ tag = tag.strip()
+ for item in self.available_tags.findItems(tag, Qt.MatchFixedString):
+ self.available_tags.takeItem(self.available_tags.row(item))
+ if tag not in self.tags:
+ self.tags.append(tag)
+
+ self.tags.sort()
+ self.applied_tags.clear()
+ for tag in self.tags:
+ self.applied_tags.addItem(tag)
+
+ self.add_tag_input.setText('')
diff --git a/src/libprs500/gui2/dialogs/tag_editor.ui b/src/libprs500/gui2/dialogs/tag_editor.ui
new file mode 100644
index 0000000000..7c09a70952
--- /dev/null
+++ b/src/libprs500/gui2/dialogs/tag_editor.ui
@@ -0,0 +1,303 @@
+
+ TagEditor
+
+
+
+ 0
+ 0
+ 588
+ 335
+
+
+
+ Tag Editor
+
+
+ :/images/chapters.svg
+
+
+ -
+
+
-
+
+
-
+
+
-
+
+
+ A&vailable tags
+
+
+ available_tags
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::MultiSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Apply tag to current book
+
+
+ ...
+
+
+ :/images/forward.svg
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ A&pplied tags
+
+
+ applied_tags
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Unapply (remove) tag from current book
+
+
+ ...
+
+
+ :/images/list_remove.svg
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ &Add tag:
+
+
+ add_tag_input
+
+
+
+ -
+
+
+ If the tag you want is not in the available list, you can add it here. Accepts a comman separated list of tags.
+
+
+
+ -
+
+
+ Add tag to available tags and apply it to current book
+
+
+ ...
+
+
+ :/images/plus.svg
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ TagEditor
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ TagEditor
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/libprs500/library/database.py b/src/libprs500/library/database.py
index f162e60fd4..f2acd1e8cb 100644
--- a/src/libprs500/library/database.py
+++ b/src/libprs500/library/database.py
@@ -829,12 +829,13 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return(decompress(data[0]))
def tags(self, index):
- '''tags as a comman separated list or None'''
+ '''tags as a comma separated list or None'''
id = self.id(index)
matches = self.conn.execute('SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=?)', (id,)).fetchall()
- if not matches:
+ if not matches or not matches[0][0]:
return None
- return matches[0][0]
+ matches = [t.lower().strip() for t in matches[0][0].split(',')]
+ return ','.join(matches)
def series_id(self, index):
id = self.id(index)
@@ -898,6 +899,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def all_series(self):
return [ (i[0], i[1]) for i in \
self.conn.execute('SELECT id, name FROM series').fetchall()]
+
+ def all_tags(self):
+ return [i[0].strip() for i in self.conn.execute('SELECT name FROM tags').fetchall() if i[0].strip()]
def conversion_options(self, id, format):
data = self.conn.execute('SELECT data FROM conversion_options WHERE book=? AND format=?', (id, format.upper())).fetchone()
@@ -1026,7 +1030,7 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
if not append:
self.conn.execute('DELETE FROM books_tags_link WHERE book=?', (id,))
for tag in set(tags):
- tag = tag.strip()
+ tag = tag.lower().strip()
if not tag:
continue
t = self.conn.execute('SELECT id FROM tags WHERE name=?', (tag,)).fetchone()