4
4
###############################################################################
5
5
# OpenLP - Open Source Lyrics Projection #
6
6
# --------------------------------------------------------------------------- #
7
# Copyright (c) 2008-2012 Raoul Snyman #
8
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
7
# Copyright (c) 2008-2014 Raoul Snyman #
8
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
9
9
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
10
# Meinert Jordan, Armin Köhler, Edwin Lunando, Joshua Miller, Stevan Pettit, #
11
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
12
# Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon #
13
# Tibble, Dave Warnock, Frode Woldsund #
10
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
11
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
12
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
13
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
14
# Frode Woldsund, Martin Zibricky #
14
15
# --------------------------------------------------------------------------- #
15
16
# This program is free software; you can redistribute it and/or modify it #
16
17
# under the terms of the GNU General Public License as published by the Free #
36
36
from xml.etree.ElementTree import ElementTree, XML
37
37
from PyQt4 import QtCore, QtGui
39
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
39
from openlp.core.lib import FileDialog, OpenLPToolbar, get_text_file_string, build_icon, \
40
40
Receiver, SettingsManager, translate, check_item_selected, \
41
check_directory_exists, create_thumb, validate_thumb
41
check_directory_exists, create_thumb, validate_thumb, ImageSource
42
42
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
43
43
BackgroundGradientType
44
44
from openlp.core.lib.settings import Settings
46
46
create_widget_action
47
47
from openlp.core.theme import Theme
48
48
from openlp.core.ui import FileRenameForm, ThemeForm
49
from openlp.core.utils import AppLocation, delete_file, get_filesystem_encoding
49
from openlp.core.utils import AppLocation, delete_file, locale_compare, \
50
get_filesystem_encoding
51
52
log = logging.getLogger(__name__)
139
140
QtCore.QObject.connect(Receiver.get_receiver(),
140
141
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
143
144
self.path = AppLocation.get_section_data_path(self.settingsSection)
144
145
check_directory_exists(self.path)
145
self.thumb_path = os.path.join(self.path, u'thumbnails')
146
check_directory_exists(self.thumb_path)
146
self.thumbPath = os.path.join(self.path, u'thumbnails')
147
check_directory_exists(self.thumbPath)
147
148
self.themeForm.path = self.path
148
self.old_background_image = None
149
self.bad_v1_name_chars = re.compile(r'[%+\[\]]')
149
self.oldBackgroundImage = None
150
self.badV1NameChars = re.compile(r'[%+\[\]]')
150
151
# Last little bits of setting up
151
152
self.configUpdated()
195
196
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
196
197
theme_name = unicode(item.text())
197
self.deleteAction.setVisible(False)
198
self.renameAction.setVisible(False)
199
self.globalAction.setVisible(False)
200
# If default theme restrict actions
201
if real_theme_name == theme_name:
202
self.deleteAction.setVisible(True)
203
self.renameAction.setVisible(True)
204
self.globalAction.setVisible(True)
198
visible = real_theme_name == theme_name
199
self.deleteAction.setVisible(visible)
200
self.renameAction.setVisible(visible)
201
self.globalAction.setVisible(visible)
205
202
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
207
204
def changeGlobalFromTab(self, theme_name):
285
283
for plugin in self.mainwindow.pluginManager.plugins:
286
284
if plugin.usesTheme(old_theme_name):
287
285
plugin.renameTheme(old_theme_name, new_theme_name)
289
286
self.mainwindow.renderer.update_theme(
290
287
new_theme_name, old_theme_name)
292
290
def onCopyTheme(self):
330
329
theme = self.getThemeData(
331
330
unicode(item.data(QtCore.Qt.UserRole).toString()))
332
331
if theme.background_type == u'image':
333
self.old_background_image = theme.background_filename
332
self.oldBackgroundImage = theme.background_filename
334
333
self.themeForm.theme = theme
335
334
self.themeForm.exec_(True)
336
self.old_background_image = None
335
self.oldBackgroundImage = None
337
336
self.mainwindow.renderer.update_theme(theme.theme_name)
339
339
def onDeleteTheme(self):
349
349
row = self.themeListWidget.row(item)
350
350
self.themeListWidget.takeItem(row)
351
351
self.deleteTheme(theme)
352
self.mainwindow.renderer.update_theme(theme, only_delete=True)
352
353
# As we do not reload the themes, push out the change. Reload the
353
354
# list as the internal lists and events need to be triggered.
354
355
self._pushThemes()
355
self.mainwindow.renderer.update_theme(theme, only_delete=True)
357
357
def deleteTheme(self, theme):
362
362
The theme to delete.
364
self.theme_list.remove(theme)
364
self.themeList.remove(theme)
365
365
thumb = u'%s.png' % theme
366
366
delete_file(os.path.join(self.path, thumb))
367
delete_file(os.path.join(self.thumb_path, thumb))
367
delete_file(os.path.join(self.thumbPath, thumb))
369
369
encoding = get_filesystem_encoding()
370
370
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
420
420
attempting to extract OpenLP themes from those files. This process
421
421
will load both OpenLP version 1 and version 2 themes.
423
files = QtGui.QFileDialog.getOpenFileNames(self,
423
files = FileDialog.getOpenFileNames(self,
424
424
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
425
425
SettingsManager.get_last_dir(self.settingsSection),
426
426
unicode(translate('OpenLP.ThemeManager',
442
442
The plugins will call back in to get the real list if they want it.
444
444
log.debug(u'Load themes from dir')
446
446
self.themeListWidget.clear()
447
447
files = SettingsManager.get_files(self.settingsSection, u'.png')
459
459
self.configUpdated()
460
460
files = SettingsManager.get_files(self.settingsSection, u'.png')
461
461
# Sort the themes by its name considering language specific characters.
462
# lower() is needed for windows!
463
files.sort(key=lambda file_name: unicode(file_name).lower(),
462
files.sort(key=lambda file_name: unicode(file_name),
465
464
# now process the file list of png files
466
465
for name in files:
467
466
# check to see file is in theme root directory
473
472
'%s (default)')) % text_name
476
thumb = os.path.join(self.thumb_path, u'%s.png' % text_name)
475
thumb = os.path.join(self.thumbPath, u'%s.png' % text_name)
477
476
item_name = QtGui.QListWidgetItem(name)
478
477
if validate_thumb(theme, thumb):
479
478
icon = build_icon(thumb)
481
480
icon = create_thumb(theme, thumb)
482
481
item_name.setIcon(icon)
483
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(text_name))
483
QtCore.Qt.UserRole, QtCore.QVariant(text_name))
484
484
self.themeListWidget.addItem(item_name)
485
self.theme_list.append(text_name)
485
self.themeList.append(text_name)
486
486
self._pushThemes()
488
488
def _pushThemes(self):
509
509
unicode(theme_name) + u'.xml')
510
510
xml = get_text_file_string(xml_file)
512
log.debug("No theme data - using default theme")
512
log.debug(u'No theme data - using default theme')
513
513
return ThemeXML()
515
515
return self._createThemeFromXml(xml, self.path)
519
519
translate('OpenLP.ThemeManager', 'Theme Already Exists'),
520
520
translate('OpenLP.ThemeManager',
521
521
'Theme %s already exists. Do you want to replace it?'
522
).replace('%s', theme_name),
523
523
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
524
524
QtGui.QMessageBox.No),
525
525
QtGui.QMessageBox.No)
547
547
xml_tree = ElementTree(element=XML(zip.read(xml_file[0]))).getroot()
548
548
v1_background = xml_tree.find(u'BackgroundType')
549
549
if v1_background is not None:
550
theme_name, file_xml, out_file, abort_import = self.unzipVersion122(dir, zip,
551
xml_file[0], xml_tree, v1_background, out_file)
550
theme_name, file_xml, out_file, abort_import = \
551
self.unzipVersion122(
552
dir, zip, xml_file[0], xml_tree, v1_background, out_file)
553
554
theme_name = xml_tree.find(u'name').text.strip()
554
555
theme_folder = os.path.join(dir, theme_name)
602
603
theme = self._createThemeFromXml(file_xml, self.path)
603
604
self.generateAndSaveImage(dir, theme_name, theme)
604
# Only show the error message, when IOError was not raised (in this
605
# case the error message has already been shown).
605
# Only show the error message, when IOError was not raised (in
606
# this case the error message has already been shown).
606
607
elif zip is not None:
607
608
critical_error_message_box(
608
609
translate('OpenLP.ThemeManager', 'Validation Error'),
611
612
log.exception(u'Theme file does not contain XML data %s' %
614
def unzipVersion122(self, dir, zip, xml_file, xml_tree, background, out_file):
615
def unzipVersion122(self, dir, zip, xml_file, xml_tree, background,
616
618
Unzip openlp.org 1.2x theme file and upgrade the theme xml. When calling
617
619
this method, please keep in mind, that some parameters are redundant.
619
621
theme_name = xml_tree.find(u'Name').text.strip()
620
theme_name = self.bad_v1_name_chars.sub(u'', theme_name)
622
theme_name = self.badV1NameChars.sub(u'', theme_name)
621
623
theme_folder = os.path.join(dir, theme_name)
622
624
theme_exists = os.path.exists(theme_folder)
623
625
if theme_exists and not self.overWriteMessageBox(theme_name):
632
634
if background.text.strip() == u'2':
633
635
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
634
636
# image file has same extension and is in subfolder
635
imagefile = filter(lambda name: os.path.splitext(name)[1].lower()
637
image_file = filter(lambda name: os.path.splitext(name)[1].lower()
636
638
== os.path.splitext(image_name)[1].lower() and name.find(r'/'),
638
if len(imagefile) >= 1:
640
if len(image_file) >= 1:
639
641
out_file = open(os.path.join(themedir, image_name), u'wb')
640
out_file.write(zip.read(imagefile[0]))
642
out_file.write(zip.read(image_file[0]))
643
645
log.exception(u'Theme file does not contain image file "%s"' %
669
671
self._writeTheme(theme, image_from, image_to)
670
672
if theme.background_type == \
671
673
BackgroundType.to_string(BackgroundType.Image):
672
self.mainwindow.imageManager.updateImage(theme.theme_name,
673
u'theme', QtGui.QColor(theme.background_border_color))
674
self.mainwindow.imageManager.updateImageBorder(
675
theme.background_filename,
676
ImageSource.Theme, QtGui.QColor(theme.background_border_color))
674
677
self.mainwindow.imageManager.processUpdates()
677
679
def _writeTheme(self, theme, image_from, image_to):
685
687
theme_dir = os.path.join(self.path, name)
686
688
check_directory_exists(theme_dir)
687
689
theme_file = os.path.join(theme_dir, name + u'.xml')
688
if self.old_background_image and \
689
image_to != self.old_background_image:
690
delete_file(self.old_background_image)
690
if self.oldBackgroundImage and image_to != self.oldBackgroundImage:
691
delete_file(self.oldBackgroundImage)
693
694
out_file = open(theme_file, u'w')
714
715
if os.path.exists(sample_path_name):
715
716
os.unlink(sample_path_name)
716
717
frame.save(sample_path_name, u'png')
717
thumb = os.path.join(self.thumb_path, u'%s.png' % name)
718
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
718
719
create_thumb(sample_path_name, thumb, False)
719
720
log.debug(u'Theme image written to %s', sample_path_name)
723
724
Called to update the themes' preview images.
725
self.mainwindow.displayProgressBar(len(self.theme_list))
726
for theme in self.theme_list:
726
self.mainwindow.displayProgressBar(len(self.themeList))
727
for theme in self.themeList:
727
728
self.mainwindow.incrementProgressBar()
728
729
self.generateAndSaveImage(
729
730
self.path, theme, self.getThemeData(theme))
820
821
theme = Theme(xml_data)
821
822
new_theme = ThemeXML()
822
new_theme.theme_name = self.bad_v1_name_chars.sub(u'', theme.Name)
823
new_theme.theme_name = self.badV1NameChars.sub(u'', theme.Name)
823
824
if theme.BackgroundType == 0:
824
825
new_theme.background_type = \
825
826
BackgroundType.to_string(BackgroundType.Solid)