This commit is contained in:
Jonathan Corwin 2010-09-04 14:35:15 +01:00
commit 704502b05d
8 changed files with 263 additions and 202 deletions

View File

@ -316,6 +316,7 @@ def expand_tags(text):
text = text.replace(tag[u'end tag'], tag[u'end html']) text = text.replace(tag[u'end tag'], tag[u'end html'])
return text return text
from spelltextedit import SpellTextEdit
from eventreceiver import Receiver from eventreceiver import Receiver
from settingsmanager import SettingsManager from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin from plugin import PluginStatus, Plugin

View File

@ -24,10 +24,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging
from PyQt4 import QtWebKit from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte from openlp.core.lib import image_to_byte
log = logging.getLogger(__name__)
HTMLSRC = u""" HTMLSRC = u"""
<html> <html>
<head> <head>
@ -39,7 +42,7 @@ HTMLSRC = u"""
border: 0; border: 0;
} }
body { body {
background-color: black; %s;
} }
.size { .size {
position: absolute; position: absolute;
@ -192,7 +195,7 @@ body {
function show_text(newtext){ function show_text(newtext){
if(timer != null) if(timer != null)
clearTimeout(timer); clearTimeout(timer);
text_fade('lyricsmain', newtext); text_fade('lyricsmain', newtext);
text_fade('lyricsoutline', newtext); text_fade('lyricsoutline', newtext);
text_fade('lyricsshadow', newtext); text_fade('lyricsshadow', newtext);
if(text_opacity()==1) return; if(text_opacity()==1) return;
@ -233,7 +236,7 @@ body {
var text = document.getElementById('lyricsmain'); var text = document.getElementById('lyricsmain');
return getComputedStyle(text, '').opacity; return getComputedStyle(text, '').opacity;
} }
function show_text_complete(){ function show_text_complete(){
return (text_opacity()==1); return (text_opacity()==1);
} }
@ -271,53 +274,89 @@ def build_html(item, screen, alert, islive):
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame) image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
else: else:
image = u'' image = u''
html = HTMLSRC % (width, height, html = HTMLSRC % (build_background_css(item, width, height),
width, height,
build_alert_css(alert, width), build_alert_css(alert, width),
build_footer_css(item), build_footer_css(item),
build_lyrics_css(item, webkitvers), build_lyrics_css(item, webkitvers),
u'true' if theme and theme.display_slideTransition and islive \ u'true' if theme and theme.display_slideTransition and islive \
else u'false', else u'false',
image, image,
build_lyrics_html(item, webkitvers)) build_lyrics_html(item, webkitvers))
return html return html
def webkit_version(): def webkit_version():
try: try:
webkitvers = float(QtWebKit.qWebKitVersion()) webkitvers = float(QtWebKit.qWebKitVersion())
log.debug(u'Webkit version = %s' % webkitvers)
except AttributeError: except AttributeError:
webkitvers = 0 webkitvers = 0
return webkitvers return webkitvers
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: ' \
u'-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, ' \
u'right top, from(%s), to(%s))' % \
(theme.background_startColor, theme.background_endColor)
else:
background = \
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
u'50%%, %s, from(%s), to(%s))' % (width, width, width,
theme.background_startColor, theme.background_endColor)
return background
def build_lyrics_css(item, webkitvers): def build_lyrics_css(item, webkitvers):
""" """
Build the lyrics display css Build the lyrics display css
`item` `item`
Service Item containing theme and location information Service Item containing theme and location information
`webkitvers` `webkitvers`
The version of qtwebkit we're using The version of qtwebkit we're using
""" """
style = """ style = """
.lyricstable { .lyricstable {
z-index:4; z-index:4;
position: absolute; position: absolute;
display: table; display: table;
%s
}
.lyricscell {
display:table-cell;
%s %s
} }
.lyricsmain { .lyricscell {
%s display:table-cell;
word-wrap: break-word;
%s
} }
.lyricsoutline { .lyricsmain {
%s %s
} }
.lyricsshadow { .lyricsoutline {
%s %s
}
.lyricsshadow {
%s
} }
""" """
theme = item.themedata theme = item.themedata
@ -332,19 +371,19 @@ def build_lyrics_css(item, webkitvers):
lyrics = build_lyrics_format_css(theme, item.main.width(), lyrics = build_lyrics_format_css(theme, item.main.width(),
item.main.height()) item.main.height())
# For performance reasons we want to show as few DIV's as possible, # For performance reasons we want to show as few DIV's as possible,
# especially when animating/transitions. # especially when animating/transitions.
# However some bugs in older versions of qtwebkit mean we need to # However some bugs in older versions of qtwebkit mean we need to
# perform workarounds and add extra divs. Only do these when needed. # 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. # 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 # with the fill, or normal text. letter-spacing=1 is workaround
# https://bugs.webkit.org/show_bug.cgi?id=44403 # https://bugs.webkit.org/show_bug.cgi?id=44403
# #
# Before 534.4 the text-shadow didn't get displayed when # Before 534.4 the text-shadow didn't get displayed when
# webkit-text-stroke was used. So use an offset text layer underneath. # webkit-text-stroke was used. So use an offset text layer underneath.
# https://bugs.webkit.org/show_bug.cgi?id=19728 # https://bugs.webkit.org/show_bug.cgi?id=19728
if webkitvers >= 533.3: if webkitvers >= 533.3:
lyricsmain += build_lyrics_outline_css(theme) lyricsmain += build_lyrics_outline_css(theme)
@ -422,11 +461,11 @@ def build_lyrics_html(item, webkitvers):
`item` `item`
Service Item containing theme and location information Service Item containing theme and location information
`webkitvers` `webkitvers`
The version of qtwebkit we're using 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. # divs for outline and shadow, since the CSS doesn't work.
# To support vertical alignment middle and bottom, nested div's using # To support vertical alignment middle and bottom, nested div's using
# display:table/display:table-cell are required for each lyric block. # display:table/display:table-cell are required for each lyric block.
@ -444,7 +483,7 @@ def build_lyrics_html(item, webkitvers):
u'<div id="lyricsmain" style="opacity:1" ' \ u'<div id="lyricsmain" style="opacity:1" ' \
u'class="lyricscell lyricsmain"></div></div>' u'class="lyricscell lyricsmain"></div></div>'
return lyrics return lyrics
def build_footer_css(item): def build_footer_css(item):
""" """
Build the display of the item footer Build the display of the item footer

View File

@ -185,44 +185,47 @@ class Renderer(object):
self.bg_frame = QtGui.QImage(self.frame.width(), self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied) self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
log.debug(u'render background %s start', self._theme.background_type) 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': if self._theme.background_type == u'solid':
painter.fillRect(self.frame.rect(), self.bg_frame = None
QtGui.QColor(self._theme.background_color)) # painter.fillRect(self.frame.rect(),
# QtGui.QColor(self._theme.background_color))
elif self._theme.background_type == u'gradient': elif self._theme.background_type == u'gradient':
self.bg_frame = None
# gradient # gradient
gradient = None # gradient = None
if self._theme.background_direction == u'horizontal': # if self._theme.background_direction == u'horizontal':
w = int(self.frame.width()) / 2 # w = int(self.frame.width()) / 2
# vertical # # vertical
gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height()) # gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
elif self._theme.background_direction == u'vertical': # elif self._theme.background_direction == u'vertical':
h = int(self.frame.height()) / 2 # h = int(self.frame.height()) / 2
# Horizontal # # Horizontal
gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h) # gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
else: # else:
w = int(self.frame.width()) / 2 # w = int(self.frame.width()) / 2
h = int(self.frame.height()) / 2 # h = int(self.frame.height()) / 2
# Circular # # Circular
gradient = QtGui.QRadialGradient(w, h, w) # gradient = QtGui.QRadialGradient(w, h, w)
gradient.setColorAt(0, # gradient.setColorAt(0,
QtGui.QColor(self._theme.background_startColor)) # QtGui.QColor(self._theme.background_startColor))
gradient.setColorAt(1, # gradient.setColorAt(1,
QtGui.QColor(self._theme.background_endColor)) # QtGui.QColor(self._theme.background_endColor))
painter.setBrush(QtGui.QBrush(gradient)) # painter.setBrush(QtGui.QBrush(gradient))
rect_path = QtGui.QPainterPath() # rect_path = QtGui.QPainterPath()
max_x = self.frame.width() # max_x = self.frame.width()
max_y = self.frame.height() # max_y = self.frame.height()
rect_path.moveTo(0, 0) # rect_path.moveTo(0, 0)
rect_path.lineTo(0, max_y) # rect_path.lineTo(0, max_y)
rect_path.lineTo(max_x, max_y) # rect_path.lineTo(max_x, max_y)
rect_path.lineTo(max_x, 0) # rect_path.lineTo(max_x, 0)
rect_path.closeSubpath() # rect_path.closeSubpath()
painter.drawPath(rect_path) # painter.drawPath(rect_path)
# painter.end()
elif self._theme.background_type == u'image': elif self._theme.background_type == u'image':
# image # image
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
painter.fillRect(self.frame.rect(), QtCore.Qt.black) painter.fillRect(self.frame.rect(), QtCore.Qt.black)
if self.bg_image: if self.bg_image:
painter.drawImage(0, 0, self.bg_image) painter.drawImage(0, 0, self.bg_image)
painter.end() painter.end()

View File

@ -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())))

View File

@ -27,141 +27,6 @@
The :mod:`ui` module provides the core user interface for OpenLP 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): class HideMode(object):
""" """
This is basically an enumeration class which specifies the mode of a Bible. This is basically an enumeration class which specifies the mode of a Bible.

View File

@ -138,7 +138,7 @@ class MainDisplay(DisplayWidget):
painter_image = QtGui.QPainter() painter_image = QtGui.QPainter()
painter_image.begin(self.black) painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black) painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
#Build the initial frame. # Build the initial frame.
initialFrame = QtGui.QImage( initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(), self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(), self.screens.current[u'size'].height(),

View File

@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate from openlp.core.lib import build_icon, translate, SpellTextEdit
from openlp.core.ui import SpellTextEdit
class Ui_CustomEditDialog(object): class Ui_CustomEditDialog(object):
def setupUi(self, customEditDialog): def setupUi(self, customEditDialog):

View File

@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate from openlp.core.lib import build_icon, translate, SpellTextEdit
from openlp.core.ui import SpellTextEdit
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object): class Ui_EditVerseDialog(object):