mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-09-29 15:31:08 -04:00
364 lines
14 KiB
Python
364 lines
14 KiB
Python
#!/usr/bin/env python
|
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
|
|
|
__license__ = 'GPL v3'
|
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
|
__docformat__ = 'restructuredtext en'
|
|
|
|
from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
|
|
|
|
from calibre.customize.ui import is_disabled
|
|
from calibre.gui2 import error_dialog
|
|
from calibre.gui2.device import device_name_for_plugboards
|
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
|
|
from calibre.gui2.preferences.plugboard_ui import Ui_Form
|
|
from calibre.customize.ui import metadata_writers, device_plugins
|
|
from calibre.library.save_to_disk import plugboard_any_format_value, \
|
|
plugboard_any_device_value, plugboard_save_to_disk_value
|
|
from calibre.library.server.content import plugboard_content_server_value, \
|
|
plugboard_content_server_formats
|
|
from calibre.utils.formatter import validation_formatter
|
|
|
|
|
|
class LineEditWithTextBox(QLineEdit):
|
|
|
|
'''
|
|
Extend the context menu of a QLineEdit to include more actions.
|
|
'''
|
|
|
|
def contextMenuEvent(self, event):
|
|
menu = self.createStandardContextMenu()
|
|
menu.addSeparator()
|
|
|
|
action_open_editor = menu.addAction(_('Open Editor'))
|
|
|
|
self.connect(action_open_editor, SIGNAL('triggered()'), self.open_editor)
|
|
menu.exec_(event.globalPos())
|
|
|
|
def open_editor(self):
|
|
t = TemplateDialog(self, self.text())
|
|
if t.exec_():
|
|
self.setText(t.textbox.toPlainText())
|
|
|
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|
|
|
def genesis(self, gui):
|
|
self.gui = gui
|
|
self.db = gui.library_view.model().db
|
|
self.current_plugboards = self.db.prefs.get('plugboards',{})
|
|
self.current_device = None
|
|
self.current_format = None
|
|
|
|
def initialize(self):
|
|
def field_cmp(x, y):
|
|
if x.startswith('#'):
|
|
if y.startswith('#'):
|
|
return cmp(x.lower(), y.lower())
|
|
else:
|
|
return 1
|
|
elif y.startswith('#'):
|
|
return -1
|
|
else:
|
|
return cmp(x.lower(), y.lower())
|
|
|
|
ConfigWidgetBase.initialize(self)
|
|
|
|
if self.gui.device_manager.connected_device is not None:
|
|
self.device_label.setText(_('Device currently connected: ') +
|
|
self.gui.device_manager.connected_device.__class__.__name__)
|
|
else:
|
|
self.device_label.setText(_('Device currently connected: None'))
|
|
|
|
self.devices = ['', 'APPLE', 'FOLDER_DEVICE']
|
|
self.device_to_formats_map = {}
|
|
for device in device_plugins():
|
|
n = device_name_for_plugboards(device)
|
|
self.device_to_formats_map[n] = device.FORMATS
|
|
if n not in self.devices:
|
|
self.devices.append(n)
|
|
self.devices.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
|
|
self.devices.insert(1, plugboard_save_to_disk_value)
|
|
self.devices.insert(1, plugboard_content_server_value)
|
|
self.device_to_formats_map[plugboard_content_server_value] = \
|
|
plugboard_content_server_formats
|
|
self.devices.insert(1, plugboard_any_device_value)
|
|
self.new_device.addItems(self.devices)
|
|
|
|
self.formats = ['']
|
|
for w in metadata_writers():
|
|
if not is_disabled(w):
|
|
for f in w.file_types:
|
|
if not f in self.formats:
|
|
self.formats.append(f)
|
|
self.formats.append('device_db')
|
|
self.formats.sort()
|
|
self.formats.insert(1, plugboard_any_format_value)
|
|
self.new_format.addItems(self.formats)
|
|
|
|
self.dest_fields = ['',
|
|
'authors', 'author_sort', 'language', 'publisher',
|
|
'tags', 'title', 'title_sort']
|
|
|
|
self.source_widgets = []
|
|
self.dest_widgets = []
|
|
for i in range(0, len(self.dest_fields)-1):
|
|
w = LineEditWithTextBox(self)
|
|
self.source_widgets.append(w)
|
|
self.fields_layout.addWidget(w, 5+i, 0, 1, 1)
|
|
w = QComboBox(self)
|
|
self.dest_widgets.append(w)
|
|
self.fields_layout.addWidget(w, 5+i, 1, 1, 1)
|
|
|
|
self.edit_device.currentIndexChanged[str].connect(self.edit_device_changed)
|
|
self.edit_format.currentIndexChanged[str].connect(self.edit_format_changed)
|
|
self.new_device.currentIndexChanged[str].connect(self.new_device_changed)
|
|
self.new_format.currentIndexChanged[str].connect(self.new_format_changed)
|
|
self.existing_plugboards.itemClicked.connect(self.existing_pb_clicked)
|
|
self.ok_button.clicked.connect(self.ok_clicked)
|
|
self.del_button.clicked.connect(self.del_clicked)
|
|
|
|
self.refilling = False
|
|
self.refill_all_boxes()
|
|
|
|
def clear_fields(self, edit_boxes=False, new_boxes=False):
|
|
self.ok_button.setEnabled(False)
|
|
self.del_button.setEnabled(False)
|
|
for w in self.source_widgets:
|
|
w.clear()
|
|
for w in self.dest_widgets:
|
|
w.clear()
|
|
if edit_boxes:
|
|
self.edit_device.setCurrentIndex(0)
|
|
self.edit_format.setCurrentIndex(0)
|
|
if new_boxes:
|
|
self.new_device.setCurrentIndex(0)
|
|
self.new_format.setCurrentIndex(0)
|
|
|
|
def set_fields(self):
|
|
self.ok_button.setEnabled(True)
|
|
self.del_button.setEnabled(True)
|
|
for w in self.source_widgets:
|
|
w.clear()
|
|
for w in self.dest_widgets:
|
|
w.addItems(self.dest_fields)
|
|
|
|
def set_field(self, i, src, dst):
|
|
self.source_widgets[i].setText(src)
|
|
idx = self.dest_fields.index(dst)
|
|
self.dest_widgets[i].setCurrentIndex(idx)
|
|
|
|
def edit_device_changed(self, txt):
|
|
self.current_device = None
|
|
if txt == '':
|
|
self.clear_fields(new_boxes=False)
|
|
return
|
|
self.clear_fields(new_boxes=True)
|
|
self.current_device = unicode(txt)
|
|
fpb = self.current_plugboards.get(self.current_format, None)
|
|
if fpb is None:
|
|
print 'edit_device_changed: none format!'
|
|
return
|
|
dpb = fpb.get(self.current_device, None)
|
|
if dpb is None:
|
|
print 'edit_device_changed: none device!'
|
|
return
|
|
self.set_fields()
|
|
for i,op in enumerate(dpb):
|
|
self.set_field(i, op[0], op[1])
|
|
self.ok_button.setEnabled(True)
|
|
self.del_button.setEnabled(True)
|
|
|
|
def edit_format_changed(self, txt):
|
|
self.edit_device.setCurrentIndex(0)
|
|
self.current_device = None
|
|
self.current_format = None
|
|
if txt == '':
|
|
self.clear_fields(new_boxes=False)
|
|
return
|
|
self.clear_fields(new_boxes=True)
|
|
txt = unicode(txt)
|
|
fpb = self.current_plugboards.get(txt, None)
|
|
if fpb is None:
|
|
print 'edit_format_changed: none editable format!'
|
|
return
|
|
self.current_format = txt
|
|
devices = ['']
|
|
for d in fpb:
|
|
devices.append(d)
|
|
self.edit_device.clear()
|
|
self.edit_device.addItems(devices)
|
|
|
|
def new_device_changed(self, txt):
|
|
self.current_device = None
|
|
if txt == '':
|
|
self.clear_fields(edit_boxes=False)
|
|
return
|
|
self.clear_fields(edit_boxes=True)
|
|
self.current_device = unicode(txt)
|
|
error = False
|
|
if self.current_format == plugboard_any_format_value:
|
|
# user specified any format.
|
|
for f in self.current_plugboards:
|
|
devs = set(self.current_plugboards[f])
|
|
if self.current_device != plugboard_save_to_disk_value and \
|
|
plugboard_any_device_value in devs:
|
|
# specific format/any device in list. conflict.
|
|
# note: any device does not match save_to_disk
|
|
error = True
|
|
break
|
|
if self.current_device in devs:
|
|
# specific format/current device in list. conflict
|
|
error = True
|
|
break
|
|
if self.current_device == plugboard_any_device_value:
|
|
# any device and a specific device already there. conflict
|
|
error = True
|
|
break
|
|
else:
|
|
# user specified specific format.
|
|
for f in self.current_plugboards:
|
|
devs = set(self.current_plugboards[f])
|
|
if f == plugboard_any_format_value and \
|
|
self.current_device in devs:
|
|
# any format/same device in list. conflict.
|
|
error = True
|
|
break
|
|
if f == self.current_format and self.current_device in devs:
|
|
# current format/current device in list. conflict
|
|
error = True
|
|
break
|
|
if f == self.current_format and plugboard_any_device_value in devs:
|
|
# current format/any device in list. conflict
|
|
error = True
|
|
break
|
|
|
|
if error:
|
|
error_dialog(self, '',
|
|
_('That format and device already has a plugboard or '
|
|
'conflicts with another plugboard.'),
|
|
show=True)
|
|
self.new_device.setCurrentIndex(0)
|
|
return
|
|
if self.current_device in self.device_to_formats_map:
|
|
allowable_formats = self.device_to_formats_map[self.current_device]
|
|
if self.current_format not in allowable_formats:
|
|
error_dialog(self, '',
|
|
_('The {0} device does not support the {1} format.').
|
|
format(self.current_device, self.current_format),
|
|
show=True)
|
|
self.new_device.setCurrentIndex(0)
|
|
return
|
|
self.set_fields()
|
|
|
|
def new_format_changed(self, txt):
|
|
self.current_format = None
|
|
self.current_device = None
|
|
self.new_device.setCurrentIndex(0)
|
|
if txt:
|
|
self.clear_fields(edit_boxes=True)
|
|
self.current_format = unicode(txt)
|
|
else:
|
|
self.clear_fields(edit_boxes=False)
|
|
|
|
def ok_clicked(self):
|
|
pb = []
|
|
for i in range(0, len(self.source_widgets)):
|
|
s = unicode(self.source_widgets[i].text())
|
|
if s:
|
|
d = self.dest_widgets[i].currentIndex()
|
|
if d != 0:
|
|
try:
|
|
validation_formatter.validate(s)
|
|
except Exception as err:
|
|
error_dialog(self, _('Invalid template'),
|
|
'<p>'+_('The template %s is invalid:')%s + \
|
|
'<br>'+str(err), show=True)
|
|
return
|
|
pb.append((s, self.dest_fields[d]))
|
|
else:
|
|
error_dialog(self, _('Invalid destination'),
|
|
'<p>'+_('The destination field cannot be blank'),
|
|
show=True)
|
|
return
|
|
if len(pb) == 0:
|
|
if self.current_format in self.current_plugboards:
|
|
fpb = self.current_plugboards[self.current_format]
|
|
if self.current_device in fpb:
|
|
del fpb[self.current_device]
|
|
if len(fpb) == 0:
|
|
del self.current_plugboards[self.current_format]
|
|
else:
|
|
if self.current_format not in self.current_plugboards:
|
|
self.current_plugboards[self.current_format] = {}
|
|
fpb = self.current_plugboards[self.current_format]
|
|
fpb[self.current_device] = pb
|
|
self.changed_signal.emit()
|
|
self.refill_all_boxes()
|
|
|
|
def del_clicked(self):
|
|
if self.current_format in self.current_plugboards:
|
|
fpb = self.current_plugboards[self.current_format]
|
|
if self.current_device in fpb:
|
|
del fpb[self.current_device]
|
|
if len(fpb) == 0:
|
|
del self.current_plugboards[self.current_format]
|
|
self.changed_signal.emit()
|
|
self.refill_all_boxes()
|
|
|
|
def existing_pb_clicked(self, Qitem):
|
|
item = Qitem.data(Qt.UserRole).toPyObject()
|
|
self.edit_format.setCurrentIndex(self.edit_format.findText(item[0]))
|
|
self.edit_device.setCurrentIndex(self.edit_device.findText(item[1]))
|
|
|
|
def refill_all_boxes(self):
|
|
if self.refilling:
|
|
return
|
|
self.refilling = True
|
|
self.current_device = None
|
|
self.current_format = None
|
|
self.clear_fields(new_boxes=True)
|
|
self.edit_format.clear()
|
|
self.edit_format.addItem('')
|
|
for format in self.current_plugboards:
|
|
self.edit_format.addItem(format)
|
|
self.edit_format.setCurrentIndex(0)
|
|
self.edit_device.clear()
|
|
self.ok_button.setEnabled(False)
|
|
self.del_button.setEnabled(False)
|
|
self.existing_plugboards.clear()
|
|
for f in self.formats:
|
|
if f not in self.current_plugboards:
|
|
continue
|
|
for d in self.devices:
|
|
if d not in self.current_plugboards[f]:
|
|
continue
|
|
ops = []
|
|
for op in self.current_plugboards[f][d]:
|
|
ops.append('([' + op[0] + '] -> ' + op[1] + ')')
|
|
txt = '%s:%s = %s\n'%(f, d, ', '.join(ops))
|
|
item = QListWidgetItem(txt)
|
|
item.setData(Qt.UserRole, (f, d))
|
|
self.existing_plugboards.addItem(item)
|
|
self.refilling = False
|
|
|
|
def restore_defaults(self):
|
|
ConfigWidgetBase.restore_defaults(self)
|
|
self.current_plugboards = {}
|
|
self.refill_all_boxes()
|
|
self.changed_signal.emit()
|
|
|
|
def commit(self):
|
|
self.db.prefs.set('plugboards', self.current_plugboards)
|
|
return ConfigWidgetBase.commit(self)
|
|
|
|
def refresh_gui(self, gui):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
from PyQt4.Qt import QApplication
|
|
app = QApplication([])
|
|
test_widget('Import/Export', 'plugboards')
|
|
|