This commit is contained in:
Andreas Preikschat 2010-09-23 17:58:19 +02:00
commit 2c1d51d826
39 changed files with 7212 additions and 5652 deletions

View File

@ -220,6 +220,7 @@ def image_to_byte(image):
``image``
The image to converted.
"""
log.debug(u'image_to_byte')
byte_array = QtCore.QByteArray()
# use buffer to store pixmap into byteArray
buffie = QtCore.QBuffer(byte_array)
@ -249,6 +250,7 @@ def resize_image(image, width, height, background=QtCore.Qt.black):
The background colour defaults to black.
"""
log.debug(u'resize_image')
preview = QtGui.QImage(image)
if not preview.isNull():
# Only resize if different size

View File

@ -27,8 +27,6 @@
import logging
from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte
log = logging.getLogger(__name__)
HTMLSRC = u"""
@ -60,7 +58,10 @@ body {
#image {
z-index:1;
}
#video {
#video1 {
z-index:2;
}
#video2 {
z-index:2;
}
#alert {
@ -82,54 +83,12 @@ body {
<script language="javascript">
var timer = null;
var video_timer = null;
var current_video = '1';
var transition = %s;
function show_video(state, path, volume, loop){
var vid = document.getElementById('video');
if(path != null){
vid.src = path;
vid.load();
}
if(volume != null){
vid.volume = volume;
}
switch(state){
case 'play':
vid.play();
vid.style.display = 'block';
if(loop)
video_timer = setInterval('video_loop()', 200);
break;
case 'pause':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
vid.style.display = 'block';
break;
case 'stop':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
vid.style.display = 'none';
vid.load();
break;
case 'close':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
vid.style.display = 'none';
vid.src = '';
break;
}
}
function video_loop(){
// The preferred method would be to use the video tag loop attribute
// Note, the preferred method for looping would be to use the
// video tag loop attribute.
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
@ -140,12 +99,73 @@ body {
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
var vid = document.getElementById('video');
if(vid.ended||vid.currentTime+0.2>=vid.duration){
vid.load();
vid.play();
}
if(current_video=='1'){
var vid = document.getElementById('video1');
var vid2 = document.getElementById('video2');
} else {
var vid = document.getElementById('video2');
var vid2 = document.getElementById('video1');
}
if(volume != null){
vid.volume = volume;
vid2.volume = volume;
}
switch(state){
case 'init':
vid.src = path;
vid2.src = path;
if(loop == null) loop = false;
vid.looping = loop;
vid2.looping = loop;
vid.load();
break;
case 'load':
vid2.style.visibility = 'hidden';
vid2.load();
break;
case 'play':
vid.play();
vid.style.visibility = 'visible';
if(vid.looping){
video_timer = setInterval(
function() {
show_video('poll');
}, 200);
}
break;
case 'pause':
if(video_timer!=null){
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
break;
case 'stop':
show_video('pause');
vid.style.visibility = 'hidden';
break;
case 'poll':
if(vid.ended||vid.currentTime+0.2>vid.duration)
show_video('swap');
break;
case 'swap':
show_video('pause');
if(current_video=='1')
current_video = '2';
else
current_video = '1';
show_video('play');
show_video('load');
break;
case 'close':
show_video('stop');
vid.src = '';
vid2.src = '';
break;
}
}
function show_image(src){
var img = document.getElementById('image');
img.src = src;
@ -274,8 +294,11 @@ body {
</script>
</head>
<body>
<img id="image" class="size" src="%s" />
<video id="video" class="size"></video>
<img id="image" class="size" %s />
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
</video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
</video>
%s
<div id="footer" class="footer"></div>
<div id="black" class="size"></div>
@ -301,10 +324,10 @@ def build_html(item, screen, alert, islive):
height = screen[u'size'].height()
theme = item.themedata
webkitvers = webkit_version()
if item.bg_frame:
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
if item.bg_image_bytes:
image = u'src="data:image/png;base64,%s"' % item.bg_image_bytes
else:
image = u''
image = u'style="display:none;"'
html = HTMLSRC % (build_background_css(item, width, height),
width, height,
build_alert_css(alert, width),
@ -426,8 +449,10 @@ def build_lyrics_css(item, webkitvers):
outline = build_lyrics_outline_css(theme)
if theme.display_shadow:
if theme.display_outline and webkitvers < 534.3:
shadow = u'padding-left: %spx; padding-top: %spx ' % \
(theme.display_shadow_size, theme.display_shadow_size)
shadow = u'padding-left: %spx; padding-top: %spx;' % \
(int(theme.display_shadow_size) +
(int(theme.display_outline_size) * 2),
theme.display_shadow_size)
shadow += build_lyrics_outline_css(theme, True)
else:
lyricsmain += u' text-shadow: %s %spx %spx;' % \
@ -487,13 +512,17 @@ def build_lyrics_format_css(theme, width, height):
valign = u'middle'
else:
valign = u'top'
if theme.display_outline:
left_margin = int(theme.display_outline_size) * 2
else:
left_margin = 0
lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; ' \
'margin:0; padding:0; width: %spx; height: %spx; ' % \
'font-size: %spt; color: %s; line-height: %d%%; margin:0;' \
'padding:0; padding-left:%spx; width: %spx; height: %spx; ' % \
(align, valign, theme.font_main_name, theme.font_main_proportion,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
width, height)
left_margin, width, height)
if theme.display_outline:
if webkit_version() < 534.3:
lyrics += u' letter-spacing: 1px;'

View File

@ -32,7 +32,8 @@ import logging
from PyQt4 import QtGui, QtCore, QtWebKit
from openlp.core.lib import resize_image, expand_tags, \
build_lyrics_format_css, build_lyrics_outline_css
build_lyrics_format_css, build_lyrics_outline_css, image_to_byte
log = logging.getLogger(__name__)
@ -54,6 +55,7 @@ class Renderer(object):
self.frame = None
self.bg_frame = None
self.bg_image = None
self.bg_image_bytes = None
def set_theme(self, theme):
"""
@ -66,15 +68,12 @@ class Renderer(object):
self._theme = theme
self.bg_frame = None
self.bg_image = None
self.bg_image_bytes = None
self._bg_image_filename = None
self.theme_name = theme.theme_name
if theme.background_type == u'image':
if theme.background_filename:
self._bg_image_filename = unicode(theme.background_filename)
if self.frame:
self.bg_image = resize_image(self._bg_image_filename,
self.frame.width(),
self.frame.height())
def set_text_rectangle(self, rect_main, rect_footer):
"""
@ -89,6 +88,22 @@ class Renderer(object):
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
self._rect = rect_main
self._rect_footer = rect_footer
self.page_width = self._rect.width()
self.page_height = self._rect.height()
if self._theme.display_shadow:
self.page_width -= int(self._theme.display_shadow_size)
self.page_height -= int(self._theme.display_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
self.page_shell = u'<html><head><style>' \
u'*{margin: 0; padding: 0; border: 0;} '\
u'#main {position:absolute; top:0px; %s %s}</style><body>' \
u'<div id="main">' % \
(build_lyrics_format_css(self._theme, self.page_width,
self.page_height), build_lyrics_outline_css(self._theme))
def set_frame_dest(self, frame_width, frame_height):
"""
@ -110,15 +125,18 @@ class Renderer(object):
self.frame.width(), self.frame.height())
if self._theme.background_type == u'image':
self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
self.frame.height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
if self.bg_image:
painter.drawImage(0, 0, self.bg_image)
painter.end()
self.bg_image_bytes = image_to_byte(self.bg_frame)
else:
self.bg_frame = None
self.bg_image_bytes = None
def format_slide(self, words, line_break):
"""
@ -139,29 +157,16 @@ class Renderer(object):
lines = verse.split(u'\n')
for line in lines:
text.append(line)
web = QtWebKit.QWebView()
web.resize(self._rect.width(), self._rect.height())
web.setVisible(False)
frame = web.page().mainFrame()
# Adjust width and height to account for shadow. outline done in css
width = self._rect.width() - int(self._theme.display_shadow_size)
height = self._rect.height() - int(self._theme.display_shadow_size)
shell = u'<html><head><style>#main {%s %s}</style><body>' \
u'<div id="main">' % \
(build_lyrics_format_css(self._theme, width, height),
build_lyrics_outline_css(self._theme))
formatted = []
html_text = u''
styled_text = u''
js_height = 'document.getElementById("main").scrollHeight'
for line in text:
styled_line = expand_tags(line) + line_end
styled_text += styled_line
html = shell + styled_text + u'</div></body></html>'
web.setHtml(html)
html = self.page_shell + styled_text + u'</div></body></html>'
self.web.setHtml(html)
# Text too long so go to next page
text_height = int(frame.evaluateJavaScript(js_height).toString())
if text_height > height:
if self.web_frame.contentsSize().height() > self.page_height:
formatted.append(html_text)
html_text = u''
styled_text = styled_line

View File

@ -223,7 +223,6 @@ class RenderManager(object):
The words to go on the slides.
"""
log.debug(u'format slide')
self.build_text_rectangle(self.themedata)
return self.renderer.format_slide(words, line_break)
def calculate_default(self, screen):

