Head 1924

This commit is contained in:
M2j 2012-04-01 18:41:37 +02:00
commit dbfeb49572
84 changed files with 68388 additions and 23979 deletions

View File

@ -21,3 +21,5 @@ openlp/core/resources.py.old
*.qm
resources/windows/warnOpenLP.txt
openlp.cfg
.idea
openlp.pro

View File

@ -33,7 +33,8 @@ from urllib import quote_plus as urlquote
from PyQt4 import QtCore
from sqlalchemy import Table, MetaData, Column, types, create_engine
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, \
OperationalError
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.pool import NullPool

View File

@ -217,6 +217,9 @@ class EventReceiver(QtCore.QObject):
Ask the plugin to process an individual service item after it has been
loaded.
``{plugin}_config_updated``
The config has changed so tell the plugin about it.
``alerts_text``
Displays an alert message.

View File

@ -458,13 +458,18 @@ def build_lyrics_format_css(theme, width, height):
# fix tag incompatibilities
if theme.display_horizontal_align == HorizontalType.Justify:
justify = u''
if theme.display_vertical_align == VerticalType.Bottom:
padding_bottom = u'0.5em'
else:
padding_bottom = u'0'
lyrics = u'%s word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
'padding: 0; padding-left: %spx; width: %spx; height: %spx; ' % \
'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx;' \
'height: %spx; ' % \
(justify, align, valign, theme.font_main_name, theme.font_main_size,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
left_margin, width, height)
padding_bottom, left_margin, width, height)
if theme.font_main_outline:
if webkit_version() <= 534.3:
lyrics += u' letter-spacing: 1px;'

View File

@ -166,7 +166,7 @@ class ImageManager(QtCore.QObject):
self.height = current_screen[u'size'].height()
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
for key, image in self._cache.iteritems():
for image in self._cache.values():
self._reset_image(image)
def update_images(self, image_type, background):
@ -176,7 +176,7 @@ class ImageManager(QtCore.QObject):
log.debug(u'update_images')
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
for key, image in self._cache.iteritems():
for image in self._cache.values():
if image.source == image_type:
image.background = background
self._reset_image(image)
@ -188,7 +188,7 @@ class ImageManager(QtCore.QObject):
log.debug(u'update_images')
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
for key, image in self._cache.iteritems():
for image in self._cache.values():
if image.source == image_type and image.name == name:
image.background = background
self._reset_image(image)

View File

@ -641,7 +641,7 @@ class MediaManagerItem(QtGui.QWidget):
if item:
self.autoSelectId = item.data(QtCore.Qt.UserRole).toInt()[0]
def search(self, string):
def search(self, string, showError=True):
"""
Performs a plugin specific search for items containing ``string``
"""

View File

@ -173,6 +173,9 @@ class Plugin(QtCore.QObject):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
self.processAddServiceEvent)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_config_updated' % self.name),
self.configUpdated)
def checkPreConditions(self):
"""
@ -395,3 +398,9 @@ class Plugin(QtCore.QObject):
Add html code to htmlbuilder.
"""
return u''
def configUpdated(self):
"""
The plugin's config has changed
"""
pass

View File

@ -289,7 +289,7 @@ class Renderer(object):
def _calculate_default(self):
"""
Calculate the default dimentions of the screen.
Calculate the default dimensions of the screen.
"""
screen_size = self.screens.current[u'size']
self.width = screen_size.width()
@ -380,6 +380,7 @@ class Renderer(object):
(build_lyrics_format_css(self.theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(self.theme_data))
self.web.setHtml(html)
self.empty_height = self.web_frame.contentsSize().height()
def _paginate_slide(self, lines, line_end):
"""
@ -600,7 +601,7 @@ class Renderer(object):
"""
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
return self.web_frame.contentsSize().height() <= self.page_height
return self.web_frame.contentsSize().height() <= self.empty_height
def _words_split(self, line):
"""

View File

@ -122,6 +122,13 @@ class SearchEdit(QtGui.QLineEdit):
menu = self.menuButton.menu()
for action in menu.actions():
if identifier == action.data().toInt()[0]:
# setPlaceholderText has been implemented in Qt 4.7 and in at
# least PyQt 4.9 (I am not sure, if it was implemented in
# PyQt 4.8).
try:
self.setPlaceholderText(action.placeholderText)
except AttributeError:
pass
self.menuButton.setDefaultAction(action)
self._currentSearchType = identifier
self.emit(QtCore.SIGNAL(u'searchTypeChanged(int)'), identifier)
@ -137,21 +144,22 @@ class SearchEdit(QtGui.QLineEdit):
identifier, an icon (QIcon instance or string) and a title for the
item in the menu. In short, they should look like this::
(<identifier>, <icon>, <title>)
(<identifier>, <icon>, <title>, <place holder text>)
For instance::
(1, <QIcon instance>, "Titles")
(1, <QIcon instance>, "Titles", "Search Song Titles...")
Or::
(2, ":/songs/authors.png", "Authors")
(2, ":/songs/authors.png", "Authors", "Search Authors...")
"""
menu = QtGui.QMenu(self)
first = None
for identifier, icon, title in items:
for identifier, icon, title, placeholder in items:
action = create_widget_action(menu, text=title, icon=icon,
data=identifier, triggers=self._onMenuActionTriggered)
action.placeholderText = placeholder
if first is None:
first = action
self._currentSearchType = identifier
@ -202,5 +210,12 @@ class SearchEdit(QtGui.QLineEdit):
action.setChecked(False)
self.menuButton.setDefaultAction(sender)
self._currentSearchType = sender.data().toInt()[0]
# setPlaceholderText has been implemented in Qt 4.7 and in at least
# PyQt 4.9 (I am not sure, if it was implemented in PyQt 4.8).
try:
self.setPlaceholderText(
self.menuButton.defaultAction().placeholderText)
except AttributeError:
pass
self.emit(QtCore.SIGNAL(u'searchTypeChanged(int)'),
self._currentSearchType)

View File

@ -300,6 +300,7 @@ class ServiceItem(object):
``path``
Defaults to *None*. Any path data, usually for images.
"""
log.debug(u'set_from_service called with path %s' % path)
header = serviceitem[u'serviceitem'][u'header']
self.title = header[u'title']
self.name = header[u'name']
@ -325,7 +326,10 @@ class ServiceItem(object):
if u'media_length' in header:
self.media_length = header[u'media_length']
if u'background_audio' in header:
self.background_audio = header[u'background_audio']
self.background_audio = []
for filename in header[u'background_audio']:
# Give them real file paths
self.background_audio.append(os.path.join(path, filename))
self.theme_overwritten = header.get(u'theme_overwritten', False)
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:

View File

@ -115,6 +115,8 @@ class UiStrings(object):
'The abbreviated unit for seconds')
self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview')
self.Search = translate('OpenLP.Ui', 'Search')
self.SearchThemes = translate(
'OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ')
self.SelectDelete = translate('OpenLP.Ui', 'You must select an item '
'to delete.')
self.SelectEdit = translate('OpenLP.Ui', 'You must select an item to '
@ -374,7 +376,7 @@ def create_widget_action(parent, name=u'', **kwargs):
The shortcut context defaults to ``QtCore.Qt.WidgetShortcut`` and the action
is added to the parents action list.
"""
kwargs.setdefault(u'context', QtCore.Qt.WidgetShortcut)
kwargs.setdefault(u'context', QtCore.Qt.WidgetShortcut)
action = create_action(parent, name, **kwargs)
parent.addAction(action)
return action

View File

@ -53,13 +53,10 @@ class AdvancedTab(SettingsTab):
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.'))
'This may not contain any of the following characters: '
'/\\?*|<>\[\]":+\n'
'See http://docs.python.org/library/datetime.html'
'#strftime-strptime-behavior for more information.'))
self.defaultImage = u':/graphics/openlp-splash-screen.png'
self.defaultColor = u'#ffffff'
self.icon_path = u':/system/system_settings.png'
@ -357,7 +354,7 @@ class AdvancedTab(SettingsTab):
translate('OpenLP.GeneralTab', '&Next Item'))
self.nextItemLabel.setText(
translate('OpenLP.GeneralTab', 'Up and down arrow keys '
'advance to the the next or previous Service Item from the '
'advance to the next or previous Service Item from the '
'top and bottom slides of each Service Item.'))
def load(self):

View File

