forked from openlp/openlp
r2019
This commit is contained in:
commit
bbc6b0d7b6
@ -36,6 +36,23 @@ from PyQt4 import QtCore, QtGui, Qt
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSource(object):
|
||||||
|
"""
|
||||||
|
This enumeration class represents different image sources. An image sources
|
||||||
|
states where an image is used. This enumeration class is need in the context
|
||||||
|
of the :class:~openlp.core.lib.imagemanager`.
|
||||||
|
|
||||||
|
``ImagePlugin``
|
||||||
|
This states that an image is being used by the image plugin.
|
||||||
|
|
||||||
|
``Theme``
|
||||||
|
This says, that the image is used by a theme.
|
||||||
|
"""
|
||||||
|
ImagePlugin = 1
|
||||||
|
Theme = 2
|
||||||
|
|
||||||
|
|
||||||
class MediaType(object):
|
class MediaType(object):
|
||||||
"""
|
"""
|
||||||
An enumeration class for types of media.
|
An enumeration class for types of media.
|
||||||
|
@ -32,6 +32,7 @@ A Thread is used to convert the image to a byte array so the user does not need
|
|||||||
to wait for the conversion to happen.
|
to wait for the conversion to happen.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import Queue
|
import Queue
|
||||||
|
|
||||||
@ -97,19 +98,34 @@ class Priority(object):
|
|||||||
|
|
||||||
class Image(object):
|
class Image(object):
|
||||||
"""
|
"""
|
||||||
This class represents an image. To mark an image as *dirty* set the instance
|
This class represents an image. To mark an image as *dirty* call the
|
||||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
:class:`ImageManager`'s ``_resetImage`` method with the Image instance as
|
||||||
to the queue of images to process.
|
argument.
|
||||||
"""
|
"""
|
||||||
secondary_priority = 0
|
secondary_priority = 0
|
||||||
def __init__(self, name, path, source, background):
|
|
||||||
self.name = name
|
def __init__(self, path, source, background):
|
||||||
|
"""
|
||||||
|
Create an image for the :class:`ImageManager`'s cache.
|
||||||
|
|
||||||
|
``path``
|
||||||
|
The image's file path. This should be an existing file path.
|
||||||
|
|
||||||
|
``source``
|
||||||
|
The source describes the image's origin. Possible values are
|
||||||
|
described in the :class:`~openlp.core.lib.ImageSource` class.
|
||||||
|
|
||||||
|
``background``
|
||||||
|
A ``QtGui.QColor`` object specifying the colour to be used to fill
|
||||||
|
the gabs if the image's ratio does not match with the display ratio.
|
||||||
|
"""
|
||||||
self.path = path
|
self.path = path
|
||||||
self.image = None
|
self.image = None
|
||||||
self.image_bytes = None
|
self.image_bytes = None
|
||||||
self.priority = Priority.Normal
|
self.priority = Priority.Normal
|
||||||
self.source = source
|
self.source = source
|
||||||
self.background = background
|
self.background = background
|
||||||
|
self.timestamp = os.stat(path).st_mtime
|
||||||
self.secondary_priority = Image.secondary_priority
|
self.secondary_priority = Image.secondary_priority
|
||||||
Image.secondary_priority += 1
|
Image.secondary_priority += 1
|
||||||
|
|
||||||
@ -118,7 +134,7 @@ class PriorityQueue(Queue.PriorityQueue):
|
|||||||
"""
|
"""
|
||||||
Customised ``Queue.PriorityQueue``.
|
Customised ``Queue.PriorityQueue``.
|
||||||
|
|
||||||
Each item in the queue must be tuple with three values. The first value
|
Each item in the queue must be a tuple with three values. The first value
|
||||||
is the :class:`Image`'s ``priority`` attribute, the second value
|
is the :class:`Image`'s ``priority`` attribute, the second value
|
||||||
the :class:`Image`'s ``secondary_priority`` attribute. The last value the
|
the :class:`Image`'s ``secondary_priority`` attribute. The last value the
|
||||||
:class:`Image` instance itself::
|
:class:`Image` instance itself::
|
||||||
@ -187,7 +203,7 @@ class ImageManager(QtCore.QObject):
|
|||||||
for image in self._cache.values():
|
for image in self._cache.values():
|
||||||
self._resetImage(image)
|
self._resetImage(image)
|
||||||
|
|
||||||
def updateImages(self, imageType, background):
|
def updateImagesBorder(self, source, background):
|
||||||
"""
|
"""
|
||||||
Border has changed so update all the images affected.
|
Border has changed so update all the images affected.
|
||||||
"""
|
"""
|
||||||
@ -195,23 +211,27 @@ class ImageManager(QtCore.QObject):
|
|||||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||||
# stream to None.
|
# stream to None.
|
||||||
for image in self._cache.values():
|
for image in self._cache.values():
|
||||||
if image.source == imageType:
|
if image.source == source:
|
||||||
image.background = background
|
image.background = background
|
||||||
self._resetImage(image)
|
self._resetImage(image)
|
||||||
|
|
||||||
def updateImage(self, name, imageType, background):
|
def updateImageBorder(self, path, source, background):
|
||||||
"""
|
"""
|
||||||
Border has changed so update the image affected.
|
Border has changed so update the image affected.
|
||||||
"""
|
"""
|
||||||
log.debug(u'updateImage')
|
log.debug(u'updateImage')
|
||||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
# Mark the image as dirty for a rebuild by setting the image and byte
|
||||||
# stream to None.
|
# stream to None.
|
||||||
for image in self._cache.values():
|
image = self._cache[(path, source)]
|
||||||
if image.source == imageType and image.name == name:
|
if image.source == source:
|
||||||
image.background = background
|
image.background = background
|
||||||
self._resetImage(image)
|
self._resetImage(image)
|
||||||
|
|
||||||
def _resetImage(self, image):
|
def _resetImage(self, image):
|
||||||
|
"""
|
||||||
|
Mark the given :class:`Image` instance as dirty by setting its ``image``
|
||||||
|
and ``image_bytes`` attributes to None.
|
||||||
|
"""
|
||||||
image.image = None
|
image.image = None
|
||||||
image.image_bytes = None
|
image.image_bytes = None
|
||||||
self._conversionQueue.modify_priority(image, Priority.Normal)
|
self._conversionQueue.modify_priority(image, Priority.Normal)
|
||||||
@ -224,13 +244,13 @@ class ImageManager(QtCore.QObject):
|
|||||||
if not self.imageThread.isRunning():
|
if not self.imageThread.isRunning():
|
||||||
self.imageThread.start()
|
self.imageThread.start()
|
||||||
|
|
||||||
def getImage(self, name):
|
def getImage(self, path, source):
|
||||||
"""
|
"""
|
||||||
Return the ``QImage`` from the cache. If not present wait for the
|
Return the ``QImage`` from the cache. If not present wait for the
|
||||||
background thread to process it.
|
background thread to process it.
|
||||||
"""
|
"""
|
||||||
log.debug(u'getImage %s' % name)
|
log.debug(u'getImage %s' % path)
|
||||||
image = self._cache[name]
|
image = self._cache[(path, source)]
|
||||||
if image.image is None:
|
if image.image is None:
|
||||||
self._conversionQueue.modify_priority(image, Priority.High)
|
self._conversionQueue.modify_priority(image, Priority.High)
|
||||||
# make sure we are running and if not give it a kick
|
# make sure we are running and if not give it a kick
|
||||||
@ -246,13 +266,13 @@ class ImageManager(QtCore.QObject):
|
|||||||
self._conversionQueue.modify_priority(image, Priority.Low)
|
self._conversionQueue.modify_priority(image, Priority.Low)
|
||||||
return image.image
|
return image.image
|
||||||
|
|
||||||
def getImageBytes(self, name):
|
def getImageBytes(self, path, source):
|
||||||
"""
|
"""
|
||||||
Returns the byte string for an image. If not present wait for the
|
Returns the byte string for an image. If not present wait for the
|
||||||
background thread to process it.
|
background thread to process it.
|
||||||
"""
|
"""
|
||||||
log.debug(u'getImageBytes %s' % name)
|
log.debug(u'getImageBytes %s' % path)
|
||||||
image = self._cache[name]
|
image = self._cache[(path, source)]
|
||||||
if image.image_bytes is None:
|
if image.image_bytes is None:
|
||||||
self._conversionQueue.modify_priority(image, Priority.Urgent)
|
self._conversionQueue.modify_priority(image, Priority.Urgent)
|
||||||
# make sure we are running and if not give it a kick
|
# make sure we are running and if not give it a kick
|
||||||
@ -262,27 +282,22 @@ class ImageManager(QtCore.QObject):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
return image.image_bytes
|
return image.image_bytes
|
||||||
|
|
||||||
def deleteImage(self, name):
|
def addImage(self, path, source, background):
|
||||||
"""
|
|
||||||
Delete the Image from the cache.
|
|
||||||
"""
|
|
||||||
log.debug(u'deleteImage %s' % name)
|
|
||||||
if name in self._cache:
|
|
||||||
self._conversionQueue.remove(self._cache[name])
|
|
||||||
del self._cache[name]
|
|
||||||
|
|
||||||
def addImage(self, name, path, source, background):
|
|
||||||
"""
|
"""
|
||||||
Add image to cache if it is not already there.
|
Add image to cache if it is not already there.
|
||||||
"""
|
"""
|
||||||
log.debug(u'addImage %s:%s' % (name, path))
|
log.debug(u'addImage %s' % path)
|
||||||
if not name in self._cache:
|
if not (path, source) in self._cache:
|
||||||
image = Image(name, path, source, background)
|
image = Image(path, source, background)
|
||||||
self._cache[name] = image
|
self._cache[(path, source)] = image
|
||||||
self._conversionQueue.put(
|
self._conversionQueue.put(
|
||||||
(image.priority, image.secondary_priority, image))
|
(image.priority, image.secondary_priority, image))
|
||||||
else:
|
# Check if the there are any images with the same path and check if the
|
||||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
# timestamp has changed.
|
||||||
|
for image in self._cache.values():
|
||||||
|
if image.path == path and image.timestamp != os.stat(path).st_mtime:
|
||||||
|
image.timestamp = os.stat(path).st_mtime
|
||||||
|
self._resetImage(image)
|
||||||
# We want only one thread.
|
# We want only one thread.
|
||||||
if not self.imageThread.isRunning():
|
if not self.imageThread.isRunning():
|
||||||
self.imageThread.start()
|
self.imageThread.start()
|
||||||
|
@ -352,24 +352,23 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
``files``
|
``files``
|
||||||
The list of files to be loaded
|
The list of files to be loaded
|
||||||
"""
|
"""
|
||||||
#FIXME: change local variables to words_separated_by_underscores.
|
new_files = []
|
||||||
newFiles = []
|
error_shown = False
|
||||||
errorShown = False
|
|
||||||
for file in files:
|
for file in files:
|
||||||
type = file.split(u'.')[-1]
|
type = file.split(u'.')[-1]
|
||||||
if type.lower() not in self.onNewFileMasks:
|
if type.lower() not in self.onNewFileMasks:
|
||||||
if not errorShown:
|
if not error_shown:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'Invalid File Type'),
|
'Invalid File Type'),
|
||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
'Invalid File %s.\nSuffix not supported'))
|
'Invalid File %s.\nSuffix not supported'))
|
||||||
% file)
|
% file)
|
||||||
errorShown = True
|
error_shown = True
|
||||||
else:
|
else:
|
||||||
newFiles.append(file)
|
new_files.append(file)
|
||||||
if files:
|
if new_files:
|
||||||
self.validateAndLoad(newFiles)
|
self.validateAndLoad(new_files)
|
||||||
|
|
||||||
def validateAndLoad(self, files):
|
def validateAndLoad(self, files):
|
||||||
"""
|
"""
|
||||||
@ -379,30 +378,29 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
``files``
|
``files``
|
||||||
The files to be loaded.
|
The files to be loaded.
|
||||||
"""
|
"""
|
||||||
#FIXME: change local variables to words_separated_by_underscores.
|
|
||||||
names = []
|
names = []
|
||||||
fullList = []
|
full_list = []
|
||||||
for count in range(self.listView.count()):
|
for count in range(self.listView.count()):
|
||||||
names.append(unicode(self.listView.item(count).text()))
|
names.append(unicode(self.listView.item(count).text()))
|
||||||
fullList.append(unicode(self.listView.item(count).
|
full_list.append(unicode(self.listView.item(count).
|
||||||
data(QtCore.Qt.UserRole).toString()))
|
data(QtCore.Qt.UserRole).toString()))
|
||||||
duplicatesFound = False
|
duplicates_found = False
|
||||||
filesAdded = False
|
files_added = False
|
||||||
for file in files:
|
for file in files:
|
||||||
filename = os.path.split(unicode(file))[1]
|
filename = os.path.split(unicode(file))[1]
|
||||||
if filename in names:
|
if filename in names:
|
||||||
duplicatesFound = True
|
duplicates_found = True
|
||||||
else:
|
else:
|
||||||
filesAdded = True
|
files_added = True
|
||||||
fullList.append(file)
|
full_list.append(file)
|
||||||
if fullList and filesAdded:
|
if full_list and files_added:
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
self.loadList(fullList)
|
self.loadList(full_list)
|
||||||
lastDir = os.path.split(unicode(files[0]))[0]
|
last_dir = os.path.split(unicode(files[0]))[0]
|
||||||
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
SettingsManager.set_last_dir(self.settingsSection, last_dir)
|
||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
self.settingsSection, self.getFileList())
|
self.settingsSection, self.getFileList())
|
||||||
if duplicatesFound:
|
if duplicates_found:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
UiStrings().Duplicate,
|
UiStrings().Duplicate,
|
||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
@ -422,13 +420,13 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
Return the current list of files
|
Return the current list of files
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
filelist = []
|
file_list = []
|
||||||
while count < self.listView.count():
|
while count < self.listView.count():
|
||||||
bitem = self.listView.item(count)
|
bitem = self.listView.item(count)
|
||||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||||
filelist.append(filename)
|
file_list.append(filename)
|
||||||
count += 1
|
count += 1
|
||||||
return filelist
|
return file_list
|
||||||
|
|
||||||
def loadList(self, list):
|
def loadList(self, list):
|
||||||
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
|
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
|
||||||
@ -477,9 +475,8 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
Allows the change of current item in the list to be actioned
|
Allows the change of current item in the list to be actioned
|
||||||
"""
|
"""
|
||||||
if Settings().value(u'advanced/single click preview',
|
if Settings().value(u'advanced/single click preview',
|
||||||
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
|
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed and \
|
||||||
and self.listView.selectedIndexes() \
|
self.listView.selectedIndexes() and self.autoSelectId == -1:
|
||||||
and self.autoSelectId == -1:
|
|
||||||
self.onPreviewClick(True)
|
self.onPreviewClick(True)
|
||||||
|
|
||||||
def onPreviewClick(self, keepFocus=False):
|
def onPreviewClick(self, keepFocus=False):
|
||||||
|
@ -32,7 +32,7 @@ from PyQt4 import QtGui, QtCore, QtWebKit
|
|||||||
|
|
||||||
from openlp.core.lib import ServiceItem, expand_tags, \
|
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||||
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||||
ItemCapabilities, FormattingTags
|
ItemCapabilities, FormattingTags, ImageSource
|
||||||
from openlp.core.lib.theme import ThemeLevel
|
from openlp.core.lib.theme import ThemeLevel
|
||||||
from openlp.core.ui import MainDisplay, ScreenList
|
from openlp.core.ui import MainDisplay, ScreenList
|
||||||
|
|
||||||
@ -82,6 +82,9 @@ class Renderer(object):
|
|||||||
self._calculate_default()
|
self._calculate_default()
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme)
|
QtCore.SIGNAL(u'theme_update_global'), self.set_global_theme)
|
||||||
|
self.web = QtWebKit.QWebView()
|
||||||
|
self.web.setVisible(False)
|
||||||
|
self.web_frame = self.web.page().mainFrame()
|
||||||
|
|
||||||
def update_display(self):
|
def update_display(self):
|
||||||
"""
|
"""
|
||||||
@ -137,8 +140,8 @@ class Renderer(object):
|
|||||||
self._theme_dimensions[theme_name]
|
self._theme_dimensions[theme_name]
|
||||||
# if No file do not update cache
|
# if No file do not update cache
|
||||||
if theme_data.background_filename:
|
if theme_data.background_filename:
|
||||||
self.image_manager.addImage(theme_data.theme_name,
|
self.image_manager.addImage(theme_data.background_filename,
|
||||||
theme_data.background_filename, u'theme',
|
ImageSource.Theme,
|
||||||
QtGui.QColor(theme_data.background_border_color))
|
QtGui.QColor(theme_data.background_border_color))
|
||||||
|
|
||||||
def pre_render(self, override_theme_data=None):
|
def pre_render(self, override_theme_data=None):
|
||||||
@ -237,14 +240,13 @@ class Renderer(object):
|
|||||||
# make big page for theme edit dialog to get line count
|
# make big page for theme edit dialog to get line count
|
||||||
serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
|
serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
|
||||||
else:
|
else:
|
||||||
self.image_manager.deleteImage(theme_data.theme_name)
|
|
||||||
serviceItem.add_from_text(VERSE)
|
serviceItem.add_from_text(VERSE)
|
||||||
serviceItem.renderer = self
|
serviceItem.renderer = self
|
||||||
serviceItem.raw_footer = FOOTER
|
serviceItem.raw_footer = FOOTER
|
||||||
# if No file do not update cache
|
# if No file do not update cache
|
||||||
if theme_data.background_filename:
|
if theme_data.background_filename:
|
||||||
self.image_manager.addImage(theme_data.theme_name,
|
self.image_manager.addImage(theme_data.background_filename,
|
||||||
theme_data.background_filename, u'theme',
|
ImageSource.Theme,
|
||||||
QtGui.QColor(theme_data.background_border_color))
|
QtGui.QColor(theme_data.background_border_color))
|
||||||
theme_data, main, footer = self.pre_render(theme_data)
|
theme_data, main, footer = self.pre_render(theme_data)
|
||||||
serviceItem.themedata = theme_data
|
serviceItem.themedata = theme_data
|
||||||
@ -404,10 +406,7 @@ class Renderer(object):
|
|||||||
if theme_data.font_main_shadow:
|
if theme_data.font_main_shadow:
|
||||||
self.page_width -= int(theme_data.font_main_shadow_size)
|
self.page_width -= int(theme_data.font_main_shadow_size)
|
||||||
self.page_height -= int(theme_data.font_main_shadow_size)
|
self.page_height -= int(theme_data.font_main_shadow_size)
|
||||||
self.web = QtWebKit.QWebView()
|
|
||||||
self.web.setVisible(False)
|
|
||||||
self.web.resize(self.page_width, self.page_height)
|
self.web.resize(self.page_width, self.page_height)
|
||||||
self.web_frame = self.web.page().mainFrame()
|
|
||||||
# Adjust width and height to account for shadow. outline done in css.
|
# Adjust width and height to account for shadow. outline done in css.
|
||||||
html = u"""<!DOCTYPE html><html><head><script>
|
html = u"""<!DOCTYPE html><html><head><script>
|
||||||
function show_text(newtext) {
|
function show_text(newtext) {
|
||||||
|
@ -36,7 +36,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
|
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, \
|
||||||
|
ImageSource
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -178,7 +179,7 @@ class ServiceItem(object):
|
|||||||
self.renderer.set_item_theme(self.theme)
|
self.renderer.set_item_theme(self.theme)
|
||||||
self.themedata, self.main, self.footer = self.renderer.pre_render()
|
self.themedata, self.main, self.footer = self.renderer.pre_render()
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
log.debug(u'Formatting slides')
|
log.debug(u'Formatting slides: %s' % self.title)
|
||||||
for slide in self._raw_frames:
|
for slide in self._raw_frames:
|
||||||
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
|
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
|
||||||
for page in pages:
|
for page in pages:
|
||||||
@ -217,8 +218,8 @@ class ServiceItem(object):
|
|||||||
self.image_border = background
|
self.image_border = background
|
||||||
self.service_item_type = ServiceItemType.Image
|
self.service_item_type = ServiceItemType.Image
|
||||||
self._raw_frames.append({u'title': title, u'path': path})
|
self._raw_frames.append({u'title': title, u'path': path})
|
||||||
self.renderer.image_manager.addImage(title, path, u'image',
|
self.renderer.image_manager.addImage(
|
||||||
self.image_border)
|
path, ImageSource.ImagePlugin, self.image_border)
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
def add_from_text(self, raw_slide, verse_tag=None):
|
def add_from_text(self, raw_slide, verse_tag=None):
|
||||||
@ -432,13 +433,12 @@ class ServiceItem(object):
|
|||||||
|
|
||||||
def get_rendered_frame(self, row):
|
def get_rendered_frame(self, row):
|
||||||
"""
|
"""
|
||||||
Returns the correct frame for a given list and
|
Returns the correct frame for a given list and renders it if required.
|
||||||
renders it if required.
|
|
||||||
"""
|
"""
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
return self._display_frames[row][u'html'].split(u'\n')[0]
|
return self._display_frames[row][u'html'].split(u'\n')[0]
|
||||||
elif self.service_item_type == ServiceItemType.Image:
|
elif self.service_item_type == ServiceItemType.Image:
|
||||||
return self._raw_frames[row][u'title']
|
return self._raw_frames[row][u'path']
|
||||||
else:
|
else:
|
||||||
return self._raw_frames[row][u'image']
|
return self._raw_frames[row][u'image']
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
|
|||||||
from PyQt4.phonon import Phonon
|
from PyQt4.phonon import Phonon
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
|
from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \
|
||||||
translate, PluginManager, expand_tags
|
translate, PluginManager, expand_tags, ImageSource
|
||||||
from openlp.core.lib.theme import BackgroundType
|
from openlp.core.lib.theme import BackgroundType
|
||||||
from openlp.core.lib.settings import Settings
|
from openlp.core.lib.settings import Settings
|
||||||
|
|
||||||
@ -274,31 +274,33 @@ class MainDisplay(Display):
|
|||||||
self.setVisible(False)
|
self.setVisible(False)
|
||||||
self.setGeometry(self.screen[u'size'])
|
self.setGeometry(self.screen[u'size'])
|
||||||
|
|
||||||
def directImage(self, name, path, background):
|
def directImage(self, path, background):
|
||||||
"""
|
"""
|
||||||
API for replacement backgrounds so Images are added directly to cache.
|
API for replacement backgrounds so Images are added directly to cache.
|
||||||
"""
|
"""
|
||||||
self.imageManager.addImage(name, path, u'image', background)
|
self.imageManager.addImage(path, ImageSource.ImagePlugin, background)
|
||||||
if hasattr(self, u'serviceItem'):
|
if not hasattr(self, u'serviceItem'):
|
||||||
self.override[u'image'] = name
|
return False
|
||||||
self.override[u'theme'] = self.serviceItem.themedata.theme_name
|
self.override[u'image'] = path
|
||||||
self.image(name)
|
self.override[u'theme'] = self.serviceItem.themedata.background_filename
|
||||||
|
self.image(path)
|
||||||
# Update the preview frame.
|
# Update the preview frame.
|
||||||
if self.isLive:
|
if self.isLive:
|
||||||
self.parent().updatePreview()
|
self.parent().updatePreview()
|
||||||
return True
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
def image(self, name):
|
def image(self, path):
|
||||||
"""
|
"""
|
||||||
Add an image as the background. The image has already been added to the
|
Add an image as the background. The image has already been added to the
|
||||||
cache.
|
cache.
|
||||||
|
|
||||||
``name``
|
``path``
|
||||||
The name of the image to be displayed.
|
The path to the image to be displayed. **Note**, the path is only
|
||||||
|
passed to identify the image. If the image has changed it has to be
|
||||||
|
re-added to the image manager.
|
||||||
"""
|
"""
|
||||||
log.debug(u'image to display')
|
log.debug(u'image to display')
|
||||||
image = self.imageManager.getImageBytes(name)
|
image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin)
|
||||||
self.controller.mediaController.video_reset(self.controller)
|
self.controller.mediaController.video_reset(self.controller)
|
||||||
self.displayImage(image)
|
self.displayImage(image)
|
||||||
|
|
||||||
@ -360,7 +362,7 @@ class MainDisplay(Display):
|
|||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
return QtGui.QPixmap.grabWidget(self)
|
return QtGui.QPixmap.grabWidget(self)
|
||||||
|
|
||||||
def buildHtml(self, serviceItem, image=None):
|
def buildHtml(self, serviceItem, image_path=u''):
|
||||||
"""
|
"""
|
||||||
Store the serviceItem and build the new HTML from it. Add the
|
Store the serviceItem and build the new HTML from it. Add the
|
||||||
HTML to the display
|
HTML to the display
|
||||||
@ -377,20 +379,23 @@ class MainDisplay(Display):
|
|||||||
Receiver.send_message(u'video_background_replaced')
|
Receiver.send_message(u'video_background_replaced')
|
||||||
self.override = {}
|
self.override = {}
|
||||||
# We have a different theme.
|
# We have a different theme.
|
||||||
elif self.override[u'theme'] != serviceItem.themedata.theme_name:
|
elif self.override[u'theme'] != \
|
||||||
|
serviceItem.themedata.background_filename:
|
||||||
Receiver.send_message(u'live_theme_changed')
|
Receiver.send_message(u'live_theme_changed')
|
||||||
self.override = {}
|
self.override = {}
|
||||||
else:
|
else:
|
||||||
# replace the background
|
# replace the background
|
||||||
background = self.imageManager. \
|
background = self.imageManager.getImageBytes(
|
||||||
getImageBytes(self.override[u'image'])
|
self.override[u'image'], ImageSource.ImagePlugin)
|
||||||
self.setTransparency(self.serviceItem.themedata.background_type ==
|
self.setTransparency(self.serviceItem.themedata.background_type ==
|
||||||
BackgroundType.to_string(BackgroundType.Transparent))
|
BackgroundType.to_string(BackgroundType.Transparent))
|
||||||
if self.serviceItem.themedata.background_filename:
|
if self.serviceItem.themedata.background_filename:
|
||||||
self.serviceItem.bg_image_bytes = self.imageManager. \
|
self.serviceItem.bg_image_bytes = self.imageManager.getImageBytes(
|
||||||
getImageBytes(self.serviceItem.themedata.theme_name)
|
self.serviceItem.themedata.background_filename,
|
||||||
if image:
|
ImageSource.Theme)
|
||||||
image_bytes = self.imageManager.getImageBytes(image)
|
if image_path:
|
||||||
|
image_bytes = self.imageManager.getImageBytes(
|
||||||
|
image_path, ImageSource.ImagePlugin)
|
||||||
else:
|
else:
|
||||||
image_bytes = None
|
image_bytes = None
|
||||||
html = build_html(self.serviceItem, self.screen, self.isLive,
|
html = build_html(self.serviceItem, self.screen, self.isLive,
|
||||||
|
@ -48,7 +48,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
|
|||||||
ShortcutListForm, FormattingTagForm
|
ShortcutListForm, FormattingTagForm
|
||||||
from openlp.core.ui.media import MediaController
|
from openlp.core.ui.media import MediaController
|
||||||
from openlp.core.utils import AppLocation, add_actions, LanguageManager, \
|
from openlp.core.utils import AppLocation, add_actions, LanguageManager, \
|
||||||
get_application_version
|
get_application_version, get_filesystem_encoding
|
||||||
from openlp.core.utils.actions import ActionList, CategoryOrder
|
from openlp.core.utils.actions import ActionList, CategoryOrder
|
||||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||||
from openlp.core.ui import ScreenList
|
from openlp.core.ui import ScreenList
|
||||||
|
@ -34,7 +34,7 @@ from collections import deque
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
|
from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
|
||||||
translate, build_icon, build_html, PluginManager, ServiceItem
|
translate, build_icon, build_html, PluginManager, ServiceItem, ImageSource
|
||||||
from openlp.core.lib.ui import UiStrings, create_action
|
from openlp.core.lib.ui import UiStrings, create_action
|
||||||
from openlp.core.lib.settings import Settings
|
from openlp.core.lib.settings import Settings
|
||||||
from openlp.core.lib import SlideLimits, ServiceItemAction
|
from openlp.core.lib import SlideLimits, ServiceItemAction
|
||||||
@ -861,8 +861,10 @@ class SlideController(Controller):
|
|||||||
# If current slide set background to image
|
# If current slide set background to image
|
||||||
if framenumber == slideno:
|
if framenumber == slideno:
|
||||||
self.serviceItem.bg_image_bytes = \
|
self.serviceItem.bg_image_bytes = \
|
||||||
self.imageManager.getImageBytes(frame[u'title'])
|
self.imageManager.getImageBytes(frame[u'path'],
|
||||||
image = self.imageManager.getImage(frame[u'title'])
|
ImageSource.ImagePlugin)
|
||||||
|
image = self.imageManager.getImage(frame[u'path'],
|
||||||
|
ImageSource.ImagePlugin)
|
||||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||||
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
||||||
slideHeight = width * (1 / self.ratio)
|
slideHeight = width * (1 / self.ratio)
|
||||||
@ -1092,14 +1094,14 @@ class SlideController(Controller):
|
|||||||
u'%s_slide' % self.serviceItem.name.lower(),
|
u'%s_slide' % self.serviceItem.name.lower(),
|
||||||
[self.serviceItem, self.isLive, row])
|
[self.serviceItem, self.isLive, row])
|
||||||
else:
|
else:
|
||||||
toDisplay = self.serviceItem.get_rendered_frame(row)
|
to_display = self.serviceItem.get_rendered_frame(row)
|
||||||
if self.serviceItem.is_text():
|
if self.serviceItem.is_text():
|
||||||
self.display.text(toDisplay)
|
self.display.text(to_display)
|
||||||
else:
|
else:
|
||||||
if start:
|
if start:
|
||||||
self.display.buildHtml(self.serviceItem, toDisplay)
|
self.display.buildHtml(self.serviceItem, to_display)
|
||||||
else:
|
else:
|
||||||
self.display.image(toDisplay)
|
self.display.image(to_display)
|
||||||
# reset the store used to display first image
|
# reset the store used to display first image
|
||||||
self.serviceItem.bg_image_bytes = None
|
self.serviceItem.bg_image_bytes = None
|
||||||
self.updatePreview()
|
self.updatePreview()
|
||||||
|
@ -228,10 +228,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
"""
|
"""
|
||||||
Detects Page changes and updates as approprate.
|
Detects Page changes and updates as approprate.
|
||||||
"""
|
"""
|
||||||
if self.page(pageId) == self.areaPositionPage:
|
enabled = self.page(pageId) == self.areaPositionPage
|
||||||
self.setOption(QtGui.QWizard.HaveCustomButton1, True)
|
self.setOption(QtGui.QWizard.HaveCustomButton1, enabled)
|
||||||
else:
|
|
||||||
self.setOption(QtGui.QWizard.HaveCustomButton1, False)
|
|
||||||
if self.page(pageId) == self.previewPage:
|
if self.page(pageId) == self.previewPage:
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
frame = self.thememanager.generateImage(self.theme)
|
frame = self.thememanager.generateImage(self.theme)
|
||||||
|
@ -38,7 +38,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
|
|
||||||
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
|
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
|
||||||
Receiver, SettingsManager, translate, check_item_selected, \
|
Receiver, SettingsManager, translate, check_item_selected, \
|
||||||
check_directory_exists, create_thumb, validate_thumb
|
check_directory_exists, create_thumb, validate_thumb, ImageSource
|
||||||
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
|
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
|
||||||
BackgroundGradientType
|
BackgroundGradientType
|
||||||
from openlp.core.lib.settings import Settings
|
from openlp.core.lib.settings import Settings
|
||||||
@ -139,14 +139,14 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
|
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
|
||||||
# Variables
|
# Variables
|
||||||
self.theme_list = []
|
self.themeList = []
|
||||||
self.path = AppLocation.get_section_data_path(self.settingsSection)
|
self.path = AppLocation.get_section_data_path(self.settingsSection)
|
||||||
check_directory_exists(self.path)
|
check_directory_exists(self.path)
|
||||||
self.thumb_path = os.path.join(self.path, u'thumbnails')
|
self.thumbPath = os.path.join(self.path, u'thumbnails')
|
||||||
check_directory_exists(self.thumb_path)
|
check_directory_exists(self.thumbPath)
|
||||||
self.themeForm.path = self.path
|
self.themeForm.path = self.path
|
||||||
self.old_background_image = None
|
self.oldBackgroundImage = None
|
||||||
self.bad_v1_name_chars = re.compile(r'[%+\[\]]')
|
self.badV1NameChars = re.compile(r'[%+\[\]]')
|
||||||
# Last little bits of setting up
|
# Last little bits of setting up
|
||||||
self.configUpdated()
|
self.configUpdated()
|
||||||
|
|
||||||
@ -194,14 +194,10 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
return
|
return
|
||||||
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
|
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||||
theme_name = unicode(item.text())
|
theme_name = unicode(item.text())
|
||||||
self.deleteAction.setVisible(False)
|
visible = real_theme_name == theme_name
|
||||||
self.renameAction.setVisible(False)
|
self.deleteAction.setVisible(visible)
|
||||||
self.globalAction.setVisible(False)
|
self.renameAction.setVisible(visible)
|
||||||
# If default theme restrict actions
|
self.globalAction.setVisible(visible)
|
||||||
if real_theme_name == theme_name:
|
|
||||||
self.deleteAction.setVisible(True)
|
|
||||||
self.renameAction.setVisible(True)
|
|
||||||
self.globalAction.setVisible(True)
|
|
||||||
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
|
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
|
||||||
|
|
||||||
def changeGlobalFromTab(self, theme_name):
|
def changeGlobalFromTab(self, theme_name):
|
||||||
@ -330,10 +326,10 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
theme = self.getThemeData(
|
theme = self.getThemeData(
|
||||||
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
||||||
if theme.background_type == u'image':
|
if theme.background_type == u'image':
|
||||||
self.old_background_image = theme.background_filename
|
self.oldBackgroundImage = theme.background_filename
|
||||||
self.themeForm.theme = theme
|
self.themeForm.theme = theme
|
||||||
self.themeForm.exec_(True)
|
self.themeForm.exec_(True)
|
||||||
self.old_background_image = None
|
self.oldBackgroundImage = None
|
||||||
self.mainwindow.renderer.update_theme(theme.theme_name)
|
self.mainwindow.renderer.update_theme(theme.theme_name)
|
||||||
|
|
||||||
def onDeleteTheme(self):
|
def onDeleteTheme(self):
|
||||||
@ -361,10 +357,10 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
``theme``
|
``theme``
|
||||||
The theme to delete.
|
The theme to delete.
|
||||||
"""
|
"""
|
||||||
self.theme_list.remove(theme)
|
self.themeList.remove(theme)
|
||||||
thumb = u'%s.png' % theme
|
thumb = u'%s.png' % theme
|
||||||
delete_file(os.path.join(self.path, thumb))
|
delete_file(os.path.join(self.path, thumb))
|
||||||
delete_file(os.path.join(self.thumb_path, thumb))
|
delete_file(os.path.join(self.thumbPath, thumb))
|
||||||
try:
|
try:
|
||||||
encoding = get_filesystem_encoding()
|
encoding = get_filesystem_encoding()
|
||||||
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
|
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
|
||||||
@ -442,7 +438,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
The plugins will call back in to get the real list if they want it.
|
The plugins will call back in to get the real list if they want it.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Load themes from dir')
|
log.debug(u'Load themes from dir')
|
||||||
self.theme_list = []
|
self.themeList = []
|
||||||
self.themeListWidget.clear()
|
self.themeListWidget.clear()
|
||||||
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
||||||
if firstTime:
|
if firstTime:
|
||||||
@ -473,16 +469,17 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
'%s (default)')) % text_name
|
'%s (default)')) % text_name
|
||||||
else:
|
else:
|
||||||
name = text_name
|
name = text_name
|
||||||
thumb = os.path.join(self.thumb_path, u'%s.png' % text_name)
|
thumb = os.path.join(self.thumbPath, u'%s.png' % text_name)
|
||||||
item_name = QtGui.QListWidgetItem(name)
|
item_name = QtGui.QListWidgetItem(name)
|
||||||
if validate_thumb(theme, thumb):
|
if validate_thumb(theme, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = create_thumb(theme, thumb)
|
icon = create_thumb(theme, thumb)
|
||||||
item_name.setIcon(icon)
|
item_name.setIcon(icon)
|
||||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(text_name))
|
item_name.setData(
|
||||||
|
QtCore.Qt.UserRole, QtCore.QVariant(text_name))
|
||||||
self.themeListWidget.addItem(item_name)
|
self.themeListWidget.addItem(item_name)
|
||||||
self.theme_list.append(text_name)
|
self.themeList.append(text_name)
|
||||||
self._pushThemes()
|
self._pushThemes()
|
||||||
|
|
||||||
def _pushThemes(self):
|
def _pushThemes(self):
|
||||||
@ -495,7 +492,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
Return the list of loaded themes
|
Return the list of loaded themes
|
||||||
"""
|
"""
|
||||||
return self.theme_list
|
return self.themeList
|
||||||
|
|
||||||
def getThemeData(self, theme_name):
|
def getThemeData(self, theme_name):
|
||||||
"""
|
"""
|
||||||
@ -509,7 +506,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
unicode(theme_name) + u'.xml')
|
unicode(theme_name) + u'.xml')
|
||||||
xml = get_text_file_string(xml_file)
|
xml = get_text_file_string(xml_file)
|
||||||
if not xml:
|
if not xml:
|
||||||
log.debug("No theme data - using default theme")
|
log.debug(u'No theme data - using default theme')
|
||||||
return ThemeXML()
|
return ThemeXML()
|
||||||
else:
|
else:
|
||||||
return self._createThemeFromXml(xml, self.path)
|
return self._createThemeFromXml(xml, self.path)
|
||||||
@ -547,8 +544,9 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
xml_tree = ElementTree(element=XML(zip.read(xml_file[0]))).getroot()
|
xml_tree = ElementTree(element=XML(zip.read(xml_file[0]))).getroot()
|
||||||
v1_background = xml_tree.find(u'BackgroundType')
|
v1_background = xml_tree.find(u'BackgroundType')
|
||||||
if v1_background is not None:
|
if v1_background is not None:
|
||||||
theme_name, file_xml, out_file, abort_import = self.unzipVersion122(dir, zip,
|
theme_name, file_xml, out_file, abort_import = \
|
||||||
xml_file[0], xml_tree, v1_background, out_file)
|
self.unzipVersion122(
|
||||||
|
dir, zip, xml_file[0], xml_tree, v1_background, out_file)
|
||||||
else:
|
else:
|
||||||
theme_name = xml_tree.find(u'name').text.strip()
|
theme_name = xml_tree.find(u'name').text.strip()
|
||||||
theme_folder = os.path.join(dir, theme_name)
|
theme_folder = os.path.join(dir, theme_name)
|
||||||
@ -601,8 +599,8 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
if file_xml:
|
if file_xml:
|
||||||
theme = self._createThemeFromXml(file_xml, self.path)
|
theme = self._createThemeFromXml(file_xml, self.path)
|
||||||
self.generateAndSaveImage(dir, theme_name, theme)
|
self.generateAndSaveImage(dir, theme_name, theme)
|
||||||
# Only show the error message, when IOError was not raised (in this
|
# Only show the error message, when IOError was not raised (in
|
||||||
# case the error message has already been shown).
|
# this case the error message has already been shown).
|
||||||
elif zip is not None:
|
elif zip is not None:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.ThemeManager', 'Validation Error'),
|
translate('OpenLP.ThemeManager', 'Validation Error'),
|
||||||
@ -611,13 +609,14 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
log.exception(u'Theme file does not contain XML data %s' %
|
log.exception(u'Theme file does not contain XML data %s' %
|
||||||
file_name)
|
file_name)
|
||||||
|
|
||||||
def unzipVersion122(self, dir, zip, xml_file, xml_tree, background, out_file):
|
def unzipVersion122(self, dir, zip, xml_file, xml_tree, background,
|
||||||
|
out_file):
|
||||||
"""
|
"""
|
||||||
Unzip openlp.org 1.2x theme file and upgrade the theme xml. When calling
|
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.
|
this method, please keep in mind, that some parameters are redundant.
|
||||||
"""
|
"""
|
||||||
theme_name = xml_tree.find(u'Name').text.strip()
|
theme_name = xml_tree.find(u'Name').text.strip()
|
||||||
theme_name = self.bad_v1_name_chars.sub(u'', theme_name)
|
theme_name = self.badV1NameChars.sub(u'', theme_name)
|
||||||
theme_folder = os.path.join(dir, theme_name)
|
theme_folder = os.path.join(dir, theme_name)
|
||||||
theme_exists = os.path.exists(theme_folder)
|
theme_exists = os.path.exists(theme_folder)
|
||||||
if theme_exists and not self.overWriteMessageBox(theme_name):
|
if theme_exists and not self.overWriteMessageBox(theme_name):
|
||||||
@ -632,12 +631,12 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
if background.text.strip() == u'2':
|
if background.text.strip() == u'2':
|
||||||
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
|
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
|
||||||
# image file has same extension and is in subfolder
|
# image file has same extension and is in subfolder
|
||||||
imagefile = filter(lambda name: os.path.splitext(name)[1].lower()
|
image_file = filter(lambda name: os.path.splitext(name)[1].lower()
|
||||||
== os.path.splitext(image_name)[1].lower() and name.find(r'/'),
|
== os.path.splitext(image_name)[1].lower() and name.find(r'/'),
|
||||||
zip.namelist())
|
zip.namelist())
|
||||||
if len(imagefile) >= 1:
|
if len(image_file) >= 1:
|
||||||
out_file = open(os.path.join(themedir, image_name), u'wb')
|
out_file = open(os.path.join(themedir, image_name), u'wb')
|
||||||
out_file.write(zip.read(imagefile[0]))
|
out_file.write(zip.read(image_file[0]))
|
||||||
out_file.close()
|
out_file.close()
|
||||||
else:
|
else:
|
||||||
log.exception(u'Theme file does not contain image file "%s"' %
|
log.exception(u'Theme file does not contain image file "%s"' %
|
||||||
@ -669,8 +668,9 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
self._writeTheme(theme, image_from, image_to)
|
self._writeTheme(theme, image_from, image_to)
|
||||||
if theme.background_type == \
|
if theme.background_type == \
|
||||||
BackgroundType.to_string(BackgroundType.Image):
|
BackgroundType.to_string(BackgroundType.Image):
|
||||||
self.mainwindow.imageManager.updateImage(theme.theme_name,
|
self.mainwindow.imageManager.updateImageBorder(
|
||||||
u'theme', QtGui.QColor(theme.background_border_color))
|
theme.background_filename,
|
||||||
|
ImageSource.Theme, QtGui.QColor(theme.background_border_color))
|
||||||
self.mainwindow.imageManager.processUpdates()
|
self.mainwindow.imageManager.processUpdates()
|
||||||
self.loadThemes()
|
self.loadThemes()
|
||||||
|
|
||||||
@ -685,9 +685,8 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
theme_dir = os.path.join(self.path, name)
|
theme_dir = os.path.join(self.path, name)
|
||||||
check_directory_exists(theme_dir)
|
check_directory_exists(theme_dir)
|
||||||
theme_file = os.path.join(theme_dir, name + u'.xml')
|
theme_file = os.path.join(theme_dir, name + u'.xml')
|
||||||
if self.old_background_image and \
|
if self.oldBackgroundImage and image_to != self.oldBackgroundImage:
|
||||||
image_to != self.old_background_image:
|
delete_file(self.oldBackgroundImage)
|
||||||
delete_file(self.old_background_image)
|
|
||||||
out_file = None
|
out_file = None
|
||||||
try:
|
try:
|
||||||
out_file = open(theme_file, u'w')
|
out_file = open(theme_file, u'w')
|
||||||
@ -714,7 +713,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
if os.path.exists(sample_path_name):
|
if os.path.exists(sample_path_name):
|
||||||
os.unlink(sample_path_name)
|
os.unlink(sample_path_name)
|
||||||
frame.save(sample_path_name, u'png')
|
frame.save(sample_path_name, u'png')
|
||||||
thumb = os.path.join(self.thumb_path, u'%s.png' % name)
|
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
|
||||||
create_thumb(sample_path_name, thumb, False)
|
create_thumb(sample_path_name, thumb, False)
|
||||||
log.debug(u'Theme image written to %s', sample_path_name)
|
log.debug(u'Theme image written to %s', sample_path_name)
|
||||||
|
|
||||||
@ -722,8 +721,8 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
Called to update the themes' preview images.
|
Called to update the themes' preview images.
|
||||||
"""
|
"""
|
||||||
self.mainwindow.displayProgressBar(len(self.theme_list))
|
self.mainwindow.displayProgressBar(len(self.themeList))
|
||||||
for theme in self.theme_list:
|
for theme in self.themeList:
|
||||||
self.mainwindow.incrementProgressBar()
|
self.mainwindow.incrementProgressBar()
|
||||||
self.generateAndSaveImage(
|
self.generateAndSaveImage(
|
||||||
self.path, theme, self.getThemeData(theme))
|
self.path, theme, self.getThemeData(theme))
|
||||||
@ -819,7 +818,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
theme = Theme(xml_data)
|
theme = Theme(xml_data)
|
||||||
new_theme = ThemeXML()
|
new_theme = ThemeXML()
|
||||||
new_theme.theme_name = self.bad_v1_name_chars.sub(u'', theme.Name)
|
new_theme.theme_name = self.badV1NameChars.sub(u'', theme.Name)
|
||||||
if theme.BackgroundType == 0:
|
if theme.BackgroundType == 0:
|
||||||
new_theme.background_type = \
|
new_theme.background_type = \
|
||||||
BackgroundType.to_string(BackgroundType.Solid)
|
BackgroundType.to_string(BackgroundType.Solid)
|
||||||
|
@ -552,11 +552,9 @@ class Ui_ThemeWizard(object):
|
|||||||
themeWizard.setButtonText(QtGui.QWizard.CustomButton1,
|
themeWizard.setButtonText(QtGui.QWizard.CustomButton1,
|
||||||
translate('OpenLP.ThemeWizard', 'Layout Preview'))
|
translate('OpenLP.ThemeWizard', 'Layout Preview'))
|
||||||
self.previewPage.setTitle(
|
self.previewPage.setTitle(
|
||||||
translate('OpenLP.ThemeWizard', 'Save and Preview'))
|
translate('OpenLP.ThemeWizard', 'Preview and Save'))
|
||||||
self.previewPage.setSubTitle(
|
self.previewPage.setSubTitle(
|
||||||
translate('OpenLP.ThemeWizard', 'View the theme and save it '
|
translate('OpenLP.ThemeWizard', 'Preview the theme and save it.'))
|
||||||
'replacing the current one or change the name to create a '
|
|
||||||
'new theme'))
|
|
||||||
self.themeNameLabel.setText(
|
self.themeNameLabel.setText(
|
||||||
translate('OpenLP.ThemeWizard', 'Theme name:'))
|
translate('OpenLP.ThemeWizard', 'Theme name:'))
|
||||||
# Align all QFormLayouts towards each other.
|
# Align all QFormLayouts towards each other.
|
||||||
|
@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
||||||
Receiver
|
Receiver, ImageSource
|
||||||
from openlp.core.lib.settings import Settings
|
from openlp.core.lib.settings import Settings
|
||||||
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
||||||
|
|
||||||
@ -98,4 +98,5 @@ class ImagePlugin(Plugin):
|
|||||||
"""
|
"""
|
||||||
background = QtGui.QColor(Settings().value(self.settingsSection
|
background = QtGui.QColor(Settings().value(self.settingsSection
|
||||||
+ u'/background color', QtCore.QVariant(u'#000000')))
|
+ u'/background color', QtCore.QVariant(u'#000000')))
|
||||||
self.liveController.imageManager.updateImages(u'image', background)
|
self.liveController.imageManager.updateImagesBorder(
|
||||||
|
ImageSource.ImagePlugin, background)
|
||||||
|
@ -229,8 +229,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
bitem = self.listView.item(item.row())
|
bitem = self.listView.item(item.row())
|
||||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
name = os.path.split(filename)[1]
|
if self.plugin.liveController.display.directImage(
|
||||||
if self.plugin.liveController.display.directImage(name,
|
|
||||||
filename, background):
|
filename, background):
|
||||||
self.resetAction.setVisible(True)
|
self.resetAction.setVisible(True)
|
||||||
else:
|
else:
|
||||||
|
@ -278,8 +278,7 @@ class MessageListener(object):
|
|||||||
item = message[0]
|
item = message[0]
|
||||||
log.debug(u'Startup called with message %s' % message)
|
log.debug(u'Startup called with message %s' % message)
|
||||||
hide_mode = message[2]
|
hide_mode = message[2]
|
||||||
file = os.path.join(item.get_frame_path(),
|
file = os.path.join(item.get_frame_path(), item.get_frame_title())
|
||||||
item.get_frame_title())
|
|
||||||
self.handler = item.title
|
self.handler = item.title
|
||||||
if self.handler == self.mediaitem.Automatic:
|
if self.handler == self.mediaitem.Automatic:
|
||||||
self.handler = self.mediaitem.findControllerByType(file)
|
self.handler = self.mediaitem.findControllerByType(file)
|
||||||
|
@ -541,12 +541,13 @@ class SongImportSourcePage(QtGui.QWizardPage):
|
|||||||
if wizard.formatWidgets[format][u'fileListWidget'].count() > 0:
|
if wizard.formatWidgets[format][u'fileListWidget'].count() > 0:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
filepath = wizard.formatWidgets[format][u'filepathEdit'].text()
|
filepath = unicode(
|
||||||
if not filepath.isEmpty():
|
wizard.formatWidgets[format][u'filepathEdit'].text())
|
||||||
if select_mode == SongFormatSelect.SingleFile \
|
if filepath:
|
||||||
and os.path.isfile(filepath):
|
if select_mode == SongFormatSelect.SingleFile and \
|
||||||
|
os.path.isfile(filepath):
|
||||||
return True
|
return True
|
||||||
elif select_mode == SongFormatSelect.SingleFolder \
|
elif select_mode == SongFormatSelect.SingleFolder and \
|
||||||
and os.path.isdir(filepath):
|
os.path.isdir(filepath):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -36,6 +36,104 @@ from ui import SongStrings
|
|||||||
|
|
||||||
WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
|
WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
|
||||||
APOSTROPHE = re.compile(u'[\'`’ʻ′]', re.UNICODE)
|
APOSTROPHE = re.compile(u'[\'`’ʻ′]', re.UNICODE)
|
||||||
|
PATTERN = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'"
|
||||||
|
r"([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I)
|
||||||
|
# RTF control words which specify a "destination" to be ignored.
|
||||||
|
DESTINATIONS = frozenset((
|
||||||
|
u'aftncn', u'aftnsep', u'aftnsepc', u'annotation', u'atnauthor',
|
||||||
|
u'atndate', u'atnicn', u'atnid', u'atnparent', u'atnref', u'atntime',
|
||||||
|
u'atrfend', u'atrfstart', u'author', u'background', u'bkmkend',
|
||||||
|
u'bkmkstart', u'blipuid', u'buptim', u'category',
|
||||||
|
u'colorschememapping', u'colortbl', u'comment', u'company', u'creatim',
|
||||||
|
u'datafield', u'datastore', u'defchp', u'defpap', u'do', u'doccomm',
|
||||||
|
u'docvar', u'dptxbxtext', u'ebcend', u'ebcstart', u'factoidname',
|
||||||
|
u'falt', u'fchars', u'ffdeftext', u'ffentrymcr', u'ffexitmcr',
|
||||||
|
u'ffformat', u'ffhelptext', u'ffl', u'ffname', u'ffstattext', u'field',
|
||||||
|
u'file', u'filetbl', u'fldinst', u'fldrslt', u'fldtype', u'fname',
|
||||||
|
u'fontemb', u'fontfile', u'footer', u'footerf', u'footerl', u'footerr',
|
||||||
|
u'footnote', u'formfield', u'ftncn', u'ftnsep', u'ftnsepc', u'g',
|
||||||
|
u'generator', u'gridtbl', u'header', u'headerf', u'headerl',
|
||||||
|
u'headerr', u'hl', u'hlfr', u'hlinkbase', u'hlloc', u'hlsrc', u'hsv',
|
||||||
|
u'htmltag', u'info', u'keycode', u'keywords', u'latentstyles',
|
||||||
|
u'lchars', u'levelnumbers', u'leveltext', u'lfolevel', u'linkval',
|
||||||
|
u'list', u'listlevel', u'listname', u'listoverride',
|
||||||
|
u'listoverridetable', u'listpicture', u'liststylename', u'listtable',
|
||||||
|
u'listtext', u'lsdlockedexcept', u'macc', u'maccPr', u'mailmerge',
|
||||||
|
u'maln', u'malnScr', u'manager', u'margPr', u'mbar', u'mbarPr',
|
||||||
|
u'mbaseJc', u'mbegChr', u'mborderBox', u'mborderBoxPr', u'mbox',
|
||||||
|
u'mboxPr', u'mchr', u'mcount', u'mctrlPr', u'md', u'mdeg', u'mdegHide',
|
||||||
|
u'mden', u'mdiff', u'mdPr', u'me', u'mendChr', u'meqArr', u'meqArrPr',
|
||||||
|
u'mf', u'mfName', u'mfPr', u'mfunc', u'mfuncPr', u'mgroupChr',
|
||||||
|
u'mgroupChrPr', u'mgrow', u'mhideBot', u'mhideLeft', u'mhideRight',
|
||||||
|
u'mhideTop', u'mhtmltag', u'mlim', u'mlimloc', u'mlimlow',
|
||||||
|
u'mlimlowPr', u'mlimupp', u'mlimuppPr', u'mm', u'mmaddfieldname',
|
||||||
|
u'mmath', u'mmathPict', u'mmathPr', u'mmaxdist', u'mmc', u'mmcJc',
|
||||||
|
u'mmconnectstr', u'mmconnectstrdata', u'mmcPr', u'mmcs',
|
||||||
|
u'mmdatasource', u'mmheadersource', u'mmmailsubject', u'mmodso',
|
||||||
|
u'mmodsofilter', u'mmodsofldmpdata', u'mmodsomappedname',
|
||||||
|
u'mmodsoname', u'mmodsorecipdata', u'mmodsosort', u'mmodsosrc',
|
||||||
|
u'mmodsotable', u'mmodsoudl', u'mmodsoudldata', u'mmodsouniquetag',
|
||||||
|
u'mmPr', u'mmquery', u'mmr', u'mnary', u'mnaryPr', u'mnoBreak',
|
||||||
|
u'mnum', u'mobjDist', u'moMath', u'moMathPara', u'moMathParaPr',
|
||||||
|
u'mopEmu', u'mphant', u'mphantPr', u'mplcHide', u'mpos', u'mr',
|
||||||
|
u'mrad', u'mradPr', u'mrPr', u'msepChr', u'mshow', u'mshp', u'msPre',
|
||||||
|
u'msPrePr', u'msSub', u'msSubPr', u'msSubSup', u'msSubSupPr', u'msSup',
|
||||||
|
u'msSupPr', u'mstrikeBLTR', u'mstrikeH', u'mstrikeTLBR', u'mstrikeV',
|
||||||
|
u'msub', u'msubHide', u'msup', u'msupHide', u'mtransp', u'mtype',
|
||||||
|
u'mvertJc', u'mvfmf', u'mvfml', u'mvtof', u'mvtol', u'mzeroAsc',
|
||||||
|
u'mzeroDesc', u'mzeroWid', u'nesttableprops', u'nextfile',
|
||||||
|
u'nonesttables', u'objalias', u'objclass', u'objdata', u'object',
|
||||||
|
u'objname', u'objsect', u'objtime', u'oldcprops', u'oldpprops',
|
||||||
|
u'oldsprops', u'oldtprops', u'oleclsid', u'operator', u'panose',
|
||||||
|
u'password', u'passwordhash', u'pgp', u'pgptbl', u'picprop', u'pict',
|
||||||
|
u'pn', u'pnseclvl', u'pntext', u'pntxta', u'pntxtb', u'printim',
|
||||||
|
u'private', u'propname', u'protend', u'protstart', u'protusertbl',
|
||||||
|
u'pxe', u'result', u'revtbl', u'revtim', u'rsidtbl', u'rxe', u'shp',
|
||||||
|
u'shpgrp', u'shpinst', u'shppict', u'shprslt', u'shptxt', u'sn', u'sp',
|
||||||
|
u'staticval', u'stylesheet', u'subject', u'sv', u'svb', u'tc',
|
||||||
|
u'template', u'themedata', u'title', u'txe', u'ud', u'upr',
|
||||||
|
u'userprops', u'wgrffmtfilter', u'windowcaption', u'writereservation',
|
||||||
|
u'writereservhash', u'xe', u'xform', u'xmlattrname', u'xmlattrvalue',
|
||||||
|
u'xmlclose', u'xmlname', u'xmlnstbl', u'xmlopen'))
|
||||||
|
# Translation of some special characters.
|
||||||
|
SPECIAL_CHARS = {
|
||||||
|
u'par': u'\n',
|
||||||
|
u'sect': u'\n\n',
|
||||||
|
# Required page and column break.
|
||||||
|
# Would be good if we could split verse into subverses here.
|
||||||
|
u'page': u'\n\n',
|
||||||
|
u'column': u'\n\n',
|
||||||
|
# Soft breaks.
|
||||||
|
u'softpage': u'[---]',
|
||||||
|
u'softcol': u'[---]',
|
||||||
|
u'line': u'\n',
|
||||||
|
u'tab': u'\t',
|
||||||
|
u'emdash': u'\u2014',
|
||||||
|
u'endash': u'\u2013',
|
||||||
|
u'emspace': u'\u2003',
|
||||||
|
u'enspace': u'\u2002',
|
||||||
|
u'qmspace': u'\u2005',
|
||||||
|
u'bullet': u'\u2022',
|
||||||
|
u'lquote': u'\u2018',
|
||||||
|
u'rquote': u'\u2019',
|
||||||
|
u'ldblquote': u'\u201C',
|
||||||
|
u'rdblquote': u'\u201D',
|
||||||
|
u'ltrmark': u'\u200E',
|
||||||
|
u'rtlmark': u'\u200F',
|
||||||
|
u'zwj': u'\u200D',
|
||||||
|
u'zwnj': u'\u200C'}
|
||||||
|
CHARSET_MAPPING = {
|
||||||
|
u'fcharset0': u'cp1252',
|
||||||
|
u'fcharset161': u'cp1253',
|
||||||
|
u'fcharset162': u'cp1254',
|
||||||
|
u'fcharset163': u'cp1258',
|
||||||
|
u'fcharset177': u'cp1255',
|
||||||
|
u'fcharset178': u'cp1256',
|
||||||
|
u'fcharset186': u'cp1257',
|
||||||
|
u'fcharset204': u'cp1251',
|
||||||
|
u'fcharset222': u'cp874',
|
||||||
|
u'fcharset238': u'cp1250'}
|
||||||
|
|
||||||
|
|
||||||
class VerseType(object):
|
class VerseType(object):
|
||||||
"""
|
"""
|
||||||
@ -366,6 +464,136 @@ def clean_song(manager, song):
|
|||||||
if song.copyright:
|
if song.copyright:
|
||||||
song.copyright = CONTROL_CHARS.sub(u'', song.copyright).strip()
|
song.copyright = CONTROL_CHARS.sub(u'', song.copyright).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def get_encoding(font, font_table, default_encoding, failed=False):
|
||||||
|
"""
|
||||||
|
Finds an encoding to use. Asks user, if necessary.
|
||||||
|
|
||||||
|
``font``
|
||||||
|
The number of currently active font.
|
||||||
|
|
||||||
|
``font_table``
|
||||||
|
Dictionary of fonts and respective encodings.
|
||||||
|
|
||||||
|
``default_encoding``
|
||||||
|
The defaul encoding to use when font_table is empty or no font is used.
|
||||||
|
|
||||||
|
``failed``
|
||||||
|
A boolean indicating whether the previous encoding didn't work.
|
||||||
|
"""
|
||||||
|
encoding = None
|
||||||
|
if font in font_table:
|
||||||
|
encoding = font_table[font]
|
||||||
|
if not encoding and default_encoding:
|
||||||
|
encoding = default_encoding
|
||||||
|
if not encoding or failed:
|
||||||
|
encoding = retrieve_windows_encoding()
|
||||||
|
default_encoding = encoding
|
||||||
|
font_table[font] = encoding
|
||||||
|
return encoding, default_encoding
|
||||||
|
|
||||||
|
|
||||||
|
def strip_rtf(text, default_encoding=None):
|
||||||
|
"""
|
||||||
|
This function strips RTF control structures and returns an unicode string.
|
||||||
|
|
||||||
|
Thanks to Markus Jarderot (MizardX) for this code, used by permission.
|
||||||
|
http://stackoverflow.com/questions/188545
|
||||||
|
|
||||||
|
``text``
|
||||||
|
RTF-encoded text, a string.
|
||||||
|
|
||||||
|
``default_encoding``
|
||||||
|
Default encoding to use when no encoding is specified.
|
||||||
|
"""
|
||||||
|
# Current font is the font tag we last met.
|
||||||
|
font = u''
|
||||||
|
# Character encoding is defined inside fonttable.
|
||||||
|
# font_table could contain eg u'0': u'cp1252'
|
||||||
|
font_table = {u'': u''}
|
||||||
|
# Stack of things to keep track of when entering/leaving groups.
|
||||||
|
stack = []
|
||||||
|
# Whether this group (and all inside it) are "ignorable".
|
||||||
|
ignorable = False
|
||||||
|
# Number of ASCII characters to skip after an unicode character.
|
||||||
|
ucskip = 1
|
||||||
|
# Number of ASCII characters left to skip.
|
||||||
|
curskip = 0
|
||||||
|
# Output buffer.
|
||||||
|
out = []
|
||||||
|
for match in PATTERN.finditer(text):
|
||||||
|
word, arg, hex, char, brace, tchar = match.groups()
|
||||||
|
if brace:
|
||||||
|
curskip = 0
|
||||||
|
if brace == u'{':
|
||||||
|
# Push state
|
||||||
|
stack.append((ucskip, ignorable, font))
|
||||||
|
elif brace == u'}':
|
||||||
|
# Pop state
|
||||||
|
ucskip, ignorable, font = stack.pop()
|
||||||
|
# \x (not a letter)
|
||||||
|
elif char:
|
||||||
|
curskip = 0
|
||||||
|
if char == u'~' and not ignorable:
|
||||||
|
out.append(u'\xA0')
|
||||||
|
elif char in u'{}\\' and not ignorable:
|
||||||
|
out.append(char)
|
||||||
|
elif char == u'-' and not ignorable:
|
||||||
|
out.append(u'\u00AD')
|
||||||
|
elif char == u'_' and not ignorable:
|
||||||
|
out.append(u'\u2011')
|
||||||
|
elif char == u'*':
|
||||||
|
ignorable = True
|
||||||
|
# \command
|
||||||
|
elif word:
|
||||||
|
curskip = 0
|
||||||
|
if word in DESTINATIONS:
|
||||||
|
ignorable = True
|
||||||
|
elif word in SPECIAL_CHARS:
|
||||||
|
out.append(SPECIAL_CHARS[word])
|
||||||
|
elif word == u'uc':
|
||||||
|
ucskip = int(arg)
|
||||||
|
elif word == u' ':
|
||||||
|
c = int(arg)
|
||||||
|
if c < 0:
|
||||||
|
c += 0x10000
|
||||||
|
out.append(unichr(c))
|
||||||
|
curskip = ucskip
|
||||||
|
elif word == u'fonttbl':
|
||||||
|
ignorable = True
|
||||||
|
elif word == u'f':
|
||||||
|
font = arg
|
||||||
|
elif word == u'ansicpg':
|
||||||
|
font_table[font] = 'cp' + arg
|
||||||
|
elif word == u'fcharset' and font not in font_table and \
|
||||||
|
word + arg in CHARSET_MAPPING:
|
||||||
|
# \ansicpg overrides \fcharset, if present.
|
||||||
|
font_table[font] = CHARSET_MAPPING[word + arg]
|
||||||
|
# \'xx
|
||||||
|
elif hex:
|
||||||
|
if curskip > 0:
|
||||||
|
curskip -= 1
|
||||||
|
elif not ignorable:
|
||||||
|
charcode = int(hex, 16)
|
||||||
|
failed = False
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
encoding, default_encoding = get_encoding(font,
|
||||||
|
font_table, default_encoding, failed=failed)
|
||||||
|
out.append(chr(charcode).decode(encoding))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
failed = True
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
elif tchar:
|
||||||
|
if curskip > 0:
|
||||||
|
curskip -= 1
|
||||||
|
elif not ignorable:
|
||||||
|
out.append(tchar)
|
||||||
|
text = u''.join(out)
|
||||||
|
return text, default_encoding
|
||||||
|
|
||||||
|
|
||||||
from xml import OpenLyrics, SongXML
|
from xml import OpenLyrics, SongXML
|
||||||
from songstab import SongsTab
|
from songstab import SongsTab
|
||||||
from mediaitem import SongMediaItem
|
from mediaitem import SongMediaItem
|
||||||
|
@ -36,7 +36,7 @@ import re
|
|||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.plugins.songs.lib import VerseType
|
from openlp.plugins.songs.lib import VerseType
|
||||||
from openlp.plugins.songs.lib import retrieve_windows_encoding
|
from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf
|
||||||
from songimport import SongImport
|
from songimport import SongImport
|
||||||
|
|
||||||
RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
|
RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
|
||||||
@ -45,101 +45,6 @@ SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*')
|
|||||||
NUMBER_REGEX = re.compile(r'[0-9]+')
|
NUMBER_REGEX = re.compile(r'[0-9]+')
|
||||||
NOTE_REGEX = re.compile(r'\(.*?\)')
|
NOTE_REGEX = re.compile(r'\(.*?\)')
|
||||||
|
|
||||||
def strip_rtf(blob, encoding):
|
|
||||||
depth = 0
|
|
||||||
control = False
|
|
||||||
clear_text = []
|
|
||||||
control_word = []
|
|
||||||
|
|
||||||
# workaround for \tx bug: remove one pair of curly braces
|
|
||||||
# if \tx is encountered
|
|
||||||
match = RTF_STRIPPING_REGEX.search(blob)
|
|
||||||
if match:
|
|
||||||
# start and end indices of match are curly braces - filter them out
|
|
||||||
blob = ''.join([blob[i] for i in xrange(len(blob))
|
|
||||||
if i != match.start() and i !=match.end()])
|
|
||||||
|
|
||||||
for c in blob:
|
|
||||||
if control:
|
|
||||||
# for delimiters, set control to False
|
|
||||||
if c == '{':
|
|
||||||
if control_word:
|
|
||||||
depth += 1
|
|
||||||
control = False
|
|
||||||
elif c == '}':
|
|
||||||
if control_word:
|
|
||||||
depth -= 1
|
|
||||||
control = False
|
|
||||||
elif c == '\\':
|
|
||||||
new_control = bool(control_word)
|
|
||||||
control = False
|
|
||||||
elif c.isspace():
|
|
||||||
control = False
|
|
||||||
else:
|
|
||||||
control_word.append(c)
|
|
||||||
if len(control_word) == 3 and control_word[0] == '\'':
|
|
||||||
control = False
|
|
||||||
if not control:
|
|
||||||
if not control_word:
|
|
||||||
if c == '{' or c == '}' or c == '\\':
|
|
||||||
clear_text.append(c)
|
|
||||||
else:
|
|
||||||
control_str = ''.join(control_word)
|
|
||||||
if control_str == 'par' or control_str == 'line':
|
|
||||||
clear_text.append(u'\n')
|
|
||||||
elif control_str == 'tab':
|
|
||||||
clear_text.append(u'\t')
|
|
||||||
# Prefer the encoding specified by the RTF data to that
|
|
||||||
# specified by the Paradox table header
|
|
||||||
# West European encoding
|
|
||||||
elif control_str == 'fcharset0':
|
|
||||||
encoding = u'cp1252'
|
|
||||||
# Greek encoding
|
|
||||||
elif control_str == 'fcharset161':
|
|
||||||
encoding = u'cp1253'
|
|
||||||
# Turkish encoding
|
|
||||||
elif control_str == 'fcharset162':
|
|
||||||
encoding = u'cp1254'
|
|
||||||
# Vietnamese encoding
|
|
||||||
elif control_str == 'fcharset163':
|
|
||||||
encoding = u'cp1258'
|
|
||||||
# Hebrew encoding
|
|
||||||
elif control_str == 'fcharset177':
|
|
||||||
encoding = u'cp1255'
|
|
||||||
# Arabic encoding
|
|
||||||
elif control_str == 'fcharset178':
|
|
||||||
encoding = u'cp1256'
|
|
||||||
# Baltic encoding
|
|
||||||
elif control_str == 'fcharset186':
|
|
||||||
encoding = u'cp1257'
|
|
||||||
# Cyrillic encoding
|
|
||||||
elif control_str == 'fcharset204':
|
|
||||||
encoding = u'cp1251'
|
|
||||||
# Thai encoding
|
|
||||||
elif control_str == 'fcharset222':
|
|
||||||
encoding = u'cp874'
|
|
||||||
# Central+East European encoding
|
|
||||||
elif control_str == 'fcharset238':
|
|
||||||
encoding = u'cp1250'
|
|
||||||
elif control_str[0] == '\'':
|
|
||||||
s = chr(int(control_str[1:3], 16))
|
|
||||||
clear_text.append(s.decode(encoding))
|
|
||||||
del control_word[:]
|
|
||||||
if c == '\\' and new_control:
|
|
||||||
control = True
|
|
||||||
elif c == '{':
|
|
||||||
depth += 1
|
|
||||||
elif c == '}':
|
|
||||||
depth -= 1
|
|
||||||
elif depth > 2:
|
|
||||||
continue
|
|
||||||
elif c == '\n' or c == '\r':
|
|
||||||
continue
|
|
||||||
elif c == '\\':
|
|
||||||
control = True
|
|
||||||
else:
|
|
||||||
clear_text.append(c)
|
|
||||||
return u''.join(clear_text)
|
|
||||||
|
|
||||||
class FieldDescEntry:
|
class FieldDescEntry:
|
||||||
def __init__(self, name, type, size):
|
def __init__(self, name, type, size):
|
||||||
@ -274,7 +179,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
self.addAuthor(author_name.strip())
|
self.addAuthor(author_name.strip())
|
||||||
if words:
|
if words:
|
||||||
# Format the lyrics
|
# Format the lyrics
|
||||||
words = strip_rtf(words, self.encoding)
|
words, self.encoding = strip_rtf(words, self.encoding)
|
||||||
verse_type = VerseType.Tags[VerseType.Verse]
|
verse_type = VerseType.Tags[VerseType.Verse]
|
||||||
for verse in SLIDE_BREAK_REGEX.split(words):
|
for verse in SLIDE_BREAK_REGEX.split(words):
|
||||||
verse = verse.strip()
|
verse = verse.strip()
|
||||||
|
@ -44,6 +44,7 @@ from powersongimport import PowerSongImport
|
|||||||
from ewimport import EasyWorshipSongImport
|
from ewimport import EasyWorshipSongImport
|
||||||
from songbeamerimport import SongBeamerImport
|
from songbeamerimport import SongBeamerImport
|
||||||
from songshowplusimport import SongShowPlusImport
|
from songshowplusimport import SongShowPlusImport
|
||||||
|
from sundayplusimport import SundayPlusImport
|
||||||
from foilpresenterimport import FoilPresenterImport
|
from foilpresenterimport import FoilPresenterImport
|
||||||
from zionworximport import ZionWorxImport
|
from zionworximport import ZionWorxImport
|
||||||
# Imports that might fail
|
# Imports that might fail
|
||||||
@ -145,9 +146,10 @@ class SongFormat(object):
|
|||||||
SongBeamer = 11
|
SongBeamer = 11
|
||||||
SongShowPlus = 12
|
SongShowPlus = 12
|
||||||
SongsOfFellowship = 13
|
SongsOfFellowship = 13
|
||||||
WordsOfWorship = 14
|
SundayPlus = 14
|
||||||
ZionWorx = 15
|
WordsOfWorship = 15
|
||||||
#CSV = 16
|
ZionWorx = 16
|
||||||
|
#CSV = 17
|
||||||
|
|
||||||
# Set optional attribute defaults
|
# Set optional attribute defaults
|
||||||
__defaults__ = {
|
__defaults__ = {
|
||||||
@ -275,6 +277,13 @@ class SongFormat(object):
|
|||||||
'The Songs of Fellowship importer has been disabled because '
|
'The Songs of Fellowship importer has been disabled because '
|
||||||
'OpenLP cannot access OpenOffice or LibreOffice.')
|
'OpenLP cannot access OpenOffice or LibreOffice.')
|
||||||
},
|
},
|
||||||
|
SundayPlus: {
|
||||||
|
u'class': SundayPlusImport,
|
||||||
|
u'name': u'SundayPlus',
|
||||||
|
u'prefix': u'sundayPlus',
|
||||||
|
u'filter': u'%s (*.ptf)' % translate(
|
||||||
|
'SongsPlugin.ImportWizardForm', 'SundayPlus Song Files')
|
||||||
|
},
|
||||||
WordsOfWorship: {
|
WordsOfWorship: {
|
||||||
u'class': WowImport,
|
u'class': WowImport,
|
||||||
u'name': u'Words of Worship',
|
u'name': u'Words of Worship',
|
||||||
@ -322,6 +331,7 @@ class SongFormat(object):
|
|||||||
SongFormat.SongBeamer,
|
SongFormat.SongBeamer,
|
||||||
SongFormat.SongShowPlus,
|
SongFormat.SongShowPlus,
|
||||||
SongFormat.SongsOfFellowship,
|
SongFormat.SongsOfFellowship,
|
||||||
|
SongFormat.SundayPlus,
|
||||||
SongFormat.WordsOfWorship,
|
SongFormat.WordsOfWorship,
|
||||||
SongFormat.ZionWorx
|
SongFormat.ZionWorx
|
||||||
]
|
]
|
||||||
|
195
openlp/plugins/songs/lib/sundayplusimport.py
Normal file
195
openlp/plugins/songs/lib/sundayplusimport.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Edwin Lunando, 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 #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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 re
|
||||||
|
|
||||||
|
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
|
||||||
|
from openlp.plugins.songs.lib import strip_rtf
|
||||||
|
from openlp.plugins.songs.lib.songimport import SongImport
|
||||||
|
|
||||||
|
HOTKEY_TO_VERSE_TYPE = {
|
||||||
|
u'1': u'v1',
|
||||||
|
u'2': u'v2',
|
||||||
|
u'3': u'v3',
|
||||||
|
u'4': u'v4',
|
||||||
|
u'5': u'v5',
|
||||||
|
u'6': u'v6',
|
||||||
|
u'7': u'v7',
|
||||||
|
u'8': u'v8',
|
||||||
|
u'9': u'v9',
|
||||||
|
u'C': u'c',
|
||||||
|
u'+': u'b',
|
||||||
|
u'Z': u'o'}
|
||||||
|
|
||||||
|
class SundayPlusImport(SongImport):
|
||||||
|
"""
|
||||||
|
Import Sunday Plus songs
|
||||||
|
|
||||||
|
The format examples can be found attached to bug report at
|
||||||
|
<http://support.openlp.org/issues/395>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, manager, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialise the class.
|
||||||
|
"""
|
||||||
|
SongImport.__init__(self, manager, **kwargs)
|
||||||
|
self.encoding = u'us-ascii'
|
||||||
|
|
||||||
|
def doImport(self):
|
||||||
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
|
for filename in self.importSource:
|
||||||
|
if self.stopImportFlag:
|
||||||
|
return
|
||||||
|
song_file = open(filename)
|
||||||
|
self.doImportFile(song_file)
|
||||||
|
song_file.close()
|
||||||
|
|
||||||
|
def doImportFile(self, file):
|
||||||
|
"""
|
||||||
|
Process the Sunday Plus file object.
|
||||||
|
"""
|
||||||
|
self.setDefaults()
|
||||||
|
if not self.parse(file.read()):
|
||||||
|
self.logError(file.name)
|
||||||
|
return
|
||||||
|
if not self.title:
|
||||||
|
self.title = self.titleFromFilename(file.name)
|
||||||
|
if not self.finish():
|
||||||
|
self.logError(file.name)
|
||||||
|
|
||||||
|
def parse(self, data, cell=False):
|
||||||
|
if len(data) == 0 or data[0:1] != '[' or data[-1] != ']':
|
||||||
|
self.logError(u'File is malformed')
|
||||||
|
return False
|
||||||
|
i = 1
|
||||||
|
verse_type = VerseType.Tags[VerseType.Verse]
|
||||||
|
while i < len(data):
|
||||||
|
# Data is held as #name: value pairs inside groups marked as [].
|
||||||
|
# Now we are looking for the name.
|
||||||
|
if data[i:i + 1] == '#':
|
||||||
|
name_end = data.find(':', i + 1)
|
||||||
|
name = data[i + 1:name_end]
|
||||||
|
i = name_end + 1
|
||||||
|
while data[i:i + 1] == ' ':
|
||||||
|
i += 1
|
||||||
|
if data[i:i + 1] == '"':
|
||||||
|
end = data.find('"', i + 1)
|
||||||
|
value = data[i + 1:end]
|
||||||
|
elif data[i:i + 1] == '[':
|
||||||
|
j = i
|
||||||
|
inside_quotes = False
|
||||||
|
while j < len(data):
|
||||||
|
char = data[j:j + 1]
|
||||||
|
if char == '"':
|
||||||
|
inside_quotes = not inside_quotes
|
||||||
|
elif not inside_quotes and char == ']':
|
||||||
|
end = j + 1
|
||||||
|
break
|
||||||
|
j += 1
|
||||||
|
value = data[i:end]
|
||||||
|
else:
|
||||||
|
end = data.find(',', i + 1)
|
||||||
|
if data.find('(', i, end) != -1:
|
||||||
|
end = data.find(')', i) + 1
|
||||||
|
value = data[i:end]
|
||||||
|
# If we are in the main group.
|
||||||
|
if cell == False:
|
||||||
|
if name == 'title':
|
||||||
|
self.title = self.decode(self.unescape(value))
|
||||||
|
elif name == 'Author':
|
||||||
|
author = self.decode(self.unescape(value))
|
||||||
|
if len(author):
|
||||||
|
self.addAuthor(author)
|
||||||
|
elif name == 'Copyright':
|
||||||
|
self.copyright = self.decode(self.unescape(value))
|
||||||
|
elif name[0:4] == 'CELL':
|
||||||
|
self.parse(value, cell = name[4:])
|
||||||
|
# We are in a verse group.
|
||||||
|
else:
|
||||||
|
if name == 'MARKER_NAME':
|
||||||
|
value = value.strip()
|
||||||
|
if len(value):
|
||||||
|
verse_type = VerseType.Tags[
|
||||||
|
VerseType.from_loose_input(value[0])]
|
||||||
|
if len(value) >= 2 and value[-1] in ['0', '1', '2',
|
||||||
|
'3', '4', '5', '6', '7', '8', '9']:
|
||||||
|
verse_type = "%s%s" % (verse_type, value[-1])
|
||||||
|
elif name == 'Hotkey':
|
||||||
|
# Hotkey always appears after MARKER_NAME, so it
|
||||||
|
# effectively overrides MARKER_NAME, if present.
|
||||||
|
if len(value) and \
|
||||||
|
value in HOTKEY_TO_VERSE_TYPE.keys():
|
||||||
|
verse_type = HOTKEY_TO_VERSE_TYPE[value]
|
||||||
|
if name == 'rtf':
|
||||||
|
value = self.unescape(value)
|
||||||
|
verse, self.encoding = strip_rtf(value, self.encoding)
|
||||||
|
lines = verse.strip().split('\n')
|
||||||
|
# If any line inside any verse contains CCLI or
|
||||||
|
# only Public Domain, we treat this as special data:
|
||||||
|
# we remove that line and add data to specific field.
|
||||||
|
for i in xrange(len(lines)):
|
||||||
|
lines[i] = lines[i].strip()
|
||||||
|
line = lines[i]
|
||||||
|
if line[:4].lower() == u'ccli':
|
||||||
|
m = re.search(r'[0-9]+', line)
|
||||||
|
if m:
|
||||||
|
self.ccliNumber = int(m.group(0))
|
||||||
|
lines.pop(i)
|
||||||
|
elif line.lower() == u'public domain':
|
||||||
|
self.copyright = u'Public Domain'
|
||||||
|
lines.pop(i)
|
||||||
|
self.addVerse('\n'.join(lines).strip(), verse_type)
|
||||||
|
if end == -1:
|
||||||
|
break
|
||||||
|
i = end + 1
|
||||||
|
i += 1
|
||||||
|
return True
|
||||||
|
|
||||||
|
def titleFromFilename(self, filename):
|
||||||
|
title = os.path.split(filename)[1]
|
||||||
|
if title.endswith(u'.ptf'):
|
||||||
|
title = title[:-4]
|
||||||
|
# For some strange reason all example files names ended with 1-7.
|
||||||
|
if title.endswith(u'1-7'):
|
||||||
|
title = title[:-3]
|
||||||
|
return title.replace(u'_', u' ')
|
||||||
|
|
||||||
|
def decode(self, blob):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
return unicode(blob, self.encoding)
|
||||||
|
except:
|
||||||
|
self.encoding = retrieve_windows_encoding()
|
||||||
|
|
||||||
|
def unescape(self, text):
|
||||||
|
text = text.replace('^^', '"')
|
||||||
|
text = text.replace('^', '\'')
|
||||||
|
return text.strip()
|
||||||
|
|
2645
resources/i18n/af.ts
2645
resources/i18n/af.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/cs.ts
2645
resources/i18n/cs.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/da.ts
2645
resources/i18n/da.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/de.ts
2645
resources/i18n/de.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/el.ts
2645
resources/i18n/el.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/en.ts
2645
resources/i18n/en.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2702
resources/i18n/es.ts
2702
resources/i18n/es.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/et.ts
2645
resources/i18n/et.ts
File diff suppressed because it is too large
Load Diff
2653
resources/i18n/fi.ts
2653
resources/i18n/fi.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/fr.ts
2645
resources/i18n/fr.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/hu.ts
2645
resources/i18n/hu.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/id.ts
2645
resources/i18n/id.ts
File diff suppressed because it is too large
Load Diff
2653
resources/i18n/it.ts
2653
resources/i18n/it.ts
File diff suppressed because it is too large
Load Diff
2756
resources/i18n/ja.ts
2756
resources/i18n/ja.ts
File diff suppressed because it is too large
Load Diff
2689
resources/i18n/ko.ts
2689
resources/i18n/ko.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/nb.ts
2645
resources/i18n/nb.ts
File diff suppressed because it is too large
Load Diff
2831
resources/i18n/nl.ts
2831
resources/i18n/nl.ts
File diff suppressed because it is too large
Load Diff
2653
resources/i18n/pl.ts
2653
resources/i18n/pl.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/ru.ts
2645
resources/i18n/ru.ts
File diff suppressed because it is too large
Load Diff
2653
resources/i18n/sq.ts
2653
resources/i18n/sq.ts
File diff suppressed because it is too large
Load Diff
2645
resources/i18n/sv.ts
2645
resources/i18n/sv.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -199,7 +199,11 @@ def download_translations():
|
|||||||
request = urllib2.Request(url + '?details')
|
request = urllib2.Request(url + '?details')
|
||||||
request.add_header('Authorization', auth_header)
|
request.add_header('Authorization', auth_header)
|
||||||
print_verbose(u'Downloading list of languages from: %s' % url)
|
print_verbose(u'Downloading list of languages from: %s' % url)
|
||||||
|
try:
|
||||||
json_response = urllib2.urlopen(request)
|
json_response = urllib2.urlopen(request)
|
||||||
|
except urllib2.HTTPError:
|
||||||
|
print_quiet(u'Username or password incorrect.')
|
||||||
|
return False
|
||||||
json_dict = json.loads(json_response.read())
|
json_dict = json.loads(json_response.read())
|
||||||
languages = [lang[u'code'] for lang in json_dict[u'available_languages']]
|
languages = [lang[u'code'] for lang in json_dict[u'available_languages']]
|
||||||
for language in languages:
|
for language in languages:
|
||||||
@ -214,6 +218,7 @@ def download_translations():
|
|||||||
fd.write(response.read())
|
fd.write(response.read())
|
||||||
fd.close()
|
fd.close()
|
||||||
print_quiet(u' Done.')
|
print_quiet(u' Done.')
|
||||||
|
return True
|
||||||
|
|
||||||
def prepare_project():
|
def prepare_project():
|
||||||
"""
|
"""
|
||||||
@ -310,7 +315,8 @@ def process_stack(command_stack):
|
|||||||
for command in command_stack:
|
for command in command_stack:
|
||||||
print_quiet(u'%d.' % (command_stack.current_index), False)
|
print_quiet(u'%d.' % (command_stack.current_index), False)
|
||||||
if command == Command.Download:
|
if command == Command.Download:
|
||||||
download_translations()
|
if not download_translations():
|
||||||
|
return
|
||||||
elif command == Command.Prepare:
|
elif command == Command.Prepare:
|
||||||
prepare_project()
|
prepare_project()
|
||||||
elif command == Command.Update:
|
elif command == Command.Update:
|
||||||
|
Loading…
Reference in New Issue
Block a user