View File

@ -97,7 +97,7 @@ class ServiceItem(object):
self.themedata = None
self.main = None
self.footer = None
self.bg_frame = None
self.bg_image_bytes = None
def _new_item(self):
"""
@ -145,7 +145,7 @@ class ServiceItem(object):
"""
log.debug(u'Render called')
self._display_frames = []
self.bg_frame = None
self.bg_image_bytes = None
line_break = True
if self.is_capable(ItemCapabilities.NoLineBreaks):
line_break = False
@ -156,7 +156,7 @@ class ServiceItem(object):
theme = self.theme
self.main, self.footer = \
self.render_manager.set_override_theme(theme, useOverride)
self.bg_frame = self.render_manager.renderer.bg_frame
self.bg_image_bytes = self.render_manager.renderer.bg_image_bytes
self.themedata = self.render_manager.renderer._theme
for slide in self._raw_frames:
before = time.time()

View File

@ -28,6 +28,7 @@ import re
import sys
try:
import enchant
from enchant import DictNotFoundError
enchant_available = True
except ImportError:
enchant_available = False
@ -43,7 +44,10 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
QtGui.QPlainTextEdit.__init__(self, *args)
# Default dictionary based on the current locale.
if enchant_available:
self.dict = enchant.Dict()
try:
self.dict = enchant.Dict()
except DictNotFoundError:
self.dict = enchant.Dict(u'en_US')
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)