@ -53,60 +53,64 @@ class GeneralTab(SettingsTab):
"""
self.setObjectName(u'GeneralTab')
SettingsTab.setupUi(self)
self.tabLayout.setStretch(1, 1)
# Monitors
self.monitorGroupBox = QtGui.QGroupBox(self.leftColumn)
self.monitorGroupBox.setObjectName(u'monitorGroupBox')
self.monitorLayout = QtGui.QFormLayout(self.monitorGroupBox)
self.monitorLayout = QtGui.QGridLayout(self.monitorGroupBox)
self.monitorLayout.setObjectName(u'monitorLayout')
self.monitorLabel = QtGui.QLabel(self.monitorGroupBox)
self.monitorLabel.setObjectName(u'monitorLabel')
self.monitorLayout.addRow(self.monitorLabel)
self.monitorRadioButton = QtGui.QRadioButton(self.monitorGroupBox)
self.monitorRadioButton.setObjectName(u'monitorRadioButton')
self.monitorLayout.addWidget(self.monitorRadioButton, 0, 0, 1, 5)
self.monitorComboBox = QtGui.QComboBox(self.monitorGroupBox)
self.monitorComboBox.setObjectName(u'monitorComboBox')
self.monitorLayout.addRow(self.monitorComboBox)
self.monitorLayout.addWidget(self.monitorComboBox, 1, 1, 1, 4)
self.displayOnMonitorCheck = QtGui.QCheckBox(self.monitorGroupBox)
self.displayOnMonitorCheck.setObjectName(u'monitorComboBox')
self.monitorLayout.addRow(self.displayOnMonitorCheck)
self.monitorLayout.addWidget(self.displayOnMonitorCheck, 2, 1, 1, 4)
# Display Position
self.overrideRadioButton = QtGui.QRadioButton(self.monitorGroupBox)
self.overrideRadioButton.setObjectName(u'overrideRadioButton')
self.monitorLayout.addWidget(self.overrideRadioButton, 3, 0, 1, 5)
# Custom position
self.customXLabel = QtGui.QLabel(self.monitorGroupBox)
self.customXLabel.setObjectName(u'customXLabel')
self.monitorLayout.addWidget(self.customXLabel, 4, 1)
self.customXValueEdit = QtGui.QSpinBox(self.monitorGroupBox)
self.customXValueEdit.setObjectName(u'customXValueEdit')
self.customXValueEdit.setRange(-9999, 9999)
self.monitorLayout.addWidget(self.customXValueEdit, 5, 1)
self.customYLabel = QtGui.QLabel(self.monitorGroupBox)
self.customYLabel.setObjectName(u'customYLabel')
self.monitorLayout.addWidget(self.customYLabel, 4, 2)
self.customYValueEdit = QtGui.QSpinBox(self.monitorGroupBox)
self.customYValueEdit.setObjectName(u'customYValueEdit')
self.customYValueEdit.setRange(-9999, 9999)
self.monitorLayout.addWidget(self.customYValueEdit, 5, 2)
self.customWidthLabel = QtGui.QLabel(self.monitorGroupBox)
self.customWidthLabel.setObjectName(u'customWidthLabel')
self.monitorLayout.addWidget(self.customWidthLabel, 4, 3)
self.customWidthValueEdit = QtGui.QSpinBox(self.monitorGroupBox)
self.customWidthValueEdit.setObjectName(u'customWidthValueEdit')
self.customWidthValueEdit.setMaximum(9999)
self.monitorLayout.addWidget(self.customWidthValueEdit, 5, 3)
self.customHeightLabel = QtGui.QLabel(self.monitorGroupBox)
self.customHeightLabel.setObjectName(u'customHeightLabel')
self.monitorLayout.addWidget(self.customHeightLabel, 4, 4)
self.customHeightValueEdit = QtGui.QSpinBox(self.monitorGroupBox)
self.customHeightValueEdit.setObjectName(u'customHeightValueEdit')
self.customHeightValueEdit.setMaximum(9999)
self.monitorLayout.addWidget(self.customHeightValueEdit, 5, 4)
# Set up the stretchiness of each column, so that the first column
# less stretchy (and therefore smaller) than the others
self.monitorLayout.setColumnStretch(0, 1)
self.monitorLayout.setColumnStretch(1, 3)
self.monitorLayout.setColumnStretch(2, 3)
self.monitorLayout.setColumnStretch(3, 3)
self.monitorLayout.setColumnStretch(4, 3)
self.leftLayout.addWidget(self.monitorGroupBox)
self.startupGroupBox = QtGui.QGroupBox(self.leftColumn)
self.startupGroupBox.setObjectName(u'startupGroupBox')
self.startupLayout = QtGui.QVBoxLayout(self.startupGroupBox)
self.startupLayout.setObjectName(u'startupLayout')
self.warningCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.warningCheckBox.setObjectName(u'warningCheckBox')
self.startupLayout.addWidget(self.warningCheckBox)
self.autoOpenCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.autoOpenCheckBox.setObjectName(u'autoOpenCheckBox')
self.startupLayout.addWidget(self.autoOpenCheckBox)
self.showSplashCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.showSplashCheckBox.setObjectName(u'showSplashCheckBox')
self.startupLayout.addWidget(self.showSplashCheckBox)
self.checkForUpdatesCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.checkForUpdatesCheckBox.setObjectName(u'checkForUpdatesCheckBox')
self.startupLayout.addWidget(self.checkForUpdatesCheckBox)
self.leftLayout.addWidget(self.startupGroupBox)
self.settingsGroupBox = QtGui.QGroupBox(self.leftColumn)
self.settingsGroupBox.setObjectName(u'settingsGroupBox')
self.settingsLayout = QtGui.QFormLayout(self.settingsGroupBox)
self.settingsLayout.setObjectName(u'settingsLayout')
self.saveCheckServiceCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.saveCheckServiceCheckBox.setObjectName(u'saveCheckServiceCheckBox')
self.settingsLayout.addRow(self.saveCheckServiceCheckBox)
self.autoUnblankCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.autoUnblankCheckBox.setObjectName(u'autoUnblankCheckBox')
self.settingsLayout.addRow(self.autoUnblankCheckBox)
self.autoPreviewCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.autoPreviewCheckBox.setObjectName(u'autoPreviewCheckBox')
self.settingsLayout.addRow(self.autoPreviewCheckBox)
# Moved here from image tab
self.timeoutLabel = QtGui.QLabel(self.settingsGroupBox)
self.timeoutLabel.setObjectName(u'timeoutLabel')
self.timeoutSpinBox = QtGui.QSpinBox(self.settingsGroupBox)
self.timeoutSpinBox.setObjectName(u'timeoutSpinBox')
self.timeoutSpinBox.setRange(1, 180)
self.settingsLayout.addRow(self.timeoutLabel, self.timeoutSpinBox)
self.leftLayout.addWidget(self.settingsGroupBox)
self.leftLayout.addStretch()
self.ccliGroupBox = QtGui.QGroupBox(self.rightColumn)
# CCLI Details
self.ccliGroupBox = QtGui.QGroupBox(self.leftColumn)
self.ccliGroupBox.setObjectName(u'ccliGroupBox')
self.ccliLayout = QtGui.QFormLayout(self.ccliGroupBox)
self.ccliLayout.setObjectName(u'ccliLayout')
@ -127,48 +131,9 @@ class GeneralTab(SettingsTab):
self.passwordEdit.setEchoMode(QtGui.QLineEdit.Password)
self.passwordEdit.setObjectName(u'passwordEdit')
self.ccliLayout.addRow(self.passwordLabel, self.passwordEdit)
self.rightLayout.addWidget(self.ccliGroupBox)
# Moved here from display tab
self.displayGroupBox = QtGui.QGroupBox(self.rightColumn)
self.displayGroupBox.setObjectName(u'displayGroupBox')
self.displayLayout = QtGui.QGridLayout(self.displayGroupBox)
self.displayLayout.setObjectName(u'displayLayout')
self.overrideCheckBox = QtGui.QCheckBox(self.displayGroupBox)
self.overrideCheckBox.setObjectName(u'overrideCheckBox')
self.displayLayout.addWidget(self.overrideCheckBox, 2, 0, 1, 4)
self.rightLayout.addWidget(self.displayGroupBox)
# Custom position
self.customXLabel = QtGui.QLabel(self.displayGroupBox)
self.customXLabel.setObjectName(u'customXLabel')
self.displayLayout.addWidget(self.customXLabel, 3, 0)
self.customXValueEdit = QtGui.QSpinBox(self.displayGroupBox)
self.customXValueEdit.setObjectName(u'customXValueEdit')
self.customXValueEdit.setRange(-9999, 9999)
self.displayLayout.addWidget(self.customXValueEdit, 4, 0)
self.customYLabel = QtGui.QLabel(self.displayGroupBox)
self.customYLabel.setObjectName(u'customYLabel')
self.displayLayout.addWidget(self.customYLabel, 3, 1)
self.customYValueEdit = QtGui.QSpinBox(self.displayGroupBox)
self.customYValueEdit.setObjectName(u'customYValueEdit')
self.customYValueEdit.setRange(-9999, 9999)
self.displayLayout.addWidget(self.customYValueEdit, 4, 1)
self.customWidthLabel = QtGui.QLabel(self.displayGroupBox)
self.customWidthLabel.setObjectName(u'customWidthLabel')
self.displayLayout.addWidget(self.customWidthLabel, 3, 2)
self.customWidthValueEdit = QtGui.QSpinBox(self.displayGroupBox)
self.customWidthValueEdit.setObjectName(u'customWidthValueEdit')
self.customWidthValueEdit.setMaximum(9999)
self.displayLayout.addWidget(self.customWidthValueEdit, 4, 2)
self.customHeightLabel = QtGui.QLabel(self.displayGroupBox)
self.customHeightLabel.setObjectName(u'customHeightLabel')
self.displayLayout.addWidget(self.customHeightLabel, 3, 3)
self.customHeightValueEdit = QtGui.QSpinBox(self.displayGroupBox)
self.customHeightValueEdit.setObjectName(u'customHeightValueEdit')
self.customHeightValueEdit.setMaximum(9999)
self.displayLayout.addWidget(self.customHeightValueEdit, 4, 3)
self.rightLayout.addWidget(self.displayGroupBox)
self.leftLayout.addWidget(self.ccliGroupBox)
# Background audio
self.audioGroupBox = QtGui.QGroupBox(self.rightColumn)
self.audioGroupBox = QtGui.QGroupBox(self.leftColumn)
self.audioGroupBox.setObjectName(u'audioGroupBox')
self.audioLayout = QtGui.QVBoxLayout(self.audioGroupBox)
self.audioLayout.setObjectName(u'audioLayout')
@ -178,11 +143,52 @@ class GeneralTab(SettingsTab):
self.repeatListCheckBox = QtGui.QCheckBox(self.audioGroupBox)
self.repeatListCheckBox.setObjectName(u'repeatListCheckBox')
self.audioLayout.addWidget(self.repeatListCheckBox)
self.rightLayout.addWidget(self.audioGroupBox)
self.leftLayout.addWidget(self.audioGroupBox)
self.leftLayout.addStretch()
# Application Startup
self.startupGroupBox = QtGui.QGroupBox(self.rightColumn)
self.startupGroupBox.setObjectName(u'startupGroupBox')
self.startupLayout = QtGui.QVBoxLayout(self.startupGroupBox)
self.startupLayout.setObjectName(u'startupLayout')
self.warningCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.warningCheckBox.setObjectName(u'warningCheckBox')
self.startupLayout.addWidget(self.warningCheckBox)
self.autoOpenCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.autoOpenCheckBox.setObjectName(u'autoOpenCheckBox')
self.startupLayout.addWidget(self.autoOpenCheckBox)
self.showSplashCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.showSplashCheckBox.setObjectName(u'showSplashCheckBox')
self.startupLayout.addWidget(self.showSplashCheckBox)
self.checkForUpdatesCheckBox = QtGui.QCheckBox(self.startupGroupBox)
self.checkForUpdatesCheckBox.setObjectName(u'checkForUpdatesCheckBox')
self.startupLayout.addWidget(self.checkForUpdatesCheckBox)
self.rightLayout.addWidget(self.startupGroupBox)
# Application Settings
self.settingsGroupBox = QtGui.QGroupBox(self.rightColumn)
self.settingsGroupBox.setObjectName(u'settingsGroupBox')
self.settingsLayout = QtGui.QFormLayout(self.settingsGroupBox)
self.settingsLayout.setObjectName(u'settingsLayout')
self.saveCheckServiceCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.saveCheckServiceCheckBox.setObjectName(u'saveCheckServiceCheckBox')
self.settingsLayout.addRow(self.saveCheckServiceCheckBox)
self.autoUnblankCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.autoUnblankCheckBox.setObjectName(u'autoUnblankCheckBox')
self.settingsLayout.addRow(self.autoUnblankCheckBox)
self.autoPreviewCheckBox = QtGui.QCheckBox(self.settingsGroupBox)
self.autoPreviewCheckBox.setObjectName(u'autoPreviewCheckBox')
self.settingsLayout.addRow(self.autoPreviewCheckBox)
# Moved here from image tab
self.timeoutLabel = QtGui.QLabel(self.settingsGroupBox)
self.timeoutLabel.setObjectName(u'timeoutLabel')
self.timeoutSpinBox = QtGui.QSpinBox(self.settingsGroupBox)
self.timeoutSpinBox.setObjectName(u'timeoutSpinBox')
self.timeoutSpinBox.setRange(1, 180)
self.settingsLayout.addRow(self.timeoutLabel, self.timeoutSpinBox)
self.rightLayout.addWidget(self.settingsGroupBox)
self.rightLayout.addStretch()
# Signals and slots
QtCore.QObject.connect(self.overrideCheckBox,
QtCore.SIGNAL(u'toggled(bool)'), self.onOverrideCheckBoxToggled)
QtCore.QObject.connect(self.overrideRadioButton,
QtCore.SIGNAL(u'toggled(bool)'), self.onOverrideRadioButtonPressed)
QtCore.QObject.connect(self.customHeightValueEdit,
QtCore.SIGNAL(u'valueChanged(int)'), self.onDisplayChanged)
QtCore.QObject.connect(self.customWidthValueEdit,
@ -209,7 +215,7 @@ class GeneralTab(SettingsTab):
self.tabTitleVisible = translate('OpenLP.GeneralTab', 'General')
self.monitorGroupBox.setTitle(translate('OpenLP.GeneralTab',
'Monitors'))
self.monitorLabel.setText(translate('OpenLP.GeneralTab',
self.monitorRadioButton.setText(translate('OpenLP.GeneralTab',
'Select monitor for output display:'))
self.displayOnMonitorCheck.setText(
translate('OpenLP.GeneralTab', 'Display if a single screen'))
@ -242,10 +248,8 @@ class GeneralTab(SettingsTab):
self.passwordLabel.setText(
translate('OpenLP.GeneralTab', 'SongSelect password:'))
# Moved from display tab
self.displayGroupBox.setTitle(
translate('OpenLP.GeneralTab', 'Display Position'))
self.overrideCheckBox.setText(translate('OpenLP.GeneralTab',
'Override display position'))
self.overrideRadioButton.setText(translate('OpenLP.GeneralTab',
'Override display position:'))
self.customXLabel.setText(translate('OpenLP.GeneralTab', 'X'))
self.customYLabel.setText(translate('OpenLP.GeneralTab', 'Y'))
self.customHeightLabel.setText(translate('OpenLP.GeneralTab', 'Height'))
@ -291,7 +295,10 @@ class GeneralTab(SettingsTab):
QtCore.QVariant(False)).toBool())
self.timeoutSpinBox.setValue(settings.value(u'loop delay',
QtCore.QVariant(5)).toInt()[0])
self.overrideCheckBox.setChecked(settings.value(u'override position',
self.monitorRadioButton.setChecked(
not settings.value(u'override position',
QtCore.QVariant(False)).toBool())
self.overrideRadioButton.setChecked(settings.value(u'override position',
QtCore.QVariant(False)).toBool())
self.customXValueEdit.setValue(settings.value(u'x position',
QtCore.QVariant(self.screens.current[u'size'].x())).toInt()[0])
@ -306,10 +313,15 @@ class GeneralTab(SettingsTab):
self.repeatListCheckBox.setChecked(settings.value(
u'audio repeat list', QtCore.QVariant(False)).toBool())
settings.endGroup()
self.customXValueEdit.setEnabled(self.overrideCheckBox.isChecked())
self.customYValueEdit.setEnabled(self.overrideCheckBox.isChecked())
self.customHeightValueEdit.setEnabled(self.overrideCheckBox.isChecked())
self.customWidthValueEdit.setEnabled(self.overrideCheckBox.isChecked())
self.monitorComboBox.setDisabled(self.overrideRadioButton.isChecked())
self.displayOnMonitorCheck.setDisabled(
self.overrideRadioButton.isChecked())
self.customXValueEdit.setEnabled(self.overrideRadioButton.isChecked())
self.customYValueEdit.setEnabled(self.overrideRadioButton.isChecked())
self.customHeightValueEdit.setEnabled(
self.overrideRadioButton.isChecked())
self.customWidthValueEdit.setEnabled(
self.overrideRadioButton.isChecked())
self.display_changed = False
settings.beginGroup(self.settingsSection)
@ -354,7 +366,7 @@ class GeneralTab(SettingsTab):
settings.setValue(u'width',
QtCore.QVariant(self.customWidthValueEdit.value()))
settings.setValue(u'override position',
QtCore.QVariant(self.overrideCheckBox.isChecked()))
QtCore.QVariant(self.overrideRadioButton.isChecked()))
settings.setValue(u'audio start paused',
QtCore.QVariant(self.startPausedCheckBox.isChecked()))
settings.setValue(u'audio repeat list',
@ -380,7 +392,7 @@ class GeneralTab(SettingsTab):
self.customYValueEdit.value(),
self.customWidthValueEdit.value(),
self.customHeightValueEdit.value())
if self.overrideCheckBox.isChecked():
if self.overrideRadioButton.isChecked():
self.screens.set_override_display()
else:
self.screens.reset_current_display()
@ -388,13 +400,15 @@ class GeneralTab(SettingsTab):
Receiver.send_message(u'config_screen_changed')
self.display_changed = False
def onOverrideCheckBoxToggled(self, checked):
def onOverrideRadioButtonPressed(self, checked):
"""
Toggle screen state depending on check box state.
``checked``
The state of the check box (boolean).
"""
self.monitorComboBox.setDisabled(checked)
self.displayOnMonitorCheck.setDisabled(checked)
self.customXValueEdit.setEnabled(checked)
self.customYValueEdit.setEnabled(checked)
self.customHeightValueEdit.setEnabled(checked)

View File

@ -37,6 +37,7 @@ from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
translate, PluginManager, expand_tags
from openlp.core.lib.theme import BackgroundType
from openlp.core.ui import HideMode, ScreenList, AlertLocation
@ -143,7 +144,7 @@ class MainDisplay(Display):
windowFlags |= QtCore.Qt.SplashScreen
self.setWindowFlags(windowFlags)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setTransparency(True)
self.setTransparency(False)
if self.isLive:
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_display_hide'), self.hideDisplay)
@ -387,6 +388,8 @@ class MainDisplay(Display):
# replace the background
background = self.imageManager. \
get_image_bytes(self.override[u'image'])
self.setTransparency(self.serviceItem.themedata.background_type ==
BackgroundType.to_string(BackgroundType.Transparent))
if self.serviceItem.themedata.background_filename:
self.serviceItem.bg_image_bytes = self.imageManager. \
get_image_bytes(self.serviceItem.themedata.theme_name)

View File

@ -24,17 +24,21 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from PyQt4 import QtCore
log = logging.getLogger(__name__)
class MediaState(object):
"""
An enumeration for possible States of the Media Player (copied partially
from Phonon::State)
An enumeration for possible States of the Media Player
"""
Loading = 0
Stopped = 1
Off = 0
Loaded = 1
Playing = 2
Paused = 4
Off = 6
Paused = 3
Stopped = 4
class MediaType(object):
@ -62,4 +66,48 @@ class MediaInfo(object):
end_time = 0
media_type = MediaType()
def get_media_players():
"""
This method extract the configured media players and overridden player from
the settings
``players_list``
this is a python list with all active media players
``overridden_player``
here an special media player is choosen for all media actions
"""
log.debug(u'get_media_players')
players = unicode(QtCore.QSettings().value(u'media/players').toString())
if not players:
players = u'webkit'
reg_ex = QtCore.QRegExp(".*\[(.*)\].*")
if QtCore.QSettings().value(u'media/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0] == QtCore.Qt.Checked:
if reg_ex.exactMatch(players):
overridden_player = u'%s' % reg_ex.cap(1)
else:
overridden_player = u'auto'
else:
overridden_player = u''
players_list = players.replace(u'[', u'').replace(u']', u'').split(u',')
return players_list, overridden_player
def set_media_players(players_list, overridden_player=u'auto'):
"""
This method saves the configured media players and overridden player to the
settings
``players_list``
this is a python list with all active media players
``overridden_player``
here an special media player is choosen for all media actions
"""
log.debug(u'set_media_players')
players = u','.join(players_list)
if QtCore.QSettings().value(u'media/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0] == \
QtCore.Qt.Checked and overridden_player != u'auto':
players = players.replace(overridden_player, u'[%s]' % overridden_player)
QtCore.QSettings().setValue(u'media/players', QtCore.QVariant(players))
from mediacontroller import MediaController

View File

@ -32,7 +32,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import OpenLPToolbar, Receiver, translate
from openlp.core.lib.mediaplayer import MediaPlayer
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, \
get_media_players, set_media_players
from openlp.core.utils import AppLocation
log = logging.getLogger(__name__)
@ -47,7 +48,6 @@ class MediaController(object):
self.parent = parent
self.mediaPlayers = {}
self.controller = []
self.overridenPlayer = ''
self.curDisplayMediaPlayer = {}
# Timer for video state
self.timer = QtCore.QTimer()
@ -58,23 +58,21 @@ class MediaController(object):
QtCore.QObject.connect(self.timer,
QtCore.SIGNAL("timeout()"), self.video_state)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_play'), self.video_play)
QtCore.SIGNAL(u'playbackPlay'), self.video_play)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_pause'), self.video_pause)
QtCore.SIGNAL(u'playbackPause'), self.video_pause)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_playback_stop'), self.video_stop)
QtCore.SIGNAL(u'playbackStop'), self.video_stop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'seek_slider'), self.video_seek)
QtCore.SIGNAL(u'seekSlider'), self.video_seek)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'volume_slider'), self.video_volume)
QtCore.SIGNAL(u'volumeSlider'), self.video_volume)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_hide'), self.video_hide)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_blank'), self.video_blank)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_unblank'), self.video_unblank)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_override_player'), self.override_player)
# Signals for background video
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_hide'), self.video_hide)
@ -84,11 +82,7 @@ class MediaController(object):
QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.set_active_players)
def set_active_players(self):
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
if len(playerSettings) == 0:
playerSettings = u'webkit'
savedPlayers = playerSettings.split(u',')
savedPlayers = get_media_players()[0]
for player in self.mediaPlayers.keys():
if player in savedPlayers:
self.mediaPlayers[player].isActive = True
@ -129,18 +123,14 @@ class MediaController(object):
controller = controller_class(self)
self.register_controllers(controller)
if self.mediaPlayers:
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
savedPlayers = playerSettings.split(u',')
savedPlayers, overriddenPlayer = get_media_players()
invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \
if not mediaPlayer in self.mediaPlayers or \
not self.mediaPlayers[mediaPlayer].check_available()]
if len(invalidMediaPlayers) > 0:
for invalidPlayer in invalidMediaPlayers:
savedPlayers.remove(invalidPlayer)
newPlayerSetting = u','.join(savedPlayers)
QtCore.QSettings().setValue(u'media/players',
QtCore.QVariant(newPlayerSetting))
set_media_players(savedPlayers, overriddenPlayer)
self.set_active_players()
return True
else:
@ -160,6 +150,11 @@ class MediaController(object):
if self.curDisplayMediaPlayer[display] \
.state == MediaState.Playing:
return
# no players are active anymore
for display in self.curDisplayMediaPlayer.keys():
if self.curDisplayMediaPlayer[display] \
.state != MediaState.Paused:
display.controller.seekSlider.setSliderPosition(0)
self.timer.stop()
def get_media_display_css(self):
@ -281,10 +276,9 @@ class MediaController(object):
controller.mediabar.setVisible(value)
if controller.isLive and controller.display:
if self.curDisplayMediaPlayer and value:
if self.curDisplayMediaPlayer[controller.display] != self.mediaPlayers[u'webkit']:
if self.curDisplayMediaPlayer[controller.display] != \
self.mediaPlayers[u'webkit']:
controller.display.setTransparency(False)
else:
controller.display.setTransparency(True)
# Special controls: Here media type specific Controls will be enabled
# (e.g. for DVD control, ...)
# TODO
@ -363,13 +357,9 @@ class MediaController(object):
"""
Select the correct media Player type from the prioritized Player list
"""
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
if QtCore.QSettings().value(u'media/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
if self.overridenPlayer != '':
usedPlayers = [self.overridenPlayer]
usedPlayers, overriddenPlayer = get_media_players()
if overriddenPlayer and overriddenPlayer != u'auto':
usedPlayers = [overriddenPlayer]
if controller.media_info.file_info.isFile():
suffix = u'*.%s' % \
controller.media_info.file_info.suffix().toLower()
@ -453,6 +443,7 @@ class MediaController(object):
display.frame.evaluateJavaScript(u'show_blank("black");')
self.curDisplayMediaPlayer[display].stop(display)
self.curDisplayMediaPlayer[display].set_visible(display, False)
controller.seekSlider.setSliderPosition(0)
def video_volume(self, msg):
"""
@ -579,15 +570,6 @@ class MediaController(object):
video_list.append(item)
return video_list
def override_player(self, override_player):
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
if override_player in usedPlayers:
self.overridenPlayer = override_player
else:
self.overridenPlayer = ''
def finalise(self):
self.timer.stop()
for controller in self.controller:

View File

