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()