openlp/openlp/core/ui/thememanager.py

855 lines
39 KiB
Python
Raw Normal View History

2009-09-13 15:14:45 +00:00
# -*- coding: utf-8 -*-
2012-12-29 15:25:29 +00:00
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2009-09-13 15:14:45 +00:00
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2012-12-29 20:56:56 +00:00
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
2012-11-11 21:16:14 +00:00
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
2012-10-21 13:16:22 +00:00
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
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 #
###############################################################################
2013-02-01 21:34:23 +00:00
"""
The Theme Manager manages adding, deleteing and modifying of themes.
"""
2009-09-13 15:14:45 +00:00
import os
import zipfile
import shutil
import logging
import re
2009-09-13 15:14:45 +00:00
from xml.etree.ElementTree import ElementTree, XML
from PyQt4 import QtCore, QtGui
2013-02-27 11:04:05 +00:00
from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \
build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb
2012-12-29 15:25:29 +00:00
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
from openlp.core.theme import Theme
from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.utils import AppLocation, delete_file, locale_compare, get_filesystem_encoding
2009-09-13 15:14:45 +00:00
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
2013-02-01 21:34:23 +00:00
2009-09-13 15:14:45 +00:00
class ThemeManager(QtGui.QWidget):
"""
Manages the orders of Theme.
"""
2013-01-24 06:00:51 +00:00
def __init__(self, parent=None):
2013-02-01 21:34:23 +00:00
"""
Constructor
"""
2009-09-13 15:14:45 +00:00
QtGui.QWidget.__init__(self, parent)
2013-01-22 21:09:43 +00:00
Registry().register(u'theme_manager', self)
2013-02-21 21:18:26 +00:00
Registry().register_function(u'bootstrap_initialise', self.load_first_time_themes)
self.settingsSection = u'themes'
self.themeForm = ThemeForm(self)
self.fileRenameForm = FileRenameForm()
# 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.setObjectName(u'toolbar')
2013-03-07 06:48:09 +00:00
self.toolbar.add_toolbar_action(u'newTheme',
text=UiStrings().NewTheme, icon=u':/themes/theme_new.png',
tooltip=translate('OpenLP.ThemeManager', 'Create a new theme.'),
triggers=self.onAddTheme)
2013-03-07 06:48:09 +00:00
self.toolbar.add_toolbar_action(u'editTheme',
text=translate('OpenLP.ThemeManager', 'Edit Theme'),
icon=u':/themes/theme_edit.png',
tooltip=translate('OpenLP.ThemeManager', 'Edit a theme.'),
triggers=self.on_edit_theme)
2013-03-07 06:48:09 +00:00
self.deleteToolbarAction = self.toolbar.add_toolbar_action(u'delete_theme',
text=translate('OpenLP.ThemeManager', 'Delete Theme'),
icon=u':/general/general_delete.png',
tooltip=translate('OpenLP.ThemeManager', 'Delete a theme.'),
triggers=self.on_delete_theme)
self.toolbar.addSeparator()
2013-03-07 06:48:09 +00:00
self.toolbar.add_toolbar_action(u'importTheme',
text=translate('OpenLP.ThemeManager', 'Import Theme'),
icon=u':/general/general_import.png',
tooltip=translate('OpenLP.ThemeManager', 'Import a theme.'),
triggers=self.on_import_theme)
2013-03-07 06:48:09 +00:00
self.toolbar.add_toolbar_action(u'exportTheme',
text=translate('OpenLP.ThemeManager', 'Export Theme'),
icon=u':/general/general_export.png',
tooltip=translate('OpenLP.ThemeManager', 'Export a theme.'),
triggers=self.on_export_theme)
self.layout.addWidget(self.toolbar)
self.theme_widget = QtGui.QWidgetAction(self.toolbar)
self.theme_widget.setObjectName(u'theme_widget')
# create theme manager list
self.theme_list_widget = QtGui.QListWidget(self)
self.theme_list_widget.setAlternatingRowColors(True)
self.theme_list_widget.setIconSize(QtCore.QSize(88, 50))
self.theme_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.theme_list_widget.setObjectName(u'theme_list_widget')
self.layout.addWidget(self.theme_list_widget)
2013-02-14 21:50:10 +00:00
self.theme_list_widget.customContextMenuRequested.connect(self.context_menu)
# build the context menu
self.menu = QtGui.QMenu()
self.edit_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Edit Theme'),
icon=u':/themes/theme_edit.png', triggers=self.on_edit_theme)
self.copy_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Copy Theme'),
icon=u':/themes/theme_edit.png', triggers=self.on_copy_theme)
self.rename_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Rename Theme'),
icon=u':/themes/theme_edit.png', triggers=self.on_rename_theme)
self.delete_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Delete Theme'),
icon=u':/general/general_delete.png', triggers=self.on_delete_theme)
self.menu.addSeparator()
self.global_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', 'Set As &Global Default'),
icon=u':/general/general_export.png',
2013-02-05 21:42:15 +00:00
triggers=self.change_global_from_screen)
self.exportAction = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Export Theme'),
icon=u':/general/general_export.png', triggers=self.on_export_theme)
2010-12-27 10:18:09 +00:00
# Signals
2013-02-14 21:50:10 +00:00
self.theme_list_widget.doubleClicked.connect(self.change_global_from_screen)
self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
2013-02-04 19:25:12 +00:00
Registry().register_function(u'theme_update_global', self.change_global_from_tab)
2013-02-06 21:52:15 +00:00
Registry().register_function(u'config_updated', self.config_updated)
2010-12-27 10:18:09 +00:00
# Variables
self.theme_list = []
self.path = AppLocation.get_section_data_path(self.settingsSection)
2011-01-09 08:17:17 +00:00
check_directory_exists(self.path)
2012-06-27 17:00:02 +00:00
self.thumbPath = os.path.join(self.path, u'thumbnails')
check_directory_exists(self.thumbPath)
2010-10-17 18:58:42 +00:00
self.themeForm.path = self.path
2012-06-27 17:00:02 +00:00
self.oldBackgroundImage = None
self.badV1NameChars = re.compile(r'[%+\[\]]')
2009-09-13 15:14:45 +00:00
# Last little bits of setting up
self.config_updated()
2010-12-27 10:18:09 +00:00
def config_updated(self):
2010-12-27 10:18:09 +00:00
"""
Triggered when Config dialog is updated.
"""
2013-02-05 21:42:15 +00:00
log.debug(u'config_updated')
self.global_theme = Settings().value(self.settingsSection + u'/global theme')
2009-09-13 15:14:45 +00:00
def check_list_state(self, item):
"""
If Default theme selected remove delete button.
"""
if item is None:
return
2012-05-19 15:10:05 +00:00
real_theme_name = item.data(QtCore.Qt.UserRole)
2012-05-17 18:57:01 +00:00
theme_name = item.text()
2011-01-11 17:44:13 +00:00
# If default theme restrict actions
if real_theme_name == theme_name:
self.deleteToolbarAction.setVisible(True)
else:
self.deleteToolbarAction.setVisible(False)
def context_menu(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.theme_list_widget.itemAt(point)
if item is None:
return
2012-05-19 15:10:05 +00:00
real_theme_name = item.data(QtCore.Qt.UserRole)
2012-03-10 08:22:52 +00:00
theme_name = unicode(item.text())
2012-06-27 17:00:02 +00:00
visible = real_theme_name == theme_name
self.delete_action.setVisible(visible)
self.rename_action.setVisible(visible)
self.global_action.setVisible(visible)
self.menu.exec_(self.theme_list_widget.mapToGlobal(point))
def change_global_from_tab(self, theme_name):
2010-06-10 01:57:59 +00:00
"""
Change the global theme when it is changed through the Themes settings
tab
"""
log.debug(u'change_global_from_tab %s', theme_name)
2013-02-02 21:16:42 +00:00
for count in range(0, self.theme_list_widget.count()):
2010-08-28 15:49:51 +00:00
# reset the old name
item = self.theme_list_widget.item(count)
old_name = item.text()
2012-05-19 15:10:05 +00:00
new_name = item.data(QtCore.Qt.UserRole)
if old_name != new_name:
self.theme_list_widget.item(count).setText(new_name)
2010-08-28 15:49:51 +00:00
# Set the new name
if theme_name == new_name:
2012-12-29 15:25:29 +00:00
name = translate('OpenLP.ThemeManager', '%s (default)') % new_name
self.theme_list_widget.item(count).setText(name)
2013-02-05 21:42:15 +00:00
self.deleteToolbarAction.setVisible(item not in self.theme_list_widget.selectedItems())
2009-09-13 15:14:45 +00:00
2013-02-05 21:42:15 +00:00
def change_global_from_screen(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
"""
2013-02-05 21:42:15 +00:00
log.debug(u'change_global_from_screen %s', index)
selected_row = self.theme_list_widget.currentRow()
2013-02-02 21:16:42 +00:00
for count in range(0, self.theme_list_widget.count()):
item = self.theme_list_widget.item(count)
old_name = item.text()
2010-08-28 15:49:51 +00:00
# reset the old name
2012-05-19 15:10:05 +00:00
if old_name != item.data(QtCore.Qt.UserRole):
self.theme_list_widget.item(count).setText(item.data(QtCore.Qt.UserRole))
2010-08-28 15:49:51 +00:00
# Set the new name
2009-10-10 18:36:58 +00:00
if count == selected_row:
self.global_theme = self.theme_list_widget.item(count).text()
2012-12-29 15:25:29 +00:00
name = translate('OpenLP.ThemeManager', '%s (default)') % self.global_theme
self.theme_list_widget.item(count).setText(name)
2012-12-29 15:25:29 +00:00
Settings().setValue(self.settingsSection + u'/global theme', self.global_theme)
2013-02-03 21:46:56 +00:00
Registry().execute(u'theme_update_global', self.global_theme)
self._push_themes()
2009-09-13 15:14:45 +00:00
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()
theme.set_default_header_footer()
2010-10-17 18:58:42 +00:00
self.themeForm.theme = theme
self.themeForm.exec_()
self.load_themes()
2009-09-13 15:14:45 +00:00
def on_rename_theme(self):
2010-09-26 06:20:24 +00:00
"""
Renames an existing theme to a new name
"""
2012-12-29 15:25:29 +00:00
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
translate('OpenLP.ThemeManager', 'Rename Confirmation'),
translate('OpenLP.ThemeManager', 'Rename %s theme?'), False, False):
item = self.theme_list_widget.currentItem()
2012-05-19 15:10:05 +00:00
old_theme_name = item.data(QtCore.Qt.UserRole)
2013-03-01 11:12:23 +00:00
self.fileRenameForm.file_name_edit.setText(old_theme_name)
if self.fileRenameForm.exec_():
2013-03-01 11:12:23 +00:00
new_theme_name = self.fileRenameForm.file_name_edit.text()
if old_theme_name == new_theme_name:
return
if self.check_if_theme_exists(new_theme_name):
old_theme_data = self.get_theme_data(old_theme_name)
self.cloneThemeData(old_theme_data, new_theme_name)
self.delete_theme(old_theme_name)
2013-01-22 21:09:43 +00:00
for plugin in self.plugin_manager.plugins:
2013-02-19 21:23:56 +00:00
if plugin.uses_theme(old_theme_name):
plugin.rename_theme(old_theme_name, new_theme_name)
2013-01-22 21:09:43 +00:00
self.renderer.update_theme(new_theme_name, old_theme_name)
self.load_themes()
2010-09-26 06:20:24 +00:00
def on_copy_theme(self):
2010-09-26 06:20:24 +00:00
"""
Copies an existing theme to a new name
"""
item = self.theme_list_widget.currentItem()
2012-05-19 15:10:05 +00:00
old_theme_name = item.data(QtCore.Qt.UserRole)
2013-03-01 11:12:23 +00:00
self.fileRenameForm.file_name_edit.setText(translate('OpenLP.ThemeManager',
2012-05-17 18:57:01 +00:00
'Copy of %s', 'Copy of <theme name>') % old_theme_name)
2010-12-24 08:07:26 +00:00
if self.fileRenameForm.exec_(True):
2013-03-01 11:12:23 +00:00
new_theme_name = self.fileRenameForm.file_name_edit.text()
if self.check_if_theme_exists(new_theme_name):
theme_data = self.get_theme_data(old_theme_name)
self.cloneThemeData(theme_data, new_theme_name)
2010-09-26 07:39:50 +00:00
def cloneThemeData(self, theme_data, new_theme_name):
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')
save_to = None
save_from = None
if theme_data.background_type == u'image':
save_to = os.path.join(self.path, new_theme_name, os.path.split(unicode(theme_data.background_filename))[1])
save_from = theme_data.background_filename
theme_data.theme_name = new_theme_name
theme_data.extend_image_filename(self.path)
self.save_theme(theme_data, save_from, save_to)
self.load_themes()
2010-09-26 06:20:24 +00:00
def on_edit_theme(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.theme_list_widget,
2012-12-29 15:25:29 +00:00
translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
item = self.theme_list_widget.currentItem()
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
2010-07-12 22:32:46 +00:00
if theme.background_type == u'image':
2012-06-27 17:00:02 +00:00
self.oldBackgroundImage = theme.background_filename
2010-10-17 18:58:42 +00:00
self.themeForm.theme = theme
self.themeForm.exec_(True)
2012-06-27 17:00:02 +00:00
self.oldBackgroundImage = None
2013-01-22 21:09:43 +00:00
self.renderer.update_theme(theme.theme_name)
self.load_themes()
2009-09-13 15:14:45 +00:00
def on_delete_theme(self):
2010-06-10 01:57:59 +00:00
"""
Delete a theme
"""
2012-12-29 15:25:29 +00:00
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
item = self.theme_list_widget.currentItem()
2012-05-17 18:57:01 +00:00
theme = item.text()
row = self.theme_list_widget.row(item)
self.theme_list_widget.takeItem(row)
self.delete_theme(theme)
2013-01-22 21:09:43 +00:00
self.renderer.update_theme(theme, only_delete=True)
2011-02-05 20:10:08 +00:00
# As we do not reload the themes, push out the change. Reload the
# list as the internal lists and events need to be triggered.
self._push_themes()
def delete_theme(self, theme):
"""
Delete a theme.
``theme``
The theme to delete.
"""
self.theme_list.remove(theme)
2011-06-12 15:17:01 +00:00
thumb = u'%s.png' % theme
2011-01-14 18:58:47 +00:00
delete_file(os.path.join(self.path, thumb))
2012-06-27 17:00:02 +00:00
delete_file(os.path.join(self.thumbPath, thumb))
try:
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
2013-02-01 20:40:23 +00:00
except OSError, shutil.Error:
2010-07-21 09:52:00 +00:00
log.exception(u'Error deleting theme %s', theme)
2009-09-13 15:14:45 +00:00
def on_export_theme(self):
"""
Export the theme in a zip file
"""
item = self.theme_list_widget.currentItem()
if item is None:
2012-12-29 15:25:29 +00:00
critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
return
2012-05-19 15:10:05 +00:00
theme = item.data(QtCore.Qt.UserRole)
path = QtGui.QFileDialog.getExistingDirectory(self,
2012-05-17 18:57:01 +00:00
translate('OpenLP.ThemeManager', 'Save Theme - (%s)') % theme,
Settings().value(self.settingsSection + u'/last directory export'))
2013-02-03 19:23:12 +00:00
self.application.set_busy_cursor()
2009-11-07 00:00:36 +00:00
if path:
Settings().setValue(self.settingsSection + u'/last directory export', path)
theme_path = os.path.join(path, theme + u'.otz')
2013-02-01 21:34:23 +00:00
theme_zip = None
2009-11-07 00:00:36 +00:00
try:
2013-02-01 21:34:23 +00:00
theme_zip = zipfile.ZipFile(theme_path, u'w')
2009-11-07 00:00:36 +00:00
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
2013-02-01 21:34:23 +00:00
theme_zip.write(
os.path.join(source, name).encode(u'utf-8'), os.path.join(theme, name).encode(u'utf-8')
2013-02-01 21:34:23 +00:00
)
QtGui.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
2012-12-29 15:25:29 +00:00
translate('OpenLP.ThemeManager', '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')
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
2012-12-29 15:25:29 +00:00
translate('OpenLP.ThemeManager', 'Your theme could not be exported due to an error.'))
2009-11-07 00:00:36 +00:00
finally:
2013-02-01 21:34:23 +00:00
if theme_zip:
theme_zip.close()
2013-02-03 19:23:12 +00:00
self.application.set_normal_cursor()
2009-09-13 15:14:45 +00:00
def on_import_theme(self):
2010-06-10 01:57:59 +00:00
"""
Opens a file dialog to select the theme file(s) to import before
2011-02-25 17:05:01 +00:00
attempting to extract OpenLP themes from those files. This process
2010-06-10 01:57:59 +00:00
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'),
Settings().value(self.settingsSection + u'/last directory import'),
2012-12-29 15:25:29 +00:00
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
2009-09-13 15:14:45 +00:00
log.info(u'New Themes %s', unicode(files))
if not files:
return
2013-02-03 19:23:12 +00:00
self.application.set_busy_cursor()
for file_name in files:
Settings().setValue(self.settingsSection + u'/last directory import', unicode(file_name))
self.unzip_theme(file_name, self.path)
self.load_themes()
2013-02-03 19:23:12 +00:00
self.application.set_normal_cursor()
2009-09-13 15:14:45 +00:00
2013-02-19 19:50:14 +00:00
def load_first_time_themes(self):
"""
Imports any themes on start up and makes sure there is at least one theme
"""
self.application.set_busy_cursor()
2013-03-01 17:45:17 +00:00
files = AppLocation.get_files(self.settingsSection, u'.otz')
2013-02-19 19:50:14 +00:00
for theme_file in files:
theme_file = os.path.join(self.path, theme_file)
self.unzip_theme(theme_file, self.path)
delete_file(theme_file)
2013-03-04 19:35:33 +00:00
files = AppLocation.get_files(self.settingsSection, u'.png')
2013-02-19 19:50:14 +00:00
# No themes have been found so create one
if not files:
theme = ThemeXML()
theme.theme_name = UiStrings().Default
self._write_theme(theme, None, None)
Settings().setValue(self.settingsSection + u'/global theme', theme.theme_name)
self.config_updated()
self.application.set_normal_cursor()
self.load_themes()
def load_themes(self):
2009-09-13 15:14:45 +00:00
"""
2013-02-19 19:50:14 +00:00
Loads the theme lists and triggers updates across 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.theme_list = []
self.theme_list_widget.clear()
files = AppLocation.get_files(self.settingsSection, u'.png')
# Sort the themes by its name considering language specific
files.sort(key=lambda file_name: unicode(file_name), cmp=locale_compare)
2011-03-20 07:37:44 +00:00
# now process the file list of png files
for name in files:
# check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
text_name = os.path.splitext(name)[0]
if text_name == self.global_theme:
2012-12-29 15:25:29 +00:00
name = translate('OpenLP.ThemeManager', '%s (default)') % text_name
2011-03-20 07:37:44 +00:00
else:
name = text_name
2012-06-27 17:00:02 +00:00
thumb = os.path.join(self.thumbPath, u'%s.png' % text_name)
2011-03-20 07:37:44 +00:00
item_name = QtGui.QListWidgetItem(name)
2011-06-12 15:17:01 +00:00
if validate_thumb(theme, thumb):
2011-03-20 07:37:44 +00:00
icon = build_icon(thumb)
else:
2011-06-12 15:17:01 +00:00
icon = create_thumb(theme, thumb)
2011-03-20 07:37:44 +00:00
item_name.setIcon(icon)
2012-05-17 15:13:09 +00:00
item_name.setData(QtCore.Qt.UserRole, text_name)
self.theme_list_widget.addItem(item_name)
self.theme_list.append(text_name)
self._push_themes()
2009-09-13 15:14:45 +00:00
def _push_themes(self):
2010-06-10 01:57:59 +00:00
"""
Notify listeners that the theme list has been updated
"""
2013-02-03 21:46:56 +00:00
Registry().execute(u'theme_update_list', self.get_themes())
2009-09-13 15:14:45 +00:00
def get_themes(self):
2010-06-10 01:57:59 +00:00
"""
Return the list of loaded themes
"""
2013-02-05 21:42:15 +00:00
log.debug(u'get themes')
return self.theme_list
2009-09-13 15:14:45 +00:00
def get_theme_data(self, theme_name):
2010-06-10 01:57:59 +00:00
"""
Returns a theme object from an XML file
2012-03-10 08:22:52 +00:00
``theme_name``
2010-06-10 01:57:59 +00:00
Name of the theme to load from file
"""
2013-02-05 21:42:15 +00:00
log.debug(u'get theme data for theme %s', theme_name)
2013-01-20 10:01:51 +00:00
xml_file = os.path.join(self.path, unicode(theme_name), unicode(theme_name) + u'.xml')
xml = get_text_file_string(xml_file)
2009-11-06 02:12:56 +00:00
if not xml:
2012-06-23 17:21:27 +00:00
log.debug(u'No theme data - using default theme')
return ThemeXML()
2010-10-11 16:14:36 +00:00
else:
return self._create_theme_fom_Xml(xml, self.path)
2009-09-13 15:14:45 +00:00
def over_write_message_box(self, theme_name):
"""
2013-02-01 21:34:23 +00:00
Display a warning box to the user that a theme already exists
"""
ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
translate('OpenLP.ThemeManager',
2012-12-29 15:25:29 +00:00
'Theme %s already exists. Do you want to replace it?').replace('%s', theme_name),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
2012-03-10 08:22:52 +00:00
return ret == QtGui.QMessageBox.Yes
2009-09-13 15:14:45 +00:00
2013-02-02 21:16:42 +00:00
def unzip_theme(self, file_name, directory):
2009-09-13 15:14:45 +00:00
"""
Unzip the theme, remove the preview file if stored
Generate a new preview file. Check the XML theme version and upgrade if
2009-09-13 15:14:45 +00:00
necessary.
"""
2012-03-10 08:22:52 +00:00
log.debug(u'Unzipping theme %s', file_name)
file_name = unicode(file_name)
2013-02-01 21:34:23 +00:00
theme_zip = None
2012-03-10 08:22:52 +00:00
out_file = None
file_xml = None
2012-06-15 16:19:46 +00:00
abort_import = True
try:
2013-02-01 21:34:23 +00:00
theme_zip = zipfile.ZipFile(file_name)
xml_file = filter(lambda name: os.path.splitext(name)[1].lower() == u'.xml', theme_zip.namelist())
if len(xml_file) != 1:
log.exception(u'Theme contains "%s" XML files' % len(xml_file))
raise Exception('validation')
2013-02-01 21:34:23 +00:00
xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
v1_background = xml_tree.find(u'BackgroundType')
if v1_background is not None:
theme_name, file_xml, out_file, abort_import = self.unzip_version_122(
2013-02-01 21:34:23 +00:00
directory, theme_zip, xml_file[0], xml_tree, v1_background, out_file)
else:
2012-03-10 08:22:52 +00:00
theme_name = xml_tree.find(u'name').text.strip()
2013-02-01 21:34:23 +00:00
theme_folder = os.path.join(directory, theme_name)
2012-03-10 08:22:52 +00:00
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.over_write_message_box(theme_name):
2012-03-10 08:22:52 +00:00
abort_import = True
return
else:
2012-03-10 08:22:52 +00:00
abort_import = False
2013-02-01 21:34:23 +00:00
for name in theme_zip.namelist():
try:
uname = unicode(name, u'utf-8')
except UnicodeDecodeError:
2012-12-29 15:25:29 +00:00
log.exception(u'Theme file contains non utf-8 filename "%s"' %
name.decode(u'utf-8', u'replace'))
raise Exception(u'validation')
2012-02-26 21:09:22 +00:00
uname = uname.replace(u'/', os.path.sep)
2012-03-10 16:44:58 +00:00
split_name = uname.split(os.path.sep)
if split_name[-1] == u'' or len(split_name) == 1:
# is directory or preview file
continue
2013-02-01 21:34:23 +00:00
full_name = os.path.join(directory, uname)
check_directory_exists(os.path.dirname(full_name))
if os.path.splitext(uname)[1].lower() == u'.xml':
2013-02-01 21:34:23 +00:00
file_xml = unicode(theme_zip.read(name), u'utf-8')
out_file = open(full_name, u'w')
2012-03-10 08:22:52 +00:00
out_file.write(file_xml.encode(u'utf-8'))
else:
out_file = open(full_name, u'wb')
2013-02-01 21:34:23 +00:00
out_file.write(theme_zip.read(name))
2012-03-10 08:22:52 +00:00
out_file.close()
except (IOError, zipfile.BadZipfile):
2012-03-10 08:22:52 +00:00
log.exception(u'Importing theme from zip failed %s' % file_name)
raise Exception(u'validation')
except Exception as info:
if unicode(info) == u'validation':
critical_error_message_box(translate('OpenLP.ThemeManager',
2012-12-29 15:25:29 +00:00
'Validation Error'), translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
else:
raise
finally:
# Close the files, to be able to continue creating the theme.
2013-02-01 21:34:23 +00:00
if theme_zip:
theme_zip.close()
2012-03-10 08:22:52 +00:00
if out_file:
out_file.close()
if not abort_import:
# As all files are closed, we can create the Theme.
2012-03-10 08:22:52 +00:00
if file_xml:
theme = self._create_theme_fom_Xml(file_xml, self.path)
2013-02-02 21:16:42 +00:00
self.generate_and_save_image(directory, theme_name, theme)
2012-06-23 17:21:27 +00:00
# Only show the error message, when IOError was not raised (in
# this case the error message has already been shown).
2013-02-01 21:34:23 +00:00
elif theme_zip is not None:
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
2012-12-29 15:25:29 +00:00
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
log.exception(u'Theme file does not contain XML data %s' % file_name)
2009-09-13 15:14:45 +00:00
def unzip_version_122(self, dir_name, zip_file, xml_file, xml_tree, background, out_file):
"""
Unzip openlp.org 1.2x theme file and upgrade the theme xml. When calling
this method, please keep in mind, that some parameters are redundant.
"""
2012-03-10 08:22:52 +00:00
theme_name = xml_tree.find(u'Name').text.strip()
2012-06-27 17:00:02 +00:00
theme_name = self.badV1NameChars.sub(u'', theme_name)
theme_folder = os.path.join(dir_name, theme_name)
2012-03-10 08:22:52 +00:00
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.over_write_message_box(theme_name):
2012-03-10 16:14:07 +00:00
return '', '', '', True
themedir = os.path.join(dir_name, theme_name)
check_directory_exists(themedir)
file_xml = unicode(zip_file.read(xml_file), u'utf-8')
file_xml = self._migrate_version_122(file_xml)
2012-03-10 08:22:52 +00:00
out_file = open(os.path.join(themedir, theme_name + u'.xml'), u'w')
out_file.write(file_xml.encode(u'utf-8'))
out_file.close()
if background.text.strip() == u'2':
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
# image file has same extension and is in subfolder
2012-06-27 17:00:02 +00:00
image_file = filter(lambda name: os.path.splitext(name)[1].lower()
== os.path.splitext(image_name)[1].lower() and name.find(r'/'), zip_file.namelist())
2012-06-27 17:00:02 +00:00
if len(image_file) >= 1:
out_file = open(os.path.join(themedir, image_name), u'wb')
out_file.write(zip_file.read(image_file[0]))
2012-03-10 08:22:52 +00:00
out_file.close()
else:
2012-12-29 15:25:29 +00:00
log.exception(u'Theme file does not contain image file "%s"' % image_name.decode(u'utf-8', u'replace'))
raise Exception(u'validation')
2012-03-10 08:22:52 +00:00
return theme_name, file_xml, out_file, False
def check_if_theme_exists(self, theme_name):
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
2012-03-10 08:22:52 +00:00
``theme_name``
2010-12-28 10:56:19 +00:00
Name of the Theme to test
2010-12-27 10:18:09 +00:00
"""
2012-03-10 08:22:52 +00:00
theme_dir = os.path.join(self.path, theme_name)
2010-12-27 10:18:09 +00:00
if os.path.exists(theme_dir):
critical_error_message_box(
2011-01-15 19:24:50 +00:00
translate('OpenLP.ThemeManager', 'Validation Error'),
2012-12-29 15:25:29 +00:00
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 save_theme(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
"""
self._write_theme(theme, image_from, image_to)
2012-12-29 15:25:29 +00:00
if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_manager.update_image_border(theme.background_filename,
2012-07-01 18:41:59 +00:00
ImageSource.Theme, QtGui.QColor(theme.background_border_color))
self.image_manager.process_updates()
def _write_theme(self, theme, image_from, image_to):
"""
Writes the theme to the disk and handles the background image if
necessary
"""
2010-11-05 19:20:41 +00:00
name = theme.theme_name
theme_pretty_xml = theme.extract_formatted_xml()
log.debug(u'save_theme %s %s', name, theme_pretty_xml.decode(u'utf-8'))
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')
2012-06-27 17:00:02 +00:00
if self.oldBackgroundImage and image_to != self.oldBackgroundImage:
delete_file(self.oldBackgroundImage)
2012-03-10 08:22:52 +00:00
out_file = None
2010-12-27 10:18:09 +00:00
try:
2012-03-10 08:22:52 +00:00
out_file = open(theme_file, u'w')
out_file.write(theme_pretty_xml)
2010-12-27 10:18:09 +00:00
except IOError:
log.exception(u'Saving theme to file failed')
finally:
2012-03-10 08:22:52 +00:00
if out_file:
out_file.close()
if image_from and image_from != image_to:
2009-11-07 00:00:36 +00:00
try:
2010-12-27 10:18:09 +00:00
encoding = get_filesystem_encoding()
2012-12-29 15:25:29 +00:00
shutil.copyfile(unicode(image_from).encode(encoding), unicode(image_to).encode(encoding))
2013-02-01 20:40:23 +00:00
except IOError, shutil.Error:
2010-12-27 10:18:09 +00:00
log.exception(u'Failed to save theme image')
self.generate_and_save_image(self.path, name, theme)
2009-09-13 15:14:45 +00:00
2013-02-02 21:16:42 +00:00
def generate_and_save_image(self, directory, name, theme):
"""
2013-02-01 21:34:23 +00:00
Generate and save a preview image
"""
2013-02-02 21:16:42 +00:00
log.debug(u'generate_and_save_image %s %s', directory, name)
frame = self.generate_image(theme)
sample_path_name = os.path.join(self.path, name + u'.png')
if os.path.exists(sample_path_name):
os.unlink(sample_path_name)
frame.save(sample_path_name, u'png')
2012-06-27 17:00:02 +00:00
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
create_thumb(sample_path_name, thumb, False)
log.debug(u'Theme image written to %s', sample_path_name)
2009-09-13 15:14:45 +00:00
def update_preview_images(self):
"""
Called to update the themes' preview images.
"""
2013-02-05 21:42:15 +00:00
log.debug('update_preview_images')
self.main_window.displayProgressBar(len(self.theme_list))
for theme in self.theme_list:
2013-03-07 08:05:43 +00:00
self.main_window.increment_progress_bar()
self.generate_and_save_image(self.path, theme, self.get_theme_data(theme))
2013-01-24 06:00:51 +00:00
self.main_window.finishedProgressBar()
self.load_themes()
def generate_image(self, theme_data, forcePage=False):
2009-09-13 15:14:45 +00:00
"""
2011-03-28 18:56:39 +00:00
Call the renderer to build a Sample Image
``theme_data``
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'generate_image \n%s ', theme_data)
2013-01-22 21:09:43 +00:00
return self.renderer.generate_preview(theme_data, forcePage)
2009-09-13 15:14:45 +00:00
def get_preview_image(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
"""
log.debug(u'get_preview_image %s ', theme)
2009-09-13 15:14:45 +00:00
image = os.path.join(self.path, theme + u'.png')
return image
def _create_theme_fom_Xml(self, theme_xml, path):
2010-06-10 19:45:02 +00:00
"""
Return a theme object using information parsed from XML
``theme_xml``
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(theme_xml)
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, confirm=True):
"""
Check to see if theme has been selected and the destructive action
is allowed.
"""
self.global_theme = Settings().value(self.settingsSection + u'/global theme')
if check_item_selected(self.theme_list_widget, select_text):
item = self.theme_list_widget.currentItem()
2012-05-17 18:57:01 +00:00
theme = item.text()
# confirm deletion
2011-02-05 17:31:13 +00:00
if confirm:
2012-12-29 15:25:29 +00:00
answer = QtGui.QMessageBox.question(self, confirm_title, confirm_text % theme,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
2011-02-05 17:31:13 +00:00
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
# should be the same unless default
2012-05-19 15:10:05 +00:00
if theme != item.data(QtCore.Qt.UserRole):
critical_error_message_box(
2012-12-29 15:25:29 +00:00
message=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:
2013-01-22 21:09:43 +00:00
for plugin in self.plugin_manager.plugins:
2013-02-19 21:23:56 +00:00
if plugin.uses_theme(theme):
2012-12-29 15:25:29 +00:00
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
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
return False
2010-12-28 10:56:19 +00:00
def _migrate_version_122(self, xml_data):
2010-12-28 10:56:19 +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
"""
theme = Theme(xml_data)
new_theme = ThemeXML()
2012-06-27 17:00:02 +00:00
new_theme.theme_name = self.badV1NameChars.sub(u'', theme.Name)
if theme.BackgroundType == BackgroundType.Solid:
2012-12-29 15:25:29 +00:00
new_theme.background_type = BackgroundType.to_string(BackgroundType.Solid)
new_theme.background_color = unicode(theme.BackgroundParameter1.name())
elif theme.BackgroundType == BackgroundType.Horizontal:
2012-12-29 15:25:29 +00:00
new_theme.background_type = BackgroundType.to_string(BackgroundType.Gradient)
new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal)
2010-12-28 10:56:19 +00:00
if theme.BackgroundParameter3.name() == 1:
2012-12-29 15:25:29 +00:00
new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal)
new_theme.background_start_color = unicode(theme.BackgroundParameter1.name())
new_theme.background_end_color = unicode(theme.BackgroundParameter2.name())
elif theme.BackgroundType == BackgroundType.Image:
2012-12-29 15:25:29 +00:00
new_theme.background_type = BackgroundType.to_string(BackgroundType.Image)
new_theme.background_filename = unicode(theme.BackgroundParameter1)
elif theme.BackgroundType == BackgroundType.Transparent:
2012-12-29 15:25:29 +00:00
new_theme.background_type = BackgroundType.to_string(BackgroundType.Transparent)
new_theme.font_main_name = theme.FontName
new_theme.font_main_color = unicode(theme.FontColor.name())
new_theme.font_main_size = theme.FontProportion * 3
new_theme.font_footer_name = theme.FontName
new_theme.font_footer_color = unicode(theme.FontColor.name())
new_theme.font_main_shadow = False
2010-12-28 10:56:19 +00:00
if theme.Shadow == 1:
new_theme.font_main_shadow = True
new_theme.font_main_shadow_color = unicode(theme.ShadowColor.name())
2010-12-28 10:56:19 +00:00
if theme.Outline == 1:
new_theme.font_main_outline = True
2012-12-29 15:25:29 +00:00
new_theme.font_main_outline_color = unicode(theme.OutlineColor.name())
vAlignCorrection = VerticalType.Top
2010-12-28 10:56:19 +00:00
if theme.VerticalAlign == 2:
vAlignCorrection = VerticalType.Middle
2010-12-28 10:56:19 +00:00
elif theme.VerticalAlign == 1:
vAlignCorrection = VerticalType.Bottom
new_theme.display_horizontal_align = theme.HorizontalAlign
new_theme.display_vertical_align = vAlignCorrection
return new_theme.extract_xml()
2013-01-22 21:09:43 +00:00
def _get_renderer(self):
"""
Adds the Renderer to the class dynamically
"""
if not hasattr(self, u'_renderer'):
self._renderer = Registry().get(u'renderer')
return self._renderer
renderer = property(_get_renderer)
def _get_image_manager(self):
"""
Adds the image manager to the class dynamically
"""
if not hasattr(self, u'_image_manager'):
self._image_manager = Registry().get(u'image_manager')
return self._image_manager
image_manager = property(_get_image_manager)
def _get_plugin_manager(self):
"""
Adds the Renderer to the class dynamically
"""
if not hasattr(self, u'_plugin_manager'):
self._plugin_manager = Registry().get(u'plugin_manager')
return self._plugin_manager
2013-01-24 06:00:51 +00:00
plugin_manager = property(_get_plugin_manager)
def _get_main_window(self):
"""
Adds the main window to the class dynamically
"""
if not hasattr(self, u'_main_window'):
self._main_window = Registry().get(u'main_window')
return self._main_window
main_window = property(_get_main_window)
2013-02-03 09:07:31 +00:00
2013-02-03 19:23:12 +00:00
def _get_application(self):
2013-02-03 09:07:31 +00:00
"""
Adds the openlp to the class dynamically
"""
2013-02-03 19:23:12 +00:00
if not hasattr(self, u'_application'):
self._application = Registry().get(u'application')
return self._application
2013-02-03 09:07:31 +00:00
2013-02-05 08:05:28 +00:00
application = property(_get_application)