@ -57,12 +57,14 @@ ADDITIONAL_EXT = {
class PhononPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a Phonon
A specialised version of the MediaPlayer class, which provides a Phonon
display.
"""
def __init__(self, parent):
MediaPlayer.__init__(self, parent, u'phonon')
self.original_name = u'Phonon'
self.display_name = u'&Phonon'
self.parent = parent
self.additional_extensions = ADDITIONAL_EXT
mimetypes.init()
@ -190,6 +192,9 @@ class PhononPlayer(MediaPlayer):
display.phononWidget.setVisible(status)
def update_ui(self, display):
if display.mediaObject.state() == Phonon.PausedState and \
self.state != MediaState.Paused:
self.stop(display)
controller = display.controller
if controller.media_info.end_time > 0:
if display.mediaObject.currentTime() > \

File diff suppressed because it is too large Load Diff

View File

@ -83,12 +83,14 @@ VIDEO_EXT = [
class VlcPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a QtWebKit
A specialised version of the MediaPlayer class, which provides a VLC
display.
"""
def __init__(self, parent):
MediaPlayer.__init__(self, parent, u'vlc')
self.original_name = u'VLC'
self.display_name = u'&VLC'
self.parent = parent
self.canFolder = True
self.audio_extensions_list = AUDIO_EXT
@ -120,7 +122,7 @@ class VlcPlayer(MediaPlayer):
display.vlcMediaPlayer.set_hwnd(int(display.vlcWidget.winId()))
elif sys.platform == "darwin": # for MacOS
display.vlcMediaPlayer.set_agl(int(display.vlcWidget.winId()))
else:
else:
# for Linux using the X Server
display.vlcMediaPlayer.set_xwindow(int(display.vlcWidget.winId()))
self.hasOwnWidget = True
@ -208,6 +210,8 @@ class VlcPlayer(MediaPlayer):
display.vlcWidget.setVisible(status)
def update_ui(self, display):
if display.vlcMedia.get_state() == vlc.State.Ended:
self.stop(display)
controller = display.controller
if controller.media_info.end_time > 0:
if display.vlcMediaPlayer.get_time() > \

View File

@ -126,7 +126,7 @@ VIDEO_JS = u"""
vid.src = '';
vid2.src = '';
break;
case 'length':
case 'length':
return vid.duration;
case 'currentTime':
return vid.currentTime;
@ -134,6 +134,8 @@ VIDEO_JS = u"""
// doesnt work currently
vid.currentTime = varVal;
break;
case 'isEnded':
return vid.ended;
case 'setVisible':
vid.style.visibility = varVal;
break;
@ -211,6 +213,8 @@ FLASH_JS = u"""
case 'seek':
// flashMovie.GotoFrame(varVal);
break;
case 'isEnded':
return false;//TODO check flash end
case 'setVisible':
text.style.visibility = varVal;
break;
@ -254,12 +258,14 @@ AUDIO_EXT = [
class WebkitPlayer(MediaPlayer):
"""
A specialised version of the MediaPlayer class, which provides a QtWebKit
A specialised version of the MediaPlayer class, which provides a QtWebKit
display.
"""
def __init__(self, parent):
MediaPlayer.__init__(self, parent, u'webkit')
self.original_name = u'WebKit'
self.display_name = u'&WebKit'
self.parent = parent
self.canBackground = True
self.audio_extensions_list = AUDIO_EXT
@ -354,7 +360,6 @@ class WebkitPlayer(MediaPlayer):
display.frame.evaluateJavaScript(u'show_flash("stop");')
else:
display.frame.evaluateJavaScript(u'show_video("stop");')
controller.seekSlider.setSliderPosition(0)
self.state = MediaState.Stopped
def volume(self, display, vol):
@ -406,6 +411,9 @@ class WebkitPlayer(MediaPlayer):
length = display.frame.evaluateJavaScript( \
u'show_flash("length");').toInt()[0]
else:
if display.frame.evaluateJavaScript( \
u'show_video("isEnded");').toString() == 'true':
self.stop(display)
(currentTime, ok) = display.frame.evaluateJavaScript( \
u'show_video("currentTime");').toFloat()
# check if conversion was ok and value is not 'NaN'

View File

@ -405,7 +405,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
# Only continue when we include the song's text.
if not self.slideTextCheckBox.isChecked():
return
for index, item in enumerate(self.serviceManager.serviceItems):
for item in self.serviceManager.serviceItems:
# Trigger Audit requests
Receiver.send_message(u'print_service_started',
[item[u'service_item']])

View File

@ -461,7 +461,7 @@ class ServiceManager(QtGui.QWidget):
log.debug(temp_file_name)
path_file_name = unicode(self.fileName())
path, file_name = os.path.split(path_file_name)
basename, extension = os.path.splitext(file_name)
basename = os.path.splitext(file_name)[0]
service_file_name = '%s.osd' % basename
log.debug(u'ServiceManager.saveFile - %s', path_file_name)
SettingsManager.set_last_dir(
@ -483,8 +483,7 @@ class ServiceManager(QtGui.QWidget):
for i, filename in \
enumerate(service_item[u'header'][u'background_audio']):
new_file = os.path.join(u'audio',
item[u'service_item']._uuid,
os.path.split(filename)[1])
item[u'service_item']._uuid, filename)
audio_files.append((filename, new_file))
service_item[u'header'][u'background_audio'][i] = new_file
# Add the service item to the service.
@ -610,16 +609,11 @@ class ServiceManager(QtGui.QWidget):
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())
translate('OpenLP.AdvancedTab', 'Service %Y-%m-%d %H-%M',
'This may not contain any of the following characters: '
'/\\?*|<>\[\]":+\nSee http://docs.python.org/library/'
'datetime.html#strftime-strptime-behavior for more '
'information.')).toString())
default_filename = time.strftime(default_pattern)
else:
default_filename = u''
@ -1359,15 +1353,15 @@ class ServiceManager(QtGui.QWidget):
Handle of the event pint passed
"""
link = event.mimeData()
if event.mimeData().hasUrls():
if link.hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
for url in event.mimeData().urls():
for url in link.urls():
filename = unicode(url.toLocalFile())
if filename.endswith(u'.osz'):
self.onLoadServiceClicked(filename)
elif event.mimeData().hasText():
plugin = unicode(event.mimeData().text())
elif link.hasText():
plugin = unicode(link.text())
item = self.serviceManagerList.itemAt(event.pos())
# ServiceManager started the drag and drop
if plugin == u'ServiceManager':

View File

@ -57,7 +57,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
def exec_(self):
# load all the settings
self.settingListWidget.clear()
for tabIndex in range(0, self.stackedLayout.count() + 1):
while self.stackedLayout.count():
# take at 0 and the rest shuffle up.
self.stackedLayout.takeAt(0)
self.insertTab(self.generalTab, 0, PluginStatus.Active)

View File

@ -186,7 +186,7 @@ class SlideController(Controller):
tooltip=translate('OpenLP.SlideController', 'Move to next.'),
shortcuts=[QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown],
context=QtCore.Qt.WidgetWithChildrenShortcut,
category=self.category, triggers=self.onSlideSelectedNext)
category=self.category, triggers=self.onSlideSelectedNextAction)
self.toolbar.addAction(self.nextItem)
self.toolbar.addSeparator()
if self.isLive:
@ -563,7 +563,8 @@ class SlideController(Controller):
Receiver.send_message('servicemanager_previous_item')
elif keypressCommand == ServiceItemAction.PreviousLastSlide:
# Go to the last slide of the previous item
Receiver.send_message('servicemanager_previous_item', u'last slide')
Receiver.send_message('servicemanager_previous_item',
u'last slide')
else:
Receiver.send_message('servicemanager_next_item')
self.keypress_loop = False
@ -1139,6 +1140,13 @@ class SlideController(Controller):
rect.y(), rect.width(), rect.height())
self.slidePreview.setPixmap(winimg)
def onSlideSelectedNextAction(self, checked):
"""
Wrapper function from create_action so we can throw away the
incorrect parameter
"""
self.onSlideSelectedNext()
def onSlideSelectedNext(self, wrap=None):
"""
Go to the next slide.
@ -1183,7 +1191,8 @@ class SlideController(Controller):
if self.slide_limits == SlideLimits.Wrap:
row = self.previewListWidget.rowCount() - 1
elif self.isLive and self.slide_limits == SlideLimits.Next:
self.keypress_queue.append(ServiceItemAction.PreviousLastSlide)
self.keypress_queue.append(
ServiceItemAction.PreviousLastSlide)
self._process_queue()
return
else:

View File

@ -137,13 +137,13 @@ class ThemeManager(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
# Variables
self.themelist = []
self.theme_list = []
self.path = AppLocation.get_section_data_path(self.settingsSection)
check_directory_exists(self.path)
self.thumbPath = os.path.join(self.path, u'thumbnails')
check_directory_exists(self.thumbPath)
self.thumb_path = os.path.join(self.path, u'thumbnails')
check_directory_exists(self.thumb_path)
self.themeForm.path = self.path
self.oldBackgroundImage = None
self.old_background_image = None
self.bad_v1_name_chars = re.compile(r'[%+\[\]]')
# Last little bits of setting up
self.configUpdated()
@ -153,10 +153,9 @@ class ThemeManager(QtGui.QWidget):
Import new themes downloaded by the first time wizard
"""
Receiver.send_message(u'cursor_busy')
encoding = get_filesystem_encoding()
files = SettingsManager.get_files(self.settingsSection, u'.otz')
for file in files:
file = os.path.join(self.path, file).encode(encoding)
file = os.path.join(self.path, file)
self.unzipTheme(file, self.path)
delete_file(file)
Receiver.send_message(u'cursor_normal')
@ -175,10 +174,10 @@ class ThemeManager(QtGui.QWidget):
"""
if item is None:
return
realThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
themeName = unicode(item.text())
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
theme_name = unicode(item.text())
# If default theme restrict actions
if realThemeName == themeName:
if real_theme_name == theme_name:
self.deleteToolbarAction.setVisible(True)
else:
self.deleteToolbarAction.setVisible(False)
@ -191,35 +190,35 @@ class ThemeManager(QtGui.QWidget):
item = self.themeListWidget.itemAt(point)
if item is None:
return
realThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
themeName = unicode(item.text())
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
theme_name = unicode(item.text())
self.deleteAction.setVisible(False)
self.renameAction.setVisible(False)
self.globalAction.setVisible(False)
# If default theme restrict actions
if realThemeName == themeName:
if real_theme_name == theme_name:
self.deleteAction.setVisible(True)
self.renameAction.setVisible(True)
self.globalAction.setVisible(True)
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
def changeGlobalFromTab(self, themeName):
def changeGlobalFromTab(self, theme_name):
"""
Change the global theme when it is changed through the Themes settings
tab
"""
log.debug(u'changeGlobalFromTab %s', themeName)
log.debug(u'changeGlobalFromTab %s', theme_name)
for count in range (0, self.themeListWidget.count()):
# reset the old name
item = self.themeListWidget.item(count)
oldName = item.text()
newName = unicode(item.data(QtCore.Qt.UserRole).toString())
if oldName != newName:
self.themeListWidget.item(count).setText(newName)
old_name = item.text()
new_name = unicode(item.data(QtCore.Qt.UserRole).toString())
if old_name != new_name:
self.themeListWidget.item(count).setText(new_name)
# Set the new name
if themeName == newName:
if theme_name == new_name:
name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % newName
'%s (default)')) % new_name
self.themeListWidget.item(count).setText(name)
def changeGlobalFromScreen(self, index=-1):
@ -231,9 +230,9 @@ class ThemeManager(QtGui.QWidget):
selected_row = self.themeListWidget.currentRow()
for count in range (0, self.themeListWidget.count()):
item = self.themeListWidget.item(count)
oldName = item.text()
old_name = item.text()
# reset the old name
if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()):
if old_name != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.themeListWidget.item(count).setText(
unicode(item.data(QtCore.Qt.UserRole).toString()))
# Set the new name
@ -269,19 +268,19 @@ class ThemeManager(QtGui.QWidget):
unicode(translate('OpenLP.ThemeManager', 'Rename %s theme?')),
False, False):
item = self.themeListWidget.currentItem()
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
old_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(old_theme_name)
if self.fileRenameForm.exec_():
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
if oldThemeName == newThemeName:
new_theme_name = unicode(self.fileRenameForm.fileNameEdit.text())
if old_theme_name == new_theme_name:
return
if self.checkIfThemeExists(newThemeName):
oldThemeData = self.getThemeData(oldThemeName)
self.cloneThemeData(oldThemeData, newThemeName)
self.deleteTheme(oldThemeName)
if self.checkIfThemeExists(new_theme_name):
old_theme_data = self.getThemeData(old_theme_name)
self.cloneThemeData(old_theme_data, new_theme_name)
self.deleteTheme(old_theme_name)
for plugin in self.mainwindow.pluginManager.plugins:
if plugin.usesTheme(oldThemeName):
plugin.renameTheme(oldThemeName, newThemeName)
if plugin.usesTheme(old_theme_name):
plugin.renameTheme(old_theme_name, new_theme_name)
self.loadThemes()
def onCopyTheme(self):
@ -289,30 +288,30 @@ class ThemeManager(QtGui.QWidget):
Copies an existing theme to a new name
"""
item = self.themeListWidget.currentItem()
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
old_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(
unicode(translate('OpenLP.ThemeManager',
'Copy of %s','Copy of <theme name>')) % oldThemeName)
'Copy of %s', 'Copy of <theme name>')) % old_theme_name)
if self.fileRenameForm.exec_(True):
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
if self.checkIfThemeExists(newThemeName):
themeData = self.getThemeData(oldThemeName)
self.cloneThemeData(themeData, newThemeName)
new_theme_name = unicode(self.fileRenameForm.fileNameEdit.text())
if self.checkIfThemeExists(new_theme_name):
theme_data = self.getThemeData(old_theme_name)
self.cloneThemeData(theme_data, new_theme_name)
def cloneThemeData(self, themeData, newThemeName):
def cloneThemeData(self, theme_data, new_theme_name):
"""
Takes a theme and makes a new copy of it as well as saving it.
"""
log.debug(u'cloneThemeData')
saveTo = None
saveFrom = None
if themeData.background_type == u'image':
saveTo = os.path.join(self.path, newThemeName,
os.path.split(unicode(themeData.background_filename))[1])
saveFrom = themeData.background_filename
themeData.theme_name = newThemeName
themeData.extend_image_filename(self.path)
self.saveTheme(themeData, saveFrom, saveTo)
save_to = None
save_from = None
if theme_data.background_type == u'image':
save_to = os.path.join(self.path, new_theme_name,
os.path.split(unicode(theme_data.background_filename))[1])
save_from = theme_data.background_filename
theme_data.theme_name = new_theme_name
theme_data.extend_image_filename(self.path)
self.saveTheme(theme_data, save_from, save_to)
def onEditTheme(self):
"""
@ -326,10 +325,10 @@ class ThemeManager(QtGui.QWidget):
theme = self.getThemeData(
unicode(item.data(QtCore.Qt.UserRole).toString()))
if theme.background_type == u'image':
self.oldBackgroundImage = theme.background_filename
self.old_background_image = theme.background_filename
self.themeForm.theme = theme
self.themeForm.exec_(True)
self.oldBackgroundImage = None
self.old_background_image = None
def onDeleteTheme(self):
"""
@ -355,10 +354,10 @@ class ThemeManager(QtGui.QWidget):
``theme``
The theme to delete.
"""
self.themelist.remove(theme)
self.theme_list.remove(theme)
thumb = u'%s.png' % theme
delete_file(os.path.join(self.path, thumb))
delete_file(os.path.join(self.thumbPath, thumb))
delete_file(os.path.join(self.thumb_path, thumb))
try:
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
@ -383,10 +382,10 @@ class ThemeManager(QtGui.QWidget):
Receiver.send_message(u'cursor_busy')
if path:
SettingsManager.set_last_dir(self.settingsSection, path, 1)
themePath = os.path.join(path, theme + u'.otz')
theme_path = os.path.join(path, theme + u'.otz')
zip = None
try:
zip = zipfile.ZipFile(themePath, u'w')
zip = zipfile.ZipFile(theme_path, u'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
@ -436,9 +435,8 @@ class ThemeManager(QtGui.QWidget):
The plugins will call back in to get the real list if they want it.
"""
log.debug(u'Load themes from dir')
self.themelist = []
self.theme_list = []
self.themeListWidget.clear()
dirList = os.listdir(self.path)
files = SettingsManager.get_files(self.settingsSection, u'.png')
if firstTime:
self.firstTime()
@ -455,29 +453,29 @@ class ThemeManager(QtGui.QWidget):
files = SettingsManager.get_files(self.settingsSection, u'.png')
# Sort the themes by its name considering language specific characters.
# lower() is needed for windows!
files.sort(key=lambda filename: unicode(filename).lower(),
files.sort(key=lambda file_name: unicode(file_name).lower(),
cmp=locale.strcoll)
# now process the file list of png files
for name in files:
# check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
textName = os.path.splitext(name)[0]
if textName == self.global_theme:
text_name = os.path.splitext(name)[0]
if text_name == self.global_theme:
name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % textName
'%s (default)')) % text_name
else:
name = textName
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
name = text_name
thumb = os.path.join(self.thumb_path, u'%s.png' % text_name)
item_name = QtGui.QListWidgetItem(name)
if validate_thumb(theme, thumb):
icon = build_icon(thumb)
else:
icon = create_thumb(theme, thumb)
item_name.setIcon(icon)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(textName))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(text_name))
self.themeListWidget.addItem(item_name)
self.themelist.append(textName)
self.theme_list.append(text_name)
self._pushThemes()
def _pushThemes(self):
@ -490,50 +488,68 @@ class ThemeManager(QtGui.QWidget):
"""
Return the list of loaded themes
"""
return self.themelist
return self.theme_list
def getThemeData(self, themeName):
def getThemeData(self, theme_name):
"""
Returns a theme object from an XML file
``themeName``
``theme_name``
Name of the theme to load from file
"""
log.debug(u'getthemedata for theme %s', themeName)
xmlFile = os.path.join(self.path, unicode(themeName),
unicode(themeName) + u'.xml')
xml = get_text_file_string(xmlFile)
log.debug(u'getthemedata for theme %s', theme_name)
xml_file = os.path.join(self.path, unicode(theme_name),
unicode(theme_name) + u'.xml')
xml = get_text_file_string(xml_file)
if not xml:
log.debug("No theme data - using default theme")
return ThemeXML()
else:
return self._createThemeFromXml(xml, self.path)
def unzipTheme(self, filename, dir):
def overWriteMessageBox(self, theme_name):
ret = QtGui.QMessageBox.question(self,
translate('OpenLP.ThemeManager', 'Theme Already Exists'),
translate('OpenLP.ThemeManager',
'Theme %s already exists. Do you want to replace it?'
% theme_name),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
return ret == QtGui.QMessageBox.Yes
def unzipTheme(self, file_name, dir):
"""
Unzip the theme, remove the preview file if stored
Generate a new preview file. Check the XML theme version and upgrade if
necessary.
"""
log.debug(u'Unzipping theme %s', filename)
filename = unicode(filename)
log.debug(u'Unzipping theme %s', file_name)
file_name = unicode(file_name)
zip = None
outfile = None
filexml = None
out_file = None
file_xml = None
try:
zip = zipfile.ZipFile(filename)
xmlfile = filter(lambda name:
zip = zipfile.ZipFile(file_name)
xml_file = filter(lambda name:
os.path.splitext(name)[1].lower() == u'.xml', zip.namelist())
if len(xmlfile) != 1:
log.exception(u'Theme contains "%s" XML files' % len(xmlfile))
if len(xml_file) != 1:
log.exception(u'Theme contains "%s" XML files' % len(xml_file))
raise Exception(u'validation')
xml_tree = ElementTree(element=XML(zip.read(xmlfile[0]))).getroot()
xml_tree = ElementTree(element=XML(zip.read(xml_file[0]))).getroot()
v1_background = xml_tree.find(u'BackgroundType')
if v1_background is not None:
(themename, filexml, outfile) = self.unzipVersion122(dir, zip,
xmlfile[0], xml_tree, v1_background, outfile)
theme_name, file_xml, out_file, abort_import = self.unzipVersion122(dir, zip,
xml_file[0], xml_tree, v1_background, out_file)
else:
themename = xml_tree.find(u'name').text.strip()
theme_name = xml_tree.find(u'name').text.strip()
theme_folder = os.path.join(dir, theme_name)
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.overWriteMessageBox(theme_name):
abort_import = True
return
else:
abort_import = False
for name in zip.namelist():
try:
uname = unicode(name, u'utf-8')
@ -542,22 +558,22 @@ class ThemeManager(QtGui.QWidget):
u' "%s"' % name.decode(u'utf-8', u'replace'))
raise Exception(u'validation')
uname = uname.replace(u'/', os.path.sep)
splitname = uname.split(os.path.sep)
if splitname[-1] == u'' or len(splitname) == 1:
split_name = uname.split(os.path.sep)
if split_name[-1] == u'' or len(split_name) == 1:
# is directory or preview file
continue
fullname = os.path.join(dir, uname)
check_directory_exists(os.path.dirname(fullname))
full_name = os.path.join(dir, uname)
check_directory_exists(os.path.dirname(full_name))
if os.path.splitext(uname)[1].lower() == u'.xml':
filexml = unicode(zip.read(name), u'utf-8')
outfile = open(fullname, u'w')
outfile.write(filexml.encode(u'utf-8'))
file_xml = unicode(zip.read(name), u'utf-8')
out_file = open(full_name, u'w')
out_file.write(file_xml.encode(u'utf-8'))
else:
outfile = open(fullname, u'wb')
outfile.write(zip.read(name))
outfile.close()
out_file = open(full_name, u'wb')
out_file.write(zip.read(name))
out_file.close()
except (IOError, zipfile.BadZipfile):
log.exception(u'Importing theme from zip failed %s' % filename)
log.exception(u'Importing theme from zip failed %s' % file_name)
raise Exception(u'validation')
except Exception as info:
if unicode(info) == u'validation':
@ -570,60 +586,65 @@ class ThemeManager(QtGui.QWidget):
# Close the files, to be able to continue creating the theme.
if zip:
zip.close()
if outfile:
outfile.close()
# As all files are closed, we can create the Theme.
if filexml:
theme = self._createThemeFromXml(filexml, self.path)
self.generateAndSaveImage(dir, themename, theme)
# Only show the error message, when IOError was not raised (in this
# case the error message has already been shown).
elif zip is not None:
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager',
'File is not a valid theme.'))
log.exception(u'Theme file does not contain XML data %s' %
filename)
if out_file:
out_file.close()
if not abort_import:
# As all files are closed, we can create the Theme.
if file_xml:
theme = self._createThemeFromXml(file_xml, self.path)
self.generateAndSaveImage(dir, theme_name, theme)
# Only show the error message, when IOError was not raised (in this
# case the error message has already been shown).
elif zip is not None:
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager',
'File is not a valid theme.'))
log.exception(u'Theme file does not contain XML data %s' %
file_name)
def unzipVersion122(self, dir, zip, xmlfile, xml_tree, background, outfile):
def unzipVersion122(self, dir, zip, xml_file, xml_tree, background, out_file):
"""
Unzip openlp.org 1.2x theme file and upgrade the theme xml. When calling
this method, please keep in mind, that some parameters are redundant.
"""
themename = xml_tree.find(u'Name').text.strip()
themename = self.bad_v1_name_chars.sub(u'', themename)
themedir = os.path.join(dir, themename)
theme_name = xml_tree.find(u'Name').text.strip()
theme_name = self.bad_v1_name_chars.sub(u'', theme_name)
theme_folder = os.path.join(dir, theme_name)
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.overWriteMessageBox(theme_name):
return '', '', '', True
themedir = os.path.join(dir, theme_name)
check_directory_exists(themedir)
filexml = unicode(zip.read(xmlfile), u'utf-8')
filexml = self._migrateVersion122(filexml)
outfile = open(os.path.join(themedir, themename + u'.xml'), u'w')
outfile.write(filexml.encode(u'utf-8'))
outfile.close()
file_xml = unicode(zip.read(xml_file), u'utf-8')
file_xml = self._migrateVersion122(file_xml)
out_file = open(os.path.join(themedir, theme_name + u'.xml'), u'w')
out_file.write(file_xml.encode(u'utf-8'))
out_file.close()
if background.text.strip() == u'2':
imagename = xml_tree.find(u'BackgroundParameter1').text.strip()
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
# image file has same extension and is in subfolder
imagefile = filter(lambda name: os.path.splitext(name)[1].lower()
== os.path.splitext(imagename)[1].lower() and name.find(r'/'),
== os.path.splitext(image_name)[1].lower() and name.find(r'/'),
zip.namelist())
if len(imagefile) >= 1:
outfile = open(os.path.join(themedir, imagename), u'wb')
outfile.write(zip.read(imagefile[0]))
outfile.close()
out_file = open(os.path.join(themedir, image_name), u'wb')
out_file.write(zip.read(imagefile[0]))
out_file.close()
else:
log.exception(u'Theme file does not contain image file "%s"' %
imagename.decode(u'utf-8', u'replace'))
image_name.decode(u'utf-8', u'replace'))
raise Exception(u'validation')
return (themename, filexml, outfile)
return theme_name, file_xml, out_file, False
def checkIfThemeExists(self, themeName):
def checkIfThemeExists(self, theme_name):
"""
Check if theme already exists and displays error message
``themeName``
``theme_name``
Name of the Theme to test
"""
theme_dir = os.path.join(self.path, themeName)
theme_dir = os.path.join(self.path, theme_name)
if os.path.exists(theme_dir):
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
@ -632,12 +653,12 @@ class ThemeManager(QtGui.QWidget):
return False
return True
def saveTheme(self, theme, imageFrom, imageTo):
def saveTheme(self, theme, image_from, image_to):
"""
Called by thememaintenance Dialog to save the theme
and to trigger the reload of the theme list
"""
self._writeTheme(theme, imageFrom, imageTo)
self._writeTheme(theme, image_from, image_to)
if theme.background_type == \
BackgroundType.to_string(BackgroundType.Image):
self.mainwindow.imageManager.update_image(theme.theme_name,
@ -645,7 +666,7 @@ class ThemeManager(QtGui.QWidget):
self.mainwindow.imageManager.process_updates()
self.loadThemes()
def _writeTheme(self, theme, imageFrom, imageTo):
def _writeTheme(self, theme, image_from, image_to):
"""
Writes the theme to the disk and handles the background image if
necessary
@ -656,24 +677,24 @@ class ThemeManager(QtGui.QWidget):
theme_dir = os.path.join(self.path, name)
check_directory_exists(theme_dir)
theme_file = os.path.join(theme_dir, name + u'.xml')
if self.oldBackgroundImage and \
imageTo != self.oldBackgroundImage:
delete_file(self.oldBackgroundImage)
outfile = None
if self.old_background_image and \
image_to != self.old_background_image:
delete_file(self.old_background_image)
out_file = None
try:
outfile = open(theme_file, u'w')
outfile.write(theme_pretty_xml)
out_file = open(theme_file, u'w')
out_file.write(theme_pretty_xml)
except IOError:
log.exception(u'Saving theme to file failed')
finally:
if outfile:
outfile.close()
if imageFrom and imageFrom != imageTo:
if out_file:
out_file.close()
if image_from and image_from != image_to:
try:
encoding = get_filesystem_encoding()
shutil.copyfile(
unicode(imageFrom).encode(encoding),
unicode(imageTo).encode(encoding))
unicode(image_from).encode(encoding),
unicode(image_to).encode(encoding))
except IOError:
log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme)
@ -681,39 +702,39 @@ class ThemeManager(QtGui.QWidget):
def generateAndSaveImage(self, dir, name, theme):
log.debug(u'generateAndSaveImage %s %s', dir, name)
frame = self.generateImage(theme)
samplepathname = os.path.join(self.path, name + u'.png')
if os.path.exists(samplepathname):
os.unlink(samplepathname)
frame.save(samplepathname, u'png')
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
create_thumb(samplepathname, thumb, False)
log.debug(u'Theme image written to %s', samplepathname)
sample_path_name = os.path.join(self.path, name + u'.png')
if os.path.exists(sample_path_name):
os.unlink(sample_path_name)
frame.save(sample_path_name, u'png')
thumb = os.path.join(self.thumb_path, u'%s.png' % name)
create_thumb(sample_path_name, thumb, False)
log.debug(u'Theme image written to %s', sample_path_name)
def updatePreviewImages(self):
"""
Called to update the themes' preview images.
"""
self.mainwindow.displayProgressBar(len(self.themelist))
for theme in self.themelist:
self.mainwindow.displayProgressBar(len(self.theme_list))
for theme in self.theme_list:
self.mainwindow.incrementProgressBar()
self.generateAndSaveImage(
self.path, theme, self.getThemeData(theme))
self.mainwindow.finishedProgressBar()
self.loadThemes()
def generateImage(self, themeData, forcePage=False):
def generateImage(self, theme_data, forcePage=False):
"""
Call the renderer to build a Sample Image
``themeData``
``theme_data``
The theme to generated a preview for.
``forcePage``
Flag to tell message lines per page need to be generated.
"""
log.debug(u'generateImage \n%s ', themeData)
log.debug(u'generateImage \n%s ', theme_data)
return self.mainwindow.renderer.generate_preview(
themeData, forcePage)
theme_data, forcePage)
def getPreviewImage(self, theme):
"""
@ -726,15 +747,15 @@ class ThemeManager(QtGui.QWidget):
image = os.path.join(self.path, theme + u'.png')
return image
def _createThemeFromXml(self, themeXml, path):
def _createThemeFromXml(self, theme_xml, path):
"""
Return a theme object using information parsed from XML
``themeXml``
``theme_xml``
The XML data to load into the theme
"""
theme = ThemeXML()
theme.parse(themeXml)
theme.parse(theme_xml)
theme.extend_image_filename(path)
return theme
@ -789,53 +810,53 @@ class ThemeManager(QtGui.QWidget):
Version 1 theme to convert
"""
theme = Theme(xml_data)
newtheme = ThemeXML()
newtheme.theme_name = self.bad_v1_name_chars.sub(u'', theme.Name)
new_theme = ThemeXML()
new_theme.theme_name = self.bad_v1_name_chars.sub(u'', theme.Name)
if theme.BackgroundType == 0:
newtheme.background_type = \
new_theme.background_type = \
BackgroundType.to_string(BackgroundType.Solid)
newtheme.background_color = \
new_theme.background_color = \
unicode(theme.BackgroundParameter1.name())
elif theme.BackgroundType == 1:
newtheme.background_type = \
new_theme.background_type = \
BackgroundType.to_string(BackgroundType.Gradient)
newtheme.background_direction = \
new_theme.background_direction = \
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
if theme.BackgroundParameter3.name() == 1:
newtheme.background_direction = \
new_theme.background_direction = \
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
newtheme.background_start_color = \
new_theme.background_start_color = \
unicode(theme.BackgroundParameter1.name())
newtheme.background_end_color = \
new_theme.background_end_color = \
unicode(theme.BackgroundParameter2.name())
elif theme.BackgroundType == 2:
newtheme.background_type = \
new_theme.background_type = \
BackgroundType.to_string(BackgroundType.Image)
newtheme.background_filename = unicode(theme.BackgroundParameter1)
new_theme.background_filename = unicode(theme.BackgroundParameter1)
elif theme.BackgroundType == 3:
newtheme.background_type = \
new_theme.background_type = \
BackgroundType.to_string(BackgroundType.Transparent)
newtheme.font_main_name = theme.FontName
newtheme.font_main_color = unicode(theme.FontColor.name())
newtheme.font_main_size = theme.FontProportion * 3
newtheme.font_footer_name = theme.FontName
newtheme.font_footer_color = unicode(theme.FontColor.name())
newtheme.font_main_shadow = False
new_theme.font_main_name = theme.FontName
new_theme.font_main_color = unicode(theme.FontColor.name())
new_theme.font_main_size = theme.FontProportion * 3
new_theme.font_footer_name = theme.FontName
new_theme.font_footer_color = unicode(theme.FontColor.name())
new_theme.font_main_shadow = False
if theme.Shadow == 1:
newtheme.font_main_shadow = True
newtheme.font_main_shadow_color = unicode(theme.ShadowColor.name())
new_theme.font_main_shadow = True
new_theme.font_main_shadow_color = unicode(theme.ShadowColor.name())
if theme.Outline == 1:
newtheme.font_main_outline = True
newtheme.font_main_outline_color = \
new_theme.font_main_outline = True
new_theme.font_main_outline_color = \
unicode(theme.OutlineColor.name())
vAlignCorrection = VerticalType.Top
if theme.VerticalAlign == 2:
vAlignCorrection = VerticalType.Middle
elif theme.VerticalAlign == 1:
vAlignCorrection = VerticalType.Bottom
newtheme.display_horizontal_align = theme.HorizontalAlign
newtheme.display_vertical_align = vAlignCorrection
return newtheme.extract_xml()
new_theme.display_horizontal_align = theme.HorizontalAlign
new_theme.display_vertical_align = vAlignCorrection
return new_theme.extract_xml()

View File

@ -53,6 +53,8 @@ APPLICATION_VERSION = {}
IMAGES_FILTER = None
UNO_CONNECTION_TYPE = u'pipe'
#UNO_CONNECTION_TYPE = u'socket'
CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)
INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)
VERSION_SPLITTER = re.compile(r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
class VersionThread(QtCore.QThread):
@ -400,7 +402,7 @@ def clean_filename(filename):
"""
if not isinstance(filename, unicode):
filename = unicode(filename, u'utf-8')
return re.sub(r'[/\\?*|<>\[\]":<>+%]+', u'_', filename).strip(u'_')
return INVALID_FILE_CHARS.sub(u'_', CONTROL_CHARS.sub(u'', filename))
def delete_file(file_path_name):
"""

View File

@ -27,7 +27,7 @@
import logging
from PyQt4 import QtCore, QtGui
from PyQt4 import QtGui
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.ui import create_action, UiStrings

View File

@ -34,6 +34,7 @@ import re
from PyQt4 import QtCore
from openlp.core.lib import translate
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__)
@ -59,6 +60,122 @@ class DisplayStyle(object):
Square = 3
class LanguageSelection(object):
"""
An enumeration for bible bookname language.
And standard strings for use throughout the bibles plugin.
"""
Bible = 0
Application = 1
English = 2
class BibleStrings(object):
"""
Provide standard strings for objects to use.
"""
__instance__ = None
def __new__(cls):
"""
Override the default object creation method to return a single instance.
"""
if not cls.__instance__:
cls.__instance__ = object.__new__(cls)
return cls.__instance__
def __init__(self):
"""
These strings should need a good reason to be retranslated elsewhere.
"""
self.Booknames = {
u'Gen': translate('BiblesPlugin', 'Genesis'),
u'Exod': translate('BiblesPlugin', 'Exodus'),
u'Lev': translate('BiblesPlugin', 'Leviticus'),
u'Num': translate('BiblesPlugin', 'Numbers'),
u'Deut': translate('BiblesPlugin', 'Deuteronomy'),
u'Josh': translate('BiblesPlugin', 'Joshua'),
u'Judg': translate('BiblesPlugin', 'Judges'),
u'Ruth': translate('BiblesPlugin', 'Ruth'),
u'1Sam': translate('BiblesPlugin', '1 Samuel'),
u'2Sam': translate('BiblesPlugin', '2 Samuel'),
u'1Kgs': translate('BiblesPlugin', '1 Kings'),
u'2Kgs': translate('BiblesPlugin', '2 Kings'),
u'1Chr': translate('BiblesPlugin', '1 Chronicles'),
u'2Chr': translate('BiblesPlugin', '2 Chronicles'),
u'Esra': translate('BiblesPlugin', 'Ezra'),
u'Neh': translate('BiblesPlugin', 'Nehemiah'),
u'Esth': translate('BiblesPlugin', 'Esther'),
u'Job': translate('BiblesPlugin', 'Job'),
u'Ps': translate('BiblesPlugin', 'Psalms'),
u'Prov': translate('BiblesPlugin', 'Proverbs'),
u'Eccl': translate('BiblesPlugin', 'Ecclesiastes'),
u'Song': translate('BiblesPlugin', 'Song of Solomon'),
u'Isa': translate('BiblesPlugin', 'Isaiah'),
u'Jer': translate('BiblesPlugin', 'Jeremiah'),
u'Lam': translate('BiblesPlugin', 'Lamentations'),
u'Ezek': translate('BiblesPlugin', 'Ezekiel'),
u'Dan': translate('BiblesPlugin', 'Daniel'),
u'Hos': translate('BiblesPlugin', 'Hosea'),
u'Joel': translate('BiblesPlugin', 'Joel'),
u'Amos': translate('BiblesPlugin', 'Amos'),
u'Obad': translate('BiblesPlugin', 'Obadiah'),
u'Jonah': translate('BiblesPlugin', 'Jonah'),
u'Mic': translate('BiblesPlugin', 'Micah'),
u'Nah': translate('BiblesPlugin', 'Nahum'),
u'Hab': translate('BiblesPlugin', 'Habakkuk'),
u'Zeph': translate('BiblesPlugin', 'Zephaniah'),
u'Hag': translate('BiblesPlugin', 'Haggai'),
u'Zech': translate('BiblesPlugin', 'Zechariah'),
u'Mal': translate('BiblesPlugin', 'Malachi'),
u'Matt': translate('BiblesPlugin', 'Matthew'),
u'Mark': translate('BiblesPlugin', 'Mark'),
u'Luke': translate('BiblesPlugin', 'Luke'),
u'John': translate('BiblesPlugin', 'John'),
u'Acts': translate('BiblesPlugin', 'Acts'),
u'Rom': translate('BiblesPlugin', 'Romans'),
u'1Cor': translate('BiblesPlugin', '1 Corinthians'),
u'2Cor': translate('BiblesPlugin', '2 Corinthians'),
u'Gal': translate('BiblesPlugin', 'Galatians'),
u'Eph': translate('BiblesPlugin', 'Ephesians'),
u'Phil': translate('BiblesPlugin', 'Philippians'),
u'Col': translate('BiblesPlugin', 'Colossians'),
u'1Thess': translate('BiblesPlugin', '1 Thessalonians'),
u'2Thess': translate('BiblesPlugin', '2 Thessalonians'),
u'1Tim': translate('BiblesPlugin', '1 Timothy'),
u'2Tim': translate('BiblesPlugin', '2 Timothy'),
u'Titus': translate('BiblesPlugin', 'Titus'),
u'Phlm': translate('BiblesPlugin', 'Philemon'),
u'Heb': translate('BiblesPlugin', 'Hebrews'),
u'Jas': translate('BiblesPlugin', 'James'),
u'1Pet': translate('BiblesPlugin', '1 Peter'),
u'2Pet': translate('BiblesPlugin', '2 Peter'),
u'1John': translate('BiblesPlugin', '1 John'),
u'2John': translate('BiblesPlugin', '2 John'),
u'3John': translate('BiblesPlugin', '3 John'),
u'Jude': translate('BiblesPlugin', 'Jude'),
u'Rev': translate('BiblesPlugin', 'Revelation'),
u'Jdt': translate('BiblesPlugin', 'Judith'),
u'Wis': translate('BiblesPlugin', 'Wisdom'),
u'Tob': translate('BiblesPlugin', 'Tobit'),
u'Sir': translate('BiblesPlugin', 'Sirach'),
u'Bar': translate('BiblesPlugin', 'Baruch'),
u'1Macc': translate('BiblesPlugin', '1 Maccabees'),
u'2Macc': translate('BiblesPlugin', '2 Maccabees'),
u'3Macc': translate('BiblesPlugin', '3 Maccabees'),
u'4Macc': translate('BiblesPlugin', '4 Maccabees'),
u'AddDan': translate('BiblesPlugin', 'Rest of Daniel'),
u'AddEsth': translate('BiblesPlugin', 'Rest of Esther'),
u'PrMan': translate('BiblesPlugin', 'Prayer of Manasses'),
u'LetJer': translate('BiblesPlugin', 'Letter of Jeremiah'),
u'PrAza': translate('BiblesPlugin', 'Prayer of Azariah'),
u'Sus': translate('BiblesPlugin', 'Susanna'),
u'Bel': translate('BiblesPlugin', 'Bel'),
u'1Esdr': translate('BiblesPlugin', '1 Esdras'),
u'2Esdr': translate('BiblesPlugin', '2 Esdras')
}
def update_reference_separators():
"""
Updates separators and matches for parsing and formating scripture
@ -66,13 +183,8 @@ def update_reference_separators():
"""
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';;')
'Double-semicolon delimited separators for parsing references. '
'Consult the developers for further information.')).split(u';;')
settings = QtCore.QSettings()
settings.beginGroup(u'bibles')
custom_separators = [
@ -139,7 +251,7 @@ def get_reference_match(match_type):
update_reference_separators()
return REFERENCE_MATCHES[match_type]
def parse_reference(reference):
def parse_reference(reference, bible, language_selection, book_ref_id=False):
"""
This is the next generation über-awesome function that takes a person's
typed in string and converts it to a list of references to be queried from
@ -147,6 +259,16 @@ def parse_reference(reference):
``reference``
A string. The Bible reference to parse.
``bible``
A object. The Bible database object.
``language_selection``
An int. The language selection the user has choosen in settings
section.
``book_ref_id``
A string. The book reference id.
Returns ``None`` or a reference list.
@ -232,6 +354,51 @@ def parse_reference(reference):
if match:
log.debug(u'Matched reference %s' % reference)
book = match.group(u'book')
if not book_ref_id:
booknames = BibleStrings().Booknames
# escape reserved characters
book_escaped = book
for character in u'\\.^$*+?{}[]()':
book_escaped = book_escaped.replace(
character, u'\\' + character)
regex_book = re.compile(u'\s*%s\s*' % u'\s*'.join(
book_escaped.split()), re.UNICODE | re.IGNORECASE)
if language_selection == LanguageSelection.Bible:
db_book = bible.get_book(book)
if db_book:
book_ref_id = db_book.book_reference_id
elif language_selection == LanguageSelection.Application:
book_list = []
for key, value in booknames.iteritems():
if regex_book.match(unicode(value)):
book_list.append(key)
books = []
if book_list:
for value in book_list:
item = BiblesResourcesDB.get_book(value)
if item:
books.append(item)
if books:
for value in books:
if bible.get_book_by_book_ref_id(value[u'id']):
book_ref_id = value[u'id']
break
elif language_selection == LanguageSelection.English:
books = BiblesResourcesDB.get_books_like(book)
if books:
book_list = []
for value in books:
if regex_book.match(value[u'name']):
book_list.append(value)
if not book_list:
book_list = books
for value in book_list:
if bible.get_book_by_book_ref_id(value[u'id']):
book_ref_id = value[u'id']
break
else:
if not bible.get_book_by_book_ref_id(book_ref_id):
book_ref_id = False
ranges = match.group(u'ranges')
range_list = get_reference_match(u'range_separator').split(ranges)
ref_list = []
@ -277,16 +444,18 @@ def parse_reference(reference):
if not to_verse:
to_verse = -1
if to_chapter > from_chapter:
ref_list.append((book, from_chapter, from_verse, -1))
ref_list.append((book_ref_id, from_chapter, from_verse, -1))
for i in range(from_chapter + 1, to_chapter):
ref_list.append((book, i, 1, -1))
ref_list.append((book, to_chapter, 1, to_verse))
ref_list.append((book_ref_id, i, 1, -1))
ref_list.append((book_ref_id, to_chapter, 1, to_verse))
elif to_verse >= from_verse or to_verse == -1:
ref_list.append((book, from_chapter, from_verse, to_verse))
ref_list.append((book_ref_id, from_chapter,
from_verse, to_verse))
elif from_verse:
ref_list.append((book, from_chapter, from_verse, from_verse))
ref_list.append((book_ref_id, from_chapter,
from_verse, from_verse))
else:
ref_list.append((book, from_chapter, 1, -1))
ref_list.append((book_ref_id, from_chapter, 1, -1))
return ref_list
else:
log.debug(u'Invalid reference: %s' % reference)

View File

@ -32,7 +32,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, SettingsTab, translate
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
update_reference_separators, get_reference_separator, LanguageSelection
log = logging.getLogger(__name__)
@ -140,9 +140,25 @@ class BiblesTab(SettingsTab):
self.scriptureReferenceLayout.addWidget(self.endSeparatorLineEdit, 3,
1)
self.leftLayout.addWidget(self.scriptureReferenceGroupBox)
self.leftLayout.addStretch()
self.rightColumn.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
self.languageSelectionGroupBox = QtGui.QGroupBox(self.rightColumn)
self.languageSelectionGroupBox.setObjectName(
u'languageSelectionGroupBox')
self.languageSelectionLayout = QtGui.QVBoxLayout(
self.languageSelectionGroupBox)
self.languageSelectionLabel = QtGui.QLabel(
self.languageSelectionGroupBox)
self.languageSelectionLabel.setObjectName(u'languageSelectionLabel')
self.languageSelectionComboBox = QtGui.QComboBox(
self.languageSelectionGroupBox)
self.languageSelectionComboBox.setObjectName(
u'languageSelectionComboBox')
self.languageSelectionComboBox.addItems([u'', u'', u''])
self.languageSelectionLayout.addWidget(self.languageSelectionLabel)
self.languageSelectionLayout.addWidget(self.languageSelectionComboBox)
self.rightLayout.addWidget(self.languageSelectionGroupBox)
self.leftLayout.addStretch()
self.rightLayout.addStretch()
# Signals and slots
QtCore.QObject.connect(
@ -198,6 +214,9 @@ class BiblesTab(SettingsTab):
self.onEndSeparatorLineEditFinished)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList)
QtCore.QObject.connect(
self.languageSelectionComboBox, QtCore.SIGNAL(u'activated(int)'),
self.onLanguageSelectionComboBoxChanged)
def retranslateUi(self):
self.verseDisplayGroupBox.setTitle(
@ -257,6 +276,23 @@ class BiblesTab(SettingsTab):
'end marks may be defined.\nThey have to be separated by a '
'vertical bar "|".\nPlease clear this edit line to use the '
'default value.'))
self.languageSelectionGroupBox.setTitle(
translate('BiblesPlugin.BiblesTab', 'Preferred Bookname Language'))
self.languageSelectionLabel.setText(translate('BiblesPlugin.BiblesTab',
'Choose the language in which the book names of the\nBible should '
'be displayed in the Bible search:'))
self.languageSelectionComboBox.setItemText(LanguageSelection.Bible,
translate('BiblesPlugin.BiblesTab', 'Bible language'))
self.languageSelectionComboBox.setItemText(
LanguageSelection.Application,
translate('BiblesPlugin.BiblesTab', 'Application language'))
self.languageSelectionComboBox.setItemText(LanguageSelection.English,
translate('BiblesPlugin.BiblesTab', 'English'))
self.languageSelectionComboBox.setToolTip(
translate('BiblesPlugin.BiblesTab', 'Multiple options:\n '
'Bible language - the language in which the Bible book names '
'were imported\n Application language - the language you have '
'chosen for OpenLP\n English - always use English book names'))
def onBibleThemeComboBoxChanged(self):
self.bible_theme = self.bibleThemeComboBox.currentText()
@ -267,6 +303,9 @@ class BiblesTab(SettingsTab):
def onLayoutStyleComboBoxChanged(self):
self.layout_style = self.layoutStyleComboBox.currentIndex()
def onLanguageSelectionComboBoxChanged(self):
self.language_selection = self.languageSelectionComboBox.currentIndex()
def onNewChaptersCheckBoxChanged(self, check_state):
self.show_new_chapters = False
# We have a set value convert to True/False.
@ -448,6 +487,9 @@ class BiblesTab(SettingsTab):
self.endSeparatorLineEdit.setPalette(
self.getGreyTextPalette(False))
self.endSeparatorCheckBox.setChecked(True)
self.language_selection = settings.value(
u'bookname language', QtCore.QVariant(0)).toInt()[0]
self.languageSelectionComboBox.setCurrentIndex(self.language_selection)
settings.endGroup()
def save(self):
@ -459,6 +501,8 @@ class BiblesTab(SettingsTab):
QtCore.QVariant(self.display_style))
settings.setValue(u'verse layout style',
QtCore.QVariant(self.layout_style))
settings.setValue(u'bookname language',
QtCore.QVariant(self.language_selection))
settings.setValue(u'second bibles', QtCore.QVariant(self.second_bibles))
settings.setValue(u'bible theme', QtCore.QVariant(self.bible_theme))
if self.verseSeparatorCheckBox.isChecked():
@ -482,6 +526,7 @@ class BiblesTab(SettingsTab):
else:
settings.remove(u'end separator')
update_reference_separators()
Receiver.send_message(u'bibles_load_list')
settings.endGroup()
def updateThemeList(self, theme_list):

