openlp/openlp/core/ui/thememanager.py

781 lines
34 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 #
# --------------------------------------------------------------------------- #
2009-12-31 12:52:01 +00:00
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 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, \
2010-11-17 18:35:04 +00:00
build_icon, Receiver, SettingsManager, translate, check_item_selected, \
BackgroundType, BackgroundGradientType
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, parent):
QtGui.QWidget.__init__(self, parent)
self.parent = parent
self.settingsSection = u'themes'
2010-07-28 13:36:29 +00:00
self.serviceComboBox = self.parent.ServiceManagerContents.themeComboBox
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setMargin(0)
2010-10-17 18:58:42 +00:00
self.themeForm = ThemeForm(self)
2010-09-26 06:20:24 +00:00
self.fileRenameForm = FileRenameForm(self)
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.themeWidget = QtGui.QWidgetAction(self.toolbar)
self.layout.addWidget(self.toolbar)
self.themeListWidget = QtGui.QListWidget(self)
self.themeListWidget.setAlternatingRowColors(True)
self.themeListWidget.setIconSize(QtCore.QSize(88, 50))
self.layout.addWidget(self.themeListWidget)
self.themeListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
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.sep1 = self.menu.addAction(u'')
self.sep1.setSeparator(True)
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'))
2009-09-13 15:14:45 +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)
2009-09-13 15:14:45 +00:00
#Variables
self.themelist = []
self.path = AppLocation.get_section_data_path(self.settingsSection)
2009-09-13 15:14:45 +00:00
self.checkThemesExists(self.path)
self.thumbPath = os.path.join(self.path, u'thumbnails')
self.checkThemesExists(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
2010-07-16 12:31:47 +00:00
self.editingDefault = False
2009-09-13 15:14:45 +00:00
# Last little bits of setting up
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):
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
2009-10-10 18:36:58 +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()
2009-10-17 18:20:07 +00:00
self.saveThemeName = u''
2010-10-17 18:58:42 +00:00
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
"""
item = self.themeListWidget.currentItem()
2010-09-26 07:39:50 +00:00
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
2010-09-26 06:20:24 +00:00
self.fileRenameForm.FileNameEdit.setText(oldThemeName)
2010-09-26 07:39:50 +00:00
self.saveThemeName = u''
2010-09-26 06:20:24 +00:00
if self.fileRenameForm.exec_():
2010-09-26 07:39:50 +00:00
newThemeName = unicode(self.fileRenameForm.FileNameEdit.text())
oldThemeData = self.getThemeData(oldThemeName)
self.deleteTheme(oldThemeName)
self.cloneThemeData(oldThemeData, 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-09-26 06:20:24 +00:00
self.fileRenameForm.FileNameEdit.setText(oldThemeName)
2010-09-26 07:39:50 +00:00
self.saveThemeName = u''
2010-09-26 06:20:24 +00:00
if self.fileRenameForm.exec_():
2010-09-26 07:39:50 +00:00
newThemeName = unicode(self.fileRenameForm.FileNameEdit.text())
2010-10-03 07:42:02 +00:00
themeData = self.getThemeData(oldThemeName)
self.cloneThemeData(themeData, newThemeName)
2010-09-26 07:39:50 +00:00
self.loadThemes()
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')
2010-10-03 07:42:02 +00:00
themeData.new_document(newThemeName)
themeData.build_xml_from_attrs()
2010-09-26 07:39:50 +00:00
save_to = None
2010-10-03 07:42:02 +00:00
save_from = None
if themeData.background_type == u'image':
save_to = os.path.join(self.path, newThemeName,
os.path.split(unicode(themeData.background_filename))[1])
save_from = themeData.background_filename
theme = themeData.extract_xml()
2010-10-24 18:20:23 +00:00
pretty_theme = themeData.extract_formatted_xml()
2010-09-26 07:39:50 +00:00
self.saveTheme(newThemeName, theme, pretty_theme, save_from, save_to)
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())
if themeName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.editingDefault = True
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
2009-11-03 18:14:25 +00:00
self.saveThemeName = unicode(
item.data(QtCore.Qt.UserRole).toString())
2010-10-17 18:58:42 +00:00
self.themeForm.theme = theme
self.themeForm.exec_()
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
"""
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())
if check_item_selected(self.themeListWidget,
translate('OpenLP.ThemeManager',
2010-06-24 15:50:40 +00:00
'You must select a theme to delete.')):
item = self.themeListWidget.currentItem()
2009-09-13 15:14:45 +00:00
theme = unicode(item.text())
2010-07-15 16:36:09 +00:00
# confirm deletion
answer = QtGui.QMessageBox.question(self,
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
2010-11-05 19:20:41 +00:00
unicode(translate('OpenLP.ThemeManager', 'Delete %s theme?'))
% theme,
2010-07-17 19:53:17 +00:00
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No), QtGui.QMessageBox.No)
2010-07-15 16:36:09 +00:00
if answer == QtGui.QMessageBox.No:
return
2009-09-13 15:14:45 +00:00
# should be the same unless default
if theme != unicode(item.data(QtCore.Qt.UserRole).toString()):
2010-06-18 23:18:08 +00:00
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
translate('OpenLP.ThemeManager',
2010-07-31 02:06:44 +00:00
'You are unable to delete the default theme.'))
2009-09-13 15:14:45 +00:00
else:
for plugin in self.parent.plugin_manager.plugins:
if plugin.usesTheme(theme):
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
2010-09-18 15:24:39 +00:00
'Theme %s is used in the %s plugin.')) % \
2010-06-18 23:18:08 +00:00
(theme, plugin.name))
return
if unicode(self.serviceComboBox.currentText()) == theme:
2010-06-10 00:03:38 +00:00
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
2010-09-18 15:24:39 +00:00
'Theme %s is used by the service manager.')) % theme)
return
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),
translate('OpenLP.ThemeManager', 'Theme (*.*)'))
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``
Name of the theme to load from file
"""
2009-09-13 15:14:45 +00:00
log.debug(u'getthemedata for theme %s', themename)
xml_file = os.path.join(self.path, unicode(themename),
unicode(themename) + u'.xml')
xml = get_text_file_string(xml_file)
2009-11-06 02:12:56 +00:00
if not xml:
2010-10-11 16:14:36 +00:00
return self.baseTheme()
else:
return self.createThemeFromXml(xml, self.path)
2009-09-13 15:14:45 +00:00
def checkThemesExists(self, dir):
2010-06-10 01:57:59 +00:00
"""
Check a theme directory exists and if not create it
``dir``
Theme directory to make sure exists
"""
2009-09-13 15:14:45 +00:00
log.debug(u'check themes')
2009-11-03 15:13:52 +00:00
if not os.path.exists(dir):
2009-09-13 15:14:45 +00:00
os.mkdir(dir)
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)
2009-11-07 00:00:36 +00:00
if not os.path.exists(theme_dir):
os.mkdir(os.path.join(dir, osfile))
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])
if not os.path.exists(theme_dir):
os.mkdir(os.path.join(dir, names[0]))
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-11-16 20:24:21 +00:00
theme = self.createThemeFromXml(filexml, self.path)
self.generateAndSaveImage(dir, themename, theme)
else:
2010-06-18 23:18:08 +00:00
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
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):
2010-06-18 23:18:08 +00:00
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
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-09-11 11:11:19 +00:00
return self.migrateVersion122(xml_data)
2009-09-13 15:14:45 +00:00
2010-06-10 13:20:43 +00:00
def migrateVersion122(self, xml_data):
2009-09-13 15:14:45 +00:00
"""
2010-06-10 01:57:59 +00:00
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
2009-09-13 15:14:45 +00:00
"""
theme = Theme(xml_data)
newtheme = ThemeXML()
2010-11-16 20:24:21 +00:00
newtheme.theme_name = theme.Name
2009-09-13 15:14:45 +00:00
if theme.BackgroundType == 0:
2010-11-17 18:35:04 +00:00
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Solid)
newtheme.background_color = \
unicode(theme.BackgroundParameter1.name())
2009-09-13 15:14:45 +00:00
elif theme.BackgroundType == 1:
2010-11-17 18:35:04 +00:00
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Gradient)
newtheme.background_direction = \
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
2009-09-13 15:14:45 +00:00
if theme.BackgroundParameter3.name() == 1:
2010-11-18 17:46:47 +00:00
newtheme.background_direction = \
2010-11-17 18:35:04 +00:00
BackgroundGradientType. \
to_string(BackgroundGradientType.Horizontal)
newtheme.background_start_color = \
unicode(theme.BackgroundParameter1.name())
newtheme.background_end_color = \
unicode(theme.BackgroundParameter2.name())
2009-09-13 15:14:45 +00:00
else:
2010-11-17 18:35:04 +00:00
newtheme.background_type = \
BackgroundType.to_string(BackgroundType.Image)
2010-11-16 20:24:21 +00:00
newtheme.background_filename = unicode(theme.BackgroundParameter1)
2010-11-17 18:35:04 +00:00
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
2009-09-13 15:14:45 +00:00
if theme.Shadow == 1:
2010-11-17 18:35:04 +00:00
newtheme.font_main_shadow = True
newtheme.font_main_shadow_color = unicode(theme.ShadowColor.name())
2009-09-13 15:14:45 +00:00
if theme.Outline == 1:
2010-11-17 18:35:04 +00:00
newtheme.font_main_outline = True
newtheme.font_main_outline_color = unicode(theme.OutlineColor.name())
2010-02-22 15:53:23 +00:00
vAlignCorrection = 0
if theme.VerticalAlign == 2:
vAlignCorrection = 1
elif theme.VerticalAlign == 1:
vAlignCorrection = 2
2010-11-16 20:24:21 +00:00
newtheme.display_horizontal_align = theme.HorizontalAlign
newtheme.display_vertical_align = vAlignCorrection
2009-09-13 15:14:45 +00:00
return newtheme.extract_xml()
2010-11-05 19:20:41 +00:00
def saveTheme(self, theme, image_from, image_to):
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)
2009-11-03 15:13:52 +00:00
if not os.path.exists(theme_dir):
2009-09-13 15:14:45 +00:00
os.mkdir(os.path.join(self.path, name))
theme_file = os.path.join(theme_dir, name + u'.xml')
log.debug(theme_file)
editedServiceTheme = False
2009-09-13 15:14:45 +00:00
result = QtGui.QMessageBox.Yes
2009-10-17 18:20:07 +00:00
if self.saveThemeName != name:
if os.path.exists(theme_file):
2010-06-18 23:18:08 +00:00
result = QtGui.QMessageBox.question(self,
translate('OpenLP.ThemeManager', 'Theme Exists'),
translate('OpenLP.ThemeManager',
'A theme with this name already '
2010-09-01 06:19:45 +00:00
'exists. Would you like to overwrite it?'),
2009-10-17 18:20:07 +00:00
(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)
2009-09-13 15:14:45 +00:00
if result == QtGui.QMessageBox.Yes:
# Save the theme, overwriting the existing theme if necessary.
2010-07-12 22:32:46 +00:00
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')
2009-11-07 00:00:36 +00:00
outfile = None
try:
outfile = open(theme_file, u'w')
outfile.write(theme_pretty_xml)
2010-05-27 16:00:51 +00:00
except IOError:
2009-11-07 00:00:36 +00:00
log.exception(u'Saving theme to file failed')
finally:
if outfile:
outfile.close()
2009-11-03 18:14:25 +00:00
if image_from and image_from != image_to:
2009-11-07 00:00:36 +00:00
try:
encoding = get_filesystem_encoding()
shutil.copyfile(
unicode(image_from).encode(encoding),
unicode(image_to).encode(encoding))
2010-05-27 16:00:51 +00:00
except IOError:
2009-11-07 00:00:36 +00:00
log.exception(u'Failed to save theme image')
2010-11-05 19:20:41 +00:00
self.generateAndSaveImage(self.path, name, theme)
2009-09-13 15:14:45 +00:00
self.loadThemes()
# Check if we need to set a new service theme
if editedServiceTheme:
newThemeIndex = self.serviceComboBox.findText(name)
if newThemeIndex != -1:
self.serviceComboBox.setCurrentIndex(newThemeIndex)
if self.editingDefault:
if self.saveThemeName != name:
newThemeItem = self.themeListWidget.findItems(name,
QtCore.Qt.MatchExactly)[0]
newThemeIndex = self.themeListWidget.indexFromItem(
newThemeItem).row()
self.global_theme = unicode(
self.themeListWidget.item(newThemeIndex).text())
newName = unicode(translate('OpenLP.ThemeManager',
'%s (default)')) % self.global_theme
self.themeListWidget.item(newThemeIndex).setText(newName)
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global',
self.global_theme)
2010-07-16 12:31:47 +00:00
self.editingDefault = False
self.pushThemes()
2010-11-06 07:58:46 +00:00
return True
2009-09-13 15:14:45 +00:00
else:
# Don't close the dialog - allow the user to change the name of
# the theme or to cancel the theme dialog completely.
return False
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)
2010-11-23 20:48:55 +00:00
def generateImage(self, themedata, forcePage=False):
2009-09-13 15:14:45 +00:00
"""
Call the RenderManager to build a Sample Image
"""
2010-07-26 15:51:18 +00:00
log.debug(u'generateImage \n%s ', themedata)
2010-11-23 20:48:55 +00:00
return self.parent.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
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
2009-11-06 02:12:56 +00:00
def createThemeFromXml(self, theme_xml, path):
2010-06-10 19:45:02 +00:00
"""
Return a theme object using information parsed from XML
``theme_xml``
The XML data to load into the theme
"""
2009-11-06 02:12:56 +00:00
theme = ThemeXML()
theme.parse(theme_xml)
theme.extend_image_filename(path)
return theme