View File

@ -99,6 +99,7 @@ class MainDisplay(DisplayWidget):
self.alertTab = None
self.hide_mode = None
self.setWindowTitle(u'OpenLP Display')
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
QtCore.Qt.WindowStaysOnTopHint)
if self.isLive:
@ -116,12 +117,18 @@ class MainDisplay(DisplayWidget):
self.screen = self.screens.current
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
self.scene = QtGui.QGraphicsScene()
self.setScene(self.scene)
self.webView = QtWebKit.QGraphicsWebView()
self.scene.addItem(self.webView)
self.webView.resize(self.screen[u'size'].width(),
self.screen[u'size'].height())
try:
self.webView = QtWebKit.QGraphicsWebView()
self.scene = QtGui.QGraphicsScene(self)
self.setScene(self.scene)
self.scene.addItem(self.webView)
self.webView.setGeometry(QtCore.QRectF(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height()))
except AttributeError:
# QGraphicsWebView a recent addition, so fall back to QWebView
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height())
self.page = self.webView.page()
self.frame = self.page.mainFrame()
QtCore.QObject.connect(self.webView,
@ -157,7 +164,7 @@ class MainDisplay(DisplayWidget):
- splash_image.height()) / 2,
splash_image)
serviceItem = ServiceItem()
serviceItem.bg_frame = initialFrame
serviceItem.bg_image_bytes = image_to_byte(initialFrame)
self.webView.setHtml(build_html(serviceItem, self.screen,
self.parent.alertTab, self.isLive))
self.initialFrame = True
@ -286,7 +293,7 @@ class MainDisplay(DisplayWidget):
"""
log.debug(u'video')
self.loaded = True
js = u'show_video("play", "%s", %s, true);' % \
js = u'show_video("init", "%s", %s, true); show_video("play");' % \
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
self.frame.evaluateJavaScript(js)
return self.preview()
@ -306,6 +313,7 @@ class MainDisplay(DisplayWidget):
# We must have a service item to preview
if not hasattr(self, u'serviceItem'):
return
Receiver.send_message(u'openlp_process_events')
if self.isLive:
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
@ -318,6 +326,12 @@ class MainDisplay(DisplayWidget):
# Important otherwise first preview will miss the background !
while not self.loaded:
Receiver.send_message(u'openlp_process_events')
# if was hidden keep it hidden
if self.isLive:
self.setVisible(True)
# if was hidden keep it hidden
if self.hide_mode and self.isLive:
self.hideDisplay(self.hide_mode)
preview = QtGui.QImage(self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
@ -325,9 +339,6 @@ class MainDisplay(DisplayWidget):
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.frame.render(painter)
painter.end()
# Make display show up if in single screen mode
if self.isLive:
self.setVisible(True)
return preview
def buildHtml(self, serviceItem):
@ -341,7 +352,9 @@ class MainDisplay(DisplayWidget):
self.serviceItem = serviceItem
html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
self.isLive)
log.debug(u'buildHtml - pre setHtml')
self.webView.setHtml(html)
log.debug(u'buildHtml - post setHtml')
if serviceItem.foot_text and serviceItem.foot_text:
self.footer(serviceItem.foot_text)
# if was hidden keep it hidden

View File

@ -383,20 +383,20 @@ class ServiceManager(QtGui.QWidget):
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
tempItem = None
setLastItem = False
while serviceIterator:
if serviceIterator.isSelected() and tempItem is None:
while serviceIterator.value():
if serviceIterator.value().isSelected() and tempItem is None:
setLastItem = True
serviceIterator.setSelected(False)
if serviceIterator.isSelected():
#We are on the first record
serviceIterator.value().setSelected(False)
if serviceIterator.value().isSelected():
# We are on the first record
if tempItem:
tempItem.setSelected(True)
serviceIterator.setSelected(False)
serviceIterator.value().setSelected(False)
else:
tempItem = serviceIterator
lastItem = serviceIterator
++serviceIterator
#Top Item was selected so set the last one
tempItem = serviceIterator.value()
lastItem = serviceIterator.value()
serviceIterator += 1
# Top Item was selected so set the last one
if setLastItem:
lastItem.setSelected(True)
@ -406,16 +406,18 @@ class ServiceManager(QtGui.QWidget):
Called by the down arrow
"""
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
firstItem = serviceIterator
firstItem = None
setSelected = False
while serviceIterator:
while serviceIterator.value():
if not firstItem:
firstItem = serviceIterator.value()
if setSelected:
setSelected = False
serviceIterator.setSelected(True)
elif serviceIterator.isSelected():
serviceIterator.setSelected(False)
serviceIterator.value().setSelected(True)
elif serviceIterator.value() and serviceIterator.value().isSelected():
serviceIterator.value().setSelected(False)
setSelected = True
++serviceIterator
serviceIterator += 1
if setSelected:
firstItem.setSelected(True)

