diff --git a/openlp.pyw b/openlp.pyw index f3455962d..017e12774 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -34,7 +34,7 @@ from subprocess import Popen, PIPE 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.ui.mainwindow import MainWindow 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']) return app_version - def notify(self, obj, evt): - #TODO needed for presentation exceptions - return QtGui.QApplication.notify(self, obj, evt) +# def notify(self, obj, evt): +# #TODO needed for presentation exceptions +# return QtGui.QApplication.notify(self, obj, evt) def run(self): """ Run the OpenLP application. """ 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.SIGNAL(u'openlp_process_events'), self.processEvents) QtCore.QObject.connect(Receiver.get_receiver(), @@ -182,7 +182,7 @@ class OpenLP(QtGui.QApplication): screens = ScreenList() # Decide how many screens we have and their size for screen in xrange(0, self.desktop().numScreens()): - size = self.desktop().screenGeometry(screen); + size = self.desktop().screenGeometry(screen) screens.add_screen({u'number': screen, u'size': size, u'primary': (self.desktop().primaryScreen() == screen)}) @@ -216,7 +216,7 @@ class OpenLP(QtGui.QApplication): def setNormalCursor(self): """ - Sets the Normal Cursor forthe Application + Sets the Normal Cursor for the Application """ self.restoreOverrideCursor() @@ -243,8 +243,7 @@ def main(): help='Set the Qt4 style (passed directly to Qt4).') # Set up logging log_path = AppLocation.get_directory(AppLocation.CacheDir) - if not os.path.exists(log_path): - os.makedirs(log_path) + check_directory_exists(log_path) filename = os.path.join(log_path, u'openlp.log') logfile = logging.FileHandler(filename, u'w') logfile.setFormatter(logging.Formatter( @@ -281,4 +280,4 @@ if __name__ == u'__main__': """ Instantiate and run the application. """ - main() + main() \ No newline at end of file diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index ebbe31597..7ad377817 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -35,52 +35,51 @@ from PyQt4 import QtCore, QtGui log = logging.getLogger(__name__) -# TODO make external and configurable in alpha 4 via a settings dialog -html_expands = [] +base_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'', - u'end tag': u'{/r}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Black', u'start tag': u'{b}', + u'end tag': u'{/r}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Black', u'start tag': u'{b}', u'start html': u'', - u'end tag': u'{/b}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Blue', u'start tag': u'{bl}', + u'end tag': u'{/b}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Blue', u'start tag': u'{bl}', u'start html': u'', - u'end tag': u'{/bl}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Yellow', u'start tag': u'{y}', + u'end tag': u'{/bl}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Yellow', u'start tag': u'{y}', u'start html': u'', - u'end tag': u'{/y}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Green', u'start tag': u'{g}', + u'end tag': u'{/y}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Green', u'start tag': u'{g}', u'start html': u'', - u'end tag': u'{/g}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Pink', u'start tag': u'{pk}', + u'end tag': u'{/g}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Pink', u'start tag': u'{pk}', u'start html': u'', - u'end tag': u'{/pk}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Orange', u'start tag': u'{o}', + u'end tag': u'{/pk}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Orange', u'start tag': u'{o}', u'start html': u'', - u'end tag': u'{/o}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Purple', u'start tag': u'{pp}', + u'end tag': u'{/o}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Purple', u'start tag': u'{pp}', u'start html': u'', - u'end tag': u'{/pp}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'White', u'start tag': u'{w}', + u'end tag': u'{/pp}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'White', u'start tag': u'{w}', u'start html': u'', - u'end tag': u'{/w}', u'end html': u'', u'protected': False}) -html_expands.append({u'desc': u'Superscript', u'start tag': u'{su}', + u'end tag': u'{/w}', u'end html': u'', u'protected': True}) +base_html_expands.append({u'desc': u'Superscript', u'start tag': u'{su}', u'start html': u'', u'end tag': u'{/su}', u'end html': u'', 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'', u'end tag': u'{/sb}', u'end html': u'', 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'

', u'end tag': u'{/p}', u'end html': u'

', 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'', u'end tag': u'{/st}', u'end html': u'', 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'', u'end tag': u'{/it}', u'end html': u'', 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'', u'end tag': u'{/u}', u'end html': u'', 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 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): """ @@ -293,7 +293,7 @@ def clean_tags(text): Remove Tags from text for display """ text = text.replace(u'
', 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'end tag'], u'') return text @@ -302,13 +302,25 @@ def expand_tags(text): """ 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'end tag'], tag[u'end html']) 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, \ BackgroundType, HorizontalType, VerticalType +from displaytags import DisplayTags from spelltextedit import SpellTextEdit from eventreceiver import Receiver from imagemanager import ImageManager diff --git a/openlp/core/lib/displaytags.py b/openlp/core/lib/displaytags.py new file mode 100644 index 000000000..dd7276a7d --- /dev/null +++ b/openlp/core/lib/displaytags.py @@ -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) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index c92960163..2d6bcce46 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -176,7 +176,8 @@ class MediaManagerItem(QtGui.QWidget): # break compatability), but it makes sense for the icon to # come before the tooltip (as you have to have an icon, but # 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): """ diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 8bc5fdb96..5a3fa41ce 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -175,6 +175,10 @@ class Plugin(QtCore.QObject): self.status = new_status QtCore.QSettings().setValue( 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): """ @@ -314,4 +318,4 @@ class Plugin(QtCore.QObject): """ Called to define all translatable texts of the plugin """ - pass \ No newline at end of file + pass diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 07279aea5..c69e1f15b 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -78,7 +78,7 @@ class SearchEdit(QtGui.QLineEdit): else: self.setStyleSheet(u'QLineEdit { padding-right: %spx; } ' % \ rightPadding) - msz = self.minimumSizeHint(); + msz = self.minimumSizeHint() self.setMinimumSize( max(msz.width(), self.clearButton.width() + (frameWidth * 2) + 2), diff --git a/openlp/core/lib/settingstab.py b/openlp/core/lib/settingstab.py index 5081d9769..297185127 100644 --- a/openlp/core/lib/settingstab.py +++ b/openlp/core/lib/settingstab.py @@ -114,6 +114,12 @@ class SettingsTab(QtGui.QWidget): """ pass + def cancel(self): + """ + Reset any settings + """ + pass + def postSetUp(self, postUpdate=False): """ Changes which need to be made after setup of application diff --git a/openlp/core/lib/spelltextedit.py b/openlp/core/lib/spelltextedit.py index ebd4046c0..30811e08b 100644 --- a/openlp/core/lib/spelltextedit.py +++ b/openlp/core/lib/spelltextedit.py @@ -36,7 +36,7 @@ except ImportError: # http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check from PyQt4 import QtCore, QtGui -from openlp.core.lib import html_expands, translate +from openlp.core.lib import translate, DisplayTags class SpellTextEdit(QtGui.QPlainTextEdit): """ @@ -88,7 +88,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit): popupMenu.insertMenu(popupMenu.actions()[0], spell_menu) tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit', 'Formatting Tags')) - for html in html_expands: + for html in DisplayTags.get_html_tags(): action = SpellAction( html[u'desc'], tagMenu) action.correct.connect(self.htmlTag) tagMenu.addAction(action) @@ -110,7 +110,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit): """ Replaces the selected text with word. """ - for html in html_expands: + for html in DisplayTags.get_html_tags(): if tag == html[u'desc']: cursor = self.textCursor() if self.textCursor().hasSelection(): @@ -158,4 +158,4 @@ class SpellAction(QtGui.QAction): def __init__(self, *args): QtGui.QAction.__init__(self, *args) self.triggered.connect(lambda x: self.correct.emit( - unicode(self.text()))) \ No newline at end of file + unicode(self.text()))) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 1e4a9854e..35b62ddda 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -598,4 +598,4 @@ class ThemeXML(object): self.font_footer_shadow_size) self.add_display(self.display_horizontal_align, self.display_vertical_align, - self.display_slide_transition) \ No newline at end of file + self.display_slide_transition) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 26c59ed0f..5a030e841 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -59,6 +59,7 @@ from splashscreen import SplashScreen from generaltab import GeneralTab from themestab import ThemesTab from advancedtab import AdvancedTab +from displaytagtab import DisplayTagTab from aboutform import AboutForm from pluginform import PluginForm from settingsform import SettingsForm diff --git a/openlp/core/ui/displaytagtab.py b/openlp/core/ui/displaytagtab.py new file mode 100644 index 000000000..3d6cd813d --- /dev/null +++ b/openlp/core/ui/displaytagtab.py @@ -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'', u'end tag': u'{/n}', + u'end html': u'', 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 diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index a08d71e27..adc2cb5fa 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -32,7 +32,8 @@ from PyQt4 import QtCore, QtGui, QtWebKit from PyQt4.phonon import Phonon from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \ - translate + build_icon, translate + from openlp.core.ui import HideMode log = logging.getLogger(__name__) @@ -102,6 +103,8 @@ class MainDisplay(DisplayWidget): self.isLive = live self.alertTab = None self.hideMode = None + mainIcon = build_icon(u':/icon/openlp-logo-16x16.png') + self.setWindowIcon(mainIcon) self.retranslateUi() self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') self.setWindowFlags(QtCore.Qt.FramelessWindowHint | diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 26e6cf06c..3162458bc 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -126,10 +126,8 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): return if status == 0: self.activePlugin.toggleStatus(PluginStatus.Active) - self.activePlugin.initialise() else: self.activePlugin.toggleStatus(PluginStatus.Inactive) - self.activePlugin.finalise() status_text = unicode( translate('OpenLP.PluginForm', '%s (Inactive)')) if self.activePlugin.status == PluginStatus.Active: diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index e0e9093d9..dc5edf599 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -28,7 +28,6 @@ import os import logging import cPickle import zipfile -from pprint import pformat log = logging.getLogger(__name__) @@ -253,7 +252,7 @@ class ServiceManager(QtGui.QWidget): self.parent.serviceSettingsSection + u'/service theme', QtCore.QVariant(u'')).toString()) 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.newAction = self.dndMenu.addAction( translate('OpenLP.ServiceManager', '&Add New Item')) @@ -988,8 +987,8 @@ class ServiceManager(QtGui.QWidget): u'expanded':expand}) self.repaintServiceList(len(self.serviceItems) + 1, 0) else: - self.serviceItems.insert(self.dropPosition, {u'service_item': item, - u'order': self.dropPosition, + self.serviceItems.insert(self.dropPosition, + {u'service_item': item, u'order': self.dropPosition, u'expanded':expand}) self.repaintServiceList(self.dropPosition, 0) # if rebuilding list make sure live is fixed. diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 537e99cc2..61c73961c 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -31,7 +31,7 @@ from openlp.core.lib import translate, build_icon class Ui_SettingsDialog(object): def setupUi(self, settingsDialog): settingsDialog.setObjectName(u'settingsDialog') - settingsDialog.resize(700, 300) + settingsDialog.resize(700, 500) settingsDialog.setWindowIcon( build_icon(u':/system/system_settings.png')) self.settingsLayout = QtGui.QVBoxLayout(settingsDialog) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 068057902..afea928b6 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -31,7 +31,7 @@ import logging from PyQt4 import QtGui 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 log = logging.getLogger(__name__) @@ -55,6 +55,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): # Advanced tab self.advancedTab = 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): """ @@ -68,9 +71,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): Add a tab to the form at a specific location """ 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( - location + 14, tab, tab.tabTitleVisible) + location + 15, tab, tab.tabTitleVisible) def removeTab(self, tab): """ @@ -93,6 +96,14 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): Receiver.send_message(u'config_updated') 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): """ Run any post-setup code for the tabs on the form diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index eaf54bdb9..0155c6a4c 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -291,9 +291,10 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): self.updateThemeAllowed = True self.themeNameLabel.setVisible(not edit) self.themeNameEdit.setVisible(not edit) + self.edit_mode = edit if edit: self.setWindowTitle(unicode(translate('OpenLP.ThemeWizard', - 'Edit Theme %s')) % self.theme.theme_name) + 'Edit Theme - %s')) % self.theme.theme_name) self.next() else: self.setWindowTitle(translate('OpenLP.ThemeWizard', 'New Theme')) @@ -581,7 +582,6 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): (QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) return - self.accepted = True saveFrom = None saveTo = None if self.theme.background_type == \ @@ -590,8 +590,12 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): os.path.split(unicode(self.theme.background_filename))[1] saveTo = os.path.join(self.path, self.theme.theme_name, filename) saveFrom = self.theme.background_filename - if self.thememanager.saveTheme(self.theme, saveFrom, saveTo): - return QtGui.QDialog.accept(self) + if not self.edit_mode and \ + 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): """ diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 0703402fd..99e5f8d5e 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -36,7 +36,7 @@ from openlp.core.ui import FileRenameForm, ThemeForm from openlp.core.theme import Theme from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \ build_icon, Receiver, SettingsManager, translate, check_item_selected, \ - BackgroundType, BackgroundGradientType + BackgroundType, BackgroundGradientType, check_directory_exists from openlp.core.utils import AppLocation, get_filesystem_encoding log = logging.getLogger(__name__) @@ -119,27 +119,38 @@ class ThemeManager(QtGui.QWidget): self.exportAction = self.menu.addAction( translate('OpenLP.ThemeManager', '&Export Theme')) self.exportAction.setIcon(build_icon(u':/general/general_export.png')) - #Signals + # Signals QtCore.QObject.connect(self.themeListWidget, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.changeGlobalFromScreen) QtCore.QObject.connect(Receiver.get_receiver(), 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.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.checkThemesExists(self.thumbPath) + check_directory_exists(self.thumbPath) self.themeForm.path = self.path self.oldBackgroundImage = None - self.editingDefault = False # 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.settingsSection + u'/global theme', QtCore.QVariant(u'')).toString()) def contextMenu(self, point): + """ + Build the Right Click Context menu and set state depending on + the type of theme. + """ item = self.themeListWidget.itemAt(point) if item is None: return @@ -220,7 +231,6 @@ class ThemeManager(QtGui.QWidget): editing form for the user to make their customisations. """ theme = ThemeXML() - self.saveThemeName = u'' self.themeForm.theme = theme self.themeForm.exec_() @@ -236,12 +246,15 @@ class ThemeManager(QtGui.QWidget): item = self.themeListWidget.currentItem() oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString()) self.fileRenameForm.fileNameEdit.setText(oldThemeName) - self.saveThemeName = oldThemeName if self.fileRenameForm.exec_(): - newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) - oldThemeData = self.getThemeData(oldThemeName) - self.deleteTheme(oldThemeName) - self.cloneThemeData(oldThemeData, newThemeName) + newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) + if self.checkIfThemeExists(newThemeName): + oldThemeData = self.getThemeData(oldThemeName) + self.deleteTheme(oldThemeName) + self.cloneThemeData(oldThemeData, newThemeName) + for plugin in self.parent.pluginManager.plugins: + if plugin.usesTheme(oldThemeName): + plugin.renameTheme(oldThemeName, newThemeName) def onCopyTheme(self): """ @@ -250,12 +263,12 @@ class ThemeManager(QtGui.QWidget): item = self.themeListWidget.currentItem() oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString()) self.fileRenameForm.fileNameEdit.setText(oldThemeName) - self.saveThemeName = u'' if self.fileRenameForm.exec_(True): - newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) - themeData = self.getThemeData(oldThemeName) - self.cloneThemeData(themeData, newThemeName) - self.loadThemes() + newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) + if self.checkIfThemeExists(newThemeName): + themeData = self.getThemeData(oldThemeName) + self.cloneThemeData(themeData, newThemeName) + self.loadThemes() def cloneThemeData(self, themeData, newThemeName): """ @@ -281,14 +294,10 @@ class ThemeManager(QtGui.QWidget): 'You must select a theme to edit.')): item = self.themeListWidget.currentItem() themeName = unicode(item.text()) - if themeName != unicode(item.data(QtCore.Qt.UserRole).toString()): - self.editingDefault = True theme = self.getThemeData( unicode(item.data(QtCore.Qt.UserRole).toString())) if theme.background_type == u'image': self.oldBackgroundImage = theme.background_filename - self.saveThemeName = unicode( - item.data(QtCore.Qt.UserRole).toString()) self.themeForm.theme = theme self.themeForm.exec_(True) @@ -449,20 +458,9 @@ class ThemeManager(QtGui.QWidget): unicode(themeName) + u'.xml') xml = get_text_file_string(xmlFile) if not xml: - return self.baseTheme() + return self._baseTheme() else: - 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) + return self._createThemeFromXml(xml, self.path) def unzipTheme(self, filename, dir): """ @@ -494,8 +492,7 @@ class ThemeManager(QtGui.QWidget): theme_dir = None if osfile.endswith(os.path.sep): theme_dir = os.path.join(dir, osfile) - if not os.path.exists(theme_dir): - os.mkdir(os.path.join(dir, osfile)) + check_directory_exists(theme_dir) else: fullpath = os.path.join(dir, osfile) names = osfile.split(os.path.sep) @@ -505,8 +502,7 @@ class ThemeManager(QtGui.QWidget): themename = names[0] if theme_dir is None: theme_dir = os.path.join(dir, names[0]) - if not os.path.exists(theme_dir): - os.mkdir(os.path.join(dir, names[0])) + check_directory_exists(theme_dir) if os.path.splitext(ucsfile)[1].lower() in [u'.xml']: xml_data = zip.read(file) try: @@ -522,19 +518,22 @@ class ThemeManager(QtGui.QWidget): outfile = open(fullpath, u'wb') outfile.write(zip.read(file)) if filexml: - theme = self.createThemeFromXml(filexml, self.path) + theme = self._createThemeFromXml(filexml, self.path) self.generateAndSaveImage(dir, themename, theme) else: - QtGui.QMessageBox.critical(self, - translate('OpenLP.ThemeManager', 'Error'), - translate('OpenLP.ThemeManager', - 'File is not a valid theme.')) + Receiver.send_message(u'openlp_error_message', { + u'title': translate('OpenLP.ThemeManager', + 'Validation Error'), + u'message':translate('OpenLP.ThemeManager', + 'File is not a valid theme.')}) log.exception(u'Theme file does not contain XML data %s' % filename) except (IOError, NameError): - QtGui.QMessageBox.critical(self, - translate('OpenLP.ThemeManager', 'Error'), - translate('OpenLP.ThemeManager', 'File is not a valid theme.')) + Receiver.send_message(u'openlp_error_message', { + u'title': translate('OpenLP.ThemeManager', + 'Validation Error'), + u'message':translate('OpenLP.ThemeManager', + 'File is not a valid theme.')}) log.exception(u'Importing theme from zip failed %s' % filename) finally: if zip: @@ -556,9 +555,161 @@ class ThemeManager(QtGui.QWidget): if tree.find(u'BackgroundType') is None: return xml_data 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.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 + # check for use in the system else where. + if testPlugin: + for plugin in self.parent.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. @@ -614,192 +765,4 @@ class ThemeManager(QtGui.QWidget): vAlignCorrection = 2 newtheme.display_horizontal_align = theme.HorizontalAlign newtheme.display_vertical_align = vAlignCorrection - 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 + return newtheme.extract_xml() \ No newline at end of file diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 1bf0a0038..f61a44584 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -249,7 +249,8 @@ class Ui_ThemeWizard(object): self.footerSizeSpinBox.setMaximum(999) self.footerSizeSpinBox.setValue(10) self.footerSizeSpinBox.setObjectName(u'FooterSizeSpinBox') - self.footerAreaLayout.addRow(self.footerSizeLabel, self.footerSizeSpinBox) + self.footerAreaLayout.addRow(self.footerSizeLabel, + self.footerSizeSpinBox) ThemeWizard.addPage(self.footerAreaPage) # Alignment Page self.alignmentPage = QtGui.QWizardPage() @@ -317,9 +318,11 @@ class Ui_ThemeWizard(object): self.areaPositionLayout.addWidget(self.mainPositionGroupBox) self.footerPositionGroupBox = QtGui.QGroupBox(self.areaPositionPage) self.footerPositionGroupBox.setObjectName(u'FooterPositionGroupBox') - self.footerPositionLayout = QtGui.QFormLayout(self.footerPositionGroupBox) + self.footerPositionLayout = QtGui.QFormLayout( + self.footerPositionGroupBox) self.footerPositionLayout.setObjectName(u'FooterPositionLayout') - self.footerPositionCheckBox = QtGui.QCheckBox(self.footerPositionGroupBox) + self.footerPositionCheckBox = QtGui.QCheckBox( + self.footerPositionGroupBox) self.footerPositionCheckBox.setObjectName(u'FooterPositionCheckBox') self.footerPositionLayout.addRow(self.footerPositionCheckBox) self.footerXLabel = QtGui.QLabel(self.footerPositionGroupBox) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index bd03b9171..1d6a566fd 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -177,8 +177,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): 'parameter to be replaced.\nDo you want to continue anyway?'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False + self.parameterEdit.setFocus() + return False # The ParameterEdit field is not empty, but we have not found '<>' # in the alert text. elif text.find(u'<>') == -1 and self.parameterEdit.text() and \ @@ -188,8 +188,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): ' contain \'<>\'.\nDo want to continue anyway?'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: - self.parameterEdit.setFocus() - return False + self.parameterEdit.setFocus() + return False text = text.replace(u'<>', unicode(self.parameterEdit.text())) self.parent.alertsmanager.displayAlert(text) return True diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 62068437a..9f4376da1 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -28,7 +28,7 @@ import logging import chardet import re -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore from sqlalchemy import Column, ForeignKey, or_, Table, types from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 6f26434de..d9210a275 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -230,7 +230,10 @@ class BGExtract(object): Receiver.send_message(u'openlp_process_events') footnotes = soup.findAll(u'sup', u'footnote') 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: ' ')] verses = BeautifulSoup(str(soup), markupMassage=cleanup) content = verses.find(u'div', u'result-text-style-normal') diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 15f171608..63c6954fb 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -26,7 +26,7 @@ import logging -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore from openlp.core.lib import Receiver, SettingsManager, translate from openlp.core.utils import AppLocation @@ -273,10 +273,10 @@ class BibleManager(object): Receiver.send_message(u'openlp_information_message', { u'title': translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'), - u'message': translate('BiblesPlugin.BibleManager', 'Your scripture ' - 'reference is either not supported by OpenLP or is invalid. ' - 'Please make sure your reference conforms to one of the ' - 'following patterns:\n\n' + u'message': translate('BiblesPlugin.BibleManager', + 'Your scripture reference is either not supported by OpenLP ' + 'or is invalid. Please make sure your reference conforms to ' + 'one of the following patterns:\n\n' 'Book Chapter\n' 'Book Chapter-Chapter\n' 'Book Chapter:Verse-Verse\n' diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index d1c092eec..066563bec 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -725,11 +725,11 @@ class BibleMediaItem(MediaManagerItem): if len(items) == 0: return False bible_text = u'' + old_item = None old_chapter = -1 raw_footer = [] raw_slides = [] raw_title = [] - first_item = True for item in items: bitem = self.listView.item(item.row()) book = self._decodeQtObject(bitem, 'book') @@ -770,9 +770,8 @@ class BibleMediaItem(MediaManagerItem): # We have to be 'Continuous'. else: bible_text = u'%s %s\u00a0%s\n' % (bible_text, verse_text, text) - if first_item: + if not old_item: start_item = item - first_item = False elif self.checkTitle(item, old_item): raw_title.append(self.formatTitle(start_item, old_item)) start_item = item diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 89e27e2fb..063a80d02 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -30,8 +30,8 @@ import os from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - context_menu_action, 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 log = logging.getLogger(__name__) @@ -88,8 +88,7 @@ class ImageMediaItem(MediaManagerItem): self.servicePath = os.path.join( AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') - if not os.path.exists(self.servicePath): - os.mkdir(self.servicePath) + check_directory_exists(self.servicePath) self.loadList(SettingsManager.load_list( self.settingsSection, self.settingsSection)) @@ -217,4 +216,4 @@ class ImageMediaItem(MediaManagerItem): 'the image file "%s" no longer exists.')) % filename}) def onPreviewClick(self): - MediaManagerItem.onPreviewClick(self) + MediaManagerItem.onPreviewClick(self) \ No newline at end of file diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index fd8c6c97d..cc75dfc2b 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ ItemCapabilities, SettingsManager, translate, check_item_selected, \ - context_menu_action, Receiver + Receiver log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index fef3bcba1..fc82600df 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -79,7 +79,12 @@ class PresentationTab(SettingsTab): for key in self.controllers: controller = self.controllers[key] checkbox = self.PresenterCheckboxes[controller.name] - checkbox.setText(controller.name) + if controller.available: + checkbox.setText(controller.name) + else: + checkbox.setText( + unicode(translate('PresentationPlugin.PresentationTab', + '%s (unvailable)')) % controller.name) self.AdvancedGroupBox.setTitle( translate('PresentationPlugin.PresentationTab', 'Advanced')) @@ -93,11 +98,10 @@ class PresentationTab(SettingsTab): """ for key in self.controllers: controller = self.controllers[key] - if controller.available: - checkbox = self.PresenterCheckboxes[controller.name] - checkbox.setChecked(QtCore.QSettings().value( - self.settingsSection + u'/' + controller.name, - QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0]) + checkbox = self.PresenterCheckboxes[controller.name] + checkbox.setChecked(QtCore.QSettings().value( + self.settingsSection + u'/' + controller.name, + QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0]) self.OverrideAppCheckBox.setChecked(QtCore.QSettings().value( self.settingsSection + u'/override app', QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]) @@ -109,16 +113,18 @@ class PresentationTab(SettingsTab): changed = False for key in self.controllers: controller = self.controllers[key] - checkbox = self.PresenterCheckboxes[controller.name] - setting_key = self.settingsSection + u'/' + controller.name - if QtCore.QSettings().value(setting_key) != checkbox.checkState(): - changed = True - QtCore.QSettings().setValue(setting_key, - QtCore.QVariant(checkbox.checkState())) - if checkbox.checkState() == QtCore.Qt.Checked: - controller.start_process() - else: - controller.kill() + if controller.available: + checkbox = self.PresenterCheckboxes[controller.name] + setting_key = self.settingsSection + u'/' + controller.name + if QtCore.QSettings().value(setting_key) != \ + checkbox.checkState(): + changed = True + QtCore.QSettings().setValue(setting_key, + QtCore.QVariant(checkbox.checkState())) + if checkbox.isChecked(): + controller.start_process() + else: + controller.kill() setting_key = self.settingsSection + u'/override app' if QtCore.QSettings().value(setting_key) != \ self.OverrideAppCheckBox.checkState(): diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 5500cada8..050d88d63 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -153,7 +153,11 @@ class SongMediaItem(MediaManagerItem): (3, u':/songs/song_search_lyrics.png', translate('SongsPlugin.MediaItem', 'Lyrics')), (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() def onSearchTextButtonClick(self): @@ -187,6 +191,12 @@ class SongMediaItem(MediaManagerItem): Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.asc()) 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): """ diff --git a/resources/forms/displaytabeditdialog.ui b/resources/forms/displaytabeditdialog.ui new file mode 100644 index 000000000..3c748594f --- /dev/null +++ b/resources/forms/displaytabeditdialog.ui @@ -0,0 +1,209 @@ + + + displayTagEdit + + + + 0 + 0 + 717 + 554 + + + + Form + + + + + 10 + 320 + 691 + 181 + + + + Edit Selection + + + + + 600 + 140 + 73 + 26 + + + + Update + + + + + + 20 + 50 + 571 + 114 + + + + + + + Description + + + Qt::AlignCenter + + + + + + + + + + Tag + + + Qt::AlignCenter + + + + + + + + 50 + 16777215 + + + + 5 + + + + + + + Start tag + + + Qt::AlignCenter + + + + + + + + + + End tag + + + Qt::AlignCenter + + + + + + + + + + + + + 540 + 510 + 162 + 26 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 530 + 280 + 71 + 26 + + + + Delete + + + + + + 610 + 280 + 71 + 26 + + + + Add + + + + + + 10 + 10 + 691 + 271 + + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + + Description + + + + + Key + + + AlignHCenter|AlignVCenter|AlignCenter + + + + + Start Tag + + + + + End Tag + + + + + + +