openlp/openlp/core/ui/thememanager.py

770 lines
33 KiB
Python
Raw Normal View History

2009-09-13 15:14:45 +00:00
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2010-12-26 11:04:47 +00:00
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
2010-07-24 22:10:47 +00:00
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
2009-09-13 15:14:45 +00:00
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
import os
import zipfile
import shutil
import logging
from xml.etree.ElementTree import ElementTree, XML
from PyQt4 import QtCore, QtGui
2010-10-17 18:58:42 +00:00
from openlp.core.ui import FileRenameForm, ThemeForm
2009-09-13 15:14:45 +00:00
from openlp.core.theme import Theme
2010-10-21 15:31:22 +00:00
from openlp.core.lib import OpenLPToolbar, ThemeXML, get_text_file_string, \
build_icon, Receiver, SettingsManager, translate, check_item_selected, \
2011-01-09 08:17:17 +00:00
BackgroundType, BackgroundGradientType, check_directory_exists
from openlp.core.utils import AppLocation, get_filesystem_encoding
2009-09-13 15:14:45 +00:00
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
2009-09-13 15:14:45 +00:00
class ThemeManager(QtGui.QWidget):
"""
Manages the orders of Theme.
"""
def __init__(self, mainwindow, parent=None):
2009-09-13 15:14:45 +00:00
QtGui.QWidget.__init__(self, parent)
self.mainwindow = mainwindow
self.settingsSection = u'themes'
self.themeForm = ThemeForm(self)
self.fileRenameForm = FileRenameForm(self)
self.serviceComboBox = self.mainwindow.ServiceManagerContents.themeComboBox
# start with the layout
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setMargin(0)
self.layout.setObjectName(u'layout')
self.toolbar = OpenLPToolbar(self)
self.toolbar.addToolbarButton(
translate('OpenLP.ThemeManager', 'New Theme'),
u':/themes/theme_new.png',
translate('OpenLP.ThemeManager', 'Create a new theme.'),
self.onAddTheme)
self.toolbar.addToolbarButton(
translate('OpenLP.ThemeManager', 'Edit Theme'),
u':/themes/theme_edit.png',
translate('OpenLP.ThemeManager', 'Edit a theme.'),
self.onEditTheme)
self.toolbar.addToolbarButton(
translate('OpenLP.ThemeManager', 'Delete Theme'),
u':/general/general_delete.png',
translate('OpenLP.ThemeManager', 'Delete a theme.'),
self.onDeleteTheme)
self.toolbar.addSeparator()
self.toolbar.addToolbarButton(
translate('OpenLP.ThemeManager', 'Import Theme'),
u':/general/general_import.png',
translate('OpenLP.ThemeManager', 'Import a theme.'),
self.onImportTheme)
self.toolbar.addToolbarButton(
translate('OpenLP.ThemeManager', 'Export Theme'),
u':/general/general_export.png',
translate('OpenLP.ThemeManager', 'Export a theme.'),
self.onExportTheme)
self.toolbar.setObjectName(u'toolbar')
self.layout.addWidget(self.toolbar)
self.themeWidget = QtGui.QWidgetAction(self.toolbar)
self.themeWidget.setObjectName(u'themeWidget')
# create theme manager list
self.themeListWidget = QtGui.QListWidget(self)
self.themeListWidget.setAlternatingRowColors(True)
self.themeListWidget.setIconSize(QtCore.QSize(88, 50))
self.themeListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.themeListWidget.setObjectName(u'themeListWidget')
self.layout.addWidget(self.themeListWidget)
QtCore.QObject.connect(self.themeListWidget,
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
self.contextMenu)
# build the context menu
self.menu = QtGui.QMenu()
self.editAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Edit Theme'))
self.editAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.copyAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Copy Theme'))
self.copyAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.renameAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Rename Theme'))
self.renameAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.deleteAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Delete Theme'))
self.deleteAction.setIcon(build_icon(u':/general/general_delete.png'))
self.separator = self.menu.addSeparator()
self.globalAction = self.menu.addAction(
translate('OpenLP.ThemeManager', 'Set As &Global Default'))
self.globalAction.setIcon(build_icon(u':/general/general_export.png'))
self.exportAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Export Theme'))
self.exportAction.setIcon(build_icon(u':/general/general_export.png'))
2010-12-27 10:18:09 +00:00
# Signals
QtCore.QObject.connect(self.themeListWidget,
2009-09-13 15:14:45 +00:00
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.changeGlobalFromScreen)
QtCore.QObject.connect(Receiver.get_receiver(),
2010-04-16 07:31:01 +00:00
QtCore.SIGNAL(u'theme_update_global'), self.changeGlobalFromTab)
2010-12-27 10:18:09 +00:00
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
# Variables
2009-09-13 15:14:45 +00:00
self.themelist = []
self.path = AppLocation.get_section_data_path(self.settingsSection)
2011-01-09 08:17:17 +00:00
check_directory_exists(self.path)
self.thumbPath = os.path.join(self.path, u'thumbnails')
2011-01-09 08:17:17 +00:00
check_directory_exists(self.thumbPath)
2010-10-17 18:58:42 +00:00
self.themeForm.path = self.path
2010-07-12 22:32:46 +00:00
self.oldBackgroundImage = None
2009-09-13 15:14:45 +00:00
# Last little bits of setting up
2010-12-27 10:18:09 +00:00
self.configUpdated()
def configUpdated(self, firstTime=False):
"""
Triggered when Config dialog is updated.
"""
2010-04-27 16:27:57 +00:00
self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme',
2010-04-28 14:17:42 +00:00
QtCore.QVariant(u'')).toString())
2009-09-13 15:14:45 +00:00
def contextMenu(self, point):
2010-12-28 10:56:19 +00:00
"""
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
realThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
themeName = unicode(item.text())
self.deleteAction.setVisible(False)
self.renameAction.setVisible(False)
2010-09-26 08:38:11 +00:00
self.globalAction.setVisible(False)
# If default theme restrict actions
if realThemeName == themeName:
self.deleteAction.setVisible(True)
self.renameAction.setVisible(True)
2010-09-26 08:38:11 +00:00
self.globalAction.setVisible(True)
action = self.menu.exec_(self.themeListWidget.mapToGlobal(point))
if action == self.editAction:
self.onEditTheme()
if action == self.copyAction:
self.onCopyTheme()
if action == self.renameAction:
self.onRenameTheme()
if action == self.deleteAction:
self.onDeleteTheme()
if action == self.globalAction:
2010-09-26 08:38:11 +00:00
self.changeGlobalFromScreen()
if action == self.exportAction:
2010-09-26 08:38:11 +00:00
self.onExportTheme()
2009-09-13 15:14:45 +00:00
def changeGlobalFromTab(self, themeName):
2010-06-10 01:57:59 +00:00
"""
Change the global theme when it is changed through the Themes settings
tab
"""
2009-09-13 15:14:45 +00:00
log.debug(u'changeGlobalFromTab %s', themeName)
for count in range (0, self.themeListWidget.count()):
2010-08-28 15:49:51 +00:00
# reset the old name
item = self.themeListWidget.item(count)
2009-09-21 17:56:36 +00:00
oldName = item.text()
2009-09-13 15:14:45 +00:00
newName = unicode(item.data(QtCore.Qt.UserRole).toString())
if oldName != newName:
self.themeListWidget.item(count).setText(newName)
2010-08-28 15:49:51 +00:00
# Set the new name
2009-09-21 23:11:50 +00:00
if themeName == newName:
name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % newName
self.themeListWidget.item(count).setText(name)
2009-09-13 15:14:45 +00:00
def changeGlobalFromScreen(self, index=-1):
2010-06-10 01:57:59 +00:00
"""
Change the global theme when a theme is double clicked upon in the
Theme Manager list
"""
2009-09-13 15:14:45 +00:00
log.debug(u'changeGlobalFromScreen %s', index)
selected_row = self.themeListWidget.currentRow()
for count in range (0, self.themeListWidget.count()):
item = self.themeListWidget.item(count)
2009-09-21 17:56:36 +00:00
oldName = item.text()
2010-08-28 15:49:51 +00:00
# reset the old name
2009-09-13 15:14:45 +00:00
if oldName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.themeListWidget.item(count).setText(
2009-09-13 15:14:45 +00:00
unicode(item.data(QtCore.Qt.UserRole).toString()))
2010-08-28 15:49:51 +00:00
# Set the new name
2009-10-10 18:36:58 +00:00
if count == selected_row:
2009-09-13 15:14:45 +00:00
self.global_theme = unicode(
self.themeListWidget.item(count).text())
name = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % self.global_theme
self.themeListWidget.item(count).setText(name)
2010-04-28 14:17:42 +00:00
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
2010-04-28 01:28:37 +00:00
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global',
self.global_theme)
2009-09-13 15:14:45 +00:00
self.pushThemes()
def onAddTheme(self):
2010-06-10 01:57:59 +00:00
"""
Loads a new theme with the default settings and then launches the theme
editing form for the user to make their customisations.
"""
2010-10-17 18:58:42 +00:00
theme = ThemeXML()
self.themeForm.theme = theme
self.themeForm.exec_()
2009-09-13 15:14:45 +00:00
2010-09-26 06:20:24 +00:00
def onRenameTheme(self):
"""
Renames an existing theme to a new name
"""
if self._validate_theme_action(unicode(translate('OpenLP.ThemeManager',
'You must select a theme to rename.')),
unicode(translate('OpenLP.ThemeManager', 'Rename Confirmation')),
unicode(translate('OpenLP.ThemeManager', 'Rename %s theme?')),
False):
item = self.themeListWidget.currentItem()
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
if self.fileRenameForm.exec_():
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
2010-12-27 10:18:09 +00:00
if self.checkIfThemeExists(newThemeName):
oldThemeData = self.getThemeData(oldThemeName)
self.deleteTheme(oldThemeName)
self.cloneThemeData(oldThemeData, newThemeName)
2011-01-09 23:24:55 +00:00
for plugin in self.mainwindow.pluginManager.plugins:
2010-12-27 10:18:09 +00:00
if plugin.usesTheme(oldThemeName):
plugin.renameTheme(oldThemeName, newThemeName)
2010-09-26 06:20:24 +00:00
def onCopyTheme(self):
"""
Copies an existing theme to a new name
"""
item = self.themeListWidget.currentItem()
2010-09-26 07:39:50 +00:00
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
2010-12-16 16:08:10 +00:00
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
2010-12-24 08:07:26 +00:00
if self.fileRenameForm.exec_(True):
2010-12-16 16:08:10 +00:00
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
2010-12-27 10:18:09 +00:00
if self.checkIfThemeExists(newThemeName):
themeData = self.getThemeData(oldThemeName)
self.cloneThemeData(themeData, newThemeName)
self.loadThemes()
2010-09-26 07:39:50 +00:00
2010-10-03 07:42:02 +00:00
def cloneThemeData(self, themeData, newThemeName):
2010-09-26 07:39:50 +00:00
"""
2010-10-03 07:42:02 +00:00
Takes a theme and makes a new copy of it as well as saving it.
2010-09-26 07:39:50 +00:00
"""
log.debug(u'cloneThemeData')
saveTo = None
saveFrom = None
2010-10-03 07:42:02 +00:00
if themeData.background_type == u'image':
saveTo = os.path.join(self.path, newThemeName,
2010-10-03 07:42:02 +00:00
os.path.split(unicode(themeData.background_filename))[1])
saveFrom = themeData.background_filename
themeData.theme_name = newThemeName
self.saveTheme(themeData, saveFrom, saveTo)
2010-09-26 06:20:24 +00:00
2009-09-13 15:14:45 +00:00
def onEditTheme(self):
2010-06-10 01:57:59 +00:00
"""
Loads the settings for the theme that is to be edited and launches the
theme editing form so the user can make their changes.
"""
if check_item_selected(self.themeListWidget,
translate('OpenLP.ThemeManager',
2010-06-24 15:50:40 +00:00
'You must select a theme to edit.')):
item = self.themeListWidget.currentItem()
themeName = unicode(item.text())
theme = self.getThemeData(
2009-09-13 15:14:45 +00:00
unicode(item.data(QtCore.Qt.UserRole).toString()))
2010-07-12 22:32:46 +00:00
if theme.background_type == u'image':
self.oldBackgroundImage = theme.background_filename
2010-10-17 18:58:42 +00:00
self.themeForm.theme = theme
self.themeForm.exec_(True)
2009-09-13 15:14:45 +00:00
2010-09-26 08:38:11 +00:00
def onDeleteTheme(self):
2010-06-10 01:57:59 +00:00
"""
Delete a theme
"""
if self._validate_theme_action(unicode(translate('OpenLP.ThemeManager',
'You must select a theme to delete.')),
unicode(translate('OpenLP.ThemeManager', 'Delete Confirmation')),
unicode(translate('OpenLP.ThemeManager', 'Delete %s theme?'))):
item = self.themeListWidget.currentItem()
2009-09-13 15:14:45 +00:00
theme = unicode(item.text())
row = self.themeListWidget.row(item)
self.themeListWidget.takeItem(row)
self.deleteTheme(theme)
def deleteTheme(self, theme):
"""
Delete a theme.
``theme``
The theme to delete.
"""
self.themelist.remove(theme)
2010-07-21 09:52:00 +00:00
thumb = theme + u'.png'
try:
2010-07-21 09:52:00 +00:00
os.remove(os.path.join(self.path, thumb))
os.remove(os.path.join(self.thumbPath, thumb))
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
except OSError:
2010-07-21 09:52:00 +00:00
log.exception(u'Error deleting theme %s', theme)
# 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()
2009-09-13 15:14:45 +00:00
def onExportTheme(self):
"""
Save the theme in a zip file
"""
item = self.themeListWidget.currentItem()
if item is None:
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
translate('OpenLP.ThemeManager',
'You have not selected a theme.'))
return
theme = unicode(item.data(QtCore.Qt.UserRole).toString())
path = QtGui.QFileDialog.getExistingDirectory(self,
unicode(translate('OpenLP.ThemeManager',
'Save Theme - (%s)')) % theme,
SettingsManager.get_last_dir(self.settingsSection, 1))
path = unicode(path)
2009-11-07 00:00:36 +00:00
if path:
SettingsManager.set_last_dir(self.settingsSection, path, 1)
2010-08-29 14:27:51 +00:00
themePath = os.path.join(path, theme + u'.otz')
2009-11-07 00:00:36 +00:00
zip = None
try:
zip = zipfile.ZipFile(themePath, u'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
2009-11-07 00:00:36 +00:00
zip.write(
os.path.join(source, name).encode(u'utf-8'),
os.path.join(theme, name).encode(u'utf-8'))
QtGui.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
2010-06-18 23:18:08 +00:00
'Your theme has been successfully exported.'))
2010-05-27 16:00:51 +00:00
except (IOError, OSError):
2009-11-07 00:00:36 +00:00
log.exception(u'Export Theme Failed')
2010-06-10 00:03:38 +00:00
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Theme Export Failed'),
translate('OpenLP.ThemeManager',
2010-06-18 23:18:08 +00:00
'Your theme could not be exported due to an error.'))
2009-11-07 00:00:36 +00:00
finally:
if zip:
zip.close()
2009-09-13 15:14:45 +00:00
def onImportTheme(self):
2010-06-10 01:57:59 +00:00
"""
Opens a file dialog to select the theme file(s) to import before
attempting to extract OpenLP themes from those files. This process
will load both OpenLP version 1 and version 2 themes.
"""
2010-06-18 23:18:08 +00:00
files = QtGui.QFileDialog.getOpenFileNames(self,
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
2010-06-18 23:18:08 +00:00
SettingsManager.get_last_dir(self.settingsSection),
2011-01-09 19:08:37 +00:00
translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;'
'Theme v2 (*.otz);;All Files (*.*)'))
2009-09-13 15:14:45 +00:00
log.info(u'New Themes %s', unicode(files))
2010-03-09 19:43:11 +00:00
if files:
2009-09-13 15:14:45 +00:00
for file in files:
2010-04-28 14:17:42 +00:00
SettingsManager.set_last_dir(
self.settingsSection, unicode(file))
2009-09-13 15:14:45 +00:00
self.unzipTheme(file, self.path)
self.loadThemes()
def loadThemes(self):
"""
Loads the theme lists and triggers updates accross the whole system
using direct calls or core functions and events for the plugins.
2009-09-13 15:14:45 +00:00
The plugins will call back in to get the real list if they want it.
"""
log.debug(u'Load themes from dir')
self.themelist = []
self.themeListWidget.clear()
dirList = os.listdir(self.path)
2010-11-13 07:09:00 +00:00
dirList.sort()
for name in dirList:
if name.endswith(u'.png'):
2010-08-28 15:49:51 +00:00
# check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
textName = os.path.splitext(name)[0]
if textName == self.global_theme:
name = unicode(translate('OpenLP.ThemeManager',
2010-06-18 23:18:08 +00:00
'%s (default)')) % textName
else:
name = textName
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
item_name = QtGui.QListWidgetItem(name)
if os.path.exists(thumb):
icon = build_icon(thumb)
else:
icon = build_icon(theme)
2010-05-25 16:16:43 +00:00
pixmap = icon.pixmap(QtCore.QSize(88, 50))
pixmap.save(thumb, u'png')
item_name.setIcon(icon)
item_name.setData(QtCore.Qt.UserRole,
QtCore.QVariant(textName))
self.themeListWidget.addItem(item_name)
self.themelist.append(textName)
2009-09-13 15:14:45 +00:00
self.pushThemes()
def pushThemes(self):
2010-06-10 01:57:59 +00:00
"""
Notify listeners that the theme list has been updated
"""
2010-04-30 21:00:17 +00:00
Receiver.send_message(u'theme_update_list', self.getThemes())
2009-09-13 15:14:45 +00:00
def getThemes(self):
2010-06-10 01:57:59 +00:00
"""
Return the list of loaded themes
"""
2009-09-13 15:14:45 +00:00
return self.themelist
def getThemeData(self, themeName):
2010-06-10 01:57:59 +00:00
"""
Returns a theme object from an XML file
``themeName``
2010-06-10 01:57:59 +00:00
Name of the theme to load from file
"""
log.debug(u'getthemedata for theme %s', themeName)
xmlFile = os.path.join(self.path, unicode(themeName),
unicode(themeName) + u'.xml')
xml = get_text_file_string(xmlFile)
2009-11-06 02:12:56 +00:00
if not xml:
2010-12-28 10:56:19 +00:00
return self._baseTheme()
2010-10-11 16:14:36 +00:00
else:
2010-12-28 10:56:19 +00:00
return self._createThemeFromXml(xml, self.path)
2009-09-13 15:14:45 +00:00
def unzipTheme(self, filename, dir):
"""
Unzip the theme, remove the preview file if stored
Generate a new preview fileCheck the XML theme version and upgrade if
necessary.
"""
log.debug(u'Unzipping theme %s', filename)
filename = unicode(filename)
2009-11-07 00:00:36 +00:00
zip = None
outfile = None
try:
zip = zipfile.ZipFile(filename)
2009-11-07 00:00:36 +00:00
filexml = None
themename = None
for file in zip.namelist():
try:
ucsfile = file.decode(u'utf-8')
except UnicodeDecodeError:
QtGui.QMessageBox.critical(
self, translate('OpenLP.ThemeManager', 'Error'),
translate('OpenLP.ThemeManager',
'File is not a valid theme.\n'
2010-06-18 23:18:08 +00:00
'The content encoding is not UTF-8.'))
2010-06-19 18:29:18 +00:00
log.exception(u'Filename "%s" is not valid UTF-8' %
file.decode(u'utf-8', u'replace'))
continue
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
2010-02-21 23:05:03 +00:00
theme_dir = None
if osfile.endswith(os.path.sep):
theme_dir = os.path.join(dir, osfile)
2011-01-09 08:17:17 +00:00
check_directory_exists(theme_dir)
2009-11-07 00:00:36 +00:00
else:
fullpath = os.path.join(dir, osfile)
names = osfile.split(os.path.sep)
2009-11-07 00:00:36 +00:00
if len(names) > 1:
# not preview file
if themename is None:
themename = names[0]
2010-02-21 23:05:03 +00:00
if theme_dir is None:
theme_dir = os.path.join(dir, names[0])
2011-01-09 08:17:17 +00:00
check_directory_exists(theme_dir)
if os.path.splitext(ucsfile)[1].lower() in [u'.xml']:
2010-06-19 10:41:13 +00:00
xml_data = zip.read(file)
try:
xml_data = xml_data.decode(u'utf-8')
except UnicodeDecodeError:
log.exception(u'Theme XML is not UTF-8 '
2010-06-19 20:21:36 +00:00
u'encoded.')
break
2010-09-11 11:11:19 +00:00
filexml = self.checkVersionAndConvert(xml_data)
2009-11-07 00:00:36 +00:00
outfile = open(fullpath, u'w')
2010-06-19 10:41:13 +00:00
outfile.write(filexml.encode(u'utf-8'))
2009-11-07 00:00:36 +00:00
else:
2010-02-22 16:57:08 +00:00
outfile = open(fullpath, u'wb')
2009-11-07 00:00:36 +00:00
outfile.write(zip.read(file))
if filexml:
2010-12-28 10:56:19 +00:00
theme = self._createThemeFromXml(filexml, self.path)
2010-11-16 20:24:21 +00:00
self.generateAndSaveImage(dir, themename, theme)
else:
2011-01-01 17:23:24 +00:00
Receiver.send_message(u'openlp_error_message', {
2011-01-09 08:17:17 +00:00
u'title': translate('OpenLP.ThemeManager',
2011-01-01 17:23:24 +00:00
'Validation Error'),
u'message':translate('OpenLP.ThemeManager',
'File is not a valid theme.')})
log.exception(u'Theme file does not contain XML data %s' %
2010-06-17 21:07:01 +00:00
filename)
2010-06-10 07:59:46 +00:00
except (IOError, NameError):
2011-01-01 17:23:24 +00:00
Receiver.send_message(u'openlp_error_message', {
2011-01-09 08:17:17 +00:00
u'title': translate('OpenLP.ThemeManager',
2011-01-01 17:23:24 +00:00
'Validation Error'),
u'message':translate('OpenLP.ThemeManager',
'File is not a valid theme.')})
log.exception(u'Importing theme from zip failed %s' % filename)
2009-11-07 00:00:36 +00:00
finally:
if zip:
zip.close()
if outfile:
outfile.close()
2009-09-13 15:14:45 +00:00
2010-09-11 11:11:19 +00:00
def checkVersionAndConvert(self, xml_data):
2009-09-13 15:14:45 +00:00
"""
2010-06-10 01:57:59 +00:00
Check if a theme is from OpenLP version 1
2010-09-11 11:11:19 +00:00
``xml_data``
2010-06-10 01:57:59 +00:00
Theme XML to check the version of
2009-09-13 15:14:45 +00:00
"""
log.debug(u'checkVersion1 ')
2010-09-11 11:11:19 +00:00
theme = xml_data.encode(u'ascii', u'xmlcharrefreplace')
2009-09-13 15:14:45 +00:00
tree = ElementTree(element=XML(theme)).getroot()
2010-09-11 11:11:19 +00:00
# look for old version 1 tags
2009-09-13 15:14:45 +00:00
if tree.find(u'BackgroundType') is None:
2010-09-11 11:11:19 +00:00
return xml_data
2009-09-13 15:14:45 +00:00
else:
2010-12-28 10:56:19 +00:00
return self._migrateVersion122(xml_data)
2009-09-13 15:14:45 +00:00
2010-12-27 10:18:09 +00:00
def checkIfThemeExists(self, themeName):
2009-09-13 15:14:45 +00:00
"""
2010-12-27 10:18:09 +00:00
Check if theme already exists and displays error message
2010-06-10 01:57:59 +00:00
2010-12-28 10:56:19 +00:00
``themeName``
Name of the Theme to test
2010-12-27 10:18:09 +00:00
"""
theme_dir = os.path.join(self.path, themeName)
if os.path.exists(theme_dir):
2011-01-01 17:23:24 +00:00
Receiver.send_message(u'openlp_error_message', {
2011-01-09 08:17:17 +00:00
u'title': translate('OpenLP.ThemeManager',
2011-01-01 17:23:24 +00:00
'Validation Error'),
u'message':translate('OpenLP.ThemeManager',
'A theme with this name already exists.')})
2010-12-27 10:18:09 +00:00
return False
return True
2009-09-13 15:14:45 +00:00
def saveTheme(self, theme, imageFrom, imageTo):
2009-09-13 15:14:45 +00:00
"""
Called by thememaintenance Dialog to save the theme
and to trigger the reload of the theme list
"""
2010-11-05 19:20:41 +00:00
name = theme.theme_name
theme_pretty_xml = theme.extract_formatted_xml()
log.debug(u'saveTheme %s %s', name, theme_pretty_xml)
2009-09-13 15:14:45 +00:00
theme_dir = os.path.join(self.path, name)
2011-01-09 08:17:17 +00:00
check_directory_exists(theme_dir)
2009-09-13 15:14:45 +00:00
theme_file = os.path.join(theme_dir, name + u'.xml')
2010-12-27 10:18:09 +00:00
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:
2009-11-07 00:00:36 +00:00
try:
2010-12-27 10:18:09 +00:00
encoding = get_filesystem_encoding()
shutil.copyfile(
unicode(imageFrom).encode(encoding),
unicode(imageTo).encode(encoding))
2010-05-27 16:00:51 +00:00
except IOError:
2010-12-27 10:18:09 +00:00
log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme)
self.loadThemes()
self.pushThemes()
2009-09-13 15:14:45 +00:00
2010-11-05 19:20:41 +00:00
def generateAndSaveImage(self, dir, name, theme):
log.debug(u'generateAndSaveImage %s %s', dir, name)
theme_xml = theme.extract_xml()
2009-09-13 15:14:45 +00:00
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)
2010-05-25 16:16:43 +00:00
pixmap = icon.pixmap(QtCore.QSize(88, 50))
pixmap.save(thumb, u'png')
2009-09-13 15:14:45 +00:00
log.debug(u'Theme image written to %s', samplepathname)
def generateImage(self, themeData, forcePage=False):
2009-09-13 15:14:45 +00:00
"""
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.
2009-09-13 15:14:45 +00:00
"""
log.debug(u'generateImage \n%s ', themeData)
return self.mainwindow.renderManager.generate_preview(themeData, forcePage)
2009-09-13 15:14:45 +00:00
def getPreviewImage(self, theme):
2010-06-10 19:45:02 +00:00
"""
Return an image representing the look of the theme
``theme``
The theme to return the image for
"""
2009-09-13 15:14:45 +00:00
log.debug(u'getPreviewImage %s ', theme)
image = os.path.join(self.path, theme + u'.png')
return image
2010-12-28 10:56:19 +00:00
def _baseTheme(self):
2010-06-10 19:45:02 +00:00
"""
Provide a base theme with sensible defaults
"""
2009-09-13 15:14:45 +00:00
log.debug(u'base theme created')
newtheme = ThemeXML()
return newtheme
2009-09-13 15:14:45 +00:00
2010-12-28 10:56:19 +00:00
def _createThemeFromXml(self, themeXml, path):
2010-06-10 19:45:02 +00:00
"""
Return a theme object using information parsed from XML
``themeXml``
2010-06-10 19:45:02 +00:00
The XML data to load into the theme
"""
2009-11-06 02:12:56 +00:00
theme = ThemeXML()
theme.parse(themeXml)
2009-11-06 02:12:56 +00:00
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
2010-12-27 10:18:09 +00:00
# check for use in the system else where.
if testPlugin:
2011-01-09 23:24:55 +00:00
for plugin in self.mainwindow.pluginManager.plugins:
2010-12-27 10:18:09 +00:00
if plugin.usesTheme(theme):
2011-01-01 17:23:24 +00:00
Receiver.send_message(u'openlp_error_message', {
2011-01-09 08:17:17 +00:00
u'title': translate('OpenLP.ThemeManager',
2011-01-01 17:23:24 +00:00
'Validation Error'),
u'message': unicode(translate('OpenLP.ThemeManager',
'Theme %s is used in the %s plugin.')) % \
(theme, plugin.name)})
2010-12-27 10:18:09 +00:00
return False
return True
2010-12-28 10:56:19 +00:00
def _migrateVersion122(self, xml_data):
"""
Convert the xml data from version 1 format to the current format.
New fields are loaded with defaults to provide a complete, working
theme containing all compatible customisations from the old theme.
``xml_data``
Version 1 theme to convert
"""
theme = Theme(xml_data)
newtheme = ThemeXML()
newtheme.theme_name = theme.Name
if theme.BackgroundType == 0:
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Solid)
newtheme.background_color = \
unicode(theme.BackgroundParameter1.name())
elif theme.BackgroundType == 1:
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Gradient)
newtheme.background_direction = \
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
if theme.BackgroundParameter3.name() == 1:
newtheme.background_direction = \
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
newtheme.background_start_color = \
unicode(theme.BackgroundParameter1.name())
newtheme.background_end_color = \
unicode(theme.BackgroundParameter2.name())
else:
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Image)
newtheme.background_filename = unicode(theme.BackgroundParameter1)
newtheme.font_main_name = theme.FontName
newtheme.font_main_color = unicode(theme.FontColor.name())
newtheme.font_main_size = theme.FontProportion * 3
newtheme.font_footer_name = theme.FontName
newtheme.font_footer_color = unicode(theme.FontColor.name())
newtheme.font_main_shadow = False
if theme.Shadow == 1:
newtheme.font_main_shadow = True
newtheme.font_main_shadow_color = unicode(theme.ShadowColor.name())
if theme.Outline == 1:
newtheme.font_main_outline = True
newtheme.font_main_outline_color = \
unicode(theme.OutlineColor.name())
vAlignCorrection = 0
if theme.VerticalAlign == 2:
vAlignCorrection = 1
elif theme.VerticalAlign == 1:
vAlignCorrection = 2
newtheme.display_horizontal_align = theme.HorizontalAlign
newtheme.display_vertical_align = vAlignCorrection
2011-01-09 23:24:55 +00:00
return newtheme.extract_xml()