View File

@ -637,9 +637,9 @@ class SlideController(QtGui.QWidget):
"""
if not self.serviceItem:
return
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive])
if self.serviceItem.is_command():
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive])
self.updatePreview()
else:
self.PreviewListWidget.selectRow(0)
@ -652,9 +652,9 @@ class SlideController(QtGui.QWidget):
index = int(message[0])
if not self.serviceItem:
return
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, index])
if self.serviceItem.is_command():
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, index])
self.updatePreview()
else:
self.PreviewListWidget.selectRow(index)
@ -769,9 +769,9 @@ class SlideController(QtGui.QWidget):
row = self.PreviewListWidget.currentRow()
self.selectedRow = 0
if row > -1 and row < self.PreviewListWidget.rowCount():
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, row])
if self.serviceItem.is_command() and self.isLive:
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, row])
self.updatePreview()
else:
frame, raw_html = self.serviceItem.get_rendered_frame(row)

View File

@ -107,7 +107,10 @@ class LanguageManager(object):
``action``
The language menu option
"""
action_name = u'%s' % action.objectName()
if action is None:
action_name = u'en'
else:
action_name = u'%s' % action.objectName()
qm_list = LanguageManager.get_qm_list()
if LanguageManager.auto_language:
language = u'[%s]' % qm_list[action_name]

View File

