Merge from custcol trunk

This commit is contained in:
Charles Haley 2010-05-01 22:59:01 +01:00
commit eda505f755
16 changed files with 570 additions and 586 deletions

View File

@ -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):

View File

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

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

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

View File

@ -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>

View File

@ -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

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

View File

@ -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>

View File

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

View File

@ -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:

View File

@ -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

View File

@ -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):

View File

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

View File

@ -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: