diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index 4718e289e..f62778820 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -174,6 +174,8 @@ def resize_image(image, width, height):
"""
preview = QtGui.QImage(image)
if not preview.isNull():
+ if preview.width() == width and preview.height == height:
+ return preview
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
realw = preview.width()
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index 1a31efa32..cad0b35cb 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -323,9 +323,15 @@ class MediaManagerItem(QtGui.QWidget):
translate('MediaManagerItem',
'&Add to selected Service Item'),
self.onAddEditClick))
- QtCore.QObject.connect(
- self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
- self.onPreviewClick)
+ if QtCore.QSettings().value(u'advanced/double click live',
+ QtCore.QVariant(False)).toBool():
+ QtCore.QObject.connect(self.listView,
+ QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
+ self.onLiveClick)
+ else:
+ QtCore.QObject.connect(self.listView,
+ QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
+ self.onPreviewClick)
def initialise(self):
"""
@@ -379,14 +385,17 @@ class MediaManagerItem(QtGui.QWidget):
"""
Validates to see if the file still exists or thumbnail is up to date
"""
- if os.path.exists(file):
+ if not os.path.exists(file):
+ return False
+ if os.path.exists(thumb):
filedate = os.stat(file).st_mtime
thumbdate = os.stat(thumb).st_mtime
#if file updated rebuild icon
if filedate > thumbdate:
self.iconFromFile(file, thumb)
- return True
- return False
+ else:
+ self.iconFromFile(file, thumb)
+ return True
def iconFromFile(self, file, thumb):
"""
diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py
index 18d4bdc9e..082070575 100644
--- a/openlp/core/lib/plugin.py
+++ b/openlp/core/lib/plugin.py
@@ -269,8 +269,22 @@ class Plugin(QtCore.QObject):
if self.settings_tab:
self.settingsForm.insertTab(self.settings_tab, self.weight)
- def canDeleteTheme(self, theme):
+ def usesTheme(self, theme):
"""
- Called to ask the plugin if a theme can be deleted
+ Called to find out if a plugin is currently using a theme.
+
+ Returns True if the theme is being used, otherwise returns False.
"""
- return True
\ No newline at end of file
+ return False
+
+ def renameTheme(self, oldTheme, newTheme):
+ """
+ Renames a theme a plugin is using making the plugin use the new name.
+
+ ``oldTheme``
+ The name of the theme the plugin should stop using.
+
+ ``newTheme``
+ The new name the plugin should now use.
+ """
+ pass
diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py
index 5b02d0593..c0c5a6109 100644
--- a/openlp/core/ui/advancedtab.py
+++ b/openlp/core/ui/advancedtab.py
@@ -53,22 +53,34 @@ class AdvancedTab(SettingsTab):
self.leftLayout = QtGui.QVBoxLayout(self.leftWidget)
self.leftLayout.setSpacing(8)
self.leftLayout.setMargin(0)
- self.recentGroupBox = QtGui.QGroupBox(self.leftWidget)
- self.recentGroupBox.setObjectName(u'recentGroupBox')
- self.recentGroupBox.setGeometry(QtCore.QRect(0, 0, 220, 57))
- self.recentGroupBox.setMaximumSize(QtCore.QSize(220, 57))
- self.recentLayout = QtGui.QHBoxLayout(self.recentGroupBox)
+ self.uiGroupBox = QtGui.QGroupBox(self.leftWidget)
+ self.uiGroupBox.setObjectName(u'uiGroupBox')
+ self.uiLayout = QtGui.QVBoxLayout(self.uiGroupBox)
+ self.uiLayout.setSpacing(8)
+ self.uiLayout.setMargin(6)
+ self.uiLayout.setObjectName(u'uiLayout')
+ self.recentLayout = QtGui.QHBoxLayout()
self.recentLayout.setSpacing(8)
self.recentLayout.setMargin(0)
self.recentLayout.setObjectName(u'recentLayout')
- self.recentLabel = QtGui.QLabel(self.recentGroupBox)
+ self.recentLabel = QtGui.QLabel(self.uiGroupBox)
self.recentLabel.setObjectName(u'recentLabel')
self.recentLayout.addWidget(self.recentLabel)
- self.recentSpinBox = QtGui.QSpinBox(self.recentGroupBox)
- self.recentSpinBox.setMinimum(0)
+ self.recentSpinBox = QtGui.QSpinBox(self.uiGroupBox)
self.recentSpinBox.setObjectName(u'recentSpinBox')
+ self.recentSpinBox.setMinimum(0)
self.recentLayout.addWidget(self.recentSpinBox)
- self.leftLayout.addWidget(self.recentGroupBox)
+ self.recentSpacer = QtGui.QSpacerItem(50, 20,
+ QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
+ self.recentLayout.addItem(self.recentSpacer)
+ self.uiLayout.addLayout(self.recentLayout)
+ self.mediaPluginCheckBox = QtGui.QCheckBox(self.uiGroupBox)
+ self.mediaPluginCheckBox.setObjectName(u'mediaPluginCheckBox')
+ self.uiLayout.addWidget(self.mediaPluginCheckBox)
+ self.doubleClickLiveCheckBox = QtGui.QCheckBox(self.uiGroupBox)
+ self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox')
+ self.uiLayout.addWidget(self.doubleClickLiveCheckBox)
+ self.leftLayout.addWidget(self.uiGroupBox)
# self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget)
# self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox')
# self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85))
@@ -97,10 +109,10 @@ class AdvancedTab(SettingsTab):
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.leftLayout.addItem(self.leftSpacer)
self.advancedTabLayout.addWidget(self.leftWidget)
-# self.rightWidget = QtGui.QWidget(self)
-# self.rightLayout = QtGui.QVBoxLayout(self.rightWidget)
-# self.rightLayout.setSpacing(8)
-# self.rightLayout.setMargin(0)
+ self.rightWidget = QtGui.QWidget(self)
+ self.rightLayout = QtGui.QVBoxLayout(self.rightWidget)
+ self.rightLayout.setSpacing(8)
+ self.rightLayout.setMargin(0)
# self.databaseGroupBox = QtGui.QGroupBox(self.rightWidget)
# self.databaseGroupBox.setObjectName(u'databaseGroupBox')
# self.databaseGroupBox.setEnabled(False)
@@ -108,7 +120,10 @@ class AdvancedTab(SettingsTab):
# self.databaseLayout.setSpacing(8)
# self.databaseLayout.setMargin(8)
# self.rightLayout.addWidget(self.databaseGroupBox)
-# self.advancedTabLayout.addWidget(self.rightWidget)
+ self.rightSpacer = QtGui.QSpacerItem(20, 40,
+ QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
+ self.rightLayout.addItem(self.rightSpacer)
+ self.advancedTabLayout.addWidget(self.rightWidget)
# QtCore.QObject.connect(self.sharedCheckBox,
# QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged)
@@ -116,9 +131,13 @@ class AdvancedTab(SettingsTab):
"""
Setup the interface translation strings.
"""
- self.recentGroupBox.setTitle(translate('AdvancedTab', 'Recent Files'))
+ self.uiGroupBox.setTitle(translate('AdvancedTab', 'UI Settings'))
self.recentLabel.setText(
- translate('AdvancedTab', 'Number of recent files to list:'))
+ translate('AdvancedTab', 'Number of recent files to display:'))
+ self.mediaPluginCheckBox.setText(translate('AdvancedTab',
+ 'Save currently selected media manager plugin'))
+ self.doubleClickLiveCheckBox.setText(translate('AdvancedTab',
+ 'Double-click to send items straight to live (requires restart)'))
# self.sharedDirGroupBox.setTitle(
# translate('AdvancedTab', 'Central Data Store'))
# self.sharedCheckBox.setText(
@@ -140,6 +159,12 @@ class AdvancedTab(SettingsTab):
u'max recent files', QtCore.QVariant(20)).toInt()[0])
self.recentSpinBox.setValue(settings.value(u'recent file count',
QtCore.QVariant(4)).toInt()[0])
+ self.mediaPluginCheckBox.setChecked(
+ settings.value(u'save current plugin',
+ QtCore.QVariant(False)).toBool())
+ self.doubleClickLiveCheckBox.setChecked(
+ settings.value(u'double click live',
+ QtCore.QVariant(False)).toBool())
settings.endGroup()
def save(self):
@@ -150,6 +175,10 @@ class AdvancedTab(SettingsTab):
settings.beginGroup(self.settingsSection)
settings.setValue(u'recent file count',
QtCore.QVariant(self.recentSpinBox.value()))
+ settings.setValue(u'save current plugin',
+ QtCore.QVariant(self.mediaPluginCheckBox.isChecked()))
+ settings.setValue(u'double click live',
+ QtCore.QVariant(self.doubleClickLiveCheckBox.isChecked()))
settings.endGroup()
def onSharedCheckBoxChanged(self, checked):
@@ -159,4 +188,3 @@ class AdvancedTab(SettingsTab):
self.sharedLabel.setEnabled(checked)
self.sharedTextEdit.setEnabled(checked)
self.sharedPushButton.setEnabled(checked)
-
diff --git a/openlp/core/ui/amendthemedialog.py b/openlp/core/ui/amendthemedialog.py
index a5cd69610..45fdf236a 100644
--- a/openlp/core/ui/amendthemedialog.py
+++ b/openlp/core/ui/amendthemedialog.py
@@ -130,9 +130,9 @@ class Ui_AmendThemeDialog(object):
self.ImageLineEdit.setObjectName(u'ImageLineEdit')
self.horizontalLayout_2.addWidget(self.ImageLineEdit)
self.ImageToolButton = QtGui.QToolButton(self.ImageFilenameWidget)
- icon1 = build_icon(u':/general/general_open.png')
- self.ImageToolButton.setIcon(icon1)
+ self.ImageToolButton.setIcon(build_icon(u':/general/general_open.png'))
self.ImageToolButton.setObjectName(u'ImageToolButton')
+ self.ImageToolButton.setAutoRaise(True)
self.horizontalLayout_2.addWidget(self.ImageToolButton)
self.BackgroundLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
self.ImageFilenameWidget)
diff --git a/openlp/core/ui/amendthemeform.py b/openlp/core/ui/amendthemeform.py
index 1c2658dc3..10198bc7a 100644
--- a/openlp/core/ui/amendthemeform.py
+++ b/openlp/core/ui/amendthemeform.py
@@ -36,15 +36,21 @@ from amendthemedialog import Ui_AmendThemeDialog
log = logging.getLogger(u'AmendThemeForm')
class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
-
+ """
+ The :class:`AmendThemeForm` class provides the user interface to set up
+ new and edit existing themes.
+ """
def __init__(self, parent):
+ """
+ Initialise the theme editor user interface
+ """
QtGui.QDialog.__init__(self, parent)
self.thememanager = parent
self.path = None
self.theme = ThemeXML()
self.setupUi(self)
- #define signals
- #Buttons
+ # define signals
+ # Buttons
QtCore.QObject.connect(self.Color1PushButton,
QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
QtCore.QObject.connect(self.Color2PushButton,
@@ -59,8 +65,8 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.ShadowColorPushButton,
QtCore.SIGNAL(u'pressed()'), self.onShadowColorPushButtonClicked)
QtCore.QObject.connect(self.ImageToolButton,
- QtCore.SIGNAL(u'pressed()'), self.onImageToolButtonClicked)
- #Combo boxes
+ QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked)
+ # Combo boxes
QtCore.QObject.connect(self.BackgroundComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected)
QtCore.QObject.connect(self.BackgroundTypeComboBox,
@@ -82,16 +88,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.SIGNAL(u'activated(int)'), self.onHorizontalComboBoxSelected)
QtCore.QObject.connect(self.VerticalComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onVerticalComboBoxSelected)
- #Spin boxes
+ # Spin boxes
QtCore.QObject.connect(self.FontMainSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainSizeSpinBoxChanged)
QtCore.QObject.connect(self.FontFooterSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterSizeSpinBoxChanged)
- QtCore.QObject.connect(self.FontMainDefaultCheckBox,
- QtCore.SIGNAL(u'stateChanged(int)'),
- self.onFontMainDefaultCheckBoxChanged)
QtCore.QObject.connect(self.FontMainXSpinBox,
QtCore.SIGNAL(u'editingFinished()'), self.onFontMainXSpinBoxChanged)
QtCore.QObject.connect(self.FontMainYSpinBox,
@@ -108,9 +111,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.FontMainLineSpacingSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainLineSpacingSpinBoxChanged)
- QtCore.QObject.connect(self.FontFooterDefaultCheckBox,
- QtCore.SIGNAL(u'stateChanged(int)'),
- self.onFontFooterDefaultCheckBoxChanged)
QtCore.QObject.connect(self.FontFooterXSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterXSpinBoxChanged)
@@ -123,16 +123,23 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.FontFooterHeightSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterHeightSpinBoxChanged)
- QtCore.QObject.connect(self.OutlineCheckBox,
- QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged)
QtCore.QObject.connect(self.ShadowSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onShadowSpinBoxChanged)
- QtCore.QObject.connect(self.ShadowCheckBox,
- QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged)
QtCore.QObject.connect(self.OutlineSpinBox,
QtCore.SIGNAL(u'editingFinished()'),
self.onOutlineSpinBoxChanged)
+ # CheckBoxes
+ QtCore.QObject.connect(self.FontMainDefaultCheckBox,
+ QtCore.SIGNAL(u'stateChanged(int)'),
+ self.onFontMainDefaultCheckBoxChanged)
+ QtCore.QObject.connect(self.FontFooterDefaultCheckBox,
+ QtCore.SIGNAL(u'stateChanged(int)'),
+ self.onFontFooterDefaultCheckBoxChanged)
+ QtCore.QObject.connect(self.OutlineCheckBox,
+ QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged)
+ QtCore.QObject.connect(self.ShadowCheckBox,
+ QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged)
QtCore.QObject.connect(self.SlideTransitionCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onSlideTransitionCheckBoxChanged)
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index ccef8a5ea..ec34a483b 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -658,6 +658,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
log.info(u'Load Themes')
self.ThemeManagerContents.loadThemes()
log.info(u'Load data from Settings')
+ if QtCore.QSettings().value(u'advanced/save current plugin',
+ QtCore.QVariant(False)).toBool():
+ savedPlugin = QtCore.QSettings().value(
+ u'advanced/current media plugin', QtCore.QVariant()).toInt()[0]
+ if savedPlugin != -1:
+ self.MediaToolBox.setCurrentIndex(savedPlugin)
self.settingsForm.postSetUp()
def setAutoLanguage(self, value):
@@ -820,6 +826,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
"""
# Clean temporary files used by services
self.ServiceManagerContents.cleanUp()
+ if QtCore.QSettings().value(u'advanced/save current plugin',
+ QtCore.QVariant(False)).toBool():
+ QtCore.QSettings().setValue(u'advanced/current media plugin',
+ QtCore.QVariant(self.MediaToolBox.currentIndex()))
# Call the cleanup method to shutdown plugins.
log.info(u'cleanup plugins')
self.plugin_manager.finalise_plugins()
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 8f9242764..70ec3ac6e 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -202,15 +202,21 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu)
self.HideMenu.setMenu(QtGui.QMenu(
translate('SlideController', 'Hide'), self.Toolbar))
- self.BlankScreen = QtGui.QAction(QtGui.QIcon( u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu)
+ self.BlankScreen = QtGui.QAction(QtGui.QIcon(
+ u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu)
self.BlankScreen.setCheckable(True)
- QtCore.QObject.connect(self.BlankScreen, QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
- self.ThemeScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu)
+ QtCore.QObject.connect(self.BlankScreen,
+ QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
+ self.ThemeScreen = QtGui.QAction(QtGui.QIcon(
+ u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu)
self.ThemeScreen.setCheckable(True)
- QtCore.QObject.connect(self.ThemeScreen, QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
- self.DesktopScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
+ QtCore.QObject.connect(self.ThemeScreen,
+ QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
+ self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
+ u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
self.DesktopScreen.setCheckable(True)
- QtCore.QObject.connect(self.DesktopScreen, QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
+ QtCore.QObject.connect(self.DesktopScreen,
+ QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
self.HideMenu.setDefaultAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.ThemeScreen)
@@ -241,9 +247,8 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarWidget(
u'Image SpinBox', self.DelaySpinBox)
self.DelaySpinBox.setSuffix(translate('SlideController', 's'))
- self.DelaySpinBox.setToolTip(
- translate('SlideController',
- 'Delay between slides in seconds'))
+ self.DelaySpinBox.setToolTip(translate('SlideController',
+ 'Delay between slides in seconds'))
self.ControllerLayout.addWidget(self.Toolbar)
#Build a Media ToolBar
self.Mediabar = OpenLPToolbar(self)
@@ -268,8 +273,7 @@ class SlideController(QtGui.QWidget):
self.volumeSlider = Phonon.VolumeSlider()
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider')
- self.Mediabar.addToolbarWidget(
- u'Audio Volume', self.volumeSlider)
+ self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
self.ControllerLayout.addWidget(self.Mediabar)
# Build the Song Toolbar
if isLive:
@@ -328,6 +332,11 @@ class SlideController(QtGui.QWidget):
# Signals
QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
+ if not self.isLive:
+ if QtCore.QSettings().value(u'advanced/double click live',
+ QtCore.QVariant(False)).toBool():
+ QtCore.QObject.connect(self.PreviewListWidget,
+ QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
if isLive:
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index 46b4d0c50..dd8c917af 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -48,6 +48,7 @@ class ThemeManager(QtGui.QWidget):
QtGui.QWidget.__init__(self, parent)
self.parent = parent
self.settingsSection = u'themes'
+ self.serviceComboBox = self.parent.ServiceManagerContents.ThemeComboBox
self.Layout = QtGui.QVBoxLayout(self)
self.Layout.setSpacing(0)
self.Layout.setMargin(0)
@@ -116,6 +117,7 @@ class ThemeManager(QtGui.QWidget):
self.thumbPath = os.path.join(self.path, u'thumbnails')
self.checkThemesExists(self.thumbPath)
self.amendThemeForm.path = self.path
+ self.oldBackgroundImage = None
# Last little bits of setting up
self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme',
@@ -182,11 +184,17 @@ class ThemeManager(QtGui.QWidget):
Loads the settings for the theme that is to be edited and launches the
theme editing form so the user can make their changes.
"""
+ self.editingDefault = False
if check_item_selected(self.ThemeListWidget, translate('ThemeManager',
'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.amendThemeForm.loadTheme(theme)
self.saveThemeName = unicode(
item.data(QtCore.Qt.UserRole).toString())
@@ -212,37 +220,44 @@ class ThemeManager(QtGui.QWidget):
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
else:
for plugin in self.parent.plugin_manager.plugins:
- if not plugin.canDeleteTheme(theme):
+ if plugin.usesTheme(theme):
QtGui.QMessageBox.critical(self,
translate('ThemeManager', 'Error'),
unicode(translate('ThemeManager',
'Theme %s is use in %s plugin.')) % \
(theme, plugin.name))
return
- if unicode(self.parent.ServiceManagerContents.ThemeComboBox \
- .currentText()) == theme:
+ if unicode(self.serviceComboBox.currentText()) == theme:
QtGui.QMessageBox.critical(self,
translate('ThemeManager', 'Error'),
unicode(translate('ThemeManager',
'Theme %s is use by the service manager.')) % theme)
return
- self.themelist.remove(theme)
- th = theme + u'.png'
row = self.ThemeListWidget.row(item)
self.ThemeListWidget.takeItem(row)
- try:
- os.remove(os.path.join(self.path, th))
- os.remove(os.path.join(self.thumbPath, th))
- encoding = get_filesystem_encoding()
- shutil.rmtree(
- os.path.join(self.path, theme).encode(encoding))
- except OSError:
- #if not present do not worry
- pass
- # As we do not reload the themes push out the change
- # Reaload the list as the internal lists and events need
- # to be triggered
- self.pushThemes()
+ self.deleteTheme(theme)
+
+ def deleteTheme(self, theme):
+ """
+ Delete a theme.
+
+ ``theme``
+ The theme to delete.
+ """
+ self.themelist.remove(theme)
+ th = theme + u'.png'
+ try:
+ os.remove(os.path.join(self.path, th))
+ os.remove(os.path.join(self.thumbPath, th))
+ encoding = get_filesystem_encoding()
+ shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
+ except OSError:
+ #if not present do not worry
+ pass
+ # As we do not reload the themes push out the change
+ # Reaload the list as the internal lists and events need
+ # to be triggered
+ self.pushThemes()
def onExportTheme(self):
"""
@@ -532,18 +547,31 @@ class ThemeManager(QtGui.QWidget):
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('ThemeManager', 'Theme Exists'),
- translate('ThemeManager',
- 'A theme with this name already exists. '
- 'Would you like to overwrite it?'),
+ translate('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.plugin_manager.plugins:
+ if plugin.usesTheme(self.saveThemeName):
+ plugin.renameTheme(self.saveThemeName, name)
+ if unicode(self.serviceComboBox.currentText()) == name:
+ editedServiceTheme = True
+ self.deleteTheme(self.saveThemeName)
if result == QtGui.QMessageBox.Yes:
# Save the theme, overwriting the existing theme if necessary.
+ if image_to and self.oldBackgroundImage and \
+ image_to != 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')
@@ -563,6 +591,26 @@ class ThemeManager(QtGui.QWidget):
log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme_xml)
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:
+ 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('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.pushThemes()
else:
# Don't close the dialog - allow the user to change the name of
# the theme or to cancel the theme dialog completely.
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index 10a4c9551..3a7baf48b 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -95,7 +95,26 @@ class BiblePlugin(Plugin):
'displayed on the screen during the service.')
return about_text
- def canDeleteTheme(self, theme):
+ def usesTheme(self, theme):
+ """
+ Called to find out if the bible plugin is currently using a theme.
+
+ Returns True if the theme is being used, otherwise returns False.
+ """
if self.settings_tab.bible_theme == theme:
- return False
- return True
\ No newline at end of file
+ return True
+ return False
+
+ def renameTheme(self, oldTheme, newTheme):
+ """
+ Rename the theme the bible plugin is using making the plugin use the
+ new name.
+
+ ``oldTheme``
+ The name of the theme the plugin should stop using. Unused for
+ this particular plugin.
+
+ ``newTheme``
+ The new name the plugin should now use.
+ """
+ self.settings_tab.bible_theme = newTheme
diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py
index 8111841d9..2cdbac362 100644
--- a/openlp/plugins/custom/customplugin.py
+++ b/openlp/plugins/custom/customplugin.py
@@ -69,8 +69,30 @@ class CustomPlugin(Plugin):
'songs plugin.
')
return about_text
- def canDeleteTheme(self, theme):
- if not self.custommanager.get_all_objects_filtered(CustomSlide,
+ def usesTheme(self, theme):
+ """
+ Called to find out if the custom plugin is currently using a theme.
+
+ Returns True if the theme is being used, otherwise returns False.
+ """
+ if self.custommanager.get_all_objects_filtered(CustomSlide,
CustomSlide.theme_name == theme):
return True
- return False
\ No newline at end of file
+ return False
+
+ def renameTheme(self, oldTheme, newTheme):
+ """
+ Renames a theme the custom plugin is using making the plugin use the
+ new name.
+
+ ``oldTheme``
+ The name of the theme the plugin should stop using.
+
+ ``newTheme``
+ The new name the plugin should now use.
+ """
+ customsUsingTheme = self.custommanager.get_all_objects_filtered(
+ CustomSlide, CustomSlide.theme_name == oldTheme)
+ for custom in customsUsingTheme:
+ custom.theme_name = newTheme
+ self.custommanager.save_object(custom)
diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py
index 8dc4861aa..0b401f484 100644
--- a/openlp/plugins/presentations/lib/impresscontroller.py
+++ b/openlp/plugins/presentations/lib/impresscontroller.py
@@ -37,8 +37,6 @@ import logging
import os
import time
-from openlp.core.lib import resize_image
-
if os.name == u'nt':
from win32com.client import Dispatch
import pywintypes
@@ -74,6 +72,7 @@ class ImpressController(PresentationController):
self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
self.process = None
self.desktop = None
+ self.manager = None
def check_available(self):
"""
@@ -104,6 +103,10 @@ class ImpressController(PresentationController):
self.process.waitForStarted()
def get_uno_desktop(self):
+ """
+ On non-Windows platforms, use Uno. Get the OpenOffice desktop
+ which will be used to manage impress
+ """
log.debug(u'get UNO Desktop Openoffice')
ctx = None
loop = 0
@@ -134,10 +137,19 @@ class ImpressController(PresentationController):
return None
def get_com_desktop(self):
+ """
+ On Windows platforms, use COM. Return the desktop object which
+ will be used to manage Impress
+ """
log.debug(u'get COM Desktop OpenOffice')
+ if not self.manager:
+ return None
return self.manager.createInstance(u'com.sun.star.frame.Desktop')
def get_com_servicemanager(self):
+ """
+ Return the OOo service manager for windows
+ """
log.debug(u'get_com_servicemanager openoffice')
try:
return Dispatch(u'com.sun.star.ServiceManager')
@@ -171,13 +183,23 @@ class ImpressController(PresentationController):
log.exception(u'Failed to terminate OpenOffice')
def add_doc(self, name):
+ """
+ Called when a new Impress document is opened
+ """
log.debug(u'Add Doc OpenOffice')
doc = ImpressDocument(self, name)
self.docs.append(doc)
return doc
class ImpressDocument(PresentationDocument):
+ """
+ Class which holds information and controls a single presentation
+ """
+
def __init__(self, controller, presentation):
+ """
+ Constructor, store information about the file and initialise
+ """
log.debug(u'Init Presentation OpenOffice')
PresentationDocument.__init__(self, controller, presentation)
self.document = None
@@ -208,9 +230,8 @@ class ImpressDocument(PresentationDocument):
desktop = self.controller.get_uno_desktop()
url = uno.systemPathToFileUrl(self.filepath)
if desktop is None:
- return
+ return False
self.desktop = desktop
- #print "s.dsk2 ", self.desktop
properties = []
properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties)
@@ -219,12 +240,13 @@ class ImpressDocument(PresentationDocument):
0, properties)
except:
log.exception(u'Failed to load presentation')
- return
+ return False
self.presentation = self.document.getPresentation()
self.presentation.Display = \
self.controller.plugin.renderManager.screens.current_display + 1
self.control = None
self.create_thumbnails()
+ return True
def create_thumbnails(self):
"""
@@ -234,30 +256,36 @@ class ImpressDocument(PresentationDocument):
if self.check_thumbnails():
return
if os.name == u'nt':
- thumbdir = u'file:///' + self.thumbnailpath.replace(
+ thumbdirurl = u'file:///' + self.get_temp_folder().replace(
u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
else:
- thumbdir = uno.systemPathToFileUrl(self.thumbnailpath)
+ thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder())
props = []
props.append(self.create_property(u'FilterName', u'impress_png_Export'))
props = tuple(props)
doc = self.document
pages = doc.getDrawPages()
+ if not os.path.isdir(self.get_temp_folder()):
+ os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()):
page = pages.getByIndex(idx)
doc.getCurrentController().setCurrentPage(page)
- path = u'%s/%s%s.png' % (thumbdir, self.controller.thumbnailprefix,
- unicode(idx + 1))
+ urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1))
+ path = os.path.join(self.get_temp_folder(),
+ unicode(idx + 1) + u'.png')
try:
- doc.storeToURL(path , props)
- preview = resize_image(path, 640, 480)
+ doc.storeToURL(urlpath, props)
+ self.convert_thumbnail(path, idx + 1)
if os.path.exists(path):
os.remove(path)
- preview.save(path, u'png')
except:
log.exception(u'%s - Unable to store openoffice preview' % path)
def create_property(self, name, value):
+ """
+ Create an OOo style property object which are passed into some
+ Uno methods
+ """
log.debug(u'create property OpenOffice')
if os.name == u'nt':
prop = self.controller.manager.\
@@ -288,6 +316,9 @@ class ImpressDocument(PresentationDocument):
self.controller.remove_doc(self)
def is_loaded(self):
+ """
+ Returns true if a presentation is loaded
+ """
log.debug(u'is loaded OpenOffice')
#print "is_loaded "
if self.presentation is None or self.document is None:
@@ -302,6 +333,9 @@ class ImpressDocument(PresentationDocument):
return True
def is_active(self):
+ """
+ Returns true if a presentation is active and running
+ """
log.debug(u'is active OpenOffice')
#print "is_active "
if not self.is_loaded():
@@ -313,10 +347,16 @@ class ImpressDocument(PresentationDocument):
return True
def unblank_screen(self):
+ """
+ Unblanks the screen
+ """
log.debug(u'unblank screen OpenOffice')
return self.control.resume()
def blank_screen(self):
+ """
+ Blanks the screen
+ """
log.debug(u'blank screen OpenOffice')
self.control.blankScreen(0)
@@ -331,6 +371,9 @@ class ImpressDocument(PresentationDocument):
return False
def stop_presentation(self):
+ """
+ Stop the presentation, remove from screen
+ """
log.debug(u'stop presentation OpenOffice')
# deactivate should hide the screen according to docs, but doesn't
#self.control.deactivate()
@@ -338,6 +381,9 @@ class ImpressDocument(PresentationDocument):
self.control = None
def start_presentation(self):
+ """
+ Start the presentation from the beginning
+ """
log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning():
self.presentation.start()
@@ -354,12 +400,21 @@ class ImpressDocument(PresentationDocument):
self.goto_slide(1)
def get_slide_number(self):
+ """
+ Return the current slide number on the screen, from 1
+ """
return self.control.getCurrentSlideIndex() + 1
def get_slide_count(self):
+ """
+ Return the total number of slides
+ """
return self.document.getDrawPages().getCount()
def goto_slide(self, slideno):
+ """
+ Go to a specific slide (from 1)
+ """
self.control.gotoSlideIndex(slideno-1)
def next_step(self):
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index e4993b91a..b9ee2b79f 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -30,14 +30,17 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \
SettingsManager, translate, check_item_selected
-from openlp.core.utils import AppLocation
from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__)
-# We have to explicitly create separate classes for each plugin
-# in order for DnD to the Service manager to work correctly.
class PresentationListView(BaseListWithDnD):
+ """
+ Class for the list of Presentations
+
+ We have to explicitly create separate classes for each plugin
+ in order for DnD to the Service manager to work correctly.
+ """
def __init__(self, parent=None):
self.PluginName = u'Presentations'
BaseListWithDnD.__init__(self, parent)
@@ -45,11 +48,14 @@ class PresentationListView(BaseListWithDnD):
class PresentationMediaItem(MediaManagerItem):
"""
This is the Presentation media manager item for Presentation Items.
- It can present files using Openoffice
+ It can present files using Openoffice and Powerpoint
"""
log.info(u'Presentations Media Item loaded')
def __init__(self, parent, icon, title, controllers):
+ """
+ Constructor. Setup defaults
+ """
self.controllers = controllers
self.PluginNameShort = u'Presentation'
self.pluginNameVisible = translate('PresentationPlugin.MediaItem',
@@ -63,6 +69,9 @@ class PresentationMediaItem(MediaManagerItem):
self.message_listener = MessageListener(self)
def retranslateUi(self):
+ """
+ The name of the plugin media displayed in UI
+ """
self.OnNewPrompt = translate('PresentationPlugin.MediaItem',
'Select Presentation(s)')
self.Automatic = translate('PresentationPlugin.MediaItem',
@@ -80,12 +89,18 @@ class PresentationMediaItem(MediaManagerItem):
'Presentations (%s)' % fileType)
def requiredIcons(self):
+ """
+ Set which icons the media manager tab should show
+ """
MediaManagerItem.requiredIcons(self)
self.hasFileIcon = True
self.hasNewIcon = False
self.hasEditIcon = False
def addEndHeaderBar(self):
+ """
+ Display custom media manager items for presentations
+ """
self.PresentationWidget = QtGui.QWidget(self)
sizePolicy = QtGui.QSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
@@ -109,15 +124,13 @@ class PresentationMediaItem(MediaManagerItem):
self.pageLayout.addWidget(self.PresentationWidget)
def initialise(self):
- self.servicePath = os.path.join(
- AppLocation.get_section_data_path(self.settingsSection),
- u'thumbnails')
+ """
+ Populate the media manager tab
+ """
self.listView.setIconSize(QtCore.QSize(88, 50))
- if not os.path.exists(self.servicePath):
- os.mkdir(self.servicePath)
list = SettingsManager.load_list(
self.settingsSection, u'presentations')
- self.loadList(list)
+ self.loadList(list, True)
for item in self.controllers:
#load the drop down selection
if self.controllers[item].enabled:
@@ -126,7 +139,12 @@ class PresentationMediaItem(MediaManagerItem):
self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0)
- def loadList(self, list):
+ def loadList(self, list, initialLoad=False):
+ """
+ Add presentations into the media manager
+ This is called both on initial load of the plugin to populate with
+ existing files, and when the user adds new files via the media manager
+ """
currlist = self.getFileList()
titles = []
for file in currlist:
@@ -136,40 +154,43 @@ class PresentationMediaItem(MediaManagerItem):
continue
filename = os.path.split(unicode(file))[1]
if titles.count(filename) > 0:
- QtGui.QMessageBox.critical(
- self, translate('PresentationPlugin.MediaItem',
- 'File exists'),
+ if not initialLoad:
+ QtGui.QMessageBox.critical(
+ self, translate('PresentationPlugin.MediaItem',
+ 'File exists'),
translate('PresentationPlugin.MediaItem',
'A presentation with that filename already exists.'),
- QtGui.QMessageBox.Ok)
- else:
- icon = None
- for controller in self.controllers:
- thumbPath = os.path.join(
- AppLocation.get_section_data_path(
- self.settingsSection),
- u'thumbnails', controller, filename)
- thumb = os.path.join(thumbPath, u'slide1.png')
- preview = os.path.join(
- AppLocation.get_section_data_path(
- self.settingsSection),
- controller, u'thumbnails', filename, u'slide1.png')
- if os.path.exists(preview):
- if os.path.exists(thumb):
- if self.validate(preview, thumb):
- icon = build_icon(thumb)
- else:
- icon = build_icon(
- u':/general/general_delete.png')
- else:
- os.makedirs(thumbPath)
- icon = self.iconFromFile(preview, thumb)
- if not icon:
+ QtGui.QMessageBox.Ok)
+ continue
+ controller_name = self.findControllerByType(filename)
+ if controller_name:
+ controller = self.controllers[controller_name]
+ doc = controller.add_doc(unicode(file))
+ thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png')
+ preview = doc.get_thumbnail_path(1, True)
+ if not preview and not initialLoad:
+ doc.load_presentation()
+ preview = doc.get_thumbnail_path(1, True)
+ doc.close_presentation()
+ if preview and self.validate(preview, thumb):
+ icon = build_icon(thumb)
+ else:
icon = build_icon(u':/general/general_delete.png')
- item_name = QtGui.QListWidgetItem(filename)
- item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
- item_name.setIcon(icon)
- self.listView.addItem(item_name)
+ else:
+ if initialLoad:
+ icon = build_icon(u':/general/general_delete.png')
+ else:
+ QtGui.QMessageBox.critical(
+ self, translate('PresentationPlugin.MediaItem',
+ 'Unsupported file'),
+ translate('PresentationPlugin.MediaItem',
+ 'This type of presentation is not supported'),
+ QtGui.QMessageBox.Ok)
+ continue
+ item_name = QtGui.QListWidgetItem(filename)
+ item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
+ item_name.setIcon(icon)
+ self.listView.addItem(item_name)
def onDeleteClick(self):
"""
@@ -184,8 +205,6 @@ class PresentationMediaItem(MediaManagerItem):
for item in items:
filepath = unicode(item.data(
QtCore.Qt.UserRole).toString())
- #not sure of this has errors
- #John please can you look at .
for cidx in self.controllers:
doc = self.controllers[cidx].add_doc(filepath)
doc.presentation_deleted()
@@ -196,6 +215,11 @@ class PresentationMediaItem(MediaManagerItem):
self.settingsSection, self.getFileList())
def generateSlideData(self, service_item, item=None):
+ """
+ Load the relevant information for displaying the presentation
+ in the slidecontroller. In the case of powerpoints, an image
+ for each slide
+ """
items = self.listView.selectedIndexes()
if len(items) > 1:
return False
@@ -213,20 +237,27 @@ class PresentationMediaItem(MediaManagerItem):
controller = self.controllers[service_item.shortname]
(path, name) = os.path.split(filename)
doc = controller.add_doc(filename)
- if doc.get_slide_preview_file(1) is None:
+ if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation()
i = 1
- img = doc.get_slide_preview_file(i)
+ img = doc.get_thumbnail_path(i, True)
while img:
service_item.add_from_command(path, name, img)
i = i + 1
- img = doc.get_slide_preview_file(i)
+ img = doc.get_thumbnail_path(i, True)
doc.close_presentation()
return True
else:
return False
def findControllerByType(self, filename):
+ """
+ Determine the default application controller to use for the selected
+ file type. This is used if "Automatic" is set as the preferred
+ controller. Find the first (alphabetic) enabled controller which
+ "supports" the extension. If none found, then look for a controller
+ which "alsosupports" it instead.
+ """
filetype = os.path.splitext(filename)[1]
if not filetype:
return None
diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py
index 2f8a1aec7..a7e6f10ce 100644
--- a/openlp/plugins/presentations/lib/messagelistener.py
+++ b/openlp/plugins/presentations/lib/messagelistener.py
@@ -41,17 +41,28 @@ class Controller(object):
log.info(u'Controller loaded')
def __init__(self, live):
+ """
+ Constructor
+ """
self.is_live = live
self.doc = None
log.info(u'%s controller loaded' % live)
def add_handler(self, controller, file, is_blank):
+ """
+ Add a handler, which is an instance of a presentation and
+ slidecontroller combination. If the slidecontroller has a display
+ then load the presentation.
+ """
log.debug(u'Live = %s, add_handler %s' % (self.is_live, file))
self.controller = controller
if self.doc is not None:
self.shutdown()
self.doc = self.controller.add_doc(file)
- self.doc.load_presentation()
+ if not self.doc.load_presentation():
+ # Display error message to user
+ # Inform slidecontroller that the action failed?
+ return
if self.is_live:
self.doc.start_presentation()
if is_blank:
@@ -60,6 +71,10 @@ class Controller(object):
self.doc.slidenumber = 0
def activate(self):
+ """
+ Active the presentation, and show it on the screen.
+ Use the last slide number.
+ """
log.debug(u'Live = %s, activate' % self.is_live)
if self.doc.is_active():
return
@@ -71,6 +86,9 @@ class Controller(object):
self.doc.goto_slide(self.doc.slidenumber)
def slide(self, slide):
+ """
+ Go to a specific slide
+ """
log.debug(u'Live = %s, slide' % self.is_live)
if not self.is_live:
return
@@ -152,6 +170,9 @@ class Controller(object):
#self.timer.stop()
def blank(self):
+ """
+ Instruct the controller to blank the presentation
+ """
log.debug(u'Live = %s, blank' % self.is_live)
if not self.is_live:
return
@@ -162,6 +183,9 @@ class Controller(object):
self.doc.blank_screen()
def stop(self):
+ """
+ Instruct the controller to stop and hide the presentation
+ """
log.debug(u'Live = %s, stop' % self.is_live)
if not self.is_live:
return
@@ -172,6 +196,9 @@ class Controller(object):
self.doc.stop_presentation()
def unblank(self):
+ """
+ Instruct the controller to unblank the presentation
+ """
log.debug(u'Live = %s, unblank' % self.is_live)
if not self.is_live:
return
@@ -246,6 +273,9 @@ class MessageListener(object):
controller.add_handler(self.controllers[self.handler], file, is_blank)
def slide(self, message):
+ """
+ React to the message to move to a specific slide
+ """
is_live = message[1]
slide = message[2]
if is_live:
@@ -254,6 +284,9 @@ class MessageListener(object):
self.preview_handler.slide(slide)
def first(self, message):
+ """
+ React to the message to move to the first slide
+ """
is_live = message[1]
if is_live:
self.live_handler.first()
@@ -261,6 +294,9 @@ class MessageListener(object):
self.preview_handler.first()
def last(self, message):
+ """
+ React to the message to move to the last slide
+ """
is_live = message[1]
if is_live:
self.live_handler.last()
@@ -268,6 +304,9 @@ class MessageListener(object):
self.preview_handler.last()
def next(self, message):
+ """
+ React to the message to move to the next animation/slide
+ """
is_live = message[1]
if is_live:
self.live_handler.next()
@@ -275,6 +314,9 @@ class MessageListener(object):
self.preview_handler.next()
def previous(self, message):
+ """
+ React to the message to move to the previous animation/slide
+ """
is_live = message[1]
if is_live:
self.live_handler.previous()
@@ -282,6 +324,10 @@ class MessageListener(object):
self.preview_handler.previous()
def shutdown(self, message):
+ """
+ React to message to shutdown the presentation. I.e. end the show
+ and close the file
+ """
is_live = message[1]
if is_live:
Receiver.send_message(u'maindisplay_show')
@@ -290,19 +336,34 @@ class MessageListener(object):
self.preview_handler.shutdown()
def hide(self, message):
+ """
+ React to the message to show the desktop
+ """
is_live = message[1]
if is_live:
self.live_handler.stop()
def blank(self, message):
+ """
+ React to the message to blank the display
+ """
is_live = message[1]
if is_live:
self.live_handler.blank()
def unblank(self, message):
+ """
+ React to the message to unblank the display
+ """
is_live = message[1]
if is_live:
self.live_handler.unblank()
def timeout(self):
+ """
+ The presentation may be timed or might be controlled by the
+ application directly, rather than through OpenLP. Poll occassionally
+ to check which slide is currently displayed so the slidecontroller
+ view can be updated
+ """
self.live_handler.poll()
diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py
index 00ded85f6..6c725e583 100644
--- a/openlp/plugins/presentations/lib/powerpointcontroller.py
+++ b/openlp/plugins/presentations/lib/powerpointcontroller.py
@@ -97,13 +97,23 @@ class PowerpointController(PresentationController):
self.process = None
def add_doc(self, name):
+ """
+ Called when a new powerpoint document is opened
+ """
log.debug(u'Add Doc PowerPoint')
doc = PowerpointDocument(self, name)
self.docs.append(doc)
return doc
class PowerpointDocument(PresentationDocument):
+ """
+ Class which holds information and controls a single presentation
+ """
+
def __init__(self, controller, presentation):
+ """
+ Constructor, store information about the file and initialise
+ """
log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation)
self.presentation = None
@@ -111,22 +121,23 @@ class PowerpointDocument(PresentationDocument):
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
- It builds the environment, starts communcations with the background
- OpenOffice task started earlier. If OpenOffice is not present is is
- started. Once the environment is available the presentation is loaded
- and started.
+ Opens the PowerPoint file using the process created earlier
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
- if not self.controller.process.Visible:
+ if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
- self.controller.process.Presentations.Open(self.filepath, False, False,
- True)
+ try:
+ self.controller.process.Presentations.Open(self.filepath, False,
+ False, True)
+ except pywintypes.com_error:
+ return False
self.presentation = self.controller.process.Presentations(
self.controller.process.Presentations.Count)
self.create_thumbnails()
+ return True
def create_thumbnails(self):
"""
@@ -139,8 +150,8 @@ class PowerpointDocument(PresentationDocument):
"""
if self.check_thumbnails():
return
- self.presentation.Export(os.path.join(self.thumbnailpath, ''), 'png',
- 320, 240)
+ self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''),
+ 'png', 320, 240)
def close_presentation(self):
"""
@@ -298,4 +309,4 @@ class PowerpointDocument(PresentationDocument):
shape = shapes(idx + 1)
if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n'
- return text
\ No newline at end of file
+ return text
diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py
index 2c108333c..2810c88d2 100644
--- a/openlp/plugins/presentations/lib/pptviewcontroller.py
+++ b/openlp/plugins/presentations/lib/pptviewcontroller.py
@@ -93,13 +93,22 @@ class PptviewController(PresentationController):
self.docs[0].close_presentation()
def add_doc(self, name):
+ """
+ Called when a new powerpoint document is opened
+ """
log.debug(u'Add Doc PPTView')
doc = PptviewDocument(self, name)
self.docs.append(doc)
return doc
class PptviewDocument(PresentationDocument):
+ """
+ Class which holds information and controls a single presentation
+ """
def __init__(self, controller, presentation):
+ """
+ Constructor, store information about the file and initialise
+ """
log.debug(u'Init Presentation PowerPoint')
PresentationDocument.__init__(self, controller, presentation)
self.presentation = None
@@ -117,17 +126,31 @@ class PptviewDocument(PresentationDocument):
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
- #if self.pptid >= 0:
- # self.close_presentation()
rendermanager = self.controller.plugin.renderManager
rect = rendermanager.screens.current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(self.filepath.replace(u'/', u'\\'))
+ if not os.path.isdir(self.get_temp_folder()):
+ os.makedirs(self.get_temp_folder())
self.pptid = self.controller.process.OpenPPT(filepath, None, rect,
- str(os.path.join(self.thumbnailpath,
- self.controller.thumbnailprefix)))
- if self.pptid:
+ str(self.get_temp_folder()) + '\\slide')
+ if self.pptid >= 0:
+ self.create_thumbnails()
self.stop_presentation()
+ return True
+ else:
+ return False
+
+ def create_thumbnails(self):
+ """
+ PPTviewLib creates large BMP's, but we want small PNG's for consistency.
+ Convert them here.
+ """
+ if self.check_thumbnails():
+ return
+ for idx in range(self.get_slide_count()):
+ path = u'%s\\slide%s.bmp' % (self.get_temp_folder(), unicode(idx + 1))
+ self.convert_thumbnail(path, idx + 1)
def close_presentation(self):
"""
@@ -224,17 +247,3 @@ class PptviewDocument(PresentationDocument):
"""
self.controller.process.PrevStep(self.pptid)
- def get_slide_preview_file(self, slide_no):
- """
- Returns an image path containing a preview for the requested slide
-
- ``slide_no``
- The slide an image is required for, starting at 1
- """
- path = os.path.join(self.thumbnailpath,
- self.controller.thumbnailprefix + unicode(slide_no) + u'.bmp')
- if os.path.isfile(path):
- return path
- else:
- return None
-
diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py
index 501776a19..ed4081ed6 100644
--- a/openlp/plugins/presentations/lib/presentationcontroller.py
+++ b/openlp/plugins/presentations/lib/presentationcontroller.py
@@ -29,7 +29,7 @@ import shutil
from PyQt4 import QtCore
-from openlp.core.lib import Receiver
+from openlp.core.lib import Receiver, resize_image
from openlp.core.utils import AppLocation
log = logging.getLogger(__name__)
@@ -63,6 +63,13 @@ class PresentationController(object):
``plugin``
The presentationplugin object
+ ``supports``
+ The primary native file types this application supports
+
+ ``alsosupports``
+ Other file types the application can import, although not necessarily
+ the first choice due to potential incompatibilities
+
**Hook Functions**
``kill()``
@@ -109,12 +116,16 @@ class PresentationController(object):
QtCore.Qt.Checked
else:
self.enabled = False
- self.thumbnailroot = os.path.join(
+ self.temp_folder = os.path.join(
+ AppLocation.get_section_data_path(self.settings_section), name)
+ self.thumbnail_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section),
- name, u'thumbnails')
- self.thumbnailprefix = u'slide'
- if not os.path.isdir(self.thumbnailroot):
- os.makedirs(self.thumbnailroot)
+ u'thumbnails')
+ self.thumbnail_prefix = u'slide'
+ if not os.path.isdir(self.thumbnail_folder):
+ os.makedirs(self.thumbnail_folder)
+ if not os.path.isdir(self.temp_folder):
+ os.makedirs(self.temp_folder)
def check_available(self):
"""
@@ -208,14 +219,19 @@ class PresentationDocument(object):
``previous_step()``
Triggers the previous slide on the running presentation
- ``get_slide_preview_file(slide_no)``
+ ``get_thumbnail_path(slide_no, check_exists)``
Returns a path to an image containing a preview for the requested slide
"""
def __init__(self, controller, name):
+ """
+ Constructor for the PresentationController class
+ """
self.slidenumber = 0
self.controller = controller
- self.store_filename(name)
+ self.filepath = name
+ if not os.path.isdir(self.get_thumbnail_folder()):
+ os.mkdir(self.get_thumbnail_folder())
def load_presentation(self):
"""
@@ -224,9 +240,10 @@ class PresentationDocument(object):
``presentation``
The file name of the presentations to the run.
-
+
+ Returns False if the file could not be opened
"""
- pass
+ return False
def presentation_deleted(self):
"""
@@ -234,33 +251,37 @@ class PresentationDocument(object):
a file, e.g. thumbnails
"""
try:
- shutil.rmtree(self.thumbnailpath)
+ shutil.rmtree(self.get_thumbnail_folder())
+ shutil.rmtree(self.get_temp_folder())
except OSError:
log.exception(u'Failed to delete presentation controller files')
- def store_filename(self, presentation):
+ def get_file_name(self):
"""
- Set properties for the filename and thumbnail paths
+ Return just the filename of the presention, without the directory
"""
- self.filepath = presentation
- self.filename = self.get_file_name(presentation)
- self.thumbnailpath = self.get_thumbnail_path(presentation)
- if not os.path.isdir(self.thumbnailpath):
- os.mkdir(self.thumbnailpath)
+ return os.path.split(self.filepath)[1]
- def get_file_name(self, presentation):
- return os.path.split(presentation)[1]
-
- def get_thumbnail_path(self, presentation):
+ def get_thumbnail_folder(self):
+ """
+ The location where thumbnail images will be stored
+ """
return os.path.join(
- self.controller.thumbnailroot, self.get_file_name(presentation))
+ self.controller.thumbnail_folder, self.get_file_name())
+
+ def get_temp_folder(self):
+ """
+ The location where thumbnail images will be stored
+ """
+ return os.path.join(
+ self.controller.temp_folder, self.get_file_name())
def check_thumbnails(self):
"""
Returns true if the thumbnail images look to exist and are more
recent than the powerpoint
"""
- lastimage = self.get_slide_preview_file(self.get_slide_count())
+ lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)):
return False
imgdate = os.stat(lastimage).st_mtime
@@ -350,16 +371,27 @@ class PresentationDocument(object):
"""
pass
- def get_slide_preview_file(self, slide_no):
+ def convert_thumbnail(self, file, idx):
+ """
+ Convert the slide image the application made to a standard 320x240
+ .png image.
+ """
+ if self.check_thumbnails():
+ return
+ if os.path.isfile(file):
+ img = resize_image(file, 320, 240)
+ img.save(self.get_thumbnail_path(idx, False))
+
+ def get_thumbnail_path(self, slide_no, check_exists):
"""
Returns an image path containing a preview for the requested slide
``slide_no``
The slide an image is required for, starting at 1
"""
- path = os.path.join(self.thumbnailpath,
- self.controller.thumbnailprefix + unicode(slide_no) + u'.png')
- if os.path.isfile(path):
+ path = os.path.join(self.get_thumbnail_folder(),
+ self.controller.thumbnail_prefix + unicode(slide_no) + u'.png')
+ if os.path.isfile(path) or not check_exists:
return path
else:
return None
diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py
index 099376ce9..980c62f47 100644
--- a/openlp/plugins/presentations/lib/presentationtab.py
+++ b/openlp/plugins/presentations/lib/presentationtab.py
@@ -32,10 +32,16 @@ class PresentationTab(SettingsTab):
PresentationsTab is the Presentations settings tab in the settings dialog.
"""
def __init__(self, title, controllers):
+ """
+ Constructor
+ """
self.controllers = controllers
SettingsTab.__init__(self, title)
def setupUi(self):
+ """
+ Create the controls for the settings tab
+ """
self.setObjectName(u'PresentationTab')
self.tabTitleVisible = translate('PresentationPlugin.PresentationTab',
'Presentations')
@@ -89,6 +95,9 @@ class PresentationTab(SettingsTab):
self.PresentationLayout.addWidget(self.PresentationRightWidget)
def retranslateUi(self):
+ """
+ Make any translation changes
+ """
self.VerseDisplayGroupBox.setTitle(
translate('PresentationPlugin.PresentationTab',
'Available Controllers'))
@@ -100,6 +109,9 @@ class PresentationTab(SettingsTab):
translate('PresentationPlugin.PresentationTab', 'available')))
def load(self):
+ """
+ Load the settings.
+ """
for key in self.controllers:
controller = self.controllers[key]
if controller.available:
@@ -109,6 +121,9 @@ class PresentationTab(SettingsTab):
QtCore.QVariant(0)).toInt()[0])
def save(self):
+ """
+ Save the settings.
+ """
for key in self.controllers:
controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name]
diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py
index 7bb256af7..50784ed51 100644
--- a/openlp/plugins/presentations/presentationplugin.py
+++ b/openlp/plugins/presentations/presentationplugin.py
@@ -33,9 +33,17 @@ from openlp.plugins.presentations.lib import *
log = logging.getLogger(__name__)
class PresentationPlugin(Plugin):
+ """
+ This plugin allowed a Presentation to be opened, controlled and displayed
+ on the output display. The plugin controls third party applications such
+ as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
+ """
log = logging.getLogger(u'PresentationPlugin')
def __init__(self, plugin_helpers):
+ """
+ PluginPresentation constructor.
+ """
log.debug(u'Initialised')
self.controllers = {}
Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
@@ -51,6 +59,10 @@ class PresentationPlugin(Plugin):
return PresentationTab(self.name, self.controllers)
def initialise(self):
+ """
+ Initialise the plugin. Determine which controllers are enabled
+ are start their processes.
+ """
log.info(u'Presentations Initialising')
Plugin.initialise(self)
self.insertToolboxItem()
@@ -59,6 +71,10 @@ class PresentationPlugin(Plugin):
self.controllers[controller].start_process()
def finalise(self):
+ """
+ Finalise the plugin. Ask all the enabled presentation applications
+ to close down their applications and release resources.
+ """
log.info(u'Plugin Finalise')
#Ask each controller to tidy up
for key in self.controllers:
@@ -75,6 +91,10 @@ class PresentationPlugin(Plugin):
self, self.icon, self.name, self.controllers)
def registerControllers(self, controller):
+ """
+ Register each presentation controller (Impress, PPT etc) and
+ store for later use
+ """
self.controllers[controller.name] = controller
def checkPreConditions(self):
@@ -109,9 +129,13 @@ class PresentationPlugin(Plugin):
return False
def about(self):
+ """
+ Return information about this plugin
+ """
about_text = translate('PresentationPlugin',
'Presentation Plugin
Delivers '
'the ability to show presentations using a number of different '
'programs. The choice of available presentation programs is '
'available to the user in a drop down box.')
- return about_text
\ No newline at end of file
+ return about_text
+
diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py
index 54a8d539f..d50bf747f 100644
--- a/openlp/plugins/songs/forms/songmaintenanceform.py
+++ b/openlp/plugins/songs/forms/songmaintenanceform.py
@@ -24,6 +24,7 @@
###############################################################################
from PyQt4 import QtGui, QtCore
+from sqlalchemy.sql import and_
from openlp.core.lib import translate
from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm
@@ -97,6 +98,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtGui.QMessageBox.critical(self, dlg_title, sel_text)
def resetAuthors(self):
+ """
+ Reloads the Authors list.
+ """
self.AuthorsListWidget.clear()
authors = self.songmanager.get_all_objects(Author, Author.display_name)
for author in authors:
@@ -109,6 +113,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.AuthorsListWidget.addItem(author_name)
def resetTopics(self):
+ """
+ Reloads the Topics list.
+ """
self.TopicsListWidget.clear()
topics = self.songmanager.get_all_objects(Topic, Topic.name)
for topic in topics:
@@ -117,13 +124,88 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.TopicsListWidget.addItem(topic_name)
def resetBooks(self):
+ """
+ Reloads the Books list.
+ """
self.BooksListWidget.clear()
books = self.songmanager.get_all_objects(Book, Book.name)
for book in books:
- book_name = QtGui.QListWidgetItem(book.name)
+ book_name = QtGui.QListWidgetItem(u'%s (%s)' % (book.name,
+ book.publisher))
book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id))
self.BooksListWidget.addItem(book_name)
+ def checkAuthor(self, new_author, edit=False):
+ """
+ Returns False when the given Author is already in the list elsewise
+ True.
+ """
+ authors = self.songmanager.get_all_objects_filtered(Author,
+ and_(
+ Author.first_name == new_author.first_name,
+ Author.last_name == new_author.last_name,
+ Author.display_name == new_author.display_name
+ )
+ )
+ if len(authors) > 0:
+ # If we edit an existing Author, we need to make sure that we do
+ # not return False when nothing has changed (because this would
+ # cause an error message later on).
+ if edit:
+ if authors[0].id == new_author.id:
+ return True
+ else:
+ return False
+ else:
+ return False
+ else:
+ return True
+
+ def checkTopic(self, new_topic, edit=False):
+ """
+ Returns False when the given Topic is already in the list elsewise True.
+ """
+ topics = self.songmanager.get_all_objects_filtered(Topic,
+ Topic.name == new_topic.name
+ )
+ if len(topics) > 0:
+ # If we edit an existing Topic, we need to make sure that we do
+ # not return False when nothing has changed (because this would
+ # cause an error message later on).
+ if edit:
+ if topics[0].id == new_topic.id:
+ return True
+ else:
+ return False
+ else:
+ return False
+ else:
+ return True
+
+ def checkBook(self, new_book, edit=False):
+ """
+ Returns False when the given Book is already in the list elsewise True.
+ """
+ books = self.songmanager.get_all_objects_filtered(Book,
+ and_(
+ Book.name == new_book.name,
+ Book.publisher == new_book.publisher
+ )
+ )
+ if len(books) > 0:
+ # If we edit an existing Book, we need to make sure that we do
+ # not return False when nothing has changed (because this would
+ # cause an error message later on).
+ if edit:
+ if books[0].id == new_book.id:
+ return True
+ else:
+ return False
+ else:
+ return False
+ else:
+ return True
+
def onAuthorAddButtonClick(self):
self.authorform.setAutoDisplayName(True)
if self.authorform.exec_():
@@ -131,86 +213,93 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
first_name=unicode(self.authorform.FirstNameEdit.text()),
last_name=unicode(self.authorform.LastNameEdit.text()),
display_name=unicode(self.authorform.DisplayEdit.text()))
- if self.songmanager.save_object(author):
- self.resetAuthors()
+ if self.checkAuthor(author):
+ if self.songmanager.save_object(author):
+ self.resetAuthors()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t add your author.'))
+ 'Could not add your author.'))
def onTopicAddButtonClick(self):
if self.topicform.exec_():
topic = Topic.populate(name=unicode(self.topicform.NameEdit.text()))
- if self.songmanager.save_object(topic):
- self.resetTopics()
+ if self.checkTopic(topic):
+ if self.songmanager.save_object(topic):
+ self.resetTopics()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t add your topic.'))
+ 'Could not add your topic.'))
def onBookAddButtonClick(self):
if self.bookform.exec_():
book = Book.populate(
name=unicode(self.bookform.NameEdit.text()),
publisher=unicode(self.bookform.PublisherEdit.text()))
- if self.songmanager.save_object(book):
- self.resetBooks()
+ if self.checkBook(book):
+ if self.songmanager.save_object(book):
+ self.resetBooks()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t add your book.'))
+ 'Could not add your book.'))
def onAuthorEditButtonClick(self):
author_id = self._getCurrentItemId(self.AuthorsListWidget)
if author_id != -1:
author = self.songmanager.get_object(Author, author_id)
- # Just make sure none of the fields is None
- if author.first_name is None:
- author.first_name = u''
- if author.last_name is None:
- author.last_name = u''
- if author.display_name is None:
- author.display_name = u''
self.authorform.setAutoDisplayName(False)
self.authorform.FirstNameEdit.setText(author.first_name)
self.authorform.LastNameEdit.setText(author.last_name)
self.authorform.DisplayEdit.setText(author.display_name)
+ # Save the author's first and last name as well as the display name
+ # for the case that they have to be restored.
+ temp_first_name = author.first_name
+ temp_last_name = author.last_name
+ temp_display_name = author.display_name
if self.authorform.exec_(False):
author.first_name = unicode(
self.authorform.FirstNameEdit.text())
author.last_name = unicode(self.authorform.LastNameEdit.text())
author.display_name = unicode(
self.authorform.DisplayEdit.text())
- if self.songmanager.save_object(author):
- self.resetAuthors()
+ if self.checkAuthor(author, True):
+ if self.songmanager.save_object(author):
+ self.resetAuthors()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ # We restore the author's old first and last name as well as
+ # his display name.
+ author.first_name = temp_first_name
+ author.last_name = temp_last_name
+ author.display_name = temp_display_name
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t save your author.'))
+ 'Could not save your author.'))
def onTopicEditButtonClick(self):
topic_id = self._getCurrentItemId(self.TopicsListWidget)
if topic_id != -1:
topic = self.songmanager.get_object(Topic, topic_id)
self.topicform.NameEdit.setText(topic.name)
+ # Save the topic's name for the case that he has to be restored.
+ temp_name = topic.name
if self.topicform.exec_(False):
topic.name = unicode(self.topicform.NameEdit.text())
- if self.songmanager.save_object(topic):
- self.resetTopics()
+ if self.checkTopic(topic, True):
+ if self.songmanager.save_object(topic):
+ self.resetTopics()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ # We restore the topics's old name.
+ topic.name = temp_name
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t save your topic.'))
+ 'Could not save your topic.'))
def onBookEditButtonClick(self):
book_id = self._getCurrentItemId(self.BooksListWidget)
@@ -218,17 +307,24 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
book = self.songmanager.get_object(Book, book_id)
self.bookform.NameEdit.setText(book.name)
self.bookform.PublisherEdit.setText(book.publisher)
+ # Save the book's name and publisher for the case that they have to
+ # be restored.
+ temp_name = book.name
+ temp_publisher = book.publisher
if self.bookform.exec_(False):
book.name = unicode(self.bookform.NameEdit.text())
book.publisher = unicode(self.bookform.PublisherEdit.text())
- if self.songmanager.save_object(book):
- self.resetBooks()
+ if self.checkBook(book, True):
+ if self.songmanager.save_object(book):
+ self.resetBooks()
else:
- QtGui.QMessageBox.critical(
- self, translate('SongsPlugin.SongMaintenanceForm',
- 'Error'),
+ # We restore the book's old name and publisher.
+ book.name = temp_name
+ book.publisher = temp_publisher
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Couldn\'t save your book.'))
+ 'Could not save your book.'))
def onAuthorDeleteButtonClick(self):
"""
@@ -236,13 +332,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
"""
self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'),
- translate('SongsPlugin.SongMaintenanceForm',
+ translate('SongsPlugin.SongMaintenanceForm',
'Are you sure you want to delete the selected author?'),
translate('SongsPlugin.SongMaintenanceForm',
- 'This author can\'t be deleted, they are currently '
+ 'This author cannot be deleted, they are currently '
'assigned to at least one song.'),
- translate('SongsPlugin.SongMaintenanceForm',
- 'No author selected!'))
+ translate('SongsPlugin.SongMaintenanceForm', 'No author selected!'))
def onTopicDeleteButtonClick(self):
"""
@@ -250,13 +345,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
"""
self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'),
- translate('SongsPlugin.SongMaintenanceForm',
- 'Are you sure you want to delete the selected topic?'),
- translate('SongsPlugin.SongMaintenanceForm',
- 'This topic can\'t be deleted, it is currently '
- 'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm',
- 'No topic selected!'))
+ 'Are you sure you want to delete the selected topic?'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'This topic cannot be deleted, it is currently '
+ 'assigned to at least one song.'),
+ translate('SongsPlugin.SongMaintenanceForm', 'No topic selected!'))
def onBookDeleteButtonClick(self):
"""
@@ -266,7 +360,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'),
translate('SongsPlugin.SongMaintenanceForm',
'Are you sure you want to delete the selected book?'),
- translate('SongsPlugin.SongMaintenanceForm',
- 'This book can\'t be deleted, it is currently '
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'This book cannot be deleted, it is currently '
'assigned to at least one song.'),
- translate('SongsPlugin.SongMaintenanceForm', u'No book selected!'))
+ translate('SongsPlugin.SongMaintenanceForm', 'No book selected!'))
diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py
index f63454e51..e7be7cae3 100644
--- a/openlp/plugins/songs/songsplugin.py
+++ b/openlp/plugins/songs/songsplugin.py
@@ -192,8 +192,30 @@ class SongsPlugin(Plugin):
'This plugin allows songs to be managed and displayed.')
return about_text
- def canDeleteTheme(self, theme):
- if not self.manager.get_all_objects_filtered(Song,
+ def usesTheme(self, theme):
+ """
+ Called to find out if the song plugin is currently using a theme.
+
+ Returns True if the theme is being used, otherwise returns False.
+ """
+ if self.manager.get_all_objects_filtered(Song,
Song.theme_name == theme):
return True
- return False
\ No newline at end of file
+ return False
+
+ def renameTheme(self, oldTheme, newTheme):
+ """
+ Renames a theme the song plugin is using making the plugin use the new
+ name.
+
+ ``oldTheme``
+ The name of the theme the plugin should stop using.
+
+ ``newTheme``
+ The new name the plugin should now use.
+ """
+ songsUsingTheme = self.manager.get_all_objects_filtered(Song,
+ Song.theme_name == oldTheme)
+ for song in songsUsingTheme:
+ song.theme_name = newTheme
+ self.custommanager.save_object(song)