This commit is contained in:
Andreas Preikschat 2011-01-10 15:57:25 +01:00
commit 39eb0ad457
44 changed files with 1619 additions and 720 deletions

View File

@ -34,7 +34,7 @@ from subprocess import Popen, PIPE
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver from openlp.core.lib import Receiver, check_directory_exists
from openlp.core.resources import qInitResources from openlp.core.resources import qInitResources
from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.mainwindow import MainWindow
from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui.exceptionform import ExceptionForm
@ -150,16 +150,16 @@ class OpenLP(QtGui.QApplication):
log.info(u'Openlp version %s' % app_version[u'version']) log.info(u'Openlp version %s' % app_version[u'version'])
return app_version return app_version
def notify(self, obj, evt): # def notify(self, obj, evt):
#TODO needed for presentation exceptions # #TODO needed for presentation exceptions
return QtGui.QApplication.notify(self, obj, evt) # return QtGui.QApplication.notify(self, obj, evt)
def run(self): def run(self):
""" """
Run the OpenLP application. Run the OpenLP application.
""" """
app_version = self._get_version() app_version = self._get_version()
#provide a listener for widgets to reqest a screen update. # provide a listener for widgets to reqest a screen update.
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents) QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
@ -216,7 +216,7 @@ class OpenLP(QtGui.QApplication):
def setNormalCursor(self): def setNormalCursor(self):
""" """
Sets the Normal Cursor forthe Application Sets the Normal Cursor for the Application
""" """
self.restoreOverrideCursor() self.restoreOverrideCursor()
@ -243,8 +243,7 @@ def main():
help='Set the Qt4 style (passed directly to Qt4).') help='Set the Qt4 style (passed directly to Qt4).')
# Set up logging # Set up logging
log_path = AppLocation.get_directory(AppLocation.CacheDir) log_path = AppLocation.get_directory(AppLocation.CacheDir)
if not os.path.exists(log_path): check_directory_exists(log_path)
os.makedirs(log_path)
filename = os.path.join(log_path, u'openlp.log') filename = os.path.join(log_path, u'openlp.log')
logfile = logging.FileHandler(filename, u'w') logfile = logging.FileHandler(filename, u'w')
logfile.setFormatter(logging.Formatter( logfile.setFormatter(logging.Formatter(
@ -281,4 +280,4 @@ if __name__ == u'__main__':
""" """
Instantiate and run the application. Instantiate and run the application.
""" """
main() main()

View File

@ -35,52 +35,51 @@ from PyQt4 import QtCore, QtGui
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# TODO make external and configurable in alpha 4 via a settings dialog base_html_expands = []
html_expands = []
html_expands.append({u'desc': u'Red', u'start tag': u'{r}', base_html_expands.append({u'desc': u'Red', u'start tag': u'{r}',
u'start html': u'<span style="-webkit-text-fill-color:red">', u'start html': u'<span style="-webkit-text-fill-color:red">',
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Black', u'start tag': u'{b}', base_html_expands.append({u'desc': u'Black', u'start tag': u'{b}',
u'start html': u'<span style="-webkit-text-fill-color:black">', u'start html': u'<span style="-webkit-text-fill-color:black">',
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Blue', u'start tag': u'{bl}', base_html_expands.append({u'desc': u'Blue', u'start tag': u'{bl}',
u'start html': u'<span style="-webkit-text-fill-color:blue">', u'start html': u'<span style="-webkit-text-fill-color:blue">',
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Yellow', u'start tag': u'{y}', base_html_expands.append({u'desc': u'Yellow', u'start tag': u'{y}',
u'start html': u'<span style="-webkit-text-fill-color:yellow">', u'start html': u'<span style="-webkit-text-fill-color:yellow">',
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Green', u'start tag': u'{g}', base_html_expands.append({u'desc': u'Green', u'start tag': u'{g}',
u'start html': u'<span style="-webkit-text-fill-color:green">', u'start html': u'<span style="-webkit-text-fill-color:green">',
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Pink', u'start tag': u'{pk}', base_html_expands.append({u'desc': u'Pink', u'start tag': u'{pk}',
u'start html': u'<span style="-webkit-text-fill-color:#CC33CC">', u'start html': u'<span style="-webkit-text-fill-color:#CC33CC">',
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Orange', u'start tag': u'{o}', base_html_expands.append({u'desc': u'Orange', u'start tag': u'{o}',
u'start html': u'<span style="-webkit-text-fill-color:#CC0033">', u'start html': u'<span style="-webkit-text-fill-color:#CC0033">',
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Purple', u'start tag': u'{pp}', base_html_expands.append({u'desc': u'Purple', u'start tag': u'{pp}',
u'start html': u'<span style="-webkit-text-fill-color:#9900FF">', u'start html': u'<span style="-webkit-text-fill-color:#9900FF">',
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'White', u'start tag': u'{w}', base_html_expands.append({u'desc': u'White', u'start tag': u'{w}',
u'start html': u'<span style="-webkit-text-fill-color:white">', u'start html': u'<span style="-webkit-text-fill-color:white">',
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': False}) u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True})
html_expands.append({u'desc': u'Superscript', u'start tag': u'{su}', base_html_expands.append({u'desc': u'Superscript', u'start tag': u'{su}',
u'start html': u'<sup>', u'end tag': u'{/su}', u'end html': u'</sup>', u'start html': u'<sup>', u'end tag': u'{/su}', u'end html': u'</sup>',
u'protected': True}) u'protected': True})
html_expands.append({u'desc': u'Subscript', u'start tag': u'{sb}', base_html_expands.append({u'desc': u'Subscript', u'start tag': u'{sb}',
u'start html': u'<sub>', u'end tag': u'{/sb}', u'end html': u'</sub>', u'start html': u'<sub>', u'end tag': u'{/sb}', u'end html': u'</sub>',
u'protected': True}) u'protected': True})
html_expands.append({u'desc': u'Paragraph', u'start tag': u'{p}', base_html_expands.append({u'desc': u'Paragraph', u'start tag': u'{p}',
u'start html': u'<p>', u'end tag': u'{/p}', u'end html': u'</p>', u'start html': u'<p>', u'end tag': u'{/p}', u'end html': u'</p>',
u'protected': True}) u'protected': True})
html_expands.append({u'desc': u'Bold', u'start tag': u'{st}', base_html_expands.append({u'desc': u'Bold', u'start tag': u'{st}',
u'start html': u'<strong>', u'end tag': u'{/st}', u'end html': u'</strong>', u'start html': u'<strong>', u'end tag': u'{/st}', u'end html': u'</strong>',
u'protected': True}) u'protected': True})
html_expands.append({u'desc': u'Italics', u'start tag': u'{it}', base_html_expands.append({u'desc': u'Italics', u'start tag': u'{it}',
u'start html': u'<em>', u'end tag': u'{/it}', u'end html': u'</em>', u'start html': u'<em>', u'end tag': u'{/it}', u'end html': u'</em>',
u'protected': True}) u'protected': True})
html_expands.append({u'desc': u'Underline', u'start tag': u'{u}', base_html_expands.append({u'desc': u'Underline', u'start tag': u'{u}',
u'start html': u'<span style="text-decoration: underline;">', u'start html': u'<span style="text-decoration: underline;">',
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True}) u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True})
@ -102,7 +101,8 @@ def translate(context, text, comment=None,
An identifying string for when the same text is used in different roles An identifying string for when the same text is used in different roles
within the same context. within the same context.
""" """
return QtCore.QCoreApplication.translate(context, text, comment, encoding, n) return QtCore.QCoreApplication.translate(
context, text, comment, encoding, n)
def get_text_file_string(text_file): def get_text_file_string(text_file):
""" """
@ -293,7 +293,7 @@ def clean_tags(text):
Remove Tags from text for display Remove Tags from text for display
""" """
text = text.replace(u'<br>', u'\n') text = text.replace(u'<br>', u'\n')
for tag in html_expands: for tag in DisplayTags.get_html_tags():
text = text.replace(tag[u'start tag'], u'') text = text.replace(tag[u'start tag'], u'')
text = text.replace(tag[u'end tag'], u'') text = text.replace(tag[u'end tag'], u'')
return text return text
@ -302,13 +302,25 @@ def expand_tags(text):
""" """
Expand tags HTML for display Expand tags HTML for display
""" """
for tag in html_expands: for tag in DisplayTags.get_html_tags():
text = text.replace(tag[u'start tag'], tag[u'start html']) text = text.replace(tag[u'start tag'], tag[u'start html'])
text = text.replace(tag[u'end tag'], tag[u'end html']) text = text.replace(tag[u'end tag'], tag[u'end html'])
return text return text
def check_directory_exists(dir):
"""
Check a theme directory exists and if not create it
``dir``
Theme directory to make sure exists
"""
log.debug(u'check_directory_exists')
if not os.path.exists(dir):
os.makedirs(dir)
from theme import ThemeLevel, ThemeXML, BackgroundGradientType, \ from theme import ThemeLevel, ThemeXML, BackgroundGradientType, \
BackgroundType, HorizontalType, VerticalType BackgroundType, HorizontalType, VerticalType
from displaytags import DisplayTags
from spelltextedit import SpellTextEdit from spelltextedit import SpellTextEdit
from eventreceiver import Receiver from eventreceiver import Receiver
from imagemanager import ImageManager from imagemanager import ImageManager

View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, 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 #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Provide Html Tag management and Display Tag access class
"""
from openlp.core.lib import base_html_expands
class DisplayTags(object):
"""
Static Class to HTML Tags to be access around the code the list is managed
by the Options Tab.
"""
html_expands = []
@staticmethod
def get_html_tags():
"""
Provide access to the html_expands list.
"""
return DisplayTags.html_expands
@staticmethod
def reset_html_tags():
"""
Resets the html_expands list.
"""
DisplayTags.html_expands = []
for html in base_html_expands:
DisplayTags.html_expands.append(html)
@staticmethod
def add_html_tag(tag):
"""
Add a new tag to the list
"""
DisplayTags.html_expands.append(tag)
@staticmethod
def remove_html_tag(id):
"""
Removes amd individual html_expands list.
"""
DisplayTags.html_expands.pop(id)

View File

@ -176,7 +176,8 @@ class MediaManagerItem(QtGui.QWidget):
# break compatability), but it makes sense for the icon to # break compatability), but it makes sense for the icon to
# come before the tooltip (as you have to have an icon, but # come before the tooltip (as you have to have an icon, but
# not neccesarily a tooltip) # not neccesarily a tooltip)
return self.toolbar.addToolbarButton(title, icon, tooltip, slot, checkable) return self.toolbar.addToolbarButton(title, icon, tooltip, slot,
checkable)
def addToolbarSeparator(self): def addToolbarSeparator(self):
""" """

View File

@ -175,6 +175,10 @@ class Plugin(QtCore.QObject):
self.status = new_status self.status = new_status
QtCore.QSettings().setValue( QtCore.QSettings().setValue(
self.settingsSection + u'/status', QtCore.QVariant(self.status)) self.settingsSection + u'/status', QtCore.QVariant(self.status))
if new_status == PluginStatus.Active:
self.initialise()
elif new_status == PluginStatus.Inactive:
self.finalise()
def isActive(self): def isActive(self):
""" """
@ -314,4 +318,4 @@ class Plugin(QtCore.QObject):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
pass pass

View File

@ -114,6 +114,12 @@ class SettingsTab(QtGui.QWidget):
""" """
pass pass
def cancel(self):
"""
Reset any settings
"""
pass
def postSetUp(self, postUpdate=False): def postSetUp(self, postUpdate=False):
""" """
Changes which need to be made after setup of application Changes which need to be made after setup of application

View File

@ -36,7 +36,7 @@ except ImportError:
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check # http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import html_expands, translate from openlp.core.lib import translate, DisplayTags
class SpellTextEdit(QtGui.QPlainTextEdit): class SpellTextEdit(QtGui.QPlainTextEdit):
""" """
@ -88,7 +88,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
popupMenu.insertMenu(popupMenu.actions()[0], spell_menu) popupMenu.insertMenu(popupMenu.actions()[0], spell_menu)
tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit', tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Formatting Tags')) 'Formatting Tags'))
for html in html_expands: for html in DisplayTags.get_html_tags():
action = SpellAction( html[u'desc'], tagMenu) action = SpellAction( html[u'desc'], tagMenu)
action.correct.connect(self.htmlTag) action.correct.connect(self.htmlTag)
tagMenu.addAction(action) tagMenu.addAction(action)
@ -110,7 +110,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
""" """
Replaces the selected text with word. Replaces the selected text with word.
""" """
for html in html_expands: for html in DisplayTags.get_html_tags():
if tag == html[u'desc']: if tag == html[u'desc']:
cursor = self.textCursor() cursor = self.textCursor()
if self.textCursor().hasSelection(): if self.textCursor().hasSelection():
@ -158,4 +158,4 @@ class SpellAction(QtGui.QAction):
def __init__(self, *args): def __init__(self, *args):
QtGui.QAction.__init__(self, *args) QtGui.QAction.__init__(self, *args)
self.triggered.connect(lambda x: self.correct.emit( self.triggered.connect(lambda x: self.correct.emit(
unicode(self.text()))) unicode(self.text())))

View File

@ -598,4 +598,4 @@ class ThemeXML(object):
self.font_footer_shadow_size) self.font_footer_shadow_size)
self.add_display(self.display_horizontal_align, self.add_display(self.display_horizontal_align,
self.display_vertical_align, self.display_vertical_align,
self.display_slide_transition) self.display_slide_transition)

View File

@ -35,11 +35,11 @@ class HideMode(object):
``Blank`` ``Blank``
This mode is used to hide all output, specifically by covering the This mode is used to hide all output, specifically by covering the
display with a black screen. display with a black screen.
``Theme`` ``Theme``
This mode is used to hide all output, but covers the display with the This mode is used to hide all output, but covers the display with the
current theme background, as opposed to black. current theme background, as opposed to black.
``Desktop`` ``Desktop``
This mode hides all output by minimising the display, leaving the user's This mode hides all output by minimising the display, leaving the user's
desktop showing. desktop showing.
@ -59,6 +59,7 @@ from splashscreen import SplashScreen
from generaltab import GeneralTab from generaltab import GeneralTab
from themestab import ThemesTab from themestab import ThemesTab
from advancedtab import AdvancedTab from advancedtab import AdvancedTab
from displaytagtab import DisplayTagTab
from aboutform import AboutForm from aboutform import AboutForm
from pluginform import PluginForm from pluginform import PluginForm
from settingsform import SettingsForm from settingsform import SettingsForm

View File

@ -0,0 +1,350 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, 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 #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
'''
The :mod:`DisplayTagTab` provides an Tag Edit facility. The Base set are
protected and included each time loaded. Custom tags can be defined and saved.
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
Tags cannot be changed.
'''
import cPickle
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, DisplayTags
class DisplayTagTab(SettingsTab):
'''
The :class:`DisplayTagTab` manages the settings tab .
'''
def __init__(self):
'''
Initialise the settings tab
'''
SettingsTab.__init__(self, u'Display Tags')
def resizeEvent(self, event=None):
pass
def preLoad(self):
"""
Initialise values before the Load takes place
"""
# Create initial copy from master
DisplayTags.reset_html_tags()
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
QtCore.QVariant(u'')).toString()
# cPickle only accepts str not unicode strings
user_expands_string = str(unicode(user_expands).encode(u'utf8'))
if user_expands_string:
user_tags = cPickle.loads(user_expand_string)
# If we have some user ones added them as well
for t in user_tags:
DisplayTags.add_html_tag(t)
self.selected = -1
def setupUi(self):
'''
Configure the UI elements for the tab.
'''
self.setObjectName(u'DisplayTagTab')
self.tabTitleVisible = \
translate(u'OpenLP.DisplayTagTab', 'Display Tags')
self.displayTagEdit = QtGui.QWidget(self)
self.editGroupBox = QtGui.QGroupBox(self.displayTagEdit)
self.editGroupBox.setGeometry(QtCore.QRect(10, 220, 650, 181))
self.editGroupBox.setObjectName(u'editGroupBox')
self.updatePushButton = QtGui.QPushButton(self.editGroupBox)
self.updatePushButton.setGeometry(QtCore.QRect(550, 140, 71, 26))
self.updatePushButton.setObjectName(u'updatePushButton')
self.layoutWidget = QtGui.QWidget(self.editGroupBox)
self.layoutWidget.setGeometry(QtCore.QRect(5, 20, 571, 114))
self.layoutWidget.setObjectName(u'layoutWidget')
self.formLayout = QtGui.QFormLayout(self.layoutWidget)
self.formLayout.setObjectName(u'formLayout')
self.descriptionLabel = QtGui.QLabel(self.layoutWidget)
self.descriptionLabel.setAlignment(QtCore.Qt.AlignCenter)
self.descriptionLabel.setObjectName(u'descriptionLabel')
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
self.descriptionLabel)
self.descriptionLineEdit = QtGui.QLineEdit(self.layoutWidget)
self.descriptionLineEdit.setObjectName(u'descriptionLineEdit')
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole,
self.descriptionLineEdit)
self.tagLabel = QtGui.QLabel(self.layoutWidget)
self.tagLabel.setAlignment(QtCore.Qt.AlignCenter)
self.tagLabel.setObjectName(u'tagLabel')
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.tagLabel)
self.tagLineEdit = QtGui.QLineEdit(self.layoutWidget)
self.tagLineEdit.setMaximumSize(QtCore.QSize(50, 16777215))
self.tagLineEdit.setMaxLength(5)
self.tagLineEdit.setObjectName(u'tagLineEdit')
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole,
self.tagLineEdit)
self.startTagLabel = QtGui.QLabel(self.layoutWidget)
self.startTagLabel.setAlignment(QtCore.Qt.AlignCenter)
self.startTagLabel.setObjectName(u'startTagLabel')
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole,
self.startTagLabel)
self.startTagLineEdit = QtGui.QLineEdit(self.layoutWidget)
self.startTagLineEdit.setObjectName(u'startTagLineEdit')
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole,
self.startTagLineEdit)
self.endTagLabel = QtGui.QLabel(self.layoutWidget)
self.endTagLabel.setAlignment(QtCore.Qt.AlignCenter)
self.endTagLabel.setObjectName(u'endTagLabel')
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole,
self.endTagLabel)
self.endTagLineEdit = QtGui.QLineEdit(self.layoutWidget)
self.endTagLineEdit.setObjectName(u'endTagLineEdit')
self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole,
self.endTagLineEdit)
self.defaultPushButton = QtGui.QPushButton(self.displayTagEdit)
self.defaultPushButton.setGeometry(QtCore.QRect(430, 188, 71, 26))
self.defaultPushButton.setObjectName(u'updatePushButton')
self.deletePushButton = QtGui.QPushButton(self.displayTagEdit)
self.deletePushButton.setGeometry(QtCore.QRect(510, 188, 71, 26))
self.deletePushButton.setObjectName(u'deletePushButton')
self.newPushButton = QtGui.QPushButton(self.displayTagEdit)
self.newPushButton.setGeometry(QtCore.QRect(600, 188, 71, 26))
self.newPushButton.setObjectName(u'newPushButton')
self.tagTableWidget = QtGui.QTableWidget(self.displayTagEdit)
self.tagTableWidget.setGeometry(QtCore.QRect(10, 10, 650, 171))
self.tagTableWidget.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarAlwaysOff)
self.tagTableWidget.setEditTriggers(
QtGui.QAbstractItemView.NoEditTriggers)
self.tagTableWidget.setAlternatingRowColors(True)
self.tagTableWidget.setSelectionMode(
QtGui.QAbstractItemView.SingleSelection)
self.tagTableWidget.setSelectionBehavior(
QtGui.QAbstractItemView.SelectRows)
self.tagTableWidget.setCornerButtonEnabled(False)
self.tagTableWidget.setObjectName(u'tagTableWidget')
self.tagTableWidget.setColumnCount(4)
self.tagTableWidget.setRowCount(0)
item = QtGui.QTableWidgetItem()
self.tagTableWidget.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
self.tagTableWidget.setHorizontalHeaderItem(1, item)
item = QtGui.QTableWidgetItem()
self.tagTableWidget.setHorizontalHeaderItem(2, item)
item = QtGui.QTableWidgetItem()
self.tagTableWidget.setHorizontalHeaderItem(3, item)
self.editGroupBox.setTitle(
translate('OpenLP.DisplayTagTab', 'Edit Selection'))
self.updatePushButton.setText(
translate('OpenLP.DisplayTagTab', 'Update'))
self.descriptionLabel.setText(
translate('OpenLP.DisplayTagTab', 'Description'))
self.tagLabel.setText(translate('OpenLP.DisplayTagTab', 'Tag'))
self.startTagLabel.setText(
translate('OpenLP.DisplayTagTab', 'Start tag'))
self.endTagLabel.setText(translate('OpenLP.DisplayTagTab', 'End tag'))
self.deletePushButton.setText(
translate('OpenLP.DisplayTagTab', 'Delete'))
self.defaultPushButton.setText(
translate('OpenLP.DisplayTagTab', 'Default'))
self.newPushButton.setText(translate('OpenLP.DisplayTagTab', 'New'))
self.tagTableWidget.horizontalHeaderItem(0)\
.setText(translate('OpenLP.DisplayTagTab', 'Description'))
self.tagTableWidget.horizontalHeaderItem(1)\
.setText(translate('OpenLP.DisplayTagTab', 'Tag id'))
self.tagTableWidget.horizontalHeaderItem(2)\
.setText(translate('OpenLP.DisplayTagTab', 'Start Html'))
self.tagTableWidget.horizontalHeaderItem(3)\
.setText(translate('OpenLP.DisplayTagTab', 'End Html'))
QtCore.QMetaObject.connectSlotsByName(self.displayTagEdit)
self.tagTableWidget.setColumnWidth(0, 120)
self.tagTableWidget.setColumnWidth(1, 40)
self.tagTableWidget.setColumnWidth(2, 240)
self.tagTableWidget.setColumnWidth(3, 200)
QtCore.QObject.connect(self.tagTableWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
QtCore.QObject.connect(self.defaultPushButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultPushed)
QtCore.QObject.connect(self.newPushButton,
QtCore.SIGNAL(u'pressed()'), self.onNewPushed)
QtCore.QObject.connect(self.updatePushButton,
QtCore.SIGNAL(u'pressed()'), self.onUpdatePushed)
QtCore.QObject.connect(self.deletePushButton,
QtCore.SIGNAL(u'pressed()'), self.onDeletePushed)
def load(self):
"""
Load Display and set field state.
"""
self.newPushButton.setEnabled(True)
self.updatePushButton.setEnabled(False)
self.deletePushButton.setEnabled(False)
for linenumber, html in enumerate(DisplayTags.get_html_tags()):
self.tagTableWidget.setRowCount(
self.tagTableWidget.rowCount() + 1)
self.tagTableWidget.setItem(linenumber, 0,
QtGui.QTableWidgetItem(html[u'desc']))
self.tagTableWidget.setItem(linenumber, 1,
QtGui.QTableWidgetItem(self._strip(html[u'start tag'])))
self.tagTableWidget.setItem(linenumber, 2,
QtGui.QTableWidgetItem(html[u'start html']))
self.tagTableWidget.setItem(linenumber, 3,
QtGui.QTableWidgetItem(html[u'end html']))
self.tagTableWidget.resizeRowsToContents()
self.descriptionLineEdit.setText(u'')
self.tagLineEdit.setText(u'')
self.startTagLineEdit.setText(u'')
self.endTagLineEdit.setText(u'')
self.descriptionLineEdit.setEnabled(False)
self.tagLineEdit.setEnabled(False)
self.startTagLineEdit.setEnabled(False)
self.endTagLineEdit.setEnabled(False)
def save(self):
"""
Save Custom tags in a pickle .
"""
temp = []
for tag in DisplayTags.get_html_tags():
if not tag[u'protected']:
temp.append(tag)
if temp:
ctemp = cPickle.dumps(temp)
QtCore.QSettings().setValue(u'displayTags/html_tags',
QtCore.QVariant(ctemp))
else:
QtCore.QSettings().setValue(u'displayTags/html_tags',
QtCore.QVariant(u''))
def cancel(self):
"""
Reset Custom tags from Settings.
"""
self.preLoad()
self._resetTable()
def onRowSelected(self):
"""
Table Row selected so display items and set field state.
"""
row = self.tagTableWidget.currentRow()
html = DisplayTags.get_html_tags()[row]
self.selected = row
self.descriptionLineEdit.setText(html[u'desc'])
self.tagLineEdit.setText(self._strip(html[u'start tag']))
self.startTagLineEdit.setText(html[u'start html'])
self.endTagLineEdit.setText(html[u'end html'])
if html[u'protected']:
self.descriptionLineEdit.setEnabled(False)
self.tagLineEdit.setEnabled(False)
self.startTagLineEdit.setEnabled(False)
self.endTagLineEdit.setEnabled(False)
self.updatePushButton.setEnabled(False)
self.deletePushButton.setEnabled(False)
else:
self.descriptionLineEdit.setEnabled(True)
self.tagLineEdit.setEnabled(True)
self.startTagLineEdit.setEnabled(True)
self.endTagLineEdit.setEnabled(True)
self.updatePushButton.setEnabled(True)
self.deletePushButton.setEnabled(True)
def onNewPushed(self):
"""
Add a new tag to list only if it is not a duplicate.
"""
for html in DisplayTags.get_html_tags():
if self._strip(html[u'start tag']) == u'n':
QtGui.QMessageBox.critical(self,
translate('OpenLP.DisplayTagTab', 'Update Error'),
translate('OpenLP.DisplayTagTab',
'Tag "n" already defined.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok)
return
# Add new tag to list
tag = {u'desc': u'New Item', u'start tag': u'{n}',
u'start html': u'<Html_here>', u'end tag': u'{/n}',
u'end html': u'</and here>', u'protected': False}
DisplayTags.add_html_tag(tag)
self._resetTable()
# Highlight new row
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
def onDefaultPushed(self):
"""
Remove all Custom Tags and reset to base set only.
"""
DisplayTags.reset_html_tags()
self._resetTable()
def onDeletePushed(self):
"""
Delete selected custom tag.
"""
if self.selected != -1:
DisplayTags.remove_html_tag(self.selected)
self.selected = -1
self._resetTable()
def onUpdatePushed(self):
"""
Update Custom Tag details if not duplicate.
"""
html_expands = DisplayTags.get_html_tags()
if self.selected != -1:
html = html_expands[self.selected]
tag = unicode(self.tagLineEdit.text())
for linenumber, html1 in enumerate(html_expands):
if self._strip(html1[u'start tag']) == tag and \
linenumber != self.selected:
QtGui.QMessageBox.critical(self,
translate('OpenLP.DisplayTagTab', 'Update Error'),
unicode(translate('OpenLP.DisplayTagTab',
'Tag %s already defined.')) % tag,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok)
return
html[u'desc'] = unicode(self.descriptionLineEdit.text())
html[u'start html'] = unicode(self.startTagLineEdit.text())
html[u'end html'] = unicode(self.endTagLineEdit.text())
html[u'start tag'] = u'{%s}' % tag
html[u'end tag'] = u'{/%s}' % tag
self.selected = -1
self._resetTable()
def _resetTable(self):
"""
Reset List for loading.
"""
self.tagTableWidget.clearContents()
self.tagTableWidget.setRowCount(0)
self.load()
def _strip(self, tag):
"""
Remove tag wrappers for editing.
"""
tag = tag.replace(u'{', u'')
tag = tag.replace(u'}', u'')
return tag

View File

@ -32,7 +32,8 @@ from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.phonon import Phonon from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \ from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
translate build_icon, translate
from openlp.core.ui import HideMode from openlp.core.ui import HideMode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -102,6 +103,8 @@ class MainDisplay(DisplayWidget):
self.isLive = live self.isLive = live
self.alertTab = None self.alertTab = None
self.hideMode = None self.hideMode = None
mainIcon = build_icon(u':/icon/openlp-logo-16x16.png')
self.setWindowIcon(mainIcon)
self.retranslateUi() self.retranslateUi()
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | self.setWindowFlags(QtCore.Qt.FramelessWindowHint |

View File

@ -142,7 +142,8 @@ class Ui_MainWindow(object):
build_icon(u':/system/system_servicemanager.png')) build_icon(u':/system/system_servicemanager.png'))
self.ServiceManagerDock.setMinimumWidth( self.ServiceManagerDock.setMinimumWidth(
self.settingsmanager.mainwindow_right) self.settingsmanager.mainwindow_right)
self.ServiceManagerContents = ServiceManager(MainWindow) self.ServiceManagerContents = ServiceManager(MainWindow,
self.ServiceManagerDock)
self.ServiceManagerDock.setWidget(self.ServiceManagerContents) self.ServiceManagerDock.setWidget(self.ServiceManagerContents)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea,
self.ServiceManagerDock) self.ServiceManagerDock)
@ -152,7 +153,8 @@ class Ui_MainWindow(object):
build_icon(u':/system/system_thememanager.png')) build_icon(u':/system/system_thememanager.png'))
self.ThemeManagerDock.setMinimumWidth( self.ThemeManagerDock.setMinimumWidth(
self.settingsmanager.mainwindow_right) self.settingsmanager.mainwindow_right)
self.ThemeManagerContents = ThemeManager(MainWindow) self.ThemeManagerContents = ThemeManager(MainWindow,
self.ThemeManagerDock)
self.ThemeManagerContents.setObjectName(u'ThemeManagerContents') self.ThemeManagerContents.setObjectName(u'ThemeManagerContents')
self.ThemeManagerDock.setWidget(self.ThemeManagerContents) self.ThemeManagerDock.setWidget(self.ThemeManagerContents)
MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea,

View File

@ -61,6 +61,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
self.programaticChange = True self.programaticChange = True
self._clearDetails() self._clearDetails()
self.programaticChange = True self.programaticChange = True
pluginListWidth = 0
for plugin in self.parent.pluginManager.plugins: for plugin in self.parent.pluginManager.plugins:
item = QtGui.QListWidgetItem(self.pluginListWidget) item = QtGui.QListWidgetItem(self.pluginListWidget)
# We do this just to make 100% sure the status is an integer as # We do this just to make 100% sure the status is an integer as
@ -83,8 +84,11 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
if plugin.icon: if plugin.icon:
item.setIcon(plugin.icon) item.setIcon(plugin.icon)
self.pluginListWidget.addItem(item) self.pluginListWidget.addItem(item)
self.pluginListWidget.setFixedWidth( pluginListWidth = max(pluginListWidth, self.fontMetrics().width(
self.pluginListWidget.sizeHint().width()) unicode(translate('OpenLP.PluginForm', '%s (Inactive)')) %
name_string[u'singular']))
self.pluginListWidget.setFixedWidth(pluginListWidth +
self.pluginListWidget.iconSize().width() + 48)
def _clearDetails(self): def _clearDetails(self):
self.statusComboBox.setCurrentIndex(-1) self.statusComboBox.setCurrentIndex(-1)
@ -126,10 +130,8 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
return return
if status == 0: if status == 0:
self.activePlugin.toggleStatus(PluginStatus.Active) self.activePlugin.toggleStatus(PluginStatus.Active)
self.activePlugin.initialise()
else: else:
self.activePlugin.toggleStatus(PluginStatus.Inactive) self.activePlugin.toggleStatus(PluginStatus.Inactive)
self.activePlugin.finalise()
status_text = unicode( status_text = unicode(
translate('OpenLP.PluginForm', '%s (Inactive)')) translate('OpenLP.PluginForm', '%s (Inactive)'))
if self.activePlugin.status == PluginStatus.Active: if self.activePlugin.status == PluginStatus.Active:

View File

@ -28,7 +28,6 @@ import os
import logging import logging
import cPickle import cPickle
import zipfile import zipfile
from pprint import pformat
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -44,33 +43,33 @@ class ServiceManagerList(QtGui.QTreeWidget):
""" """
Set up key bindings and mouse behaviour for the service list Set up key bindings and mouse behaviour for the service list
""" """
def __init__(self, parent=None, name=None): def __init__(self, mainwindow, parent=None, name=None):
QtGui.QTreeWidget.__init__(self, parent) QtGui.QTreeWidget.__init__(self, parent)
self.parent = parent self.mainwindow = mainwindow
def keyPressEvent(self, event): def keyPressEvent(self, event):
if isinstance(event, QtGui.QKeyEvent): if isinstance(event, QtGui.QKeyEvent):
#here accept the event and do something #here accept the event and do something
if event.key() == QtCore.Qt.Key_Enter: if event.key() == QtCore.Qt.Key_Enter:
self.parent.makeLive() self.mainwindow.makeLive()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_Home: elif event.key() == QtCore.Qt.Key_Home:
self.parent.onServiceTop() self.mainwindow.onServiceTop()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_End: elif event.key() == QtCore.Qt.Key_End:
self.parent.onServiceEnd() self.mainwindow.onServiceEnd()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_PageUp: elif event.key() == QtCore.Qt.Key_PageUp:
self.parent.onServiceUp() self.mainwindow.onServiceUp()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_PageDown: elif event.key() == QtCore.Qt.Key_PageDown:
self.parent.onServiceDown() self.mainwindow.onServiceDown()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_Up: elif event.key() == QtCore.Qt.Key_Up:
self.parent.onMoveSelectionUp() self.mainwindow.onMoveSelectionUp()
event.accept() event.accept()
elif event.key() == QtCore.Qt.Key_Down: elif event.key() == QtCore.Qt.Key_Down:
self.parent.onMoveSelectionDown() self.mainwindow.onMoveSelectionDown()
event.accept() event.accept()
event.ignore() event.ignore()
else: else:
@ -99,12 +98,12 @@ class ServiceManager(QtGui.QWidget):
the resources used into one OSZ file for use on any OpenLP v2 installation. the resources used into one OSZ file for use on any OpenLP v2 installation.
Also handles the UI tasks of moving things up and down etc. Also handles the UI tasks of moving things up and down etc.
""" """
def __init__(self, parent): def __init__(self, mainwindow, parent=None):
""" """
Sets up the service manager, toolbars, list view, et al. Sets up the service manager, toolbars, list view, et al.
""" """
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.parent = parent self.mainwindow = mainwindow
self.serviceItems = [] self.serviceItems = []
self.serviceName = u'' self.serviceName = u''
self.suffixes = [] self.suffixes = []
@ -113,8 +112,8 @@ class ServiceManager(QtGui.QWidget):
# is a new service and has not been saved # is a new service and has not been saved
self._modified = False self._modified = False
self._fileName = u'' self._fileName = u''
self.serviceNoteForm = ServiceNoteForm(self.parent) self.serviceNoteForm = ServiceNoteForm(self.mainwindow)
self.serviceItemEditForm = ServiceItemEditForm(self.parent) self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow)
# start with the layout # start with the layout
self.layout = QtGui.QVBoxLayout(self) self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0) self.layout.setSpacing(0)
@ -145,8 +144,10 @@ class ServiceManager(QtGui.QWidget):
self.themeComboBox = QtGui.QComboBox(self.toolbar) self.themeComboBox = QtGui.QComboBox(self.toolbar)
self.themeComboBox.setToolTip(translate('OpenLP.ServiceManager', self.themeComboBox.setToolTip(translate('OpenLP.ServiceManager',
'Select a theme for the service')) 'Select a theme for the service'))
self.themeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.themeComboBox.setSizeAdjustPolicy(
QtGui.QSizePolicy.Fixed) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.themeComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.themeComboBox.setObjectName(u'themeComboBox') self.themeComboBox.setObjectName(u'themeComboBox')
self.toolbar.addToolbarWidget(u'ThemeWidget', self.themeComboBox) self.toolbar.addToolbarWidget(u'ThemeWidget', self.themeComboBox)
self.toolbar.setObjectName(u'toolbar') self.toolbar.setObjectName(u'toolbar')
@ -248,10 +249,10 @@ class ServiceManager(QtGui.QWidget):
QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate) QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate)
# Last little bits of setting up # Last little bits of setting up
self.service_theme = unicode(QtCore.QSettings().value( self.service_theme = unicode(QtCore.QSettings().value(
self.parent.serviceSettingsSection + u'/service theme', self.mainwindow.serviceSettingsSection + u'/service theme',
QtCore.QVariant(u'')).toString()) QtCore.QVariant(u'')).toString())
self.servicePath = AppLocation.get_section_data_path(u'servicemanager') self.servicePath = AppLocation.get_section_data_path(u'servicemanager')
#build the drag and drop context menu # build the drag and drop context menu
self.dndMenu = QtGui.QMenu() self.dndMenu = QtGui.QMenu()
self.newAction = self.dndMenu.addAction( self.newAction = self.dndMenu.addAction(
translate('OpenLP.ServiceManager', '&Add New Item')) translate('OpenLP.ServiceManager', '&Add New Item'))
@ -295,7 +296,7 @@ class ServiceManager(QtGui.QWidget):
""" """
self._modified = modified self._modified = modified
serviceFile = self.shortFileName() or u'Untitled Service' serviceFile = self.shortFileName() or u'Untitled Service'
self.parent.setServiceModified(modified, serviceFile) self.mainwindow.setServiceModified(modified, serviceFile)
def isModified(self): def isModified(self):
""" """
@ -308,7 +309,8 @@ class ServiceManager(QtGui.QWidget):
Setter for service file. Setter for service file.
""" """
self._fileName = unicode(fileName) self._fileName = unicode(fileName)
self.parent.setServiceModified(self.isModified(), self.shortFileName()) self.mainwindow.setServiceModified(self.isModified(),
self.shortFileName())
QtCore.QSettings(). \ QtCore.QSettings(). \
setValue(u'service/last file',QtCore.QVariant(fileName)) setValue(u'service/last file',QtCore.QVariant(fileName))
@ -342,7 +344,7 @@ class ServiceManager(QtGui.QWidget):
Create a new service. Create a new service.
""" """
if self.isModified(): if self.isModified():
result = QtGui.QMessageBox.question(self.parent, result = QtGui.QMessageBox.question(self.mainwindow,
translate('OpenLP.ServiceManager', 'Save Changes'), translate('OpenLP.ServiceManager', 'Save Changes'),
translate('OpenLP.ServiceManager', 'The current service has ' translate('OpenLP.ServiceManager', 'The current service has '
'been modified, would you like to save it?'), 'been modified, would you like to save it?'),
@ -357,7 +359,7 @@ class ServiceManager(QtGui.QWidget):
def onLoadServiceClicked(self): def onLoadServiceClicked(self):
if self.isModified(): if self.isModified():
result = QtGui.QMessageBox.question(self.parent, result = QtGui.QMessageBox.question(self.mainwindow,
translate('OpenLP.ServiceManager', 'Save Changes'), translate('OpenLP.ServiceManager', 'Save Changes'),
translate('OpenLP.ServiceManager', 'The current service has ' translate('OpenLP.ServiceManager', 'The current service has '
'been modified, would you like to save it?'), 'been modified, would you like to save it?'),
@ -367,14 +369,14 @@ class ServiceManager(QtGui.QWidget):
return False return False
elif result == QtGui.QMessageBox.Save: elif result == QtGui.QMessageBox.Save:
self.saveFile() self.saveFile()
fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.parent, fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.mainwindow,
translate('OpenLP.ServiceManager', 'Open File'), translate('OpenLP.ServiceManager', 'Open File'),
SettingsManager.get_last_dir(self.parent.serviceSettingsSection), SettingsManager.get_last_dir(self.mainwindow.serviceSettingsSection),
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'OpenLP Service Files (*.osz)'))) 'OpenLP Service Files (*.osz)')))
if not fileName: if not fileName:
return False return False
SettingsManager.set_last_dir(self.parent.serviceSettingsSection, SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
split_filename(fileName)[0]) split_filename(fileName)[0])
self.loadFile(fileName) self.loadFile(fileName)
@ -408,7 +410,7 @@ class ServiceManager(QtGui.QWidget):
else: else:
fileName = self.fileName() fileName = self.fileName()
log.debug(u'ServiceManager.saveFile - %s' % fileName) log.debug(u'ServiceManager.saveFile - %s' % fileName)
SettingsManager.set_last_dir(self.parent.serviceSettingsSection, SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
split_filename(fileName)[0]) split_filename(fileName)[0])
service = [] service = []
serviceFileName = fileName.replace(u'.osz', u'.osd') serviceFileName = fileName.replace(u'.osz', u'.osd')
@ -448,7 +450,7 @@ class ServiceManager(QtGui.QWidget):
except (IOError, OSError): except (IOError, OSError):
# if not present do not worry # if not present do not worry
pass pass
self.parent.addRecentFile(fileName) self.mainwindow.addRecentFile(fileName)
self.setModified(False) self.setModified(False)
return True return True
@ -457,9 +459,9 @@ class ServiceManager(QtGui.QWidget):
Get a file name and then call :function:`ServiceManager.saveFile` to Get a file name and then call :function:`ServiceManager.saveFile` to
save the file. save the file.
""" """
fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.parent, fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow,
translate('OpenLP.ServiceManager', 'Save Service'), translate('OpenLP.ServiceManager', 'Save Service'),
SettingsManager.get_last_dir(self.parent.serviceSettingsSection), SettingsManager.get_last_dir(self.mainwindow.serviceSettingsSection),
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'OpenLP Service Files (*.osz)'))) 'OpenLP Service Files (*.osz)')))
if not fileName: if not fileName:
@ -509,7 +511,7 @@ class ServiceManager(QtGui.QWidget):
self.newFile() self.newFile()
for item in items: for item in items:
serviceItem = ServiceItem() serviceItem = ServiceItem()
serviceItem.render_manager = self.parent.renderManager serviceItem.render_manager = self.mainwindow.renderManager
serviceItem.set_from_service(item, self.servicePath) serviceItem.set_from_service(item, self.servicePath)
self.validateItem(serviceItem) self.validateItem(serviceItem)
self.addServiceItem(serviceItem) self.addServiceItem(serviceItem)
@ -536,7 +538,7 @@ class ServiceManager(QtGui.QWidget):
if zip: if zip:
zip.close() zip.close()
self.setFileName(fileName) self.setFileName(fileName)
self.parent.addRecentFile(fileName) self.mainwindow.addRecentFile(fileName)
self.setModified(False) self.setModified(False)
QtCore.QSettings(). \ QtCore.QSettings(). \
setValue(u'service/last file',QtCore.QVariant(fileName)) setValue(u'service/last file',QtCore.QVariant(fileName))
@ -887,9 +889,9 @@ class ServiceManager(QtGui.QWidget):
""" """
log.debug(u'onThemeComboBoxSelected') log.debug(u'onThemeComboBoxSelected')
self.service_theme = unicode(self.themeComboBox.currentText()) self.service_theme = unicode(self.themeComboBox.currentText())
self.parent.renderManager.set_service_theme(self.service_theme) self.mainwindow.renderManager.set_service_theme(self.service_theme)
QtCore.QSettings().setValue( QtCore.QSettings().setValue(
self.parent.serviceSettingsSection + u'/service theme', self.mainwindow.serviceSettingsSection + u'/service theme',
QtCore.QVariant(self.service_theme)) QtCore.QVariant(self.service_theme))
self.regenerateServiceItems() self.regenerateServiceItems()
@ -899,7 +901,7 @@ class ServiceManager(QtGui.QWidget):
sure the theme combo box is in the correct state. sure the theme combo box is in the correct state.
""" """
log.debug(u'themeChange') log.debug(u'themeChange')
if self.parent.renderManager.theme_level == ThemeLevel.Global: if self.mainwindow.renderManager.theme_level == ThemeLevel.Global:
self.toolbar.actions[u'ThemeLabel'].setVisible(False) self.toolbar.actions[u'ThemeLabel'].setVisible(False)
self.toolbar.actions[u'ThemeWidget'].setVisible(False) self.toolbar.actions[u'ThemeWidget'].setVisible(False)
else: else:
@ -914,7 +916,7 @@ class ServiceManager(QtGui.QWidget):
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
log.debug(u'regenerateServiceItems') log.debug(u'regenerateServiceItems')
# force reset of renderer as theme data has changed # force reset of renderer as theme data has changed
self.parent.renderManager.themedata = None self.mainwindow.renderManager.themedata = None
if self.serviceItems: if self.serviceItems:
tempServiceItems = self.serviceItems tempServiceItems = self.serviceItems
self.serviceManagerList.clear() self.serviceManagerList.clear()
@ -949,7 +951,7 @@ class ServiceManager(QtGui.QWidget):
newItem.merge(item[u'service_item']) newItem.merge(item[u'service_item'])
item[u'service_item'] = newItem item[u'service_item'] = newItem
self.repaintServiceList(itemcount + 1, 0) self.repaintServiceList(itemcount + 1, 0)
self.parent.liveController.replaceServiceManagerItem(newItem) self.mainwindow.liveController.replaceServiceManagerItem(newItem)
self.setModified(True) self.setModified(True)
def addServiceItem(self, item, rebuild=False, expand=None, replace=False): def addServiceItem(self, item, rebuild=False, expand=None, replace=False):
@ -971,7 +973,7 @@ class ServiceManager(QtGui.QWidget):
item.merge(self.serviceItems[sitem][u'service_item']) item.merge(self.serviceItems[sitem][u'service_item'])
self.serviceItems[sitem][u'service_item'] = item self.serviceItems[sitem][u'service_item'] = item
self.repaintServiceList(sitem + 1, 0) self.repaintServiceList(sitem + 1, 0)
self.parent.liveController.replaceServiceManagerItem(item) self.mainwindow.liveController.replaceServiceManagerItem(item)
else: else:
# nothing selected for dnd # nothing selected for dnd
if self.dropPosition == 0: if self.dropPosition == 0:
@ -986,13 +988,13 @@ class ServiceManager(QtGui.QWidget):
u'expanded':expand}) u'expanded':expand})
self.repaintServiceList(len(self.serviceItems) + 1, 0) self.repaintServiceList(len(self.serviceItems) + 1, 0)
else: else:
self.serviceItems.insert(self.dropPosition, {u'service_item': item, self.serviceItems.insert(self.dropPosition,
u'order': self.dropPosition, {u'service_item': item, u'order': self.dropPosition,
u'expanded':expand}) u'expanded':expand})
self.repaintServiceList(self.dropPosition, 0) self.repaintServiceList(self.dropPosition, 0)
# if rebuilding list make sure live is fixed. # if rebuilding list make sure live is fixed.
if rebuild: if rebuild:
self.parent.liveController.replaceServiceManagerItem(item) self.mainwindow.liveController.replaceServiceManagerItem(item)
self.dropPosition = 0 self.dropPosition = 0
self.setModified(True) self.setModified(True)
@ -1002,7 +1004,7 @@ class ServiceManager(QtGui.QWidget):
""" """
item, count = self.findServiceItem() item, count = self.findServiceItem()
if self.serviceItems[item][u'service_item'].is_valid: if self.serviceItems[item][u'service_item'].is_valid:
self.parent.previewController.addServiceManagerItem( self.mainwindow.previewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], count) self.serviceItems[item][u'service_item'], count)
else: else:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
@ -1026,18 +1028,18 @@ class ServiceManager(QtGui.QWidget):
""" """
item, count = self.findServiceItem() item, count = self.findServiceItem()
if self.serviceItems[item][u'service_item'].is_valid: if self.serviceItems[item][u'service_item'].is_valid:
self.parent.liveController.addServiceManagerItem( self.mainwindow.liveController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], count) self.serviceItems[item][u'service_item'], count)
if QtCore.QSettings().value( if QtCore.QSettings().value(
self.parent.generalSettingsSection + u'/auto preview', self.mainwindow.generalSettingsSection + u'/auto preview',
QtCore.QVariant(False)).toBool(): QtCore.QVariant(False)).toBool():
item += 1 item += 1
if self.serviceItems and item < len(self.serviceItems) and \ if self.serviceItems and item < len(self.serviceItems) and \
self.serviceItems[item][u'service_item'].is_capable( self.serviceItems[item][u'service_item'].is_capable(
ItemCapabilities.AllowsPreview): ItemCapabilities.AllowsPreview):
self.parent.previewController.addServiceManagerItem( self.mainwindow.previewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], 0) self.serviceItems[item][u'service_item'], 0)
self.parent.liveController.PreviewListWidget.setFocus() self.mainwindow.liveController.PreviewListWidget.setFocus()
else: else:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('OpenLP.ServiceManager', 'Missing Display Handler'), translate('OpenLP.ServiceManager', 'Missing Display Handler'),
@ -1157,7 +1159,7 @@ class ServiceManager(QtGui.QWidget):
index = 0 index = 0
self.service_theme = u'' self.service_theme = u''
self.themeComboBox.setCurrentIndex(index) self.themeComboBox.setCurrentIndex(index)
self.parent.renderManager.set_service_theme(self.service_theme) self.mainwindow.renderManager.set_service_theme(self.service_theme)
self.regenerateServiceItems() self.regenerateServiceItems()
def onThemeChangeAction(self): def onThemeChangeAction(self):

View File

@ -31,7 +31,7 @@ from openlp.core.lib import translate, build_icon
class Ui_SettingsDialog(object): class Ui_SettingsDialog(object):
def setupUi(self, settingsDialog): def setupUi(self, settingsDialog):
settingsDialog.setObjectName(u'settingsDialog') settingsDialog.setObjectName(u'settingsDialog')
settingsDialog.resize(700, 300) settingsDialog.resize(700, 500)
settingsDialog.setWindowIcon( settingsDialog.setWindowIcon(
build_icon(u':/system/system_settings.png')) build_icon(u':/system/system_settings.png'))
self.settingsLayout = QtGui.QVBoxLayout(settingsDialog) self.settingsLayout = QtGui.QVBoxLayout(settingsDialog)

View File

@ -31,7 +31,7 @@ import logging
from PyQt4 import QtGui from PyQt4 import QtGui
from openlp.core.lib import Receiver from openlp.core.lib import Receiver
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab, DisplayTagTab
from settingsdialog import Ui_SettingsDialog from settingsdialog import Ui_SettingsDialog
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -55,6 +55,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
# Advanced tab # Advanced tab
self.advancedTab = AdvancedTab() self.advancedTab = AdvancedTab()
self.addTab(u'Advanced', self.advancedTab) self.addTab(u'Advanced', self.advancedTab)
# Edit Display Tags tab
self.displayTagTab = DisplayTagTab()
self.addTab(u'Display Tags', self.displayTagTab)
def addTab(self, name, tab): def addTab(self, name, tab):
""" """
@ -68,9 +71,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
Add a tab to the form at a specific location Add a tab to the form at a specific location
""" """
log.debug(u'Inserting %s tab' % tab.tabTitle) log.debug(u'Inserting %s tab' % tab.tabTitle)
# 14 : There are 3 tables currently and locations starts at -10 # 15 : There are 4 tables currently and locations starts at -10
self.settingsTabWidget.insertTab( self.settingsTabWidget.insertTab(
location + 14, tab, tab.tabTitleVisible) location + 15, tab, tab.tabTitleVisible)
def removeTab(self, tab): def removeTab(self, tab):
""" """
@ -93,6 +96,14 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
Receiver.send_message(u'config_updated') Receiver.send_message(u'config_updated')
return QtGui.QDialog.accept(self) return QtGui.QDialog.accept(self)
def reject(self):
"""
Process the form saving the settings
"""
for tabIndex in range(0, self.settingsTabWidget.count()):
self.settingsTabWidget.widget(tabIndex).cancel()
return QtGui.QDialog.reject(self)
def postSetUp(self): def postSetUp(self):
""" """
Run any post-setup code for the tabs on the form Run any post-setup code for the tabs on the form

View File

@ -212,8 +212,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
""" """
Updates the lines on a page on the wizard Updates the lines on a page on the wizard
""" """
self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', \ self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm',
'(%d lines per slide)' % int(lines)))) '(%d lines per slide)')) % int(lines))
def resizeEvent(self, event=None): def resizeEvent(self, event=None):
""" """
@ -291,9 +291,10 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.updateThemeAllowed = True self.updateThemeAllowed = True
self.themeNameLabel.setVisible(not edit) self.themeNameLabel.setVisible(not edit)
self.themeNameEdit.setVisible(not edit) self.themeNameEdit.setVisible(not edit)
self.edit_mode = edit
if edit: if edit:
self.setWindowTitle(unicode(translate('OpenLP.ThemeWizard', self.setWindowTitle(unicode(translate('OpenLP.ThemeWizard',
'Edit Theme %s')) % self.theme.theme_name) 'Edit Theme - %s')) % self.theme.theme_name)
self.next() self.next()
else: else:
self.setWindowTitle(translate('OpenLP.ThemeWizard', 'New Theme')) self.setWindowTitle(translate('OpenLP.ThemeWizard', 'New Theme'))
@ -581,7 +582,6 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
(QtGui.QMessageBox.Ok), (QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok)
return return
self.accepted = True
saveFrom = None saveFrom = None
saveTo = None saveTo = None
if self.theme.background_type == \ if self.theme.background_type == \
@ -590,8 +590,12 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
os.path.split(unicode(self.theme.background_filename))[1] os.path.split(unicode(self.theme.background_filename))[1]
saveTo = os.path.join(self.path, self.theme.theme_name, filename) saveTo = os.path.join(self.path, self.theme.theme_name, filename)
saveFrom = self.theme.background_filename saveFrom = self.theme.background_filename
if self.thememanager.saveTheme(self.theme, saveFrom, saveTo): if not self.edit_mode and \
return QtGui.QDialog.accept(self) not self.thememanager.checkIfThemeExists(self.theme.theme_name):
return
self.accepted = True
self.thememanager.saveTheme(self.theme, saveFrom, saveTo)
return QtGui.QDialog.accept(self)
def _colorButton(self, field): def _colorButton(self, field):
""" """

View File

@ -36,7 +36,7 @@ from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.theme import Theme from openlp.core.theme import Theme
from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \ from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \
build_icon, Receiver, SettingsManager, translate, check_item_selected, \ build_icon, Receiver, SettingsManager, translate, check_item_selected, \
BackgroundType, BackgroundGradientType BackgroundType, BackgroundGradientType, check_directory_exists
from openlp.core.utils import AppLocation, get_filesystem_encoding from openlp.core.utils import AppLocation, get_filesystem_encoding
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -45,13 +45,13 @@ class ThemeManager(QtGui.QWidget):
""" """
Manages the orders of Theme. Manages the orders of Theme.
""" """
def __init__(self, parent): def __init__(self, mainwindow, parent=None):
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.parent = parent self.mainwindow = mainwindow
self.settingsSection = u'themes' self.settingsSection = u'themes'
self.themeForm = ThemeForm(self) self.themeForm = ThemeForm(self)
self.fileRenameForm = FileRenameForm(self) self.fileRenameForm = FileRenameForm(self)
self.serviceComboBox = self.parent.ServiceManagerContents.themeComboBox self.serviceComboBox = self.mainwindow.ServiceManagerContents.themeComboBox
# start with the layout # start with the layout
self.layout = QtGui.QVBoxLayout(self) self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0) self.layout.setSpacing(0)
@ -119,27 +119,38 @@ class ThemeManager(QtGui.QWidget):
self.exportAction = self.menu.addAction( self.exportAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Export Theme')) translate('OpenLP.ThemeManager', '&Export Theme'))
self.exportAction.setIcon(build_icon(u':/general/general_export.png')) self.exportAction.setIcon(build_icon(u':/general/general_export.png'))
#Signals # Signals
QtCore.QObject.connect(self.themeListWidget, QtCore.QObject.connect(self.themeListWidget,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.changeGlobalFromScreen) self.changeGlobalFromScreen)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_global'), self.changeGlobalFromTab) QtCore.SIGNAL(u'theme_update_global'), self.changeGlobalFromTab)
#Variables QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
# Variables
self.themelist = [] self.themelist = []
self.path = AppLocation.get_section_data_path(self.settingsSection) self.path = AppLocation.get_section_data_path(self.settingsSection)
self.checkThemesExists(self.path) check_directory_exists(self.path)
self.thumbPath = os.path.join(self.path, u'thumbnails') self.thumbPath = os.path.join(self.path, u'thumbnails')
self.checkThemesExists(self.thumbPath) check_directory_exists(self.thumbPath)
self.themeForm.path = self.path self.themeForm.path = self.path
self.oldBackgroundImage = None self.oldBackgroundImage = None
self.editingDefault = False
# Last little bits of setting up # Last little bits of setting up
self.configUpdated()
def configUpdated(self, firstTime=False):
"""
Triggered when Config dialog is updated.
"""
self.global_theme = unicode(QtCore.QSettings().value( self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme', self.settingsSection + u'/global theme',
QtCore.QVariant(u'')).toString()) QtCore.QVariant(u'')).toString())
def contextMenu(self, point): def contextMenu(self, point):
"""
Build the Right Click Context menu and set state depending on
the type of theme.
"""
item = self.themeListWidget.itemAt(point) item = self.themeListWidget.itemAt(point)
if item is None: if item is None:
return return
@ -220,7 +231,6 @@ class ThemeManager(QtGui.QWidget):
editing form for the user to make their customisations. editing form for the user to make their customisations.
""" """
theme = ThemeXML() theme = ThemeXML()
self.saveThemeName = u''
self.themeForm.theme = theme self.themeForm.theme = theme
self.themeForm.exec_() self.themeForm.exec_()
@ -236,12 +246,15 @@ class ThemeManager(QtGui.QWidget):
item = self.themeListWidget.currentItem() item = self.themeListWidget.currentItem()
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString()) oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName) self.fileRenameForm.fileNameEdit.setText(oldThemeName)
self.saveThemeName = oldThemeName
if self.fileRenameForm.exec_(): if self.fileRenameForm.exec_():
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
oldThemeData = self.getThemeData(oldThemeName) if self.checkIfThemeExists(newThemeName):
self.deleteTheme(oldThemeName) oldThemeData = self.getThemeData(oldThemeName)
self.cloneThemeData(oldThemeData, newThemeName) self.deleteTheme(oldThemeName)
self.cloneThemeData(oldThemeData, newThemeName)
for plugin in self.mainwindow.pluginManager.plugins:
if plugin.usesTheme(oldThemeName):
plugin.renameTheme(oldThemeName, newThemeName)
def onCopyTheme(self): def onCopyTheme(self):
""" """
@ -250,12 +263,12 @@ class ThemeManager(QtGui.QWidget):
item = self.themeListWidget.currentItem() item = self.themeListWidget.currentItem()
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString()) oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName) self.fileRenameForm.fileNameEdit.setText(oldThemeName)
self.saveThemeName = u''
if self.fileRenameForm.exec_(True): if self.fileRenameForm.exec_(True):
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
themeData = self.getThemeData(oldThemeName) if self.checkIfThemeExists(newThemeName):
self.cloneThemeData(themeData, newThemeName) themeData = self.getThemeData(oldThemeName)
self.loadThemes() self.cloneThemeData(themeData, newThemeName)
self.loadThemes()
def cloneThemeData(self, themeData, newThemeName): def cloneThemeData(self, themeData, newThemeName):
""" """
@ -281,14 +294,10 @@ class ThemeManager(QtGui.QWidget):
'You must select a theme to edit.')): 'You must select a theme to edit.')):
item = self.themeListWidget.currentItem() item = self.themeListWidget.currentItem()
themeName = unicode(item.text()) themeName = unicode(item.text())
if themeName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.editingDefault = True
theme = self.getThemeData( theme = self.getThemeData(
unicode(item.data(QtCore.Qt.UserRole).toString())) unicode(item.data(QtCore.Qt.UserRole).toString()))
if theme.background_type == u'image': if theme.background_type == u'image':
self.oldBackgroundImage = theme.background_filename self.oldBackgroundImage = theme.background_filename
self.saveThemeName = unicode(
item.data(QtCore.Qt.UserRole).toString())
self.themeForm.theme = theme self.themeForm.theme = theme
self.themeForm.exec_(True) self.themeForm.exec_(True)
@ -449,20 +458,9 @@ class ThemeManager(QtGui.QWidget):
unicode(themeName) + u'.xml') unicode(themeName) + u'.xml')
xml = get_text_file_string(xmlFile) xml = get_text_file_string(xmlFile)
if not xml: if not xml:
return self.baseTheme() return self._baseTheme()
else: else:
return self.createThemeFromXml(xml, self.path) return self._createThemeFromXml(xml, self.path)
def checkThemesExists(self, dir):
"""
Check a theme directory exists and if not create it
``dir``
Theme directory to make sure exists
"""
log.debug(u'check themes')
if not os.path.exists(dir):
os.mkdir(dir)
def unzipTheme(self, filename, dir): def unzipTheme(self, filename, dir):
""" """
@ -494,8 +492,7 @@ class ThemeManager(QtGui.QWidget):
theme_dir = None theme_dir = None
if osfile.endswith(os.path.sep): if osfile.endswith(os.path.sep):
theme_dir = os.path.join(dir, osfile) theme_dir = os.path.join(dir, osfile)
if not os.path.exists(theme_dir): check_directory_exists(theme_dir)
os.mkdir(os.path.join(dir, osfile))
else: else:
fullpath = os.path.join(dir, osfile) fullpath = os.path.join(dir, osfile)
names = osfile.split(os.path.sep) names = osfile.split(os.path.sep)
@ -505,8 +502,7 @@ class ThemeManager(QtGui.QWidget):
themename = names[0] themename = names[0]
if theme_dir is None: if theme_dir is None:
theme_dir = os.path.join(dir, names[0]) theme_dir = os.path.join(dir, names[0])
if not os.path.exists(theme_dir): check_directory_exists(theme_dir)
os.mkdir(os.path.join(dir, names[0]))
if os.path.splitext(ucsfile)[1].lower() in [u'.xml']: if os.path.splitext(ucsfile)[1].lower() in [u'.xml']:
xml_data = zip.read(file) xml_data = zip.read(file)
try: try:
@ -522,19 +518,22 @@ class ThemeManager(QtGui.QWidget):
outfile = open(fullpath, u'wb') outfile = open(fullpath, u'wb')
outfile.write(zip.read(file)) outfile.write(zip.read(file))
if filexml: if filexml:
theme = self.createThemeFromXml(filexml, self.path) theme = self._createThemeFromXml(filexml, self.path)
self.generateAndSaveImage(dir, themename, theme) self.generateAndSaveImage(dir, themename, theme)
else: else:
QtGui.QMessageBox.critical(self, Receiver.send_message(u'openlp_error_message', {
translate('OpenLP.ThemeManager', 'Error'), u'title': translate('OpenLP.ThemeManager',
translate('OpenLP.ThemeManager', 'Validation Error'),
'File is not a valid theme.')) u'message':translate('OpenLP.ThemeManager',
'File is not a valid theme.')})
log.exception(u'Theme file does not contain XML data %s' % log.exception(u'Theme file does not contain XML data %s' %
filename) filename)
except (IOError, NameError): except (IOError, NameError):
QtGui.QMessageBox.critical(self, Receiver.send_message(u'openlp_error_message', {
translate('OpenLP.ThemeManager', 'Error'), u'title': translate('OpenLP.ThemeManager',
translate('OpenLP.ThemeManager', 'File is not a valid theme.')) 'Validation Error'),
u'message':translate('OpenLP.ThemeManager',
'File is not a valid theme.')})
log.exception(u'Importing theme from zip failed %s' % filename) log.exception(u'Importing theme from zip failed %s' % filename)
finally: finally:
if zip: if zip:
@ -556,9 +555,161 @@ class ThemeManager(QtGui.QWidget):
if tree.find(u'BackgroundType') is None: if tree.find(u'BackgroundType') is None:
return xml_data return xml_data
else: else:
return self.migrateVersion122(xml_data) return self._migrateVersion122(xml_data)
def migrateVersion122(self, xml_data): def checkIfThemeExists(self, themeName):
"""
Check if theme already exists and displays error message
``themeName``
Name of the Theme to test
"""
theme_dir = os.path.join(self.path, themeName)
if os.path.exists(theme_dir):
Receiver.send_message(u'openlp_error_message', {
u'title': translate('OpenLP.ThemeManager',
'Validation Error'),
u'message':translate('OpenLP.ThemeManager',
'A theme with this name already exists.')})
return False
return True
def saveTheme(self, theme, imageFrom, imageTo):
"""
Called by thememaintenance Dialog to save the theme
and to trigger the reload of the theme list
"""
name = theme.theme_name
theme_pretty_xml = theme.extract_formatted_xml()
log.debug(u'saveTheme %s %s', name, theme_pretty_xml)
theme_dir = os.path.join(self.path, name)
check_directory_exists(theme_dir)
theme_file = os.path.join(theme_dir, name + u'.xml')
if imageTo and self.oldBackgroundImage and \
imageTo != self.oldBackgroundImage:
try:
os.remove(self.oldBackgroundImage)
except OSError:
log.exception(u'Unable to remove old theme background')
outfile = None
try:
outfile = open(theme_file, u'w')
outfile.write(theme_pretty_xml)
except IOError:
log.exception(u'Saving theme to file failed')
finally:
if outfile:
outfile.close()
if imageFrom and imageFrom != imageTo:
try:
encoding = get_filesystem_encoding()
shutil.copyfile(
unicode(imageFrom).encode(encoding),
unicode(imageTo).encode(encoding))
except IOError:
log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme)
self.loadThemes()
self.pushThemes()
def generateAndSaveImage(self, dir, name, theme):
log.debug(u'generateAndSaveImage %s %s', dir, name)
theme_xml = theme.extract_xml()
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)
icon = build_icon(frame)
pixmap = icon.pixmap(QtCore.QSize(88, 50))
pixmap.save(thumb, u'png')
log.debug(u'Theme image written to %s', samplepathname)
def generateImage(self, themeData, forcePage=False):
"""
Call the RenderManager to build a Sample Image
``themeData``
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)
return self.mainwindow.renderManager.generate_preview(themeData, forcePage)
def getPreviewImage(self, theme):
"""
Return an image representing the look of the theme
``theme``
The theme to return the image for
"""
log.debug(u'getPreviewImage %s ', theme)
image = os.path.join(self.path, theme + u'.png')
return image
def _baseTheme(self):
"""
Provide a base theme with sensible defaults
"""
log.debug(u'base theme created')
newtheme = ThemeXML()
return newtheme
def _createThemeFromXml(self, themeXml, path):
"""
Return a theme object using information parsed from XML
``themeXml``
The XML data to load into the theme
"""
theme = ThemeXML()
theme.parse(themeXml)
theme.extend_image_filename(path)
return theme
def _validate_theme_action(self, select_text, confirm_title, confirm_text,
testPlugin=True):
"""
Check to see if theme has been selected and the destructive action
is allowed.
"""
self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme',
QtCore.QVariant(u'')).toString())
if check_item_selected(self.themeListWidget, select_text):
item = self.themeListWidget.currentItem()
theme = unicode(item.text())
# confirm deletion
answer = QtGui.QMessageBox.question(self, confirm_title,
confirm_text % theme, QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
# should be the same unless default
if theme != unicode(item.data(QtCore.Qt.UserRole).toString()):
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
translate('OpenLP.ThemeManager',
'You are unable to delete the default theme.'))
return False
# check for use in the system else where.
if testPlugin:
for plugin in self.mainwindow.pluginManager.plugins:
if plugin.usesTheme(theme):
Receiver.send_message(u'openlp_error_message', {
u'title': translate('OpenLP.ThemeManager',
'Validation Error'),
u'message': unicode(translate('OpenLP.ThemeManager',
'Theme %s is used in the %s plugin.')) % \
(theme, plugin.name)})
return False
return True
def _migrateVersion122(self, xml_data):
""" """
Convert the xml data from version 1 format to the current format. Convert the xml data from version 1 format to the current format.
@ -615,191 +766,3 @@ class ThemeManager(QtGui.QWidget):
newtheme.display_horizontal_align = theme.HorizontalAlign newtheme.display_horizontal_align = theme.HorizontalAlign
newtheme.display_vertical_align = vAlignCorrection newtheme.display_vertical_align = vAlignCorrection
return newtheme.extract_xml() return newtheme.extract_xml()
def saveTheme(self, theme, imageFrom, imageTo):
"""
Called by thememaintenance Dialog to save the theme
and to trigger the reload of the theme list
"""
name = theme.theme_name
theme_pretty_xml = theme.extract_formatted_xml()
log.debug(u'saveTheme %s %s', name, theme_pretty_xml)
theme_dir = os.path.join(self.path, name)
if not os.path.exists(theme_dir):
os.mkdir(os.path.join(self.path, name))
theme_file = os.path.join(theme_dir, name + u'.xml')
log.debug(theme_file)
editedServiceTheme = False
result = QtGui.QMessageBox.Yes
if self.saveThemeName != name:
if os.path.exists(theme_file):
result = QtGui.QMessageBox.question(self,
translate('OpenLP.ThemeManager', 'Theme Exists'),
translate('OpenLP.ThemeManager',
'A theme with this name already '
'exists. Would you like to overwrite it?'),
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
if self.saveThemeName != u'':
for plugin in self.parent.pluginManager.plugins:
if plugin.usesTheme(self.saveThemeName):
plugin.renameTheme(self.saveThemeName, name)
if unicode(self.serviceComboBox.currentText()) == name:
editedServiceTheme = True
if result == QtGui.QMessageBox.Yes:
# Save the theme, overwriting the existing theme if necessary.
if imageTo and self.oldBackgroundImage and \
imageTo != self.oldBackgroundImage:
try:
os.remove(self.oldBackgroundImage)
except OSError:
log.exception(u'Unable to remove old theme background')
outfile = None
try:
outfile = open(theme_file, u'w')
outfile.write(theme_pretty_xml)
except IOError:
log.exception(u'Saving theme to file failed')
finally:
if outfile:
outfile.close()
if imageFrom and imageFrom != imageTo:
try:
encoding = get_filesystem_encoding()
shutil.copyfile(
unicode(imageFrom).encode(encoding),
unicode(imageTo).encode(encoding))
except IOError:
log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme)
self.loadThemes()
# Check if we need to set a new service theme
if editedServiceTheme:
newThemeIndex = self.serviceComboBox.findText(name)
if newThemeIndex != -1:
self.serviceComboBox.setCurrentIndex(newThemeIndex)
if self.editingDefault:
if self.saveThemeName != name:
newThemeItem = self.themeListWidget.findItems(name,
QtCore.Qt.MatchExactly)[0]
newThemeIndex = self.themeListWidget.indexFromItem(
newThemeItem).row()
self.global_theme = unicode(
self.themeListWidget.item(newThemeIndex).text())
newName = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % self.global_theme
self.themeListWidget.item(newThemeIndex).setText(newName)
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global',
self.global_theme)
self.editingDefault = False
self.pushThemes()
return True
else:
# Don't close the dialog - allow the user to change the name of
# the theme or to cancel the theme dialog completely.
return False
def generateAndSaveImage(self, dir, name, theme):
log.debug(u'generateAndSaveImage %s %s', dir, name)
theme_xml = theme.extract_xml()
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)
icon = build_icon(frame)
pixmap = icon.pixmap(QtCore.QSize(88, 50))
pixmap.save(thumb, u'png')
log.debug(u'Theme image written to %s', samplepathname)
def generateImage(self, themeData, forcePage=False):
"""
Call the RenderManager to build a Sample Image
``themeData``
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)
return self.parent.renderManager.generate_preview(themeData, forcePage)
def getPreviewImage(self, theme):
"""
Return an image representing the look of the theme
``theme``
The theme to return the image for
"""
log.debug(u'getPreviewImage %s ', theme)
image = os.path.join(self.path, theme + u'.png')
return image
def baseTheme(self):
"""
Provide a base theme with sensible defaults
"""
log.debug(u'base theme created')
newtheme = ThemeXML()
return newtheme
def createThemeFromXml(self, themeXml, path):
"""
Return a theme object using information parsed from XML
``themeXml``
The XML data to load into the theme
"""
theme = ThemeXML()
theme.parse(themeXml)
theme.extend_image_filename(path)
return theme
def _validate_theme_action(self, select_text, confirm_title, confirm_text,
testPlugin=True):
"""
Check to see if theme has been selected and the destructive action
is allowed.
"""
self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme',
QtCore.QVariant(u'')).toString())
if check_item_selected(self.themeListWidget, select_text):
item = self.themeListWidget.currentItem()
theme = unicode(item.text())
# confirm deletion
answer = QtGui.QMessageBox.question(self, confirm_title,
confirm_text % theme, QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
# should be the same unless default
if theme != unicode(item.data(QtCore.Qt.UserRole).toString()):
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
translate('OpenLP.ThemeManager',
'You are unable to delete the default theme.'))
return False
else:
if testPlugin:
for plugin in self.parent.pluginManager.plugins:
if plugin.usesTheme(theme):
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
'Theme %s is used in the %s plugin.')) % \
(theme, plugin.name))
return False
if unicode(self.serviceComboBox.currentText()) == theme:
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
'Theme %s is used by the service manager.')) % theme)
return False
return True

View File

@ -46,8 +46,8 @@ class ThemesTab(SettingsTab):
self.DefaultComboBox = QtGui.QComboBox(self.GlobalGroupBox) self.DefaultComboBox = QtGui.QComboBox(self.GlobalGroupBox)
self.DefaultComboBox.setSizeAdjustPolicy( self.DefaultComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.DefaultComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.DefaultComboBox.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.DefaultComboBox.setObjectName(u'DefaultComboBox') self.DefaultComboBox.setObjectName(u'DefaultComboBox')
self.GlobalGroupBoxLayout.addWidget(self.DefaultComboBox) self.GlobalGroupBoxLayout.addWidget(self.DefaultComboBox)
self.DefaultListView = QtGui.QLabel(self.GlobalGroupBox) self.DefaultListView = QtGui.QLabel(self.GlobalGroupBox)

View File

@ -249,7 +249,8 @@ class Ui_ThemeWizard(object):
self.footerSizeSpinBox.setMaximum(999) self.footerSizeSpinBox.setMaximum(999)
self.footerSizeSpinBox.setValue(10) self.footerSizeSpinBox.setValue(10)
self.footerSizeSpinBox.setObjectName(u'FooterSizeSpinBox') self.footerSizeSpinBox.setObjectName(u'FooterSizeSpinBox')
self.footerAreaLayout.addRow(self.footerSizeLabel, self.footerSizeSpinBox) self.footerAreaLayout.addRow(self.footerSizeLabel,
self.footerSizeSpinBox)
ThemeWizard.addPage(self.footerAreaPage) ThemeWizard.addPage(self.footerAreaPage)
# Alignment Page # Alignment Page
self.alignmentPage = QtGui.QWizardPage() self.alignmentPage = QtGui.QWizardPage()
@ -317,9 +318,11 @@ class Ui_ThemeWizard(object):
self.areaPositionLayout.addWidget(self.mainPositionGroupBox) self.areaPositionLayout.addWidget(self.mainPositionGroupBox)
self.footerPositionGroupBox = QtGui.QGroupBox(self.areaPositionPage) self.footerPositionGroupBox = QtGui.QGroupBox(self.areaPositionPage)
self.footerPositionGroupBox.setObjectName(u'FooterPositionGroupBox') self.footerPositionGroupBox.setObjectName(u'FooterPositionGroupBox')
self.footerPositionLayout = QtGui.QFormLayout(self.footerPositionGroupBox) self.footerPositionLayout = QtGui.QFormLayout(
self.footerPositionGroupBox)
self.footerPositionLayout.setObjectName(u'FooterPositionLayout') self.footerPositionLayout.setObjectName(u'FooterPositionLayout')
self.footerPositionCheckBox = QtGui.QCheckBox(self.footerPositionGroupBox) self.footerPositionCheckBox = QtGui.QCheckBox(
self.footerPositionGroupBox)
self.footerPositionCheckBox.setObjectName(u'FooterPositionCheckBox') self.footerPositionCheckBox.setObjectName(u'FooterPositionCheckBox')
self.footerPositionLayout.addRow(self.footerPositionCheckBox) self.footerPositionLayout.addRow(self.footerPositionCheckBox)
self.footerXLabel = QtGui.QLabel(self.footerPositionGroupBox) self.footerXLabel = QtGui.QLabel(self.footerPositionGroupBox)
@ -473,8 +476,6 @@ class Ui_ThemeWizard(object):
self.mainColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.mainColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:'))
self.mainSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) self.mainSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:'))
self.mainSizeSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) self.mainSizeSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt'))
self.mainLineCountLabel.setText(
translate('OpenLP.ThemeWizard', '(%d lines per slide)'))
self.lineSpacingLabel.setText( self.lineSpacingLabel.setText(
translate('OpenLP.ThemeWizard', 'Line Spacing:')) translate('OpenLP.ThemeWizard', 'Line Spacing:'))
self.lineSpacingSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) self.lineSpacingSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt'))
@ -566,17 +567,17 @@ class Ui_ThemeWizard(object):
self.themeNameLabel.setText( self.themeNameLabel.setText(
translate('OpenLP.ThemeWizard', 'Theme name:')) translate('OpenLP.ThemeWizard', 'Theme name:'))
# Align all QFormLayouts towards each other. # Align all QFormLayouts towards each other.
width = max(self.backgroundLabel.minimumSizeHint().width(), labelWidth = max(self.backgroundLabel.minimumSizeHint().width(),
self.colorLabel.minimumSizeHint().width()) self.colorLabel.minimumSizeHint().width(),
width = max(width, self.gradientStartLabel.minimumSizeHint().width()) self.gradientStartLabel.minimumSizeHint().width(),
width = max(width, self.gradientEndLabel.minimumSizeHint().width()) self.gradientEndLabel.minimumSizeHint().width(),
width = max(width, self.gradientTypeLabel.minimumSizeHint().width()) self.gradientTypeLabel.minimumSizeHint().width(),
width = max(width, self.imageLabel.minimumSizeHint().width()) self.imageLabel.minimumSizeHint().width())
self.backgroundTypeSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.backgroundTypeSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.colorSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.colorSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.gradientSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.gradientSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.imageSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.imageSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)

View File

@ -73,7 +73,7 @@ class AlertsPlugin(Plugin):
self.toolsAlertItem.setStatusTip( self.toolsAlertItem.setStatusTip(
translate('AlertsPlugin', 'Show an alert message.')) translate('AlertsPlugin', 'Show an alert message.'))
self.toolsAlertItem.setShortcut(u'F7') self.toolsAlertItem.setShortcut(u'F7')
self.serviceManager.parent.ToolsMenu.addAction(self.toolsAlertItem) self.serviceManager.mainwindow.ToolsMenu.addAction(self.toolsAlertItem)
QtCore.QObject.connect(self.toolsAlertItem, QtCore.QObject.connect(self.toolsAlertItem,
QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger) QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger)
self.toolsAlertItem.setVisible(False) self.toolsAlertItem.setVisible(False)

View File

@ -234,7 +234,7 @@ class Ui_BibleImportWizard(object):
QtGui.QSizePolicy.Minimum) QtGui.QSizePolicy.Minimum)
self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole,
self.openlp1Spacer) self.openlp1Spacer)
self.selectStack.addWidget(self.openlp1Widget) self.selectStack.addWidget(self.openlp1Widget)
self.selectPageLayout.addLayout(self.selectStack) self.selectPageLayout.addLayout(self.selectStack)
bibleImportWizard.addPage(self.selectPage) bibleImportWizard.addPage(self.selectPage)
# License Page # License Page
@ -373,19 +373,19 @@ class Ui_BibleImportWizard(object):
'you want to use this importer, you will need to install the ' 'you want to use this importer, you will need to install the '
'"python-sqlite" module.')) '"python-sqlite" module.'))
# Align all QFormLayouts towards each other. # Align all QFormLayouts towards each other.
width = max(self.formatLabel.minimumSizeHint().width(), labelWidth = max(self.formatLabel.minimumSizeHint().width(),
self.osisFileLabel.minimumSizeHint().width()) self.osisFileLabel.minimumSizeHint().width(),
width = max(width, self.csvBooksLabel.minimumSizeHint().width()) self.csvBooksLabel.minimumSizeHint().width(),
width = max(width, self.csvVersesLabel.minimumSizeHint().width()) self.csvVersesLabel.minimumSizeHint().width(),
width = max(width, self.openSongFileLabel.minimumSizeHint().width()) self.openSongFileLabel.minimumSizeHint().width(),
width = max(width, self.openlp1FileLabel.minimumSizeHint().width()) self.openlp1FileLabel.minimumSizeHint().width())
self.formatSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.formatSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.osisSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.osisSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.csvSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.csvSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.openSongSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.openSongSpacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.openlp1Spacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, self.openlp1Spacer.changeSize(labelWidth, 0,
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)

View File

@ -76,8 +76,8 @@ class BiblesTab(SettingsTab):
self.BibleThemeComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox) self.BibleThemeComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox)
self.BibleThemeComboBox.setSizeAdjustPolicy( self.BibleThemeComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.BibleThemeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.BibleThemeComboBox.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.BibleThemeComboBox.addItem(u'') self.BibleThemeComboBox.addItem(u'')
self.BibleThemeComboBox.setObjectName(u'BibleThemeComboBox') self.BibleThemeComboBox.setObjectName(u'BibleThemeComboBox')
self.VerseDisplayLayout.addRow(self.BibleThemeLabel, self.VerseDisplayLayout.addRow(self.BibleThemeLabel,
@ -88,8 +88,8 @@ class BiblesTab(SettingsTab):
self.VerseDisplayLayout.addRow(self.ChangeNoteLabel) self.VerseDisplayLayout.addRow(self.ChangeNoteLabel)
self.leftLayout.addWidget(self.VerseDisplayGroupBox) self.leftLayout.addWidget(self.VerseDisplayGroupBox)
self.leftLayout.addStretch() self.leftLayout.addStretch()
self.rightColumn.setSizePolicy(QtGui.QSizePolicy.Expanding, self.rightColumn.setSizePolicy(
QtGui.QSizePolicy.Preferred) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
self.rightLayout.addStretch() self.rightLayout.addStretch()
# Signals and slots # Signals and slots
QtCore.QObject.connect( QtCore.QObject.connect(

View File

@ -231,6 +231,9 @@ class BGExtract(object):
footnotes = soup.findAll(u'sup', u'footnote') footnotes = soup.findAll(u'sup', u'footnote')
if footnotes: if footnotes:
[footnote.extract() for footnote in footnotes] [footnote.extract() for footnote in footnotes]
crossrefs = soup.findAll(u'sup', u'xref')
if crossrefs:
[crossref.extract() for crossref in crossrefs]
cleanup = [(re.compile('\s+'), lambda match: ' ')] cleanup = [(re.compile('\s+'), lambda match: ' ')]
verses = BeautifulSoup(str(soup), markupMassage=cleanup) verses = BeautifulSoup(str(soup), markupMassage=cleanup)
content = verses.find(u'div', u'result-text-style-normal') content = verses.find(u'div', u'result-text-style-normal')
@ -306,7 +309,7 @@ class BSExtract(object):
finally: finally:
if not content: if not content:
return None return None
verse_number = re.compile(r'v(\d{2})(\d{3})(\d{3}) verse') verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse')
verses = {} verses = {}
for verse in content: for verse in content:
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')

View File

@ -273,10 +273,10 @@ class BibleManager(object):
Receiver.send_message(u'openlp_information_message', { Receiver.send_message(u'openlp_information_message', {
u'title': translate('BiblesPlugin.BibleManager', u'title': translate('BiblesPlugin.BibleManager',
'Scripture Reference Error'), 'Scripture Reference Error'),
u'message': translate('BiblesPlugin.BibleManager', 'Your scripture ' u'message': translate('BiblesPlugin.BibleManager',
'reference is either not supported by OpenLP or is invalid. ' 'Your scripture reference is either not supported by OpenLP '
'Please make sure your reference conforms to one of the ' 'or is invalid. Please make sure your reference conforms to '
'following patterns:\n\n' 'one of the following patterns:\n\n'
'Book Chapter\n' 'Book Chapter\n'
'Book Chapter-Chapter\n' 'Book Chapter-Chapter\n'
'Book Chapter:Verse-Verse\n' 'Book Chapter:Verse-Verse\n'

View File

@ -86,6 +86,8 @@ class BibleMediaItem(MediaManagerItem):
self.quickVersionLabel = QtGui.QLabel(self.quickTab) self.quickVersionLabel = QtGui.QLabel(self.quickTab)
self.quickVersionLabel.setObjectName(u'quickVersionLabel') self.quickVersionLabel.setObjectName(u'quickVersionLabel')
self.quickVersionComboBox = QtGui.QComboBox(self.quickTab) self.quickVersionComboBox = QtGui.QComboBox(self.quickTab)
self.quickVersionComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.quickVersionComboBox.setSizePolicy( self.quickVersionComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.quickVersionComboBox.setObjectName(u'quickVersionComboBox') self.quickVersionComboBox.setObjectName(u'quickVersionComboBox')
@ -95,6 +97,8 @@ class BibleMediaItem(MediaManagerItem):
self.quickSecondLabel = QtGui.QLabel(self.quickTab) self.quickSecondLabel = QtGui.QLabel(self.quickTab)
self.quickSecondLabel.setObjectName(u'quickSecondLabel') self.quickSecondLabel.setObjectName(u'quickSecondLabel')
self.quickSecondComboBox = QtGui.QComboBox(self.quickTab) self.quickSecondComboBox = QtGui.QComboBox(self.quickTab)
self.quickSecondComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.quickSecondComboBox.setSizePolicy( self.quickSecondComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.quickSecondComboBox.setObjectName(u'quickSecondComboBox') self.quickSecondComboBox.setObjectName(u'quickSecondComboBox')
@ -103,6 +107,8 @@ class BibleMediaItem(MediaManagerItem):
self.quickSearchTypeLabel = QtGui.QLabel(self.quickTab) self.quickSearchTypeLabel = QtGui.QLabel(self.quickTab)
self.quickSearchTypeLabel.setObjectName(u'quickSearchTypeLabel') self.quickSearchTypeLabel.setObjectName(u'quickSearchTypeLabel')
self.quickSearchComboBox = QtGui.QComboBox(self.quickTab) self.quickSearchComboBox = QtGui.QComboBox(self.quickTab)
self.quickSearchComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.quickSearchComboBox.setSizePolicy( self.quickSearchComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.quickSearchComboBox.setObjectName(u'quickSearchComboBox') self.quickSearchComboBox.setObjectName(u'quickSearchComboBox')
@ -118,6 +124,8 @@ class BibleMediaItem(MediaManagerItem):
self.quickClearLabel = QtGui.QLabel(self.quickTab) self.quickClearLabel = QtGui.QLabel(self.quickTab)
self.quickClearLabel.setObjectName(u'quickClearLabel') self.quickClearLabel.setObjectName(u'quickClearLabel')
self.quickClearComboBox = QtGui.QComboBox(self.quickTab) self.quickClearComboBox = QtGui.QComboBox(self.quickTab)
self.quickClearComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.quickClearComboBox.setSizePolicy( self.quickClearComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.quickClearComboBox.setObjectName(u'quickClearComboBox') self.quickClearComboBox.setObjectName(u'quickClearComboBox')
@ -144,6 +152,8 @@ class BibleMediaItem(MediaManagerItem):
self.advancedLayout.addWidget(self.advancedVersionLabel, 0, 0, self.advancedLayout.addWidget(self.advancedVersionLabel, 0, 0,
QtCore.Qt.AlignRight) QtCore.Qt.AlignRight)
self.advancedVersionComboBox = QtGui.QComboBox(self.advancedTab) self.advancedVersionComboBox = QtGui.QComboBox(self.advancedTab)
self.advancedVersionComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.advancedVersionComboBox.setSizePolicy( self.advancedVersionComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.advancedVersionComboBox.setObjectName(u'advancedVersionComboBox') self.advancedVersionComboBox.setObjectName(u'advancedVersionComboBox')
@ -154,6 +164,8 @@ class BibleMediaItem(MediaManagerItem):
self.advancedLayout.addWidget(self.advancedSecondLabel, 1, 0, self.advancedLayout.addWidget(self.advancedSecondLabel, 1, 0,
QtCore.Qt.AlignRight) QtCore.Qt.AlignRight)
self.advancedSecondComboBox = QtGui.QComboBox(self.advancedTab) self.advancedSecondComboBox = QtGui.QComboBox(self.advancedTab)
self.advancedSecondComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.advancedSecondComboBox.setSizePolicy( self.advancedSecondComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.advancedSecondComboBox.setObjectName(u'advancedSecondComboBox') self.advancedSecondComboBox.setObjectName(u'advancedSecondComboBox')
@ -164,6 +176,8 @@ class BibleMediaItem(MediaManagerItem):
self.advancedLayout.addWidget(self.advancedBookLabel, 2, 0, self.advancedLayout.addWidget(self.advancedBookLabel, 2, 0,
QtCore.Qt.AlignRight) QtCore.Qt.AlignRight)
self.advancedBookComboBox = QtGui.QComboBox(self.advancedTab) self.advancedBookComboBox = QtGui.QComboBox(self.advancedTab)
self.advancedBookComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.advancedBookComboBox.setSizePolicy( self.advancedBookComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.advancedBookComboBox.setObjectName(u'advancedBookComboBox') self.advancedBookComboBox.setObjectName(u'advancedBookComboBox')
@ -201,6 +215,8 @@ class BibleMediaItem(MediaManagerItem):
self.advancedLayout.addWidget(self.advancedClearLabel, 6, 0, self.advancedLayout.addWidget(self.advancedClearLabel, 6, 0,
QtCore.Qt.AlignRight) QtCore.Qt.AlignRight)
self.advancedClearComboBox = QtGui.QComboBox(self.quickTab) self.advancedClearComboBox = QtGui.QComboBox(self.quickTab)
self.advancedClearComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength)
self.advancedClearComboBox.setSizePolicy( self.advancedClearComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.advancedClearComboBox.setObjectName(u'advancedClearComboBox') self.advancedClearComboBox.setObjectName(u'advancedClearComboBox')
@ -709,11 +725,11 @@ class BibleMediaItem(MediaManagerItem):
if len(items) == 0: if len(items) == 0:
return False return False
bible_text = u'' bible_text = u''
old_item = None
old_chapter = -1 old_chapter = -1
raw_footer = [] raw_footer = []
raw_slides = [] raw_slides = []
raw_title = [] raw_title = []
first_item = True
for item in items: for item in items:
bitem = self.listView.item(item.row()) bitem = self.listView.item(item.row())
book = self._decodeQtObject(bitem, 'book') book = self._decodeQtObject(bitem, 'book')
@ -754,9 +770,8 @@ class BibleMediaItem(MediaManagerItem):
# We have to be 'Continuous'. # We have to be 'Continuous'.
else: else:
bible_text = u'%s %s\u00a0%s\n' % (bible_text, verse_text, text) bible_text = u'%s %s\u00a0%s\n' % (bible_text, verse_text, text)
if first_item: if not old_item:
start_item = item start_item = item
first_item = False
elif self.checkTitle(item, old_item): elif self.checkTitle(item, old_item):
raw_title.append(self.formatTitle(start_item, old_item)) raw_title.append(self.formatTitle(start_item, old_item))
start_item = item start_item = item

View File

@ -30,7 +30,8 @@ import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \
ItemCapabilities, SettingsManager, translate, check_item_selected, Receiver ItemCapabilities, SettingsManager, translate, check_item_selected, \
Receiver, check_directory_exists
from openlp.core.utils import AppLocation, get_images_filter from openlp.core.utils import AppLocation, get_images_filter
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -87,8 +88,7 @@ class ImageMediaItem(MediaManagerItem):
self.servicePath = os.path.join( self.servicePath = os.path.join(
AppLocation.get_section_data_path(self.settingsSection), AppLocation.get_section_data_path(self.settingsSection),
u'thumbnails') u'thumbnails')
if not os.path.exists(self.servicePath): check_directory_exists(self.servicePath)
os.mkdir(self.servicePath)
self.loadList(SettingsManager.load_list( self.loadList(SettingsManager.load_list(
self.settingsSection, self.settingsSection)) self.settingsSection, self.settingsSection))
@ -216,4 +216,4 @@ class ImageMediaItem(MediaManagerItem):
'the image file "%s" no longer exists.')) % filename}) 'the image file "%s" no longer exists.')) % filename})
def onPreviewClick(self): def onPreviewClick(self):
MediaManagerItem.onPreviewClick(self) MediaManagerItem.onPreviewClick(self)

View File

@ -116,8 +116,10 @@ class PresentationMediaItem(MediaManagerItem):
self.displayTypeLabel = QtGui.QLabel(self.presentationWidget) self.displayTypeLabel = QtGui.QLabel(self.presentationWidget)
self.displayTypeLabel.setObjectName(u'displayTypeLabel') self.displayTypeLabel.setObjectName(u'displayTypeLabel')
self.displayTypeComboBox = QtGui.QComboBox(self.presentationWidget) self.displayTypeComboBox = QtGui.QComboBox(self.presentationWidget)
self.displayTypeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.displayTypeComboBox.setSizeAdjustPolicy(
QtGui.QSizePolicy.Fixed) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.displayTypeComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.displayTypeComboBox.setObjectName(u'displayTypeComboBox') self.displayTypeComboBox.setObjectName(u'displayTypeComboBox')
self.displayTypeLabel.setBuddy(self.displayTypeComboBox) self.displayTypeLabel.setBuddy(self.displayTypeComboBox)
self.displayLayout.addRow(self.displayTypeLabel, self.displayLayout.addRow(self.displayTypeLabel,

View File

@ -116,7 +116,8 @@ class PresentationTab(SettingsTab):
if controller.available: if controller.available:
checkbox = self.PresenterCheckboxes[controller.name] checkbox = self.PresenterCheckboxes[controller.name]
setting_key = self.settingsSection + u'/' + controller.name setting_key = self.settingsSection + u'/' + controller.name
if QtCore.QSettings().value(setting_key) != checkbox.checkState(): if QtCore.QSettings().value(setting_key) != \
checkbox.checkState():
changed = True changed = True
QtCore.QSettings().setValue(setting_key, QtCore.QSettings().setValue(setting_key,
QtCore.QVariant(checkbox.checkState())) QtCore.QVariant(checkbox.checkState()))

View File

@ -46,8 +46,8 @@ class RemoteTab(SettingsTab):
self.addressLabel = QtGui.QLabel(self.serverSettingsGroupBox) self.addressLabel = QtGui.QLabel(self.serverSettingsGroupBox)
self.addressLabel.setObjectName(u'addressLabel') self.addressLabel.setObjectName(u'addressLabel')
self.addressEdit = QtGui.QLineEdit(self.serverSettingsGroupBox) self.addressEdit = QtGui.QLineEdit(self.serverSettingsGroupBox)
self.addressEdit.setSizePolicy(QtGui.QSizePolicy.Preferred, self.addressEdit.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
self.addressEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp( self.addressEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(
u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self)) u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self))
self.addressEdit.setObjectName(u'addressEdit') self.addressEdit.setObjectName(u'addressEdit')

View File

@ -54,7 +54,7 @@ class Ui_AuthorsDialog(object):
self.displayEdit.setObjectName(u'displayEdit') self.displayEdit.setObjectName(u'displayEdit')
self.displayLabel.setBuddy(self.displayEdit) self.displayLabel.setBuddy(self.displayEdit)
self.authorLayout.addRow(self.displayLabel, self.displayEdit) self.authorLayout.addRow(self.displayLabel, self.displayEdit)
self.dialogLayout.addLayout(self.authorLayout) self.dialogLayout.addLayout(self.authorLayout)
self.buttonBox = QtGui.QDialogButtonBox(authorsDialog) self.buttonBox = QtGui.QDialogButtonBox(authorsDialog)
self.buttonBox.setStandardButtons( self.buttonBox.setStandardButtons(
QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel) QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel)

View File

@ -114,8 +114,8 @@ class Ui_EditSongDialog(object):
self.authorsComboBox = QtGui.QComboBox(self.authorsGroupBox) self.authorsComboBox = QtGui.QComboBox(self.authorsGroupBox)
self.authorsComboBox.setSizeAdjustPolicy( self.authorsComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.authorsComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.authorsComboBox.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.authorsComboBox.setEditable(True) self.authorsComboBox.setEditable(True)
self.authorsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.authorsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
self.authorsComboBox.setObjectName(u'authorsComboBox') self.authorsComboBox.setObjectName(u'authorsComboBox')
@ -155,8 +155,8 @@ class Ui_EditSongDialog(object):
self.topicsComboBox = QtGui.QComboBox(self.topicsGroupBox) self.topicsComboBox = QtGui.QComboBox(self.topicsGroupBox)
self.topicsComboBox.setSizeAdjustPolicy( self.topicsComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.topicsComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.topicsComboBox.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.topicsComboBox.setEditable(True) self.topicsComboBox.setEditable(True)
self.topicsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.topicsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
self.topicsComboBox.setObjectName(u'topicsComboBox') self.topicsComboBox.setObjectName(u'topicsComboBox')
@ -184,8 +184,10 @@ class Ui_EditSongDialog(object):
self.songBookNameLabel = QtGui.QLabel(self.songBookGroupBox) self.songBookNameLabel = QtGui.QLabel(self.songBookGroupBox)
self.songBookNameLabel.setObjectName(u'songBookNameLabel') self.songBookNameLabel.setObjectName(u'songBookNameLabel')
self.songBookComboBox = QtGui.QComboBox(self.songBookGroupBox) self.songBookComboBox = QtGui.QComboBox(self.songBookGroupBox)
self.songBookComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.songBookComboBox.setSizeAdjustPolicy(
QtGui.QSizePolicy.Fixed) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.songBookComboBox.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.songBookComboBox.setEditable(True) self.songBookComboBox.setEditable(True)
self.songBookComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.songBookComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
self.songBookComboBox.setObjectName(u'songBookComboBox') self.songBookComboBox.setObjectName(u'songBookComboBox')
@ -216,8 +218,8 @@ class Ui_EditSongDialog(object):
self.themeComboBox = QtGui.QComboBox(self.themeGroupBox) self.themeComboBox = QtGui.QComboBox(self.themeGroupBox)
self.themeComboBox.setSizeAdjustPolicy( self.themeComboBox.setSizeAdjustPolicy(
QtGui.QComboBox.AdjustToMinimumContentsLength) QtGui.QComboBox.AdjustToMinimumContentsLength)
self.themeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, self.themeComboBox.setSizePolicy(
QtGui.QSizePolicy.Fixed) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
self.themeComboBox.setEditable(True) self.themeComboBox.setEditable(True)
self.themeComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.themeComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
self.themeComboBox.setObjectName(u'themeComboBox') self.themeComboBox.setObjectName(u'themeComboBox')

View File

@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, translate from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.forms import EditVerseForm from openlp.plugins.songs.forms import EditVerseForm
from openlp.plugins.songs.lib import SongXMLBuilder, SongXMLParser, VerseType from openlp.plugins.songs.lib import SongXML, VerseType
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
from editsongdialog import Ui_EditSongDialog from editsongdialog import Ui_EditSongDialog
@ -263,8 +263,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if isinstance(self.song.lyrics, buffer): if isinstance(self.song.lyrics, buffer):
self.song.lyrics = unicode(self.song.lyrics) self.song.lyrics = unicode(self.song.lyrics)
if self.song.lyrics.startswith(u'<?xml version='): if self.song.lyrics.startswith(u'<?xml version='):
songXML = SongXMLParser(self.song.lyrics) songXML = SongXML()
verseList = songXML.get_verses() verseList = songXML.get_verses(self.song.lyrics)
for count, verse in enumerate(verseList): for count, verse in enumerate(verseList):
self.verseListWidget.setRowCount( self.verseListWidget.setRowCount(
self.verseListWidget.rowCount() + 1) self.verseListWidget.rowCount() + 1)
@ -731,7 +731,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def processLyrics(self): def processLyrics(self):
log.debug(u'processLyrics') log.debug(u'processLyrics')
try: try:
sxml = SongXMLBuilder() sxml = SongXML()
text = u'' text = u''
multiple = [] multiple = []
for i in range(0, self.verseListWidget.rowCount()): for i in range(0, self.verseListWidget.rowCount()):

View File

@ -73,12 +73,12 @@ class SongImportForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.openLP1BrowseButton, QtCore.QObject.connect(self.openLP1BrowseButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onOpenLP1BrowseButtonClicked) self.onOpenLP1BrowseButtonClicked)
#QtCore.QObject.connect(self.openLyricsAddButton, QtCore.QObject.connect(self.openLyricsAddButton,
# QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
# self.onOpenLyricsAddButtonClicked) self.onOpenLyricsAddButtonClicked)
#QtCore.QObject.connect(self.openLyricsRemoveButton, QtCore.QObject.connect(self.openLyricsRemoveButton,
# QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
# self.onOpenLyricsRemoveButtonClicked) self.onOpenLyricsRemoveButtonClicked)
QtCore.QObject.connect(self.openSongAddButton, QtCore.QObject.connect(self.openSongAddButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onOpenSongAddButtonClicked) self.onOpenSongAddButtonClicked)
@ -167,16 +167,15 @@ class SongImportForm(QtGui.QWizard, Ui_SongImportWizard):
self.openLP1BrowseButton.setFocus() self.openLP1BrowseButton.setFocus()
return False return False
elif source_format == SongFormat.OpenLyrics: elif source_format == SongFormat.OpenLyrics:
# if self.openLyricsFileListWidget.count() == 0: if self.openLyricsFileListWidget.count() == 0:
# QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
# translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
# 'No OpenLyrics Files Selected'), 'No OpenLyrics Files Selected'),
# translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
# 'You need to add at least one OpenLyrics ' 'You need to add at least one OpenLyrics '
# 'song file to import from.')) 'song file to import from.'))
# self.openLyricsAddButton.setFocus() self.openLyricsAddButton.setFocus()
# return False return False
return False
elif source_format == SongFormat.OpenSong: elif source_format == SongFormat.OpenSong:
if self.openSongFileListWidget.count() == 0: if self.openSongFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
@ -337,15 +336,15 @@ class SongImportForm(QtGui.QWizard, Ui_SongImportWizard):
'openlp.org v1.x Databases') 'openlp.org v1.x Databases')
) )
#def onOpenLyricsAddButtonClicked(self): def onOpenLyricsAddButtonClicked(self):
# self.getFiles( self.getFiles(
# translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
# 'Select OpenLyrics Files'), 'Select OpenLyrics Files'),
# self.openLyricsFileListWidget self.openLyricsFileListWidget
# ) )
#def onOpenLyricsRemoveButtonClicked(self): def onOpenLyricsRemoveButtonClicked(self):
# self.removeSelectedItems(self.openLyricsFileListWidget) self.removeSelectedItems(self.openLyricsFileListWidget)
def onOpenSongAddButtonClicked(self): def onOpenSongAddButtonClicked(self):
self.getFiles( self.getFiles(
@ -435,7 +434,7 @@ class SongImportForm(QtGui.QWizard, Ui_SongImportWizard):
self.formatComboBox.setCurrentIndex(0) self.formatComboBox.setCurrentIndex(0)
self.openLP2FilenameEdit.setText(u'') self.openLP2FilenameEdit.setText(u'')
self.openLP1FilenameEdit.setText(u'') self.openLP1FilenameEdit.setText(u'')
#self.openLyricsFileListWidget.clear() self.openLyricsFileListWidget.clear()
self.openSongFileListWidget.clear() self.openSongFileListWidget.clear()
self.wordsOfWorshipFileListWidget.clear() self.wordsOfWorshipFileListWidget.clear()
self.ccliFileListWidget.clear() self.ccliFileListWidget.clear()

View File

@ -39,7 +39,7 @@ class Ui_SongImportWizard(object):
QtGui.QWizard.IndependentPages | QtGui.QWizard.IndependentPages |
QtGui.QWizard.NoBackButtonOnStartPage | QtGui.QWizard.NoBackButtonOnStartPage |
QtGui.QWizard.NoBackButtonOnLastPage) QtGui.QWizard.NoBackButtonOnLastPage)
# Welcome Page # Welcome Page
self.welcomePage = QtGui.QWizardPage() self.welcomePage = QtGui.QWizardPage()
self.welcomePage.setPixmap(QtGui.QWizard.WatermarkPixmap, self.welcomePage.setPixmap(QtGui.QWizard.WatermarkPixmap,
QtGui.QPixmap(u':/wizards/wizard_importsong.bmp')) QtGui.QPixmap(u':/wizards/wizard_importsong.bmp'))
@ -81,9 +81,6 @@ class Ui_SongImportWizard(object):
self.addSingleFileSelectItem(u'openLP1', None, True) self.addSingleFileSelectItem(u'openLP1', None, True)
# OpenLyrics # OpenLyrics
self.addMultiFileSelectItem(u'openLyrics', u'OpenLyrics', True) self.addMultiFileSelectItem(u'openLyrics', u'OpenLyrics', True)
# set OpenLyrics to disabled by default
self.openLyricsDisabledWidget.setVisible(True)
self.openLyricsImportWidget.setVisible(False)
# Open Song # Open Song
self.addMultiFileSelectItem(u'openSong', u'OpenSong') self.addMultiFileSelectItem(u'openSong', u'OpenSong')
# Words of Worship # Words of Worship
@ -177,10 +174,10 @@ class Ui_SongImportWizard(object):
'importer has been disabled due to a missing Python module. If ' 'importer has been disabled due to a missing Python module. If '
'you want to use this importer, you will need to install the ' 'you want to use this importer, you will need to install the '
'"python-sqlite" module.')) '"python-sqlite" module.'))
#self.openLyricsAddButton.setText( self.openLyricsAddButton.setText(
# translate('SongsPlugin.ImportWizardForm', 'Add Files...')) translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
#self.openLyricsRemoveButton.setText( self.openLyricsRemoveButton.setText(
# translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
self.openLyricsDisabledLabel.setText( self.openLyricsDisabledLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'The OpenLyrics ' translate('SongsPlugin.ImportWizardForm', 'The OpenLyrics '
'importer has not yet been developed, but as you can see, we are ' 'importer has not yet been developed, but as you can see, we are '

View File

@ -36,8 +36,6 @@ class Ui_SongMaintenanceDialog(object):
self.dialogLayout = QtGui.QGridLayout(songMaintenanceDialog) self.dialogLayout = QtGui.QGridLayout(songMaintenanceDialog)
self.dialogLayout.setObjectName(u'dialogLayout') self.dialogLayout.setObjectName(u'dialogLayout')
self.typeListWidget = QtGui.QListWidget(songMaintenanceDialog) self.typeListWidget = QtGui.QListWidget(songMaintenanceDialog)
# Caution: fixed widget width
self.typeListWidget.setFixedWidth(172)
self.typeListWidget.setIconSize(QtCore.QSize(32, 32)) self.typeListWidget.setIconSize(QtCore.QSize(32, 32))
self.typeListWidget.setUniformItemSizes(True) self.typeListWidget.setUniformItemSizes(True)
self.typeListWidget.setObjectName(u'typeListWidget') self.typeListWidget.setObjectName(u'typeListWidget')
@ -147,12 +145,12 @@ class Ui_SongMaintenanceDialog(object):
def retranslateUi(self, songMaintenanceDialog): def retranslateUi(self, songMaintenanceDialog):
songMaintenanceDialog.setWindowTitle( songMaintenanceDialog.setWindowTitle(
translate('SongsPlugin.SongMaintenanceForm', 'Song Maintenance')) translate('SongsPlugin.SongMaintenanceForm', 'Song Maintenance'))
self.listItemAuthors.setText( authorsString = translate('SongsPlugin.SongMaintenanceForm', 'Authors')
translate('SongsPlugin.SongMaintenanceForm', 'Authors')) topicsString = translate('SongsPlugin.SongMaintenanceForm', 'Topics')
self.listItemTopics.setText( booksString = translate('SongsPlugin.SongMaintenanceForm', 'Song Books')
translate('SongsPlugin.SongMaintenanceForm', 'Topics')) self.listItemAuthors.setText(authorsString)
self.listItemBooks.setText( self.listItemTopics.setText(topicsString)
translate('SongsPlugin.SongMaintenanceForm', 'Song Books')) self.listItemBooks.setText(booksString)
self.authorsAddButton.setText( self.authorsAddButton.setText(
translate('SongsPlugin.SongMaintenanceForm', '&Add')) translate('SongsPlugin.SongMaintenanceForm', '&Add'))
self.authorsEditButton.setText( self.authorsEditButton.setText(
@ -171,3 +169,8 @@ class Ui_SongMaintenanceDialog(object):
translate('SongsPlugin.SongMaintenanceForm', '&Edit')) translate('SongsPlugin.SongMaintenanceForm', '&Edit'))
self.booksDeleteButton.setText( self.booksDeleteButton.setText(
translate('SongsPlugin.SongMaintenanceForm', '&Delete')) translate('SongsPlugin.SongMaintenanceForm', '&Delete'))
typeListWidth = max(self.fontMetrics().width(authorsString),
self.fontMetrics().width(topicsString),
self.fontMetrics().width(booksString))
self.typeListWidget.setFixedWidth(typeListWidth +
self.typeListWidget.iconSize().width() + 32)

View File

@ -175,6 +175,6 @@ def retrieve_windows_encoding(recommendation=None):
return None return None
return filter(lambda item: item[1] == choice[0], encodings)[0][0] return filter(lambda item: item[1] == choice[0], encodings)[0][0]
from xml import LyricsXML, SongXMLBuilder, SongXMLParser, OpenLyricsParser from xml import OpenLyrics, SongXML
from songstab import SongsTab from songstab import SongsTab
from mediaitem import SongMediaItem from mediaitem import SongMediaItem

View File

@ -26,6 +26,7 @@
from opensongimport import OpenSongImport from opensongimport import OpenSongImport
from olpimport import OpenLPSongImport from olpimport import OpenLPSongImport
from openlyricsimport import OpenLyricsImport
from wowimport import WowImport from wowimport import WowImport
from cclifileimport import CCLIFileImport from cclifileimport import CCLIFileImport
from ewimport import EasyWorshipSongImport from ewimport import EasyWorshipSongImport
@ -77,8 +78,10 @@ class SongFormat(object):
""" """
if format == SongFormat.OpenLP2: if format == SongFormat.OpenLP2:
return OpenLPSongImport return OpenLPSongImport
if format == SongFormat.OpenLP1: elif format == SongFormat.OpenLP1:
return OpenLP1SongImport return OpenLP1SongImport
elif format == SongFormat.OpenLyrics:
return OpenLyricsImport
elif format == SongFormat.OpenSong: elif format == SongFormat.OpenSong:
return OpenSongImport return OpenSongImport
elif format == SongFormat.SongsOfFellowship: elif format == SongFormat.SongsOfFellowship:
@ -93,7 +96,6 @@ class SongFormat(object):
return EasyWorshipSongImport return EasyWorshipSongImport
elif format == SongFormat.SongBeamer: elif format == SongFormat.SongBeamer:
return SongBeamerImport return SongBeamerImport
# else:
return None return None
@staticmethod @staticmethod

View File

@ -35,7 +35,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
ItemCapabilities, translate, check_item_selected ItemCapabilities, translate, check_item_selected
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm SongImportForm
from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser from openlp.plugins.songs.lib import OpenLyrics, SongXML
from openlp.plugins.songs.lib.db import Author, Song from openlp.plugins.songs.lib.db import Author, Song
from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.searchedit import SearchEdit
@ -58,7 +58,7 @@ class SongMediaItem(MediaManagerItem):
self.ListViewWithDnD_class = SongListView self.ListViewWithDnD_class = SongListView
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.edit_song_form = EditSongForm(self, self.parent.manager) self.edit_song_form = EditSongForm(self, self.parent.manager)
self.openLyrics = OpenLyricsParser(self.parent.manager) self.openLyrics = OpenLyrics(self.parent.manager)
self.singleServiceItem = False self.singleServiceItem = False
self.song_maintenance_form = SongMaintenanceForm( self.song_maintenance_form = SongMaintenanceForm(
self.parent.manager, self) self.parent.manager, self)
@ -153,7 +153,11 @@ class SongMediaItem(MediaManagerItem):
(3, u':/songs/song_search_lyrics.png', (3, u':/songs/song_search_lyrics.png',
translate('SongsPlugin.MediaItem', 'Lyrics')), translate('SongsPlugin.MediaItem', 'Lyrics')),
(4, u':/songs/song_search_author.png', (4, u':/songs/song_search_author.png',
translate('SongsPlugin.MediaItem', 'Authors'))]) translate('SongsPlugin.MediaItem', 'Authors')),
(5, u':/slides/slide_theme.png',
translate('SongsPlugin.MediaItem', 'Themes'))
])
self.configUpdated() self.configUpdated()
def onSearchTextButtonClick(self): def onSearchTextButtonClick(self):
@ -187,6 +191,12 @@ class SongMediaItem(MediaManagerItem):
Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.like(u'%' + search_keywords + u'%'),
Author.display_name.asc()) Author.display_name.asc())
self.displayResultsAuthor(search_results) self.displayResultsAuthor(search_results)
elif search_type == 5:
log.debug(u'Theme Search')
search_results = self.parent.manager.get_all_objects(Song,
Song.theme_name == search_keywords,
Song.search_lyrics.asc())
self.displayResultsSong(search_results)
def onSongListLoad(self): def onSongListLoad(self):
""" """
@ -312,15 +322,14 @@ class SongMediaItem(MediaManagerItem):
translate('SongsPlugin.MediaItem', translate('SongsPlugin.MediaItem',
'You must select an item to delete.')): 'You must select an item to delete.')):
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
ans = QtGui.QMessageBox.question(self, if QtGui.QMessageBox.question(self,
translate('SongsPlugin.MediaItem', 'Delete Song(s)?'), translate('SongsPlugin.MediaItem', 'Delete Song(s)?'),
translate('SongsPlugin.MediaItem', translate('SongsPlugin.MediaItem',
'Are you sure you want to delete the %n selected song(s)?', '', 'Are you sure you want to delete the %n selected song(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)), QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok| QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok |
QtGui.QMessageBox.Cancel), QtGui.QMessageBox.Cancel),
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok) == QtGui.QMessageBox.Cancel:
if ans == QtGui.QMessageBox.Cancel:
return return
for item in items: for item in items:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
@ -352,8 +361,7 @@ class SongMediaItem(MediaManagerItem):
service_item.theme = song.theme_name service_item.theme = song.theme_name
service_item.edit_id = item_id service_item.edit_id = item_id
if song.lyrics.startswith(u'<?xml version='): if song.lyrics.startswith(u'<?xml version='):
songXML = SongXMLParser(song.lyrics) verseList = SongXML().get_verses(song.lyrics)
verseList = songXML.get_verses()
# no verse list or only 1 space (in error) # no verse list or only 1 space (in error)
if not song.verse_order or not song.verse_order.strip(): if not song.verse_order or not song.verse_order.strip():
for verse in verseList: for verse in verseList:
@ -394,8 +402,8 @@ class SongMediaItem(MediaManagerItem):
service_item.audit = [ service_item.audit = [
song.title, author_audit, song.copyright, unicode(song.ccli_number) song.title, author_audit, song.copyright, unicode(song.ccli_number)
] ]
service_item.data_string = {u'title':song.search_title, service_item.data_string = {u'title': song.search_title,
u'authors':author_list} u'authors': author_list}
service_item.xml_version = self.openLyrics.song_to_xml(song) service_item.xml_version = self.openLyrics.song_to_xml(song)
return True return True
@ -406,8 +414,8 @@ class SongMediaItem(MediaManagerItem):
log.debug(u'serviceLoad') log.debug(u'serviceLoad')
if item.data_string: if item.data_string:
search_results = self.parent.manager.get_all_objects(Song, search_results = self.parent.manager.get_all_objects(Song,
Song.search_title == Song.search_title == re.compile(r'\W+', re.UNICODE).sub(u' ',
item.data_string[u'title'].split(u'@')[0].lower() , item.data_string[u'title'].split(u'@')[0].lower()).strip(),
Song.search_title.asc()) Song.search_title.asc())
author_list = item.data_string[u'authors'].split(u', ') author_list = item.data_string[u'authors'].split(u', ')
# The service item always has an author (at least it has u'' as # The service item always has an author (at least it has u'' as
@ -416,7 +424,6 @@ class SongMediaItem(MediaManagerItem):
if u'' in author_list: if u'' in author_list:
author_list.remove(u'') author_list.remove(u'')
editId = 0 editId = 0
uuid = item._uuid
add_song = True add_song = True
if search_results: if search_results:
for song in search_results: for song in search_results:
@ -443,7 +450,7 @@ class SongMediaItem(MediaManagerItem):
# Update service with correct song id. # Update service with correct song id.
if editId != 0: if editId != 0:
Receiver.send_message(u'service_item_update', Receiver.send_message(u'service_item_update',
u'%s:%s' %(editId, uuid)) u'%s:%s' % (editId, item._uuid))
def collateSongTitles(self, song_1, song_2): def collateSongTitles(self, song_1, song_2):
""" """

View File

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, 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 #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`openlyricsimport` module provides the functionality for importing
songs which are saved as OpenLyrics files.
"""
import logging
import os
from lxml import etree
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib import OpenLyrics
log = logging.getLogger(__name__)
class OpenLyricsImport(SongImport):
"""
This provides the Openlyrics import.
"""
def __init__(self, master_manager, **kwargs):
"""
Initialise the import.
"""
log.debug(u'initialise OpenLyricsImport')
SongImport.__init__(self, master_manager)
self.master_manager = master_manager
self.openLyrics = OpenLyrics(master_manager)
if kwargs.has_key(u'filename'):
self.import_source = kwargs[u'filename']
if kwargs.has_key(u'filenames'):
self.import_source = kwargs[u'filenames']
def do_import(self):
"""
Imports the songs.
"""
self.import_wizard.importProgressBar.setMaximum(len(self.import_source))
for file_path in self.import_source:
if self.stop_import_flag:
return False
self.import_wizard.incrementProgressBar(unicode(translate(
'SongsPlugin.OpenLyricsImport', 'Importing %s...')) %
os.path.basename(file_path))
parser = etree.XMLParser(remove_blank_text=True)
file = etree.parse(file_path, parser)
xml = unicode(etree.tostring(file))
if self.openLyrics.xml_to_song(xml) == 0:
log.debug(u'File could not be imported: %s' % file_path)
# Importing this song failed! For now we stop import.
return False
return True

View File

@ -31,7 +31,7 @@ from PyQt4 import QtCore
from openlp.core.lib import Receiver, translate from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.xml import SongXMLBuilder from openlp.plugins.songs.lib.xml import SongXML
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -270,7 +270,7 @@ class SongImport(QtCore.QObject):
song.song_number = self.song_number song.song_number = self.song_number
song.search_lyrics = u'' song.search_lyrics = u''
verses_changed_to_other = {} verses_changed_to_other = {}
sxml = SongXMLBuilder() sxml = SongXML()
other_count = 1 other_count = 1
for (versetag, versetext) in self.verses: for (versetag, versetext) in self.verses:
if versetag[0] == u'C': if versetag[0] == u'C':

View File

@ -24,55 +24,73 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`xml` module provides the XML functionality for songs The :mod:`xml` module provides the XML functionality.
The basic XML is of the format:: The basic XML for storing the lyrics in the song database is of the format::
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<song version="1.0"> <song version="1.0">
<lyrics language="en"> <lyrics>
<verse type="chorus" label="1"> <verse type="chorus" label="1">
<![CDATA[ ... ]]> <![CDATA[ ... ]]>
</verse> </verse>
</lyrics> </lyrics>
</song> </song>
The XML of `OpenLyrics <http://openlyrics.info/>`_ songs is of the format::
<song xmlns="http://openlyrics.info/namespace/2009/song"
version="0.7"
createdIn="OpenLP 1.9.0"
modifiedIn="ChangingSong 0.0.1"
modifiedDate="2010-01-28T13:15:30+01:00">
<properties>
<titles>
<title>Amazing Grace</title>
</titles>
</properties>
<lyrics>
<verse name="v1">
<lines>
<line>Amazing grace how sweet the sound</line>
</lines>
</verse>
</lyrics>
</song>
""" """
import logging import logging
import re import re
from lxml import etree, objectify from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.db import Author, Song from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class SongXMLBuilder(object): class SongXML(object):
""" """
This class builds the XML used to describe songs. This class builds and parses the XML used to describe songs.
""" """
log.info(u'SongXMLBuilder Loaded') log.info(u'SongXML Loaded')
def __init__(self, song_language=None): def __init__(self):
""" """
Set up the song builder. Set up the default variables.
``song_language``
The language used in this song
""" """
lang = u'en'
if song_language:
lang = song_language
self.song_xml = objectify.fromstring(u'<song version="1.0" />') self.song_xml = objectify.fromstring(u'<song version="1.0" />')
self.lyrics = etree.SubElement(self.song_xml, u'lyrics', language=lang) self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
def add_verse_to_lyrics(self, type, number, content): def add_verse_to_lyrics(self, type, number, content):
""" """
Add a verse to the ``<lyrics>`` tag. Add a verse to the *<lyrics>* tag.
``type`` ``type``
A string denoting the type of verse. Possible values are "Chorus", A string denoting the type of verse. Possible values are "V",
"Verse", "Bridge", and "Custom". "C", "B", "P", "I", "E" and "O".
``number`` ``number``
An integer denoting the number of the item, for example: verse 1. An integer denoting the number of the item, for example: verse 1.
@ -80,18 +98,11 @@ class SongXMLBuilder(object):
``content`` ``content``
The actual text of the verse to be stored. The actual text of the verse to be stored.
""" """
verse = etree.Element(u'verse', type = unicode(type), verse = etree.Element(u'verse', type=unicode(type),
label = unicode(number)) label=unicode(number))
verse.text = etree.CDATA(content) verse.text = etree.CDATA(content)
self.lyrics.append(verse) self.lyrics.append(verse)
def dump_xml(self):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return etree.tostring(self.song_xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=True)
def extract_xml(self): def extract_xml(self):
""" """
Extract our newly created XML song. Extract our newly created XML song.
@ -99,16 +110,10 @@ class SongXMLBuilder(object):
return etree.tostring(self.song_xml, encoding=u'UTF-8', return etree.tostring(self.song_xml, encoding=u'UTF-8',
xml_declaration=True) xml_declaration=True)
def get_verses(self, xml):
class SongXMLParser(object):
"""
A class to read in and parse a song's XML.
"""
log.info(u'SongXMLParser Loaded')
def __init__(self, xml):
""" """
Set up our song XML parser. Iterates through the verses in the XML and returns a list of verses
and their attributes.
``xml`` ``xml``
The XML of the song to be parsed. The XML of the song to be parsed.
@ -120,12 +125,6 @@ class SongXMLParser(object):
self.song_xml = objectify.fromstring(xml) self.song_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception(u'Invalid xml %s', xml) log.exception(u'Invalid xml %s', xml)
def get_verses(self):
"""
Iterates through the verses in the XML and returns a list of verses
and their attributes.
"""
xml_iter = self.song_xml.getiterator() xml_iter = self.song_xml.getiterator()
verse_list = [] verse_list = []
for element in xml_iter: for element in xml_iter:
@ -142,137 +141,109 @@ class SongXMLParser(object):
return etree.dump(self.song_xml) return etree.dump(self.song_xml)
class LyricsXML(object): class OpenLyrics(object):
""" """
This class represents the XML in the ``lyrics`` field of a song. This class represents the converter for OpenLyrics XML to/from a song.
"""
def __init__(self, song=None):
if song:
if song.lyrics.startswith(u'<?xml'):
self.parse(song.lyrics)
else:
self.extract(song.lyrics)
else:
self.languages = []
def parse(self, xml): As OpenLyrics has a rich set of different features, we cannot support them
""" all. The following features are supported by the :class:`OpenLyricsParser`::
Parse XML from the ``lyrics`` field in the database, and set the list
of verses from it.
``xml`` *<authors>*
The XML to parse. OpenLP does not support the attribute *type* and *lang*.
"""
try:
self.languages = []
song = objectify.fromstring(xml)
for lyrics in song.lyrics:
language = {
u'language': lyrics.attrib[u'language'],
u'verses': []
}
for verse in lyrics.verse:
language[u'verses'].append({
u'type': verse.attrib[u'type'],
u'label': verse.attrib[u'label'],
u'text': unicode(verse.text)
})
self.lyrics.append(language)
return True
except etree.XMLSyntaxError:
return False
def extract(self, text): *<chord>*
""" This property is not supported.
If the ``lyrics`` field in the database is not XML, this method is
called and used to construct the verse structure similar to the output
of the ``parse`` function.
``text`` *<comments>*
The text to pull verses out of. The *<comments>* property is fully supported. But comments in lyrics
""" are not supported.
text = text.replace('\r\n', '\n')
verses = text.split('\n\n')
self.languages = [{u'language': u'en', u'verses': []}]
counter = 0
for verse in verses:
counter = counter + 1
self.languages[0][u'verses'].append({
u'type': u'verse',
u'label': unicode(counter),
u'text': verse
})
return True
def add_verse(self, type, label, text): *<copyright>*
""" This property is fully supported.
Add a verse to the list of verses.
``type`` *<customVersion>*
The type of list, one of "verse", "chorus", "bridge", "pre-chorus", This property is not supported.
"intro", "outtro".
``label`` *<key>*
The number associated with this verse, like 1 or 2. This property is not supported.
``text`` *<keywords>*
The text of the verse. This property is not supported.
"""
self.verses.append({
u'type': type,
u'label': label,
u'text': text
})
def export(self): *<lines>*
""" The attribute *part* is not supported.
Build up the XML for the verse structure.
"""
lyrics_output = u''
for language in self.languages:
verse_output = u''
for verse in language[u'verses']:
verse_output = verse_output + \
u'<verse type="%s" label="%s"><![CDATA[%s]]></verse>' % \
(verse[u'type'], verse[u'label'], verse[u'text'])
lyrics_output = lyrics_output + \
u'<lyrics language="%s">%s</lyrics>' % \
(language[u'language'], verse_output)
song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \
u'<song version="1.0">%s</song>' % lyrics_output
return song_output
*<publisher>*
This property is not supported.
class OpenLyricsParser(object): *<songbooks>*
""" As OpenLP does only support one songbook, we cannot consider more than
This class represents the converter for Song to/from OpenLyrics XML. one songbook.
*<tempo>*
This property is not supported.
*<themes>*
Topics, as they are called in OpenLP, are fully supported, whereby only
the topic text (e. g. Grace) is considered, but neither the *id* nor
*lang*.
*<transposition>*
This property is not supported.
*<variant>*
This property is not supported.
*<verse name="v1a" lang="he" translit="en">*
The attribute *translit* and *lang* are not supported.
*<verseOrder>*
OpenLP supports this property.
""" """
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self.manager = manager
def song_to_xml(self, song): def song_to_xml(self, song, pretty_print=False):
""" """
Convert the song to OpenLyrics Format Convert the song to OpenLyrics Format.
""" """
song_xml_parser = SongXMLParser(song.lyrics) sxml = SongXML()
verse_list = song_xml_parser.get_verses() verse_list = sxml.get_verses(song.lyrics)
song_xml = objectify.fromstring( song_xml = objectify.fromstring(
u'<song version="0.7" createdIn="OpenLP 2.0"/>') u'<song version="0.7" createdIn="OpenLP 2.0"/>')
properties = etree.SubElement(song_xml, u'properties') properties = etree.SubElement(song_xml, u'properties')
titles = etree.SubElement(properties, u'titles') titles = etree.SubElement(properties, u'titles')
self._add_text_to_element(u'title', titles, song.title) self._add_text_to_element(u'title', titles, song.title.strip())
if song.alternate_title: if song.alternate_title:
self._add_text_to_element(u'title', titles, song.alternate_title) self._add_text_to_element(
if song.theme_name: u'title', titles, song.alternate_title.strip())
themes = etree.SubElement(properties, u'themes') if song.comments:
self._add_text_to_element(u'theme', themes, song.theme_name) comments = etree.SubElement(properties, u'comments')
self._add_text_to_element(u'copyright', properties, song.copyright) self._add_text_to_element(u'comment', comments, song.comments)
self._add_text_to_element(u'verseOrder', properties, song.verse_order) if song.copyright:
self._add_text_to_element(u'copyright', properties, song.copyright)
if song.verse_order:
self._add_text_to_element(
u'verseOrder', properties, song.verse_order)
if song.ccli_number: if song.ccli_number:
self._add_text_to_element(u'ccliNo', properties, song.ccli_number) self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
authors = etree.SubElement(properties, u'authors') if song.authors:
for author in song.authors: authors = etree.SubElement(properties, u'authors')
self._add_text_to_element(u'author', authors, author.display_name) for author in song.authors:
self._add_text_to_element(
u'author', authors, author.display_name)
book = self.manager.get_object_filtered(
Book, Book.id == song.song_book_id)
if book is not None:
book = book.name
songbooks = etree.SubElement(properties, u'songbooks')
element = self._add_text_to_element(
u'songbook', songbooks, None, book)
element.set(u'entry', song.song_number)
if song.topics:
themes = etree.SubElement(properties, u'themes')
for topic in song.topics:
self._add_text_to_element(u'theme', themes, topic.name)
lyrics = etree.SubElement(song_xml, u'lyrics') lyrics = etree.SubElement(song_xml, u'lyrics')
for verse in verse_list: for verse in verse_list:
verse_tag = u'%s%s' % ( verse_tag = u'%s%s' % (
@ -282,78 +253,36 @@ class OpenLyricsParser(object):
element = self._add_text_to_element(u'lines', element) element = self._add_text_to_element(u'lines', element)
for line in unicode(verse[1]).split(u'\n'): for line in unicode(verse[1]).split(u'\n'):
self._add_text_to_element(u'line', element, line) self._add_text_to_element(u'line', element, line)
return self._extract_xml(song_xml) return self._extract_xml(song_xml, pretty_print)
def xml_to_song(self, xml): def xml_to_song(self, xml):
""" """
Create a Song from OpenLyrics format xml Create and save a song from OpenLyrics format xml to the database. Since
we also export XML from external sources (e. g. OpenLyrics import), we
cannot ensure, that it completely conforms to the OpenLyrics standard.
``xml``
The XML to parse (unicode).
""" """
# No xml get out of here # No xml get out of here.
if not xml: if not xml:
return 0 return 0
song = Song() song = Song()
if xml[:5] == u'<?xml': if xml[:5] == u'<?xml':
xml = xml[38:] xml = xml[38:]
# Remove chords from xml.
xml = re.compile(u'<chord name=".*?"/>').sub(u'', xml)
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)
properties = song_xml.properties properties = song_xml.properties
song.copyright = unicode(properties.copyright.text) self._process_copyright(properties, song)
if song.copyright == u'None': self._process_cclinumber(properties, song)
song.copyright = u'' self._process_titles(properties, song)
song.verse_order = unicode(properties.verseOrder.text) # The verse order is processed with the lyrics!
if song.verse_order == u'None': self._process_lyrics(properties, song_xml.lyrics, song)
song.verse_order = u'' self._process_comments(properties, song)
song.topics = [] self._process_authors(properties, song)
song.book = None self._process_songbooks(properties, song)
theme_name = None self._process_topics(properties, song)
try:
song.ccli_number = unicode(properties.ccliNo.text)
except:
song.ccli_number = u''
try:
theme_name = unicode(properties.themes.theme)
except:
pass
if theme_name:
song.theme_name = theme_name
else:
song.theme_name = u''
# Process Titles
for title in properties.titles.title:
if not song.title:
song.title = unicode(title.text)
song.search_title = unicode(song.title)
song.alternate_title = u''
else:
song.alternate_title = unicode(title.text)
song.search_title += u'@' + song.alternate_title
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
unicode(song.search_title)).lower()
# Process Lyrics
sxml = SongXMLBuilder()
search_text = u''
for lyrics in song_xml.lyrics:
for verse in song_xml.lyrics.verse:
text = u''
for line in verse.lines.line:
line = unicode(line)
if not text:
text = line
else:
text += u'\n' + line
type = VerseType.expand_string(verse.attrib[u'name'][0])
sxml.add_verse_to_lyrics(type, verse.attrib[u'name'][1], text)
search_text = search_text + text
song.search_lyrics = search_text.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
song.comments = u''
song.song_number = u''
# Process Authors
try:
for author in properties.authors.author:
self._process_author(author.text, song)
except:
# No Author in XML so ignore
pass
self.manager.save_object(song) self.manager.save_object(song)
return song.id return song.id
@ -367,33 +296,258 @@ class OpenLyricsParser(object):
parent.append(element) parent.append(element)
return element return element
def _extract_xml(self, xml, pretty_print):
"""
Extract our newly created XML song.
"""
return etree.tostring(xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=pretty_print)
def _get(self, element, attribute):
"""
This returns the element's attribute as unicode string.
``element``
The element.
``attribute``
The element's attribute (unicode).
"""
if element.get(attribute) is not None:
return unicode(element.get(attribute))
return u''
def _text(self, element):
"""
This returns the text of an element as unicode string.
``element``
The element.
"""
if element.text is not None:
return unicode(element.text)
return u''
def _process_authors(self, properties, song):
"""
Adds the authors specified in the XML to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
authors = []
try:
for author in properties.authors.author:
display_name = self._text(author)
if display_name:
authors.append(display_name)
except AttributeError:
pass
if not authors:
# Add "Author unknown" (can be translated).
authors.append((unicode(translate('SongsPlugin.XML',
'Author unknown'))))
for display_name in authors:
author = self.manager.get_object_filtered(Author,
Author.display_name == display_name)
if author is None:
# We need to create a new author, as the author does not exist.
author = Author.populate(display_name=display_name,
last_name=display_name.split(u' ')[-1],
first_name=u' '.join(display_name.split(u' ')[:-1]))
self.manager.save_object(author)
song.authors.append(author)
def _process_cclinumber(self, properties, song):
"""
Adds the CCLI number to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
song.ccli_number = self._text(properties.ccliNo)
except AttributeError:
song.ccli_number = u''
def _process_comments(self, properties, song):
"""
Joins the comments specified in the XML and add it to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
comments_list = []
for comment in properties.comments.comment:
commenttext = self._text(comment)
if commenttext:
comments_list.append(commenttext)
song.comments = u'\n'.join(comments_list)
except AttributeError:
song.comments = u''
def _process_copyright(self, properties, song):
"""
Adds the copyright to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
song.copyright = self._text(properties.copyright)
except AttributeError:
song.copyright = u''
def _process_lyrics(self, properties, lyrics, song):
"""
Processes the verses and search_lyrics for the song.
``properties``
The properties object (lxml.objectify.ObjectifiedElement).
``lyrics``
The lyrics object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
sxml = SongXML()
search_text = u''
temp_verse_order = []
for verse in lyrics.verse:
text = u''
for lines in verse.lines:
if text:
text += u'\n'
text += u'\n'.join([unicode(line) for line in lines.line])
verse_name = self._get(verse, u'name')
verse_type = unicode(VerseType.expand_string(verse_name[0]))[0]
verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name)
verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
# OpenLyrics allows e. g. "c", but we need "c1".
if not verse_number:
verse_number = u'1'
temp_verse_order.append((verse_type, verse_number, verse_part))
sxml.add_verse_to_lyrics(verse_type, verse_number, text)
search_text = search_text + text
song.search_lyrics = search_text.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
# Process verse order
try:
song.verse_order = self._text(properties.verseOrder)
except AttributeError:
# We have to process the temp_verse_order, as the verseOrder
# property is not present.
previous_type = u''
previous_number = u''
previous_part = u''
verse_order = []
# Currently we do not support different "parts"!
for name in temp_verse_order:
if name[0] == previous_type:
if name[1] != previous_number:
verse_order.append(u''.join((name[0], name[1])))
else:
verse_order.append(u''.join((name[0], name[1])))
previous_type = name[0]
previous_number = name[1]
previous_part = name[2]
song.verse_order = u' '.join(verse_order)
def _process_songbooks(self, properties, song):
"""
Adds the song book and song number specified in the XML to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
song.song_book_id = 0
song.song_number = u''
try:
for songbook in properties.songbooks.songbook:
bookname = self._get(songbook, u'name')
if bookname:
book = self.manager.get_object_filtered(Book,
Book.name == bookname)
if book is None:
# We need to create a book, because it does not exist.
book = Book.populate(name=bookname, publisher=u'')
self.manager.save_object(book)
song.song_book_id = book.id
try:
if self._get(songbook, u'entry'):
song.song_number = self._get(songbook, u'entry')
except AttributeError:
pass
# We does only support one song book, so take the first one.
break
except AttributeError:
pass
def _process_titles(self, properties, song):
"""
Processes the titles specified in the song's XML.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
for title in properties.titles.title:
if not song.title:
song.title = self._text(title)
song.search_title = unicode(song.title)
song.alternate_title = u''
else:
song.alternate_title = self._text(title)
song.search_title += u'@' + song.alternate_title
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
unicode(song.search_title)).lower()
def _process_topics(self, properties, song):
"""
Adds the topics to the song.
``properties``
The property object (lxml.objectify.ObjectifiedElement).
``song``
The song object.
"""
try:
for topictext in properties.themes.theme:
topictext = self._text(topictext)
if topictext:
topic = self.manager.get_object_filtered(Topic,
Topic.name == topictext)
if topic is None:
# We need to create a topic, because it does not exist.
topic = Topic.populate(name=topictext)
self.manager.save_object(topic)
song.topics.append(topic)
except AttributeError:
pass
def _dump_xml(self, xml): def _dump_xml(self, xml):
""" """
Debugging aid to dump XML so that we can see what we have. Debugging aid to dump XML so that we can see what we have.
""" """
return etree.tostring(xml, encoding=u'UTF-8', return etree.tostring(xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=True) xml_declaration=True, pretty_print=True)
def _extract_xml(self, xml):
"""
Extract our newly created XML song.
"""
return etree.tostring(xml, encoding=u'UTF-8',
xml_declaration=True)
def _process_author(self, name, song):
"""
Find or create an Author from display_name.
"""
name = unicode(name)
author = self.manager.get_object_filtered(Author,
Author.display_name == name)
if author:
# should only be one! so take the first
song.authors.append(author)
else:
# Need a new author
new_author = Author.populate(first_name=name.rsplit(u' ', 1)[0],
last_name=name.rsplit(u' ', 1)[1], display_name=name)
self.manager.save_object(new_author)
song.authors.append(new_author)

View File

@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXMLParser from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML
from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.db import init_schema, Song
from openlp.plugins.songs.lib.importer import SongFormat from openlp.plugins.songs.lib.importer import SongFormat
@ -153,7 +153,7 @@ class SongsPlugin(Plugin):
song.search_title = self.whitespace.sub(u' ', song.title.lower() + \ song.search_title = self.whitespace.sub(u' ', song.title.lower() + \
u' ' + song.alternate_title.lower()) u' ' + song.alternate_title.lower())
lyrics = u'' lyrics = u''
verses = SongXMLParser(song.lyrics).get_verses() verses = SongXML().get_verses(song.lyrics)
for verse in verses: for verse in verses:
lyrics = lyrics + self.whitespace.sub(u' ', verse[1]) + u' ' lyrics = lyrics + self.whitespace.sub(u' ', verse[1]) + u' '
song.search_lyrics = lyrics.lower() song.search_lyrics = lyrics.lower()

View File

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>displayTagEdit</class>
<widget class="QWidget" name="displayTagEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>717</width>
<height>554</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QGroupBox" name="editGroupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>320</y>
<width>691</width>
<height>181</height>
</rect>
</property>
<property name="title">
<string>Edit Selection</string>
</property>
<widget class="QPushButton" name="updatePushButton">
<property name="geometry">
<rect>
<x>600</x>
<y>140</y>
<width>73</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Update</string>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>571</width>
<height>114</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="descriptionLabel">
<property name="text">
<string>Description</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="descriptionLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="tagLabel">
<property name="text">
<string>Tag</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="tagLineEdit">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="maxLength">
<number>5</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="startTagLabel">
<property name="text">
<string>Start tag</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="startTagLineEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="endTagLabel">
<property name="text">
<string>End tag</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="endTagLineEdit"/>
</item>
</layout>
</widget>
</widget>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>540</x>
<y>510</y>
<width>162</width>
<height>26</height>
</rect>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QPushButton" name="deletePushButton">
<property name="geometry">
<rect>
<x>530</x>
<y>280</y>
<width>71</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
<widget class="QPushButton" name="addPushButton">
<property name="geometry">
<rect>
<x>610</x>
<y>280</y>
<width>71</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
<widget class="QTableWidget" name="tagTableWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>691</width>
<height>271</height>
</rect>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Description</string>
</property>
</column>
<column>
<property name="text">
<string>Key</string>
</property>
<property name="textAlignment">
<set>AlignHCenter|AlignVCenter|AlignCenter</set>
</property>
</column>
<column>
<property name="text">
<string>Start Tag</string>
</property>
</column>
<column>
<property name="text">
<string>End Tag</string>
</property>
</column>
</widget>
</widget>
<resources/>
<connections/>
</ui>