@ -40,7 +40,7 @@ class AlertsPlugin(Plugin):
log.info(u'Alerts Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Alerts', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Alerts', u'1.9.3', plugin_helpers)
self.weight = -3
self.icon = build_icon(u':/plugins/plugin_alerts.png')
self.alertsmanager = AlertsManager(self)

View File

@ -37,7 +37,7 @@ class BiblePlugin(Plugin):
log.info(u'Bible Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Bibles', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Bibles', u'1.9.3', plugin_helpers)
self.weight = -9
self.icon_path = u':/plugins/plugin_bibles.png'
self.icon = build_icon(self.icon_path)

View File

@ -287,6 +287,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
def setDefaults(self):
settings = QtCore.QSettings()
settings.beginGroup(self.bibleplugin.settingsSection)
self.restart()
self.finishButton.setVisible(False)
self.cancelButton.setVisible(True)
self.setField(u'source_format', QtCore.QVariant(0))
self.setField(u'osis_location', QtCore.QVariant(''))
self.setField(u'csv_booksfile', QtCore.QVariant(''))

View File

@ -370,9 +370,6 @@ class BibleMediaItem(MediaManagerItem):
def onSearchProgressShow(self):
self.SearchProgress.setVisible(True)
Receiver.send_message(u'openlp_process_events')
#self.SearchProgress.setMinimum(0)
#self.SearchProgress.setMaximum(2)
#self.SearchProgress.setValue(1)
def onSearchProgressHide(self):
self.SearchProgress.setVisible(False)

View File

@ -47,7 +47,7 @@ class CustomPlugin(Plugin):
log.info(u'Custom Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Custom', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Custom', u'1.9.3', plugin_helpers)
self.weight = -5
self.custommanager = Manager(u'custom', init_schema)
self.edit_custom_form = EditCustomForm(self.custommanager)

View File

@ -35,7 +35,7 @@ class ImagePlugin(Plugin):
log.info(u'Image Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Images', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Images', u'1.9.3', plugin_helpers)
self.weight = -7
self.icon_path = u':/plugins/plugin_images.png'
self.icon = build_icon(self.icon_path)

View File

@ -37,7 +37,7 @@ class MediaPlugin(Plugin):
log.info(u'%s MediaPlugin loaded', __name__)
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Media', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Media', u'1.9.3', plugin_helpers)
self.weight = -6
self.icon_path = u':/plugins/plugin_media.png'
self.icon = build_icon(self.icon_path)

View File

@ -51,7 +51,7 @@ class PresentationPlugin(Plugin):
"""
log.debug(u'Initialised')
self.controllers = {}
Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Presentations', u'1.9.3', plugin_helpers)
self.weight = -8
self.icon_path = u':/plugins/plugin_presentations.png'
self.icon = build_icon(self.icon_path)

View File

@ -38,7 +38,7 @@ class RemotesPlugin(Plugin):
"""
remotes constructor
"""
Plugin.__init__(self, u'Remotes', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Remotes', u'1.9.3', plugin_helpers)
self.icon = build_icon(u':/plugins/plugin_remote.png')
self.weight = -1
self.server = None

View File

@ -109,6 +109,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.genericRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onGenericRemoveButtonClicked)
QtCore.QObject.connect(self.ewBrowseButton,
QtCore.SIGNAL(u'clicked()'),
self.onEWBrowseButtonClicked)
QtCore.QObject.connect(self.cancelButton,
QtCore.SIGNAL(u'clicked(bool)'),
self.onCancelButtonClicked)
@ -214,6 +217,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
'presentation file to import from.'))
self.genericAddButton.setFocus()
return False
elif source_format == SongFormat.EasyWorship:
if self.ewFilenameEdit.text().isEmpty():
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.ImportWizardForm',
'No EasyWorship Song Database Selected'),
translate('SongsPlugin.ImportWizardForm',
'You need to select an EasyWorship song database '
'file to import from.'))
self.ewBrowseButton.setFocus()
return False
return True
elif self.currentId() == 2:
# Progress page
@ -322,6 +335,13 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def onGenericRemoveButtonClicked(self):
self.removeSelectedItems(self.genericFileListWidget)
def onEWBrowseButtonClicked(self):
self.getFileName(
translate('SongsPlugin.ImportWizardForm',
'Select EasyWorship Database File'),
self.ewFilenameEdit
)
def onCancelButtonClicked(self, checked):
"""
Stop the import on pressing the cancel button.
@ -341,6 +361,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def setDefaults(self):
self.restart()
self.finishButton.setVisible(False)
self.cancelButton.setVisible(True)
self.formatComboBox.setCurrentIndex(0)
self.openLP2FilenameEdit.setText(u'')
self.openLP1FilenameEdit.setText(u'')
@ -350,6 +372,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.ccliFileListWidget.clear()
self.songsOfFellowshipFileListWidget.clear()
self.genericFileListWidget.clear()
self.ewFilenameEdit.setText(u'')
#self.csvFilenameEdit.setText(u'')
def incrementProgressBar(self, status_text, increment=1):
@ -420,6 +443,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
importer = self.plugin.importSongs(SongFormat.Generic,
filenames=self.getListOfFiles(self.genericFileListWidget)
)
elif source_format == SongFormat.EasyWorship:
# Import an OpenLP 2.0 database
importer = self.plugin.importSongs(SongFormat.EasyWorship,
filename=unicode(self.ewFilenameEdit.text())
)
success = importer.do_import()
if success:
# reload songs

View File

@ -96,6 +96,7 @@ class Ui_SongImportWizard(object):
self.formatComboBox.addItem(u'')
self.formatComboBox.addItem(u'')
self.formatComboBox.addItem(u'')
self.formatComboBox.addItem(u'')
# self.formatComboBox.addItem(u'')
self.formatLayout.addWidget(self.formatComboBox)
self.formatSpacer = QtGui.QSpacerItem(40, 20,
@ -413,6 +414,30 @@ class Ui_SongImportWizard(object):
self.genericImportLayout.addLayout(self.genericButtonLayout)
self.genericLayout.addWidget(self.genericImportWidget)
self.formatStackedWidget.addWidget(self.genericPage)
# EasyWorship
self.ewPage = QtGui.QWidget()
self.ewPage.setObjectName(u'ewPage')
self.ewLayout = QtGui.QFormLayout(self.ewPage)
self.ewLayout.setMargin(0)
self.ewLayout.setSpacing(8)
self.ewLayout.setObjectName(u'ewLayout')
self.ewFilenameLabel = QtGui.QLabel(self.ewPage)
self.ewFilenameLabel.setObjectName(u'ewFilenameLabel')
self.ewLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
self.ewFilenameLabel)
self.ewFileLayout = QtGui.QHBoxLayout()
self.ewFileLayout.setSpacing(8)
self.ewFileLayout.setObjectName(u'ewFileLayout')
self.ewFilenameEdit = QtGui.QLineEdit(self.ewPage)
self.ewFilenameEdit.setObjectName(u'ewFilenameEdit')
self.ewFileLayout.addWidget(self.ewFilenameEdit)
self.ewBrowseButton = QtGui.QToolButton(self.ewPage)
self.ewBrowseButton.setIcon(openIcon)
self.ewBrowseButton.setObjectName(u'ewBrowseButton')
self.ewFileLayout.addWidget(self.ewBrowseButton)
self.ewLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
self.ewFileLayout)
self.formatStackedWidget.addWidget(self.ewPage)
# Commented out for future use.
# self.csvPage = QtGui.QWidget()
# self.csvPage.setObjectName(u'CSVPage')
@ -497,7 +522,9 @@ class Ui_SongImportWizard(object):
self.formatComboBox.setItemText(7,
translate('SongsPlugin.ImportWizardForm',
'Generic Document/Presentation'))
# self.formatComboBox.setItemText(8,
self.formatComboBox.setItemText(8,
translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
# self.formatComboBox.setItemText(9,
# translate('SongsPlugin.ImportWizardForm', 'CSV'))
self.openLP2FilenameLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
@ -549,6 +576,10 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'The generic document/'
'presentation importer has been disabled because OpenLP cannot '
'find OpenOffice.org on your computer.'))
self.ewFilenameLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
self.ewBrowseButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Browse...'))
# self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText(

View File

@ -0,0 +1,255 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
"""
The :mod:`ewimport` module provides the functionality for importing
EasyWorship song databases into the current installation database.
"""
import sys
import os
import struct
from songimport import SongImport
def strip_rtf(blob):
depth = 0
control = False
clear_text = []
control_word = []
for c in blob:
if control:
# for delimiters, set control to False
if c == '{':
if len(control_word) > 0:
depth += 1
control = False
elif c == '}':
if len(control_word) > 0:
depth -= 1
control = False
elif c == '\\':
new_control = (len(control_word) > 0)
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 len(control_word) == 0:
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'\n')
elif control_str[0] == '\'':
# Really should take RTF character set into account but
# for now assume ANSI (Windows-1252) and call it good
s = chr(int(control_str[1:3], 16))
clear_text.append(s.decode(u'windows-1252'))
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):
self.name = name
self.type = type
self.size = size
class EasyWorshipSongImport(SongImport):
"""
The :class:`EasyWorshipSongImport` class provides OpenLP with the
ability to import EasyWorship song files.
"""
def __init__(self, manager, **kwargs):
self.import_source = kwargs[u'filename']
SongImport.__init__(self, manager)
def do_import(self):
# Open the DB and MB files if they exist
import_source_mb = self.import_source.replace('.DB', '.MB')
if not os.path.isfile(self.import_source):
return False
if not os.path.isfile(import_source_mb):
return False
db_size = os.path.getsize(self.import_source)
if db_size < 0x800:
return False
db_file = open(self.import_source, 'rb')
self.memo_file = open(import_source_mb, 'rb')
# Don't accept files that are clearly not paradox files
record_size, header_size, block_size, first_block, num_fields \
= struct.unpack('<hhxb8xh17xh', db_file.read(35))
if header_size != 0x800 or block_size < 1 or block_size > 4:
db_file.close()
self.memo_file.close()
return False
# There does not appear to be a _reliable_ way of getting the number
# of songs/records, so let's use file blocks for measuring progress.
total_blocks = (db_size - header_size) / (block_size * 1024)
self.import_wizard.importProgressBar.setMaximum(total_blocks)
# Read the field description information
db_file.seek(120)
field_info = db_file.read(num_fields * 2)
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
field_names = db_file.read(header_size - db_file.tell()).split('\0',
num_fields)
field_names.pop()
field_descs = []
for i,field_name in enumerate(field_names):
field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
field_descs.append(FieldDescEntry(field_name, field_type,
field_size))
self.set_record_struct(field_descs)
# Pick out the field description indexes we will need
success = True
try:
fi_title = self.find_field(u'Title')
fi_author = self.find_field(u'Author')
fi_copy = self.find_field(u'Copyright')
fi_admin = self.find_field(u'Administrator')
fi_words = self.find_field(u'Words')
fi_ccli = self.find_field(u'Song Number')
except IndexError:
# This is the wrong table
success = False
# Loop through each block of the file
cur_block = first_block
while cur_block != 0 and success:
db_file.seek(header_size + ((cur_block - 1) * 1024 * block_size))
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
rec_count = (rec_count + record_size) / record_size
# Loop through each record within the current block
for i in range(rec_count):
if self.stop_import_flag:
success = False
break
raw_record = db_file.read(record_size)
self.fields = self.record_struct.unpack(raw_record)
self.set_defaults()
self.title = self.get_field(fi_title)
self.import_wizard.incrementProgressBar(
u'Importing "%s"...' % self.title, 0)
self.copyright = self.get_field(fi_copy) + \
u', Administered by ' + self.get_field(fi_admin)
self.ccli_number = self.get_field(fi_ccli)
# Format the lyrics
if self.stop_import_flag:
success = False
break
words = self.get_field(fi_words)
words = strip_rtf(words)
for verse in words.split(u'\n\n'):
self.add_verse(verse.strip(), u'V')
# Split up the authors
authors = self.get_field(fi_author)
author_list = authors.split(u'/')
if len(author_list) < 2:
author_list = authors.split(u',')
for author_name in author_list:
self.add_author(author_name.strip())
if self.stop_import_flag:
success = False
break
self.finish()
if not self.stop_import_flag:
self.import_wizard.incrementProgressBar(u'')
db_file.close()
self.memo_file.close()
return success
def find_field(self, field_name):
return [i for i,x in enumerate(self.field_descs) \
if x.name == field_name][0]
def set_record_struct(self, field_descs):
# Begin with empty field struct list
fsl = ['>']
for field_desc in field_descs:
if field_desc.type == 1:
# string
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 3:
# 16-bit int
fsl.append('H')
elif field_desc.type == 4:
# 32-bit int
fsl.append('I')
elif field_desc.type == 9:
# Logical
fsl.append('B')
elif field_desc.type == 0x0c:
# Memo
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 0x0d:
# Blob
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 0x15:
# Timestamp
fsl.append('Q')
else:
fsl.append('%ds' % field_desc.size)
self.record_struct = struct.Struct(''.join(fsl))
self.field_descs = field_descs
def get_field(self, field_desc_index):
field = self.fields[field_desc_index]
field_desc = self.field_descs[field_desc_index]
# Check for 'blank' entries
if isinstance(field, str):
if len(field.rstrip('\0')) == 0:
return u''
elif field == 0:
return 0
# Format the field depending on the field type
if field_desc.type == 1:
# string
return field.rstrip('\0').decode(u'windows-1252')
elif field_desc.type == 3:
# 16-bit int
return field ^ 0x8000
elif field_desc.type == 4:
# 32-bit int
return field ^ 0x80000000
elif field_desc.type == 9:
# Logical
return (field ^ 0x80 == 1)
elif field_desc.type == 0x0c or field_desc.type == 0x0d:
# Memo or Blob
sub_block, block_start, blob_size = \
struct.unpack_from('<bhxi', field, len(field)-10)
self.memo_file.seek(block_start * 256)
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
if memo_block_type == 2:
self.memo_file.seek(8, os.SEEK_CUR)
elif memo_block_type == 3:
if sub_block < 0 or sub_block > 63:
return u'';
self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
sub_block_start, = struct.unpack('B', self.memo_file.read(1))
self.memo_file.seek((block_start * 256) +
(sub_block_start * 16))
else:
return u'';
return self.memo_file.read(blob_size)
else:
return 0

View File

@ -28,6 +28,7 @@ from opensongimport import OpenSongImport
from olpimport import OpenLPSongImport
from wowimport import WowImport
from cclifileimport import CCLIFileImport
from ewimport import EasyWorshipSongImport
# Imports that might fail
try:
from olp1import import OpenLP1SongImport
@ -61,7 +62,8 @@ class SongFormat(object):
CCLI = 5
SongsOfFellowship = 6
Generic = 7
CSV = 8
#CSV = 8
EasyWorship = 8
@staticmethod
def get_class(format):
@ -85,6 +87,8 @@ class SongFormat(object):
return OooImport
elif format == SongFormat.CCLI:
return CCLIFileImport
elif format == SongFormat.EasyWorship:
return EasyWorshipSongImport
# else:
return None
@ -101,7 +105,8 @@ class SongFormat(object):
SongFormat.WordsOfWorship,
SongFormat.CCLI,
SongFormat.SongsOfFellowship,
SongFormat.Generic
SongFormat.Generic,
SongFormat.EasyWorship
]
@staticmethod

View File

@ -50,7 +50,7 @@ class SongsPlugin(Plugin):
"""
Create and set up the Songs plugin.
"""
Plugin.__init__(self, u'Songs', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'Songs', u'1.9.3', plugin_helpers)
self.weight = -10
self.manager = Manager(u'songs', init_schema)
self.icon_path = u':/plugins/plugin_songs.png'

