forked from openlp/openlp
Head
This commit is contained in:
commit
7561cd42b5
@ -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)
|
||||
|
@ -541,12 +541,13 @@ class SongImportSourcePage(QtGui.QWizardPage):
|
||||
if wizard.formatWidgets[format][u'fileListWidget'].count() > 0:
|
||||
return True
|
||||
else:
|
||||
filepath = wizard.formatWidgets[format][u'filepathEdit'].text()
|
||||
if not filepath.isEmpty():
|
||||
if select_mode == SongFormatSelect.SingleFile \
|
||||
and os.path.isfile(filepath):
|
||||
filepath = unicode(
|
||||
wizard.formatWidgets[format][u'filepathEdit'].text())
|
||||
if filepath:
|
||||
if select_mode == SongFormatSelect.SingleFile and \
|
||||
os.path.isfile(filepath):
|
||||
return True
|
||||
elif select_mode == SongFormatSelect.SingleFolder \
|
||||
and os.path.isdir(filepath):
|
||||
elif select_mode == SongFormatSelect.SingleFolder and \
|
||||
os.path.isdir(filepath):
|
||||
return True
|
||||
return False
|
||||
|
@ -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()
|
||||
|
@ -44,6 +44,7 @@ from powersongimport import PowerSongImport
|
||||
from ewimport import EasyWorshipSongImport
|
||||
from songbeamerimport import SongBeamerImport
|
||||
from songshowplusimport import SongShowPlusImport
|
||||
from sundayplusimport import SundayPlusImport
|
||||
from foilpresenterimport import FoilPresenterImport
|
||||
from zionworximport import ZionWorxImport
|
||||
# Imports that might fail
|
||||
@ -145,9 +146,10 @@ class SongFormat(object):
|
||||
SongBeamer = 11
|
||||
SongShowPlus = 12
|
||||
SongsOfFellowship = 13
|
||||
WordsOfWorship = 14
|
||||
ZionWorx = 15
|
||||
#CSV = 16
|
||||
SundayPlus = 14
|
||||
WordsOfWorship = 15
|
||||
ZionWorx = 16
|
||||
#CSV = 17
|
||||
|
||||
# Set optional attribute defaults
|
||||
__defaults__ = {
|
||||
@ -275,6 +277,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',
|
||||
@ -322,6 +331,7 @@ class SongFormat(object):
|
||||
SongFormat.SongBeamer,
|
||||
SongFormat.SongShowPlus,
|
||||
SongFormat.SongsOfFellowship,
|
||||
SongFormat.SundayPlus,
|
||||
SongFormat.WordsOfWorship,
|
||||
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()
|
||||
|
@ -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 = []
|
||||
|
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
2706
resources/i18n/es.ts
2706
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.add_header('Authorization', auth_header)
|
||||
print_verbose(u'Downloading list of languages from: %s' % url)
|
||||
json_response = urllib2.urlopen(request)
|
||||
try:
|
||||
json_response = urllib2.urlopen(request)
|
||||
except urllib2.HTTPError:
|
||||
print_quiet(u'Username or password incorrect.')
|
||||
return False
|
||||
json_dict = json.loads(json_response.read())
|
||||
languages = [lang[u'code'] for lang in json_dict[u'available_languages']]
|
||||
for language in languages:
|
||||
@ -214,6 +218,7 @@ def download_translations():
|
||||
fd.write(response.read())
|
||||
fd.close()
|
||||
print_quiet(u' Done.')
|
||||
return True
|
||||
|
||||
def prepare_project():
|
||||
"""
|
||||
@ -310,7 +315,8 @@ def process_stack(command_stack):
|
||||
for command in command_stack:
|
||||
print_quiet(u'%d.' % (command_stack.current_index), False)
|
||||
if command == Command.Download:
|
||||
download_translations()
|
||||
if not download_translations():
|
||||
return
|
||||
elif command == Command.Prepare:
|
||||
prepare_project()
|
||||
elif command == Command.Update:
|
||||
|
Loading…
Reference in New Issue
Block a user