mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make tagging system fully case insensitive and implement a tag editor
This commit is contained in:
parent
d4cf1ad9aa
commit
4f5dafad5a
@ -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
|
||||
|
@ -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:
|
||||
|
@ -155,11 +155,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" >
|
||||
<widget class="QLineEdit" name="tags" >
|
||||
<property name="toolTip" >
|
||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLineEdit" name="tags" >
|
||||
<property name="toolTip" >
|
||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="tag_editor_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/chapters.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
@ -218,9 +235,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" >
|
||||
<widget class="QLineEdit" name="isbn" />
|
||||
</item>
|
||||
<item row="8" column="0" >
|
||||
<widget class="QLabel" name="label_9" >
|
||||
<property name="text" >
|
||||
@ -234,6 +248,9 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" >
|
||||
<widget class="QLineEdit" name="isbn" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
93
src/libprs500/gui2/dialogs/tag_editor.py
Normal file
93
src/libprs500/gui2/dialogs/tag_editor.py
Normal file
@ -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('')
|
303
src/libprs500/gui2/dialogs/tag_editor.ui
Normal file
303
src/libprs500/gui2/dialogs/tag_editor.ui
Normal file
@ -0,0 +1,303 @@
|
||||
<ui version="4.0" >
|
||||
<class>TagEditor</class>
|
||||
<widget class="QDialog" name="TagEditor" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>335</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Tag Editor</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="../images.qrc" >:/images/chapters.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>A&vailable tags</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>available_tags</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="available_tags" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="apply_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Apply tag to current book</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/forward.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>A&pplied tags</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>applied_tags</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="applied_tags" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="unapply_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Unapply (remove) tag from current book</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/list_remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>&Add tag:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>add_tag_input</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="add_tag_input" >
|
||||
<property name="toolTip" >
|
||||
<string>If the tag you want is not in the available list, you can add it here. Accepts a comman separated list of tags.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="add_tag_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Add tag to available tags and apply it to current book</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../images.qrc" />
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TagEditor</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TagEditor</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -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)
|
||||
@ -899,6 +900,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
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()
|
||||
if data:
|
||||
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user