View File

@ -41,7 +41,7 @@ class SongUsagePlugin(Plugin):
log.info(u'SongUsage Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'SongUsage', u'1.9.2', plugin_helpers)
Plugin.__init__(self, u'SongUsage', u'1.9.3', plugin_helpers)
self.weight = -4
self.icon = build_icon(u':/plugins/plugin_songusage.png')
self.songusagemanager = None

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

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

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

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

View File

@ -52,6 +52,7 @@ This is done easily via the ``-d``, ``-p`` and ``-u`` options::
import os
import urllib
import re
from shutil import copy
from optparse import OptionParser
from PyQt4 import QtCore
@ -85,7 +86,9 @@ class CommandStack(object):
return len(self.data)
def __getitem__(self, index):
if self.data[index].get(u'arguments'):
if not index in self.data:
return None
elif self.data[index].get(u'arguments'):
return self.data[index][u'command'], self.data[index][u'arguments']
else:
return self.data[index][u'command']
@ -110,6 +113,21 @@ class CommandStack(object):
def reset(self):
self.current_index = 0
def arguments(self):
if self.data[self.current_index - 1].get(u'arguments'):
return self.data[self.current_index - 1][u'arguments']
else:
return []
def __repr__(self):
results = []
for item in self.data:
if item.get(u'arguments'):
results.append(str((item[u'command'], item[u'arguments'])))
else:
results.append(str((item[u'command'], )))
return u'[%s]' % u', '.join(results)
def print_verbose(text):
"""
@ -227,11 +245,12 @@ def update_translations():
else:
os.chdir(os.path.abspath(u'..'))
run(u'pylupdate4 -verbose -noobsolete openlp.pro')
os.chdir(os.path.abspath(u'scripts'))
def generate_binaries():
print u'Generate the related *.qm files'
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
print u'You have no generated a project file yet, please run this ' + \
print u'You have not generated a project file yet, please run this ' + \
u'script with the -p option. It is also recommended that you ' + \
u'this script with the -u option to update the translation ' + \
u'files as well.'
@ -239,6 +258,17 @@ def generate_binaries():
else:
os.chdir(os.path.abspath(u'..'))
run(u'lrelease openlp.pro')
os.chdir(os.path.abspath(u'scripts'))
src_path = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n')
dest_path = os.path.join(os.path.abspath(u'..'), u'openlp', u'i18n')
if not os.path.exists(dest_path):
os.makedirs(dest_path)
src_list = os.listdir(src_path)
for file in src_list:
if re.search('.qm$', file):
copy(os.path.join(src_path, u'%s' % file),
os.path.join(dest_path, u'%s' % file))
def create_translation(language):
"""
@ -248,13 +278,15 @@ def create_translation(language):
The language file to create.
"""
print "Create new Translation File"
if not language.endswith(u'.ts'):
language += u'.ts'
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
download_file(u'en.ts', filename)
print u'\n** Please Note **\n'
print u'In order to get this file into OpenLP and onto the Pootle ' + \
print u' ** Please Note **'
print u' In order to get this file into OpenLP and onto the Pootle ' + \
u'translation server you will need to subscribe to the OpenLP' + \
u'Translators mailing list, and request that your language file ' + \
u'be added to the project.\n'
u'be added to the project.'
print u' Done'
def process_stack(command_stack):
@ -278,7 +310,7 @@ def process_stack(command_stack):
elif command == Command.Generate:
generate_binaries()
elif command == Command.Create:
command, arguments = command_stack[command_stack.current_index]
arguments = command_stack.arguments()
create_translation(*arguments)
print u'Finished processing commands.'
else:
@ -293,7 +325,7 @@ def main():
parser = OptionParser(usage=usage)
parser.add_option('-d', '--download-ts', dest='download',
action='store_true', help='download language files from Pootle')
parser.add_option('-c', '--create', dest=u'create', metavar='LANG',
parser.add_option('-c', '--create', dest='create', metavar='LANG',
help='create a new translation file for language LANG, e.g. "en_GB"')
parser.add_option('-p', '--prepare', dest='prepare', action='store_true',
help='generate a project file, used to update the translations')
@ -330,4 +362,3 @@ if __name__ == u'__main__':
print u'You need to run this script from the scripts directory.'
else:
main()

View File

@ -179,7 +179,16 @@ def copy_windows_files():
copy(os.path.join(iss_path, u'OpenLP.ico'), os.path.join(dist_path, u'OpenLP.ico'))
copy(os.path.join(iss_path, u'LICENSE.txt'), os.path.join(dist_path, u'LICENSE.txt'))
def update_translations():
print u'Updating translations...'
os.chdir(script_path)
translation_utils = Popen(u'python translation_utils.py -dpu')
code = translation_utils.wait()
if code != 0:
print u'Error running translation_utils.py'
def compile_translations():
print u'Compiling translations...'
files = os.listdir(i18n_path)
if not os.path.exists(os.path.join(dist_path, u'i18n')):
os.makedirs(os.path.join(dist_path, u'i18n'))
@ -221,6 +230,7 @@ def main():
copy_enchant()
copy_plugins()
copy_windows_files()
update_translations()
compile_translations()
run_innosetup()
print "Done."