View File

@ -25,13 +25,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import chardet
import logging
import os
import sqlite3
from PyQt4 import QtCore
from sqlalchemy import Column, ForeignKey, or_, Table, types
from sqlalchemy import Column, ForeignKey, or_, Table, types, func
from sqlalchemy.orm import class_mapper, mapper, relation
from sqlalchemy.orm.exc import UnmappedClassError
@ -427,33 +427,30 @@ class BibleDB(QtCore.QObject, Manager):
The book object to get the chapter count for.
"""
log.debug(u'BibleDB.get_chapter_count("%s")', book.name)
count = self.session.query(Verse.chapter).join(Book)\
.filter(Book.book_reference_id==book.book_reference_id)\
.distinct().count()
count = self.session.query(func.max(Verse.chapter)).join(Book).filter(
Book.book_reference_id==book.book_reference_id).scalar()
if not count:
return 0
else:
return count
return count
def get_verse_count(self, book_id, chapter):
def get_verse_count(self, book_ref_id, chapter):
"""
Return the number of verses in a chapter.
``book``
The book containing the chapter.
``book_ref_id``
The book reference id.
``chapter``
The chapter to get the verse count for.
"""
log.debug(u'BibleDB.get_verse_count("%s", "%s")', book_id, chapter)
count = self.session.query(Verse).join(Book)\
.filter(Book.book_reference_id==book_id)\
log.debug(u'BibleDB.get_verse_count("%s", "%s")', book_ref_id, chapter)
count = self.session.query(func.max(Verse.verse)).join(Book)\
.filter(Book.book_reference_id==book_ref_id)\
.filter(Verse.chapter==chapter)\
.count()
.scalar()
if not count:
return 0
else:
return count
return count
def get_language(self, bible_name=None):
"""
@ -595,6 +592,35 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
else:
return None
@staticmethod
def get_books_like(string):
"""
Return the books which include string.
``string``
The string to search for in the booknames or abbreviations.
"""
log.debug(u'BiblesResourcesDB.get_book_like("%s")', string)
if not isinstance(string, unicode):
name = unicode(string)
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
u'abbreviation, chapters FROM book_reference WHERE '
u'LOWER(name) LIKE ? OR LOWER(abbreviation) LIKE ?',
(u'%' + string.lower() + u'%', u'%' + string.lower() + u'%'))
if books:
return [
{
u'id': book[0],
u'testament_id': book[1],
u'name': unicode(book[2]),
u'abbreviation': unicode(book[3]),
u'chapters': book[4]
}
for book in books
]
else:
return None
@staticmethod
def get_book_by_id(id):
"""
@ -621,23 +647,23 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return None
@staticmethod
def get_chapter(book_id, chapter):
def get_chapter(book_ref_id, chapter):
"""
Return the chapter details for a specific chapter of a book.
``book_id``
``book_ref_id``
The id of a book.
``chapter``
The chapter number.
"""
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id,
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_ref_id,
chapter)
if not isinstance(chapter, int):
chapter = int(chapter)
chapters = BiblesResourcesDB.run_sql(u'SELECT id, book_reference_id, '
u'chapter, verse_count FROM chapters WHERE book_reference_id = ?',
(book_id,))
(book_ref_id,))
try:
return {
u'id': chapters[chapter-1][0],
@ -649,21 +675,21 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return None
@staticmethod
def get_chapter_count(book_id):
def get_chapter_count(book_ref_id):
"""
Return the number of chapters in a book.
``book_id``
``book_ref_id``
The id of the book.
"""
log.debug(u'BiblesResourcesDB.get_chapter_count("%s")', book_id)
details = BiblesResourcesDB.get_book_by_id(book_id)
log.debug(u'BiblesResourcesDB.get_chapter_count("%s")', book_ref_id)
details = BiblesResourcesDB.get_book_by_id(book_ref_id)
if details:
return details[u'chapters']
return 0
@staticmethod
def get_verse_count(book_id, chapter):
def get_verse_count(book_ref_id, chapter):
"""
Return the number of verses in a chapter.
@ -673,9 +699,9 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``chapter``
The number of the chapter.
"""
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id,
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_ref_id,
chapter)
details = BiblesResourcesDB.get_chapter(book_id, chapter)
details = BiblesResourcesDB.get_chapter(book_ref_id, chapter)
if details:
return details[u'verse_count']
return 0

View File

@ -33,7 +33,8 @@ 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, get_reference_separator
from openlp.plugins.bibles.lib import parse_reference, \
get_reference_separator, LanguageSelection
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from csvbible import CSVBible
from http import HTTPBible
@ -227,6 +228,19 @@ class BibleManager(object):
for book in self.db_cache[bible].get_books()
]
def get_book_by_id(self, bible, id):
"""
Returns a book object by given id.
``bible``
Unicode. The Bible to get the list of books from.
``id``
Unicode. The book_reference_id to get the book for.
"""
log.debug(u'BibleManager.get_book_by_id("%s", "%s")', bible, id)
return self.db_cache[bible].get_book_by_book_ref_id(id)
def get_chapter_count(self, bible, book):
"""
Returns the number of Chapters for a given book.
@ -252,7 +266,16 @@ class BibleManager(object):
book_ref_id = db_book.book_reference_id
return self.db_cache[bible].get_verse_count(book_ref_id, chapter)
def get_verses(self, bible, versetext, firstbible=False, show_error=True):
def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter):
"""
Returns all the number of verses for a given
book_ref_id and chapterMaxBibleBookVerses.
"""
log.debug(u'BibleManager.get_verse_count_by_book_ref_id("%s", "%s", '
u'"%s")', bible, book_ref_id, chapter)
return self.db_cache[bible].get_verse_count(book_ref_id, chapter)
def get_verses(self, bible, versetext, book_ref_id=False, show_error=True):
"""
Parses a scripture reference, fetches the verses from the Bible
specified, and returns a list of ``Verse`` objects.
@ -270,6 +293,10 @@ class BibleManager(object):
- Genesis 1:1-10,15-20
- Genesis 1:1-2:10
- Genesis 1:1-10,2:1-10
``book_ref_id``
Unicode. The book referece id from the book in versetext.
For second bible this is necessary.
"""
log.debug(u'BibleManager.get_verses("%s", "%s")', bible, versetext)
if not bible:
@ -282,30 +309,12 @@ class BibleManager(object):
'Import Wizard to install one or more Bibles.')
})
return None
reflist = parse_reference(versetext)
language_selection = QtCore.QSettings().value(
self.settingsSection + u'/bookname language',
QtCore.QVariant(0)).toInt()[0]
reflist = parse_reference(versetext, self.db_cache[bible],
language_selection, book_ref_id)
if reflist:
new_reflist = []
for item in reflist:
if item:
if firstbible:
db_book = self.db_cache[firstbible].get_book(item[0])
db_book = self.db_cache[bible].get_book_by_book_ref_id(
db_book.book_reference_id)
else:
db_book = self.db_cache[bible].get_book(item[0])
if db_book:
book_id = db_book.book_reference_id
log.debug(u'Book name corrected to "%s"', db_book.name)
new_reflist.append((book_id, item[1], item[2],
item[3]))
else:
log.debug(u'OpenLP failed to find book %s', item[0])
critical_error_message_box(
translate('BiblesPlugin', 'No Book Found'),
translate('BiblesPlugin', 'No matching book '
'could be found in this Bible. Check that you have '
'spelled the name of the book correctly.'))
reflist = new_reflist
return self.db_cache[bible].get_verses(reflist, show_error)
else:
if show_error:

