diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 7c522bc67..210dabdd2 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -27,6 +27,8 @@ """ The :mod:`advancedtab` provides an advanced settings facility. """ +from datetime import datetime, timedelta + from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate, build_icon, Receiver @@ -43,12 +45,26 @@ class AdvancedTab(SettingsTab): """ Initialise the settings tab """ - self.display_changed = False - advancedTranslated = translate('OpenLP.AdvancedTab', 'Advanced') - self.default_image = u':/graphics/openlp-splash-screen.png' - self.default_color = u'#ffffff' + self.displayChanged = False + # 7 stands for now, 0 to 6 is Monday to Sunday. + self.defaultServiceDay = 7 + # 11 o'clock is the most popular time for morning service. + self.defaultServiceHour = 11 + self.defaultServiceMinute = 0 + self.defaultServiceName = unicode(translate('OpenLP.AdvancedTab', + 'Service %Y-%m-%d %H-%M', + 'This is the default default service name template, which can be ' + 'found under Advanced in Settings, Configure OpenLP. Please do not ' + 'include any of the following characters: /\\?*|<>\[\]":+\n' + 'You can use any of the directives as shown on page ' + 'http://docs.python.org/library/datetime.html' + '#strftime-strptime-behavior , but if possible, please keep ' + 'the resulting string sortable by name.')) + self.defaultImage = u':/graphics/openlp-splash-screen.png' + self.defaultColor = u'#ffffff' self.icon_path = u':/system/system_settings.png' - SettingsTab.__init__(self, parent, u'Advanced', advancedTranslated) + advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced') + SettingsTab.__init__(self, parent, u'Advanced', advanced_translated) def setupUi(self): """ @@ -85,6 +101,61 @@ class AdvancedTab(SettingsTab): u'enableAutoCloseCheckBox') self.uiLayout.addRow(self.enableAutoCloseCheckBox) self.leftLayout.addWidget(self.uiGroupBox) + # Default service name + self.serviceNameGroupBox = QtGui.QGroupBox(self.leftColumn) + self.serviceNameGroupBox.setObjectName(u'serviceNameGroupBox') + self.serviceNameLayout = QtGui.QFormLayout( + self.serviceNameGroupBox) + self.serviceNameCheckBox = QtGui.QCheckBox( + self.serviceNameGroupBox) + self.serviceNameCheckBox.setObjectName(u'serviceNameCheckBox') + self.serviceNameLayout.setObjectName(u'serviceNameLayout') + self.serviceNameLayout.addRow(self.serviceNameCheckBox) + self.serviceNameTimeLabel = QtGui.QLabel(self.serviceNameGroupBox) + self.serviceNameTimeLabel.setObjectName(u'serviceNameTimeLabel') + self.serviceNameDay = QtGui.QComboBox( + self.serviceNameGroupBox) + self.serviceNameDay.addItems( + [u'', u'', u'', u'', u'', u'', u'', u'']) + self.serviceNameDay.setObjectName( + u'serviceNameDay') + self.serviceNameTime = QtGui.QTimeEdit(self.serviceNameGroupBox) + self.serviceNameTime.setObjectName(u'serviceNameTime') + self.serviceNameTimeHBox = QtGui.QHBoxLayout() + self.serviceNameTimeHBox.setObjectName(u'serviceNameTimeHBox') + self.serviceNameTimeHBox.addWidget(self.serviceNameDay) + self.serviceNameTimeHBox.addWidget(self.serviceNameTime) + self.serviceNameLayout.addRow(self.serviceNameTimeLabel, + self.serviceNameTimeHBox) + self.serviceNameLabel = QtGui.QLabel(self.serviceNameGroupBox) + self.serviceNameLabel.setObjectName(u'serviceNameLabel') + self.serviceNameEdit = QtGui.QLineEdit(self.serviceNameGroupBox) + self.serviceNameEdit.setObjectName(u'serviceNameEdit') + self.serviceNameEdit.setValidator(QtGui.QRegExpValidator( + QtCore.QRegExp(r'[^/\\?*|<>\[\]":+]+'), self)) + self.serviceNameRevertButton = QtGui.QToolButton( + self.serviceNameGroupBox) + self.serviceNameRevertButton.setObjectName( + u'serviceNameRevertButton') + self.serviceNameRevertButton.setIcon( + build_icon(u':/general/general_revert.png')) + self.serviceNameHBox = QtGui.QHBoxLayout() + self.serviceNameHBox.setObjectName(u'serviceNameHBox') + self.serviceNameHBox.addWidget(self.serviceNameEdit) + self.serviceNameHBox.addWidget(self.serviceNameRevertButton) + self.serviceNameLayout.addRow(self.serviceNameLabel, + self.serviceNameHBox) + self.serviceNameExampleLabel = QtGui.QLabel( + self.serviceNameGroupBox) + self.serviceNameExampleLabel.setObjectName( + u'serviceNameExampleLabel') + self.serviceNameExample = QtGui.QLabel(self.serviceNameGroupBox) + self.serviceNameExample.setObjectName(u'serviceNameExample') + self.serviceNameLayout.addRow(self.serviceNameExampleLabel, + self.serviceNameExample) + self.leftLayout.addWidget(self.serviceNameGroupBox) + self.leftLayout.addStretch() + # Default Image self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn) self.defaultImageGroupBox.setObjectName(u'defaultImageGroupBox') self.defaultImageLayout = QtGui.QFormLayout(self.defaultImageGroupBox) @@ -114,16 +185,16 @@ class AdvancedTab(SettingsTab): self.defaultFileLayout.addWidget(self.defaultRevertButton) self.defaultImageLayout.addRow(self.defaultFileLabel, self.defaultFileLayout) - self.leftLayout.addWidget(self.defaultImageGroupBox) - self.hideMouseGroupBox = QtGui.QGroupBox(self.leftColumn) + self.rightLayout.addWidget(self.defaultImageGroupBox) + # Hide mouse + self.hideMouseGroupBox = QtGui.QGroupBox(self.rightColumn) self.hideMouseGroupBox.setObjectName(u'hideMouseGroupBox') self.hideMouseLayout = QtGui.QVBoxLayout(self.hideMouseGroupBox) self.hideMouseLayout.setObjectName(u'hideMouseLayout') self.hideMouseCheckBox = QtGui.QCheckBox(self.hideMouseGroupBox) self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox') self.hideMouseLayout.addWidget(self.hideMouseCheckBox) - self.leftLayout.addWidget(self.hideMouseGroupBox) - self.leftLayout.addStretch() + self.rightLayout.addWidget(self.hideMouseGroupBox) # Service Item Slide Limits self.slideGroupBox = QtGui.QGroupBox(self.rightColumn) self.slideGroupBox.setObjectName(u'slideGroupBox') @@ -165,6 +236,21 @@ class AdvancedTab(SettingsTab): self.rightLayout.addWidget(self.x11GroupBox) self.rightLayout.addStretch() + self.shouldUpdateServiceNameExample = False + QtCore.QObject.connect(self.serviceNameCheckBox, + QtCore.SIGNAL(u'toggled(bool)'), self.serviceNameCheckBoxToggled) + QtCore.QObject.connect(self.serviceNameDay, + QtCore.SIGNAL(u'currentIndexChanged(int)'), + self.onServiceNameDayChanged) + QtCore.QObject.connect(self.serviceNameTime, + QtCore.SIGNAL(u'timeChanged(QTime)'), + self.updateServiceNameExample) + QtCore.QObject.connect(self.serviceNameEdit, + QtCore.SIGNAL(u'textChanged(QString)'), + self.updateServiceNameExample) + QtCore.QObject.connect(self.serviceNameRevertButton, + QtCore.SIGNAL(u'pressed()'), + self.onServiceNameRevertButtonPressed) QtCore.QObject.connect(self.defaultColorButton, QtCore.SIGNAL(u'pressed()'), self.onDefaultColorButtonPressed) QtCore.QObject.connect(self.defaultBrowseButton, @@ -200,6 +286,40 @@ class AdvancedTab(SettingsTab): 'Expand new service items on creation')) self.enableAutoCloseCheckBox.setText(translate('OpenLP.AdvancedTab', 'Enable application exit confirmation')) + self.serviceNameGroupBox.setTitle( + translate('OpenLP.AdvancedTab', 'Default Service Name')) + self.serviceNameCheckBox.setText( + translate('OpenLP.AdvancedTab', 'Enable default service name')) + self.serviceNameTimeLabel.setText( + translate('OpenLP.AdvancedTab', 'Date and Time:')) + self.serviceNameDay.setItemText(0, + translate('OpenLP.AdvancedTab', 'Monday')) + self.serviceNameDay.setItemText(1, + translate('OpenLP.AdvancedTab', 'Tuesday')) + self.serviceNameDay.setItemText(2, + translate('OpenLP.AdvancedTab', 'Wednesday')) + self.serviceNameDay.setItemText(3, + translate('OpenLP.AdvancedTab', 'Thurdsday')) + self.serviceNameDay.setItemText(4, + translate('OpenLP.AdvancedTab', 'Friday')) + self.serviceNameDay.setItemText(5, + translate('OpenLP.AdvancedTab', 'Saturday')) + self.serviceNameDay.setItemText(6, + translate('OpenLP.AdvancedTab', 'Sunday')) + self.serviceNameDay.setItemText(7, + translate('OpenLP.AdvancedTab', 'Now')) + self.serviceNameTime.setToolTip(translate('OpenLP.AdvancedTab', + 'Time when usual service starts.')) + self.serviceNameLabel.setText( + translate('OpenLP.AdvancedTab', 'Name:')) + self.serviceNameEdit.setToolTip(translate('OpenLP.AdvancedTab', + 'Consult the OpenLP manual for usage.')) + self.serviceNameRevertButton.setToolTip(unicode( + translate('OpenLP.AdvancedTab', + 'Revert to the default service name "%s".')) % + self.defaultServiceName) + self.serviceNameExampleLabel.setText(translate('OpenLP.AdvancedTab', + 'Example:')) self.hideMouseGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'Mouse Cursor')) self.hideMouseCheckBox.setText(translate('OpenLP.AdvancedTab', @@ -270,9 +390,24 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(True)).toBool()) self.hideMouseCheckBox.setChecked( settings.value(u'hide mouse', QtCore.QVariant(False)).toBool()) + self.serviceNameDay.setCurrentIndex( + settings.value(u'default service day', + QtCore.QVariant(self.defaultServiceDay)).toInt()[0]) + self.serviceNameTime.setTime(QtCore.QTime( + settings.value(u'default service hour', + self.defaultServiceHour).toInt()[0], + settings.value(u'default service minute', + self.defaultServiceMinute).toInt()[0])) + self.shouldUpdateServiceNameExample = True + self.serviceNameEdit.setText(settings.value(u'default service name', + self.defaultServiceName).toString()) + default_service_enabled = settings.value(u'default service enabled', + QtCore.QVariant(True)).toBool() + self.serviceNameCheckBox.setChecked(default_service_enabled) + self.serviceNameCheckBoxToggled(default_service_enabled) self.x11BypassCheckBox.setChecked( settings.value(u'x11 bypass wm', QtCore.QVariant(True)).toBool()) - self.default_color = settings.value(u'default color', + self.defaultColor = settings.value(u'default color', QtCore.QVariant(u'#ffffff')).toString() self.defaultFileEdit.setText(settings.value(u'default image', QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\ @@ -287,7 +422,7 @@ class AdvancedTab(SettingsTab): self.nextItemRadioButton.setChecked(True) settings.endGroup() self.defaultColorButton.setStyleSheet( - u'background-color: %s' % self.default_color) + u'background-color: %s' % self.defaultColor) def save(self): """ @@ -295,6 +430,21 @@ class AdvancedTab(SettingsTab): """ settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) + settings.setValue(u'default service enabled', + self.serviceNameCheckBox.isChecked()) + service_name = unicode(self.serviceNameEdit.text()) + preset_is_valid = self.generateServiceNameExample()[0] + if service_name == self.defaultServiceName or not preset_is_valid: + settings.remove(u'default service name') + self.serviceNameEdit.setText(service_name) + else: + settings.setValue(u'default service name', service_name) + settings.setValue(u'default service day', + self.serviceNameDay.currentIndex()) + settings.setValue(u'default service hour', + self.serviceNameTime.time().hour()) + settings.setValue(u'default service minute', + self.serviceNameTime.time().minute()) settings.setValue(u'recent file count', QtCore.QVariant(self.recentSpinBox.value())) settings.setValue(u'save current plugin', @@ -311,22 +461,65 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(self.hideMouseCheckBox.isChecked())) settings.setValue(u'x11 bypass wm', QtCore.QVariant(self.x11BypassCheckBox.isChecked())) - settings.setValue(u'default color', self.default_color) + settings.setValue(u'default color', self.defaultColor) settings.setValue(u'default image', self.defaultFileEdit.text()) settings.setValue(u'slide limits', QtCore.QVariant(self.slide_limits)) settings.endGroup() - if self.display_changed: + if self.displayChanged: Receiver.send_message(u'config_screen_changed') - self.display_changed = False + self.displayChanged = False Receiver.send_message(u'slidecontroller_update_slide_limits') + def serviceNameCheckBoxToggled(self, default_service_enabled): + self.serviceNameDay.setEnabled(default_service_enabled) + time_enabled = default_service_enabled and \ + self.serviceNameDay.currentIndex() is not 7 + self.serviceNameTime.setEnabled(time_enabled) + self.serviceNameEdit.setEnabled(default_service_enabled) + self.serviceNameRevertButton.setEnabled(default_service_enabled) + + def generateServiceNameExample(self): + preset_is_valid = True + if self.serviceNameDay.currentIndex() == 7: + time = datetime.now() + else: + now = datetime.now() + day_delta = self.serviceNameDay.currentIndex() - now.weekday() + if day_delta < 0: + day_delta += 7 + time = now + timedelta(days=day_delta) + time = time.replace(hour = self.serviceNameTime.time().hour(), + minute = self.serviceNameTime.time().minute()) + try: + service_name_example = time.strftime(unicode( + self.serviceNameEdit.text())) + except ValueError: + preset_is_valid = False + service_name_example = translate('OpenLP.AdvancedTab', + 'Syntax error.') + return preset_is_valid, service_name_example + + def updateServiceNameExample(self, returned_value): + if not self.shouldUpdateServiceNameExample: + return + name_example = self.generateServiceNameExample()[1] + self.serviceNameExample.setText(name_example) + + def onServiceNameDayChanged(self, service_day): + self.serviceNameTime.setEnabled(service_day is not 7) + self.updateServiceNameExample(None) + + def onServiceNameRevertButtonPressed(self): + self.serviceNameEdit.setText(self.defaultServiceName) + self.serviceNameEdit.setFocus() + def onDefaultColorButtonPressed(self): new_color = QtGui.QColorDialog.getColor( - QtGui.QColor(self.default_color), self) + QtGui.QColor(self.defaultColor), self) if new_color.isValid(): - self.default_color = new_color.name() + self.defaultColor = new_color.name() self.defaultColorButton.setStyleSheet( - u'background-color: %s' % self.default_color) + u'background-color: %s' % self.defaultColor) def onDefaultBrowseButtonPressed(self): file_filters = u'%s;;%s (*.*) (*)' % (get_images_filter(), @@ -349,7 +542,7 @@ class AdvancedTab(SettingsTab): ``checked`` The state of the check box (boolean). """ - self.display_changed = True + self.displayChanged = True def onEndSlideButtonPressed(self): self.slide_limits = SlideLimits.End diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index f2c4a7702..2217dc168 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -31,6 +31,7 @@ import os import shutil import zipfile from tempfile import mkstemp +from datetime import datetime, timedelta log = logging.getLogger(__name__) @@ -612,10 +613,44 @@ class ServiceManager(QtGui.QWidget): Get a file name and then call :func:`ServiceManager.saveFile` to save the file. """ + default_service_enabled = QtCore.QSettings().value( + u'advanced/default service enabled', QtCore.QVariant(True)).toBool() + if default_service_enabled: + service_day = QtCore.QSettings().value( + u'advanced/default service day', 7).toInt()[0] + if service_day == 7: + time = datetime.now() + else: + service_hour = QtCore.QSettings().value( + u'advanced/default service hour', 11).toInt()[0] + service_minute = QtCore.QSettings().value( + u'advanced/default service minute', 0).toInt()[0] + now = datetime.now() + day_delta = service_day - now.weekday() + if day_delta < 0: + day_delta += 7 + time = now + timedelta(days=day_delta) + time = time.replace(hour=service_hour, minute=service_minute) + default_pattern = unicode(QtCore.QSettings().value( + u'advanced/default service name', + translate('OpenLP.AdvancedTab', + 'Service %Y-%m-%d %H-%M', + 'This is the default default service name template, which can ' + 'be found under Advanced in Settings, Configure OpenLP. ' + 'Please do not include any of the following characters: ' + '/\\?*|<>\[\]":+\n' + 'You can use any of the directives as shown on page ' + 'http://docs.python.org/library/datetime.html' + '#strftime-strptime-behavior , but if possible, please keep ' + 'the resulting string sortable by name.')).toString()) + default_filename = time.strftime(default_pattern) + else: + default_filename = u'' + directory = unicode(SettingsManager.get_last_dir( + self.mainwindow.servicemanagerSettingsSection)) + path = os.path.join(directory, default_filename) fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - UiStrings().SaveService, - SettingsManager.get_last_dir( - self.mainwindow.servicemanagerSettingsSection), + UiStrings().SaveService, path, translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) if not fileName: return False diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 0539aef0c..6189f108f 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -31,8 +31,15 @@ plugin. import logging import re +from PyQt4 import QtCore + +from openlp.core.lib import translate + log = logging.getLogger(__name__) +REFERENCE_MATCHES = {} +REFERENCE_SEPARATORS = {} + class LayoutStyle(object): """ An enumeration for bible screen layout styles. @@ -52,39 +59,85 @@ class DisplayStyle(object): Square = 3 +def update_reference_separators(): + """ + Updates separators and matches for parsing and formating scripture + references. + """ + default_separators = unicode(translate('BiblesPlugin', + ':|v|V|verse|verses;;-|to;;,|and;;end', + 'This are 4 values seperated each by two semicolons representing the ' + 'seperators for parsing references. This values are verse separators, ' + 'range separators, list separators and end mark. Alternative values ' + 'to be seperated by a vertical bar "|". In this case the first value ' + 'is the default and used for the display format. If a semicolon should ' + 'be used you have to give an alternative separator afterwards to allow ' + 'OpenLP correct splitting of the translation.')).split(u';;') + settings = QtCore.QSettings() + settings.beginGroup(u'bibles') + custom_separators = [ + unicode(settings.value(u'verse separator').toString()), + unicode(settings.value(u'range separator').toString()), + unicode(settings.value(u'list separator').toString()), + unicode(settings.value(u'end separator').toString())] + settings.endGroup() + for index, role in enumerate([u'v', u'r', u'l', u'e']): + if custom_separators[index].strip(u'|') == u'': + source_string = default_separators[index].strip(u'|') + else: + source_string = custom_separators[index].strip(u'|') + while u'||' in source_string: + source_string = source_string.replace(u'||', u'|') + if role != u'e': + REFERENCE_SEPARATORS[u'sep_%s_display' % role] = \ + source_string.split(u'|')[0] + # escape reserved characters + for character in u'\\.^$*+?{}[]()': + source_string = source_string.replace(character, u'\\' + character) + # add various unicode alternatives + source_string = source_string.replace(u'-', + u'(?:[-\u00AD\u2010\u2011\u2012\u2013\u2014\u2212\uFE63\uFF0D])') + source_string = source_string.replace(u',', u'(?:[,\u201A])') + REFERENCE_SEPARATORS[u'sep_%s' % role] = u'\s*(?:%s)\s*' % source_string + REFERENCE_SEPARATORS[u'sep_%s_default' % role] = \ + default_separators[index] + # verse range match: (:)?(-((:)?|end)?)? + range_regex = u'(?:(?P[0-9]+)%(sep_v)s)?' \ + u'(?P[0-9]+)(?P%(sep_r)s(?:(?:(?P' \ + u'[0-9]+)%(sep_v)s)?(?P[0-9]+)|%(sep_e)s)?)?' % \ + REFERENCE_SEPARATORS + REFERENCE_MATCHES[u'range'] = re.compile(u'^\s*%s\s*$' % range_regex, + re.UNICODE) + REFERENCE_MATCHES[u'range_separator'] = re.compile( + REFERENCE_SEPARATORS[u'sep_l'], re.UNICODE) + # full reference match: ((,(?!$)|(?=$)))+ + REFERENCE_MATCHES[u'full'] = re.compile( + u'^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' \ + % dict(REFERENCE_SEPARATORS.items() + [(u'range_regex', range_regex)]), + re.UNICODE) + +def get_reference_separator(separator_type): + """ + Provides separators for parsing and formatting scripture references. + + ``separator_type`` + The role and format of the separator. + """ + if len(REFERENCE_SEPARATORS) == 0: + update_reference_separators() + return REFERENCE_SEPARATORS[separator_type] + def get_reference_match(match_type): """ - Provides the regexes and matches to use while parsing strings for bible - references. + Provides matches for parsing scripture references strings. ``match_type`` - The type of reference information trying to be extracted in this call. + The type of match is ``range_separator``, ``range`` or ``full``. """ - local_separator = unicode(u':;;\s*[:vV]\s*;;-;;\s*-\s*;;,;;\s*,\s*;;end' - ).split(u';;') # English - # local_separator = unicode(u',;;\s*,\s*;;-;;\s*-\s*;;.;;\.;;[Ee]nde' - # ).split(u';;') # German - separators = { - u'sep_v_display': local_separator[0], u'sep_v': local_separator[1], - u'sep_r_display': local_separator[2], u'sep_r': local_separator[3], - u'sep_l_display': local_separator[4], u'sep_l': local_separator[5], - u'sep_e': local_separator[6]} - - # verse range match: (:)?(-((:)?|end)?)? - range_string = str(r'(?:(?P[0-9]+)%(sep_v)s)?(?P' - r'[0-9]+)(?P%(sep_r)s(?:(?:(?P[0-9]+)%(sep_v)s)?' - r'(?P[0-9]+)|%(sep_e)s)?)?') % separators - if match_type == u'range': - return re.compile(r'^\s*' + range_string + r'\s*$', re.UNICODE) - elif match_type == u'range_separator': - return re.compile(separators[u'sep_l']) - elif match_type == u'full': - # full reference match: ((,|(?=$)))+ - return re.compile(str(r'^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:' + range_string + r'(?:%(sep_l)s|(?=\s*$)))+)\s*$') - % separators, re.UNICODE) - else: - return separators[match_type] + if len(REFERENCE_MATCHES) == 0: + update_reference_separators() + return REFERENCE_MATCHES[match_type] def parse_reference(reference): """ @@ -140,7 +193,7 @@ def parse_reference(reference): If there is a range separator without further verse declaration the last refered chapter is addressed until the end. - ``range_string`` is a regular expression which matches for verse range + ``range_regex`` is a regular expression which matches for verse range declarations: ``(?:(?P[0-9]+)%(sep_v)s)?`` @@ -169,9 +222,9 @@ def parse_reference(reference): are optional leading digits followed by non-digits. The group ends before the whitspace in front of the next digit. - ``(?P(?:`` + range_string + ``(?:%(sep_l)s|(?=\s*$)))+)\s*$`` + ``(?P(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$`` The second group contains all ``ranges``. This can be multiple - declarations of a range_string separated by a list separator. + declarations of range_regex separated by a list separator. """ log.debug(u'parse_reference("%s")', reference) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 8402d6e11..e8e0d9e2c 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -30,8 +30,9 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsTab, translate -from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle from openlp.core.lib.ui import UiStrings, find_and_set_in_combo_box +from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \ + update_reference_separators, get_reference_separator log = logging.getLogger(__name__) @@ -90,6 +91,55 @@ class BiblesTab(SettingsTab): self.changeNoteLabel.setObjectName(u'changeNoteLabel') self.verseDisplayLayout.addRow(self.changeNoteLabel) self.leftLayout.addWidget(self.verseDisplayGroupBox) + self.scriptureReferenceGroupBox = QtGui.QGroupBox(self.leftColumn) + self.scriptureReferenceGroupBox.setObjectName( + u'scriptureReferenceGroupBox') + self.scriptureReferenceLayout = QtGui.QGridLayout( + self.scriptureReferenceGroupBox) + self.verseSeparatorCheckBox = QtGui.QCheckBox( + self.scriptureReferenceGroupBox) + self.verseSeparatorCheckBox.setObjectName(u'verseSeparatorCheckBox') + self.scriptureReferenceLayout.addWidget(self.verseSeparatorCheckBox, 0, + 0) + self.verseSeparatorLineEdit = QtGui.QLineEdit( + self.scriptureReferenceGroupBox) +# self.verseSeparatorLineEdit.setPalette + self.verseSeparatorLineEdit.setObjectName(u'verseSeparatorLineEdit') + self.scriptureReferenceLayout.addWidget(self.verseSeparatorLineEdit, 0, + 1) + self.rangeSeparatorCheckBox = QtGui.QCheckBox( + self.scriptureReferenceGroupBox) + self.rangeSeparatorCheckBox.setObjectName(u'rangeSeparatorCheckBox') + self.scriptureReferenceLayout.addWidget(self.rangeSeparatorCheckBox, 1, + 0) + self.rangeSeparatorLineEdit = QtGui.QLineEdit( + self.scriptureReferenceGroupBox) + self.rangeSeparatorLineEdit.setObjectName(u'rangeSeparatorLineEdit') + self.scriptureReferenceLayout.addWidget(self.rangeSeparatorLineEdit, 1, + 1) + self.listSeparatorCheckBox = QtGui.QCheckBox( + self.scriptureReferenceGroupBox) + self.listSeparatorCheckBox.setObjectName(u'listSeparatorCheckBox') + self.scriptureReferenceLayout.addWidget(self.listSeparatorCheckBox, 2, + 0) + self.listSeparatorLineEdit = QtGui.QLineEdit( + self.scriptureReferenceGroupBox) + self.listSeparatorLineEdit.setObjectName(u'listSeparatorLineEdit') + self.scriptureReferenceLayout.addWidget(self.listSeparatorLineEdit, 2, + 1) + self.endSeparatorCheckBox = QtGui.QCheckBox( + self.scriptureReferenceGroupBox) + self.endSeparatorCheckBox.setObjectName(u'endSeparatorCheckBox') + self.scriptureReferenceLayout.addWidget(self.endSeparatorCheckBox, 3, + 0) + self.endSeparatorLineEdit = QtGui.QLineEdit( + self.scriptureReferenceGroupBox) + self.endSeparatorLineEdit.setObjectName(u'endSeparatorLineEdit') + self.endSeparatorLineEdit.setValidator(QtGui.QRegExpValidator( + QtCore.QRegExp(r'[^0-9]*'), self.endSeparatorLineEdit)) + self.scriptureReferenceLayout.addWidget(self.endSeparatorLineEdit, 3, + 1) + self.leftLayout.addWidget(self.scriptureReferenceGroupBox) self.leftLayout.addStretch() self.rightColumn.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) @@ -110,6 +160,42 @@ class BiblesTab(SettingsTab): QtCore.QObject.connect( self.bibleSecondCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), self.onBibleSecondCheckBox) + QtCore.QObject.connect( + self.verseSeparatorCheckBox, QtCore.SIGNAL(u'clicked(bool)'), + self.onVerseSeparatorCheckBoxClicked) + QtCore.QObject.connect( + self.verseSeparatorLineEdit, QtCore.SIGNAL(u'textEdited(QString)'), + self.onVerseSeparatorLineEditEdited) + QtCore.QObject.connect( + self.verseSeparatorLineEdit, QtCore.SIGNAL(u'editingFinished()'), + self.onVerseSeparatorLineEditFinished) + QtCore.QObject.connect( + self.rangeSeparatorCheckBox, QtCore.SIGNAL(u'clicked(bool)'), + self.onRangeSeparatorCheckBoxClicked) + QtCore.QObject.connect( + self.rangeSeparatorLineEdit, QtCore.SIGNAL(u'textEdited(QString)'), + self.onRangeSeparatorLineEditEdited) + QtCore.QObject.connect( + self.rangeSeparatorLineEdit, QtCore.SIGNAL(u'editingFinished()'), + self.onRangeSeparatorLineEditFinished) + QtCore.QObject.connect( + self.listSeparatorCheckBox, QtCore.SIGNAL(u'clicked(bool)'), + self.onListSeparatorCheckBoxClicked) + QtCore.QObject.connect( + self.listSeparatorLineEdit, QtCore.SIGNAL(u'textEdited(QString)'), + self.onListSeparatorLineEditEdited) + QtCore.QObject.connect( + self.listSeparatorLineEdit, QtCore.SIGNAL(u'editingFinished()'), + self.onListSeparatorLineEditFinished) + QtCore.QObject.connect( + self.endSeparatorCheckBox, QtCore.SIGNAL(u'clicked(bool)'), + self.onEndSeparatorCheckBoxClicked) + QtCore.QObject.connect( + self.endSeparatorLineEdit, QtCore.SIGNAL(u'textEdited(QString)'), + self.onEndSeparatorLineEditEdited) + QtCore.QObject.connect( + self.endSeparatorLineEdit, QtCore.SIGNAL(u'editingFinished()'), + self.onEndSeparatorLineEditFinished) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList) @@ -141,6 +227,36 @@ class BiblesTab(SettingsTab): 'Note:\nChanges do not affect verses already in the service.')) self.bibleSecondCheckBox.setText( translate('BiblesPlugin.BiblesTab', 'Display second Bible verses')) + self.scriptureReferenceGroupBox.setTitle( + translate('BiblesPlugin.BiblesTab', 'Custom Scripture References')) + self.verseSeparatorCheckBox.setText( + translate('BiblesPlugin.BiblesTab', 'Verse Separator:')) + self.rangeSeparatorCheckBox.setText( + translate('BiblesPlugin.BiblesTab', 'Range Separator:')) + self.listSeparatorCheckBox.setText( + translate('BiblesPlugin.BiblesTab', 'List Separator:')) + self.endSeparatorCheckBox.setText( + translate('BiblesPlugin.BiblesTab', 'End Mark:')) + self.verseSeparatorLineEdit.setToolTip( + translate('BiblesPlugin.BiblesTab', 'Multiple alternative ' + 'verse separators may be defined.\nThey have to be separated ' + 'by a vertical bar "|".\nPlease clear this edit line to use ' + 'the default value.')) + self.rangeSeparatorLineEdit.setToolTip( + translate('BiblesPlugin.BiblesTab', 'Multiple alternative ' + 'range separators may be defined.\nThey have to be separated ' + 'by a vertical bar "|".\nPlease clear this edit line to use ' + 'the default value.')) + self.listSeparatorLineEdit.setToolTip( + translate('BiblesPlugin.BiblesTab', 'Multiple alternative ' + 'list separators may be defined.\nThey have to be separated ' + 'by a vertical bar "|".\nPlease clear this edit line to use ' + 'the default value.')) + self.endSeparatorLineEdit.setToolTip( + translate('BiblesPlugin.BiblesTab', 'Multiple alternative ' + 'end marks may be defined.\nThey have to be separated by a ' + 'vertical bar "|".\nPlease clear this edit line to use the ' + 'default value.')) def onBibleThemeComboBoxChanged(self): self.bible_theme = self.bibleThemeComboBox.currentText() @@ -163,6 +279,106 @@ class BiblesTab(SettingsTab): if check_state == QtCore.Qt.Checked: self.second_bibles = True + def onVerseSeparatorCheckBoxClicked(self, checked): + if checked: + self.verseSeparatorLineEdit.setFocus() + else: + self.verseSeparatorLineEdit.setText( + get_reference_separator(u'sep_v_default')) + self.verseSeparatorLineEdit.setPalette( + self.getGreyTextPalette(not checked)) + + def onVerseSeparatorLineEditEdited(self, text): + self.verseSeparatorCheckBox.setChecked(True) + self.verseSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + + def onVerseSeparatorLineEditFinished(self): + if self.verseSeparatorLineEdit.isModified(): + text = self.verseSeparatorLineEdit.text() + if text == get_reference_separator(u'sep_v_default') or \ + text.remove(u'|').isEmpty(): + self.verseSeparatorCheckBox.setChecked(False) + self.verseSeparatorLineEdit.setText( + get_reference_separator(u'sep_v_default')) + self.verseSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + + def onRangeSeparatorCheckBoxClicked(self, checked): + if checked: + self.rangeSeparatorLineEdit.setFocus() + else: + self.rangeSeparatorLineEdit.setText( + get_reference_separator(u'sep_r_default')) + self.rangeSeparatorLineEdit.setPalette( + self.getGreyTextPalette(not checked)) + + def onRangeSeparatorLineEditEdited(self, text): + self.rangeSeparatorCheckBox.setChecked(True) + self.rangeSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + + def onRangeSeparatorLineEditFinished(self): + if self.rangeSeparatorLineEdit.isModified(): + text = self.rangeSeparatorLineEdit.text() + if text == get_reference_separator(u'sep_r_default') or \ + text.remove(u'|').isEmpty(): + self.rangeSeparatorCheckBox.setChecked(False) + self.rangeSeparatorLineEdit.setText( + get_reference_separator(u'sep_r_default')) + self.rangeSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + + def onListSeparatorCheckBoxClicked(self, checked): + if checked: + self.listSeparatorLineEdit.setFocus() + else: + self.listSeparatorLineEdit.setText( + get_reference_separator(u'sep_l_default')) + self.listSeparatorLineEdit.setPalette( + self.getGreyTextPalette(not checked)) + + def onListSeparatorLineEditEdited(self, text): + self.listSeparatorCheckBox.setChecked(True) + self.listSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + + def onListSeparatorLineEditFinished(self): + if self.listSeparatorLineEdit.isModified(): + text = self.listSeparatorLineEdit.text() + if text == get_reference_separator(u'sep_l_default') or \ + text.remove(u'|').isEmpty(): + self.listSeparatorCheckBox.setChecked(False) + self.listSeparatorLineEdit.setText( + get_reference_separator(u'sep_l_default')) + self.listSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + + def onEndSeparatorCheckBoxClicked(self, checked): + if checked: + self.endSeparatorLineEdit.setFocus() + else: + self.endSeparatorLineEdit.setText( + get_reference_separator(u'sep_e_default')) + self.endSeparatorLineEdit.setPalette( + self.getGreyTextPalette(not checked)) + + def onEndSeparatorLineEditEdited(self, text): + self.endSeparatorCheckBox.setChecked(True) + self.endSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + + def onEndSeparatorLineEditFinished(self): + if self.endSeparatorLineEdit.isModified(): + text = self.endSeparatorLineEdit.text() + if text == get_reference_separator(u'sep_e_default') or \ + text.remove(u'|').isEmpty(): + self.endSeparatorCheckBox.setChecked(False) + self.endSeparatorLineEdit.setText( + get_reference_separator(u'sep_e_default')) + self.endSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + def load(self): settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) @@ -180,6 +396,58 @@ class BiblesTab(SettingsTab): self.displayStyleComboBox.setCurrentIndex(self.display_style) self.layoutStyleComboBox.setCurrentIndex(self.layout_style) self.bibleSecondCheckBox.setChecked(self.second_bibles) + verse_separator = unicode(settings.value(u'verse separator').toString()) + if (verse_separator.strip(u'|') == u'') or \ + (verse_separator == get_reference_separator(u'sep_v_default')): + self.verseSeparatorLineEdit.setText( + get_reference_separator(u'sep_v_default')) + self.verseSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + self.verseSeparatorCheckBox.setChecked(False) + else: + self.verseSeparatorLineEdit.setText(verse_separator) + self.verseSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + self.verseSeparatorCheckBox.setChecked(True) + range_separator = unicode(settings.value(u'range separator').toString()) + if (range_separator.strip(u'|') == u'') or \ + (range_separator == get_reference_separator(u'sep_r_default')): + self.rangeSeparatorLineEdit.setText( + get_reference_separator(u'sep_r_default')) + self.rangeSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + self.rangeSeparatorCheckBox.setChecked(False) + else: + self.rangeSeparatorLineEdit.setText(range_separator) + self.rangeSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + self.rangeSeparatorCheckBox.setChecked(True) + list_separator = unicode(settings.value(u'list separator').toString()) + if (list_separator.strip(u'|') == u'') or \ + (list_separator == get_reference_separator(u'sep_l_default')): + self.listSeparatorLineEdit.setText( + get_reference_separator(u'sep_l_default')) + self.listSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + self.listSeparatorCheckBox.setChecked(False) + else: + self.listSeparatorLineEdit.setText(list_separator) + self.listSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + self.listSeparatorCheckBox.setChecked(True) + end_separator = unicode(settings.value(u'end separator').toString()) + if (end_separator.strip(u'|') == u'') or \ + (end_separator == get_reference_separator(u'sep_e_default')): + self.endSeparatorLineEdit.setText( + get_reference_separator(u'sep_e_default')) + self.endSeparatorLineEdit.setPalette( + self.getGreyTextPalette(True)) + self.endSeparatorCheckBox.setChecked(False) + else: + self.endSeparatorLineEdit.setText(end_separator) + self.endSeparatorLineEdit.setPalette( + self.getGreyTextPalette(False)) + self.endSeparatorCheckBox.setChecked(True) settings.endGroup() def save(self): @@ -193,6 +461,27 @@ class BiblesTab(SettingsTab): QtCore.QVariant(self.layout_style)) settings.setValue(u'second bibles', QtCore.QVariant(self.second_bibles)) settings.setValue(u'bible theme', QtCore.QVariant(self.bible_theme)) + if self.verseSeparatorCheckBox.isChecked(): + settings.setValue(u'verse separator', + self.verseSeparatorLineEdit.text()) + else: + settings.remove(u'verse separator') + if self.rangeSeparatorCheckBox.isChecked(): + settings.setValue(u'range separator', + self.rangeSeparatorLineEdit.text()) + else: + settings.remove(u'range separator') + if self.listSeparatorCheckBox.isChecked(): + settings.setValue(u'list separator', + self.listSeparatorLineEdit.text()) + else: + settings.remove(u'list separator') + if self.endSeparatorCheckBox.isChecked(): + settings.setValue(u'end separator', + self.endSeparatorLineEdit.text()) + else: + settings.remove(u'end separator') + update_reference_separators() settings.endGroup() def updateThemeList(self, theme_list): @@ -209,3 +498,15 @@ class BiblesTab(SettingsTab): for theme in theme_list: self.bibleThemeComboBox.addItem(theme) find_and_set_in_combo_box(self.bibleThemeComboBox, self.bible_theme) + + def getGreyTextPalette(self, greyed): + """ + Returns a QPalette with greyed out text as used for placeholderText. + """ + palette = QtGui.QPalette() + color = self.palette().color(QtGui.QPalette.Active, QtGui.QPalette.Text) + if greyed: + color.setAlpha(128) + palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) + return palette + diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 873b42cee..c231f2062 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore from openlp.core.lib import Receiver, SettingsManager, translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import AppLocation, delete_file -from openlp.plugins.bibles.lib import parse_reference +from openlp.plugins.bibles.lib import parse_reference, get_reference_separator from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from csvbible import CSVBible from http import HTTPBible @@ -309,19 +309,29 @@ class BibleManager(object): return self.db_cache[bible].get_verses(reflist, show_error) else: if show_error: + reference_seperators = { + u'verse': get_reference_separator(u'sep_v_display'), + u'range': get_reference_separator(u'sep_r_display'), + u'list': get_reference_separator(u'sep_l_display')} Receiver.send_message(u'openlp_information_message', { u'title': translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'), - u'message': translate('BiblesPlugin.BibleManager', + u'message': unicode(translate('BiblesPlugin.BibleManager', 'Your scripture reference is either not supported by ' 'OpenLP or is invalid. Please make sure your reference ' - 'conforms to one of the following patterns:\n\n' + 'conforms to one of the following patterns or consult the ' + 'manual:\n\n' 'Book Chapter\n' - 'Book Chapter-Chapter\n' - 'Book Chapter:Verse-Verse\n' - 'Book Chapter:Verse-Verse,Verse-Verse\n' - 'Book Chapter:Verse-Verse,Chapter:Verse-Verse\n' - 'Book Chapter:Verse-Chapter:Verse') + 'Book Chapter%(range)sChapter\n' + 'Book Chapter%(verse)sVerse%(range)sVerse\n' + 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sVerse' + '%(range)sVerse\n' + 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sChapter' + '%(verse)sVerse%(range)sVerse\n' + 'Book Chapter%(verse)sVerse%(range)sChapter%(verse)sVerse', + 'Please pay attention to the appended "s" of the wildcards ' + 'and refrain from translating the words inside the ' + 'names in the brackets.')) % reference_seperators }) return None diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index d7b3fff01..5ec4daa50 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -38,7 +38,7 @@ from openlp.core.lib.ui import UiStrings, add_widget_completer, \ find_and_set_in_combo_box, build_icon from openlp.plugins.bibles.forms import BibleImportForm from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \ - VerseReferenceList, get_reference_match + VerseReferenceList, get_reference_separator log = logging.getLogger(__name__) @@ -634,8 +634,8 @@ class BibleMediaItem(MediaManagerItem): chapter_to = self.advancedToChapter.currentText() verse_from = self.advancedFromVerse.currentText() verse_to = self.advancedToVerse.currentText() - verse_separator = get_reference_match(u'sep_v_display') - range_separator = get_reference_match(u'sep_r_display') + verse_separator = get_reference_separator(u'sep_v_display') + range_separator = get_reference_separator(u'sep_r_display') verse_range = chapter_from + verse_separator + verse_from + \ range_separator + chapter_to + verse_separator + verse_to versetext = u'%s %s' % (book, verse_range) @@ -737,7 +737,7 @@ class BibleMediaItem(MediaManagerItem): Displays the search results in the media manager. All data needed for further action is saved for/in each row. """ - verse_separator = get_reference_match(u'sep_v_display') + verse_separator = get_reference_separator(u'sep_v_display') version = self.plugin.manager.get_meta_data(bible, u'Version').value copyright = self.plugin.manager.get_meta_data(bible, u'Copyright').value permissions = \ @@ -890,8 +890,8 @@ class BibleMediaItem(MediaManagerItem): ``old_item`` The last item of a range. """ - verse_separator = get_reference_match(u'sep_v_display') - range_separator = get_reference_match(u'sep_r_display') + verse_separator = get_reference_separator(u'sep_v_display') + range_separator = get_reference_separator(u'sep_r_display') old_chapter = self._decodeQtObject(old_bitem, 'chapter') old_verse = self._decodeQtObject(old_bitem, 'verse') start_book = self._decodeQtObject(start_bitem, 'book') @@ -971,7 +971,7 @@ class BibleMediaItem(MediaManagerItem): ``verse`` The verse number (int). """ - verse_separator = get_reference_match(u'sep_v_display') + verse_separator = get_reference_separator(u'sep_v_display') if not self.settings.show_new_chapters or old_chapter != chapter: verse_text = unicode(chapter) + verse_separator + unicode(verse) else: