forked from openlp/openlp
Head
This commit is contained in:
commit
577be9a9c1
@ -27,6 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
from openlp.core import main
|
||||
|
||||
|
||||
@ -34,4 +35,10 @@ if __name__ == u'__main__':
|
||||
"""
|
||||
Instantiate and run the application.
|
||||
"""
|
||||
# Mac OS X passes arguments like '-psn_XXXX' to gui application.
|
||||
# This argument is process serial number. However, this causes
|
||||
# conflict with other OpenLP arguments. Since we do not use this
|
||||
# argument we can delete it to avoid any potential conflicts.
|
||||
if sys.platform.startswith('darwin'):
|
||||
sys.argv = filter(lambda x: not x.startswith('-psn'), sys.argv)
|
||||
main()
|
||||
|
@ -36,6 +36,23 @@ from PyQt4 import QtCore, QtGui, Qt
|
||||
|
||||
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):
|
||||
"""
|
||||
An enumeration class for types of media.
|
||||
@ -265,14 +282,15 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
if image_ratio == resize_ratio:
|
||||
# We neither need to centre the image nor add "bars" to the image.
|
||||
return preview
|
||||
realw = preview.width()
|
||||
realh = preview.height()
|
||||
real_width = preview.width()
|
||||
real_height = preview.height()
|
||||
# and move it to the centre of the preview space
|
||||
new_image = QtGui.QImage(width, height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||
painter.drawImage(
|
||||
(width - real_width) / 2, (height - real_height) / 2, preview)
|
||||
return new_image
|
||||
|
||||
|
||||
|
@ -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.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import Queue
|
||||
|
||||
@ -97,19 +98,34 @@ class Priority(object):
|
||||
|
||||
class Image(object):
|
||||
"""
|
||||
This class represents an image. To mark an image as *dirty* set the instance
|
||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||
to the queue of images to process.
|
||||
This class represents an image. To mark an image as *dirty* call the
|
||||
:class:`ImageManager`'s ``_resetImage`` method with the Image instance as
|
||||
argument.
|
||||
"""
|
||||
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.image = None
|
||||
self.image_bytes = None
|
||||
self.priority = Priority.Normal
|
||||
self.source = source
|
||||
self.background = background
|
||||
self.timestamp = os.stat(path).st_mtime
|
||||
self.secondary_priority = Image.secondary_priority
|
||||
Image.secondary_priority += 1
|
||||
|
||||
@ -118,7 +134,7 @@ class PriorityQueue(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
|
||||
the :class:`Image`'s ``secondary_priority`` attribute. The last value the
|
||||
:class:`Image` instance itself::
|
||||
@ -187,7 +203,7 @@ class ImageManager(QtCore.QObject):
|
||||
for image in self._cache.values():
|
||||
self._resetImage(image)
|
||||
|
||||
def updateImages(self, imageType, background):
|
||||
def updateImagesBorder(self, source, background):
|
||||
"""
|
||||
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
|
||||
# stream to None.
|
||||
for image in self._cache.values():
|
||||
if image.source == imageType:
|
||||
if image.source == source:
|
||||
image.background = background
|
||||
self._resetImage(image)
|
||||
|
||||
def updateImage(self, name, imageType, background):
|
||||
def updateImageBorder(self, path, source, background):
|
||||
"""
|
||||
Border has changed so update the image affected.
|
||||
"""
|
||||
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.
|
||||
for image in self._cache.values():
|
||||
if image.source == imageType and image.name == name:
|
||||
image.background = background
|
||||
self._resetImage(image)
|
||||
image = self._cache[(path, source)]
|
||||
if image.source == source:
|
||||
image.background = background
|
||||
self._resetImage(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_bytes = None
|
||||
self._conversionQueue.modify_priority(image, Priority.Normal)
|
||||
@ -224,13 +244,13 @@ class ImageManager(QtCore.QObject):
|
||||
if not self.imageThread.isRunning():
|
||||
self.imageThread.start()
|
||||
|
||||
def getImage(self, name):
|
||||
def getImage(self, path, source):
|
||||
"""
|
||||
Return the ``QImage`` from the cache. If not present wait for the
|
||||
background thread to process it.
|
||||
"""
|
||||
log.debug(u'getImage %s' % name)
|
||||
image = self._cache[name]
|
||||
log.debug(u'getImage %s' % path)
|
||||
image = self._cache[(path, source)]
|
||||
if image.image is None:
|
||||
self._conversionQueue.modify_priority(image, Priority.High)
|
||||
# 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)
|
||||
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
|
||||
background thread to process it.
|
||||
"""
|
||||
log.debug(u'getImageBytes %s' % name)
|
||||
image = self._cache[name]
|
||||
log.debug(u'getImageBytes %s' % path)
|
||||
image = self._cache[(path, source)]
|
||||
if image.image_bytes is None:
|
||||
self._conversionQueue.modify_priority(image, Priority.Urgent)
|
||||
# make sure we are running and if not give it a kick
|
||||
@ -262,27 +282,22 @@ class ImageManager(QtCore.QObject):
|
||||
time.sleep(0.1)
|
||||
return image.image_bytes
|
||||
|
||||
def deleteImage(self, name):
|
||||
"""
|
||||
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):
|
||||
def addImage(self, path, source, background):
|
||||
"""
|
||||
Add image to cache if it is not already there.
|
||||
"""
|
||||
log.debug(u'addImage %s:%s' % (name, path))
|
||||
if not name in self._cache:
|
||||
image = Image(name, path, source, background)
|
||||
self._cache[name] = image
|
||||
log.debug(u'addImage %s' % path)
|
||||
if not (path, source) in self._cache:
|
||||
image = Image(path, source, background)
|
||||
self._cache[(path, source)] = image
|
||||
self._conversionQueue.put(
|
||||
(image.priority, image.secondary_priority, image))
|
||||
else:
|
||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
||||
# Check if the there are any images with the same path and check if the
|
||||
# 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.
|
||||
if not self.imageThread.isRunning():
|
||||
self.imageThread.start()
|
||||
|
@ -352,24 +352,23 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
``files``
|
||||
The list of files to be loaded
|
||||
"""
|
||||
#FIXME: change local variables to words_separated_by_underscores.
|
||||
newFiles = []
|
||||
errorShown = False
|
||||
new_files = []
|
||||
error_shown = False
|
||||
for file in files:
|
||||
type = file.split(u'.')[-1]
|
||||
if type.lower() not in self.onNewFileMasks:
|
||||
if not errorShown:
|
||||
if not error_shown:
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'Invalid File Type'),
|
||||
unicode(translate('OpenLP.MediaManagerItem',
|
||||
'Invalid File %s.\nSuffix not supported'))
|
||||
% file)
|
||||
errorShown = True
|
||||
error_shown = True
|
||||
else:
|
||||
newFiles.append(file)
|
||||
if files:
|
||||
self.validateAndLoad(newFiles)
|
||||
new_files.append(file)
|
||||
if new_files:
|
||||
self.validateAndLoad(new_files)
|
||||
|
||||
def validateAndLoad(self, files):
|
||||
"""
|
||||
@ -379,30 +378,29 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
``files``
|
||||
The files to be loaded.
|
||||
"""
|
||||
#FIXME: change local variables to words_separated_by_underscores.
|
||||
names = []
|
||||
fullList = []
|
||||
full_list = []
|
||||
for count in range(self.listView.count()):
|
||||
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()))
|
||||
duplicatesFound = False
|
||||
filesAdded = False
|
||||
duplicates_found = False
|
||||
files_added = False
|
||||
for file in files:
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
if filename in names:
|
||||
duplicatesFound = True
|
||||
duplicates_found = True
|
||||
else:
|
||||
filesAdded = True
|
||||
fullList.append(file)
|
||||
if fullList and filesAdded:
|
||||
files_added = True
|
||||
full_list.append(file)
|
||||
if full_list and files_added:
|
||||
self.listView.clear()
|
||||
self.loadList(fullList)
|
||||
lastDir = os.path.split(unicode(files[0]))[0]
|
||||
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
||||
self.loadList(full_list)
|
||||
last_dir = os.path.split(unicode(files[0]))[0]
|
||||
SettingsManager.set_last_dir(self.settingsSection, last_dir)
|
||||
SettingsManager.set_list(self.settingsSection,
|
||||
self.settingsSection, self.getFileList())
|
||||
if duplicatesFound:
|
||||
if duplicates_found:
|
||||
critical_error_message_box(
|
||||
UiStrings().Duplicate,
|
||||
unicode(translate('OpenLP.MediaManagerItem',
|
||||
@ -422,13 +420,13 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
Return the current list of files
|
||||
"""
|
||||
count = 0
|
||||
filelist = []
|
||||
file_list = []
|
||||
while count < self.listView.count():
|
||||
bitem = self.listView.item(count)
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
filelist.append(filename)
|
||||
file_list.append(filename)
|
||||
count += 1
|
||||
return filelist
|
||||
return file_list
|
||||
|
||||
def loadList(self, list):
|
||||
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
|
||||
"""
|
||||
if Settings().value(u'advanced/single click preview',
|
||||
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
|
||||
and self.listView.selectedIndexes() \
|
||||
and self.autoSelectId == -1:
|
||||
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed and \
|
||||
self.listView.selectedIndexes() and self.autoSelectId == -1:
|
||||
self.onPreviewClick(True)
|
||||
|
||||
def onPreviewClick(self, keepFocus=False):
|
||||
|
@ -32,7 +32,7 @@ from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||
ItemCapabilities, FormattingTags
|
||||
ItemCapabilities, FormattingTags, ImageSource
|
||||
from openlp.core.lib.theme import ThemeLevel
|
||||
from openlp.core.ui import MainDisplay, ScreenList
|
||||
|
||||
@ -82,6 +82,9 @@ class Renderer(object):
|
||||
self._calculate_default()
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
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):
|
||||
"""
|
||||
@ -137,8 +140,8 @@ class Renderer(object):
|
||||
self._theme_dimensions[theme_name]
|
||||
# if No file do not update cache
|
||||
if theme_data.background_filename:
|
||||
self.image_manager.addImage(theme_data.theme_name,
|
||||
theme_data.background_filename, u'theme',
|
||||
self.image_manager.addImage(theme_data.background_filename,
|
||||
ImageSource.Theme,
|
||||
QtGui.QColor(theme_data.background_border_color))
|
||||
|
||||
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
|
||||
serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
|
||||
else:
|
||||
self.image_manager.deleteImage(theme_data.theme_name)
|
||||
serviceItem.add_from_text(VERSE)
|
||||
serviceItem.renderer = self
|
||||
serviceItem.raw_footer = FOOTER
|
||||
# if No file do not update cache
|
||||
if theme_data.background_filename:
|
||||
self.image_manager.addImage(theme_data.theme_name,
|
||||
theme_data.background_filename, u'theme',
|
||||
self.image_manager.addImage(theme_data.background_filename,
|
||||
ImageSource.Theme,
|
||||
QtGui.QColor(theme_data.background_border_color))
|
||||
theme_data, main, footer = self.pre_render(theme_data)
|
||||
serviceItem.themedata = theme_data
|
||||
@ -404,10 +406,7 @@ class Renderer(object):
|
||||
if theme_data.font_main_shadow:
|
||||
self.page_width -= 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_frame = self.web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css.
|
||||
html = u"""<!DOCTYPE html><html><head><script>
|
||||
function show_text(newtext) {
|
||||
|
@ -36,7 +36,8 @@ import logging
|
||||
import os
|
||||
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__)
|
||||
|
||||
@ -178,7 +179,7 @@ class ServiceItem(object):
|
||||
self.renderer.set_item_theme(self.theme)
|
||||
self.themedata, self.main, self.footer = self.renderer.pre_render()
|
||||
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:
|
||||
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
|
||||
for page in pages:
|
||||
@ -217,8 +218,8 @@ class ServiceItem(object):
|
||||
self.image_border = background
|
||||
self.service_item_type = ServiceItemType.Image
|
||||
self._raw_frames.append({u'title': title, u'path': path})
|
||||
self.renderer.image_manager.addImage(title, path, u'image',
|
||||
self.image_border)
|
||||
self.renderer.image_manager.addImage(
|
||||
path, ImageSource.ImagePlugin, self.image_border)
|
||||
self._new_item()
|
||||
|
||||
def add_from_text(self, raw_slide, verse_tag=None):
|
||||
@ -432,13 +433,12 @@ class ServiceItem(object):
|
||||
|
||||
def get_rendered_frame(self, row):
|
||||
"""
|
||||
Returns the correct frame for a given list and
|
||||
renders it if required.
|
||||
Returns the correct frame for a given list and renders it if required.
|
||||
"""
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
return self._display_frames[row][u'html'].split(u'\n')[0]
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
return self._raw_frames[row][u'title']
|
||||
return self._raw_frames[row][u'path']
|
||||
else:
|
||||
return self._raw_frames[row][u'image']
|
||||
|
||||
|
@ -41,7 +41,7 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import translate, PluginStatus, Receiver, build_icon, \
|
||||
check_directory_exists
|
||||
from openlp.core.lib.settings import Settings
|
||||
from openlp.core.utils import get_web_page, AppLocation
|
||||
from openlp.core.utils import get_web_page, AppLocation, get_filesystem_encoding
|
||||
from firsttimewizard import Ui_FirstTimeWizard, FirstTimePage
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -65,7 +65,8 @@ class ThemeScreenshotThread(QtCore.QThread):
|
||||
filename = config.get(u'theme_%s' % theme, u'filename')
|
||||
screenshot = config.get(u'theme_%s' % theme, u'screenshot')
|
||||
urllib.urlretrieve(u'%s%s' % (self.parent().web, screenshot),
|
||||
os.path.join(gettempdir(), u'openlp', screenshot))
|
||||
os.path.join(unicode(gettempdir(), get_filesystem_encoding()),
|
||||
u'openlp', screenshot))
|
||||
item = QtGui.QListWidgetItem(title, self.parent().themesListWidget)
|
||||
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(filename))
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
@ -115,7 +116,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
Set up display at start of theme edit.
|
||||
"""
|
||||
self.restart()
|
||||
check_directory_exists(os.path.join(gettempdir(), u'openlp'))
|
||||
check_directory_exists(os.path.join(
|
||||
unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
|
||||
self.noInternetFinishButton.setVisible(False)
|
||||
# Check if this is a re-run of the wizard.
|
||||
self.hasRunWizard = Settings().value(
|
||||
@ -304,8 +306,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
item = self.themesListWidget.item(index)
|
||||
if item.data(QtCore.Qt.UserRole) == QtCore.QVariant(filename):
|
||||
break
|
||||
item.setIcon(build_icon(
|
||||
os.path.join(gettempdir(), u'openlp', screenshot)))
|
||||
item.setIcon(build_icon(os.path.join(unicode(gettempdir(),
|
||||
get_filesystem_encoding()), u'openlp', screenshot)))
|
||||
|
||||
def _getFileSize(self, url):
|
||||
site = urllib.urlopen(url)
|
||||
@ -426,7 +428,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
self._setPluginStatus(self.alertCheckBox, u'alerts/status')
|
||||
if self.webAccess:
|
||||
# Build directories for downloads
|
||||
songs_destination = os.path.join(unicode(gettempdir()), u'openlp')
|
||||
songs_destination = os.path.join(
|
||||
unicode(gettempdir(), get_filesystem_encoding()), u'openlp')
|
||||
bibles_destination = AppLocation.get_section_data_path(u'bibles')
|
||||
themes_destination = AppLocation.get_section_data_path(u'themes')
|
||||
# Download songs
|
||||
|
@ -37,7 +37,7 @@ from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
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.settings import Settings
|
||||
|
||||
@ -274,31 +274,33 @@ class MainDisplay(Display):
|
||||
self.setVisible(False)
|
||||
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.
|
||||
"""
|
||||
self.imageManager.addImage(name, path, u'image', background)
|
||||
if hasattr(self, u'serviceItem'):
|
||||
self.override[u'image'] = name
|
||||
self.override[u'theme'] = self.serviceItem.themedata.theme_name
|
||||
self.image(name)
|
||||
# Update the preview frame.
|
||||
if self.isLive:
|
||||
self.parent().updatePreview()
|
||||
return True
|
||||
return False
|
||||
self.imageManager.addImage(path, ImageSource.ImagePlugin, background)
|
||||
if not hasattr(self, u'serviceItem'):
|
||||
return False
|
||||
self.override[u'image'] = path
|
||||
self.override[u'theme'] = self.serviceItem.themedata.background_filename
|
||||
self.image(path)
|
||||
# Update the preview frame.
|
||||
if self.isLive:
|
||||
self.parent().updatePreview()
|
||||
return True
|
||||
|
||||
def image(self, name):
|
||||
def image(self, path):
|
||||
"""
|
||||
Add an image as the background. The image has already been added to the
|
||||
cache.
|
||||
|
||||
``name``
|
||||
The name of the image to be displayed.
|
||||
``path``
|
||||
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')
|
||||
image = self.imageManager.getImageBytes(name)
|
||||
image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin)
|
||||
self.controller.mediaController.video_reset(self.controller)
|
||||
self.displayImage(image)
|
||||
|
||||
@ -360,7 +362,7 @@ class MainDisplay(Display):
|
||||
self.setVisible(True)
|
||||
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
|
||||
HTML to the display
|
||||
@ -377,20 +379,23 @@ class MainDisplay(Display):
|
||||
Receiver.send_message(u'video_background_replaced')
|
||||
self.override = {}
|
||||
# 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')
|
||||
self.override = {}
|
||||
else:
|
||||
# replace the background
|
||||
background = self.imageManager. \
|
||||
getImageBytes(self.override[u'image'])
|
||||
background = self.imageManager.getImageBytes(
|
||||
self.override[u'image'], ImageSource.ImagePlugin)
|
||||
self.setTransparency(self.serviceItem.themedata.background_type ==
|
||||
BackgroundType.to_string(BackgroundType.Transparent))
|
||||
if self.serviceItem.themedata.background_filename:
|
||||
self.serviceItem.bg_image_bytes = self.imageManager. \
|
||||
getImageBytes(self.serviceItem.themedata.theme_name)
|
||||
if image:
|
||||
image_bytes = self.imageManager.getImageBytes(image)
|
||||
self.serviceItem.bg_image_bytes = self.imageManager.getImageBytes(
|
||||
self.serviceItem.themedata.background_filename,
|
||||
ImageSource.Theme)
|
||||
if image_path:
|
||||
image_bytes = self.imageManager.getImageBytes(
|
||||
image_path, ImageSource.ImagePlugin)
|
||||
else:
|
||||
image_bytes = None
|
||||
html = build_html(self.serviceItem, self.screen, self.isLive,
|
||||
|
@ -48,7 +48,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
|
||||
ShortcutListForm, FormattingTagForm
|
||||
from openlp.core.ui.media import MediaController
|
||||
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.ui.firsttimeform import FirstTimeForm
|
||||
from openlp.core.ui import ScreenList
|
||||
@ -1035,8 +1035,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
# Make sure it's a .conf file.
|
||||
if not export_file_name.endswith(u'conf'):
|
||||
export_file_name = export_file_name + u'.conf'
|
||||
temp_file = os.path.join(unicode(gettempdir()),
|
||||
u'openlp', u'exportConf.tmp')
|
||||
temp_file = os.path.join(unicode(gettempdir(),
|
||||
get_filesystem_encoding()), u'openlp', u'exportConf.tmp')
|
||||
self.saveSettings()
|
||||
setting_sections = []
|
||||
# Add main sections.
|
||||
|
@ -330,7 +330,24 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
Copies the display text to the clipboard as plain text
|
||||
"""
|
||||
self.update_song_usage()
|
||||
self.mainWindow.clipboard.setText(self.document.toPlainText())
|
||||
cursor = QtGui.QTextCursor(self.document)
|
||||
cursor.select(QtGui.QTextCursor.Document)
|
||||
clipboard_text = cursor.selectedText()
|
||||
# We now have the unprocessed unicode service text in the cursor
|
||||
# So we replace u2028 with \n and u2029 with \n\n and a few others
|
||||
clipboard_text = clipboard_text.replace(u'\u2028', u'\n')
|
||||
clipboard_text = clipboard_text.replace(u'\u2029', u'\n\n')
|
||||
clipboard_text = clipboard_text.replace(u'\u2018', u'\'')
|
||||
clipboard_text = clipboard_text.replace(u'\u2019', u'\'')
|
||||
clipboard_text = clipboard_text.replace(u'\u201c', u'"')
|
||||
clipboard_text = clipboard_text.replace(u'\u201d', u'"')
|
||||
clipboard_text = clipboard_text.replace(u'\u2026', u'...')
|
||||
clipboard_text = clipboard_text.replace(u'\u2013', u'-')
|
||||
clipboard_text = clipboard_text.replace(u'\u2014', u'-')
|
||||
# remove the icon from the text
|
||||
clipboard_text = clipboard_text.replace(u'\ufffc\xa0', u'')
|
||||
# and put it all on the clipboard
|
||||
self.mainWindow.clipboard.setText(clipboard_text)
|
||||
|
||||
def copyHtmlText(self):
|
||||
"""
|
||||
|
@ -34,7 +34,7 @@ from collections import deque
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
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.settings import Settings
|
||||
from openlp.core.lib import SlideLimits, ServiceItemAction
|
||||
@ -861,8 +861,10 @@ class SlideController(Controller):
|
||||
# If current slide set background to image
|
||||
if framenumber == slideno:
|
||||
self.serviceItem.bg_image_bytes = \
|
||||
self.imageManager.getImageBytes(frame[u'title'])
|
||||
image = self.imageManager.getImage(frame[u'title'])
|
||||
self.imageManager.getImageBytes(frame[u'path'],
|
||||
ImageSource.ImagePlugin)
|
||||
image = self.imageManager.getImage(frame[u'path'],
|
||||
ImageSource.ImagePlugin)
|
||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
||||
slideHeight = width * (1 / self.ratio)
|
||||
@ -1092,14 +1094,14 @@ class SlideController(Controller):
|
||||
u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, row])
|
||||
else:
|
||||
toDisplay = self.serviceItem.get_rendered_frame(row)
|
||||
to_display = self.serviceItem.get_rendered_frame(row)
|
||||
if self.serviceItem.is_text():
|
||||
self.display.text(toDisplay)
|
||||
self.display.text(to_display)
|
||||
else:
|
||||
if start:
|
||||
self.display.buildHtml(self.serviceItem, toDisplay)
|
||||
self.display.buildHtml(self.serviceItem, to_display)
|
||||
else:
|
||||
self.display.image(toDisplay)
|
||||
self.display.image(to_display)
|
||||
# reset the store used to display first image
|
||||
self.serviceItem.bg_image_bytes = None
|
||||
self.updatePreview()
|
||||
|
@ -228,10 +228,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
"""
|
||||
Detects Page changes and updates as approprate.
|
||||
"""
|
||||
if self.page(pageId) == self.areaPositionPage:
|
||||
self.setOption(QtGui.QWizard.HaveCustomButton1, True)
|
||||
else:
|
||||
self.setOption(QtGui.QWizard.HaveCustomButton1, False)
|
||||
enabled = self.page(pageId) == self.areaPositionPage
|
||||
self.setOption(QtGui.QWizard.HaveCustomButton1, enabled)
|
||||
if self.page(pageId) == self.previewPage:
|
||||
self.updateTheme()
|
||||
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, \
|
||||
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, \
|
||||
BackgroundGradientType
|
||||
from openlp.core.lib.settings import Settings
|
||||
@ -139,14 +139,14 @@ class ThemeManager(QtGui.QWidget):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
|
||||
# Variables
|
||||
self.theme_list = []
|
||||
self.themeList = []
|
||||
self.path = AppLocation.get_section_data_path(self.settingsSection)
|
||||
check_directory_exists(self.path)
|
||||
self.thumb_path = os.path.join(self.path, u'thumbnails')
|
||||
check_directory_exists(self.thumb_path)
|
||||
self.thumbPath = os.path.join(self.path, u'thumbnails')
|
||||
check_directory_exists(self.thumbPath)
|
||||
self.themeForm.path = self.path
|
||||
self.old_background_image = None
|
||||
self.bad_v1_name_chars = re.compile(r'[%+\[\]]')
|
||||
self.oldBackgroundImage = None
|
||||
self.badV1NameChars = re.compile(r'[%+\[\]]')
|
||||
# Last little bits of setting up
|
||||
self.configUpdated()
|
||||
|
||||
@ -194,14 +194,10 @@ class ThemeManager(QtGui.QWidget):
|
||||
return
|
||||
real_theme_name = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
theme_name = unicode(item.text())
|
||||
self.deleteAction.setVisible(False)
|
||||
self.renameAction.setVisible(False)
|
||||
self.globalAction.setVisible(False)
|
||||
# If default theme restrict actions
|
||||
if real_theme_name == theme_name:
|
||||
self.deleteAction.setVisible(True)
|
||||
self.renameAction.setVisible(True)
|
||||
self.globalAction.setVisible(True)
|
||||
visible = real_theme_name == theme_name
|
||||
self.deleteAction.setVisible(visible)
|
||||
self.renameAction.setVisible(visible)
|
||||
self.globalAction.setVisible(visible)
|
||||
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
|
||||
|
||||
def changeGlobalFromTab(self, theme_name):
|
||||
@ -330,10 +326,10 @@ class ThemeManager(QtGui.QWidget):
|
||||
theme = self.getThemeData(
|
||||
unicode(item.data(QtCore.Qt.UserRole).toString()))
|
||||
if theme.background_type == u'image':
|
||||
self.old_background_image = theme.background_filename
|
||||
self.oldBackgroundImage = theme.background_filename
|
||||
self.themeForm.theme = theme
|
||||
self.themeForm.exec_(True)
|
||||
self.old_background_image = None
|
||||
self.oldBackgroundImage = None
|
||||
self.mainwindow.renderer.update_theme(theme.theme_name)
|
||||
|
||||
def onDeleteTheme(self):
|
||||
@ -361,10 +357,10 @@ class ThemeManager(QtGui.QWidget):
|
||||
``theme``
|
||||
The theme to delete.
|
||||
"""
|
||||
self.theme_list.remove(theme)
|
||||
self.themeList.remove(theme)
|
||||
thumb = u'%s.png' % theme
|
||||
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:
|
||||
encoding = get_filesystem_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.
|
||||
"""
|
||||
log.debug(u'Load themes from dir')
|
||||
self.theme_list = []
|
||||
self.themeList = []
|
||||
self.themeListWidget.clear()
|
||||
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
||||
if firstTime:
|
||||
@ -473,16 +469,17 @@ class ThemeManager(QtGui.QWidget):
|
||||
'%s (default)')) % text_name
|
||||
else:
|
||||
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)
|
||||
if validate_thumb(theme, thumb):
|
||||
icon = build_icon(thumb)
|
||||
else:
|
||||
icon = create_thumb(theme, thumb)
|
||||
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.theme_list.append(text_name)
|
||||
self.themeList.append(text_name)
|
||||
self._pushThemes()
|
||||
|
||||
def _pushThemes(self):
|
||||
@ -495,7 +492,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
"""
|
||||
Return the list of loaded themes
|
||||
"""
|
||||
return self.theme_list
|
||||
return self.themeList
|
||||
|
||||
def getThemeData(self, theme_name):
|
||||
"""
|
||||
@ -509,7 +506,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
unicode(theme_name) + u'.xml')
|
||||
xml = get_text_file_string(xml_file)
|
||||
if not xml:
|
||||
log.debug("No theme data - using default theme")
|
||||
log.debug(u'No theme data - using default theme')
|
||||
return ThemeXML()
|
||||
else:
|
||||
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()
|
||||
v1_background = xml_tree.find(u'BackgroundType')
|
||||
if v1_background is not None:
|
||||
theme_name, file_xml, out_file, abort_import = self.unzipVersion122(dir, zip,
|
||||
xml_file[0], xml_tree, v1_background, out_file)
|
||||
theme_name, file_xml, out_file, abort_import = \
|
||||
self.unzipVersion122(
|
||||
dir, zip, xml_file[0], xml_tree, v1_background, out_file)
|
||||
else:
|
||||
theme_name = xml_tree.find(u'name').text.strip()
|
||||
theme_folder = os.path.join(dir, theme_name)
|
||||
@ -601,8 +599,8 @@ class ThemeManager(QtGui.QWidget):
|
||||
if file_xml:
|
||||
theme = self._createThemeFromXml(file_xml, self.path)
|
||||
self.generateAndSaveImage(dir, theme_name, theme)
|
||||
# Only show the error message, when IOError was not raised (in this
|
||||
# case the error message has already been shown).
|
||||
# Only show the error message, when IOError was not raised (in
|
||||
# this case the error message has already been shown).
|
||||
elif zip is not None:
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.ThemeManager', 'Validation Error'),
|
||||
@ -611,13 +609,14 @@ class ThemeManager(QtGui.QWidget):
|
||||
log.exception(u'Theme file does not contain XML data %s' %
|
||||
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
|
||||
this method, please keep in mind, that some parameters are redundant.
|
||||
"""
|
||||
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_exists = os.path.exists(theme_folder)
|
||||
if theme_exists and not self.overWriteMessageBox(theme_name):
|
||||
@ -632,12 +631,12 @@ class ThemeManager(QtGui.QWidget):
|
||||
if background.text.strip() == u'2':
|
||||
image_name = xml_tree.find(u'BackgroundParameter1').text.strip()
|
||||
# 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'/'),
|
||||
zip.namelist())
|
||||
if len(imagefile) >= 1:
|
||||
if len(image_file) >= 1:
|
||||
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()
|
||||
else:
|
||||
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)
|
||||
if theme.background_type == \
|
||||
BackgroundType.to_string(BackgroundType.Image):
|
||||
self.mainwindow.imageManager.updateImage(theme.theme_name,
|
||||
u'theme', QtGui.QColor(theme.background_border_color))
|
||||
self.mainwindow.imageManager.updateImageBorder(
|
||||
theme.background_filename,
|
||||
ImageSource.Theme, QtGui.QColor(theme.background_border_color))
|
||||
self.mainwindow.imageManager.processUpdates()
|
||||
self.loadThemes()
|
||||
|
||||
@ -685,9 +685,8 @@ class ThemeManager(QtGui.QWidget):
|
||||
theme_dir = os.path.join(self.path, name)
|
||||
check_directory_exists(theme_dir)
|
||||
theme_file = os.path.join(theme_dir, name + u'.xml')
|
||||
if self.old_background_image and \
|
||||
image_to != self.old_background_image:
|
||||
delete_file(self.old_background_image)
|
||||
if self.oldBackgroundImage and image_to != self.oldBackgroundImage:
|
||||
delete_file(self.oldBackgroundImage)
|
||||
out_file = None
|
||||
try:
|
||||
out_file = open(theme_file, u'w')
|
||||
@ -714,7 +713,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
if os.path.exists(sample_path_name):
|
||||
os.unlink(sample_path_name)
|
||||
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)
|
||||
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.
|
||||
"""
|
||||
self.mainwindow.displayProgressBar(len(self.theme_list))
|
||||
for theme in self.theme_list:
|
||||
self.mainwindow.displayProgressBar(len(self.themeList))
|
||||
for theme in self.themeList:
|
||||
self.mainwindow.incrementProgressBar()
|
||||
self.generateAndSaveImage(
|
||||
self.path, theme, self.getThemeData(theme))
|
||||
@ -819,7 +818,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
"""
|
||||
theme = Theme(xml_data)
|
||||
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:
|
||||
new_theme.background_type = \
|
||||
BackgroundType.to_string(BackgroundType.Solid)
|
||||
|
@ -552,11 +552,9 @@ class Ui_ThemeWizard(object):
|
||||
themeWizard.setButtonText(QtGui.QWizard.CustomButton1,
|
||||
translate('OpenLP.ThemeWizard', 'Layout Preview'))
|
||||
self.previewPage.setTitle(
|
||||
translate('OpenLP.ThemeWizard', 'Save and Preview'))
|
||||
translate('OpenLP.ThemeWizard', 'Preview and Save'))
|
||||
self.previewPage.setSubTitle(
|
||||
translate('OpenLP.ThemeWizard', 'View the theme and save it '
|
||||
'replacing the current one or change the name to create a '
|
||||
'new theme'))
|
||||
translate('OpenLP.ThemeWizard', 'Preview the theme and save it.'))
|
||||
self.themeNameLabel.setText(
|
||||
translate('OpenLP.ThemeWizard', 'Theme name:'))
|
||||
# Align all QFormLayouts towards each other.
|
||||
|
@ -40,7 +40,7 @@ from openlp.core.lib import Receiver, SettingsManager, translate, \
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.lib.settings import Settings
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.utils import AppLocation, delete_file
|
||||
from openlp.core.utils import AppLocation, delete_file, get_filesystem_encoding
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, \
|
||||
BiblesResourcesDB
|
||||
from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract
|
||||
@ -73,7 +73,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.suffix = u'.sqlite'
|
||||
self.settingsSection = u'bibles'
|
||||
self.path = AppLocation.get_section_data_path(self.settingsSection)
|
||||
self.temp_dir = os.path.join(gettempdir(), u'openlp')
|
||||
self.temp_dir = os.path.join(
|
||||
unicode(gettempdir(), get_filesystem_encoding()), u'openlp')
|
||||
self.files = self.manager.old_bible_databases
|
||||
self.success = {}
|
||||
self.newbibles = {}
|
||||
|
@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
||||
Receiver
|
||||
Receiver, ImageSource
|
||||
from openlp.core.lib.settings import Settings
|
||||
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
||||
|
||||
@ -98,4 +98,5 @@ class ImagePlugin(Plugin):
|
||||
"""
|
||||
background = QtGui.QColor(Settings().value(self.settingsSection
|
||||
+ 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())
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
name = os.path.split(filename)[1]
|
||||
if self.plugin.liveController.display.directImage(name,
|
||||
if self.plugin.liveController.display.directImage(
|
||||
filename, background):
|
||||
self.resetAction.setVisible(True)
|
||||
else:
|
||||
|
@ -278,8 +278,7 @@ class MessageListener(object):
|
||||
item = message[0]
|
||||
log.debug(u'Startup called with message %s' % message)
|
||||
hide_mode = message[2]
|
||||
file = os.path.join(item.get_frame_path(),
|
||||
item.get_frame_title())
|
||||
file = os.path.join(item.get_frame_path(), item.get_frame_title())
|
||||
self.handler = item.title
|
||||
if self.handler == self.mediaitem.Automatic:
|
||||
self.handler = self.mediaitem.findControllerByType(file)
|
||||
|
@ -36,6 +36,104 @@ from ui import SongStrings
|
||||
|
||||
WHITESPACE = re.compile(r'[\W_]+', 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):
|
||||
"""
|
||||
@ -366,6 +464,136 @@ def clean_song(manager, song):
|
||||
if song.copyright:
|
||||
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 songstab import SongsTab
|
||||
from mediaitem import SongMediaItem
|
||||
|
@ -36,7 +36,7 @@ import re
|
||||
|
||||
from openlp.core.lib import translate
|
||||
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
|
||||
|
||||
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]+')
|
||||
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:
|
||||
def __init__(self, name, type, size):
|
||||
@ -274,7 +179,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
self.addAuthor(author_name.strip())
|
||||
if words:
|
||||
# Format the lyrics
|
||||
words = strip_rtf(words, self.encoding)
|
||||
words, self.encoding = strip_rtf(words, self.encoding)
|
||||
verse_type = VerseType.Tags[VerseType.Verse]
|
||||
for verse in SLIDE_BREAK_REGEX.split(words):
|
||||
verse = verse.strip()
|
||||
|
@ -45,6 +45,7 @@ from ewimport import EasyWorshipSongImport
|
||||
from songbeamerimport import SongBeamerImport
|
||||
from songshowplusimport import SongShowPlusImport
|
||||
from songproimport import SongProImport
|
||||
from sundayplusimport import SundayPlusImport
|
||||
from foilpresenterimport import FoilPresenterImport
|
||||
from zionworximport import ZionWorxImport
|
||||
# Imports that might fail
|
||||
@ -75,6 +76,7 @@ if os.name == u'nt':
|
||||
except ImportError:
|
||||
log.exception('Error importing %s', 'MediaShoutImport')
|
||||
|
||||
|
||||
class SongFormatSelect(object):
|
||||
"""
|
||||
This is a special enumeration class listing available file selection modes.
|
||||
@ -154,8 +156,9 @@ class SongFormat(object):
|
||||
SongPro = 13
|
||||
SongShowPlus = 14
|
||||
SongsOfFellowship = 15
|
||||
WordsOfWorship = 16
|
||||
ZionWorx = 17
|
||||
SundayPlus = 16
|
||||
WordsOfWorship = 17
|
||||
ZionWorx = 18
|
||||
|
||||
# Set optional attribute defaults
|
||||
__defaults__ = {
|
||||
@ -303,6 +306,13 @@ class SongFormat(object):
|
||||
'The Songs of Fellowship importer has been disabled because '
|
||||
'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: {
|
||||
u'class': WowImport,
|
||||
u'name': u'Words of Worship',
|
||||
@ -346,6 +356,7 @@ class SongFormat(object):
|
||||
SongFormat.SongPro,
|
||||
SongFormat.SongShowPlus,
|
||||
SongFormat.SongsOfFellowship,
|
||||
SongFormat.SundayPlus,
|
||||
SongFormat.WordsOfWorship,
|
||||
SongFormat.ZionWorx
|
||||
]
|
||||
|
@ -338,8 +338,9 @@ class SongMediaItem(MediaManagerItem):
|
||||
def onImportClick(self):
|
||||
if not hasattr(self, u'importWizard'):
|
||||
self.importWizard = SongImportForm(self, self.plugin)
|
||||
if self.importWizard.exec_() == QtGui.QDialog.Accepted:
|
||||
Receiver.send_message(u'songs_load_list')
|
||||
self.importWizard.exec_()
|
||||
# Run song load as list may have been cancelled but some songs loaded
|
||||
Receiver.send_message(u'songs_load_list')
|
||||
|
||||
def onExportClick(self):
|
||||
if not hasattr(self, u'exportWizard'):
|
||||
|
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()
|
||||
|
@ -36,6 +36,7 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
||||
Receiver
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.core.lib.ui import UiStrings, create_action
|
||||
from openlp.core.utils import get_filesystem_encoding
|
||||
from openlp.core.utils.actions import ActionList
|
||||
from openlp.plugins.songs.lib import clean_song, upgrade, SongMediaItem, \
|
||||
SongsTab
|
||||
@ -234,7 +235,8 @@ class SongsPlugin(Plugin):
|
||||
new songs into the database.
|
||||
"""
|
||||
self.onToolsReindexItemTriggered()
|
||||
db_dir = unicode(os.path.join(gettempdir(), u'openlp'))
|
||||
db_dir = unicode(os.path.join(
|
||||
unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
|
||||
if not os.path.exists(db_dir):
|
||||
return
|
||||
song_dbs = []
|
||||
|
Loading…
Reference in New Issue
Block a user