openlp/openlp/core/ui/thememanager.py

758 lines
36 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 #
# --------------------------------------------------------------------------- #
2013-12-24 08:56:50 +00:00
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 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
from xml.etree.ElementTree import ElementTree, XML
from PyQt4 import QtCore, QtGui
2013-12-23 07:03:56 +00:00
from openlp.core.common import Registry, AppLocation, Settings, OpenLPMixin, RegistryMixin, check_directory_exists, \
UiStrings, translate
2013-12-13 17:44:05 +00:00
from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, get_text_file_string, build_icon, \
2013-10-13 21:07:28 +00:00
check_item_selected, create_thumb, validate_thumb
2013-10-13 13:51:13 +00:00
from openlp.core.lib.theme import ThemeXML, BackgroundType
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
2013-10-13 13:51:13 +00:00
from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper
from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding
2009-09-13 15:14:45 +00:00
2010-02-27 15:31:23 +00:00
2013-12-23 07:03:56 +00:00
class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper):
2009-09-13 15:14:45 +00:00
"""
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
"""
2013-07-18 14:34:33 +00:00
super(ThemeManager, self).__init__(parent)
2013-08-31 18:17:38 +00:00
self.settings_section = 'themes'
2013-03-16 11:05:52 +00:00
self.theme_form = ThemeForm(self)
self.file_rename_form = FileRenameForm()
# start with the layout
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setMargin(0)
2013-08-31 18:17:38 +00:00
self.layout.setObjectName('layout')
self.toolbar = OpenLPToolbar(self)
2013-08-31 18:17:38 +00:00
self.toolbar.setObjectName('toolbar')
self.toolbar.add_toolbar_action('newTheme',
2013-12-23 07:03:56 +00:00
text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
tooltip=translate('OpenLP.ThemeManager', 'Create a new theme.'),
triggers=self.on_add_theme)
2013-08-31 18:17:38 +00:00
self.toolbar.add_toolbar_action('editTheme',
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', 'Edit Theme'),
icon=':/themes/theme_edit.png',
tooltip=translate('OpenLP.ThemeManager', 'Edit a theme.'),
triggers=self.on_edit_theme)
2013-08-31 18:17:38 +00:00
self.delete_toolbar_action = self.toolbar.add_toolbar_action('delete_theme',
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager',
'Delete Theme'),
icon=':/general/general_delete.png',
tooltip=translate('OpenLP.ThemeManager',
'Delete a theme.'),
triggers=self.on_delete_theme)
self.toolbar.addSeparator()
2013-08-31 18:17:38 +00:00
self.toolbar.add_toolbar_action('importTheme',
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', 'Import Theme'),
icon=':/general/general_import.png',
tooltip=translate('OpenLP.ThemeManager', 'Import a theme.'),
triggers=self.on_import_theme)
2013-08-31 18:17:38 +00:00
self.toolbar.add_toolbar_action('exportTheme',
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', 'Export Theme'),
icon=':/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)
2013-08-31 18:17:38 +00:00
self.theme_widget.setObjectName('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)
2013-08-31 18:17:38 +00:00
self.theme_list_widget.setObjectName('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,
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', '&Edit Theme'),
icon=':/themes/theme_edit.png', triggers=self.on_edit_theme)
self.copy_action = create_widget_action(self.menu,
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', '&Copy Theme'),
icon=':/themes/theme_edit.png', triggers=self.on_copy_theme)
self.rename_action = create_widget_action(self.menu,
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', '&Rename Theme'),
icon=':/themes/theme_edit.png', triggers=self.on_rename_theme)
self.delete_action = create_widget_action(self.menu,
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', '&Delete Theme'),
icon=':/general/general_delete.png', triggers=self.on_delete_theme)
self.menu.addSeparator()
self.global_action = create_widget_action(self.menu,
2013-12-23 07:03:56 +00:00
text=translate('OpenLP.ThemeManager', 'Set As &Global Default'),
icon=':/general/general_export.png',
triggers=self.change_global_from_screen)
2013-12-24 06:15:41 +00:00
self.export_action = create_widget_action(self.menu,
text=translate('OpenLP.ThemeManager', '&Export Theme'),
icon=':/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-08-31 18:17:38 +00:00
Registry().register_function('theme_update_global', self.change_global_from_tab)
2010-12-27 10:18:09 +00:00
# Variables
self.theme_list = []
2013-03-16 11:05:52 +00:00
self.old_background_image = None
2009-09-13 15:14:45 +00:00
2013-12-23 07:03:56 +00:00
def bootstrap_initialise(self):
"""
process the bootstrap initialise setup request
"""
self.log_debug('initialise called')
self.global_theme = Settings().value(self.settings_section + '/global theme')
self.build_theme_path()
self.load_first_time_themes()
def bootstrap_post_set_up(self):
"""
process the bootstrap post setup request
"""
self._push_themes()
def check_list_state(self, item, field=None):
"""
If Default theme selected remove delete button.
2013-12-23 07:03:56 +00:00
Note for some reason a dummy field is required. Nothing is passed!
"""
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:
2013-03-16 11:05:52 +00:00
self.delete_toolbar_action.setVisible(True)
else:
2013-03-16 11:05:52 +00:00
self.delete_toolbar_action.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)
2013-08-31 18:17:38 +00:00
theme_name = str(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))
2013-03-11 19:11:46 +00:00
def change_global_from_tab(self):
2010-06-10 01:57:59 +00:00
"""
2013-03-10 20:19:42 +00:00
Change the global theme when it is changed through the Themes settings tab
2010-06-10 01:57:59 +00:00
"""
2013-08-31 18:17:38 +00:00
self.global_theme = Settings().value(self.settings_section + '/global theme')
2013-12-23 07:03:56 +00:00
self.log_debug('change_global_from_tab %s' % self.global_theme)
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
2013-03-11 19:11:46 +00:00
if self.global_theme == 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-03-16 11:05:52 +00:00
self.delete_toolbar_action.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
"""
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)
2013-08-31 18:17:38 +00:00
Settings().setValue(self.settings_section + '/global theme', self.global_theme)
Registry().execute('theme_update_global')
self._push_themes()
2009-09-13 15:14:45 +00:00
2013-12-23 07:03:56 +00:00
def on_add_theme(self, field=None):
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()
2013-03-16 11:05:52 +00:00
self.theme_form.theme = theme
self.theme_form.exec_()
self.load_themes()
2009-09-13 15:14:45 +00:00
2013-12-23 07:03:56 +00:00
def on_rename_theme(self, field=None):
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.'),
2013-12-23 07:03:56 +00:00
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-16 11:05:52 +00:00
self.file_rename_form.file_name_edit.setText(old_theme_name)
if self.file_rename_form.exec_():
new_theme_name = self.file_rename_form.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)
2013-03-16 11:05:52 +00:00
self.clone_theme_data(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
2013-12-23 07:03:56 +00:00
def on_copy_theme(self, field=None):
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-16 11:05:52 +00:00
self.file_rename_form.file_name_edit.setText(translate('OpenLP.ThemeManager',
2013-12-23 07:03:56 +00:00
'Copy of %s', 'Copy of <theme name>') % old_theme_name)
2013-03-16 11:05:52 +00:00
if self.file_rename_form.exec_(True):
new_theme_name = self.file_rename_form.file_name_edit.text()
if self.check_if_theme_exists(new_theme_name):
theme_data = self.get_theme_data(old_theme_name)
2013-03-16 11:05:52 +00:00
self.clone_theme_data(theme_data, new_theme_name)
2010-09-26 07:39:50 +00:00
2013-03-16 11:05:52 +00:00
def clone_theme_data(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
"""
save_to = None
save_from = None
2013-08-31 18:17:38 +00:00
if theme_data.background_type == 'image':
save_to = os.path.join(self.path, new_theme_name, os.path.split(str(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
2013-12-23 07:03:56 +00:00
def on_edit_theme(self, field=None):
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,
2013-12-23 07:03:56 +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))
2013-08-31 18:17:38 +00:00
if theme.background_type == 'image':
2013-03-16 11:05:52 +00:00
self.old_background_image = theme.background_filename
self.theme_form.theme = theme
self.theme_form.exec_(True)
self.old_background_image = 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
2013-12-23 07:03:56 +00:00
def on_delete_theme(self, field=None):
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.'),
2013-12-23 07:03:56 +00:00
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)
2013-08-31 18:17:38 +00:00
thumb = '%s.png' % theme
2011-01-14 18:58:47 +00:00
delete_file(os.path.join(self.path, thumb))
2013-03-16 11:05:52 +00:00
delete_file(os.path.join(self.thumb_path, thumb))
try:
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
2013-10-13 13:51:13 +00:00
except OSError as os_error:
shutil.Error = os_error
2013-12-23 07:03:56 +00:00
self.log_exception('Error deleting theme %s', theme)
2009-09-13 15:14:45 +00:00
2013-12-23 07:03:56 +00:00
def on_export_theme(self, field=None):
"""
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,
2013-12-23 07:03:56 +00:00
translate('OpenLP.ThemeManager', 'Save Theme - (%s)') % theme,
Settings().value(self.settings_section +
'/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:
2013-08-31 18:17:38 +00:00
Settings().setValue(self.settings_section + '/last directory export', path)
theme_path = os.path.join(path, theme + '.otz')
2013-02-01 21:34:23 +00:00
theme_zip = None
2009-11-07 00:00:36 +00:00
try:
2013-08-31 18:17:38 +00:00
theme_zip = zipfile.ZipFile(theme_path, '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(
2013-08-31 18:17:38 +00:00
os.path.join(source, name).encode('utf-8'), os.path.join(theme, name).encode('utf-8')
2013-02-01 21:34:23 +00:00
)
QtGui.QMessageBox.information(self,
2013-12-23 07:03:56 +00:00
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
'Your theme has been successfully exported.'))
2010-05-27 16:00:51 +00:00
except (IOError, OSError):
2013-12-23 07:03:56 +00:00
self.log_exception('Export Theme Failed')
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
2013-12-23 07:03:56 +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
2013-12-23 07:03:56 +00:00
def on_import_theme(self, field=None):
2010-06-10 01:57:59 +00:00
"""
2013-03-16 11:05:52 +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-10 01:57:59 +00:00
"""
2013-08-22 20:20:02 +00:00
files = FileDialog.getOpenFileNames(self,
2013-12-23 07:03:56 +00:00
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
Settings().value(self.settings_section + '/last directory import'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
2013-12-24 06:15:41 +00:00
self.log_info('New Themes %s' % str(files))
if not files:
return
2013-02-03 19:23:12 +00:00
self.application.set_busy_cursor()
for file_name in files:
2013-08-31 18:17:38 +00:00
Settings().setValue(self.settings_section + '/last directory import', str(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-08-31 18:17:38 +00:00
files = AppLocation.get_files(self.settings_section, '.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-08-31 18:17:38 +00:00
files = AppLocation.get_files(self.settings_section, '.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)
2013-08-31 18:17:38 +00:00
Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
2013-02-19 19:50:14 +00:00
self.application.set_normal_cursor()
self.load_themes()
def load_themes(self):
2009-09-13 15:14:45 +00:00
"""
2013-10-23 19:25:46 +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.
"""
self.theme_list = []
self.theme_list_widget.clear()
2013-08-31 18:17:38 +00:00
files = AppLocation.get_files(self.settings_section, '.png')
# Sort the themes by its name considering language specific
2013-08-31 18:17:38 +00:00
files.sort(key=lambda file_name: get_locale_key(str(file_name)))
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
2013-08-31 18:17:38 +00:00
thumb = os.path.join(self.thumb_path, '%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-08-31 18:17:38 +00:00
Registry().execute('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
"""
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-12-23 07:03:56 +00:00
self.log_debug('get theme data for theme %s' % theme_name)
2013-08-31 18:17:38 +00:00
xml_file = os.path.join(self.path, str(theme_name), str(theme_name) + '.xml')
xml = get_text_file_string(xml_file)
2009-11-06 02:12:56 +00:00
if not xml:
2013-12-23 07:03:56 +00:00
self.log_debug('No theme data - using default theme')
return ThemeXML()
2010-10-11 16:14:36 +00:00
else:
2013-10-13 13:51:13 +00:00
return self._create_theme_from_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'),
2013-12-23 07:03:56 +00:00
translate('OpenLP.ThemeManager',
2013-12-24 06:15:41 +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.
"""
2013-12-23 07:03:56 +00:00
self.log_debug('Unzipping theme %s', file_name)
2013-08-31 18:17:38 +00:00
file_name = str(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)
2013-08-31 18:17:38 +00:00
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
if len(xml_file) != 1:
2013-12-23 07:03:56 +00:00
self.log_error('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()
2013-10-13 13:51:13 +00:00
theme_name = xml_tree.find('name').text.strip()
theme_folder = os.path.join(directory, theme_name)
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.over_write_message_box(theme_name):
abort_import = True
return
else:
2013-10-13 13:51:13 +00:00
abort_import = False
for name in theme_zip.namelist():
name = name.replace('/', os.path.sep)
split_name = name.split(os.path.sep)
if split_name[-1] == '' or len(split_name) == 1:
# is directory or preview file
continue
full_name = os.path.join(directory, name)
check_directory_exists(os.path.dirname(full_name))
if os.path.splitext(name)[1].lower() == '.xml':
file_xml = str(theme_zip.read(name), 'utf-8')
out_file = open(full_name, 'w')
out_file.write(file_xml)
else:
2013-10-13 13:51:13 +00:00
out_file = open(full_name, 'wb')
out_file.write(theme_zip.read(name))
out_file.close()
except (IOError, zipfile.BadZipfile):
2013-12-23 07:03:56 +00:00
self.log_exception('Importing theme from zip failed %s' % file_name)
2013-08-31 18:17:38 +00:00
raise Exception('validation')
except Exception as info:
2013-08-31 18:17:38 +00:00
if str(info) == 'validation':
2013-12-23 07:03:56 +00:00
critical_error_message_box(translate('OpenLP.ThemeManager', '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:
2013-10-13 13:51:13 +00:00
theme = self._create_theme_from_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.'))
2013-12-23 07:03:56 +00:00
self.log_error('Theme file does not contain XML data %s' % file_name)
2009-09-13 15:14:45 +00:00
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
"""
2013-12-24 06:15:41 +00:00
Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list
2009-09-13 15:14:45 +00:00
"""
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,
2013-12-23 07:03:56 +00:00
ImageSource.Theme,
QtGui.QColor(theme.background_border_color))
self.image_manager.process_updates()
def _write_theme(self, theme, image_from, image_to):
"""
2013-12-24 06:15:41 +00:00
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()
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)
2013-08-31 18:17:38 +00:00
theme_file = os.path.join(theme_dir, name + '.xml')
2013-03-16 11:05:52 +00:00
if self.old_background_image and image_to != self.old_background_image:
delete_file(self.old_background_image)
2012-03-10 08:22:52 +00:00
out_file = None
2010-12-27 10:18:09 +00:00
try:
2013-08-31 18:17:38 +00:00
out_file = open(theme_file, 'w')
2013-06-23 14:38:41 +00:00
out_file.write(theme_pretty_xml.decode('UTF-8'))
2010-12-27 10:18:09 +00:00
except IOError:
2013-12-23 07:03:56 +00:00
self.log_exception('Saving theme to file failed')
2010-12-27 10:18:09 +00:00
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()
2013-08-31 18:17:38 +00:00
shutil.copyfile(str(image_from).encode(encoding), str(image_to).encode(encoding))
except IOError as xxx_todo_changeme:
shutil.Error = xxx_todo_changeme
2013-12-23 07:03:56 +00:00
self.log_exception('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
"""
frame = self.generate_image(theme)
2013-08-31 18:17:38 +00:00
sample_path_name = os.path.join(self.path, name + '.png')
if os.path.exists(sample_path_name):
os.unlink(sample_path_name)
2013-08-31 18:17:38 +00:00
frame.save(sample_path_name, 'png')
thumb = os.path.join(self.thumb_path, '%s.png' % name)
create_thumb(sample_path_name, thumb, False)
2009-09-13 15:14:45 +00:00
def update_preview_images(self):
"""
Called to update the themes' preview images.
"""
2013-03-16 11:05:52 +00:00
self.main_window.display_progress_bar(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-03-16 11:05:52 +00:00
self.main_window.finished_progress_bar()
self.load_themes()
2013-10-23 19:25:46 +00:00
def generate_image(self, theme_data, force_page=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.
2013-10-23 19:25:46 +00:00
``force_page``
Flag to tell message lines per page need to be generated.
2009-09-13 15:14:45 +00:00
"""
2013-10-23 19:25:46 +00:00
return self.renderer.generate_preview(theme_data, force_page)
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
"""
2013-12-23 07:03:56 +00:00
return os.path.join(self.path, theme + '.png')
2009-09-13 15:14:45 +00:00
2013-10-13 13:51:13 +00:00
def _create_theme_from_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
2013-10-23 19:25:46 +00:00
def _validate_theme_action(self, select_text, confirm_title, confirm_text, test_plugin=True, confirm=True):
"""
2013-12-24 06:15:41 +00:00
Check to see if theme has been selected and the destructive action is allowed.
"""
2013-08-31 18:17:38 +00:00
self.global_theme = Settings().value(self.settings_section + '/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,
2013-12-23 07:03:56 +00:00
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
2011-02-05 17:31:13 +00:00
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.
2013-10-23 19:25:46 +00:00
if test_plugin:
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'),
2013-12-23 07:03:56 +00:00
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
2013-01-22 21:09:43 +00:00
def _get_renderer(self):
"""
Adds the Renderer to the class dynamically
"""
2013-08-31 18:17:38 +00:00
if not hasattr(self, '_renderer'):
self._renderer = Registry().get('renderer')
2013-01-22 21:09:43 +00:00
return self._renderer
renderer = property(_get_renderer)
def _get_image_manager(self):
"""
Adds the image manager to the class dynamically
"""
2013-08-31 18:17:38 +00:00
if not hasattr(self, '_image_manager'):
self._image_manager = Registry().get('image_manager')
2013-01-22 21:09:43 +00:00
return self._image_manager
image_manager = property(_get_image_manager)
def _get_plugin_manager(self):
"""
Adds the Renderer to the class dynamically
"""
2013-08-31 18:17:38 +00:00
if not hasattr(self, '_plugin_manager'):
self._plugin_manager = Registry().get('plugin_manager')
2013-01-22 21:09:43 +00:00
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
"""
2013-08-31 18:17:38 +00:00
if not hasattr(self, '_main_window'):
self._main_window = Registry().get('main_window')
2013-01-24 06:00:51 +00:00
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
"""
2013-06-21 05:16:35 +00:00
Adds the openlp to the class dynamically.
Windows needs to access the application in a dynamic manner.
2013-02-03 09:07:31 +00:00
"""
2013-08-31 18:17:38 +00:00
if os.name == 'nt':
return Registry().get('application')
2013-06-21 05:16:35 +00:00
else:
2013-08-31 18:17:38 +00:00
if not hasattr(self, '_application'):
self._application = Registry().get('application')
2013-06-21 05:16:35 +00:00
return self._application
2013-02-03 09:07:31 +00:00
2013-02-05 08:05:28 +00:00
application = property(_get_application)