mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Merge from custcol trunk
This commit is contained in:
commit
eda505f755
@ -4,6 +4,7 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from itertools import izip
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
from calibre.customize import Plugin as _Plugin
|
||||
|
||||
@ -238,7 +239,7 @@ class OutputProfile(Plugin):
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return ', '.join(tags)
|
||||
return escape(', '.join(tags))
|
||||
|
||||
class iPadOutput(OutputProfile):
|
||||
|
||||
@ -383,7 +384,8 @@ class KindleOutput(OutputProfile):
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return 'ttt '.join(tags)+'ttt '
|
||||
return u'%s <br/><span style="color: white">%s</span>' % (', '.join(tags),
|
||||
'ttt '.join(tags)+'ttt ')
|
||||
|
||||
class KindleDXOutput(OutputProfile):
|
||||
|
||||
@ -399,7 +401,8 @@ class KindleDXOutput(OutputProfile):
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return 'ttt '.join(tags)+'ttt '
|
||||
return u'%s <br/><span style="color: white">%s</span>' % (', '.join(tags),
|
||||
'ttt '.join(tags)+'ttt ')
|
||||
|
||||
class IlliadOutput(OutputProfile):
|
||||
|
||||
|
@ -10,7 +10,7 @@ import threading, Queue
|
||||
|
||||
|
||||
class DeviceManager(object):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.devices = []
|
||||
self.device_jobs = Queue(0)
|
||||
@ -21,19 +21,19 @@ class Job(object):
|
||||
def __init__(self, func, args):
|
||||
self.completed = False
|
||||
self.exception = None
|
||||
|
||||
|
||||
|
||||
class Worker(threading.Thread):
|
||||
|
||||
def __init__(self, jobs):
|
||||
|
||||
def __init__(self, jobs):
|
||||
self.jobs = jobs
|
||||
self.results = []
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
|
||||
|
||||
def run(self):
|
||||
'''Thread loops taking jobs from the queue as they become available'''
|
||||
while True:
|
||||
job = self.jobs.get(True, None)
|
||||
self.jobs.get(True, None)
|
||||
# Do job
|
||||
self.jobs.task_done()
|
||||
self.jobs.task_done()
|
||||
|
@ -99,7 +99,7 @@ class Jacket(object):
|
||||
except:
|
||||
tags = []
|
||||
if tags:
|
||||
tags = '<b>Tags: </b>' + escape(self.opts.dest.tags_to_string(tags))
|
||||
tags = '<b>Tags: </b>' + self.opts.dest.tags_to_string(tags)
|
||||
else:
|
||||
tags = ''
|
||||
try:
|
||||
|
@ -354,7 +354,6 @@ if another paragraph_def is found, the state changes to collect_tokens.
|
||||
def __tab_stop_func(self, line):
|
||||
"""
|
||||
"""
|
||||
type = 'tabs-%s' % self.__tab_type
|
||||
self.__att_val_dict['tabs'] += '%s:' % self.__tab_type
|
||||
self.__att_val_dict['tabs'] += '%s;' % line[20:-1]
|
||||
self.__tab_type = 'left'
|
||||
@ -373,7 +372,6 @@ if another paragraph_def is found, the state changes to collect_tokens.
|
||||
"""
|
||||
leader = self.__tab_type_dict.get(self.__token_info)
|
||||
if leader != None:
|
||||
type = 'tabs-%s' % self.__tab_type
|
||||
self.__att_val_dict['tabs'] += '%s^' % leader
|
||||
else:
|
||||
if self.__run_level > 3:
|
||||
|
@ -318,7 +318,6 @@ class Styles:
|
||||
Try to add the number to dictionary entry tabs-left, or tabs-right, etc.
|
||||
If the dictionary entry doesn't exist, create one.
|
||||
"""
|
||||
type = 'tabs-%s' % self.__tab_type
|
||||
try:
|
||||
if self.__leader_found:
|
||||
self.__styles_dict['par'][self.__styles_num]['tabs']\
|
||||
@ -362,7 +361,6 @@ class Styles:
|
||||
leader = self.__tab_type_dict.get(self.__token_info)
|
||||
if leader != None:
|
||||
leader += '^'
|
||||
type = 'tabs-%s' % self.__tab_type
|
||||
try:
|
||||
self.__styles_dict['par'][self.__styles_num]['tabs'] += ':%s;' % leader
|
||||
except KeyError:
|
||||
|
@ -1,17 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import QDialog
|
||||
from calibre.gui2 import ResizableDialog
|
||||
from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog
|
||||
|
||||
class CommentsDialog(QDialog, Ui_CommentsDialog):
|
||||
def __init__(self, parent, text):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_CommentsDialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
if text is not None:
|
||||
self.textbox.setPlainText(text)
|
||||
self.textbox.setTabChangesFocus(True)
|
||||
#!/usr/bin/env python
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
from PyQt4.Qt import QDialog
|
||||
from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog
|
||||
|
||||
class CommentsDialog(QDialog, Ui_CommentsDialog):
|
||||
def __init__(self, parent, text):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_CommentsDialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
if text is not None:
|
||||
self.textbox.setPlainText(text)
|
||||
self.textbox.setTabChangesFocus(True)
|
||||
|
@ -1,83 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CommentsDialog</class>
|
||||
<widget class="QDialog" name="CommentsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>336</width>
|
||||
<height>235</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Comments</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>311</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="textbox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CommentsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>229</x>
|
||||
<y>211</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>234</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CommentsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>297</x>
|
||||
<y>217</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>234</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CommentsDialog</class>
|
||||
<widget class="QDialog" name="CommentsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>336</width>
|
||||
<height>235</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Comments</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>311</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="textbox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CommentsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>229</x>
|
||||
<y>211</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>234</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CommentsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>297</x>
|
||||
<y>217</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>234</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
@ -1,6 +1,6 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, re, time, textwrap, sys, copy
|
||||
import os, re, time, textwrap, copy
|
||||
|
||||
from PyQt4.Qt import QDialog, QListWidgetItem, QIcon, \
|
||||
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
||||
@ -17,7 +17,6 @@ from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \
|
||||
ALL_COLUMNS, NONE, info_dialog, choose_files, \
|
||||
warning_dialog, ResizableDialog
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2.library import BooksModel
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.ebooks.oeb.iterator import is_supported
|
||||
from calibre.library import server_config
|
||||
@ -666,10 +665,10 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
return
|
||||
|
||||
def add_custcol(self):
|
||||
d = CreateCustomColumn(self, False, self.model.orig_headers, ALL_COLUMNS)
|
||||
CreateCustomColumn(self, False, self.model.orig_headers, ALL_COLUMNS)
|
||||
|
||||
def edit_custcol(self):
|
||||
d = CreateCustomColumn(self, True, self.model.orig_headers, ALL_COLUMNS)
|
||||
CreateCustomColumn(self, True, self.model.orig_headers, ALL_COLUMNS)
|
||||
|
||||
def view_server_logs(self):
|
||||
from calibre.library.server import log_access_file, log_error_file
|
||||
|
@ -1,123 +1,121 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''Dialog to create a new custom column'''
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, QObject
|
||||
from PyQt4.Qt import QDialog, Qt, QMessageBox, QListWidgetItem, QVariant
|
||||
from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn
|
||||
from calibre.gui2 import ALL_COLUMNS, qstring_to_unicode
|
||||
|
||||
class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
column_types = {
|
||||
0:{'datatype':'text', 'text':_('Text, column shown in tags browser'), 'is_multiple':False},
|
||||
1:{'datatype':'*text', 'text':_('Comma separated text, shown in tags browser'), 'is_multiple':True},
|
||||
2:{'datatype':'comments', 'text':_('Text, column not shown in tags browser'), 'is_multiple':False},
|
||||
3:{'datatype':'datetime', 'text':_('Date'), 'is_multiple':False},
|
||||
4:{'datatype':'float', 'text':_('Float'), 'is_multiple':False},
|
||||
5:{'datatype':'int', 'text':_('Integer'), 'is_multiple':False},
|
||||
6:{'datatype':'rating', 'text':_('Rating (stars)'), 'is_multiple':False},
|
||||
7:{'datatype':'bool', 'text':_('Yes/No'), 'is_multiple':False},
|
||||
}
|
||||
def __init__(self, parent, editing, standard_colheads, standard_colnames):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_QCreateCustomColumn.__init__(self)
|
||||
self.setupUi(self)
|
||||
self.connect(self.button_box, SIGNAL("accepted()"), self.accept)
|
||||
self.connect(self.button_box, SIGNAL("rejected()"), self.reject)
|
||||
self.parent = parent
|
||||
self.editing_col = editing
|
||||
self.standard_colheads = standard_colheads
|
||||
self.standard_colnames = standard_colnames
|
||||
if not self.editing_col:
|
||||
for t in self.column_types:
|
||||
self.column_type_box.addItem(self.column_types[t]['text'])
|
||||
self.exec_()
|
||||
return
|
||||
idx = parent.columns.currentRow()
|
||||
if idx < 0:
|
||||
self.parent.messagebox(_('No column has been selected'))
|
||||
return
|
||||
col = qstring_to_unicode(parent.columns.item(idx).data(Qt.UserRole).toString())
|
||||
if col not in parent.custcols:
|
||||
self.parent.messagebox(_('Selected column is not a user-defined column'))
|
||||
return
|
||||
|
||||
c = parent.custcols[col]
|
||||
self.column_name_box.setText(c['label'])
|
||||
self.column_heading_box.setText(c['name'])
|
||||
ct = c['datatype'] if not c['is_multiple'] else '*text'
|
||||
self.orig_column_number = c['num']
|
||||
self.orig_column_name = col
|
||||
column_numbers = dict(map(lambda x:(self.column_types[x]['datatype'], x), self.column_types))
|
||||
self.column_type_box.addItem(self.column_types[column_numbers[ct]]['text'])
|
||||
self.exec_()
|
||||
|
||||
def accept(self):
|
||||
col = qstring_to_unicode(self.column_name_box.text())
|
||||
col_heading = qstring_to_unicode(self.column_heading_box.text())
|
||||
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
|
||||
if col_type == '*text':
|
||||
col_type='text'
|
||||
is_multiple = True
|
||||
else:
|
||||
is_multiple = False
|
||||
if not col:
|
||||
self.parent.messagebox(_('No lookup name was provided'))
|
||||
return
|
||||
if not col_heading:
|
||||
self.parent.messagebox(_('No column heading was provided'))
|
||||
return
|
||||
bad_col = False
|
||||
if col in self.parent.custcols:
|
||||
if not self.editing_col or self.parent.custcols[col]['num'] != self.orig_column_number:
|
||||
bad_col = True
|
||||
if col in self.standard_colnames:
|
||||
bad_col = True
|
||||
if bad_col:
|
||||
self.parent.messagebox(_('The lookup name %s is already used')%col)
|
||||
return
|
||||
bad_head = False
|
||||
for t in self.parent.custcols:
|
||||
if self.parent.custcols[t]['name'] == col_heading:
|
||||
if not self.editing_col or self.parent.custcols[t]['num'] != self.orig_column_number:
|
||||
bad_head = True
|
||||
for t in self.standard_colheads:
|
||||
if self.standard_colheads[t] == col_heading:
|
||||
bad_head = True
|
||||
if bad_head:
|
||||
self.parent.messagebox(_('The heading %s is already used')%col_heading)
|
||||
return
|
||||
if col.find(':') >= 0 or col.find(' ') >= 0 and \
|
||||
(not is_alpha(col) or is_lower(col)):
|
||||
self.parent.messagebox(_('The lookup name must be lower case and cannot contain ":"s or spaces'))
|
||||
return
|
||||
|
||||
if not self.editing_col:
|
||||
self.parent.custcols[col] = {
|
||||
'label':col,
|
||||
'name':col_heading,
|
||||
'datatype':col_type,
|
||||
'editable':True,
|
||||
'display':None,
|
||||
'normalized':None,
|
||||
'num':None,
|
||||
'is_multiple':is_multiple,
|
||||
}
|
||||
item = QListWidgetItem(col_heading, self.parent.columns)
|
||||
item.setData(Qt.UserRole, QVariant(col))
|
||||
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||
item.setCheckState(Qt.Checked)
|
||||
else:
|
||||
idx = self.parent.columns.currentRow()
|
||||
item = self.parent.columns.item(idx)
|
||||
item.setData(Qt.UserRole, QVariant(col))
|
||||
item.setText(col_heading)
|
||||
self.parent.custcols[self.orig_column_name]['label'] = col
|
||||
self.parent.custcols[self.orig_column_name]['name'] = col_heading
|
||||
self.parent.custcols[self.orig_column_name]['*edited'] = True
|
||||
self.parent.custcols[self.orig_column_name]['*must_restart'] = True
|
||||
QDialog.accept(self)
|
||||
|
||||
def reject(self):
|
||||
QDialog.reject(self)
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''Dialog to create a new custom column'''
|
||||
|
||||
from PyQt4.QtCore import SIGNAL
|
||||
from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant
|
||||
from calibre.gui2.dialogs.config.create_custom_column_ui import Ui_QCreateCustomColumn
|
||||
|
||||
class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
column_types = {
|
||||
0:{'datatype':'text', 'text':_('Text, column shown in tags browser'), 'is_multiple':False},
|
||||
1:{'datatype':'*text', 'text':_('Comma separated text, shown in tags browser'), 'is_multiple':True},
|
||||
2:{'datatype':'comments', 'text':_('Text, column not shown in tags browser'), 'is_multiple':False},
|
||||
3:{'datatype':'datetime', 'text':_('Date'), 'is_multiple':False},
|
||||
4:{'datatype':'float', 'text':_('Float'), 'is_multiple':False},
|
||||
5:{'datatype':'int', 'text':_('Integer'), 'is_multiple':False},
|
||||
6:{'datatype':'rating', 'text':_('Rating (stars)'), 'is_multiple':False},
|
||||
7:{'datatype':'bool', 'text':_('Yes/No'), 'is_multiple':False},
|
||||
}
|
||||
def __init__(self, parent, editing, standard_colheads, standard_colnames):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_QCreateCustomColumn.__init__(self)
|
||||
self.setupUi(self)
|
||||
self.connect(self.button_box, SIGNAL("accepted()"), self.accept)
|
||||
self.connect(self.button_box, SIGNAL("rejected()"), self.reject)
|
||||
self.parent = parent
|
||||
self.editing_col = editing
|
||||
self.standard_colheads = standard_colheads
|
||||
self.standard_colnames = standard_colnames
|
||||
if not self.editing_col:
|
||||
for t in self.column_types:
|
||||
self.column_type_box.addItem(self.column_types[t]['text'])
|
||||
self.exec_()
|
||||
return
|
||||
idx = parent.columns.currentRow()
|
||||
if idx < 0:
|
||||
self.parent.messagebox(_('No column has been selected'))
|
||||
return
|
||||
col = unicode(parent.columns.item(idx).data(Qt.UserRole).toString())
|
||||
if col not in parent.custcols:
|
||||
self.parent.messagebox(_('Selected column is not a user-defined column'))
|
||||
return
|
||||
|
||||
c = parent.custcols[col]
|
||||
self.column_name_box.setText(c['label'])
|
||||
self.column_heading_box.setText(c['name'])
|
||||
ct = c['datatype'] if not c['is_multiple'] else '*text'
|
||||
self.orig_column_number = c['num']
|
||||
self.orig_column_name = col
|
||||
column_numbers = dict(map(lambda x:(self.column_types[x]['datatype'], x), self.column_types))
|
||||
self.column_type_box.addItem(self.column_types[column_numbers[ct]]['text'])
|
||||
self.exec_()
|
||||
|
||||
def accept(self):
|
||||
col = unicode(self.column_name_box.text())
|
||||
col_heading = unicode(self.column_heading_box.text())
|
||||
col_type = self.column_types[self.column_type_box.currentIndex()]['datatype']
|
||||
if col_type == '*text':
|
||||
col_type='text'
|
||||
is_multiple = True
|
||||
else:
|
||||
is_multiple = False
|
||||
if not col:
|
||||
self.parent.messagebox(_('No lookup name was provided'))
|
||||
return
|
||||
if not col_heading:
|
||||
self.parent.messagebox(_('No column heading was provided'))
|
||||
return
|
||||
bad_col = False
|
||||
if col in self.parent.custcols:
|
||||
if not self.editing_col or self.parent.custcols[col]['num'] != self.orig_column_number:
|
||||
bad_col = True
|
||||
if col in self.standard_colnames:
|
||||
bad_col = True
|
||||
if bad_col:
|
||||
self.parent.messagebox(_('The lookup name %s is already used')%col)
|
||||
return
|
||||
bad_head = False
|
||||
for t in self.parent.custcols:
|
||||
if self.parent.custcols[t]['name'] == col_heading:
|
||||
if not self.editing_col or self.parent.custcols[t]['num'] != self.orig_column_number:
|
||||
bad_head = True
|
||||
for t in self.standard_colheads:
|
||||
if self.standard_colheads[t] == col_heading:
|
||||
bad_head = True
|
||||
if bad_head:
|
||||
self.parent.messagebox(_('The heading %s is already used')%col_heading)
|
||||
return
|
||||
if ':' in col or ' ' in col or col.lower() != col:
|
||||
self.parent.messagebox(_('The lookup name must be lower case and cannot contain ":"s or spaces'))
|
||||
return
|
||||
|
||||
if not self.editing_col:
|
||||
self.parent.custcols[col] = {
|
||||
'label':col,
|
||||
'name':col_heading,
|
||||
'datatype':col_type,
|
||||
'editable':True,
|
||||
'display':None,
|
||||
'normalized':None,
|
||||
'num':None,
|
||||
'is_multiple':is_multiple,
|
||||
}
|
||||
item = QListWidgetItem(col_heading, self.parent.columns)
|
||||
item.setData(Qt.UserRole, QVariant(col))
|
||||
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||
item.setCheckState(Qt.Checked)
|
||||
else:
|
||||
idx = self.parent.columns.currentRow()
|
||||
item = self.parent.columns.item(idx)
|
||||
item.setData(Qt.UserRole, QVariant(col))
|
||||
item.setText(col_heading)
|
||||
self.parent.custcols[self.orig_column_name]['label'] = col
|
||||
self.parent.custcols[self.orig_column_name]['name'] = col_heading
|
||||
self.parent.custcols[self.orig_column_name]['*edited'] = True
|
||||
self.parent.custcols[self.orig_column_name]['*must_restart'] = True
|
||||
QDialog.accept(self)
|
||||
|
||||
def reject(self):
|
||||
QDialog.reject(self)
|
||||
|
@ -1,142 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QCreateCustomColumn</class>
|
||||
<widget class="QDialog" name="QCreateCustomColumn">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>391</width>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Tag-based Column</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>371</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Lookup name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Column heading</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="column_name_box">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Used for searching the column. Must be lower case and not contain spaces or colons.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="column_heading_box">
|
||||
<property name="toolTip">
|
||||
<string>Column heading in the library view and category name in tags browser</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Column type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="column_type_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>What kind of information will be kept in the column.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create and edit custom columns</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>column_name_box</tabstop>
|
||||
<tabstop>column_heading_box</tabstop>
|
||||
<tabstop>column_type_box</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QCreateCustomColumn</class>
|
||||
<widget class="QDialog" name="QCreateCustomColumn">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>391</width>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Tag-based Column</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>371</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Lookup name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Column heading</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="column_name_box">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Used for searching the column. Must be lower case and not contain spaces or colons.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="column_heading_box">
|
||||
<property name="toolTip">
|
||||
<string>Column heading in the library view and category name in tags browser</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Column type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="column_type_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>What kind of information will be kept in the column.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create and edit custom columns</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>column_name_box</tabstop>
|
||||
<tabstop>column_heading_box</tabstop>
|
||||
<tabstop>column_type_box</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -1,186 +1,182 @@
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from copy import copy
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QVariant
|
||||
from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QComboBox, \
|
||||
QIcon, QListWidgetItem
|
||||
from PyQt4.Qt import QString
|
||||
|
||||
from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories
|
||||
from calibre.gui2 import qstring_to_unicode, config
|
||||
from calibre.gui2 import question_dialog, error_dialog
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.constants import islinux
|
||||
|
||||
class Item:
|
||||
def __init__(self, name, label, index, icon, exists):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.index = index
|
||||
self.icon = icon
|
||||
self.exists = exists
|
||||
def __str__(self):
|
||||
return 'name=%s, label=%s, index=%s, exists='%(self.name, self.label, self.index, self.exists)
|
||||
|
||||
class TagCategories(QDialog, Ui_TagCategories):
|
||||
category_labels = ['', 'author', 'series', 'publisher', 'tag']
|
||||
|
||||
def __init__(self, window, db, index=None):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagCategories.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.db = db
|
||||
self.index = index
|
||||
self.applied_items = []
|
||||
|
||||
category_icons = [None, QIcon(I('user_profile.svg')), QIcon(I('series.svg')),
|
||||
QIcon(I('publisher.png')), QIcon(I('tags.svg'))]
|
||||
category_values = [None,
|
||||
lambda: [n for (id, n) in self.db.all_authors()],
|
||||
lambda: [n for (id, n) in self.db.all_series()],
|
||||
lambda: [n for (id, n) in self.db.all_publishers()],
|
||||
lambda: self.db.all_tags()
|
||||
]
|
||||
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')]
|
||||
|
||||
self.all_items = []
|
||||
self.all_items_dict = {}
|
||||
for idx,label in enumerate(self.category_labels):
|
||||
if idx == 0:
|
||||
continue
|
||||
for n in category_values[idx]():
|
||||
t = Item(name=n, label=label, index=len(self.all_items),icon=category_icons[idx], exists=True)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[label+':'+n] = t
|
||||
|
||||
self.categories = dict.copy(config['user_categories'])
|
||||
if self.categories is None:
|
||||
self.categories = {}
|
||||
for cat in self.categories:
|
||||
for item,l in enumerate(self.categories[cat]):
|
||||
key = ':'.join([l[1], l[0]])
|
||||
t = self.all_items_dict.get(key, None)
|
||||
if t is None:
|
||||
t = Item(name=l[0], label=l[1], index=len(self.all_items),
|
||||
icon=category_icons[self.category_labels.index(l[1])], exists=False)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[key] = t
|
||||
l[2] = t.index
|
||||
|
||||
self.all_items_sorted = sorted(self.all_items, cmp=lambda x,y: cmp(x.name.lower(), y.name.lower()))
|
||||
self.display_filtered_categories(0)
|
||||
|
||||
for v in category_names:
|
||||
self.category_filter_box.addItem(v)
|
||||
self.current_cat_name = None
|
||||
|
||||
self.connect(self.apply_button, SIGNAL('clicked()'), self.apply_tags)
|
||||
self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_tags)
|
||||
self.connect(self.add_category_button, SIGNAL('clicked()'), self.add_category)
|
||||
self.connect(self.category_box, SIGNAL('currentIndexChanged(int)'), self.select_category)
|
||||
self.connect(self.category_filter_box, SIGNAL('currentIndexChanged(int)'), self.display_filtered_categories)
|
||||
self.connect(self.delete_category_button, SIGNAL('clicked()'), self.del_category)
|
||||
if islinux:
|
||||
self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
|
||||
else:
|
||||
self.connect(self.available_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
|
||||
self.connect(self.applied_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
|
||||
|
||||
self.populate_category_list()
|
||||
return
|
||||
self.select_category(0)
|
||||
|
||||
def make_list_widget(self, item):
|
||||
n = item.name if item.exists else item.name + _(' (not on any book)')
|
||||
w = QListWidgetItem(item.icon, n)
|
||||
w.setData(Qt.UserRole, item.index)
|
||||
return w
|
||||
|
||||
def display_filtered_categories(self, idx):
|
||||
idx = idx if idx is not None else self.category_filter_box.currentIndex()
|
||||
self.available_items_box.clear()
|
||||
self.applied_items_box.clear()
|
||||
for item in self.all_items_sorted:
|
||||
if idx == 0 or item.label == self.category_labels[idx]:
|
||||
if item.index not in self.applied_items and item.exists:
|
||||
self.available_items_box.addItem(self.make_list_widget(item))
|
||||
for index in self.applied_items:
|
||||
self.applied_items_box.addItem(self.make_list_widget(self.all_items[index]))
|
||||
|
||||
def apply_tags(self, node=None):
|
||||
if self.current_cat_name is None:
|
||||
return
|
||||
nodes = self.available_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
if index not in self.applied_items:
|
||||
self.applied_items.append(index)
|
||||
self.applied_items.sort(cmp=lambda x, y:cmp(self.all_items[x].name.lower(), self.all_items[y].name.lower()))
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def unapply_tags(self, node=None):
|
||||
nodes = self.applied_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
self.applied_items.remove(index)
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def add_category(self):
|
||||
self.save_category()
|
||||
cat_name = qstring_to_unicode(self.input_box.text()).strip()
|
||||
if cat_name == '':
|
||||
return False
|
||||
if cat_name not in self.categories:
|
||||
self.category_box.clear()
|
||||
self.current_cat_name = cat_name
|
||||
self.categories[cat_name] = []
|
||||
self.applied_items = []
|
||||
self.populate_category_list()
|
||||
self.category_box.setCurrentIndex(self.category_box.findText(cat_name))
|
||||
else:
|
||||
self.select_category(self.category_box.findText(cat_name))
|
||||
return True
|
||||
|
||||
def del_category(self):
|
||||
if self.current_cat_name is not None:
|
||||
if not confirm('<p>'+_('The current tag category will be '
|
||||
'<b>permanently deleted</b>. Are you sure?')
|
||||
+'</p>', 'tag_category_delete', self):
|
||||
return
|
||||
del self.categories[self.current_cat_name]
|
||||
self.current_cat_name = None
|
||||
self.category_box.removeItem(self.category_box.currentIndex())
|
||||
|
||||
def select_category(self, idx):
|
||||
self.save_category()
|
||||
s = self.category_box.itemText(idx)
|
||||
if s:
|
||||
self.current_cat_name = unicode(s)
|
||||
else:
|
||||
self.current_cat_name = None
|
||||
if self.current_cat_name:
|
||||
self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])]
|
||||
else:
|
||||
self.applied_items = []
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def accept(self):
|
||||
self.save_category()
|
||||
config['user_categories'] = self.categories
|
||||
QDialog.accept(self)
|
||||
|
||||
def save_category(self):
|
||||
if self.current_cat_name is not None:
|
||||
l = []
|
||||
for index in self.applied_items:
|
||||
item = self.all_items[index]
|
||||
l.append([item.name, item.label, item.index])
|
||||
self.categories[self.current_cat_name] = l
|
||||
|
||||
def populate_category_list(self):
|
||||
for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
|
||||
self.category_box.addItem(n)
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
from PyQt4.QtGui import QDialog, QIcon, QListWidgetItem
|
||||
|
||||
from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories
|
||||
from calibre.gui2 import qstring_to_unicode, config
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.constants import islinux
|
||||
|
||||
class Item:
|
||||
def __init__(self, name, label, index, icon, exists):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.index = index
|
||||
self.icon = icon
|
||||
self.exists = exists
|
||||
def __str__(self):
|
||||
return 'name=%s, label=%s, index=%s, exists='%(self.name, self.label, self.index, self.exists)
|
||||
|
||||
class TagCategories(QDialog, Ui_TagCategories):
|
||||
category_labels = ['', 'author', 'series', 'publisher', 'tag']
|
||||
|
||||
def __init__(self, window, db, index=None):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagCategories.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.db = db
|
||||
self.index = index
|
||||
self.applied_items = []
|
||||
|
||||
category_icons = [None, QIcon(I('user_profile.svg')), QIcon(I('series.svg')),
|
||||
QIcon(I('publisher.png')), QIcon(I('tags.svg'))]
|
||||
category_values = [None,
|
||||
lambda: [n for (id, n) in self.db.all_authors()],
|
||||
lambda: [n for (id, n) in self.db.all_series()],
|
||||
lambda: [n for (id, n) in self.db.all_publishers()],
|
||||
lambda: self.db.all_tags()
|
||||
]
|
||||
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')]
|
||||
|
||||
self.all_items = []
|
||||
self.all_items_dict = {}
|
||||
for idx,label in enumerate(self.category_labels):
|
||||
if idx == 0:
|
||||
continue
|
||||
for n in category_values[idx]():
|
||||
t = Item(name=n, label=label, index=len(self.all_items),icon=category_icons[idx], exists=True)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[label+':'+n] = t
|
||||
|
||||
self.categories = dict.copy(config['user_categories'])
|
||||
if self.categories is None:
|
||||
self.categories = {}
|
||||
for cat in self.categories:
|
||||
for item,l in enumerate(self.categories[cat]):
|
||||
key = ':'.join([l[1], l[0]])
|
||||
t = self.all_items_dict.get(key, None)
|
||||
if t is None:
|
||||
t = Item(name=l[0], label=l[1], index=len(self.all_items),
|
||||
icon=category_icons[self.category_labels.index(l[1])], exists=False)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[key] = t
|
||||
l[2] = t.index
|
||||
|
||||
self.all_items_sorted = sorted(self.all_items, cmp=lambda x,y: cmp(x.name.lower(), y.name.lower()))
|
||||
self.display_filtered_categories(0)
|
||||
|
||||
for v in category_names:
|
||||
self.category_filter_box.addItem(v)
|
||||
self.current_cat_name = None
|
||||
|
||||
self.connect(self.apply_button, SIGNAL('clicked()'), self.apply_tags)
|
||||
self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_tags)
|
||||
self.connect(self.add_category_button, SIGNAL('clicked()'), self.add_category)
|
||||
self.connect(self.category_box, SIGNAL('currentIndexChanged(int)'), self.select_category)
|
||||
self.connect(self.category_filter_box, SIGNAL('currentIndexChanged(int)'), self.display_filtered_categories)
|
||||
self.connect(self.delete_category_button, SIGNAL('clicked()'), self.del_category)
|
||||
if islinux:
|
||||
self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
|
||||
else:
|
||||
self.connect(self.available_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
|
||||
self.connect(self.applied_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
|
||||
|
||||
self.populate_category_list()
|
||||
return
|
||||
self.select_category(0)
|
||||
|
||||
def make_list_widget(self, item):
|
||||
n = item.name if item.exists else item.name + _(' (not on any book)')
|
||||
w = QListWidgetItem(item.icon, n)
|
||||
w.setData(Qt.UserRole, item.index)
|
||||
return w
|
||||
|
||||
def display_filtered_categories(self, idx):
|
||||
idx = idx if idx is not None else self.category_filter_box.currentIndex()
|
||||
self.available_items_box.clear()
|
||||
self.applied_items_box.clear()
|
||||
for item in self.all_items_sorted:
|
||||
if idx == 0 or item.label == self.category_labels[idx]:
|
||||
if item.index not in self.applied_items and item.exists:
|
||||
self.available_items_box.addItem(self.make_list_widget(item))
|
||||
for index in self.applied_items:
|
||||
self.applied_items_box.addItem(self.make_list_widget(self.all_items[index]))
|
||||
|
||||
def apply_tags(self, node=None):
|
||||
if self.current_cat_name is None:
|
||||
return
|
||||
nodes = self.available_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
if index not in self.applied_items:
|
||||
self.applied_items.append(index)
|
||||
self.applied_items.sort(cmp=lambda x, y:cmp(self.all_items[x].name.lower(), self.all_items[y].name.lower()))
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def unapply_tags(self, node=None):
|
||||
nodes = self.applied_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
self.applied_items.remove(index)
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def add_category(self):
|
||||
self.save_category()
|
||||
cat_name = qstring_to_unicode(self.input_box.text()).strip()
|
||||
if cat_name == '':
|
||||
return False
|
||||
if cat_name not in self.categories:
|
||||
self.category_box.clear()
|
||||
self.current_cat_name = cat_name
|
||||
self.categories[cat_name] = []
|
||||
self.applied_items = []
|
||||
self.populate_category_list()
|
||||
self.category_box.setCurrentIndex(self.category_box.findText(cat_name))
|
||||
else:
|
||||
self.select_category(self.category_box.findText(cat_name))
|
||||
return True
|
||||
|
||||
def del_category(self):
|
||||
if self.current_cat_name is not None:
|
||||
if not confirm('<p>'+_('The current tag category will be '
|
||||
'<b>permanently deleted</b>. Are you sure?')
|
||||
+'</p>', 'tag_category_delete', self):
|
||||
return
|
||||
del self.categories[self.current_cat_name]
|
||||
self.current_cat_name = None
|
||||
self.category_box.removeItem(self.category_box.currentIndex())
|
||||
|
||||
def select_category(self, idx):
|
||||
self.save_category()
|
||||
s = self.category_box.itemText(idx)
|
||||
if s:
|
||||
self.current_cat_name = unicode(s)
|
||||
else:
|
||||
self.current_cat_name = None
|
||||
if self.current_cat_name:
|
||||
self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])]
|
||||
else:
|
||||
self.applied_items = []
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def accept(self):
|
||||
self.save_category()
|
||||
config['user_categories'] = self.categories
|
||||
QDialog.accept(self)
|
||||
|
||||
def save_category(self):
|
||||
if self.current_cat_name is not None:
|
||||
l = []
|
||||
for index in self.applied_items:
|
||||
item = self.all_items[index]
|
||||
l.append([item.name, item.label, item.index])
|
||||
self.categories[self.current_cat_name] = l
|
||||
|
||||
def populate_category_list(self):
|
||||
for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
|
||||
self.category_box.addItem(n)
|
||||
|
@ -6,16 +6,15 @@ import os, textwrap, traceback, re, shutil, functools
|
||||
from operator import attrgetter
|
||||
from math import cos, sin, pi
|
||||
from contextlib import closing
|
||||
from datetime import date
|
||||
|
||||
from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \
|
||||
QPainterPath, QLinearGradient, QBrush, \
|
||||
QPen, QStyle, QPainter, QStyleOptionViewItemV4, \
|
||||
QIcon, QImage, QMenu, \
|
||||
QStyledItemDelegate, QCompleter, QIntValidator, \
|
||||
QPlainTextEdit, QDoubleValidator, QCheckBox, QMessageBox
|
||||
QDoubleValidator, QCheckBox
|
||||
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, pyqtSignal, \
|
||||
SIGNAL, QObject, QSize, QModelIndex, QDate, QRect
|
||||
SIGNAL, QObject, QSize, QModelIndex, QDate
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.ebooks.metadata import string_to_authors, fmt_sidx, authors_to_string
|
||||
@ -25,7 +24,7 @@ from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
||||
from calibre.gui2.widgets import EnLineEdit, TagsLineEdit
|
||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import tweaks, prefs
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import dt_factory, qt_to_dt, isoformat
|
||||
from calibre.utils.pyparsing import ParseException
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
@ -222,10 +221,7 @@ class CcBoolDelegate(QStyledItemDelegate):
|
||||
QStyledItemDelegate.__init__(self, parent)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
m = index.model()
|
||||
col = m.column_map[index.column()]
|
||||
editor = QCheckBox(parent)
|
||||
val = m.db.data[index.row()][m.db.FIELD_MAP[m.custom_columns[col]['num']]]
|
||||
if tweaks['bool_custom_columns_are_tristate'] == 'no':
|
||||
pass
|
||||
else:
|
||||
|
@ -8,7 +8,6 @@ Browsing book collection by tags.
|
||||
'''
|
||||
|
||||
from itertools import izip
|
||||
from copy import copy
|
||||
|
||||
from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
|
||||
QFont, SIGNAL, QSize, QIcon, QPoint, \
|
||||
@ -435,4 +434,4 @@ class TagsModel(QAbstractItemModel):
|
||||
continue
|
||||
tags_seen.append(tag.name)
|
||||
ans.append('%s%s:"=%s"'%(prefix, category, tag.name))
|
||||
return ans
|
||||
return ans
|
||||
|
@ -62,8 +62,6 @@ from calibre.library.caches import CoverCache
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.dialogs.tag_categories import TagCategories
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
class SaveMenu(QMenu):
|
||||
|
||||
def __init__(self, parent):
|
||||
@ -129,7 +127,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
pixmap_to_data(pixmap))
|
||||
|
||||
def __init__(self, listener, opts, actions, parent=None):
|
||||
self.last_time = datetime.now()
|
||||
self.last_time = datetime.datetime.now()
|
||||
self.preferences_action, self.quit_action = actions
|
||||
self.spare_servers = []
|
||||
self.must_restart_before_config = False
|
||||
@ -2167,7 +2165,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
return
|
||||
for row in rows:
|
||||
path = self.library_view.model().db.abspath(row.row())
|
||||
QDesktopServices.openUrl(QUrl('file:'+path))
|
||||
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
||||
|
||||
|
||||
def view_book(self, triggered):
|
||||
|
@ -13,7 +13,7 @@ from datetime import timedelta
|
||||
from PyQt4.QtCore import QThread, QReadWriteLock
|
||||
from PyQt4.QtGui import QImage
|
||||
|
||||
from calibre.utils.config import tweaks, prefs
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import parse_date, now
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
from calibre.utils.pyparsing import ParseException
|
||||
@ -541,4 +541,4 @@ class ResultCache(SearchQueryParser):
|
||||
return []
|
||||
|
||||
def set_search_restriction(self, s):
|
||||
self.search_restriction = '' if not s else 'search:"%s"' % (s.strip())
|
||||
self.search_restriction = '' if not s else 'search:"%s"' % (s.strip())
|
||||
|
@ -54,7 +54,7 @@ def set_metadata(stream, mi):
|
||||
|
||||
while True:
|
||||
try:
|
||||
ret = p.wait()
|
||||
p.wait()
|
||||
break
|
||||
except OSError, e:
|
||||
if e.errno == errno.EINTR:
|
||||
|
Loading…
x
Reference in New Issue
Block a user