View File

@ -38,7 +38,8 @@ 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_separator
VerseReferenceList, get_reference_separator, LanguageSelection, BibleStrings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__)
@ -353,9 +354,12 @@ class BibleMediaItem(MediaManagerItem):
find_and_set_in_combo_box(self.quickVersionComboBox, bible)
self.quickSearchEdit.setSearchTypes([
(BibleSearch.Reference, u':/bibles/bibles_search_reference.png',
translate('BiblesPlugin.MediaItem', 'Scripture Reference')),
translate('BiblesPlugin.MediaItem', 'Scripture Reference'),
translate(
'BiblesPlugin.MediaItem', 'Search Scripture Reference...')),
(BibleSearch.Text, u':/bibles/bibles_search_text.png',
translate('BiblesPlugin.MediaItem', 'Text Search'))
translate('BiblesPlugin.MediaItem', 'Text Search'),
translate('BiblesPlugin.MediaItem', 'Search Text...'))
])
self.quickSearchEdit.setCurrentSearchType(QtCore.QSettings().value(
u'%s/last search type' % self.settingsSection,
@ -424,20 +428,37 @@ class BibleMediaItem(MediaManagerItem):
book_data = book_data_temp
self.advancedBookComboBox.clear()
first = True
language_selection = QtCore.QSettings().value(
self.settingsSection + u'/bookname language',
QtCore.QVariant(0)).toInt()[0]
booknames = BibleStrings().Booknames
for book in book_data:
row = self.advancedBookComboBox.count()
self.advancedBookComboBox.addItem(book[u'name'])
if language_selection == LanguageSelection.Bible:
self.advancedBookComboBox.addItem(book[u'name'])
elif language_selection == LanguageSelection.Application:
data = BiblesResourcesDB.get_book_by_id(
book[u'book_reference_id'])
self.advancedBookComboBox.addItem(
booknames[data[u'abbreviation']])
elif language_selection == LanguageSelection.English:
data = BiblesResourcesDB.get_book_by_id(
book[u'book_reference_id'])
self.advancedBookComboBox.addItem(data[u'name'])
self.advancedBookComboBox.setItemData(
row, QtCore.QVariant(book[u'chapters']))
row, QtCore.QVariant(book[u'book_reference_id']))
if first:
first = False
self.initialiseChapterVerse(bible, book[u'name'],
book[u'chapters'])
book[u'book_reference_id'])
def initialiseChapterVerse(self, bible, book, chapter_count):
log.debug(u'initialiseChapterVerse %s, %s', bible, book)
self.chapter_count = chapter_count
verse_count = self.plugin.manager.get_verse_count(bible, book, 1)
def initialiseChapterVerse(self, bible, book, book_ref_id):
log.debug(u'initialiseChapterVerse %s, %s, %s', bible, book,
book_ref_id)
book = self.plugin.manager.get_book_by_id(bible, book_ref_id)
self.chapter_count = self.plugin.manager.get_chapter_count(bible, book)
verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(bible,
book_ref_id, 1)
if verse_count == 0:
self.advancedSearchButton.setEnabled(False)
critical_error_message_box(
@ -456,6 +477,7 @@ class BibleMediaItem(MediaManagerItem):
completion depends on the bible. It is only updated when we are doing a
reference search, otherwise the auto completion list is removed.
"""
log.debug(u'updateAutoCompleter')
# Save the current search type to the configuration.
QtCore.QSettings().setValue(u'%s/last search type' %
self.settingsSection,
@ -480,7 +502,23 @@ class BibleMediaItem(MediaManagerItem):
secondbook.book_reference_id:
book_data_temp.append(book)
book_data = book_data_temp
books = [book.name + u' ' for book in book_data]
language_selection = QtCore.QSettings().value(
self.settingsSection + u'/bookname language',
QtCore.QVariant(0)).toInt()[0]
if language_selection == LanguageSelection.Bible:
books = [book.name + u' ' for book in book_data]
elif language_selection == LanguageSelection.Application:
booknames = BibleStrings().Booknames
for book in book_data:
data = BiblesResourcesDB.get_book_by_id(
book.book_reference_id)
books.append(unicode(
booknames[data[u'abbreviation']]) + u' ')
elif language_selection == LanguageSelection.English:
for book in book_data:
data = BiblesResourcesDB.get_book_by_id(
book.book_reference_id)
books.append(data[u'name'] + u' ')
books.sort(cmp=locale.strcoll)
add_widget_completer(books, self.quickSearchEdit)
@ -547,29 +585,31 @@ class BibleMediaItem(MediaManagerItem):
self.initialiseChapterVerse(
unicode(self.advancedVersionComboBox.currentText()),
unicode(self.advancedBookComboBox.currentText()),
self.advancedBookComboBox.itemData(item).toInt()[0])
unicode(self.advancedBookComboBox.itemData(item).toString()))
def onAdvancedFromVerse(self):
chapter_from = int(self.advancedFromChapter.currentText())
chapter_to = int(self.advancedToChapter.currentText())
if chapter_from == chapter_to:
bible = unicode(self.advancedVersionComboBox.currentText())
book = unicode(self.advancedBookComboBox.currentText())
book_ref_id = unicode(self.advancedBookComboBox.itemData(
int(self.advancedBookComboBox.currentIndex())).toString())
verse_from = int(self.advancedFromVerse.currentText())
verse_count = self.plugin.manager.get_verse_count(bible, book,
chapter_to)
verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(
bible, book_ref_id, chapter_to)
self.adjustComboBox(verse_from, verse_count,
self.advancedToVerse, True)
def onAdvancedToChapter(self):
bible = unicode(self.advancedVersionComboBox.currentText())
book = unicode(self.advancedBookComboBox.currentText())
book_ref_id = unicode(self.advancedBookComboBox.itemData(
int(self.advancedBookComboBox.currentIndex())).toString())
chapter_from = int(self.advancedFromChapter.currentText())
chapter_to = int(self.advancedToChapter.currentText())
verse_from = int(self.advancedFromVerse.currentText())
verse_to = int(self.advancedToVerse.currentText())
verse_count = self.plugin.manager.get_verse_count(bible, book,
chapter_to)
verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(bible,
book_ref_id, chapter_to)
if chapter_from == chapter_to and verse_from > verse_to:
self.adjustComboBox(verse_from, verse_count, self.advancedToVerse)
else:
@ -577,11 +617,12 @@ class BibleMediaItem(MediaManagerItem):
def onAdvancedFromChapter(self):
bible = unicode(self.advancedVersionComboBox.currentText())
book = unicode(self.advancedBookComboBox.currentText())
book_ref_id = unicode(self.advancedBookComboBox.itemData(
int(self.advancedBookComboBox.currentIndex())).toString())
chapter_from = int(self.advancedFromChapter.currentText())
chapter_to = int(self.advancedToChapter.currentText())
verse_count = self.plugin.manager.get_verse_count(bible, book,
chapter_from)
verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(bible,
book_ref_id, chapter_from)
self.adjustComboBox(1, verse_count, self.advancedFromVerse)
if chapter_from > chapter_to:
self.adjustComboBox(1, verse_count, self.advancedToVerse)
@ -630,6 +671,8 @@ class BibleMediaItem(MediaManagerItem):
bible = unicode(self.advancedVersionComboBox.currentText())
second_bible = unicode(self.advancedSecondComboBox.currentText())
book = unicode(self.advancedBookComboBox.currentText())
book_ref_id = unicode(self.advancedBookComboBox.itemData(
int(self.advancedBookComboBox.currentIndex())).toString())
chapter_from = self.advancedFromChapter.currentText()
chapter_to = self.advancedToChapter.currentText()
verse_from = self.advancedFromVerse.currentText()
@ -640,10 +683,11 @@ class BibleMediaItem(MediaManagerItem):
range_separator + chapter_to + verse_separator + verse_to
versetext = u'%s %s' % (book, verse_range)
Receiver.send_message(u'cursor_busy')
self.search_results = self.plugin.manager.get_verses(bible, versetext)
self.search_results = self.plugin.manager.get_verses(bible, versetext,
book_ref_id)
if second_bible:
self.second_search_results = self.plugin.manager.get_verses(
second_bible, versetext, bible)
second_bible, versetext, book_ref_id)
if not self.advancedLockButton.isChecked():
self.listView.clear()
if self.listView.count() != 0:
@ -671,7 +715,8 @@ class BibleMediaItem(MediaManagerItem):
self.search_results = self.plugin.manager.get_verses(bible, text)
if second_bible and self.search_results:
self.second_search_results = self.plugin.manager.get_verses(
second_bible, text, bible)
second_bible, text,
self.search_results[0].book.book_reference_id)
else:
# We are doing a 'Text Search'.
Receiver.send_message(u'cursor_busy')
@ -984,13 +1029,13 @@ class BibleMediaItem(MediaManagerItem):
return u'{su}[%s]{/su}' % verse_text
return u'{su}%s{/su}' % verse_text
def search(self, string):
def search(self, string, showError):
"""
Search for some Bible verses (by reference).
"""
bible = unicode(self.quickVersionComboBox.currentText())
search_results = self.plugin.manager.get_verses(bible, string, False,
False)
showError)
if search_results:
versetext = u' '.join([verse.text for verse in search_results])
return [[string, versetext]]

View File

@ -92,9 +92,10 @@ class CustomMediaItem(MediaManagerItem):
def initialise(self):
self.searchTextEdit.setSearchTypes([
(CustomSearch.Titles, u':/songs/song_search_title.png',
translate('SongsPlugin.MediaItem', 'Titles')),
translate('SongsPlugin.MediaItem', 'Titles'),
translate('SongsPlugin.MediaItem', 'Search Titles...')),
(CustomSearch.Themes, u':/slides/slide_theme.png',
UiStrings().Themes)
UiStrings().Themes, UiStrings().SearchThemes)
])
self.loadList(self.manager.get_all_objects(
CustomSlide, order_by_ref=CustomSlide.title))
@ -177,7 +178,7 @@ class CustomMediaItem(MediaManagerItem):
UiStrings().ConfirmDelete,
translate('CustomPlugin.MediaItem',
'Are you sure you want to delete the %n selected custom'
' slides(s)?', '',
' slide(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
@ -267,7 +268,7 @@ class CustomMediaItem(MediaManagerItem):
self.searchTextEdit.clear()
self.onSearchTextButtonClick()
def search(self, string):
def search(self, string, showError):
search_results = self.manager.get_all_objects(CustomSlide,
or_(func.lower(CustomSlide.title).like(u'%' +
string.lower() + u'%'),

View File

@ -189,7 +189,7 @@ class ImageMediaItem(MediaManagerItem):
# Continue with the existing images.
for bitem in items:
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
(path, name) = os.path.split(filename)
name = os.path.split(filename)[1]
service_item.add_from_image(filename, name, background)
return True
@ -220,7 +220,7 @@ class ImageMediaItem(MediaManagerItem):
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename):
(path, name) = os.path.split(filename)
name = os.path.split(filename)[1]
if self.plugin.liveController.display.directImage(name,
filename, background):
self.resetAction.setVisible(True)
@ -234,7 +234,7 @@ class ImageMediaItem(MediaManagerItem):
'There was a problem replacing your background, '
'the image file "%s" no longer exists.')) % filename)
def search(self, string):
def search(self, string, showError):
files = SettingsManager.load_list(self.settingsSection, u'images')
results = []
string = string.lower()

View File

@ -37,6 +37,7 @@ from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
media_item_combo_box
from openlp.core.ui import Controller, Display
from openlp.core.ui.media import get_media_players, set_media_players
log = logging.getLogger(__name__)
@ -142,8 +143,11 @@ class MediaMediaItem(MediaManagerItem):
self.overridePlayerChanged)
def overridePlayerChanged(self, index):
Receiver.send_message(u'media_override_player', \
u'%s' % self.displayTypeComboBox.currentText())
player = get_media_players()[0]
if index == 0:
set_media_players(player)
else:
set_media_players(player, player[index-1])
def onResetClick(self):
"""
@ -239,28 +243,31 @@ class MediaMediaItem(MediaManagerItem):
self.plugin.mediaController.setup_display( \
self.mediaController.previewDisplay)
def populateDisplayTypes(self):
"""
Load the combobox with the enabled media players,
allowing user to select a specific player if settings allow
"""
# block signals to avoid unnecessary overridePlayerChanged Signales
# while combo box creation
self.displayTypeComboBox.blockSignals(True)
self.displayTypeComboBox.clear()
playerSettings = str(QtCore.QSettings().value(u'media/players',
QtCore.QVariant(u'webkit')).toString())
usedPlayers = playerSettings.split(u',')
for title in usedPlayers:
usedPlayers, overridePlayer = get_media_players()
mediaPlayers = self.plugin.mediaController.mediaPlayers
currentIndex = 0
for player in usedPlayers:
# load the drop down selection
self.displayTypeComboBox.addItem(title)
self.displayTypeComboBox.addItem(mediaPlayers[player].original_name)
if overridePlayer == player:
currentIndex = len(self.displayTypeComboBox)
if self.displayTypeComboBox.count() > 1:
self.displayTypeComboBox.insertItem(0, self.automatic)
self.displayTypeComboBox.setCurrentIndex(0)
if QtCore.QSettings().value(self.settingsSection + u'/override player',
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
self.displayTypeComboBox.setCurrentIndex(currentIndex)
if overridePlayer:
self.mediaWidget.show()
else:
self.mediaWidget.hide()
self.displayTypeComboBox.blockSignals(False)
def onDeleteClick(self):
"""
@ -309,7 +316,7 @@ class MediaMediaItem(MediaManagerItem):
media = filter(lambda x: os.path.splitext(x)[1] in ext, media)
return media
def search(self, string):
def search(self, string, showError):
files = SettingsManager.load_list(self.settingsSection, u'media')
results = []
string = string.lower()

View File

@ -28,7 +28,15 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, Receiver
from openlp.core.lib.ui import UiStrings
from openlp.core.lib.ui import UiStrings, create_up_down_push_button_set
from openlp.core.ui.media import get_media_players, set_media_players
class MediaQCheckBox(QtGui.QCheckBox):
"""
MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
"""
def setPlayerName(self, name):
self.playerName = name
class MediaTab(SettingsTab):
"""
@ -49,7 +57,7 @@ class MediaTab(SettingsTab):
self.playerCheckBoxes = {}
for key, player in self.mediaPlayers.iteritems():
player = self.mediaPlayers[key]
checkbox = QtGui.QCheckBox(self.mediaPlayerGroupBox)
checkbox = MediaQCheckBox(self.mediaPlayerGroupBox)
checkbox.setEnabled(player.available)
checkbox.setObjectName(player.name + u'CheckBox')
self.playerCheckBoxes[player.name] = checkbox
@ -57,7 +65,7 @@ class MediaTab(SettingsTab):
self.leftLayout.addWidget(self.mediaPlayerGroupBox)
self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn)
self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox')
self.playerOrderLayout = QtGui.QVBoxLayout(self.playerOrderGroupBox)
self.playerOrderLayout = QtGui.QHBoxLayout(self.playerOrderGroupBox)
self.playerOrderLayout.setObjectName(u'playerOrderLayout')
self.playerOrderlistWidget = QtGui.QListWidget( \
self.playerOrderGroupBox)
@ -76,18 +84,15 @@ class MediaTab(SettingsTab):
QtGui.QAbstractItemView.NoEditTriggers)
self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget')
self.playerOrderLayout.addWidget(self.playerOrderlistWidget)
self.orderingButtonsWidget = QtGui.QWidget(self.playerOrderGroupBox)
self.orderingButtonsWidget.setObjectName(u'orderingButtonsWidget')
self.orderingButtonLayout = QtGui.QHBoxLayout( \
self.orderingButtonsWidget)
self.orderingButtonLayout = QtGui.QVBoxLayout()
self.orderingButtonLayout.setObjectName(u'orderingButtonLayout')
self.orderingDownButton = QtGui.QPushButton(self.orderingButtonsWidget)
self.orderingDownButton.setObjectName(u'orderingDownButton')
self.orderingButtonLayout.addWidget(self.orderingDownButton)
self.orderingUpButton = QtGui.QPushButton(self.playerOrderGroupBox)
self.orderingUpButton.setObjectName(u'orderingUpButton')
self.orderingButtonLayout.addStretch(1)
self.orderingUpButton, self.orderingDownButton = \
create_up_down_push_button_set(self)
self.orderingButtonLayout.addWidget(self.orderingUpButton)
self.playerOrderLayout.addWidget(self.orderingButtonsWidget)
self.orderingButtonLayout.addWidget(self.orderingDownButton)
self.orderingButtonLayout.addStretch(1)
self.playerOrderLayout.addLayout(self.orderingButtonLayout)
self.leftLayout.addWidget(self.playerOrderGroupBox)
self.advancedGroupBox = QtGui.QGroupBox(self.leftColumn)
self.advancedGroupBox.setObjectName(u'advancedGroupBox')
@ -105,10 +110,6 @@ class MediaTab(SettingsTab):
QtCore.QObject.connect(checkbox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onPlayerCheckBoxChanged)
QtCore.QObject.connect(self.orderingUpButton,
QtCore.SIGNAL(u'pressed()'), self.onOrderingUpButtonPressed)
QtCore.QObject.connect(self.orderingDownButton,
QtCore.SIGNAL(u'pressed()'), self.onOrderingDownButtonPressed)
def retranslateUi(self):
self.mediaPlayerGroupBox.setTitle(
@ -116,31 +117,28 @@ class MediaTab(SettingsTab):
for key in self.mediaPlayers:
player = self.mediaPlayers[key]
checkbox = self.playerCheckBoxes[player.name]
checkbox.setPlayerName(player.name)
if player.available:
checkbox.setText(player.name)
checkbox.setText(player.display_name)
else:
checkbox.setText(
unicode(translate('MediaPlugin.MediaTab',
'%s (unavailable)')) % player.name)
'%s (unavailable)')) % player.display_name)
self.playerOrderGroupBox.setTitle(
translate('MediaPlugin.MediaTab', 'Player Order'))
self.orderingDownButton.setText(
translate('MediaPlugin.MediaTab', 'Down'))
self.orderingUpButton.setText(
translate('MediaPlugin.MediaTab', 'Up'))
self.advancedGroupBox.setTitle(UiStrings().Advanced)
self.overridePlayerCheckBox.setText(
translate('MediaPlugin.MediaTab',
'Allow media player to be overriden'))
'Allow media player to be overridden'))
def onPlayerCheckBoxChanged(self, check_state):
player = self.sender().text()
player = self.sender().playerName
if check_state == QtCore.Qt.Checked:
if player not in self.usedPlayers:
self.usedPlayers.append(player)
else:
if player in self.usedPlayers:
self.usedPlayers.takeAt(self.usedPlayers.indexOf(player))
self.usedPlayers.remove(player)
self.updatePlayerList()
def updatePlayerList(self):
@ -152,31 +150,31 @@ class MediaTab(SettingsTab):
self.playerCheckBoxes[u'%s' % player].setEnabled(False)
else:
self.playerCheckBoxes[u'%s' % player].setEnabled(True)
self.playerOrderlistWidget.addItem(player)
self.playerOrderlistWidget.addItem(
self.mediaPlayers[unicode(player)].original_name)
def onOrderingUpButtonPressed(self):
currentRow = self.playerOrderlistWidget.currentRow()
if currentRow > 0:
item = self.playerOrderlistWidget.takeItem(currentRow)
self.playerOrderlistWidget.insertItem(currentRow - 1, item)
self.playerOrderlistWidget.setCurrentRow(currentRow - 1)
self.usedPlayers.move(currentRow, currentRow - 1)
def onUpButtonClicked(self):
row = self.playerOrderlistWidget.currentRow()
if row <= 0:
return
item = self.playerOrderlistWidget.takeItem(row)
self.playerOrderlistWidget.insertItem(row - 1, item)
self.playerOrderlistWidget.setCurrentRow(row - 1)
self.usedPlayers.insert(row - 1, self.usedPlayers.pop(row))
def onOrderingDownButtonPressed(self):
currentRow = self.playerOrderlistWidget.currentRow()
if currentRow < self.playerOrderlistWidget.count() - 1:
item = self.playerOrderlistWidget.takeItem(currentRow)
self.playerOrderlistWidget.insertItem(currentRow + 1, item)
self.playerOrderlistWidget.setCurrentRow(currentRow + 1)
self.usedPlayers.move(currentRow, currentRow + 1)
def onDownButtonClicked(self):
row = self.playerOrderlistWidget.currentRow()
if row == -1 or row > self.playerOrderlistWidget.count() - 1:
return
item = self.playerOrderlistWidget.takeItem(row)
self.playerOrderlistWidget.insertItem(row + 1, item)
self.playerOrderlistWidget.setCurrentRow(row + 1)
self.usedPlayers.insert(row + 1, self.usedPlayers.pop(row))
def load(self):
if self.savedUsedPlayers:
self.usedPlayers = self.savedUsedPlayers
self.savedUsedPlayers = None
self.usedPlayers = QtCore.QSettings().value(
self.settingsSection + u'/players',
QtCore.QVariant(u'webkit')).toString().split(u',')
self.usedPlayers = get_media_players()[0]
self.savedUsedPlayers = self.usedPlayers
for key in self.mediaPlayers:
player = self.mediaPlayers[key]
@ -193,18 +191,14 @@ class MediaTab(SettingsTab):
def save(self):
override_changed = False
player_string_changed = False
old_players = QtCore.QSettings().value(
self.settingsSection + u'/players',
QtCore.QVariant(u'webkit')).toString()
new_players = self.usedPlayers.join(u',')
if old_players != new_players:
old_players, override_player = get_media_players()
if self.usedPlayers != old_players:
# clean old Media stuff
QtCore.QSettings().setValue(self.settingsSection + u'/players',
QtCore.QVariant(new_players))
set_media_players(self.usedPlayers, override_player)
player_string_changed = True
override_changed = True
setting_key = self.settingsSection + u'/override player'
if QtCore.QSettings().value(setting_key) != \
if QtCore.QSettings().value(setting_key).toInt()[0] != \
self.overridePlayerCheckBox.checkState():
QtCore.QSettings().setValue(setting_key,
QtCore.QVariant(self.overridePlayerCheckBox.checkState()))

