openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #03208
[Merge] lp:~trb143/openlp/renderer into lp:openlp
Tim Bentley has proposed merging lp:~trb143/openlp/renderer into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Speed up backgrounds by removing images and using CSS instead
move spelling text widget to own file
fix up highlighting of fields for spelling and tagging.
--
https://code.launchpad.net/~trb143/openlp/renderer/+merge/34612
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/renderer into lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py 2010-09-03 18:30:56 +0000
+++ openlp/core/lib/__init__.py 2010-09-04 12:47:41 +0000
@@ -316,6 +316,7 @@
text = text.replace(tag[u'end tag'], tag[u'end html'])
return text
+from spelltextedit import SpellTextEdit
from eventreceiver import Receiver
from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin
=== modified file 'openlp/core/lib/htmlbuilder.py'
--- openlp/core/lib/htmlbuilder.py 2010-09-03 18:30:56 +0000
+++ openlp/core/lib/htmlbuilder.py 2010-09-04 12:47:41 +0000
@@ -24,10 +24,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
+import logging
from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte
+log = logging.getLogger(__name__)
+
HTMLSRC = u"""
<html>
<head>
@@ -39,7 +42,7 @@
border: 0;
}
body {
- background-color: black;
+ %s;
}
.size {
position: absolute;
@@ -192,7 +195,7 @@
function show_text(newtext){
if(timer != null)
clearTimeout(timer);
- text_fade('lyricsmain', newtext);
+ text_fade('lyricsmain', newtext);
text_fade('lyricsoutline', newtext);
text_fade('lyricsshadow', newtext);
if(text_opacity()==1) return;
@@ -233,7 +236,7 @@
var text = document.getElementById('lyricsmain');
return getComputedStyle(text, '').opacity;
}
-
+
function show_text_complete(){
return (text_opacity()==1);
}
@@ -265,6 +268,7 @@
"""
try:
webkitvers = float(QtWebKit.qWebKitVersion())
+ log.debug(u'Webkit version = %s' % webkitvers)
except AttributeError:
webkitvers = 0
width = screen[u'size'].width()
@@ -274,47 +278,80 @@
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
else:
image = u''
- html = HTMLSRC % (width, height,
+ html = HTMLSRC % (build_background_css(item, width, height),
+ width, height,
build_alert_css(alert, width),
build_footer_css(item),
build_lyrics_css(item, webkitvers),
u'true' if theme and theme.display_slideTransition and islive \
else u'false',
- image,
+ image,
build_lyrics_html(item, webkitvers))
return html
+def build_background_css(item, width, height):
+ """
+ Build the background css
+
+ `item`
+ Service Item containing theme and location information
+
+ """
+ width = int(width) / 2
+ theme = item.themedata
+ background = u'background-color: black'
+ if theme:
+ if theme.background_type == u'solid':
+ background = u'background-color: %s' % theme.background_color
+ else:
+ if theme.background_direction == u'horizontal':
+ background = \
+ u'background: -webkit-gradient(linear, left top, left bottom, ' \
+ 'from(%s), to(%s))' % (theme.background_startColor,
+ theme.background_endColor)
+ elif theme.background_direction == u'vertical':
+ background = \
+ u'background: -webkit-gradient(linear, left top, right top,' \
+ 'from(%s), to(%s))' % (theme.background_startColor,
+ theme.background_endColor)
+ else:
+ background = \
+ u'background: -webkit-gradient(radial, %s 50%%, 100, %s 50%%, %s,' \
+ 'from(%s), to(%s))' % (width, width, width, theme.background_startColor,
+ theme.background_endColor)
+ return background
+
def build_lyrics_css(item, webkitvers):
"""
Build the video display css
`item`
Service Item containing theme and location information
-
+
`webkitvers`
The version of qtwebkit we're using
"""
style = """
-.lyricstable {
- z-index:4;
- position: absolute;
- display: table;
- %s
-}
-.lyricscell {
- display:table-cell;
- word-wrap: break-word;
- %s
-}
-.lyricsmain {
-%s
-}
-.lyricsoutline {
-%s
-}
-.lyricsshadow {
-%s
+.lyricstable {
+ z-index:4;
+ position: absolute;
+ display: table;
+ %s
+}
+.lyricscell {
+ display:table-cell;
+ word-wrap: break-word;
+ %s
+}
+.lyricsmain {
+%s
+}
+.lyricsoutline {
+%s
+}
+.lyricsshadow {
+%s
}
"""
theme = item.themedata
@@ -341,23 +378,23 @@
lyrics = u'width: %spx; height: %spx; text-align: %s; ' \
'vertical-align: %s; font-family: %s; font-size: %spt; ' \
'color: %s; line-height: %d%%;' % \
- (item.main.width(), item.main.height(), align, valign,
- theme.font_main_name, theme.font_main_proportion,
+ (item.main.width(), item.main.height(), align, valign,
+ theme.font_main_name, theme.font_main_proportion,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment))
# For performance reasons we want to show as few DIV's as possible,
- # especially when animating/transitions.
- # However some bugs in older versions of qtwebkit mean we need to
+ # especially when animating/transitions.
+ # However some bugs in older versions of qtwebkit mean we need to
# perform workarounds and add extra divs. Only do these when needed.
#
- # Before 533.3 the webkit-text-fill colour wasn't displayed, only the
+ # Before 533.3 the webkit-text-fill colour wasn't displayed, only the
# stroke (outline) color. So put stroke layer underneath the main text.
#
- # Before 534.4 the webkit-text-stroke was sometimes out of alignment
+ # Before 534.4 the webkit-text-stroke was sometimes out of alignment
# with the fill, or normal text. letter-spacing=1 is workaround
# https://bugs.webkit.org/show_bug.cgi?id=44403
#
- # Before 534.4 the text-shadow didn't get displayed when
- # webkit-text-stroke was used. So use an offset text layer underneath.
+ # Before 534.4 the text-shadow didn't get displayed when
+ # webkit-text-stroke was used. So use an offset text layer underneath.
# https://bugs.webkit.org/show_bug.cgi?id=19728
if theme.display_outline:
if webkitvers < 534.3:
@@ -373,7 +410,7 @@
u'-webkit-text-fill-color: %s; ' \
u' padding-left: %spx; padding-top: %spx' % \
(float(theme.display_outline_size) / 16,
- theme.display_shadow_color, theme.display_shadow_color,
+ theme.display_shadow_color, theme.display_shadow_color,
theme.display_shadow_size, theme.display_shadow_size)
if theme.display_shadow and \
(not theme.display_outline or webkitvers >= 534.3):
@@ -382,18 +419,18 @@
theme.display_shadow_size)
lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow)
return lyrics_css
-
+
def build_lyrics_html(item, webkitvers):
"""
Build the HTML required to show the lyrics
`item`
Service Item containing theme and location information
-
+
`webkitvers`
The version of qtwebkit we're using
"""
- # Bugs in some versions of QtWebKit mean we sometimes need additional
+ # Bugs in some versions of QtWebKit mean we sometimes need additional
# divs for outline and shadow, since the CSS doesn't work.
# To support vertical alignment middle and bottom, nested div's using
# display:table/display:table-cell are required for each lyric block.
@@ -411,7 +448,7 @@
u'<div id="lyricsmain" style="opacity:1" ' \
u'class="lyricscell lyricsmain"></div></div>'
return lyrics
-
+
def build_footer_css(item):
"""
Build the display of the item footer
=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py 2010-09-03 18:33:55 +0000
+++ openlp/core/lib/renderer.py 2010-09-04 12:47:41 +0000
@@ -190,44 +190,47 @@
self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
log.debug(u'render background %s start', self._theme.background_type)
- painter = QtGui.QPainter()
- painter.begin(self.bg_frame)
if self._theme.background_type == u'solid':
- painter.fillRect(self.frame.rect(),
- QtGui.QColor(self._theme.background_color))
+ self.bg_frame = None
+# painter.fillRect(self.frame.rect(),
+# QtGui.QColor(self._theme.background_color))
elif self._theme.background_type == u'gradient':
+ self.bg_frame = None
# gradient
- gradient = None
- if self._theme.background_direction == u'horizontal':
- w = int(self.frame.width()) / 2
- # vertical
- gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
- elif self._theme.background_direction == u'vertical':
- h = int(self.frame.height()) / 2
- # Horizontal
- gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
- else:
- w = int(self.frame.width()) / 2
- h = int(self.frame.height()) / 2
- # Circular
- gradient = QtGui.QRadialGradient(w, h, w)
- gradient.setColorAt(0,
- QtGui.QColor(self._theme.background_startColor))
- gradient.setColorAt(1,
- QtGui.QColor(self._theme.background_endColor))
- painter.setBrush(QtGui.QBrush(gradient))
- rect_path = QtGui.QPainterPath()
- max_x = self.frame.width()
- max_y = self.frame.height()
- rect_path.moveTo(0, 0)
- rect_path.lineTo(0, max_y)
- rect_path.lineTo(max_x, max_y)
- rect_path.lineTo(max_x, 0)
- rect_path.closeSubpath()
- painter.drawPath(rect_path)
+# gradient = None
+# if self._theme.background_direction == u'horizontal':
+# w = int(self.frame.width()) / 2
+# # vertical
+# gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
+# elif self._theme.background_direction == u'vertical':
+# h = int(self.frame.height()) / 2
+# # Horizontal
+# gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
+# else:
+# w = int(self.frame.width()) / 2
+# h = int(self.frame.height()) / 2
+# # Circular
+# gradient = QtGui.QRadialGradient(w, h, w)
+# gradient.setColorAt(0,
+# QtGui.QColor(self._theme.background_startColor))
+# gradient.setColorAt(1,
+# QtGui.QColor(self._theme.background_endColor))
+# painter.setBrush(QtGui.QBrush(gradient))
+# rect_path = QtGui.QPainterPath()
+# max_x = self.frame.width()
+# max_y = self.frame.height()
+# rect_path.moveTo(0, 0)
+# rect_path.lineTo(0, max_y)
+# rect_path.lineTo(max_x, max_y)
+# rect_path.lineTo(max_x, 0)
+# rect_path.closeSubpath()
+# painter.drawPath(rect_path)
+# painter.end()
elif self._theme.background_type == u'image':
# image
+ 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()
+ painter.end()
=== added file 'openlp/core/lib/spelltextedit.py'
--- openlp/core/lib/spelltextedit.py 1970-01-01 00:00:00 +0000
+++ openlp/core/lib/spelltextedit.py 2010-09-04 12:47:41 +0000
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
+# Carsten Tinggaard, 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 re
+import sys
+try:
+ import enchant
+ enchant_available = True
+except ImportError:
+ enchant_available = False
+
+# based on code from
+# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
+
+from PyQt4 import QtCore, QtGui
+from openlp.core.lib import html_expands, translate, context_menu_action
+
+class SpellTextEdit(QtGui.QPlainTextEdit):
+ def __init__(self, *args):
+ QtGui.QPlainTextEdit.__init__(self, *args)
+ # Default dictionary based on the current locale.
+ if enchant_available:
+ self.dict = enchant.Dict()
+ self.highlighter = Highlighter(self.document())
+ self.highlighter.setDict(self.dict)
+
+ def mousePressEvent(self, event):
+ if event.button() == QtCore.Qt.RightButton:
+ # Rewrite the mouse event to a left button event so the cursor is
+ # moved to the location of the pointer.
+ event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
+ event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
+ QtCore.Qt.NoModifier)
+ QtGui.QPlainTextEdit.mousePressEvent(self, event)
+
+ def contextMenuEvent(self, event):
+ popup_menu = self.createStandardContextMenu()
+ # Select the word under the cursor.
+ cursor = self.textCursor()
+ # only select text if not already selected
+ if not cursor.hasSelection():
+ cursor.select(QtGui.QTextCursor.WordUnderCursor)
+ self.setTextCursor(cursor)
+ # Check if the selected word is misspelled and offer spelling
+ # suggestions if it is.
+ if enchant_available and self.textCursor().hasSelection():
+ text = unicode(self.textCursor().selectedText())
+ if not self.dict.check(text):
+ spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
+ 'Spelling Suggestions'))
+ for word in self.dict.suggest(text):
+ action = SpellAction(word, spell_menu)
+ action.correct.connect(self.correctWord)
+ spell_menu.addAction(action)
+ # Only add the spelling suggests to the menu if there are
+ # suggestions.
+ if len(spell_menu.actions()) != 0:
+ popup_menu.insertSeparator(popup_menu.actions()[0])
+ popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
+ tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
+ 'Formatting Tags'))
+ for html in html_expands:
+ action = SpellAction( html[u'desc'], tag_menu)
+ action.correct.connect(self.htmlTag)
+ tag_menu.addAction(action)
+ popup_menu.insertSeparator(popup_menu.actions()[0])
+ popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
+ popup_menu.exec_(event.globalPos())
+
+ def correctWord(self, word):
+ """
+ Replaces the selected text with word.
+ """
+ cursor = self.textCursor()
+ cursor.beginEditBlock()
+ cursor.removeSelectedText()
+ cursor.insertText(word)
+ cursor.endEditBlock()
+
+ def htmlTag(self, tag):
+ """
+ Replaces the selected text with word.
+ """
+ for html in html_expands:
+ if tag == html[u'desc']:
+ cursor = self.textCursor()
+ if self.textCursor().hasSelection():
+ text = cursor.selectedText()
+ cursor.beginEditBlock()
+ cursor.removeSelectedText()
+ cursor.insertText(html[u'start tag'])
+ cursor.insertText(text)
+ cursor.insertText(html[u'end tag'])
+ cursor.endEditBlock()
+ else:
+ cursor = self.textCursor()
+ cursor.insertText(html[u'start tag'])
+ cursor.insertText(html[u'end tag'])
+
+class Highlighter(QtGui.QSyntaxHighlighter):
+
+ WORDS = u'(?iu)[\w\']+'
+
+ def __init__(self, *args):
+ QtGui.QSyntaxHighlighter.__init__(self, *args)
+ self.dict = None
+
+ def setDict(self, dict):
+ self.dict = dict
+
+ def highlightBlock(self, text):
+ if not self.dict:
+ return
+ text = unicode(text)
+ format = QtGui.QTextCharFormat()
+ format.setUnderlineColor(QtCore.Qt.red)
+ format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
+ for word_object in re.finditer(self.WORDS, text):
+ if not self.dict.check(word_object.group()):
+ self.setFormat(word_object.start(),
+ word_object.end() - word_object.start(), format)
+
+class SpellAction(QtGui.QAction):
+ """
+ A special QAction that returns the text in a signal.
+ """
+ correct = QtCore.pyqtSignal(unicode)
+
+ def __init__(self, *args):
+ QtGui.QAction.__init__(self, *args)
+ self.triggered.connect(lambda x: self.correct.emit(
+ unicode(self.text())))
=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py 2010-08-28 23:52:10 +0000
+++ openlp/core/ui/__init__.py 2010-09-04 12:47:41 +0000
@@ -27,141 +27,6 @@
The :mod:`ui` module provides the core user interface for OpenLP
"""
-# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
-
-import re
-import sys
-try:
- import enchant
- enchant_available = True
-except ImportError:
- enchant_available = False
-
-from PyQt4 import QtCore, QtGui
-from openlp.core.lib import html_expands, translate, context_menu_action
-
-class SpellTextEdit(QtGui.QPlainTextEdit):
- def __init__(self, *args):
- QtGui.QPlainTextEdit.__init__(self, *args)
- # Default dictionary based on the current locale.
- if enchant_available:
- self.dict = enchant.Dict()
- self.highlighter = Highlighter(self.document())
- self.highlighter.setDict(self.dict)
-
- def mousePressEvent(self, event):
- if event.button() == QtCore.Qt.RightButton:
- # Rewrite the mouse event to a left button event so the cursor is
- # moved to the location of the pointer.
- event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
- event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
- QtCore.Qt.NoModifier)
- QtGui.QPlainTextEdit.mousePressEvent(self, event)
-
- def contextMenuEvent(self, event):
- popup_menu = self.createStandardContextMenu()
- # Select the word under the cursor.
- cursor = self.textCursor()
- cursor.select(QtGui.QTextCursor.WordUnderCursor)
- self.setTextCursor(cursor)
- # Check if the selected word is misspelled and offer spelling
- # suggestions if it is.
- if enchant_available and self.textCursor().hasSelection():
- text = unicode(self.textCursor().selectedText())
- if not self.dict.check(text):
- spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
- 'Spelling Suggestions'))
- for word in self.dict.suggest(text):
- action = SpellAction(word, spell_menu)
- action.correct.connect(self.correctWord)
- spell_menu.addAction(action)
- # Only add the spelling suggests to the menu if there are
- # suggestions.
- if len(spell_menu.actions()) != 0:
- popup_menu.insertSeparator(popup_menu.actions()[0])
- popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
- tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
- 'Formatting Tags'))
- for html in html_expands:
- action = SpellAction( html[u'desc'], tag_menu)
- action.correct.connect(self.htmlTag)
- tag_menu.addAction(action)
- popup_menu.insertSeparator(popup_menu.actions()[0])
- popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
-
- popup_menu.exec_(event.globalPos())
-
- def correctWord(self, word):
- """
- Replaces the selected text with word.
- """
- cursor = self.textCursor()
- cursor.beginEditBlock()
-
- cursor.removeSelectedText()
- cursor.insertText(word)
-
- cursor.endEditBlock()
-
- def htmlTag(self, tag):
- """
- Replaces the selected text with word.
- """
- for html in html_expands:
- if tag == html[u'desc']:
- cursor = self.textCursor()
- if self.textCursor().hasSelection():
- text = cursor.selectedText()
- cursor.beginEditBlock()
- cursor.removeSelectedText()
- cursor.insertText(html[u'start tag'])
- cursor.insertText(text)
- cursor.insertText(html[u'end tag'])
- cursor.endEditBlock()
- else:
- cursor = self.textCursor()
- cursor.insertText(html[u'start tag'])
- cursor.insertText(html[u'end tag'])
-
-class Highlighter(QtGui.QSyntaxHighlighter):
-
- WORDS = u'(?iu)[\w\']+'
-
- def __init__(self, *args):
- QtGui.QSyntaxHighlighter.__init__(self, *args)
-
- self.dict = None
-
- def setDict(self, dict):
- self.dict = dict
-
- def highlightBlock(self, text):
- if not self.dict:
- return
-
- text = unicode(text)
-
- format = QtGui.QTextCharFormat()
- format.setUnderlineColor(QtCore.Qt.red)
- format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
-
- for word_object in re.finditer(self.WORDS, text):
- if not self.dict.check(word_object.group()):
- self.setFormat(word_object.start(),
- word_object.end() - word_object.start(), format)
-
-class SpellAction(QtGui.QAction):
- """
- A special QAction that returns the text in a signal.
- """
- correct = QtCore.pyqtSignal(unicode)
-
- def __init__(self, *args):
- QtGui.QAction.__init__(self, *args)
-
- self.triggered.connect(lambda x: self.correct.emit(
- unicode(self.text())))
-
class HideMode(object):
"""
This is basically an enumeration class which specifies the mode of a Bible.
=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py 2010-09-03 19:49:09 +0000
+++ openlp/core/ui/maindisplay.py 2010-09-04 12:47:41 +0000
@@ -138,7 +138,7 @@
painter_image = QtGui.QPainter()
painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
- #Build the initial frame.
+ # Build the initial frame.
initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
=== modified file 'openlp/plugins/custom/forms/editcustomdialog.py'
--- openlp/plugins/custom/forms/editcustomdialog.py 2010-08-26 15:50:11 +0000
+++ openlp/plugins/custom/forms/editcustomdialog.py 2010-09-04 12:47:41 +0000
@@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import build_icon, translate
-from openlp.core.ui import SpellTextEdit
+from openlp.core.lib import build_icon, translate, SpellTextEdit
class Ui_CustomEditDialog(object):
def setupUi(self, customEditDialog):
=== modified file 'openlp/plugins/songs/forms/editversedialog.py'
--- openlp/plugins/songs/forms/editversedialog.py 2010-08-28 19:40:27 +0000
+++ openlp/plugins/songs/forms/editversedialog.py 2010-09-04 12:47:41 +0000
@@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import build_icon, translate
-from openlp.core.ui import SpellTextEdit
+from openlp.core.lib import build_icon, translate, SpellTextEdit
from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object):
Follow ups