forked from openlp/openlp
Head 1924
This commit is contained in:
commit
dbfeb49572
@ -21,3 +21,5 @@ openlp/core/resources.py.old
|
||||
*.qm
|
||||
resources/windows/warnOpenLP.txt
|
||||
openlp.cfg
|
||||
.idea
|
||||
openlp.pro
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;'
|
||||
|
@ -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)
|
||||
|
@ -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``
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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']:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
@ -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() > \
|
||||
|
@ -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'
|
||||
|
@ -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']])
|
||||
|
@ -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':
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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]]
|
||||
|
@ -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'%'),
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()))
|
||||
|
@ -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 = []
|
||||
|
@ -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():
|
||||
|
@ -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 #
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 #
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
2235
resources/i18n/af.ts
2235
resources/i18n/af.ts
File diff suppressed because it is too large
Load Diff
2346
resources/i18n/cs.ts
2346
resources/i18n/cs.ts
File diff suppressed because it is too large
Load Diff
6934
resources/i18n/da.ts
Normal file
6934
resources/i18n/da.ts
Normal file
File diff suppressed because it is too large
Load Diff
2228
resources/i18n/de.ts
2228
resources/i18n/de.ts
File diff suppressed because it is too large
Load Diff
2603
resources/i18n/el.ts
2603
resources/i18n/el.ts
File diff suppressed because it is too large
Load Diff
5099
resources/i18n/en.ts
5099
resources/i18n/en.ts
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
3276
resources/i18n/es.ts
3276
resources/i18n/es.ts
File diff suppressed because it is too large
Load Diff
2225
resources/i18n/et.ts
2225
resources/i18n/et.ts
File diff suppressed because it is too large
Load Diff
6853
resources/i18n/fi.ts
Normal file
6853
resources/i18n/fi.ts
Normal file
File diff suppressed because it is too large
Load Diff
4449
resources/i18n/fr.ts
4449
resources/i18n/fr.ts
File diff suppressed because it is too large
Load Diff
2282
resources/i18n/hu.ts
2282
resources/i18n/hu.ts
File diff suppressed because it is too large
Load Diff
2690
resources/i18n/id.ts
2690
resources/i18n/id.ts
File diff suppressed because it is too large
Load Diff
6857
resources/i18n/it.ts
Normal file
6857
resources/i18n/it.ts
Normal file
File diff suppressed because it is too large
Load Diff
3183
resources/i18n/ja.ts
3183
resources/i18n/ja.ts
File diff suppressed because it is too large
Load Diff
2812
resources/i18n/ko.ts
2812
resources/i18n/ko.ts
File diff suppressed because it is too large
Load Diff
3103
resources/i18n/nb.ts
3103
resources/i18n/nb.ts
File diff suppressed because it is too large
Load Diff
3294
resources/i18n/nl.ts
3294
resources/i18n/nl.ts
File diff suppressed because it is too large
Load Diff
6855
resources/i18n/pl.ts
Normal file
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
3399
resources/i18n/ru.ts
3399
resources/i18n/ru.ts
File diff suppressed because it is too large
Load Diff
4730
resources/i18n/sq.ts
4730
resources/i18n/sq.ts
File diff suppressed because it is too large
Load Diff
2884
resources/i18n/sv.ts
2884
resources/i18n/sv.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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(
|
||||
|
2
setup.py
2
setup.py
@ -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=[
|
||||
|
Loading…
Reference in New Issue
Block a user