View File

@ -322,7 +322,7 @@ class PresentationMediaItem(MediaManagerItem):
return controller
return None
def search(self, string):
def search(self, string, showError):
files = SettingsManager.load_list(
self.settingsSection, u'presentations')
results = []

View File

@ -85,7 +85,7 @@ class PresentationTab(SettingsTab):
self.AdvancedGroupBox.setTitle(UiStrings().Advanced)
self.OverrideAppCheckBox.setText(
translate('PresentationPlugin.PresentationTab',
'Allow presentation application to be overriden'))
'Allow presentation application to be overridden'))
def setControllerText(self, checkbox, controller):
if checkbox.isEnabled():

View File

@ -4,12 +4,12 @@
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
# Woldsund #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #

View File

@ -1,12 +1,12 @@
/*****************************************************************************
* OpenLP - Open Source Lyrics Projection *
* ------------------------------------------------------------------------- *
* Copyright (c) 2008-2010 Raoul Snyman *
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
* Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, *
* Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, *
* Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode *
* Woldsund *
* Copyright (c) 2008-2012 Raoul Snyman *
* Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan *
* Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, *
* Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias *
* Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund *
* ------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *

View File

@ -1,12 +1,12 @@
/*****************************************************************************
* OpenLP - Open Source Lyrics Projection *
* ------------------------------------------------------------------------- *
* Copyright (c) 2008-2010 Raoul Snyman *
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
* Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, *
* Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, *
* Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode *
* Woldsund *
* Copyright (c) 2008-2012 Raoul Snyman *
* Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan *
* Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, *
* Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias *
* Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund *
* ------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *
@ -72,7 +72,9 @@ window.OpenLP = {
);
},
loadController: function (event) {
event.preventDefault();
if (event) {
event.preventDefault();
}
$.getJSON(
"/api/controller/live/text",
function (data, status) {
@ -91,6 +93,7 @@ window.OpenLP = {
li.children("a").click(OpenLP.setSlide);
ul.append(li);
}
OpenLP.currentItem = data.results.item;
ul.listview("refresh");
}
);
@ -208,9 +211,8 @@ window.OpenLP = {
},
showAlert: function (event) {
event.preventDefault();
var text = "{\"request\": {\"text\": \"" +
$("#alert-text").val().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") +
"\"}}";
var alert = OpenLP.escapeString($("#alert-text").val())
var text = "{\"request\": {\"text\": \"" + alert + "\"}}";
$.getJSON(
"/api/alert",
{"data": text},
@ -221,9 +223,8 @@ window.OpenLP = {
},
search: function (event) {
event.preventDefault();
var text = "{\"request\": {\"text\": \"" +
$("#search-text").val().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") +
"\"}}";
var query = OpenLP.escapeString($("#search-text").val())
var text = "{\"request\": {\"text\": \"" + query + "\"}}";
$.getJSON(
"/api/" + $("#search-plugin").val() + "/search",
{"data": text},
@ -280,6 +281,9 @@ window.OpenLP = {
);
$("#options").dialog("close");
$.mobile.changePage("#service-manager");
},
escapeString: function (string) {
return string.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")
}
}
// Service Manager

View File

@ -1,12 +1,12 @@
/*****************************************************************************
* OpenLP - Open Source Lyrics Projection *
* ------------------------------------------------------------------------- *
* Copyright (c) 2008-2010 Raoul Snyman *
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
* Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, *
* Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, *
* Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode *
* Woldsund *
* Copyright (c) 2008-2012 Raoul Snyman *
* Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan *
* Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, *
* Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias *
* Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund *
* ------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *
@ -46,7 +46,7 @@ body {
}
#clock {
font-size: 40pt;
font-size: 30pt;
color: yellow;
text-align: right;
}

View File

@ -4,12 +4,12 @@
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
# Woldsund #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #

View File

@ -1,12 +1,12 @@
/*****************************************************************************
* OpenLP - Open Source Lyrics Projection *
* ------------------------------------------------------------------------- *
* Copyright (c) 2008-2010 Raoul Snyman *
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
* Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, *
* Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, *
* Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode *
* Woldsund *
* Copyright (c) 2008-2012 Raoul Snyman *
* Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan *
* Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, *
* Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias *
* Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund *
* ------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *

View File

@ -111,15 +111,12 @@ the remotes.
{"results": {"items": [{...}, {...}]}}
"""
import json
import logging
import os
import urlparse
import re
try:
import json
except ImportError:
import simplejson as json
import urllib
import urlparse
from PyQt4 import QtCore, QtNetwork
from mako.template import Template
@ -314,11 +311,14 @@ class HttpConnection(object):
"""
log.debug(u'ready to read socket')
if self.socket.canReadLine():
data = self.socket.readLine()
data = QtCore.QByteArray.fromPercentEncoding(data)
data = unicode(data, 'utf8')
log.debug(u'received: ' + data)
words = data.split(u' ')
data = str(self.socket.readLine())
try:
log.debug(u'received: ' + data)
except UnicodeDecodeError:
# Malicious request containing non-ASCII characters.
self.close()
return
words = data.split(' ')
response = None
if words[0] == u'GET':
url = urlparse.urlparse(words[1])
@ -426,9 +426,19 @@ class HttpConnection(object):
"""
Send an alert.
"""
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
Receiver.send_message(u'alerts_text', [text])
return HttpResponse(json.dumps({u'results': {u'success': True}}),
plugin = self.parent.plugin.pluginManager.get_plugin_by_name("alerts")
if plugin.status == PluginStatus.Active:
try:
text = json.loads(
self.url_params[u'data'][0])[u'request'][u'text']
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
text = urllib.unquote(text)
Receiver.send_message(u'alerts_text', [text])
success = True
else:
success = False
return HttpResponse(json.dumps({u'results': {u'success': success}}),
{u'Content-Type': u'application/json'})
def controller(self, type, action):
@ -463,9 +473,14 @@ class HttpConnection(object):
item[u'selected'] = (self.parent.current_slide == index)
data.append(item)
json_data = {u'results': {u'slides': data}}
if current_item:
json_data[u'results'][u'item'] = self.parent.current_item._uuid
else:
if self.url_params and self.url_params.get(u'data'):
data = json.loads(self.url_params[u'data'][0])
try:
data = json.loads(self.url_params[u'data'][0])
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
log.info(data)
# This slot expects an int within a list.
id = data[u'request'][u'id']
@ -485,7 +500,10 @@ class HttpConnection(object):
else:
event += u'_item'
if self.url_params and self.url_params.get(u'data'):
data = json.loads(self.url_params[u'data'][0])
try:
data = json.loads(self.url_params[u'data'][0])
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
Receiver.send_message(event, data[u'request'][u'id'])
else:
Receiver.send_message(event)
@ -518,11 +536,15 @@ class HttpConnection(object):
``type``
The plugin name to search in.
"""
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
try:
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
text = urllib.unquote(text)
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and \
plugin.mediaItem and plugin.mediaItem.hasSearch:
results = plugin.mediaItem.search(text)
results = plugin.mediaItem.search(text, False)
else:
results = []
return HttpResponse(
@ -533,20 +555,28 @@ class HttpConnection(object):
"""
Go live on an item of type ``type``.
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
try:
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and plugin.mediaItem:
plugin.mediaItem.goLive(id, remote=True)
return HttpResponse(code=u'200 OK')
def add_to_service(self, type):
"""
Add item of type ``type`` to the end of the service.
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
try:
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
except KeyError, ValueError:
return HttpResponse(code=u'400 Bad Request')
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and plugin.mediaItem:
item_id = plugin.mediaItem.createItemFromId(id)
plugin.mediaItem.addToService(item_id, remote=True)
return HttpResponse(code=u'200 OK')
def send_response(self, response):
http = u'HTTP/1.1 %s\r\n' % response.code

View File

@ -27,7 +27,7 @@
from PyQt4 import QtCore, QtGui, QtNetwork
from openlp.core.lib import SettingsTab, translate
from openlp.core.lib import SettingsTab, translate, Receiver
ZERO_URL = u'0.0.0.0'
@ -87,7 +87,8 @@ class RemoteTab(SettingsTab):
self.qrLayout = QtGui.QVBoxLayout(self.androidAppGroupBox)
self.qrLayout.setObjectName(u'qrLayout')
self.qrCodeLabel = QtGui.QLabel(self.androidAppGroupBox)
self.qrCodeLabel.setPixmap(QtGui.QPixmap(u':/remotes/android_app_qr.png'))
self.qrCodeLabel.setPixmap(QtGui.QPixmap(
u':/remotes/android_app_qr.png'))
self.qrCodeLabel.setAlignment(QtCore.Qt.AlignCenter)
self.qrCodeLabel.setObjectName(u'qrCodeLabel')
self.qrLayout.addWidget(self.qrCodeLabel)
@ -160,12 +161,20 @@ class RemoteTab(SettingsTab):
self.setUrls()
def save(self):
changed = False
if QtCore.QSettings().value(self.settingsSection + u'/ip address',
QtCore.QVariant(ZERO_URL).toString() != self.addressEdit.text() or
QtCore.QSettings().value(self.settingsSection + u'/port',
QtCore.QVariant(4316).toInt()[0]) != self.portSpinBox.value()):
changed = True
QtCore.QSettings().setValue(self.settingsSection + u'/port',
QtCore.QVariant(self.portSpinBox.value()))
QtCore.QSettings().setValue(self.settingsSection + u'/ip address',
QtCore.QVariant(self.addressEdit.text()))
QtCore.QSettings().setValue(self.settingsSection + u'/twelve hour',
QtCore.QVariant(self.twelveHour))
if changed:
Receiver.send_message(u'remotes_config_updated')
def onTwelveHourCheckBoxChanged(self, check_state):
self.twelveHour = False

View File

@ -86,3 +86,11 @@ class RemotesPlugin(Plugin):
self.textStrings[StringContent.VisibleName] = {
u'title': translate('RemotePlugin', 'Remote', 'container title')
}
def configUpdated(self):
"""
Called when Config is changed to restart the server on new address or
port
"""
self.finalise()
self.initialise()

View File

@ -280,8 +280,15 @@ class Ui_EditSongDialog(object):
self.songTabWidget.addTab(self.audioTab, u'')
# Last few bits
self.dialogLayout.addWidget(self.songTabWidget)
self.bottomLayout = QtGui.QHBoxLayout()
self.bottomLayout.setObjectName(u'bottomLayout')
self.warningLabel = QtGui.QLabel(editSongDialog)
self.warningLabel.setObjectName(u'warningLabel')
self.warningLabel.setVisible(False)
self.bottomLayout.addWidget(self.warningLabel)
self.buttonBox = create_accept_reject_button_box(editSongDialog)
self.dialogLayout.addWidget(self.buttonBox)
self.bottomLayout.addWidget(self.buttonBox)
self.dialogLayout.addLayout(self.bottomLayout)
self.retranslateUi(editSongDialog)
QtCore.QMetaObject.connectSlotsByName(editSongDialog)
@ -349,14 +356,19 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', '&Remove'))
self.audioRemoveAllButton.setText(
translate('SongsPlugin.EditSongForm', 'Remove &All'))
self.warningLabel.setText(
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong>'
' Not all of the verses are in use.'))
def editSongDialogComboBox(parent, name):
"""
Utility method to generate a standard combo box for this dialog.
"""
comboBox = QtGui.QComboBox(parent)
comboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
comboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
comboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
comboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
comboBox.setEditable(True)
comboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
comboBox.setObjectName(name)

View File

@ -92,6 +92,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtCore.QObject.connect(self.verseListWidget,
QtCore.SIGNAL(u'itemClicked(QTableWidgetItem*)'),
self.onVerseListViewPressed)
QtCore.QObject.connect(self.verseOrderEdit,
QtCore.SIGNAL(u'textChanged(QString)'),
self.onVerseOrderTextChanged)
QtCore.QObject.connect(self.themeAddButton,
QtCore.SIGNAL(u'clicked()'),
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
@ -358,7 +361,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def onAuthorAddButtonClicked(self):
item = int(self.authorsComboBox.currentIndex())
text = unicode(self.authorsComboBox.currentText())
text = unicode(self.authorsComboBox.currentText()).strip(u' \r\n\t')
# This if statement is for OS X, which doesn't seem to work well with
# the QCompleter autocompletion class. See bug #812628.
if text in self.authors:
# Index 0 is a blank string, so add 1
item = self.authors.index(text) + 1
if item == 0 and text:
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.EditSongForm', 'Add Author'),
@ -481,6 +489,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseListWidget.setItem(
self.verseListWidget.rowCount() - 1, 0, item)
self.tagRows()
# Check if all verse tags are used.
self.onVerseOrderTextChanged(self.verseOrderEdit.text())
def onVerseEditButtonClicked(self):
item = self.verseListWidget.currentItem()
@ -508,6 +518,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
item.setData(QtCore.Qt.UserRole, tempId[row])
self.verseListWidget.setItem(row, 0, item)
self.tagRows()
# Check if all verse tags are used.
self.onVerseOrderTextChanged(self.verseOrderEdit.text())
def onVerseEditAllButtonClicked(self):
verse_list = u''
@ -567,6 +579,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.tagRows()
self.verseEditButton.setEnabled(False)
self.verseDeleteButton.setEnabled(False)
# Check if all verse tags are used.
self.onVerseOrderTextChanged(self.verseOrderEdit.text())
def onVerseDeleteButtonClicked(self):
self.verseListWidget.removeRow(self.verseListWidget.currentRow())
@ -574,7 +588,76 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseEditButton.setEnabled(False)
self.verseDeleteButton.setEnabled(False)
def _validate_song(self):
def onVerseOrderTextChanged(self, text):
verses = []
verse_names = []
order = self.__extractVerseOrder(text)
for index in range(0, self.verseListWidget.rowCount()):
verse = self.verseListWidget.item(index, 0)
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
if verse not in verse_names:
verses.append(verse)
verse_names.append(u'%s%s' % (
VerseType.translated_tag(verse[0]), verse[1:]))
verses_not_used = []
for verse in verses:
if not verse in order:
verses_not_used.append(verse)
self.warningLabel.setVisible(len(verses_not_used) > 0)
def __extractVerseOrder(self, verse_order):
order = []
order_names = unicode(verse_order).split()
for item in order_names:
if len(item) == 1:
verse_index = VerseType.from_translated_tag(item, None)
if verse_index is not None:
order.append(VerseType.Tags[verse_index] + u'1')
else:
# it matches no verses anyway
order.append(u'')
else:
verse_index = VerseType.from_translated_tag(item[0], None)
if verse_index is None:
# it matches no verses anyway
order.append(u'')
else:
verse_tag = VerseType.Tags[verse_index]
verse_num = item[1:].lower()
order.append(verse_tag + verse_num)
return order
def __validateVerseList(self, verse_order, verse_count):
verses = []
invalid_verses = []
verse_names = []
order_names = unicode(verse_order).split()
order = self.__extractVerseOrder(verse_order)
for index in range(0, verse_count):
verse = self.verseListWidget.item(index, 0)
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
if verse not in verse_names:
verses.append(verse)
verse_names.append(u'%s%s' % (
VerseType.translated_tag(verse[0]), verse[1:]))
for count, item in enumerate(order):
if item not in verses:
invalid_verses.append(order_names[count])
if invalid_verses:
valid = create_separated_list(verse_names)
if len(invalid_verses) > 1:
critical_error_message_box(message=unicode(translate(
'SongsPlugin.EditSongForm', 'The verse order is invalid. '
'There are no verses corresponding to %s. Valid entries '
'are %s.')) % (u', '.join(invalid_verses), valid))
else:
critical_error_message_box(message=unicode(translate(
'SongsPlugin.EditSongForm', 'The verse order is invalid. '
'There is no verse corresponding to %s. Valid entries '
'are %s.')) % (invalid_verses[0], valid))
return len(invalid_verses) == 0
def __validateSong(self):
"""
Check the validity of the song.
"""
@ -604,57 +687,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
'You need to have an author for this song.'))
return False
if self.verseOrderEdit.text():
order = []
order_names = unicode(self.verseOrderEdit.text()).split()
for item in order_names:
if len(item) == 1:
verse_index = VerseType.from_translated_tag(item, None)
if verse_index is not None:
order.append(VerseType.Tags[verse_index] + u'1')
else:
# it matches no verses anyway
order.append(u'')
else:
verse_index = VerseType.from_translated_tag(item[0], None)
if verse_index is None:
# it matches no verses anyway
order.append(u'')
else:
verse_tag = VerseType.Tags[verse_index]
verse_num = item[1:].lower()
order.append(verse_tag + verse_num)
verses = []
verse_names = []
for index in range(0, self.verseListWidget.rowCount()):
verse = self.verseListWidget.item(index, 0)
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
if verse not in verse_names:
verses.append(verse)
verse_names.append(u'%s%s' % (
VerseType.translated_tag(verse[0]), verse[1:]))
for count, item in enumerate(order):
if item not in verses:
valid = create_separated_list(verse_names)
critical_error_message_box(
message=unicode(translate('SongsPlugin.EditSongForm',
'The verse order is invalid. There is no verse '
'corresponding to %s. Valid entries are %s.')) % \
(order_names[count], valid))
return False
for count, verse in enumerate(verses):
if verse not in order:
self.songTabWidget.setCurrentIndex(0)
self.verseOrderEdit.setFocus()
answer = QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Warning'),
unicode(translate('SongsPlugin.EditSongForm',
'You have not used %s anywhere in the verse '
'order. Are you sure you want to save the song '
'like this?')) % verse_names[count],
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
item = int(self.songBookComboBox.currentIndex())
result = self.__validateVerseList(self.verseOrderEdit.text(),
self.verseListWidget.rowCount())
if not result:
return False
text = unicode(self.songBookComboBox.currentText())
if self.songBookComboBox.findText(text, QtCore.Qt.MatchExactly) < 0:
if QtGui.QMessageBox.question(self,
@ -790,7 +826,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
log.debug(u'SongEditForm.accept')
self.clearCaches()
if self._validate_song():
if self.__validateSong():
self.saveSong()
self.song = None
QtGui.QDialog.accept(self)

View File

@ -554,7 +554,9 @@ class SongImportForm(OpenLPWizard):
Get CCLI song database files
"""
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.CCLI,
self.ccliFileListWidget)
self.ccliFileListWidget, u'%s (*.usr *.txt)'
% translate('SongsPlugin.ImportWizardForm',
'CCLI SongSelect Files'))
def onCCLIRemoveButtonClicked(self):
"""
@ -595,15 +597,22 @@ class SongImportForm(OpenLPWizard):
self.removeSelectedItems(self.genericFileListWidget)
def onEasySlidesBrowseButtonClicked(self):
"""
Get EasySlides song database file
"""
self.getFileName(WizardStrings.OpenTypeFile % WizardStrings.ES,
self.easySlidesFilenameEdit)
self.easySlidesFilenameEdit, u'%s (*.xml)'
% translate('SongsPlugin.ImportWizardForm',
'EasySlides XML File'))
def onEWBrowseButtonClicked(self):
"""
Get EasyWorship song database files
"""
self.getFileName(WizardStrings.OpenTypeFile % WizardStrings.EW,
self.ewFilenameEdit)
self.ewFilenameEdit, u'%s (*.db)'
% translate('SongsPlugin.ImportWizardForm',
'EasyWorship Song Database'))
def onSongBeamerAddButtonClicked(self):
"""

View File

@ -29,6 +29,7 @@ import re
from PyQt4 import QtGui
from openlp.core.lib import translate
from openlp.core.utils import CONTROL_CHARS
from db import Author
from ui import SongStrings
@ -256,6 +257,13 @@ def clean_string(string):
Strips punctuation from the passed string to assist searching
"""
return WHITESPACE.sub(u' ', APOSTROPHE.sub(u'', string)).lower()
def clean_title(title):
"""
Cleans the song title by removing Unicode control chars groups C0 & C1,
as well as any trailing spaces
"""
return CONTROL_CHARS.sub(u'', title).rstrip()
def clean_song(manager, song):
"""
@ -275,10 +283,14 @@ def clean_song(manager, song):
song.alternate_title = unicode(song.alternate_title)
if isinstance(song.lyrics, buffer):
song.lyrics = unicode(song.lyrics)
song.title = song.title.rstrip() if song.title else u''
if song.alternate_title is None:
if song.title:
song.title = clean_title(song.title)
else:
song.title = u''
if song.alternate_title:
song.alternate_title = clean_title(song.alternate_title)
else:
song.alternate_title = u''
song.alternate_title = song.alternate_title.strip()
song.search_title = clean_string(song.title) + u'@' + \
clean_string(song.alternate_title)
# Only do this, if we the song is a 1.9.4 song (or older).

View File

@ -159,6 +159,12 @@ class CCLIFileImport(SongImport):
song_author = u''
song_topics = u''
for line in textList:
if line.startswith(u'[S '):
ccli, line = line.split(u']', 1)
if ccli.startswith(u'[S A'):
self.ccliNumber = ccli[4:].strip()
else:
self.ccliNumber = ccli[3:].strip()
if line.startswith(u'Title='):
self.title = line[6:].strip()
elif line.startswith(u'Author='):
@ -166,9 +172,7 @@ class CCLIFileImport(SongImport):
elif line.startswith(u'Copyright='):
self.copyright = line[10:].strip()
elif line.startswith(u'Themes='):
song_topics = line[7:].strip()
elif line.startswith(u'[S A'):
self.ccliNumber = line[4:-3].strip()
song_topics = line[7:].strip().replace(u' | ', u'/t')
elif line.startswith(u'Fields='):
# Fields contain single line indicating verse, chorus, etc,
# /t delimited, same as with words field. store seperately
@ -193,6 +197,7 @@ class CCLIFileImport(SongImport):
check_first_verse_line = True
verse_text = unicode(words_list[counter])
verse_text = verse_text.replace(u'/n', u'\n')
verse_text = verse_text.replace(u' | ', u'\n')
verse_lines = verse_text.split(u'\n', 1)
if check_first_verse_line:
if verse_lines[0].startswith(u'(PRE-CHORUS'):
@ -243,7 +248,7 @@ class CCLIFileImport(SongImport):
<Empty line>
Song CCLI number
# e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
Song copyright
Song copyright (if it begins ©, otherwise after authors)
# e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
Song authors # e.g. Lenny LeBlanc | Paul Baloche
Licencing info
@ -322,11 +327,17 @@ class CCLIFileImport(SongImport):
#line_number=2, copyright
if line_number == 2:
line_number += 1
self.copyright = clean_line
if clean_line.startswith(u'©'):
self.copyright = clean_line
else:
song_author = clean_line
#n=3, authors
elif line_number == 3:
line_number += 1
song_author = clean_line
if song_author:
self.copyright = clean_line
else:
song_author = clean_line
#line_number=4, comments lines before last line
elif line_number == 4 and not clean_line.startswith(u'CCL'):
self.comments += clean_line

View File

@ -36,7 +36,7 @@ from sqlalchemy.sql import or_
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
translate, check_item_selected, PluginStatus, create_separated_list
from openlp.core.lib.ui import UiStrings, create_action, create_widget_action
from openlp.core.lib.ui import UiStrings, create_widget_action
from openlp.core.utils import AppLocation
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm
@ -151,16 +151,22 @@ class SongMediaItem(MediaManagerItem):
def initialise(self):
self.searchTextEdit.setSearchTypes([
(SongSearch.Entire, u':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song')),
translate('SongsPlugin.MediaItem', 'Entire Song'),
translate('SongsPlugin.MediaItem', 'Search Entire Song...')),
(SongSearch.Titles, u':/songs/song_search_title.png',
translate('SongsPlugin.MediaItem', 'Titles')),
translate('SongsPlugin.MediaItem', 'Titles'),
translate('SongsPlugin.MediaItem', 'Search Titles...')),
(SongSearch.Lyrics, u':/songs/song_search_lyrics.png',
translate('SongsPlugin.MediaItem', 'Lyrics')),
translate('SongsPlugin.MediaItem', 'Lyrics'),
translate('SongsPlugin.MediaItem', 'Search Lyrics...')),
(SongSearch.Authors, u':/songs/song_search_author.png',
SongStrings.Authors),
SongStrings.Authors,
translate('SongsPlugin.MediaItem', 'Search Authors...')),
(SongSearch.Books, u':/songs/song_book_edit.png',
SongStrings.SongBooks),
(SongSearch.Themes, u':/slides/slide_theme.png', UiStrings().Themes)
SongStrings.SongBooks,
translate('SongsPlugin.MediaItem', 'Search Song Books...')),
(SongSearch.Themes, u':/slides/slide_theme.png',
UiStrings().Themes, UiStrings().SearchThemes)
])
self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value(
u'%s/last search type' % self.settingsSection,
@ -586,7 +592,7 @@ class SongMediaItem(MediaManagerItem):
Receiver.send_message(u'service_item_update',
u'%s:%s:%s' % (editId, item._uuid, temporary))
def search(self, string):
def search(self, string, showError):
"""
Search for some songs
"""

View File

@ -38,6 +38,7 @@ from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib import OpenLyrics
from openlp.plugins.songs.lib.xml import OpenLyricsError
log = logging.getLogger(__name__)
@ -73,3 +74,7 @@ class OpenLyricsImport(SongImport):
except etree.XMLSyntaxError:
log.exception(u'XML syntax error in file %s' % file_path)
self.logError(file_path, SongStrings.XMLSyntaxError)
except OpenLyricsError as exception:
log.exception(u'OpenLyricsException %d in file %s: %s'
% (exception.type, file_path, exception.log_message))
self.logError(file_path, exception.display_message)

View File

@ -31,6 +31,7 @@ import re
from lxml import objectify
from lxml.etree import Error, LxmlError
from openlp.core.lib import translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
@ -128,6 +129,12 @@ class OpenSongImport(SongImport):
log.exception(u'Error parsing XML')
return
root = tree.getroot()
if root.tag != u'song':
self.logError(file.name, unicode(
translate('SongsPlugin.OpenSongImport',
('Invalid OpenSong song file. Missing '
'song tag.'))))
return
fields = dir(root)
decode = {
u'copyright': self.addCopyright,

View File

@ -31,6 +31,7 @@ Worship songs into the OpenLP database.
import os
import logging
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
BLOCK_TYPES = (u'V', u'C', u'B')
@ -52,18 +53,19 @@ class WowImport(SongImport):
* A block can be a verse, chorus or bridge.
File Header:
Bytes are counted from one, i.e. the first byte is byte 1. These bytes,
up to the 56 byte, can change but no real meaning has been found. The
Bytes are counted from one, i.e. the first byte is byte 1. The first 19
bytes should be "WoW File \\nSong Words" The bytes after this and up to
the 56th byte, can change but no real meaning has been found. The
56th byte specifies how many blocks there are. The first block starts
with byte 83 after the "CSongDoc::CBlock" declaration.
Blocks:
Each block has a starting header, some lines of text, and an ending
footer. Each block starts with 4 bytes, the first byte specifies how
many lines are in that block, the next three bytes are null bytes.
footer. Each block starts with a 32 bit number, which specifies how
many lines are in that block.
Each block ends with 4 bytes, the first of which defines what type of
block it is, and the rest which are null bytes:
Each block ends with a 32 bit number, which defines what type of
block it is:
* ``NUL`` (0x00) - Verse
* ``SOH`` (0x01) - Chorus
@ -76,7 +78,6 @@ class WowImport(SongImport):
Each line starts with a byte which specifies how long that line is,
the line text, and ends with a null byte.
Footer:
The footer follows on after the last block, the first byte specifies
the length of the author text, followed by the author text, if
@ -107,22 +108,28 @@ class WowImport(SongImport):
for file in self.importSource:
if self.stopImportFlag:
return
file_name = os.path.split(file)[1]
# Get the song title
self.title = file_name.rpartition(u'.')[0]
self.setDefaults()
song_data = open(file, 'rb')
if song_data.read(19) != u'WoW File\nSong Words':
self.logError(file)
self.logError(file, unicode(
translate('SongsPlugin.WordsofWorshipSongImport',
('Invalid Words of Worship song file. Missing '
'"Wow File\\nSong Words" header.'))))
continue
# Seek to byte which stores number of blocks in the song
song_data.seek(56)
no_of_blocks = ord(song_data.read(1))
song_data.seek(66)
if song_data.read(16) != u'CSongDoc::CBlock':
self.logError(file, unicode(
translate('SongsPlugin.WordsofWorshipSongImport',
('Invalid Words of Worship song file. Missing '
'"CSongDoc::CBlock" string.'))))
continue
# Seek to the beging of the first block
song_data.seek(82)
for block in range(no_of_blocks):
self.linesToRead = ord(song_data.read(1))
# Skip 3 nulls to the beginnig of the 1st line
song_data.seek(3, os.SEEK_CUR)
self.linesToRead = ord(song_data.read(4)[:1])
block_text = u''
while self.linesToRead:
self.lineText = unicode(
@ -132,9 +139,7 @@ class WowImport(SongImport):
block_text += u'\n'
block_text += self.lineText
self.linesToRead -= 1
block_type = BLOCK_TYPES[ord(song_data.read(1))]
# Skip 3 nulls at the end of the block
song_data.seek(3, os.SEEK_CUR)
block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
# Blocks are seperated by 2 bytes, skip them, but not if
# this is the last block!
if block + 1 < no_of_blocks:
@ -150,6 +155,9 @@ class WowImport(SongImport):
if copyright_length:
self.addCopyright(unicode(
song_data.read(copyright_length), u'cp1252'))
file_name = os.path.split(file)[1]
# Get the song title
self.title = file_name.rpartition(u'.')[0]
song_data.close()
if not self.finish():
self.logError(file)

View File

@ -66,7 +66,7 @@ import re
from lxml import etree, objectify
from openlp.core.lib import FormattingTags
from openlp.core.lib import FormattingTags, translate
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
from openlp.core.utils import get_application_version
@ -673,9 +673,22 @@ class OpenLyrics(object):
sxml = SongXML()
verses = {}
verse_def_list = []
lyrics = song_xml.lyrics
try:
lyrics = song_xml.lyrics
except AttributeError:
raise OpenLyricsError(OpenLyricsError.LyricsError,
'<lyrics> tag is missing.',
unicode(translate('OpenLP.OpenLyricsImportError',
'<lyrics> tag is missing.')))
try:
verse_list = lyrics.verse
except AttributeError:
raise OpenLyricsError(OpenLyricsError.VerseError,
'<verse> tag is missing.',
unicode(translate('OpenLP.OpenLyricsImportError',
'<verse> tag is missing.')))
# Loop over the "verse" elements.
for verse in lyrics.verse:
for verse in verse_list:
text = u''
# Loop over the "lines" elements.
for lines in verse.lines:
@ -791,3 +804,15 @@ class OpenLyrics(object):
"""
return etree.tostring(xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=True)
class OpenLyricsError(Exception):
# XML tree is missing the lyrics tag
LyricsError = 1
# XML tree has no verse tags
VerseError = 2
def __init__(self, type, log_message, display_message):
self.type = type
self.log_message = log_message
self.display_message = display_message

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6934
resources/i18n/da.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6853
resources/i18n/fi.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6857
resources/i18n/it.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6855
resources/i18n/pl.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -185,11 +185,11 @@ def download_translations():
**Note:** URLs and headers need to remain strings, not unicode.
"""
global username, password
if not username:
username = raw_input(u'Transifex username: ')
if not password:
password = getpass(u'Transifex password: ')
print_quiet(u'Download translation files from Transifex')
if not username:
username = raw_input(u' Transifex username: ')
if not password:
password = getpass(u' Transifex password: ')
# First get the list of languages
url = SERVER_URL + 'resource/ents/'
base64string = base64.encodestring(

View File

@ -104,7 +104,7 @@ OpenLP (previously openlp.org) is free church presentation software, or lyrics p
url='http://openlp.org/',
license='GNU General Public License',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
scripts=['openlp.pyw', 'scripts/openlp-remoteclient.py'],
scripts=['openlp.pyw'],
include_package_data=True,
zip_safe=False,
install_requires=[