diff --git a/copyright.txt b/copyright.txt index f56b7712d..64d028205 100644 --- a/copyright.txt +++ b/copyright.txt @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### # OpenLP - Open Source Lyrics Projection # diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index f86f9036f..4bb0f0d28 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -43,14 +43,15 @@ from traceback import format_exception from PyQt4 import QtCore, QtGui -from openlp.core.lib import Settings, ScreenList, UiStrings, Registry, check_directory_exists +from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists +from openlp.core.lib import ScreenList, Registry from openlp.core.resources import qInitResources from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui import SplashScreen -from openlp.core.utils import AppLocation, LanguageManager, VersionThread, get_application_version +from openlp.core.utils import LanguageManager, VersionThread, get_application_version __all__ = ['OpenLP', 'main'] @@ -183,7 +184,8 @@ class OpenLP(QtGui.QApplication): ``traceback`` A traceback object with the details of where the exception occurred. """ - log.exception(''.join(format_exception(exctype, value, traceback))) + # We can't log.exception here because the last exception no longer exists, we're actually busy handling it. + log.critical(''.join(format_exception(exctype, value, traceback))) if not hasattr(self, 'exception_form'): self.exception_form = ExceptionForm() self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback))) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py new file mode 100644 index 000000000..42fde7065 --- /dev/null +++ b/openlp/core/common/__init__.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +The :mod:`common` module contains most of the components and libraries that make +OpenLP work. +""" +import os +import logging +import sys + +from PyQt4 import QtCore + +log = logging.getLogger(__name__) + + +def check_directory_exists(directory, do_not_log=False): + """ + Check a theme directory exists and if not create it + + ``directory`` + The directory to make sure exists + + ``do_not_log`` + To not log anything. This is need for the start up, when the log isn't ready. + """ + if not do_not_log: + log.debug('check_directory_exists %s' % directory) + try: + if not os.path.exists(directory): + os.makedirs(directory) + except IOError: + pass + + +def get_frozen_path(frozen_option, non_frozen_option): + """ + Return a path based on the system status. + """ + if hasattr(sys, 'frozen') and sys.frozen == 1: + return frozen_option + return non_frozen_option + + +class ThemeLevel(object): + """ + Provides an enumeration for the level a theme applies to + """ + Global = 1 + Service = 2 + Song = 3 + + +def translate(context, text, comment=None, encoding=QtCore.QCoreApplication.CodecForTr, n=-1, + qt_translate=QtCore.QCoreApplication.translate): + """ + A special shortcut method to wrap around the Qt4 translation functions. This abstracts the translation procedure so + that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. + + ``context`` + The translation context, used to give each string a context or a namespace. + + ``text`` + The text to put into the translation tables for translation. + + ``comment`` + An identifying string for when the same text is used in different roles within the same context. + """ + return qt_translate(context, text, comment, encoding, n) + + +class SlideLimits(object): + """ + Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down + arrow keys + """ + End = 1 + Wrap = 2 + Next = 3 + +from .uistrings import UiStrings +from .settings import Settings +from .applocation import AppLocation + diff --git a/openlp/core/utils/applocation.py b/openlp/core/common/applocation.py similarity index 91% rename from openlp/core/utils/applocation.py rename to openlp/core/common/applocation.py index 726cbbeda..41b47ecbe 100644 --- a/openlp/core/utils/applocation.py +++ b/openlp/core/common/applocation.py @@ -27,14 +27,13 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`openlp.core.utils.applocation` module provides an utility for OpenLP receiving the data path etc. +The :mod:`openlp.core.common.applocation` module provides an utility for OpenLP receiving the data path etc. """ import logging import os import sys -from openlp.core.lib import Settings -from openlp.core.utils import _get_frozen_path +from openlp.core.common import Settings if sys.platform != 'win32' and sys.platform != 'darwin': @@ -45,7 +44,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': XDG_BASE_AVAILABLE = False import openlp -from openlp.core.lib import check_directory_exists +from openlp.core.common import check_directory_exists, get_frozen_path log = logging.getLogger(__name__) @@ -74,15 +73,15 @@ class AppLocation(object): The directory type you want, for instance the data directory. Default *AppLocation.AppDir* """ if dir_type == AppLocation.AppDir: - return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) + return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) elif dir_type == AppLocation.PluginsDir: app_path = os.path.abspath(os.path.split(sys.argv[0])[0]) - return _get_frozen_path(os.path.join(app_path, 'plugins'), + return get_frozen_path(os.path.join(app_path, 'plugins'), os.path.join(os.path.split(openlp.__file__)[0], 'plugins')) elif dir_type == AppLocation.VersionDir: - return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) + return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) elif dir_type == AppLocation.LanguageDir: - app_path = _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type)) + app_path = get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type)) return os.path.join(app_path, 'i18n') elif dir_type == AppLocation.DataDir and AppLocation.BaseDir: return os.path.join(AppLocation.BaseDir, 'data') @@ -171,4 +170,3 @@ def _get_os_dir_path(dir_type): if dir_type == AppLocation.DataDir: return os.path.join(str(os.getenv('HOME')), '.openlp', 'data') return os.path.join(str(os.getenv('HOME')), '.openlp') - diff --git a/openlp/core/lib/settings.py b/openlp/core/common/settings.py similarity index 99% rename from openlp/core/lib/settings.py rename to openlp/core/common/settings.py index a124132ab..0b5aebc69 100644 --- a/openlp/core/lib/settings.py +++ b/openlp/core/common/settings.py @@ -36,8 +36,7 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.lib import SlideLimits, UiStrings -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import ThemeLevel, SlideLimits, UiStrings log = logging.getLogger(__name__) diff --git a/openlp/core/lib/uistrings.py b/openlp/core/common/uistrings.py similarity index 99% rename from openlp/core/lib/uistrings.py rename to openlp/core/common/uistrings.py index 6d4b4d250..d7a4c1c42 100644 --- a/openlp/core/lib/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -31,7 +31,7 @@ The :mod:`uistrings` module provides standard strings for OpenLP. """ import logging -from openlp.core.lib import translate +from openlp.core.common import translate log = logging.getLogger(__name__) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index bc427830e..67ac409df 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -37,6 +37,8 @@ import os from PyQt4 import QtCore, QtGui, Qt +from openlp.core.common import translate + log = logging.getLogger(__name__) @@ -72,16 +74,6 @@ class MediaType(object): Video = 2 -class SlideLimits(object): - """ - Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down - arrow keys - """ - End = 1 - Wrap = 2 - Next = 3 - - class ServiceItemAction(object): """ Provides an enumeration for the required action moving between service items by left/right arrow keys @@ -91,24 +83,6 @@ class ServiceItemAction(object): Next = 3 -def translate(context, text, comment=None, encoding=QtCore.QCoreApplication.CodecForTr, n=-1, - qt_translate=QtCore.QCoreApplication.translate): - """ - A special shortcut method to wrap around the Qt4 translation functions. This abstracts the translation procedure so - that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. - - ``context`` - The translation context, used to give each string a context or a namespace. - - ``text`` - The text to put into the translation tables for translation. - - ``comment`` - An identifying string for when the same text is used in different roles within the same context. - """ - return qt_translate(context, text, comment, encoding, n) - - def get_text_file_string(text_file): """ Open a file and return its content as unicode string. If the supplied file name is not a file then the function @@ -327,57 +301,36 @@ def expand_tags(text): return text -def check_directory_exists(directory, do_not_log=False): - """ - Check a theme directory exists and if not create it - - ``directory`` - The directory to make sure exists - - ``do_not_log`` - To not log anything. This is need for the start up, when the log isn't ready. - """ - if not do_not_log: - log.debug('check_directory_exists %s' % directory) - try: - if not os.path.exists(directory): - os.makedirs(directory) - except IOError: - pass - - -def create_separated_list(stringlist): +def create_separated_list(string_list): """ Returns a string that represents a join of a list of strings with a localized separator. This function corresponds to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from http://www.unicode.org/reports/tr35/#ListPatterns - ``stringlist`` + ``string_list`` List of unicode strings """ if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \ LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): - return QtCore.QLocale().createSeparatedList(stringlist) - if not stringlist: + return QtCore.QLocale().createSeparatedList(string_list) + if not string_list: return '' - elif len(stringlist) == 1: - return stringlist[0] - elif len(stringlist) == 2: + elif len(string_list) == 1: + return string_list[0] + elif len(string_list) == 2: return translate('OpenLP.core.lib', '%s and %s', - 'Locale list separator: 2 items') % (stringlist[0], stringlist[1]) + 'Locale list separator: 2 items') % (string_list[0], string_list[1]) else: merged = translate('OpenLP.core.lib', '%s, and %s', - 'Locale list separator: end') % (stringlist[-2], stringlist[-1]) - for index in reversed(list(range(1, len(stringlist) - 2))): + 'Locale list separator: end') % (string_list[-2], string_list[-1]) + for index in reversed(list(range(1, len(string_list) - 2))): merged = translate('OpenLP.core.lib', '%s, %s', - 'Locale list separator: middle') % (stringlist[index], merged) - return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (stringlist[0], merged) + 'Locale list separator: middle') % (string_list[index], merged) + return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged) from .registry import Registry -from .uistrings import UiStrings from .screen import ScreenList -from .settings import Settings from .listwidgetwithdnd import ListWidgetWithDnD from .treewidgetwithdnd import TreeWidgetWithDnD from .formattingtags import FormattingTags diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 19cde98eb..429c30262 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -41,9 +41,9 @@ from sqlalchemy.pool import NullPool from alembic.migration import MigrationContext from alembic.operations import Operations -from openlp.core.lib import translate, Settings +from openlp.core.common import AppLocation, Settings, translate from openlp.core.lib.ui import critical_error_message_box -from openlp.core.utils import AppLocation, delete_file +from openlp.core.utils import delete_file log = logging.getLogger(__name__) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index 01d679a17..58e3226d1 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -31,12 +31,13 @@ Provide HTML Tag management and Formatting Tag access class """ import json -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate class FormattingTags(object): """ - Static Class to HTML Tags to be access around the code the list is managed by the Options Tab. + Static Class for HTML Tags to be access around the code the list is managed by the Options Tab. """ html_expands = [] @@ -48,22 +49,15 @@ class FormattingTags(object): return FormattingTags.html_expands @staticmethod - def save_html_tags(): + def save_html_tags(new_tags): """ - Saves all formatting tags except protected ones. + Saves all formatting tags except protected ones + + `new_tags` + The tags to be saved.. """ - tags = [] - for tag in FormattingTags.html_expands: - if not tag['protected'] and not tag.get('temporary'): - # Using dict ensures that copy is made and encoding of values a little later does not affect tags in - # the original list - tags.append(dict(tag)) - tag = tags[-1] - # Remove key 'temporary' from tags. It is not needed to be saved. - if 'temporary' in tag: - del tag['temporary'] # Formatting Tags were also known as display tags. - Settings().setValue('formattingTags/html_tags', json.dumps(tags) if tags else '') + Settings().setValue('formattingTags/html_tags', json.dumps(new_tags) if new_tags else '') @staticmethod def load_tags(): diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 5aa701d95..07a83f336 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -26,7 +26,372 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +This module is responsible for generating the HTML for :class:`~openlp.core.ui.maindisplay`. The ``build_html`` function +is the function which has to be called from outside. The generated and returned HTML will look similar to this:: + + + + OpenLP Display + + + + + + + + + + + + + + +
+ +
+ + +""" import logging from PyQt4 import QtWebKit @@ -114,12 +479,6 @@ sup { document.getElementById('black').style.display = black; document.getElementById('lyricsmain').style.visibility = lyrics; document.getElementById('image').style.visibility = lyrics; - outline = document.getElementById('lyricsoutline') - if(outline != null) - outline.style.visibility = lyrics; - shadow = document.getElementById('lyricsshadow') - if(shadow != null) - shadow.style.visibility = lyrics; document.getElementById('footer').style.visibility = lyrics; } @@ -138,9 +497,6 @@ sup { */ var txt = document.getElementById('lyricsmain'); if(window.getComputedStyle(txt).textAlign == 'justify'){ - var outline = document.getElementById('lyricsoutline'); - if(outline != null) - txt = outline; if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){ new_text = new_text.replace(/(\s| )+(?![^<]*>)/g, function(match) { @@ -150,8 +506,6 @@ sup { } } text_fade('lyricsmain', new_text); - text_fade('lyricsoutline', new_text); - text_fade('lyricsshadow', new_text.replace(match, '')); } function text_fade(id, new_text){ @@ -190,7 +544,7 @@ sup { %s -%s +
@@ -222,8 +576,7 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): """ width = screen['size'].width() height = screen['size'].height() - theme = item.themedata - webkit_ver = webkit_version() + theme_data = item.themedata # Image generated and poked in if background: bgimage_src = 'src="data:image/png;base64,%s"' % background @@ -247,12 +600,12 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): build_background_css(item, width), css_additions, build_footer_css(item, height), - build_lyrics_css(item, webkit_ver), - 'true' if theme and theme.display_slide_transition and is_live else 'false', + build_lyrics_css(item), + 'true' if theme_data and theme_data.display_slide_transition and is_live else 'false', js_additions, - bgimage_src, image_src, - html_additions, - build_lyrics_html(item, webkit_ver) + bgimage_src, + image_src, + html_additions ) return html @@ -303,16 +656,13 @@ def build_background_css(item, width): return background -def build_lyrics_css(item, webkit_ver): +def build_lyrics_css(item): """ Build the lyrics display css ``item`` Service Item containing theme and location information - ``webkitvers`` - The version of qtwebkit we're using - """ style = """ .lyricstable { @@ -328,81 +678,44 @@ def build_lyrics_css(item, webkit_ver): %s } .lyricsmain { -%s + %s } -.lyricsoutline { -%s -} -.lyricsshadow { -%s -} - """ - theme = item.themedata +""" + theme_data = item.themedata lyricstable = '' lyrics = '' lyricsmain = '' - outline = '' - shadow = '' - if theme and item.main: + if theme_data and item.main: lyricstable = 'left: %spx; top: %spx;' % (item.main.x(), item.main.y()) - lyrics = build_lyrics_format_css(theme, item.main.width(), item.main.height()) - # 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 perform workarounds and add extra divs. Only - # do these when needed. - # - # Before 533.3 the webkit-text-fill colour wasn't displayed, only the stroke (outline) color. So put stroke - # layer underneath the main text. - # - # Up to 534.3 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 - # - # Up to 534.3 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 webkit_ver >= 533.3: - lyricsmain += build_lyrics_outline_css(theme) - else: - outline = build_lyrics_outline_css(theme) - if theme.font_main_shadow: - if theme.font_main_outline and webkit_ver <= 534.3: - shadow = 'padding-left: %spx; padding-top: %spx;' % \ - (int(theme.font_main_shadow_size) + (int(theme.font_main_outline_size) * 2), - theme.font_main_shadow_size) - shadow += build_lyrics_outline_css(theme, True) - else: - lyricsmain += ' text-shadow: %s %spx %spx;' % \ - (theme.font_main_shadow_color, theme.font_main_shadow_size, theme.font_main_shadow_size) - lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow) + lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height()) + lyricsmain += build_lyrics_outline_css(theme_data) + if theme_data.font_main_shadow: + lyricsmain += ' text-shadow: %s %spx %spx;' % \ + (theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size) + lyrics_css = style % (lyricstable, lyrics, lyricsmain) return lyrics_css -def build_lyrics_outline_css(theme, is_shadow=False): +def build_lyrics_outline_css(theme_data): """ Build the css which controls the theme outline. Also used by renderer for splitting verses - ``theme`` + ``theme_data`` Object containing theme information - - ``is_shadow`` - If true, use the shadow colors instead """ - if theme.font_main_outline: - size = float(theme.font_main_outline_size) / 16 - if is_shadow: - fill_color = theme.font_main_shadow_color - outline_color = theme.font_main_shadow_color - else: - fill_color = theme.font_main_color - outline_color = theme.font_main_outline_color + if theme_data.font_main_outline: + size = float(theme_data.font_main_outline_size) / 16 + fill_color = theme_data.font_main_color + outline_color = theme_data.font_main_outline_color return ' -webkit-text-stroke: %sem %s; -webkit-text-fill-color: %s; ' % (size, outline_color, fill_color) - else: - return '' + return '' -def build_lyrics_format_css(theme, width, height): +def build_lyrics_format_css(theme_data, width, height): """ Build the css which controls the theme format. Also used by renderer for splitting verses - ``theme`` + ``theme_data`` Object containing theme information ``width`` @@ -411,17 +724,17 @@ def build_lyrics_format_css(theme, width, height): ``height`` Height of the lyrics block """ - align = HorizontalType.Names[theme.display_horizontal_align] - valign = VerticalType.Names[theme.display_vertical_align] - if theme.font_main_outline: - left_margin = int(theme.font_main_outline_size) * 2 + align = HorizontalType.Names[theme_data.display_horizontal_align] + valign = VerticalType.Names[theme_data.display_vertical_align] + if theme_data.font_main_outline: + left_margin = int(theme_data.font_main_outline_size) * 2 else: left_margin = 0 justify = 'white-space:pre-wrap;' # fix tag incompatibilities - if theme.display_horizontal_align == HorizontalType.Justify: + if theme_data.display_horizontal_align == HorizontalType.Justify: justify = '' - if theme.display_vertical_align == VerticalType.Bottom: + if theme_data.display_vertical_align == VerticalType.Bottom: padding_bottom = '0.5em' else: padding_bottom = '0' @@ -429,41 +742,13 @@ def build_lyrics_format_css(theme, width, height): 'text-align: %s; vertical-align: %s; font-family: %s; ' \ 'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \ 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ - (justify, align, valign, theme.font_main_name, theme.font_main_size, - theme.font_main_color, 100 + int(theme.font_main_line_adjustment), padding_bottom, left_margin, width, height) - if theme.font_main_outline: - if webkit_version() <= 534.3: - lyrics += ' letter-spacing: 1px;' - if theme.font_main_italics: - lyrics += ' font-style:italic; ' - if theme.font_main_bold: - lyrics += ' font-weight:bold; ' - return lyrics - - -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 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. - lyrics = '' - theme = item.themedata - if webkitvers <= 534.3 and theme and theme.font_main_outline: - lyrics += '
' - if webkitvers < 533.3: - lyrics += '
' - lyrics += '
' + (justify, align, valign, theme_data.font_main_name, theme_data.font_main_size, + theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, + left_margin, width, height) + if theme_data.font_main_italics: + lyrics += 'font-style:italic; ' + if theme_data.font_main_bold: + lyrics += 'font-weight:bold; ' return lyrics diff --git a/openlp/core/lib/json/theme.json b/openlp/core/lib/json/theme.json new file mode 100644 index 000000000..e8862d0b4 --- /dev/null +++ b/openlp/core/lib/json/theme.json @@ -0,0 +1,59 @@ +{ + "background" : { + "border_color": "#000000", + "color": "#000000", + "direction": "vertical", + "end_color": "#000000", + "filename": "", + "start_color": "#000000", + "type": "solid" + }, + "display" :{ + "horizontal_align": 0, + "slide_transition": false, + "vertical_align": 0 + }, + "font": { + "footer": { + "bold": false, + "color": "#FFFFFF", + "height": 78, + "italics": false, + "line_adjustment": 0, + "location": "", + "name": "Arial", + "outline": false, + "outline_color": "#000000", + "outline_size": 2, + "override": false, + "shadow": true, + "shadow_color": "#000000", + "shadow_size": 5, + "size": 12, + "width": 1004, + "x": 10, + "y": 690 + }, + "main": { + "bold": false, + "color": "#FFFFFF", + "height": 690, + "italics": false, + "line_adjustment": 0, + "location": "", + "name": "Arial", + "outline": false, + "outline_color": "#000000", + "outline_size": 2, + "override": false, + "shadow": true, + "shadow_color": "#000000", + "shadow_size": 5, + "size": 40, + "width": 1004, + "x": 10, + "y": 10 + } + }, + "theme_name": "" +} diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 9e7eb8d73..01e16eef3 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -35,8 +35,9 @@ import re from PyQt4 import QtCore, QtGui +from openlp.core.common import Settings, UiStrings, translate from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \ - ServiceItemContext, Settings, Registry, UiStrings, translate + ServiceItemContext, Registry from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box @@ -82,10 +83,17 @@ class MediaManagerItem(QtGui.QWidget): """ Constructor to create the media manager item. """ - super(MediaManagerItem, self).__init__() + super(MediaManagerItem, self).__init__(parent) + self.plugin = plugin + self._setup() + self.setup_item() + + def _setup(self): + """ + Run some initial setup. This method is separate from __init__ in order to mock it out in tests. + """ self.hide() self.whitespace = re.compile(r'[\W_]+', re.UNICODE) - self.plugin = plugin visible_title = self.plugin.get_string(StringContent.VisibleName) self.title = str(visible_title['title']) Registry().register(self.plugin.name, self) @@ -106,6 +114,12 @@ class MediaManagerItem(QtGui.QWidget): QtCore.QObject.connect(self, QtCore.SIGNAL('%s_go_live' % self.plugin.name), self.go_live_remote) QtCore.QObject.connect(self, QtCore.SIGNAL('%s_add_to_service' % self.plugin.name), self.add_to_service_remote) + def setup_item(self): + """ + Override this for additional Plugin setup + """ + pass + def required_icons(self): """ This method is called to define the icons for the plugin. It provides a default set and the plugin is able to diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index fc9830398..2e6f42c6b 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtCore -from openlp.core.lib import Settings, Registry, UiStrings +from openlp.core.common import Settings, UiStrings +from openlp.core.lib import Registry from openlp.core.utils import get_application_version log = logging.getLogger(__name__) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 7b385d140..af7126535 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -35,7 +35,7 @@ import logging import imp from openlp.core.lib import Plugin, PluginStatus, Registry -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation log = logging.getLogger(__name__) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 4b2aacda6..b743f3962 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -31,9 +31,10 @@ import logging from PyQt4 import QtGui, QtCore, QtWebKit -from openlp.core.lib import Settings, FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \ +from openlp.core.common import Settings +from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \ ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import ThemeLevel from openlp.core.ui import MainDisplay log = logging.getLogger(__name__) diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index d1cca99cd..ddae9fba1 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -36,7 +36,8 @@ import copy from PyQt4 import QtCore -from openlp.core.lib import Registry, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Registry log = logging.getLogger(__name__) @@ -244,7 +245,6 @@ class ScreenList(object): """ Loads the screen size and the monitor number from the settings. """ - from openlp.core.lib import Settings # Add the screen settings to the settings dict. This has to be done here due to cyclic dependency. # Do not do this anywhere else. screen_settings = { diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index cbb3c8458..789854c78 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -39,7 +39,8 @@ import uuid from PyQt4 import QtGui -from openlp.core.lib import ImageSource, Settings, Registry, build_icon, clean_tags, expand_tags, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import ImageSource, Registry, build_icon, clean_tags, expand_tags log = logging.getLogger(__name__) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 28c0bcc91..86b126ed8 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -32,69 +32,16 @@ Provide the theme XML and handling functions for OpenLP v2 themes. import os import re import logging +import json from xml.dom.minidom import Document from lxml import etree, objectify +from openlp.core.common import AppLocation -from openlp.core.lib import str_to_bool, ScreenList +from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string log = logging.getLogger(__name__) -BLANK_THEME_XML = \ -''' - - - - - #000000 - - - #000000 - #000000 - vertical - - - #000000 - - - Arial - #FFFFFF - 40 - False - False - 0 - True - False - - - - Arial - #FFFFFF - 12 - False - False - 0 - True - False - - - - 0 - 0 - False - - -''' - - -class ThemeLevel(object): - """ - Provides an enumeration for the level a theme applies to - """ - Global = 1 - Service = 2 - Song = 3 - class BackgroundType(object): """ @@ -217,9 +164,32 @@ class ThemeXML(object): """ Initialise the theme object. """ - # Create the minidom document - self.theme_xml = Document() - self.parse_xml(BLANK_THEME_XML) + # basic theme object with defaults + json_dir = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'core', 'lib', 'json') + json_file = os.path.join(json_dir, 'theme.json') + jsn = get_text_file_string(json_file) + jsn = json.loads(jsn) + self.expand_json(jsn) + + def expand_json(self, var, prev=None): + """ + Expand the json objects and make into variables. + + ``var`` + The array list to be processed. + + ``prev`` + The preceding string to add to the key to make the variable. + """ + for key, value in var.items(): + if prev: + key = prev + "_" + key + else: + key = key + if isinstance(value, dict): + self.expand_json(value, key) + else: + setattr(self, key, value) def extend_image_filename(self, path): """ diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 14ffdc2e8..631187505 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -33,7 +33,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry, build_icon from openlp.core.utils.actions import ActionList diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py deleted file mode 100644 index 7bb581512..000000000 --- a/openlp/core/theme/theme.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2013 Raoul Snyman # -# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # -# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # -# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # -# 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, Martin Zibricky, Patrick Zimmermann # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -OpenLP version 1 theme handling - -Provides reference data, a default v1 XML theme and class wrapper for -processing version 1 themes in OpenLP version 2. -""" - -from xml.etree.ElementTree import ElementTree, XML -from PyQt4 import QtGui - -DELPHI_COLORS = { - 'clAqua': 0x00FFFF, - 'clBlack': 0x000000, - 'clBlue': 0x0000FF, - 'clFuchsia': 0xFF00FF, - 'clGray': 0x808080, - 'clGreen': 0x008000, - 'clLime': 0x00FF00, - 'clMaroon': 0x800000, - 'clNavy': 0x000080, - 'clOlive': 0x808000, - 'clPurple': 0x800080, - 'clRed': 0xFF0000, - 'clSilver': 0xC0C0C0, - 'clTeal': 0x008080, - 'clWhite': 0xFFFFFF, - 'clYellow': 0xFFFF00 -} - -BLANK_STYLE_XML = \ -''' - - BlankStyle - 1 - 0 - $000000 - - - Arial - clWhite - 30 - pixels - 0 - 0 - 0 - 0 - 0 - -''' - - -class Theme(object): - """ - Provide a class wrapper storing data from an XML theme - - ``name`` - Theme name - - ``BackgroundMode`` - The behaviour of the background. Valid modes are: - - * ``0`` - Transparent - * ``1`` - Opaque - - ``BackgroundType`` - The content of the background. Valid types are: - - * ``0`` - solid color - * ``1`` - gradient color - * ``2`` - image - - ``BackgroundParameter1`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - image filename - * ``gradient`` - start color - * ``solid`` - color - - ``BackgroundParameter2`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - border color - * ``gradient`` - end color - * ``solid`` - N/A - - ``BackgroundParameter3`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - N/A - * ``gradient`` - The direction of the gradient. Valid entries are: - - * ``0`` - vertical - * ``1`` - horizontal - - * ``solid`` - N/A - - ``FontName`` - Name of the font to use for the main font. - - ``FontColor`` - The color for the main font - - ``FontProportion`` - The size of the main font - - ``FontUnits`` - The units for FontProportion, either or - - ``Shadow`` - The shadow type to apply to the main font. - - * ``0`` - no shadow - * non-zero - use shadow - - ``ShadowColor`` - Color for the shadow - - ``Outline`` - The outline to apply to the main font - - * ``0`` - no outline - * non-zero - use outline - - ``OutlineColor`` - Color for the outline (or None if Outline is 0) - - ``HorizontalAlign`` - The horizontal alignment to apply to text. Valid alignments are: - - * ``0`` - left align - * ``1`` - right align - * ``2`` - centre align - - ``VerticalAlign`` - The vertical alignment to apply to the text. Valid alignments are: - - * ``0`` - top align - * ``1`` - bottom align - * ``2`` - centre align - - ``WrapStyle`` - The wrap style to apply to the text. Valid styles are: - - * ``0`` - normal - * ``1`` - lyrics - """ - - def __init__(self, xml): - """ - Initialise a theme with data from xml - - ``xml`` - The data to initialise the theme with - """ - # init to defaults - self._set_from_xml(BLANK_STYLE_XML) - self._set_from_xml(xml) - - def _get_as_string(self): - """ - Return single line string representation of a theme - """ - theme_strings = [] - keys = dir(self) - keys.sort() - for key in keys: - if key[0:1] != '_': - theme_strings.append('_%s_' % (getattr(self, key))) - return ''.join(theme_strings) - - def _set_from_xml(self, xml): - """ - Set theme class attributes with data from XML - - ``xml`` - The data to apply to the theme - """ - root = ElementTree(element=XML(xml.encode('ascii', 'xmlcharrefreplace'))) - xml_iter = root.getiterator() - for element in xml_iter: - delphi_color_change = False - if element.tag != 'Theme': - element_text = element.text - val = 0 - if element_text is None: - val = element_text - # strings need special handling to sort the colours out - if isinstance(element_text, str): - if element_text[0] == '$': - # might be a hex number - try: - val = int(element_text[1:], 16) - except ValueError: - # nope - pass - elif element_text in DELPHI_COLORS: - val = DELPHI_COLORS[element_text] - delphi_color_change = True - else: - try: - val = int(element_text) - except ValueError: - val = element_text - if (element.tag.find('Color') > 0 or (element.tag.find('BackgroundParameter') == 0 and - isinstance(val, int))): - # convert to a wx.Colour - if not delphi_color_change: - val = QtGui.QColor(val & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF) - else: - val = QtGui.QColor((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF) - setattr(self, element.tag, val) - - def __str__(self): - """ - Provide Python string representation for the class (multiline output) - """ - theme_strings = [] - for key in dir(self): - if key[0:1] != '_': - theme_strings.append('%30s : %s' % (key, getattr(self, key))) - return '\n'.join(theme_strings) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 14ac2fc47..691736c8f 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -95,13 +95,15 @@ from .aboutform import AboutForm from .pluginform import PluginForm from .settingsform import SettingsForm from .formattingtagform import FormattingTagForm +from .formattingtagcontroller import FormattingTagController from .shortcutlistform import ShortcutListForm from .mediadockmanager import MediaDockManager from .servicemanager import ServiceManager +from .thememanagerhelper import ThemeManagerHelper from .thememanager import ThemeManager __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm', 'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm', - 'FormattingTagForm', 'ShortcutListForm'] + 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper'] diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index e96553803..f825e9a63 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 92f565a71..83bd83e77 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -36,9 +36,9 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.lib import SettingsTab, Settings, UiStrings, translate, build_icon -from openlp.core.utils import AppLocation, format_time, get_images_filter -from openlp.core.lib import SlideLimits +from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate +from openlp.core.lib import SettingsTab, build_icon +from openlp.core.utils import format_time, get_images_filter log = logging.getLogger(__name__) @@ -283,7 +283,7 @@ class AdvancedTab(SettingsTab): self.service_name_day.setItemText(0, translate('OpenLP.AdvancedTab', 'Monday')) self.service_name_day.setItemText(1, translate('OpenLP.AdvancedTab', 'Tuesday')) self.service_name_day.setItemText(2, translate('OpenLP.AdvancedTab', 'Wednesday')) - self.service_name_day.setItemText(3, translate('OpenLP.AdvancedTab', 'Thurdsday')) + self.service_name_day.setItemText(3, translate('OpenLP.AdvancedTab', 'Thursday')) self.service_name_day.setItemText(4, translate('OpenLP.AdvancedTab', 'Friday')) self.service_name_day.setItemText(5, translate('OpenLP.AdvancedTab', 'Saturday')) self.service_name_day.setItemText(6, translate('OpenLP.AdvancedTab', 'Sunday')) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 2dc034f71..f1e57a545 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -85,7 +85,7 @@ try: except ImportError: VLC_VERSION = '-' -from openlp.core.lib import UiStrings, Settings, translate +from openlp.core.common import Settings, UiStrings, translate from openlp.core.utils import get_application_version from .exceptiondialog import Ui_ExceptionDialog @@ -101,7 +101,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): """ Constructor. """ - super(ExceptionForm, self).__init__(self.main_window) + super(ExceptionForm, self).__init__() self.setupUi(self) self.settings_section = 'crashreport' diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 79268f560..90fb8c648 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -34,7 +34,8 @@ from PyQt4 import QtGui from .filerenamedialog import Ui_FileRenameDialog -from openlp.core.lib import translate, Registry +from openlp.core.common import translate +from openlp.core.lib import Registry class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog): diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 509316658..e48395000 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,8 +41,9 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, check_directory_exists, translate -from openlp.core.utils import AppLocation, get_web_page +from openlp.core.common import AppLocation, Settings, check_directory_exists, translate +from openlp.core.lib import PluginStatus, Registry, build_icon +from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage log = logging.getLogger(__name__) diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 59dd95053..793d0adab 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -31,7 +31,7 @@ The UI widgets of the language selection dialog. """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 35a27d494..ac51de955 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui import sys -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import add_welcome_page diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py new file mode 100644 index 000000000..f2c081c51 --- /dev/null +++ b/openlp/core/ui/formattingtagcontroller.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are protected and included each time loaded. +Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags +cannot be changed. +""" + +import re +from openlp.core.common import translate +from openlp.core.lib import FormattingTags + + +class FormattingTagController(object): + """ + The :class:`FormattingTagController` manages the non UI functions . + """ + def __init__(self): + """ + Initiator + """ + self.html_tag_regex = re.compile(r'<(?:(?P/(?=[^\s/>]+>))?' + r'(?P[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P/)?' + r'|(?P!\[CDATA\[(?:(?!\]\]>).)*\]\])' + r'|(?P\?(?:(?!\?>).)*\?)' + r'|(?P!--(?:(?!-->).)*--))>', re.UNICODE) + self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern) + + def pre_save(self): + """ + Cleanup the array before save validation runs + """ + self.protected_tags = [tag for tag in FormattingTags.html_expands if tag.get('protected')] + self.custom_tags = [] + + def validate_for_save(self, desc, tag, start_html, end_html): + """ + Validate a custom tag and add to the tags array if valid.. + + `desc` + Explanation of the tag. + + `tag` + The tag in the song used to mark the text. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ + for linenumber, html1 in enumerate(self.protected_tags): + if self._strip(html1['start tag']) == tag: + return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + if self._strip(html1['desc']) == desc: + return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag + for linenumber, html1 in enumerate(self.custom_tags): + if self._strip(html1['start tag']) == tag: + return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + if self._strip(html1['desc']) == desc: + return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag + tag = { + 'desc': desc, + 'start tag': '{%s}' % tag, + 'start html': start_html, + 'end tag': '{/%s}' % tag, + 'end html': end_html, + 'protected': False, + 'temporary': False + } + self.custom_tags.append(tag) + + def save_tags(self): + """ + Save the new tags if they are valid. + """ + FormattingTags.save_html_tags(self.custom_tags) + FormattingTags.load_tags() + + def _strip(self, tag): + """ + Remove tag wrappers for editing. + + `tag` + Tag to be stripped + """ + tag = tag.replace('{', '') + tag = tag.replace('}', '') + return tag + + def start_html_to_end_html(self, start_html): + """ + Return the end HTML for a given start HTML or None if invalid. + + `start_html` + The start html tag. + + """ + end_tags = [] + match = self.html_regex.match(start_html) + if match: + match = self.html_tag_regex.search(start_html) + while match: + if match.group('tag'): + tag = match.group('tag').lower() + if match.group('close'): + if match.group('empty') or not end_tags or end_tags.pop() != tag: + return + elif not match.group('empty'): + end_tags.append(tag) + match = self.html_tag_regex.search(start_html, match.end()) + return ''.join(map(lambda tag: '' % tag, reversed(end_tags))) + + def start_tag_changed(self, start_html, end_html): + """ + Validate the HTML tags when the start tag has been changed. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ + end = self.start_html_to_end_html(start_html) + if not end_html: + if not end: + return translate('OpenLP.FormattingTagForm', 'Start tag %s is not valid HTML' % start_html), None + return None, end + return None, None + + def end_tag_changed(self, start_html, end_html): + """ + Validate the HTML tags when the end tag has been changed. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ + end = self.start_html_to_end_html(start_html) + if not end_html: + return None, end + if end and end != end_html: + return translate('OpenLP.FormattingTagForm', + 'End tag %s does not match end tag for start tag %s' % (end, start_html)), None + return None, None \ No newline at end of file diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index 20c982f36..f7855e470 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -31,7 +31,8 @@ The UI widgets for the formatting tags window. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -45,12 +46,34 @@ class Ui_FormattingTagDialog(object): """ formatting_tag_dialog.setObjectName('formatting_tag_dialog') formatting_tag_dialog.resize(725, 548) - self.list_data_grid_layout = QtGui.QGridLayout(formatting_tag_dialog) + self.list_data_grid_layout = QtGui.QVBoxLayout(formatting_tag_dialog) self.list_data_grid_layout.setMargin(8) self.list_data_grid_layout.setObjectName('list_data_grid_layout') + self.tag_table_widget_read_label = QtGui.QLabel() + self.list_data_grid_layout.addWidget(self.tag_table_widget_read_label) + self.tag_table_widget_read = QtGui.QTableWidget(formatting_tag_dialog) + self.tag_table_widget_read.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.tag_table_widget_read.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tag_table_widget_read.setAlternatingRowColors(True) + self.tag_table_widget_read.setCornerButtonEnabled(False) + self.tag_table_widget_read.setObjectName('tag_table_widget_read') + self.tag_table_widget_read.setColumnCount(4) + self.tag_table_widget_read.setRowCount(0) + self.tag_table_widget_read.horizontalHeader().setStretchLastSection(True) + item = QtGui.QTableWidgetItem() + self.tag_table_widget_read.setHorizontalHeaderItem(0, item) + item = QtGui.QTableWidgetItem() + self.tag_table_widget_read.setHorizontalHeaderItem(1, item) + item = QtGui.QTableWidgetItem() + self.tag_table_widget_read.setHorizontalHeaderItem(2, item) + item = QtGui.QTableWidgetItem() + self.tag_table_widget_read.setHorizontalHeaderItem(3, item) + self.list_data_grid_layout.addWidget(self.tag_table_widget_read) + self.tag_table_widget_label = QtGui.QLabel() + self.list_data_grid_layout.addWidget(self.tag_table_widget_label) self.tag_table_widget = QtGui.QTableWidget(formatting_tag_dialog) self.tag_table_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.tag_table_widget.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.tag_table_widget.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers) self.tag_table_widget.setAlternatingRowColors(True) self.tag_table_widget.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.tag_table_widget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) @@ -67,59 +90,26 @@ class Ui_FormattingTagDialog(object): self.tag_table_widget.setHorizontalHeaderItem(2, item) item = QtGui.QTableWidgetItem() self.tag_table_widget.setHorizontalHeaderItem(3, item) - self.list_data_grid_layout.addWidget(self.tag_table_widget, 0, 0, 1, 1) - self.horizontal_layout = QtGui.QHBoxLayout() - self.horizontal_layout.setObjectName('horizontal_layout') - spacer_item = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontal_layout.addItem(spacer_item) - self.delete_push_button = QtGui.QPushButton(formatting_tag_dialog) - self.delete_push_button.setObjectName('delete_push_button') - self.horizontal_layout.addWidget(self.delete_push_button) - self.list_data_grid_layout.addLayout(self.horizontal_layout, 1, 0, 1, 1) - self.edit_group_box = QtGui.QGroupBox(formatting_tag_dialog) - self.edit_group_box.setObjectName('edit_group_box') - self.data_grid_layout = QtGui.QGridLayout(self.edit_group_box) - self.data_grid_layout.setObjectName('data_grid_layout') - self.description_label = QtGui.QLabel(self.edit_group_box) - self.description_label.setAlignment(QtCore.Qt.AlignCenter) - self.description_label.setObjectName('description_label') - self.data_grid_layout.addWidget(self.description_label, 0, 0, 1, 1) - self.description_line_edit = QtGui.QLineEdit(self.edit_group_box) - self.description_line_edit.setObjectName('description_line_edit') - self.data_grid_layout.addWidget(self.description_line_edit, 0, 1, 2, 1) - self.new_push_button = QtGui.QPushButton(self.edit_group_box) - self.new_push_button.setObjectName('new_push_button') - self.data_grid_layout.addWidget(self.new_push_button, 0, 2, 2, 1) - self.tag_label = QtGui.QLabel(self.edit_group_box) - self.tag_label.setAlignment(QtCore.Qt.AlignCenter) - self.tag_label.setObjectName('tag_label') - self.data_grid_layout.addWidget(self.tag_label, 2, 0, 1, 1) - self.tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - self.tag_line_edit.setMaximumSize(QtCore.QSize(50, 16777215)) - self.tag_line_edit.setMaxLength(5) - self.tag_line_edit.setObjectName('tag_line_edit') - self.data_grid_layout.addWidget(self.tag_line_edit, 2, 1, 1, 1) - self.start_tag_label = QtGui.QLabel(self.edit_group_box) - self.start_tag_label.setAlignment(QtCore.Qt.AlignCenter) - self.start_tag_label.setObjectName('start_tag_label') - self.data_grid_layout.addWidget(self.start_tag_label, 3, 0, 1, 1) - self.start_tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - self.start_tag_line_edit.setObjectName('start_tag_line_edit') - self.data_grid_layout.addWidget(self.start_tag_line_edit, 3, 1, 1, 1) - self.end_tag_label = QtGui.QLabel(self.edit_group_box) - self.end_tag_label.setAlignment(QtCore.Qt.AlignCenter) - self.end_tag_label.setObjectName('end_tag_label') - self.data_grid_layout.addWidget(self.end_tag_label, 4, 0, 1, 1) - self.end_tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - self.end_tag_line_edit.setObjectName('end_tag_line_edit') - self.data_grid_layout.addWidget(self.end_tag_line_edit, 4, 1, 1, 1) - self.save_push_button = QtGui.QPushButton(self.edit_group_box) - self.save_push_button.setObjectName('save_push_button') - self.data_grid_layout.addWidget(self.save_push_button, 4, 2, 1, 1) - self.list_data_grid_layout.addWidget(self.edit_group_box, 2, 0, 1, 1) - self.button_box = create_button_box(formatting_tag_dialog, 'button_box', ['close']) - self.list_data_grid_layout.addWidget(self.button_box, 3, 0, 1, 1) - + self.list_data_grid_layout.addWidget(self.tag_table_widget) + self.edit_button_layout = QtGui.QHBoxLayout() + self.new_button = QtGui.QPushButton(formatting_tag_dialog) + self.new_button.setIcon(build_icon(':/general/general_new.png')) + self.new_button.setObjectName('new_button') + self.edit_button_layout.addWidget(self.new_button) + self.delete_button = QtGui.QPushButton(formatting_tag_dialog) + self.delete_button.setIcon(build_icon(':/general/general_delete.png')) + self.delete_button.setObjectName('delete_button') + self.edit_button_layout.addWidget(self.delete_button) + self.edit_button_layout.addStretch() + self.list_data_grid_layout.addLayout(self.edit_button_layout) + self.button_box = create_button_box(formatting_tag_dialog, 'button_box', + ['cancel', 'save', 'defaults']) + self.save_button = self.button_box.button(QtGui.QDialogButtonBox.Save) + self.save_button.setObjectName('save_button') + self.restore_button = self.button_box.button(QtGui.QDialogButtonBox.RestoreDefaults) + self.restore_button.setIcon(build_icon(':/general/general_revert.png')) + self.restore_button.setObjectName('restore_button') + self.list_data_grid_layout.addWidget(self.button_box) self.retranslateUi(formatting_tag_dialog) def retranslateUi(self, formatting_tag_dialog): @@ -127,14 +117,19 @@ class Ui_FormattingTagDialog(object): Translate the UI on the fly """ formatting_tag_dialog.setWindowTitle(translate('OpenLP.FormattingTagDialog', 'Configure Formatting Tags')) - self.edit_group_box.setTitle(translate('OpenLP.FormattingTagDialog', 'Edit Selection')) - self.save_push_button.setText(translate('OpenLP.FormattingTagDialog', 'Save')) - self.description_label.setText(translate('OpenLP.FormattingTagDialog', 'Description')) - self.tag_label.setText(translate('OpenLP.FormattingTagDialog', 'Tag')) - self.start_tag_label.setText(translate('OpenLP.FormattingTagDialog', 'Start HTML')) - self.end_tag_label.setText(translate('OpenLP.FormattingTagDialog', 'End HTML')) - self.delete_push_button.setText(UiStrings().Delete) - self.new_push_button.setText(UiStrings().New) + self.delete_button.setText(UiStrings().Delete) + self.new_button.setText(UiStrings().New) + self.tag_table_widget_read_label.setText(translate('OpenLP.FormattingTagDialog', 'Default Formatting')) + self.tag_table_widget_read.horizontalHeaderItem(0).\ + setText(translate('OpenLP.FormattingTagDialog', 'Description')) + self.tag_table_widget_read.horizontalHeaderItem(1).setText(translate('OpenLP.FormattingTagDialog', 'Tag')) + self.tag_table_widget_read.horizontalHeaderItem(2).\ + setText(translate('OpenLP.FormattingTagDialog', 'Start HTML')) + self.tag_table_widget_read.horizontalHeaderItem(3).setText(translate('OpenLP.FormattingTagDialog', 'End HTML')) + self.tag_table_widget_read.setColumnWidth(0, 120) + self.tag_table_widget_read.setColumnWidth(1, 80) + self.tag_table_widget_read.setColumnWidth(2, 330) + self.tag_table_widget_label.setText(translate('OpenLP.FormattingTagDialog', 'Custom Formatting')) self.tag_table_widget.horizontalHeaderItem(0).setText(translate('OpenLP.FormattingTagDialog', 'Description')) self.tag_table_widget.horizontalHeaderItem(1).setText(translate('OpenLP.FormattingTagDialog', 'Tag')) self.tag_table_widget.horizontalHeaderItem(2).setText(translate('OpenLP.FormattingTagDialog', 'Start HTML')) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index d61eab23d..420a9c2a2 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -31,14 +31,26 @@ The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are pro Custom tags can be defined and saved. The Custom Tag arrays are saved in a json string so QSettings works on them. Base Tags cannot be changed. """ + from PyQt4 import QtGui -from openlp.core.lib import FormattingTags, translate -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.common import translate +from openlp.core.lib import FormattingTags from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog +from openlp.core.ui.formattingtagcontroller import FormattingTagController -class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): +class EditColumn(object): + """ + Hides the magic numbers for the table columns + """ + Description = 0 + Tag = 1 + StartHtml = 2 + EndHtml = 3 + + +class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagController): """ The :class:`FormattingTagForm` manages the settings tab . """ @@ -48,17 +60,17 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ super(FormattingTagForm, self).__init__(parent) self.setupUi(self) + self.services = FormattingTagController() self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) - self.new_push_button.clicked.connect(self.on_new_clicked) - self.save_push_button.clicked.connect(self.on_saved_clicked) - self.delete_push_button.clicked.connect(self.on_delete_clicked) + self.new_button.clicked.connect(self.on_new_clicked) + #self.save_button.clicked.connect(self.on_saved_clicked) + self.delete_button.clicked.connect(self.on_delete_clicked) + self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed) self.button_box.rejected.connect(self.close) - self.description_line_edit.textEdited.connect(self.on_text_edited) - self.tag_line_edit.textEdited.connect(self.on_text_edited) - self.start_tag_line_edit.textEdited.connect(self.on_text_edited) - self.end_tag_line_edit.textEdited.connect(self.on_text_edited) # Forces reloading of tags from openlp configuration. FormattingTags.load_tags() + self.is_deleting = False + self.reloading = False def exec_(self): """ @@ -66,138 +78,128 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ # Create initial copy from master self._reloadTable() - self.selected = -1 return QtGui.QDialog.exec_(self) def on_row_selected(self): """ Table Row selected so display items and set field state. """ - self.save_push_button.setEnabled(False) - self.selected = self.tag_table_widget.currentRow() - html = FormattingTags.get_html_tags()[self.selected] - self.description_line_edit.setText(html['desc']) - self.tag_line_edit.setText(self._strip(html['start tag'])) - self.start_tag_line_edit.setText(html['start html']) - self.end_tag_line_edit.setText(html['end html']) - if html['protected']: - self.description_line_edit.setEnabled(False) - self.tag_line_edit.setEnabled(False) - self.start_tag_line_edit.setEnabled(False) - self.end_tag_line_edit.setEnabled(False) - self.delete_push_button.setEnabled(False) - else: - self.description_line_edit.setEnabled(True) - self.tag_line_edit.setEnabled(True) - self.start_tag_line_edit.setEnabled(True) - self.end_tag_line_edit.setEnabled(True) - self.delete_push_button.setEnabled(True) - - def on_text_edited(self, text): - """ - Enable the ``save_push_button`` when any of the selected tag's properties - has been changed. - """ - self.save_push_button.setEnabled(True) + self.delete_button.setEnabled(True) def on_new_clicked(self): """ - Add a new tag to list only if it is not a duplicate. + Add a new tag to edit list and select it for editing. """ - for html in FormattingTags.get_html_tags(): - if self._strip(html['start tag']) == 'n': - critical_error_message_box( - translate('OpenLP.FormattingTagForm', 'Update Error'), - translate('OpenLP.FormattingTagForm', 'Tag "n" already defined.')) - return - # Add new tag to list - tag = { - 'desc': translate('OpenLP.FormattingTagForm', 'New Tag'), - 'start tag': '{n}', - 'start html': translate('OpenLP.FormattingTagForm', ''), - 'end tag': '{/n}', - 'end html': translate('OpenLP.FormattingTagForm', ''), - 'protected': False, - 'temporary': False - } - FormattingTags.add_html_tags([tag]) - FormattingTags.save_html_tags() - self._reloadTable() - # Highlight new row - self.tag_table_widget.selectRow(self.tag_table_widget.rowCount() - 1) - self.on_row_selected() + new_row = self.tag_table_widget.rowCount() + self.tag_table_widget.insertRow(new_row) + self.tag_table_widget.setItem(new_row, 0, + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag%s') % str(new_row))) + self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%s' % str(new_row))) + self.tag_table_widget.setItem(new_row, 2, + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) + self.tag_table_widget.setItem(new_row, 3, QtGui.QTableWidgetItem('')) + self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.scrollToBottom() + self.tag_table_widget.selectRow(new_row) def on_delete_clicked(self): """ - Delete selected custom tag. + Delete selected custom row. """ - if self.selected != -1: - FormattingTags.remove_html_tag(self.selected) - # As the first items are protected we should not have to take care - # of negative indexes causing tracebacks. - self.tag_table_widget.selectRow(self.selected - 1) - self.selected = -1 - FormattingTags.save_html_tags() - self._reloadTable() + selected = self.tag_table_widget.currentRow() + if selected != -1: + self.is_deleting = True + self.tag_table_widget.removeRow(selected) - def on_saved_clicked(self): + def accept(self): """ Update Custom Tag details if not duplicate and save the data. """ - html_expands = FormattingTags.get_html_tags() - if self.selected != -1: - html = html_expands[self.selected] - tag = self.tag_line_edit.text() - for linenumber, html1 in enumerate(html_expands): - if self._strip(html1['start tag']) == tag and linenumber != self.selected: - critical_error_message_box( - translate('OpenLP.FormattingTagForm', 'Update Error'), - translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag) - return - html['desc'] = self.description_line_edit.text() - html['start html'] = self.start_tag_line_edit.text() - html['end html'] = self.end_tag_line_edit.text() - html['start tag'] = '{%s}' % tag - html['end tag'] = '{/%s}' % tag - # Keep temporary tags when the user changes one. - html['temporary'] = False - self.selected = -1 - FormattingTags.save_html_tags() - self._reloadTable() + count = 0 + self.services.pre_save() + while count < self.tag_table_widget.rowCount(): + error = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(), + self.tag_table_widget.item(count, 1).text(), self.tag_table_widget.item(count, 2).text(), + self.tag_table_widget.item(count, 3).text()) + if error: + QtGui.QMessageBox.warning(self, + translate('OpenLP.FormattingTagForm', 'Validation Error'), error, QtGui.QMessageBox.Ok) + self.tag_table_widget.selectRow(count) + return + count += 1 + self.services.save_tags() + QtGui.QDialog.accept(self) def _reloadTable(self): """ Reset List for loading. """ + self.reloading = True + self.tag_table_widget_read.clearContents() + self.tag_table_widget_read.setRowCount(0) self.tag_table_widget.clearContents() self.tag_table_widget.setRowCount(0) - self.new_push_button.setEnabled(True) - self.save_push_button.setEnabled(False) - self.delete_push_button.setEnabled(False) + self.new_button.setEnabled(True) + self.delete_button.setEnabled(False) for linenumber, html in enumerate(FormattingTags.get_html_tags()): - self.tag_table_widget.setRowCount(self.tag_table_widget.rowCount() + 1) - self.tag_table_widget.setItem(linenumber, 0, QtGui.QTableWidgetItem(html['desc'])) - self.tag_table_widget.setItem(linenumber, 1, QtGui.QTableWidgetItem(self._strip(html['start tag']))) - self.tag_table_widget.setItem(linenumber, 2, QtGui.QTableWidgetItem(html['start html'])) - self.tag_table_widget.setItem(linenumber, 3, QtGui.QTableWidgetItem(html['end html'])) - # Permanent (persistent) tags do not have this key. - if 'temporary' not in html: + if html['protected']: + line = self.tag_table_widget_read.rowCount() + self.tag_table_widget_read.setRowCount(line + 1) + self.tag_table_widget_read.setItem(line, 0, QtGui.QTableWidgetItem(html['desc'])) + self.tag_table_widget_read.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html['start tag']))) + self.tag_table_widget_read.setItem(line, 2, QtGui.QTableWidgetItem(html['start html'])) + self.tag_table_widget_read.setItem(line, 3, QtGui.QTableWidgetItem(html['end html'])) + self.tag_table_widget_read.resizeRowsToContents() + else: + line = self.tag_table_widget.rowCount() + self.tag_table_widget.setRowCount(line + 1) + self.tag_table_widget.setItem(line, 0, QtGui.QTableWidgetItem(html['desc'])) + self.tag_table_widget.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html['start tag']))) + self.tag_table_widget.setItem(line, 2, QtGui.QTableWidgetItem(html['start html'])) + self.tag_table_widget.setItem(line, 3, QtGui.QTableWidgetItem(html['end html'])) + self.tag_table_widget.resizeRowsToContents() + # Permanent (persistent) tags do not have this key html['temporary'] = False - self.tag_table_widget.resizeRowsToContents() - self.description_line_edit.setText('') - self.tag_line_edit.setText('') - self.start_tag_line_edit.setText('') - self.end_tag_line_edit.setText('') - self.description_line_edit.setEnabled(False) - self.tag_line_edit.setEnabled(False) - self.start_tag_line_edit.setEnabled(False) - self.end_tag_line_edit.setEnabled(False) + self.reloading = False - def _strip(self, tag): + def on_current_cell_changed(self, cur_row, cur_col, pre_row, pre_col): """ - Remove tag wrappers for editing. + This function processes all user edits in the table. It is called on each cell change. """ - tag = tag.replace('{', '') - tag = tag.replace('}', '') - return tag + if self.is_deleting: + self.is_deleting = False + return + if self.reloading: + return + # only process for editable rows + if self.tag_table_widget.item(pre_row, 0): + item = self.tag_table_widget.item(pre_row, pre_col) + text = item.text() + errors = None + if pre_col is EditColumn.Description: + if not text: + errors = translate('OpenLP.FormattingTagForm', 'Description is missing') + elif pre_col is EditColumn.Tag: + if not text: + errors = translate('OpenLP.FormattingTagForm', 'Tag is missing') + elif pre_col is EditColumn.StartHtml: + # HTML edited + item = self.tag_table_widget.item(pre_row, 3) + end_html = item.text() + errors, tag = self.services.start_tag_changed(text, end_html) + if tag: + self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) + self.tag_table_widget.resizeRowsToContents() + elif pre_col is EditColumn.EndHtml: + # HTML edited + item = self.tag_table_widget.item(pre_row, 2) + start_html = item.text() + errors, tag = self.services.end_tag_changed(start_html, text) + if tag: + self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) + if errors: + QtGui.QMessageBox.warning(self, + translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, QtGui.QMessageBox.Ok) + #self.tag_table_widget.selectRow(pre_row - 1) + self.tag_table_widget.resizeRowsToContents() + diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 42bba94d3..2e758ed34 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -33,7 +33,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, SettingsTab, ScreenList, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab, ScreenList log = logging.getLogger(__name__) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index f2c1033ed..af9e38f15 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -44,8 +44,8 @@ import sys from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL from PyQt4.phonon import Phonon -from openlp.core.lib import ServiceItem, Settings, ImageSource, Registry, build_html, expand_tags, \ - image_to_byte, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import ServiceItem, ImageSource, Registry, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType from openlp.core.lib import ScreenList @@ -243,8 +243,6 @@ class MainDisplay(Display): # Windows if there are many items in the service to re-render. # Setting the div elements direct seems to solve the issue self.frame.findFirstElement("#lyricsmain").setInnerXml(slide) - self.frame.findFirstElement("#lyricsoutline").setInnerXml(slide) - self.frame.findFirstElement("#lyricsshadow").setInnerXml(slide) def alert(self, text, location): """ diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index edd898be1..3382d281d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -42,12 +42,14 @@ from datetime import datetime from PyQt4 import QtCore, QtGui from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \ - Settings, ScreenList, build_icon, check_directory_exists, translate + ScreenList, build_icon from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm + +from openlp.core.common import AppLocation, Settings, check_directory_exists, translate from openlp.core.ui.media import MediaController -from openlp.core.utils import AppLocation, LanguageManager, add_actions, get_application_version +from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.firsttimeform import FirstTimeForm diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 19771862f..02c22fc68 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -31,7 +31,7 @@ The :mod:`~openlp.core.ui.media` module contains classes and objects for media p """ import logging -from openlp.core.lib import Settings +from openlp.core.common import Settings from PyQt4 import QtCore diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b1fa43ea9..eb2d932ee 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -35,11 +35,12 @@ import os import datetime from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, Settings, Registry, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players from openlp.core.ui.media.mediaplayer import MediaPlayer -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation from openlp.core.ui import DisplayControllerType log = logging.getLogger(__name__) diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 0ea0bf2ff..9991d682c 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -36,7 +36,8 @@ from datetime import datetime from PyQt4 import QtGui from PyQt4.phonon import Phonon -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer @@ -45,36 +46,22 @@ from openlp.core.ui.media.mediaplayer import MediaPlayer log = logging.getLogger(__name__) ADDITIONAL_EXT = { - 'audio/ac3': ['.ac3'], - 'audio/flac': ['.flac'], - 'audio/x-m4a': ['.m4a'], - 'audio/midi': ['.mid', '.midi'], - 'audio/x-mp3': ['.mp3'], - 'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'], - 'audio/qcelp': ['.qcp'], - 'audio/x-wma': ['.wma'], - 'audio/x-ms-wma': ['.wma'], - 'video/x-flv': ['.flv'], - 'video/x-matroska': ['.mpv', '.mkv'], - 'video/x-wmv': ['.wmv'], - 'video/x-mpg': ['.mpg'], - 'video/mpeg': ['.mp4', '.mts', '.mov'], - 'video/x-ms-wmv': ['.wmv']} - -VIDEO_CSS = """ -#videobackboard { - z-index:3; - background-color: %(bgcolor)s; + 'audio/ac3': ['.ac3'], + 'audio/flac': ['.flac'], + 'audio/x-m4a': ['.m4a'], + 'audio/midi': ['.mid', '.midi'], + 'audio/x-mp3': ['.mp3'], + 'audio/mpeg': ['.mp3', '.mp2', '.mpga', '.mpega', '.m4a'], + 'audio/qcelp': ['.qcp'], + 'audio/x-wma': ['.wma'], + 'audio/x-ms-wma': ['.wma'], + 'video/x-flv': ['.flv'], + 'video/x-matroska': ['.mpv', '.mkv'], + 'video/x-wmv': ['.wmv'], + 'video/x-mpg': ['.mpg'], + 'video/mpeg': ['.mp4', '.mts', '.mov'], + 'video/x-ms-wmv': ['.wmv'] } -#video1 { - background-color: %(bgcolor)s; - z-index:4; -} -#video2 { - background-color: %(bgcolor)s; - z-index:4; -} -""" class PhononPlayer(MediaPlayer): @@ -268,8 +255,7 @@ class PhononPlayer(MediaPlayer): """ Add css style sheets to htmlbuilder """ - background = QtGui.QColor(Settings().value('players/background color')).name() - return VIDEO_CSS % {'bgcolor': background} + return '' def get_info(self): """ diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py index b01f37ac7..4aa9feb0f 100644 --- a/openlp/core/ui/media/playertab.py +++ b/openlp/core/ui/media/playertab.py @@ -31,7 +31,8 @@ The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab fo """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import create_button from openlp.core.ui.media import get_media_players, set_media_players diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 2055f287b..139751603 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -37,7 +37,8 @@ import sys from PyQt4 import QtGui -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 3983a1e8d..c8cae2d13 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -33,7 +33,8 @@ from PyQt4 import QtGui import logging -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index d0bc0f103..fe7a185d6 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -31,7 +31,7 @@ The UI widgets of the plugin view dialog #""" from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate +from openlp.core.common import UiStrings, translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index f0c7dfaf7..504f04d59 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtGui -from openlp.core.lib import PluginStatus, Registry, translate +from openlp.core.common import translate +from openlp.core.lib import PluginStatus, Registry from .plugindialog import Ui_PluginViewDialog log = logging.getLogger(__name__) diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 6f007cf64..13308d6aa 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -31,7 +31,8 @@ The UI widgets of the print service dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import SpellTextEdit, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import SpellTextEdit, build_icon class ZoomSize(object): diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 660b708d8..6c2dab2be 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -36,9 +36,10 @@ import os from PyQt4 import QtCore, QtGui from lxml import html -from openlp.core.lib import Settings, UiStrings, Registry, translate, get_text_file_string +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, get_text_file_string from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation DEFAULT_CSS = """/* Edit this file to customize the service order print. Note, that not all CSS diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index 72b48d8f5..938366d6d 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -31,7 +31,7 @@ The UI widgets for the service item edit dialog """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index ad4168bfd..3e4b053cc 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -29,7 +29,7 @@ """ The service item edit dialog """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from openlp.core.lib import Registry from .serviceitemeditdialog import Ui_ServiceItemEditDialog diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index c00f4159b..58904fd98 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,13 +42,12 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, Settings, PluginStatus, Registry, \ - UiStrings, build_icon, translate, str_to_bool, check_directory_exists -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, build_icon from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm -from openlp.core.utils import AppLocation, delete_file, split_filename, format_time +from openlp.core.utils import delete_file, split_filename, format_time from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index fed016bed..18998c78d 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -31,7 +31,8 @@ The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm` """ from PyQt4 import QtGui -from openlp.core.lib import SpellTextEdit, Registry, translate +from openlp.core.common import translate +from openlp.core.lib import SpellTextEdit, Registry from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 87923630a..31b5c841c 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -31,7 +31,8 @@ The UI widgets of the settings dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 7e2c091c8..c70311a4b 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -31,7 +31,8 @@ The list of shortcuts within a dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index f49b66678..efe876e3e 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -33,8 +33,8 @@ import re from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings -from openlp.core.utils import translate +from openlp.core.lib import Registry +from openlp.core.common import Settings, translate from openlp.core.utils.actions import ActionList from .shortcutlistdialog import Ui_ShortcutListDialog diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index a04af6525..043838f36 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -37,8 +37,9 @@ from collections import deque from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, SlideLimits, \ - ServiceItemAction, Settings, Registry, UiStrings, ScreenList, build_icon, build_html, translate +from openlp.core.common import Settings, SlideLimits, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, Registry, \ + ScreenList, build_icon, build_html from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index dfd794f26..24e11c14e 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -31,7 +31,7 @@ The UI widgets for the time dialog """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate +from openlp.core.common import UiStrings, translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 0a0867b3f..308453978 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -33,7 +33,8 @@ from PyQt4 import QtGui from .starttimedialog import Ui_StartTimeDialog -from openlp.core.lib import UiStrings, Registry, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fe92d679b..46f632827 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, Registry, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ThemeLayoutForm diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index ae492941a..41b2dabd0 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -31,7 +31,7 @@ The layout of the theme """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index cbc6701df..8e1838d5d 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -38,18 +38,18 @@ import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui -from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \ - build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb -from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate +from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \ + check_item_selected, create_thumb, validate_thumb +from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action -from openlp.core.theme import Theme -from openlp.core.ui import FileRenameForm, ThemeForm -from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_filesystem_encoding +from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper +from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding log = logging.getLogger(__name__) -class ThemeManager(QtGui.QWidget): +class ThemeManager(QtGui.QWidget, ThemeManagerHelper): """ Manages the orders of Theme. """ @@ -328,8 +328,8 @@ class ThemeManager(QtGui.QWidget): try: encoding = get_filesystem_encoding() shutil.rmtree(os.path.join(self.path, theme).encode(encoding)) - except OSError as xxx_todo_changeme1: - shutil.Error = xxx_todo_changeme1 + except OSError as os_error: + shutil.Error = os_error log.exception('Error deleting theme %s', theme) def on_export_theme(self): @@ -469,7 +469,7 @@ class ThemeManager(QtGui.QWidget): log.debug('No theme data - using default theme') return ThemeXML() else: - return self._create_theme_fom_Xml(xml, self.path) + return self._create_theme_from_Xml(xml, self.path) def over_write_message_box(self, theme_name): """ @@ -501,35 +501,30 @@ class ThemeManager(QtGui.QWidget): log.exception('Theme contains "%s" XML files' % len(xml_file)) raise Exception('validation') xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot() - v1_background = xml_tree.find('BackgroundType') - if v1_background is not None: - theme_name, file_xml, out_file, abort_import = \ - self.unzip_version_122(directory, theme_zip, xml_file[0], xml_tree, v1_background, out_file) + theme_name = xml_tree.find('name').text.strip() + theme_folder = os.path.join(directory, theme_name) + theme_exists = os.path.exists(theme_folder) + if theme_exists and not self.over_write_message_box(theme_name): + abort_import = True + return else: - theme_name = xml_tree.find('name').text.strip() - theme_folder = os.path.join(directory, theme_name) - theme_exists = os.path.exists(theme_folder) - if theme_exists and not self.over_write_message_box(theme_name): - abort_import = True - return + abort_import = False + for name in theme_zip.namelist(): + name = name.replace('/', os.path.sep) + split_name = name.split(os.path.sep) + if split_name[-1] == '' or len(split_name) == 1: + # is directory or preview file + continue + full_name = os.path.join(directory, name) + check_directory_exists(os.path.dirname(full_name)) + if os.path.splitext(name)[1].lower() == '.xml': + file_xml = str(theme_zip.read(name), 'utf-8') + out_file = open(full_name, 'w') + out_file.write(file_xml) else: - abort_import = False - for name in theme_zip.namelist(): - name = name.replace('/', os.path.sep) - split_name = name.split(os.path.sep) - if split_name[-1] == '' or len(split_name) == 1: - # is directory or preview file - continue - full_name = os.path.join(directory, name) - check_directory_exists(os.path.dirname(full_name)) - if os.path.splitext(name)[1].lower() == '.xml': - file_xml = str(theme_zip.read(name), 'utf-8') - out_file = open(full_name, 'w') - out_file.write(file_xml) - else: - out_file = open(full_name, 'wb') - out_file.write(theme_zip.read(name)) - out_file.close() + out_file = open(full_name, 'wb') + out_file.write(theme_zip.read(name)) + out_file.close() except (IOError, zipfile.BadZipfile): log.exception('Importing theme from zip failed %s' % file_name) raise Exception('validation') @@ -548,7 +543,7 @@ class ThemeManager(QtGui.QWidget): if not abort_import: # As all files are closed, we can create the Theme. if file_xml: - theme = self._create_theme_fom_Xml(file_xml, self.path) + theme = self._create_theme_from_Xml(file_xml, self.path) self.generate_and_save_image(directory, theme_name, theme) # Only show the error message, when IOError was not raised (in # this case the error message has already been shown). @@ -558,38 +553,6 @@ class ThemeManager(QtGui.QWidget): translate('OpenLP.ThemeManager', 'File is not a valid theme.')) log.exception('Theme file does not contain XML data %s' % file_name) - def unzip_version_122(self, dir_name, zip_file, 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('Name').text.strip() - theme_name = self.bad_v1_name_chars.sub('', theme_name) - theme_folder = os.path.join(dir_name, theme_name) - theme_exists = os.path.exists(theme_folder) - if theme_exists and not self.over_write_message_box(theme_name): - return '', '', '', True - themedir = os.path.join(dir_name, theme_name) - check_directory_exists(themedir) - file_xml = str(zip_file.read(xml_file), 'utf-8') - file_xml = self._migrate_version_122(file_xml) - out_file = open(os.path.join(themedir, theme_name + '.xml'), 'w') - out_file.write(file_xml.encode('utf-8')) - out_file.close() - if background.text.strip() == '2': - image_name = xml_tree.find('BackgroundParameter1').text.strip() - # image file has same extension and is in subfolder - image_file = [name for name in zip_file.namelist() if os.path.splitext(name)[1].lower() - == os.path.splitext(image_name)[1].lower() and name.find(r'/')] - if len(image_file) >= 1: - out_file = open(os.path.join(themedir, image_name), 'wb') - out_file.write(zip_file.read(image_file[0])) - out_file.close() - else: - log.exception('Theme file does not contain image file "%s"' % image_name.decode('utf-8', 'replace')) - raise Exception('validation') - return theme_name, file_xml, out_file, False - def check_if_theme_exists(self, theme_name): """ Check if theme already exists and displays error message @@ -697,7 +660,7 @@ class ThemeManager(QtGui.QWidget): image = os.path.join(self.path, theme + '.png') return image - def _create_theme_fom_Xml(self, theme_xml, path): + def _create_theme_from_Xml(self, theme_xml, path): """ Return a theme object using information parsed from XML @@ -741,55 +704,6 @@ class ThemeManager(QtGui.QWidget): return True return False - def _migrate_version_122(self, xml_data): - """ - Convert the xml data from version 1 format to the current format. - - New fields are loaded with defaults to provide a complete, working - theme containing all compatible customisations from the old theme. - - ``xml_data`` - Version 1 theme to convert - """ - theme = Theme(xml_data) - new_theme = ThemeXML() - new_theme.theme_name = self.bad_v1_name_chars.sub('', theme.Name) - if theme.BackgroundType == BackgroundType.Solid: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Solid) - new_theme.background_color = str(theme.BackgroundParameter1.name()) - elif theme.BackgroundType == BackgroundType.Horizontal: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Gradient) - new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal) - if theme.BackgroundParameter3.name() == 1: - new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal) - new_theme.background_start_color = str(theme.BackgroundParameter1.name()) - new_theme.background_end_color = str(theme.BackgroundParameter2.name()) - elif theme.BackgroundType == BackgroundType.Image: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Image) - new_theme.background_filename = str(theme.BackgroundParameter1) - elif theme.BackgroundType == BackgroundType.Transparent: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Transparent) - new_theme.font_main_name = theme.FontName - new_theme.font_main_color = str(theme.FontColor.name()) - new_theme.font_main_size = theme.FontProportion * 3 - new_theme.font_footer_name = theme.FontName - new_theme.font_footer_color = str(theme.FontColor.name()) - new_theme.font_main_shadow = False - if theme.Shadow == 1: - new_theme.font_main_shadow = True - new_theme.font_main_shadow_color = str(theme.ShadowColor.name()) - if theme.Outline == 1: - new_theme.font_main_outline = True - new_theme.font_main_outline_color = str(theme.OutlineColor.name()) - vAlignCorrection = VerticalType.Top - if theme.VerticalAlign == 2: - vAlignCorrection = VerticalType.Middle - elif theme.VerticalAlign == 1: - vAlignCorrection = VerticalType.Bottom - new_theme.display_horizontal_align = theme.HorizontalAlign - new_theme.display_vertical_align = vAlignCorrection - return new_theme.extract_xml() - def _get_renderer(self): """ Adds the Renderer to the class dynamically diff --git a/openlp/core/theme/__init__.py b/openlp/core/ui/thememanagerhelper.py similarity index 91% rename from openlp/core/theme/__init__.py rename to openlp/core/ui/thememanagerhelper.py index 4db399fa7..8fa02bde5 100644 --- a/openlp/core/theme/__init__.py +++ b/openlp/core/ui/thememanagerhelper.py @@ -27,10 +27,12 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`~openlp.core.theme` module contains all the themeing functions used by -OpenLP when displaying a song or a scripture. +The Theme Controller helps manages adding, deleteing and modifying of themes. """ -from openlp.core.theme.theme import Theme -__all__ = ['Theme'] +class ThemeManagerHelper(object): + """ + Manages the non ui theme functions. + """ + pass \ No newline at end of file diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index c3c93f3af..be06f6ffc 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -33,8 +33,8 @@ The Themes configuration tab from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import Settings, ThemeLevel, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index f0674e924..d681a4428 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -31,7 +31,8 @@ The Create/Edit theme wizard """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 3a8669b1c..255695a65 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -32,9 +32,10 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP. import logging import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.lib import Registry, Settings, UiStrings, build_icon, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, build_icon from openlp.core.lib.ui import add_welcome_page log = logging.getLogger(__name__) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index d2e664e75..6ceb592f4 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -37,11 +37,14 @@ import os import re from subprocess import Popen, PIPE import sys -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse from PyQt4 import QtGui, QtCore -from openlp.core.lib import Registry, Settings +from openlp.core.common import AppLocation, Settings +from openlp.core.lib import Registry if sys.platform != 'win32' and sys.platform != 'darwin': @@ -51,7 +54,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': except ImportError: XDG_BASE_AVAILABLE = False -from openlp.core.lib import translate +from openlp.core.common import translate log = logging.getLogger(__name__) APPLICATION_VERSION = {} @@ -81,15 +84,6 @@ class VersionThread(QtCore.QThread): Registry().execute('openlp_version_check', '%s' % version) -def _get_frozen_path(frozen_option, non_frozen_option): - """ - Return a path based on the system status. - """ - if hasattr(sys, 'frozen') and sys.frozen == 1: - return frozen_option - return non_frozen_option - - def get_application_version(): """ Returns the application version of the running instance of OpenLP:: @@ -418,18 +412,17 @@ def get_natural_key(string): """ key = DIGITS_OR_NONDIGITS.findall(string) key = [int(part) if part.isdigit() else get_locale_key(part) for part in key] - # Python 3 does not support comparision of different types anymore. So make sure, that we do not compare str + # Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str # and int. if string[0].isdigit(): return [b''] + key return key -from .applocation import AppLocation from .languagemanager import LanguageManager from .actions import ActionList -__all__ = ['AppLocation', 'ActionList', 'LanguageManager', 'get_application_version', 'check_latest_version', +__all__ = ['ActionList', 'LanguageManager', 'get_application_version', 'check_latest_version', 'add_actions', 'get_filesystem_encoding', 'get_web_page', 'get_uno_command', 'get_uno_instance', 'delete_file', 'clean_filename', 'format_time', 'get_locale_key', 'get_natural_key'] diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 6feeda276..82619a6e7 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -34,7 +34,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Settings +from openlp.core.common import Settings log = logging.getLogger(__name__) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index ed58cc037..8208435d1 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -35,8 +35,7 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.utils import AppLocation -from openlp.core.lib import Settings, translate +from openlp.core.common import AppLocation, Settings, translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index b13230196..f41b3490c 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtGui -from openlp.core.lib import Plugin, Settings, StringContent, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action, UiStrings from openlp.core.lib.theme import VerticalType diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index 6f189237f..02f41c7f8 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 97a8a5efa..d0f78e4cb 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui, QtCore -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.alerts.lib.db import AlertItem from .alertdialog import Ui_AlertDialog diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 047f91674..88341b77a 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -35,7 +35,8 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry log = logging.getLogger(__name__) diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index f57caaf7c..5a1512c4b 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab from openlp.core.lib.ui import create_valign_selection_widgets diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 18aab2a0d..4121b474c 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -67,6 +67,9 @@ __default_settings__ = { class BiblePlugin(Plugin): + """ + The Bible plugin provides a plugin for managing and displaying Bibles. + """ log.info('Bible Plugin loaded') def __init__(self): @@ -74,13 +77,14 @@ class BiblePlugin(Plugin): self.weight = -9 self.icon_path = ':/plugins/plugin_bibles.png' self.icon = build_icon(self.icon_path) - self.manager = None + self.manager = BibleManager(self) def initialise(self): + """ + Initialise the Bible plugin. + """ log.info('bibles Initialising') - if self.manager is None: - self.manager = BibleManager(self) - Plugin.initialise(self) + super(BiblePlugin, self).initialise() self.import_bible_item.setVisible(True) action_list = ActionList.get_instance() action_list.add_action(self.import_bible_item, UiStrings().Import) @@ -107,7 +111,7 @@ class BiblePlugin(Plugin): """ Perform tasks on application startup """ - Plugin.app_startup(self) + super(BiblePlugin, self).app_startup() if self.manager.old_bible_databases: if QtGui.QMessageBox.information(self.main_window, translate('OpenLP', 'Information'), diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 459fcbe8b..f99d8138b 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -32,13 +32,13 @@ The bible import functions for OpenLP import logging import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.lib import Settings, UiStrings, translate +from openlp.core.common import AppLocation, Settings, UiStrings, translate from openlp.core.lib.db import delete_database from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation, get_locale_key +from openlp.core.utils import get_locale_key from openlp.plugins.bibles.lib.manager import BibleFormat from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 299387e67..cc164363b 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -36,10 +36,11 @@ from tempfile import gettempdir from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, UiStrings, translate, check_directory_exists +from openlp.core.common import AppLocation, UiStrings, Settings, check_directory_exists, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation, delete_file +from openlp.core.utils import delete_file from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 90a5bae40..605a4fb95 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box class Ui_BookNameDialog(object): diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index 04a0e2fe7..1fb359c86 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -36,7 +36,7 @@ import re from PyQt4.QtGui import QDialog from PyQt4 import QtCore -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.booknamedialog import Ui_BookNameDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index 6e608d8df..6c832f5ee 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings from openlp.plugins.bibles.lib.db import BiblesResourcesDB diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index e0163a8b8..5e2c94f79 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -33,7 +33,8 @@ import re from PyQt4 import QtGui -from openlp.core.lib import Registry, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from .editbibledialog import Ui_EditBibleDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index 533848187..c69eb8828 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box class Ui_LanguageDialog(object): diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index 88d9906cd..dcfb14462 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -34,7 +34,7 @@ import logging from PyQt4.QtGui import QDialog -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.languagedialog import \ Ui_LanguageDialog diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index df816d436..38575a974 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -33,7 +33,8 @@ plugin. import logging import re -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index dd95d9b33..807cb2195 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, update_reference_separators, \ get_reference_separator, LanguageSelection diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index 69ba12062..40ae13359 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -60,7 +60,7 @@ import logging import chardet import csv -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 8eaabd5ed..8aefcfa8b 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -38,10 +38,11 @@ from sqlalchemy import Column, ForeignKey, Table, or_, types, func from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.lib import Registry, translate +from openlp.core.common import AppLocation, translate +from openlp.core.lib import Registry from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box -from openlp.core.utils import AppLocation, clean_filename +from openlp.core.utils import clean_filename from . import upgrade log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 0fee09265..22ff6185a 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -38,7 +38,8 @@ from html.parser import HTMLParseError from bs4 import BeautifulSoup, NavigableString, Tag -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import get_web_page from openlp.plugins.bibles.lib import SearchResults diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 3ee4862ec..f2feb238f 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -30,8 +30,9 @@ import logging import os -from openlp.core.lib import Registry, Settings, translate -from openlp.core.utils import AppLocation, delete_file +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import Registry +from openlp.core.utils import delete_file from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from .csvbible import CSVBible diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 94f015fdb..d0414d49c 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -31,8 +31,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, Settings, UiStrings, \ - create_separated_list, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, create_separated_list from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \ critical_error_message_box, find_and_set_in_combo_box, build_icon @@ -64,6 +64,11 @@ class BibleMediaItem(MediaManagerItem): self.lock_icon = build_icon(':/bibles/bibles_search_lock.png') self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ # Place to store the search results for both bibles. self.settings = self.plugin.settings_tab self.quick_preview_allowed = True diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index d2b156345..efe0044ef 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -30,7 +30,7 @@ import logging from lxml import etree, objectify -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 6184bff05..a0aabb5a2 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -33,8 +33,7 @@ import chardet import codecs import re -from openlp.core.lib import translate -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation, translate from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/versereferencelist.py b/openlp/plugins/bibles/lib/versereferencelist.py index 1f2d761e0..e8daada46 100644 --- a/openlp/plugins/bibles/lib/versereferencelist.py +++ b/openlp/plugins/bibles/lib/versereferencelist.py @@ -27,6 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### + class VerseReferenceList(object): """ The VerseReferenceList class encapsulates a list of verse references, but maintains the order in which they were diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 20adefa9a..106b3e339 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 80b3c8cc9..bf000d308 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import SpellTextEdit, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import SpellTextEdit from openlp.core.lib.ui import create_button, create_button_box class Ui_CustomSlideEditDialog(object): diff --git a/openlp/plugins/custom/forms/editcustomslideform.py b/openlp/plugins/custom/forms/editcustomslideform.py index 65a8deb68..181f6c6af 100644 --- a/openlp/plugins/custom/forms/editcustomslideform.py +++ b/openlp/plugins/custom/forms/editcustomslideform.py @@ -30,7 +30,7 @@ import logging -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from .editcustomslidedialog import Ui_CustomSlideEditDialog diff --git a/openlp/plugins/custom/lib/customtab.py b/openlp/plugins/custom/lib/customtab.py index f62c4547d..c66711f49 100644 --- a/openlp/plugins/custom/lib/customtab.py +++ b/openlp/plugins/custom/lib/customtab.py @@ -33,7 +33,8 @@ for the Custom Slides plugin, which is inserted into the configuration dialog. from PyQt4 import QtCore, QtGui -from openlp.core.lib import SettingsTab, Settings, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import SettingsTab class CustomTab(SettingsTab): diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index a01ba427f..386045b40 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -32,8 +32,9 @@ import logging from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_, func, and_ -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, Settings, PluginStatus,\ - UiStrings, check_item_selected, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus,\ + check_item_selected from openlp.plugins.custom.forms.editcustomform import EditCustomForm from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder from openlp.plugins.custom.lib.db import CustomSlide @@ -58,6 +59,11 @@ class CustomMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'custom/custom' super(CustomMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.edit_custom_form = EditCustomForm(self, self.main_window, self.plugin.manager) self.single_service_item = False self.quick_preview_allowed = True @@ -65,7 +71,7 @@ class CustomMediaItem(MediaManagerItem): # Holds information about whether the edit is remotely triggered and # which Custom is required. self.remote_custom = -1 - self.manager = plugin.manager + self.manager = self.plugin.manager def add_end_header_bar(self): self.toolbar.addSeparator() diff --git a/openlp/plugins/images/forms/addgroupdialog.py b/openlp/plugins/images/forms/addgroupdialog.py index e76332287..95e2118f4 100644 --- a/openlp/plugins/images/forms/addgroupdialog.py +++ b/openlp/plugins/images/forms/addgroupdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 29bad676d..fafac53aa 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.images.forms.addgroupdialog import Ui_AddGroupDialog diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py index ec87df2bb..76b0d7849 100644 --- a/openlp/plugins/images/forms/choosegroupdialog.py +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index a6712d941..137610c1f 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -31,10 +31,11 @@ from PyQt4 import QtGui import logging -from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, Settings, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, build_icon from openlp.core.lib.db import Manager from openlp.plugins.images.lib import ImageMediaItem, ImageTab -from openlp.plugins.images.lib.db import init_schema, ImageFilenames +from openlp.plugins.images.lib.db import init_schema log = logging.getLogger(__name__) diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index b408c1361..20e810b7e 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab class ImageTab(SettingsTab): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9e396c3cf..f1e0bfdb4 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,11 +32,11 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \ - StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \ - create_thumb, translate, validate_thumb +from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists, translate +from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, \ + StringContent, TreeWidgetWithDnD, build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box -from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_images_filter +from openlp.core.utils import delete_file, get_locale_key, get_images_filter from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups @@ -52,10 +52,18 @@ class ImageMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'images/image' + self.manager = None + self.choose_group_form = None + self.add_group_form = None super(ImageMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.quick_preview_allowed = True self.has_search = True - self.manager = plugin.manager + self.manager = self.plugin.manager self.choose_group_form = ChooseGroupForm(self) self.add_group_form = AddGroupForm(self) self.fill_groups_combobox(self.choose_group_form.group_combobox) @@ -91,8 +99,8 @@ class ImageMediaItem(MediaManagerItem): self.list_view.setIconSize(QtCore.QSize(88, 50)) self.list_view.setIndentation(self.list_view.default_indentation) self.list_view.allow_internal_dnd = True - self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') - check_directory_exists(self.servicePath) + self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') + check_directory_exists(self.service_path) # Load images from the database self.load_full_list( self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) @@ -193,7 +201,7 @@ class ImageMediaItem(MediaManagerItem): """ images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id) for image in images: - delete_file(os.path.join(self.servicePath, os.path.split(image.filename)[1])) + delete_file(os.path.join(self.service_path, os.path.split(image.filename)[1])) self.manager.delete_object(ImageFilenames, image.id) image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == image_group.id) for group in image_groups: @@ -215,7 +223,7 @@ class ImageMediaItem(MediaManagerItem): if row_item: item_data = row_item.data(0, QtCore.Qt.UserRole) if isinstance(item_data, ImageFilenames): - delete_file(os.path.join(self.servicePath, row_item.text(0))) + delete_file(os.path.join(self.service_path, row_item.text(0))) if item_data.group_id == 0: self.list_view.takeTopLevelItem(self.list_view.indexOfTopLevelItem(row_item)) else: @@ -339,7 +347,7 @@ class ImageMediaItem(MediaManagerItem): for imageFile in images: log.debug('Loading image: %s', imageFile.filename) filename = os.path.split(imageFile.filename)[1] - thumb = os.path.join(self.servicePath, filename) + thumb = os.path.join(self.service_path, filename) if not os.path.exists(imageFile.filename): icon = build_icon(':/general/general_delete.png') else: @@ -672,7 +680,16 @@ class ImageMediaItem(MediaManagerItem): translate('ImagePlugin.MediaItem', 'There was a problem replacing your background, ' 'the image file "%s" no longer exists.') % filename) - def search(self, string, showError): + def search(self, string, show_error=True): + """ + Perform a search on the image file names. + + ``string`` + The glob to search for + + ``show_error`` + Unused. + """ files = self.manager.get_all_objects(ImageFilenames, filter_clause=ImageFilenames.filename.contains(string), order_by_ref=ImageFilenames.filename) results = [] diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index a5b0397c5..3d2d5b26e 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,12 +32,13 @@ import os from PyQt4 import QtCore, QtGui +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \ - Settings, UiStrings, build_icon, check_item_selected, check_directory_exists, translate + build_icon, check_item_selected from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players -from openlp.core.utils import AppLocation, get_locale_key +from openlp.core.utils import get_locale_key log = logging.getLogger(__name__) @@ -61,10 +62,15 @@ class MediaMediaItem(MediaManagerItem): self.background = False self.automatic = '' super(MediaMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.single_service_item = False self.has_search = True self.media_object = None - self.display_controller = DisplayController(parent) + self.display_controller = DisplayController(self.parent()) self.display_controller.controller_layout = QtGui.QVBoxLayout() self.media_controller.register_controller(self.display_controller) self.media_controller.set_controls_visible(self.display_controller, False) diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index 7798b2cf3..40b6a1ea8 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import Settings, SettingsTab, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab class MediaQ_check_box(QtGui.QCheckBox): diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 896ff95d1..d6a943832 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import Plugin, Registry, StringContent, build_icon from openlp.plugins.media.lib import MediaMediaItem, MediaTab diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 9add8c8a4..a89877ac6 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -32,8 +32,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext, Settings, UiStrings, \ - build_icon, check_item_selected, create_thumb, translate, validate_thumb +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext,\ + build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.utils import get_locale_key from openlp.plugins.presentations.lib import MessageListener @@ -52,14 +53,26 @@ class PresentationMediaItem(MediaManagerItem): """ log.info('Presentations Media Item loaded') - def __init__(self, parent, plugin, icon, controllers): + def __init__(self, parent, plugin, controllers): """ Constructor. Setup defaults """ - self.controllers = controllers self.icon_path = 'presentations/presentation' - self.Automatic = '' + self.controllers = controllers super(PresentationMediaItem, self).__init__(parent, plugin) + + def retranslateUi(self): + """ + The name of the plugin media displayed in UI + """ + self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') + self.automatic = translate('PresentationPlugin.MediaItem', 'Automatic') + self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:')) + + def setup_item(self): + """ + Do some additional setup. + """ self.message_listener = MessageListener(self) self.has_search = True self.single_service_item = False @@ -68,14 +81,6 @@ class PresentationMediaItem(MediaManagerItem): # Allow DnD from the desktop self.list_view.activateDnD() - def retranslateUi(self): - """ - The name of the plugin media displayed in UI - """ - self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') - self.Automatic = translate('PresentationPlugin.MediaItem', 'Automatic') - self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:')) - def build_file_mask_string(self): """ Build the list of file extensions to be used in the Open file dialog. @@ -137,7 +142,7 @@ class PresentationMediaItem(MediaManagerItem): if self.controllers[item].enabled(): self.display_type_combo_box.addItem(item) if self.display_type_combo_box.count() > 1: - self.display_type_combo_box.insertItem(0, self.Automatic) + self.display_type_combo_box.insertItem(0, self.automatic) self.display_type_combo_box.setCurrentIndex(0) if Settings().value(self.settings_section + '/override app') == QtCore.Qt.Checked: self.presentation_widget.show() diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index bd9311673..fba9c8b4d 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -326,7 +326,7 @@ class MessageListener(object): else: self.media_item.generate_slide_data(self, item, None, False, False, ServiceItemContext.Preview) - if self.handler == self.media_item.Automatic: + if self.handler == self.media_item.automatic: self.handler = self.media_item.findControllerByType(file) if not self.handler: return diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index c6fa7dc49..6b2dccfa1 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -32,10 +32,10 @@ import logging from tempfile import NamedTemporaryFile import re from subprocess import check_output, CalledProcessError, STDOUT -#from PyQt4 import QtCore, QtGui from openlp.core.utils import AppLocation -from openlp.core.lib import ScreenList, Settings +from openlp.core.common import Settings +from openlp.core.lib import ScreenList from .presentationcontroller import PresentationController, PresentationDocument diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 0a70b174a..17911606c 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -33,8 +33,8 @@ import shutil from PyQt4 import QtCore -from openlp.core.lib import Registry, Settings, check_directory_exists, create_thumb, validate_thumb -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation, Settings, check_directory_exists +from openlp.core.lib import Registry, create_thumb, validate_thumb log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 1d6d9a16e..cc0cf81e9 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -29,11 +29,10 @@ from PyQt4 import QtGui -from openlp.core.lib import Settings, SettingsTab, UiStrings, translate, build_icon +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab, build_icon from openlp.core.lib.ui import critical_error_message_box from .pdfcontroller import PdfController -#from openlp.plugins.presentations.lib.pdfcontroller import PdfController -#from pdfcontroller import check_binary class PresentationTab(SettingsTab): """ diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 91f4d6096..e2293ccd0 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -35,8 +35,8 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab @@ -112,8 +112,7 @@ class PresentationPlugin(Plugin): """ Create the Media Manager List. """ - self.media_item = PresentationMediaItem( - self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers) + self.media_item = PresentationMediaItem(self.main_window.media_dock_manager.media_dock, self, self.controllers) def register_controllers(self, controller): """ diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index f12fbb290..ce13ed812 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -121,12 +121,11 @@ import urllib.request import urllib.error from urllib.parse import urlparse, parse_qs - from mako.template import Template from PyQt4 import QtCore -from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte -from openlp.core.utils import AppLocation, translate +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import Registry, PluginStatus, StringContent, image_to_byte log = logging.getLogger(__name__) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 7776812fa..0ac77115c 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -37,12 +37,10 @@ import ssl import socket import os import logging -from urllib.parse import urlparse, parse_qs from PyQt4 import QtCore -from openlp.core.lib import Settings -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation, Settings from openlp.plugins.remotes.lib import HttpRouter diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 17d368bd2..3953d777f 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -31,9 +31,8 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork -from openlp.core.lib import Settings, SettingsTab, translate -from openlp.core.utils import AppLocation - +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import SettingsTab ZERO_URL = '0.0.0.0' diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index e058089aa..f4121c85a 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -37,7 +37,6 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Registry, translate from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation from openlp.plugins.songs.lib import delete_song from openlp.plugins.songs.lib.db import Song, MediaFile from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget @@ -45,6 +44,7 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) + class DuplicateSongRemovalForm(OpenLPWizard): """ This is the Duplicate Song Removal Wizard. It provides functionality to diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 16e680587..e867aca37 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 747988583..ed4676929 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -38,10 +38,9 @@ import shutil from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list, \ - check_directory_exists +from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate +from openlp.core.lib import Registry, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box -from openlp.core.utils import AppLocation from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings @@ -692,7 +691,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.verse_edit_button.setEnabled(False) self.verse_delete_button.setEnabled(False) - def on_verse_order_text_changed(self, text): """ Checks if the verse order is complete or missing. Shows a error message according to the state of the verse diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 26c07395c..f0b9262a7 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -34,7 +34,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, UiStrings, create_separated_list, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry, create_separated_list, build_icon from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.db import Song diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index f1f63ffb5..2105e5e35 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -35,7 +35,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.common import Settings +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index ea908fb0f..3e0363772 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon +from openlp.core.common import UiStrings +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 67d9ee3ad..142cca1e7 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -32,7 +32,8 @@ import os from PyQt4 import QtGui, QtCore from sqlalchemy.sql import and_ -from openlp.core.lib import Registry, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.authorsform import AuthorsForm from openlp.plugins.songs.forms.topicsform import TopicsForm diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 12874aa89..d2c25bd15 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -36,8 +36,9 @@ import re from PyQt4 import QtGui +from openlp.core.common import AppLocation from openlp.core.lib import translate -from openlp.core.utils import AppLocation, CONTROL_CHARS +from openlp.core.utils import CONTROL_CHARS from openlp.plugins.songs.lib.db import MediaFile, Song from .db import Author from .ui import SongStrings diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 8e7a9f36e..acdfddae7 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -32,7 +32,7 @@ The :mod:`importer` modules provides the general song import functionality. import os import logging -from openlp.core.lib import translate, UiStrings +from openlp.core.common import translate, UiStrings from openlp.core.ui.wizard import WizardStrings from .opensongimport import OpenSongImport from .easyslidesimport import EasySlidesImport diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 68cd706a3..b8b1b5dcd 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,10 +35,10 @@ import shutil from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, Settings, \ - UiStrings, translate, check_item_selected, create_separated_list, check_directory_exists +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \ + check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action -from openlp.core.utils import AppLocation from openlp.plugins.songs.forms.editsongform import EditSongForm from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from openlp.plugins.songs.forms.songimportform import SongImportForm @@ -72,6 +72,11 @@ class SongMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'songs/song' super(SongMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.single_service_item = False # Holds information about whether the edit is remotely triggered and which Song is required. self.remote_song = -1 diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index e95f78232..19078c9ec 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -36,7 +36,7 @@ from sqlalchemy import create_engine, MetaData, Table from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, sessionmaker from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.db import BaseModel from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song diff --git a/openlp/plugins/songs/lib/oooimport.py b/openlp/plugins/songs/lib/oooimport.py index f74c022a7..bb7654d01 100644 --- a/openlp/plugins/songs/lib/oooimport.py +++ b/openlp/plugins/songs/lib/oooimport.py @@ -51,6 +51,7 @@ except ImportError: PAGE_AFTER = 5 PAGE_BOTH = 6 + class OooImport(SongImport): """ Import songs from Impress/Powerpoint docs using Impress diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index a1d17d595..8f927dd8c 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -35,7 +35,8 @@ import os from lxml import etree -from openlp.core.lib import Registry, check_directory_exists, translate +from openlp.core.common import check_directory_exists, translate +from openlp.core.lib import Registry from openlp.core.utils import clean_filename from openlp.plugins.songs.lib.xml import OpenLyrics diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index 669f861e3..3e60c9994 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -33,13 +33,14 @@ import re from lxml import objectify from lxml.etree import Error, LxmlError -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) + class OpenSongImport(SongImport): """ Import songs exported from OpenSong diff --git a/openlp/plugins/songs/lib/powersongimport.py b/openlp/plugins/songs/lib/powersongimport.py index 7ab802505..88cea0b72 100644 --- a/openlp/plugins/songs/lib/powersongimport.py +++ b/openlp/plugins/songs/lib/powersongimport.py @@ -34,7 +34,7 @@ import logging import fnmatch import os -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index f00e98e63..79e6f6263 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -102,10 +102,10 @@ class SongBeamerImport(SongImport): """ Receive a single file or a list of files to import. """ - self.import_wizard.progress_bar.setMaximum(len(self.import_source)) if not isinstance(self.import_source, list): return - for file in self.import_source: + self.import_wizard.progress_bar.setMaximum(len(self.import_source)) + for import_file in self.import_source: # TODO: check that it is a valid SongBeamer file if self.stop_import_flag: return @@ -113,12 +113,13 @@ class SongBeamerImport(SongImport): self.currentVerse = '' self.currentVerseType = VerseType.tags[VerseType.Verse] read_verses = False - file_name = os.path.split(file)[1] - if os.path.isfile(file): - detect_file = open(file, 'r') + file_name = os.path.split(import_file)[1] + if os.path.isfile(import_file): + # First open in binary mode to detect the encoding + detect_file = open(import_file, 'rb') details = chardet.detect(detect_file.read()) detect_file.close() - infile = codecs.open(file, 'r', details['encoding']) + infile = codecs.open(import_file, 'r', details['encoding']) song_data = infile.readlines() infile.close() else: @@ -149,7 +150,7 @@ class SongBeamerImport(SongImport): self.replaceHtmlTags() self.addVerse(self.currentVerse, self.currentVerseType) if not self.finish(): - self.logError(file) + self.logError(import_file) def replaceHtmlTags(self): """ diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 2381959ad..ead897e0e 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -34,9 +34,9 @@ import os from PyQt4 import QtCore -from openlp.core.lib import Registry, translate, check_directory_exists +from openlp.core.common import AppLocation, check_directory_exists, translate +from openlp.core.lib import Registry from openlp.core.ui.wizard import WizardStrings -from openlp.core.utils import AppLocation from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/lib/songproimport.py b/openlp/plugins/songs/lib/songproimport.py index 5673eaa34..a7384afd3 100644 --- a/openlp/plugins/songs/lib/songproimport.py +++ b/openlp/plugins/songs/lib/songproimport.py @@ -35,6 +35,7 @@ import re from openlp.plugins.songs.lib import strip_rtf from openlp.plugins.songs.lib.songimport import SongImport + class SongProImport(SongImport): """ The :class:`SongProImport` class provides the ability to import song files diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index dad5ecfc0..a82ae0c98 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -27,16 +27,17 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songshowplusimport` module provides the functionality for importing -SongShow Plus songs into the OpenLP database. +The :mod:`songshowplusimport` module provides the functionality for importing SongShow Plus songs into the OpenLP +database. """ +import chardet import os import logging import re import struct from openlp.core.ui.wizard import WizardStrings -from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding from openlp.plugins.songs.lib.songimport import SongImport TITLE = 1 @@ -55,6 +56,7 @@ CUSTOM_VERSE = 37 log = logging.getLogger(__name__) + class SongShowPlusImport(SongImport): """ The :class:`SongShowPlusImport` class provides the ability to import song files from SongShow Plus. @@ -132,41 +134,43 @@ class SongShowPlusImport(SongImport): else: length_descriptor, = struct.unpack("B", song_data.read(1)) log.debug(length_descriptor_size) - data = song_data.read(length_descriptor).decode() + data = song_data.read(length_descriptor) if block_key == TITLE: - self.title = data + self.title = self.decode(data) elif block_key == AUTHOR: - authors = data.split(" / ") + authors = self.decode(data).split(" / ") for author in authors: if author.find(",") !=-1: authorParts = author.split(", ") author = authorParts[1] + " " + authorParts[0] self.parse_author(author) elif block_key == COPYRIGHT: - self.addCopyright(data) + self.addCopyright(self.decode(data)) elif block_key == CCLI_NO: self.ccliNumber = int(data) elif block_key == VERSE: - self.addVerse(data, "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: - self.addVerse(data, "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) elif block_key == BRIDGE: - self.addVerse(data, "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) elif block_key == TOPIC: - self.topics.append(data) + self.topics.append(self.decode(data)) elif block_key == COMMENTS: - self.comments = data + self.comments = self.decode(data) elif block_key == VERSE_ORDER: - verse_tag = self.to_openlp_verse_tag(data, True) + verse_tag = self.to_openlp_verse_tag(self.decode(data), True) if verse_tag: + if not isinstance(verse_tag, str): + verse_tag = self.decode(verse_tag) self.ssp_verse_order_list.append(verse_tag) elif block_key == SONG_BOOK: - self.songBookName = data + self.songBookName = self.decode(data) elif block_key == SONG_NUMBER: self.songNumber = ord(data) elif block_key == CUSTOM_VERSE: verse_tag = self.to_openlp_verse_tag(verse_name) - self.addVerse(data, verse_tag) + self.addVerse(self.decode(data), verse_tag) else: log.debug("Unrecognised blockKey: %s, data: %s" % (block_key, data)) song_data.seek(next_block_starts) @@ -204,3 +208,9 @@ class SongShowPlusImport(SongImport): verse_tag = VerseType.tags[VerseType.Other] verse_number = self.other_list[verse_name] return verse_tag + verse_number + + def decode(self, data): + try: + return str(data, chardet.detect(data)['encoding']) + except: + return str(data, retrieve_windows_encoding()) \ No newline at end of file diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index f2a36e0c8..c422f1231 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import Settings, SettingsTab, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import SettingsTab class SongsTab(SettingsTab): diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index de864b470..ce876fe81 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -32,6 +32,7 @@ for the songs plugin. """ from openlp.core.lib import translate + class SongStrings(object): """ Provide standard strings for use throughout the songs plugin. diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/worshipcenterproimport.py index b1de90634..a58ace982 100644 --- a/openlp/plugins/songs/lib/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/worshipcenterproimport.py @@ -34,7 +34,7 @@ import logging import pyodbc -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/wowimport.py b/openlp/plugins/songs/lib/wowimport.py index 3f06b4df8..8e9023e2f 100644 --- a/openlp/plugins/songs/lib/wowimport.py +++ b/openlp/plugins/songs/lib/wowimport.py @@ -33,7 +33,7 @@ Worship songs into the OpenLP database. import os import logging -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport BLOCK_TYPES = ('V', 'C', 'B') diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index e788948b6..de1d33a22 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -68,7 +68,8 @@ import re from lxml import etree, objectify -from openlp.core.lib import FormattingTags, translate +from openlp.core.common import translate +from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.core.utils import get_application_version diff --git a/openlp/plugins/songs/lib/zionworximport.py b/openlp/plugins/songs/lib/zionworximport.py index 50839f832..315f99708 100644 --- a/openlp/plugins/songs/lib/zionworximport.py +++ b/openlp/plugins/songs/lib/zionworximport.py @@ -33,7 +33,7 @@ ZionWorx songs into the OpenLP database. import csv import logging -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 3418dd7e4..6e6f7ea77 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -38,7 +38,8 @@ import sqlite3 from PyQt4 import QtCore, QtGui -from openlp.core.lib import Plugin, StringContent, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index a47afb9f4..4c7b57285 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index 4ae9756b3..babd9d178 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -27,9 +27,10 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedeletedialog import Ui_SongUsageDeleteDialog diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index efe84a7c8..278feebf8 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 725668bd3..6ff73b068 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -33,7 +33,8 @@ import os from PyQt4 import QtGui from sqlalchemy.sql import and_ -from openlp.core.lib import Registry, Settings, translate, check_directory_exists +from openlp.core.common import Settings, check_directory_exists, translate +from openlp.core.lib import Registry from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedetaildialog import Ui_SongUsageDetailDialog diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index bed0e5be3..077b81155 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -32,7 +32,8 @@ from datetime import datetime from PyQt4 import QtCore, QtGui -from openlp.core.lib import Plugin, Registry, Settings, StringContent, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, Registry, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index 607525bf2..0758b30e3 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -44,7 +44,7 @@ from distutils.version import LooseVersion try: import nose except ImportError: - pass + nose = None IS_WIN = sys.platform.startswith('win') @@ -90,21 +90,34 @@ MODULES = [ OPTIONAL_MODULES = [ - ('MySQLdb', ' (MySQL support)'), - ('psycopg2', ' (PostgreSQL support)'), - ('nose', ' (testing framework)'), - ('mock', ' (testing module)'), + ('MySQLdb', '(MySQL support)', True), + ('psycopg2', '(PostgreSQL support)', True), + ('nose', '(testing framework)', True), + ('mock', '(testing module)', sys.version_info[1] < 3), ] w = sys.stdout.write def check_vers(version, required, text): + """ + Check the version of a dependency. Returns ``True`` if the version is greater than or equal, or False if less than. + + ``version`` + The actual version of the dependency + + ``required`` + The required version of the dependency + + ``text`` + The dependency's name + """ + space = (27 - len(required) - len(text)) * ' ' if not isinstance(version, str): version = '.'.join(map(str, version)) if not isinstance(required, str): required = '.'.join(map(str, required)) - w(' %s >= %s ... ' % (text, required)) + w(' %s >= %s ... ' % (text, required) + space) if LooseVersion(version) >= LooseVersion(required): w(version + os.linesep) return True @@ -113,6 +126,29 @@ def check_vers(version, required, text): return False +def check_module(mod, text='', indent=' '): + """ + Check that a module is installed. + + ``mod`` + The module to check for. + + ``text`` + The text to display. + + ``indent`` + How much to indent the text by. + """ + space = (31 - len(mod) - len(text)) * ' ' + w(indent + '%s %s... ' % (mod, text) + space) + try: + __import__(mod) + w('OK') + except ImportError: + w('FAIL') + w(os.linesep) + + def print_vers_fail(required, text): print(' %s >= %s ... FAIL' % (text, required)) @@ -143,18 +179,10 @@ def verify_versions(): print_vers_fail(VERS['enchant'], 'enchant') -def check_module(mod, text='', indent=' '): - space = (30 - len(mod) - len(text)) * ' ' - w(indent + '%s%s... ' % (mod, text) + space) - try: - __import__(mod) - w('OK') - except ImportError: - w('FAIL') - w(os.linesep) - - -def verify_pyenchant(): +def print_enchant_backends_and_languages(): + """ + Check if PyEnchant is installed. + """ w('Enchant (spell checker)... ') try: import enchant @@ -167,14 +195,15 @@ def verify_pyenchant(): w('FAIL' + os.linesep) -def verify_pyqt(): +def print_qt_image_formats(): + """ + Print out the image formats that Qt4 supports. + """ w('Qt4 image formats... ') try: from PyQt4 import QtGui - read_f = ', '.join([str(format).lower() - for format in QtGui.QImageReader.supportedImageFormats()]) - write_f = ', '.join([str(format).lower() - for format in QtGui.QImageWriter.supportedImageFormats()]) + read_f = ', '.join([bytes(fmt).decode().lower() for fmt in QtGui.QImageReader.supportedImageFormats()]) + write_f = ', '.join([bytes(fmt).decode().lower() for fmt in QtGui.QImageWriter.supportedImageFormats()]) w(os.linesep) print(' read: %s' % read_f) print(' write: %s' % write_f) @@ -183,20 +212,25 @@ def verify_pyqt(): def main(): + """ + Run the dependency checker. + """ + print('Checking Python version...') verify_python() print('Checking for modules...') for m in MODULES: check_module(m) print('Checking for optional modules...') for m in OPTIONAL_MODULES: - check_module(m[0], text=m[1]) + if m[2]: + check_module(m[0], text=m[1]) if IS_WIN: print('Checking for Windows specific modules...') for m in WIN32_MODULES: check_module(m) verify_versions() - verify_pyqt() - verify_pyenchant() + print_qt_image_formats() + print_enchant_backends_and_languages() if __name__ == '__main__': main() diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py index 24d8d5b28..3f471cf61 100644 --- a/tests/functional/__init__.py +++ b/tests/functional/__init__.py @@ -7,7 +7,13 @@ sip.setapi('QTime', 2) sip.setapi('QUrl', 2) sip.setapi('QVariant', 2) +import sys from PyQt4 import QtGui +if sys.version_info[1] >= 3: + from unittest.mock import patch, MagicMock +else: + from mock import patch, MagicMock + # Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a QApplication. application = QtGui.QApplication([]) diff --git a/tests/functional/openlp_core_common/__init__.py b/tests/functional/openlp_core_common/__init__.py new file mode 100644 index 000000000..f87606f07 --- /dev/null +++ b/tests/functional/openlp_core_common/__init__.py @@ -0,0 +1 @@ +__author__ = 'tim' diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py new file mode 100644 index 000000000..0dcb2e6b1 --- /dev/null +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Functional tests to test the AppLocation class and related methods. +""" +import copy +from unittest import TestCase + +from openlp.core.common import AppLocation, get_frozen_path +from tests.functional import patch + +FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] + + +class TestAppLocation(TestCase): + """ + A test suite to test out various methods around the AppLocation class. + """ + def get_data_path_test(self): + """ + Test the AppLocation.get_data_path() method + """ + with patch('openlp.core.common.applocation.Settings') as mocked_class, \ + patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + mocked_settings = mocked_class.return_value + mocked_settings.contains.return_value = False + mocked_get_directory.return_value = 'test/dir' + mocked_check_directory_exists.return_value = True + mocked_os.path.normpath.return_value = 'test/dir' + + # WHEN: we call AppLocation.get_data_path() + data_path = AppLocation.get_data_path() + print(data_path) + + # THEN: check that all the correct methods were called, and the result is correct + mocked_settings.contains.assert_called_with('advanced/data path') + mocked_get_directory.assert_called_with(AppLocation.DataDir) + mocked_check_directory_exists.assert_called_with('test/dir') + self.assertEqual('test/dir', data_path, 'Result should be "test/dir"') + + def get_data_path_with_custom_location_test(self): + """ + Test the AppLocation.get_data_path() method when a custom location is set in the settings + """ + with patch('openlp.core.common.applocation.Settings') as mocked_class,\ + patch('openlp.core.common.applocation.os') as mocked_os: + # GIVEN: A mocked out Settings class which returns a custom data location + mocked_settings = mocked_class.return_value + mocked_settings.contains.return_value = True + mocked_settings.value.return_value.toString.return_value = 'custom/dir' + mocked_os.path.normpath.return_value = 'custom/dir' + + # WHEN: we call AppLocation.get_data_path() + data_path = AppLocation.get_data_path() + + # THEN: the mocked Settings methods were called and the value returned was our set up value + mocked_settings.contains.assert_called_with('advanced/data path') + mocked_settings.value.assert_called_with('advanced/data path') + self.assertEqual('custom/dir', data_path, 'Result should be "custom/dir"') + + def get_files_no_section_no_extension_test(self): + """ + Test the AppLocation.get_files() method with no parameters passed. + """ + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.os.listdir') as mocked_listdir: + # GIVEN: Our mocked modules/methods. + mocked_get_data_path.return_value = 'test/dir' + mocked_listdir.return_value = copy.deepcopy(FILE_LIST) + + # When: Get the list of files. + result = AppLocation.get_files() + + # Then: check if the file lists are identical. + self.assertListEqual(FILE_LIST, result, 'The file lists should be identical.') + + def get_files_test(self): + """ + Test the AppLocation.get_files() method with all parameters passed. + """ + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.os.listdir') as mocked_listdir: + # GIVEN: Our mocked modules/methods. + mocked_get_data_path.return_value = 'test/dir' + mocked_listdir.return_value = copy.deepcopy(FILE_LIST) + + # When: Get the list of files. + result = AppLocation.get_files('section', '.mp3') + + # Then: Check if the section parameter was used correctly. + mocked_listdir.assert_called_with('test/dir/section') + + # Then: check if the file lists are identical. + self.assertListEqual(['file5.mp3', 'file6.mp3'], result, 'The file lists should be identical.') + + def get_section_data_path_test(self): + """ + Test the AppLocation.get_section_data_path() method + """ + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: + # GIVEN: A mocked out AppLocation.get_data_path() + mocked_get_data_path.return_value = 'test/dir' + mocked_check_directory_exists.return_value = True + + # WHEN: we call AppLocation.get_data_path() + data_path = AppLocation.get_section_data_path('section') + + # THEN: check that all the correct methods were called, and the result is correct + mocked_check_directory_exists.assert_called_with('test/dir/section') + self.assertEqual('test/dir/section', data_path, 'Result should be "test/dir/section"') + + def get_directory_for_app_dir_test(self): + """ + Test the AppLocation.get_directory() method for AppLocation.AppDir + """ + # GIVEN: A mocked out _get_frozen_path function + with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path: + mocked_get_frozen_path.return_value = 'app/dir' + + # WHEN: We call AppLocation.get_directory + directory = AppLocation.get_directory(AppLocation.AppDir) + + # THEN: check that the correct directory is returned + self.assertEqual('app/dir', directory, 'Directory should be "app/dir"') + + def get_directory_for_plugins_dir_test(self): + """ + Test the AppLocation.get_directory() method for AppLocation.PluginsDir + """ + # GIVEN: _get_frozen_path, abspath, split and sys are mocked out + with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path, \ + patch('openlp.core.common.applocation.os.path.abspath') as mocked_abspath, \ + patch('openlp.core.common.applocation.os.path.split') as mocked_split, \ + patch('openlp.core.common.applocation.sys') as mocked_sys: + mocked_abspath.return_value = 'plugins/dir' + mocked_split.return_value = ['openlp'] + mocked_get_frozen_path.return_value = 'plugins/dir' + mocked_sys.frozen = 1 + mocked_sys.argv = ['openlp'] + + # WHEN: We call AppLocation.get_directory + directory = AppLocation.get_directory(AppLocation.PluginsDir) + + # THEN: The correct directory should be returned + self.assertEqual('plugins/dir', directory, 'Directory should be "plugins/dir"') + + def get_frozen_path_in_unfrozen_app_test(self): + """ + Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) + """ + with patch('openlp.core.utils.sys') as mocked_sys: + # GIVEN: The sys module "without" a "frozen" attribute + mocked_sys.frozen = None + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The non-frozen parameter is returned + self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') + + def get_frozen_path_in_frozen_app_test(self): + """ + Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller) + """ + with patch('openlp.core.common.sys') as mocked_sys: + # GIVEN: The sys module *with* a "frozen" attribute + mocked_sys.frozen = 1 + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The frozen parameter is returned + self.assertEqual('frozen', frozen_path, 'Should return "frozen"') diff --git a/tests/functional/openlp_core_common/test_settings.py b/tests/functional/openlp_core_common/test_settings.py new file mode 100644 index 000000000..77ae85842 --- /dev/null +++ b/tests/functional/openlp_core_common/test_settings.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.settings package. +""" +import os +from tempfile import mkstemp + +from unittest import TestCase +from PyQt4 import QtGui + +from openlp.core.common import Settings + + +class TestSettings(TestCase): + """ + Test the functions in the Settings module + """ + def setUp(self): + """ + Create the UI + """ + fd, self.ini_file = mkstemp('.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + os.unlink(self.ini_file) + os.unlink(Settings().fileName()) + + def settings_basic_test(self): + """ + Test the Settings creation and its default usage + """ + # GIVEN: A new Settings setup + + # WHEN reading a setting for the first time + default_value = Settings().value('core/has run wizard') + + # THEN the default value is returned + self.assertFalse(default_value, 'The default value should be False') + + # WHEN a new value is saved into config + Settings().setValue('core/has run wizard', True) + + # THEN the new value is returned when re-read + self.assertTrue(Settings().value('core/has run wizard'), 'The saved value should have been returned') + + def settings_override_test(self): + """ + Test the Settings creation and its override usage + """ + # GIVEN: an override for the settings + screen_settings = { + 'test/extend': 'very wide', + } + Settings().extend_default_settings(screen_settings) + + # WHEN reading a setting for the first time + extend = Settings().value('test/extend') + + # THEN the default value is returned + self.assertEqual('very wide', extend, 'The default value of "very wide" should be returned') + + # WHEN a new value is saved into config + Settings().setValue('test/extend', 'very short') + + # THEN the new value is returned when re-read + self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') + + def settings_override_with_group_test(self): + """ + Test the Settings creation and its override usage - with groups + """ + # GIVEN: an override for the settings + screen_settings = { + 'test/extend': 'very wide', + } + Settings.extend_default_settings(screen_settings) + + # WHEN reading a setting for the first time + settings = Settings() + settings.beginGroup('test') + extend = settings.value('extend') + + # THEN the default value is returned + self.assertEqual('very wide', extend, 'The default value defined should be returned') + + # WHEN a new value is saved into config + Settings().setValue('test/extend', 'very short') + + # THEN the new value is returned when re-read + self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') diff --git a/tests/functional/openlp_core_common/test_uistrings.py b/tests/functional/openlp_core_common/test_uistrings.py new file mode 100644 index 000000000..b7f5ecc23 --- /dev/null +++ b/tests/functional/openlp_core_common/test_uistrings.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.uistrings package. +""" +from unittest import TestCase + +from openlp.core.common import UiStrings + + +class TestUiStrings(TestCase): + + def check_same_instance_test(self): + """ + Test the UiStrings class - we always should have only one instance of the UiStrings class. + """ + # WHEN: Create two instances of the UiStrings class. + first_instance = UiStrings() + second_instance = UiStrings() + + # THEN: Check if the instances are the same. + self.assertIs(first_instance, second_instance, 'Two UiStrings objects should be the same instance') + + diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index b485f61e2..cdb046642 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -1,14 +1,42 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ from unittest import TestCase -from mock import MagicMock, patch from sqlalchemy.pool import NullPool from sqlalchemy.orm.scoping import ScopedSession from sqlalchemy import MetaData from openlp.core.lib.db import init_db, get_upgrade_op +from tests.functional import patch, MagicMock class TestDB(TestCase): diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core_lib/test_formattingtags.py index 5d04544fa..53d7519c7 100644 --- a/tests/functional/openlp_core_lib/test_formattingtags.py +++ b/tests/functional/openlp_core_lib/test_formattingtags.py @@ -1,12 +1,39 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib.formattingtags package. """ import copy from unittest import TestCase -from mock import patch - from openlp.core.lib import FormattingTags +from tests.functional import patch TAG = { @@ -32,7 +59,7 @@ class TestFormattingTags(TestCase): Test the FormattingTags class' get_html_tags static method. """ with patch('openlp.core.lib.translate') as mocked_translate, \ - patch('openlp.core.lib.settings') as mocked_settings, \ + patch('openlp.core.common.settings') as mocked_settings, \ patch('openlp.core.lib.formattingtags.json') as mocked_json: # GIVEN: Our mocked modules and functions. mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate @@ -53,7 +80,7 @@ class TestFormattingTags(TestCase): FormattingTags class - test the get_html_tags(), add_html_tags() and remove_html_tag() methods. """ with patch('openlp.core.lib.translate') as mocked_translate, \ - patch('openlp.core.lib.settings') as mocked_settings, \ + patch('openlp.core.common.settings') as mocked_settings, \ patch('openlp.core.lib.formattingtags.json') as mocked_json: # GIVEN: Our mocked modules and functions. mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py new file mode 100644 index 000000000..0ffab5458 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -0,0 +1,324 @@ +""" +Package to test the openlp.core.lib.htmlbuilder module. +""" + +from unittest import TestCase +from mock import MagicMock, patch + +from PyQt4 import QtCore + +from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \ + build_lyrics_format_css, build_footer_css +from openlp.core.lib.theme import HorizontalType, VerticalType + + +HTML = """ + + + +OpenLP Display + + + + + + +plugin HTML +
+ +
+ + +""" +BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed' +LYRICS_CSS = """ +.lyricstable { + z-index: 5; + position: absolute; + display: table; + left: 10px; top: 20px; +} +.lyricscell { + display: table-cell; + word-wrap: break-word; + -webkit-transition: opacity 0.4s ease; + lyrics_format_css +} +.lyricsmain { + text-shadow: #000000 5px 5px; +} +""" +LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' +LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \ + 'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \ + 'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; ' +FOOTER_CSS = """ + left: 10px; + bottom: 0px; + width: 1260px; + font-family: Arial; + font-size: 12pt; + color: #FFFFFF; + text-align: left; + white-space: nowrap; + """ + + +class Htmbuilder(TestCase): + def build_html_test(self): + """ + Test the build_html() function + """ + # GIVEN: Mocked arguments and function. + with patch('openlp.core.lib.htmlbuilder.build_background_css') as mocked_build_background_css, \ + patch('openlp.core.lib.htmlbuilder.build_footer_css') as mocked_build_footer_css, \ + patch('openlp.core.lib.htmlbuilder.build_lyrics_css') as mocked_build_lyrics_css: + # Mocked function. + mocked_build_background_css.return_value = '' + mocked_build_footer_css.return_value = 'dummy: dummy;' + mocked_build_lyrics_css.return_value = '' + # Mocked arguments. + item = MagicMock() + item.bg_image_bytes = None + screen = MagicMock() + is_live = False + background = None + plugin = MagicMock() + plugin.get_display_css.return_value = 'plugin CSS' + plugin.get_display_javascript.return_value = 'plugin JS' + plugin.get_display_html.return_value = 'plugin HTML' + plugins = [plugin] + + # WHEN: Create the html. + html = build_html(item, screen, is_live, background, plugins=plugins) + + # THEN: The returned html should match. + assert html == HTML + + def build_background_css_radial_test(self): + """ + Test the build_background_css() function with a radial background + """ + # GIVEN: Mocked arguments. + item = MagicMock() + item.themedata.background_start_color = '#000000' + item.themedata.background_end_color = '#FFFFFF' + width = 10 + + # WHEN: Create the css. + css = build_background_css(item, width) + + # THEN: The returned css should match. + assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.' + + def build_lyrics_css_test(self): + """ + Test the build_lyrics_css() function + """ + # GIVEN: Mocked method and arguments. + with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_build_lyrics_format_css, \ + patch('openlp.core.lib.htmlbuilder.build_lyrics_outline_css') as mocked_build_lyrics_outline_css: + mocked_build_lyrics_format_css.return_value = 'lyrics_format_css' + mocked_build_lyrics_outline_css.return_value = '' + item = MagicMock() + item.main = QtCore.QRect(10, 20, 10, 20) + item.themedata.font_main_shadow = True + item.themedata.font_main_shadow_color = '#000000' + item.themedata.font_main_shadow_size = 5 + + # WHEN: Create the css. + css = build_lyrics_css(item) + + # THEN: The css should be equal. + assert LYRICS_CSS == css, 'The lyrics css should be equal.' + + def build_lyrics_outline_css_test(self): + """ + Test the build_lyrics_outline_css() function + """ + # GIVEN: The mocked theme data. + theme_data = MagicMock() + theme_data.font_main_outline = True + theme_data.font_main_outline_size = 2 + theme_data.font_main_color = '#FFFFFF' + theme_data.font_main_outline_color = '#000000' + + # WHEN: Create the css. + css = build_lyrics_outline_css(theme_data) + + # THEN: The css should be equal. + assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.' + + def build_lyrics_format_css_test(self): + """ + Test the build_lyrics_format_css() function + """ + # GIVEN: Mocked arguments. + theme_data = MagicMock() + theme_data.display_horizontal_align = HorizontalType.Justify + theme_data.display_vertical_align = VerticalType.Bottom + theme_data.font_main_name = 'Arial' + theme_data.font_main_size = 40 + theme_data.font_main_color = '#FFFFFF' + theme_data.font_main_italics = True + theme_data.font_main_bold = True + theme_data.font_main_line_adjustment = 8 + width = 1580 + height = 810 + + # WHEN: Get the css. + css = build_lyrics_format_css(theme_data, width, height) + + # THEN: They should be equal. + assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.' + + def build_footer_css_test(self): + """ + Test the build_footer_css() function + """ + # GIVEN: Create a theme. + item = MagicMock() + item.footer = QtCore.QRect(10, 921, 1260, 103) + item.themedata.font_footer_name = 'Arial' + item.themedata.font_footer_size = 12 + item.themedata.font_footer_color = '#FFFFFF' + height = 1024 + + # WHEN: create the css. + css = build_footer_css(item, height) + + # THEN: THE css should be the same. + assert FOOTER_CSS == css, 'The footer strings should be equal.' + diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index a1bc7624a..472011884 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -1,15 +1,41 @@ -""" - Package to test the openlp.core.ui package. -""" +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.ui package. +""" import os -from unittest import TestCase -from PyQt4 import QtCore, QtGui +from unittest import TestCase +from PyQt4 import QtGui from openlp.core.lib import Registry, ImageManager, ScreenList - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index fa7fe92e9..361b8d2cb 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -1,3 +1,31 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ @@ -6,22 +34,21 @@ import os from unittest import TestCase from datetime import datetime, timedelta -from mock import MagicMock, patch from PyQt4 import QtCore, QtGui -from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \ +from openlp.core.common import check_directory_exists, translate +from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \ build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags - +from tests.functional import MagicMock, patch TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) - class TestLib(TestCase): - def str_to_bool_with_bool_test(self): + def str_to_bool_with_bool_true_test(self): """ - Test the str_to_bool function with boolean input + Test the str_to_bool function with boolean input of True """ # GIVEN: A boolean value set to true true_boolean = True @@ -30,9 +57,13 @@ class TestLib(TestCase): true_result = str_to_bool(true_boolean) # THEN: We should get back a True bool - assert isinstance(true_result, bool), 'The result should be a boolean' - assert true_result is True, 'The result should be True' + self.assertIsInstance(true_result, bool, 'The result should be a boolean') + self.assertTrue(true_result, 'The result should be True') + def str_to_bool_with_bool_false_test(self): + """ + Test the str_to_bool function with boolean input of False + """ # GIVEN: A boolean value set to false false_boolean = False @@ -40,12 +71,12 @@ class TestLib(TestCase): false_result = str_to_bool(false_boolean) # THEN: We should get back a True bool - assert isinstance(false_result, bool), 'The result should be a boolean' - assert false_result is False, 'The result should be True' + self.assertIsInstance(false_result, bool, 'The result should be a boolean') + self.assertFalse(false_result, 'The result should be True') - def str_to_bool_with_invalid_test(self): + def str_to_bool_with_integer_test(self): """ - Test the str_to_bool function with a set of invalid inputs + Test the str_to_bool function with an integer input """ # GIVEN: An integer value int_string = 1 @@ -54,8 +85,12 @@ class TestLib(TestCase): int_result = str_to_bool(int_string) # THEN: we should get back a false - assert int_result is False, 'The result should be False' + self.assertFalse(int_result, 'The result should be False') + def str_to_bool_with_invalid_string_test(self): + """ + Test the str_to_bool function with an invalid string + """ # GIVEN: An string value with completely invalid input invalid_string = 'my feet are wet' @@ -63,11 +98,11 @@ class TestLib(TestCase): str_result = str_to_bool(invalid_string) # THEN: we should get back a false - assert str_result is False, 'The result should be False' + self.assertFalse(str_result, 'The result should be False') - def str_to_bool_with_false_values_test(self): + def str_to_bool_with_string_false_test(self): """ - Test the str_to_bool function with a set of false inputs + Test the str_to_bool function with a string saying "false" """ # GIVEN: A string set to "false" false_string = 'false' @@ -76,8 +111,12 @@ class TestLib(TestCase): false_result = str_to_bool(false_string) # THEN: we should get back a false - assert false_result is False, 'The result should be False' + self.assertFalse(false_result, 'The result should be False') + def str_to_bool_with_string_no_test(self): + """ + Test the str_to_bool function with a string saying "NO" + """ # GIVEN: An string set to "NO" no_string = 'NO' @@ -85,11 +124,11 @@ class TestLib(TestCase): str_result = str_to_bool(no_string) # THEN: we should get back a false - assert str_result is False, 'The result should be False' + self.assertFalse(str_result, 'The result should be False') - def str_to_bool_with_true_values_test(self): + def str_to_bool_with_true_string_value_test(self): """ - Test the str_to_bool function with a set of true inputs + Test the str_to_bool function with a string set to "True" """ # GIVEN: A string set to "True" true_string = 'True' @@ -98,8 +137,12 @@ class TestLib(TestCase): true_result = str_to_bool(true_string) # THEN: we should get back a true - assert true_result is True, 'The result should be True' + self.assertTrue(true_result, 'The result should be True') + def str_to_bool_with_yes_string_value_test(self): + """ + Test the str_to_bool function with a string set to "yes" + """ # GIVEN: An string set to "yes" yes_string = 'yes' @@ -107,7 +150,7 @@ class TestLib(TestCase): str_result = str_to_bool(yes_string) # THEN: we should get back a true - assert str_result is True, 'The result should be True' + self.assertTrue(str_result, 'The result should be True') def translate_test(self): """ @@ -126,7 +169,7 @@ class TestLib(TestCase): # THEN: the translated string should be returned, and the mocked function should have been called mocked_translate.assert_called_with(context, text, comment, encoding, n) - assert result == 'Translated string', 'The translated string should have been returned' + self.assertEqual('Translated string', result, 'The translated string should have been returned') def check_directory_exists_test(self): """ @@ -143,7 +186,7 @@ class TestLib(TestCase): # THEN: Only os.path.exists should have been called mocked_exists.assert_called_with(directory_to_check) - assert not mocked_makedirs.called, 'os.makedirs should not have been called' + self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called') # WHEN: os.path.exists returns False and we check the directory exists mocked_exists.return_value = False @@ -181,13 +224,14 @@ class TestLib(TestCase): # THEN: The result should be False mocked_isfile.assert_called_with(filename) - assert result is False, 'False should be returned if no file exists' + self.assertFalse(result, 'False should be returned if no file exists') def get_text_file_string_read_error_test(self): """ Test the get_text_file_string() method when a read error happens """ - with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('openlp.core.lib.open', create=True) as mocked_open: + with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, \ + patch('openlp.core.lib.open', create=True) as mocked_open: # GIVEN: A mocked-out open() which raises an exception and isfile returns True filename = 'testfile.txt' mocked_isfile.return_value = True @@ -199,13 +243,13 @@ class TestLib(TestCase): # THEN: None should be returned mocked_isfile.assert_called_with(filename) mocked_open.assert_called_with(filename, 'r') - assert result is None, 'None should be returned if the file cannot be opened' + self.assertIsNone(result, 'None should be returned if the file cannot be opened') def get_text_file_string_decode_error_test(self): """ Test the get_text_file_string() method when the contents cannot be decoded """ - assert True, 'Impossible to test due to conflicts when mocking out the "open" function' + self.skipTest('Impossible to test due to conflicts when mocking out the "open" function') def build_icon_with_qicon_test(self): """ @@ -220,7 +264,7 @@ class TestLib(TestCase): result = build_icon(mocked_icon) # THEN: The result should be our mocked QIcon - assert result is mocked_icon, 'The result should be the mocked QIcon' + self.assertIs(mocked_icon, result, 'The result should be the mocked QIcon') def build_icon_with_resource_test(self): """ @@ -242,7 +286,7 @@ class TestLib(TestCase): MockedQPixmap.assert_called_with(resource_uri) # There really should be more assert statements here but due to type checking and things they all break. The # best we can do is to assert that we get back a MagicMock object. - assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out' + self.assertIsInstance(result, MagicMock, 'The result should be a MagicMock, because we mocked it out') def image_to_byte_test(self): """ @@ -267,7 +311,8 @@ class TestLib(TestCase): mocked_buffer.open.assert_called_with('writeonly') mocked_image.save.assert_called_with(mocked_buffer, "PNG") mocked_byte_array.toBase64.assert_called_with() - assert result == 'base64mock', 'The result should be the return value of the mocked out base64 method' + self.assertEqual('base64mock', result, + 'The result should be the return value of the mocked out base64 method') def create_thumb_with_size_test(self): """ @@ -286,16 +331,16 @@ class TestLib(TestCase): pass # Only continue when the thumb does not exist. - assert not os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists.' + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') # WHEN: Create the thumb. icon = create_thumb(image_path, thumb_path, size=thumb_size) # THEN: Check if the thumb was created. - assert os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists.' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon.' - assert not icon.isNull(), 'The icon should not be null.' - assert QtGui.QImageReader(thumb_path).size() == thumb_size, 'The thumb should have the given size.' + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(thumb_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') # Remove the thumb so that the test actually tests if the thumb will be created. try: @@ -318,7 +363,7 @@ class TestLib(TestCase): # THEN: The selectedIndexes function should have been called and the result should be true mocked_list_widget.selectedIndexes.assert_called_with() - assert result, 'The result should be True' + self.assertTrue(result, 'The result should be True') def check_item_selected_false_test(self): """ @@ -326,7 +371,7 @@ class TestLib(TestCase): """ # GIVEN: A mocked out QtGui module and a list widget with selected indexes with patch('openlp.core.lib.QtGui') as MockedQtGui, \ - patch('openlp.core.lib.translate') as mocked_translate: + patch('openlp.core.lib.translate') as mocked_translate: mocked_translate.return_value = 'mocked translate' mocked_list_widget = MagicMock() mocked_list_widget.selectedIndexes.return_value = False @@ -339,7 +384,7 @@ class TestLib(TestCase): # THEN: The selectedIndexes function should have been called and the result should be true mocked_list_widget.selectedIndexes.assert_called_with() MockedQtGui.QMessageBox.information.assert_called_with('parent', 'mocked translate', 'message') - assert not result, 'The result should be False' + self.assertFalse(result, 'The result should be False') def clean_tags_test(self): """ @@ -361,7 +406,7 @@ class TestLib(TestCase): result_string = clean_tags(string_to_pass) # THEN: The strings should be identical. - assert result_string == wanted_string, 'The strings should be identical.' + self.assertEqual(wanted_string, result_string, 'The strings should be identical') def expand_tags_test(self): """ @@ -400,7 +445,7 @@ class TestLib(TestCase): result_string = expand_tags(string_to_pass) # THEN: The strings should be identical. - assert result_string == wanted_string, 'The strings should be identical.' + self.assertEqual(wanted_string, result_string, 'The strings should be identical.') def validate_thumb_file_does_not_exist_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 17e3c3e67..725efd29e 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -1,12 +1,40 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib.pluginmanager package. """ from unittest import TestCase -from mock import MagicMock - +from openlp.core.common import Settings from openlp.core.lib.pluginmanager import PluginManager -from openlp.core.lib import Settings, Registry, PluginStatus +from openlp.core.lib import Registry, PluginStatus +from tests.functional import MagicMock class TestPluginManager(TestCase): @@ -42,8 +70,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_media_manager() # THEN: The create_media_manager_item() method should have been called - assert mocked_plugin.create_media_manager_item.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, + 'The create_media_manager_item() method should not have been called.') def hook_media_manager_with_active_plugin_test(self): """ @@ -75,8 +103,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The hook_settings_tabs() method should have been called - assert mocked_plugin.create_media_manager_item.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, + 'The create_media_manager_item() method should not have been called.') def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self): """ @@ -95,8 +123,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same - assert mocked_plugin.create_settings_tab.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_settings_tab.call_count, + 'The create_media_manager_item() method should not have been called.') self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, 'The plugins on the settings form should be the same as the plugins in the plugin manager') @@ -117,10 +145,10 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The create_media_manager_item() method should have been called with the mocked settings form - assert mocked_plugin.create_settings_tab.call_count == 1, \ - 'The create_media_manager_item() method should have been called once.' - self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, - 'The plugins on the settings form should be the same as the plugins in the plugin manager') + self.assertEqual(1, mocked_plugin.create_settings_tab.call_count, + 'The create_media_manager_item() method should have been called once.') + self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins, + 'The plugins on the settings form should be the same as the plugins in the plugin manager') def hook_settings_tabs_with_active_plugin_and_no_form_test(self): """ @@ -152,8 +180,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_import_menu() # THEN: The create_media_manager_item() method should have been called - assert mocked_plugin.add_import_menu_item.call_count == 0, \ - 'The add_import_menu_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count, + 'The add_import_menu_item() method should not have been called.') def hook_import_menu_with_active_plugin_test(self): """ @@ -185,8 +213,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_export_menu() # THEN: The add_export_menu_Item() method should not have been called - assert mocked_plugin.add_export_menu_Item.call_count == 0, \ - 'The add_export_menu_Item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count, + 'The add_export_menu_Item() method should not have been called.') def hook_export_menu_with_active_plugin_test(self): """ @@ -219,8 +247,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_upgrade_plugin_settings(settings) # THEN: The upgrade_settings() method should not have been called - assert mocked_plugin.upgrade_settings.call_count == 0, \ - 'The upgrade_settings() method should not have been called.' + self.assertEqual(0, mocked_plugin.upgrade_settings.call_count, + 'The upgrade_settings() method should not have been called.') def hook_upgrade_plugin_settings_with_active_plugin_test(self): """ @@ -253,8 +281,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_tools_menu() # THEN: The add_tools_menu_item() method should have been called - assert mocked_plugin.add_tools_menu_item.call_count == 0, \ - 'The add_tools_menu_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count, + 'The add_tools_menu_item() method should not have been called.') def hook_tools_menu_with_active_plugin_test(self): """ @@ -288,7 +316,7 @@ class TestPluginManager(TestCase): # THEN: The is_active() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.initialise.call_count == 0, 'The initialise() method should not have been called.' + self.assertEqual(0, mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.') def initialise_plugins_with_active_plugin_test(self): """ @@ -324,7 +352,7 @@ class TestPluginManager(TestCase): # THEN: The is_active() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.finalise.call_count == 0, 'The finalise() method should not have been called.' + self.assertEqual(0, mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.') def finalise_plugins_with_active_plugin_test(self): """ @@ -392,8 +420,8 @@ class TestPluginManager(TestCase): # THEN: The isActive() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.new_service_created.call_count == 0,\ - 'The new_service_created() method should not have been called.' + self.assertEqual(0, mocked_plugin.new_service_created.call_count, + 'The new_service_created() method should not have been called.') def new_service_created_with_active_plugin_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_registry.py b/tests/functional/openlp_core_lib/test_registry.py index c8003559f..06307630b 100644 --- a/tests/functional/openlp_core_lib/test_registry.py +++ b/tests/functional/openlp_core_lib/test_registry.py @@ -1,12 +1,39 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ import os from unittest import TestCase -from mock import MagicMock - from openlp.core.lib import Registry +from tests.functional import MagicMock TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core_lib/test_screen.py index a73883ab9..ba635ce83 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core_lib/test_screen.py @@ -1,14 +1,40 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.lib.screenlist package. """ - from unittest import TestCase -from mock import MagicMock from PyQt4 import QtGui, QtCore from openlp.core.lib import Registry, ScreenList - +from tests.functional import MagicMock SCREEN = { 'primary': False, @@ -55,5 +81,6 @@ class TestScreenList(TestCase): # THEN: The screen should have been added and the screens should be identical new_screen_count = len(self.screens.screen_list) - assert old_screen_count + 1 == new_screen_count, 'The new_screens list should be bigger' - assert SCREEN == self.screens.screen_list.pop(), 'The 2nd screen should be identical to the first screen' + self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger') + self.assertEqual(SCREEN, self.screens.screen_list.pop(), + 'The 2nd screen should be identical to the first screen') diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index c387d83ad..6893b8ca3 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -1,16 +1,43 @@ # -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ - Package to test the openlp.core.lib package. +Package to test the openlp.core.lib package. """ import os -import json -import tempfile from unittest import TestCase -from mock import MagicMock, patch + + +from tests.functional import MagicMock, patch +from tests.utils import assert_length, convert_file_service_item from openlp.core.lib import ItemCapabilities, ServiceItem, Registry -from lxml import objectify, etree + VERSE = 'The Lord said to {r}Noah{/r}: \n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ @@ -20,7 +47,6 @@ VERSE = 'The Lord said to {r}Noah{/r}: \n'\ '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}'\ 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456'] - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) @@ -36,7 +62,7 @@ class TestServiceItem(TestCase): Registry().register('renderer', mocked_renderer) Registry().register('image_manager', MagicMock()) - def serviceitem_basic_test(self): + def service_item_basic_test(self): """ Test the Service Item - basic test """ @@ -46,10 +72,10 @@ class TestServiceItem(TestCase): service_item = ServiceItem(None) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert service_item.missing_frames() is True, 'There should not be any frames in the service item' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + self.assertTrue(service_item.missing_frames(), 'There should not be any frames in the service item') - def serviceitem_load_custom_from_service_test(self): + def service_item_load_custom_from_service_test(self): """ Test the Service Item - adding a custom slide from a saved service """ @@ -57,24 +83,29 @@ class TestServiceItem(TestCase): service_item = ServiceItem(None) service_item.add_icon = MagicMock() - # WHEN: adding a custom from a saved Service - line = self.convert_file_service_item('serviceitem_custom_1.osj') + # WHEN: We add a custom from a saved service + line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj') service_item.set_from_service(line) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert len(service_item._display_frames) == 0, 'The service item should have no display frames' - assert len(service_item.capabilities) == 5, 'There should be 5 default custom item capabilities' - service_item.render(True) - assert service_item.get_display_title() == 'Test Custom', 'The title should be "Test Custom"' - assert service_item.get_frames()[0]['text'] == VERSE[:-1], \ - 'The returned text matches the input, except the last line feed' - assert service_item.get_rendered_frame(1) == VERSE.split('\n', 1)[0], 'The first line has been returned' - assert service_item.get_frame_title(0) == 'Slide 1', '"Slide 1" has been returned as the title' - assert service_item.get_frame_title(1) == 'Slide 2', '"Slide 2" has been returned as the title' - assert service_item.get_frame_title(2) == '', 'Blank has been returned as the title of slide 3' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + assert_length(0, service_item._display_frames, 'The service item should have no display frames') + assert_length(5, service_item.capabilities, 'There should be 5 default custom item capabilities') - def serviceitem_load_image_from_service_test(self): + # WHEN: We render the frames of the service item + service_item.render(True) + + # THEN: The frames should also be valid + self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"') + self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'], + 'The returned text matches the input, except the last line feed') + self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1), + 'The first line has been returned') + self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title') + self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title') + self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3') + + def service_item_load_image_from_service_test(self): """ Test the Service Item - adding an image from a saved service """ @@ -87,29 +118,34 @@ class TestServiceItem(TestCase): service_item.add_icon = MagicMock() # WHEN: adding an image from a saved Service and mocked exists - line = self.convert_file_service_item('serviceitem_image_1.osj') + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_1.osj') with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: mocked_exists.return_value = True service_item.set_from_service(line, TEST_PATH) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert service_item.get_rendered_frame(0) == test_file, 'The first frame should match the path to the image' - assert service_item.get_frames()[0] == frame_array, 'The return should match frame array1' - assert service_item.get_frame_path(0) == test_file, 'The frame path should match the full path to the image' - assert service_item.get_frame_title(0) == image_name, 'The frame title should match the image name' - assert service_item.get_display_title() == image_name, 'The display title should match the first image name' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + self.assertEqual(test_file, service_item.get_rendered_frame(0), + 'The first frame should match the path to the image') + self.assertEqual(frame_array, service_item.get_frames()[0], + 'The return should match frame array1') + self.assertEqual(test_file, service_item.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(image_name, service_item.get_frame_title(0), + 'The frame title should match the image name') + self.assertEqual(image_name, service_item.get_display_title(), + 'The display title should match the first image name') + self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), + 'This service item should be able to be Maintained') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), + 'This service item should be able to be be Previewed') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), + 'This service item should be able to be run in a can be made to Loop') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), + 'This service item should be able to have new items added to it') - def serviceitem_load_image_from_local_service_test(self): + def service_item_load_image_from_local_service_test(self): """ Test the Service Item - adding an image from a saved local service """ @@ -128,50 +164,42 @@ class TestServiceItem(TestCase): service_item2.add_icon = MagicMock() # WHEN: adding an image from a saved Service and mocked exists - line = self.convert_file_service_item('serviceitem_image_2.osj') - line2 = self.convert_file_service_item('serviceitem_image_2.osj', 1) + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj') + line2 = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj', 1) with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: mocked_exists.return_value = True service_item2.set_from_service(line2) service_item.set_from_service(line) - # THEN: We should get back a valid service item # This test is copied from service_item.py, but is changed since to conform to # new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now. - assert service_item.is_valid is True, 'The first service item should be valid' - assert service_item2.is_valid is True, 'The second service item should be valid' - assert service_item.get_rendered_frame(0) == test_file1, 'The first frame should match the path to the image' - assert service_item2.get_rendered_frame(0) == test_file2, 'The Second frame should match the path to the image' - assert service_item.get_frames()[0] == frame_array1, 'The return should match the frame array1' - assert service_item2.get_frames()[0] == frame_array2, 'The return should match the frame array2' - assert service_item.get_frame_path(0) == test_file1, 'The frame path should match the full path to the image' - assert service_item2.get_frame_path(0) == test_file2, 'The frame path should match the full path to the image' - assert service_item.get_frame_title(0) == image_name1, 'The 1st frame title should match the image name' - assert service_item2.get_frame_title(0) == image_name2, 'The 2nd frame title should match the image name' - assert service_item.title.lower() == service_item.name, \ - 'The plugin name should match the display title, as there are > 1 Images' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' - - def convert_file_service_item(self, name, row=0): - service_file = os.path.join(TEST_PATH, name) - try: - open_file = open(service_file, 'r') - items = json.load(open_file) - first_line = items[row] - except IOError: - first_line = '' - finally: - open_file.close() - return first_line - + self.assertTrue(service_item.is_valid, 'The first service item should be valid') + self.assertTrue(service_item2.is_valid, 'The second service item should be valid') + self.assertEqual(test_file1, service_item.get_rendered_frame(0), + 'The first frame should match the path to the image') + self.assertEqual(test_file2, service_item2.get_rendered_frame(0), + 'The Second frame should match the path to the image') + self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1') + self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2') + self.assertEqual(test_file1, service_item.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(test_file2, service_item2.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(image_name1, service_item.get_frame_title(0), + 'The 1st frame title should match the image name') + self.assertEqual(image_name2, service_item2.get_frame_title(0), + 'The 2nd frame title should match the image name') + self.assertEqual(service_item.name, service_item.title.lower(), + 'The plugin name should match the display title, as there are > 1 Images') + self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), + 'This service item should be able to be Maintained') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), + 'This service item should be able to be be Previewed') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), + 'This service item should be able to be run in a can be made to Loop') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), + 'This service item should be able to have new items added to it') diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py deleted file mode 100644 index 444b206d6..000000000 --- a/tests/functional/openlp_core_lib/test_settings.py +++ /dev/null @@ -1,95 +0,0 @@ -""" - Package to test the openlp.core.lib.settings package. -""" -import os -from unittest import TestCase -from tempfile import mkstemp - -from openlp.core.lib import Settings - -from PyQt4 import QtGui - - -class TestSettings(TestCase): - """ - Test the functions in the Settings module - """ - def setUp(self): - """ - Create the UI - """ - fd, self.ini_file = mkstemp('.ini') - Settings().set_filename(self.ini_file) - self.application = QtGui.QApplication.instance() - - def tearDown(self): - """ - Delete all the C++ objects at the end so that we don't have a segfault - """ - del self.application - os.unlink(self.ini_file) - os.unlink(Settings().fileName()) - - def settings_basic_test(self): - """ - Test the Settings creation and its default usage - """ - # GIVEN: A new Settings setup - - # WHEN reading a setting for the first time - default_value = Settings().value('core/has run wizard') - - # THEN the default value is returned - assert default_value is False, 'The default value should be False' - - # WHEN a new value is saved into config - Settings().setValue('core/has run wizard', True) - - # THEN the new value is returned when re-read - assert Settings().value('core/has run wizard') is True, 'The saved value should have been returned' - - def settings_override_test(self): - """ - Test the Settings creation and its override usage - """ - # GIVEN: an override for the settings - screen_settings = { - 'test/extend': 'very wide', - } - Settings().extend_default_settings(screen_settings) - - # WHEN reading a setting for the first time - extend = Settings().value('test/extend') - - # THEN the default value is returned - assert extend == 'very wide', 'The default value of "very wide" should be returned' - - # WHEN a new value is saved into config - Settings().setValue('test/extend', 'very short') - - # THEN the new value is returned when re-read - assert Settings().value('test/extend') == 'very short', 'The saved value should be returned' - - def settings_override_with_group_test(self): - """ - Test the Settings creation and its override usage - with groups - """ - # GIVEN: an override for the settings - screen_settings = { - 'test/extend': 'very wide', - } - Settings.extend_default_settings(screen_settings) - - # WHEN reading a setting for the first time - settings = Settings() - settings.beginGroup('test') - extend = settings.value('extend') - - # THEN the default value is returned - assert extend == 'very wide', 'The default value defined should be returned' - - # WHEN a new value is saved into config - Settings().setValue('test/extend', 'very short') - - # THEN the new value is returned when re-read - assert Settings().value('test/extend') == 'very short', 'The saved value should be returned' diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py new file mode 100644 index 000000000..1ece64b34 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.theme package. +""" +from tests.functional import MagicMock, patch +from unittest import TestCase + +from openlp.core.lib.theme import ThemeXML + + +class TestTheme(TestCase): + """ + Test the functions in the Theme module + """ + def setUp(self): + """ + Create the UI + """ + pass + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + pass + + def test_new_theme(self): + """ + Test the theme creation - basic test + """ + # GIVEN: A new theme + + # WHEN: A theme is created + default_theme = ThemeXML() + + # THEN: We should get some default behaviours + self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') + self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') + self.assertTrue(default_theme.display_vertical_align == 0, + 'There theme should have display_vertical_align of 0') + self.assertTrue(default_theme.font_footer_name == "Arial", + 'There theme should has font_footer_name of Arial') + self.assertTrue(default_theme.font_main_bold is False, 'There theme should has font_main_bold of false') \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_uistrings.py b/tests/functional/openlp_core_lib/test_uistrings.py deleted file mode 100644 index 0070533db..000000000 --- a/tests/functional/openlp_core_lib/test_uistrings.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Package to test the openlp.core.lib.uistrings package. -""" - -from unittest import TestCase - -from openlp.core.lib import UiStrings - - -class TestUiStrings(TestCase): - - def check_same_instance_test(self): - """ - Test the UiStrings class - we always should have only one instance of the UiStrings class. - """ - # WHEN: Create two instances of the UiStrings class. - first_instance = UiStrings() - second_instance = UiStrings() - - # THEN: Check if the instances are the same. - assert first_instance is second_instance, "They should be the same instance!" - - diff --git a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py new file mode 100644 index 000000000..dc9495775 --- /dev/null +++ b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.ui.formattingtagscontroller package. +""" +from unittest import TestCase + +from openlp.core.ui import FormattingTagController + + +class TestFormattingTagController(TestCase): + + def setUp(self): + self.services = FormattingTagController() + + def test_strip(self): + """ + Test that the _strip strips the correct chars + """ + # GIVEN: An instance of the Formatting Tag Form and a string containing a tag + tag = '{tag}' + + # WHEN: Calling _strip + result = self.services._strip(tag) + + # THEN: The tag should be returned with the wrappers removed. + self.assertEqual(result, 'tag', 'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') + + def test_end_tag_changed_processes_correctly(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of start , end tags and error messages + tests = [] + test = {'start': '', 'end': None, 'gen': '', 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, + 'valid': 'End tag does not match end tag for start tag '} + tests.append(test) + + # WHEN: Testing each one of them in turn + for test in tests: + error, result = self.services.end_tag_changed(test['start'], test['end']) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test['gen'], + 'Function should handle end tag correctly : %s and %s for %s ' % (test['gen'], result, test['start'])) + self.assertTrue(error == test['valid'], + 'Function should not generate unexpected error messages : %s ' % error) + + def test_start_tag_changed_processes_correctly(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of start , end tags and error messages + tests = [] + test = {'start': '', 'end': '', 'gen': '', 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, 'valid': None} + tests.append(test) + test = {'start': 'superfly', 'end': '', 'gen': None, 'valid': 'Start tag superfly is not valid HTML'} + tests.append(test) + + # WHEN: Testing each one of them in turn + for test in tests: + error, result = self.services.start_tag_changed(test['start'], test['end']) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test['gen'], + 'Function should handle end tag correctly : %s and %s ' % (test['gen'], result)) + self.assertTrue(error == test['valid'], + 'Function should not generate unexpected error messages : %s ' % error) + + def test_start_html_to_end_html(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of valid and invalid tags + tests = {'': '', '': '', 'superfly': '', '': None, + '': ''} + + # WHEN: Testing each one of them + for test1, test2 in tests.items(): + result = self.services.start_html_to_end_html(test1) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test2, 'Calculated end tag should be valid: %s and %s = %s' + % (test1, test2, result)) \ No newline at end of file diff --git a/tests/functional/openlp_core_ui/tests_formattingtagsform.py b/tests/functional/openlp_core_ui/tests_formattingtagsform.py new file mode 100644 index 000000000..7dc3d4b6f --- /dev/null +++ b/tests/functional/openlp_core_ui/tests_formattingtagsform.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.ui.formattingtagsform package. +""" +from unittest import TestCase + +from tests.functional import MagicMock, patch + +from openlp.core.ui.formattingtagform import FormattingTagForm + +# TODO: Tests Still TODO +# __init__ +# exec_ +# on_new_clicked +# on_delete_clicked +# on_saved_clicked +# _reloadTable + + +class TestFormattingTagForm(TestCase): + + def setUp(self): + self.init_patcher = patch('openlp.core.ui.formattingtagform.FormattingTagForm.__init__') + self.qdialog_patcher = patch('openlp.core.ui.formattingtagform.QtGui.QDialog') + self.ui_formatting_tag_dialog_patcher = patch('openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') + self.mocked_init = self.init_patcher.start() + self.mocked_qdialog = self.qdialog_patcher.start() + self.mocked_ui_formatting_tag_dialog = self.ui_formatting_tag_dialog_patcher.start() + self.mocked_init.return_value = None + + def tearDown(self): + self.init_patcher.stop() + self.qdialog_patcher.stop() + self.ui_formatting_tag_dialog_patcher.stop() + + def test_on_text_edited(self): + """ + Test that the appropriate actions are preformed when on_text_edited is called + """ + + # GIVEN: An instance of the Formatting Tag Form and a mocked save_push_button + form = FormattingTagForm() + form.save_button = MagicMock() + + # WHEN: on_text_edited is called with an arbitrary value + #form.on_text_edited('text') + + # THEN: setEnabled and setDefault should have been called on save_push_button + #form.save_button.setEnabled.assert_called_with(True) + diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 0fa096b07..6b4972b37 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -1,3 +1,31 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Package to test the openlp.core.utils.actions package. """ @@ -7,11 +35,14 @@ from unittest import TestCase from PyQt4 import QtGui, QtCore -from openlp.core.lib import Settings +from openlp.core.common import Settings from openlp.core.utils import ActionList class TestActionList(TestCase): + """ + Test the ActionList class + """ def setUp(self): """ diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_utils/test_applocation.py deleted file mode 100644 index 997ff7417..000000000 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ /dev/null @@ -1,147 +0,0 @@ -""" -Functional tests to test the AppLocation class and related methods. -""" -import copy -from unittest import TestCase - -from mock import patch - -from openlp.core.utils import AppLocation - - -FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] - - -class TestAppLocation(TestCase): - """ - A test suite to test out various methods around the AppLocation class. - """ - def get_data_path_test(self): - """ - Test the AppLocation.get_data_path() method - """ - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: - # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() - mocked_settings = mocked_class.return_value - mocked_settings.contains.return_value = False - mocked_get_directory.return_value = 'test/dir' - mocked_check_directory_exists.return_value = True - mocked_os.path.normpath.return_value = 'test/dir' - - # WHEN: we call AppLocation.get_data_path() - data_path = AppLocation.get_data_path() - - # THEN: check that all the correct methods were called, and the result is correct - mocked_settings.contains.assert_called_with('advanced/data path') - mocked_get_directory.assert_called_with(AppLocation.DataDir) - mocked_check_directory_exists.assert_called_with('test/dir') - assert data_path == 'test/dir', 'Result should be "test/dir"' - - def get_data_path_with_custom_location_test(self): - """ - Test the AppLocation.get_data_path() method when a custom location is set in the settings - """ - with patch('openlp.core.utils.applocation.Settings') as mocked_class,\ - patch('openlp.core.utils.applocation.os') as mocked_os: - # GIVEN: A mocked out Settings class which returns a custom data location - mocked_settings = mocked_class.return_value - mocked_settings.contains.return_value = True - mocked_settings.value.return_value.toString.return_value = 'custom/dir' - mocked_os.path.normpath.return_value = 'custom/dir' - - # WHEN: we call AppLocation.get_data_path() - data_path = AppLocation.get_data_path() - - # THEN: the mocked Settings methods were called and the value returned was our set up value - mocked_settings.contains.assert_called_with('advanced/data path') - mocked_settings.value.assert_called_with('advanced/data path') - assert data_path == 'custom/dir', 'Result should be "custom/dir"' - - def get_files_no_section_no_extension_test(self): - """ - Test the AppLocation.get_files() method with no parameters passed. - """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.os.listdir') as mocked_listdir: - # GIVEN: Our mocked modules/methods. - mocked_get_data_path.return_value = 'test/dir' - mocked_listdir.return_value = copy.deepcopy(FILE_LIST) - - # When: Get the list of files. - result = AppLocation.get_files() - - # Then: check if the file lists are identical. - assert result == FILE_LIST, 'The file lists should be identical.' - - def get_files_test(self): - """ - Test the AppLocation.get_files() method with all parameters passed. - """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.os.listdir') as mocked_listdir: - # GIVEN: Our mocked modules/methods. - mocked_get_data_path.return_value = 'test/dir' - mocked_listdir.return_value = copy.deepcopy(FILE_LIST) - - # When: Get the list of files. - result = AppLocation.get_files('section', '.mp3') - - # Then: Check if the section parameter was used correctly. - mocked_listdir.assert_called_with('test/dir/section') - - # Then: check if the file lists are identical. - assert result == ['file5.mp3', 'file6.mp3'], 'The file lists should be identical.' - - def get_section_data_path_test(self): - """ - Test the AppLocation.get_section_data_path() method - """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists: - # GIVEN: A mocked out AppLocation.get_data_path() - mocked_get_data_path.return_value = 'test/dir' - mocked_check_directory_exists.return_value = True - - # WHEN: we call AppLocation.get_data_path() - data_path = AppLocation.get_section_data_path('section') - - # THEN: check that all the correct methods were called, and the result is correct - mocked_check_directory_exists.assert_called_with('test/dir/section') - assert data_path == 'test/dir/section', 'Result should be "test/dir/section"' - - def get_directory_for_app_dir_test(self): - """ - Test the AppLocation.get_directory() method for AppLocation.AppDir - """ - with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path: - mocked_get_frozen_path.return_value = 'app/dir' - - # WHEN: We call AppLocation.get_directory - directory = AppLocation.get_directory(AppLocation.AppDir) - - # THEN: - assert directory == 'app/dir', 'Directory should be "app/dir"' - - def get_directory_for_plugins_dir_test(self): - """ - Test the AppLocation.get_directory() method for AppLocation.PluginsDir - """ - with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path, \ - patch('openlp.core.utils.applocation.os.path.abspath') as mocked_abspath, \ - patch('openlp.core.utils.applocation.os.path.split') as mocked_split, \ - patch('openlp.core.utils.applocation.sys') as mocked_sys: - mocked_abspath.return_value = 'plugins/dir' - mocked_split.return_value = ['openlp'] - mocked_get_frozen_path.return_value = 'plugins/dir' - mocked_sys.frozen = 1 - mocked_sys.argv = ['openlp'] - - # WHEN: We call AppLocation.get_directory - directory = AppLocation.get_directory(AppLocation.PluginsDir) - - # THEN: - assert directory == 'plugins/dir', 'Directory should be "plugins/dir"' - diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 6eacb2e48..dfc31b245 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -1,25 +1,52 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ Functional tests to test the AppLocation class and related methods. """ from unittest import TestCase -from mock import patch - -from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ +from openlp.core.utils import clean_filename, get_filesystem_encoding, get_locale_key, \ get_natural_key, split_filename +from tests.functional import patch class TestUtils(TestCase): """ A test suite to test out various methods around the AppLocation class. """ - def get_filesystem_encoding_test(self): + def get_filesystem_encoding_sys_function_not_called_test(self): """ - Test the get_filesystem_encoding() function + Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function """ + # GIVEN: sys.getfilesystemencoding returns "cp1252" with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: - # GIVEN: sys.getfilesystemencoding returns "cp1252" mocked_getfilesystemencoding.return_value = 'cp1252' # WHEN: get_filesystem_encoding() is called @@ -27,10 +54,16 @@ class TestUtils(TestCase): # THEN: getdefaultencoding should have been called mocked_getfilesystemencoding.assert_called_with() - assert not mocked_getdefaultencoding.called - assert result == 'cp1252', 'The result should be "cp1252"' + self.assertEqual(0, mocked_getdefaultencoding.called, 'getdefaultencoding should not have been called') + self.assertEqual('cp1252', result, 'The result should be "cp1252"') - # GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8" + def get_filesystem_encoding_sys_function_is_called_test(self): + """ + Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function + """ + # GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8" + with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ + patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: mocked_getfilesystemencoding.return_value = None mocked_getdefaultencoding.return_value = 'utf-8' @@ -40,23 +73,7 @@ class TestUtils(TestCase): # THEN: getdefaultencoding should have been called mocked_getfilesystemencoding.assert_called_with() mocked_getdefaultencoding.assert_called_with() - assert result == 'utf-8', 'The result should be "utf-8"' - - def get_frozen_path_test(self): - """ - Test the _get_frozen_path() function - """ - with patch('openlp.core.utils.sys') as mocked_sys: - # GIVEN: The sys module "without" a "frozen" attribute - mocked_sys.frozen = None - # WHEN: We call _get_frozen_path() with two parameters - # THEN: The non-frozen parameter is returned - assert _get_frozen_path('frozen', 'not frozen') == 'not frozen', 'Should return "not frozen"' - # GIVEN: The sys module *with* a "frozen" attribute - mocked_sys.frozen = 1 - # WHEN: We call _get_frozen_path() with two parameters - # THEN: The frozen parameter is returned - assert _get_frozen_path('frozen', 'not frozen') == 'frozen', 'Should return "frozen"' + self.assertEqual('utf-8', result, 'The result should be "utf-8"') def split_filename_with_file_path_test(self): """ @@ -72,7 +89,7 @@ class TestUtils(TestCase): result = split_filename(file_path) # THEN: A tuple should be returned. - assert result == wanted_result, 'A tuple with the directory and file name should have been returned.' + self.assertEqual(wanted_result, result, 'A tuple with the dir and file name should have been returned') def split_filename_with_dir_path_test(self): """ @@ -88,8 +105,8 @@ class TestUtils(TestCase): result = split_filename(file_path) # THEN: A tuple should be returned. - assert result == wanted_result, \ - 'A two-entry tuple with the directory and file name (empty) should have been returned.' + self.assertEqual(wanted_result, result, + 'A two-entry tuple with the directory and file name (empty) should have been returned.') def clean_filename_test(self): """ @@ -103,40 +120,24 @@ class TestUtils(TestCase): result = clean_filename(invalid_name) # THEN: The file name should be cleaned. - assert result == wanted_name, 'The file name should not contain any special characters.' + self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.') - def get_locale_key_windows_test(self): + def get_locale_key_test(self): """ Test the get_locale_key(string) function """ - with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language, \ - patch('openlp.core.utils.os') as mocked_os: + with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language: # GIVEN: The language is German # 0x00C3 (A with diaresis) should be sorted as "A". 0x00DF (sharp s) should be sorted as "ss". mocked_get_language.return_value = 'de' - mocked_os.name = 'nt' unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] - # WHEN: We sort the list and use get_locale_key() to generate the sorting keys - # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] - assert test_passes, 'Strings should be sorted properly' - def get_locale_key_linux_test(self): - - """ - Test the get_locale_key(string) function - """ - with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language, \ - patch('openlp.core.utils.os.name') as mocked_os: - # GIVEN: The language is German - # 0x00C3 (A with diaresis) should be sorted as "A". 0x00DF (sharp s) should be sorted as "ss". - mocked_get_language.return_value = 'de' - mocked_os.name = 'linux' - unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] # WHEN: We sort the list and use get_locale_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_locale_key) + # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] - assert test_passes, 'Strings should be sorted properly' + self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, + 'Strings should be sorted properly') def get_natural_key_test(self): """ @@ -146,7 +147,9 @@ class TestUtils(TestCase): # GIVEN: The language is English (a language, which sorts digits before letters) mocked_get_language.return_value = 'en' unsorted_list = ['item 10a', 'item 3b', '1st item'] + # WHEN: We sort the list and use get_natural_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_natural_key) + # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_natural_key) == ['1st item', 'item 3b', 'item 10a'] - assert test_passes, 'Numbers should be sorted naturally' + self.assertEqual(['1st item', 'item 3b', 'item 10a'], sorted_list, 'Numbers should be sorted naturally') diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/functional/openlp_plugins/bibles/test_lib.py index 942b4deee..11044475b 100644 --- a/tests/functional/openlp_plugins/bibles/test_lib.py +++ b/tests/functional/openlp_plugins/bibles/test_lib.py @@ -1,3 +1,31 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Bibles plugin. """ diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py index 372264a76..bb3179dda 100644 --- a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -1,15 +1,43 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the versereferencelist submodule of the Bibles plugin. """ from unittest import TestCase + from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList + class TestVerseReferenceList(TestCase): - def setUp(self): - """ - Initializes all we need - """ - + """ + Test the VerseReferenceList class + """ def add_first_verse_test(self): """ Test the addition of a verse to the empty list @@ -20,12 +48,12 @@ class TestVerseReferenceList(TestCase): chapter = 1 verse = 1 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' - + # WHEN: We add it to the verse list - reference_list.add(book, chapter, verse, version, copyright, permission) - + reference_list.add(book, chapter, verse, version, copyright_, permission) + # THEN: The entries should be in the first entry of the list self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') self.assertEqual(reference_list.verse_list[0]['book'], book, 'The book in first entry should be %s' % book) @@ -33,7 +61,7 @@ class TestVerseReferenceList(TestCase): self.assertEqual(reference_list.verse_list[0]['start'], verse, 'The start in first entry should be %u' % verse) self.assertEqual(reference_list.verse_list[0]['version'], version, 'The version in first entry should be %s' % version) self.assertEqual(reference_list.verse_list[0]['end'], verse, 'The end in first entry should be %u' % verse) - + def add_next_verse_test(self): """ Test the addition of the following verse @@ -44,17 +72,18 @@ class TestVerseReferenceList(TestCase): verse = 1 next_verse = 2 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' reference_list = VerseReferenceList() - reference_list.add(book, chapter, verse, version, copyright, permission) + reference_list.add(book, chapter, verse, version, copyright_, permission) # WHEN: We add the following verse to the verse list - reference_list.add(book, chapter, next_verse, version, copyright, permission) - + reference_list.add(book, chapter, next_verse, version, copyright_, permission) + # THEN: The current index should be 0 and the end pointer of the entry should be '2' self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') - self.assertEqual(reference_list.verse_list[0]['end'], next_verse, 'The end in first entry should be %u' % next_verse) + self.assertEqual(reference_list.verse_list[0]['end'], next_verse, + 'The end in first entry should be %u' % next_verse) def add_another_verse_test(self): """ @@ -64,19 +93,18 @@ class TestVerseReferenceList(TestCase): book = 'testBook' chapter = 1 verse = 1 - next_verse = 2 another_book = 'testBook2' another_chapter = 2 another_verse = 5 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' reference_list = VerseReferenceList() - reference_list.add(book, chapter, verse, version, copyright, permission) + reference_list.add(book, chapter, verse, version, copyright_, permission) # WHEN: We add a verse of another book to the verse list - reference_list.add(another_book, another_chapter, another_verse, version, copyright, permission) - + reference_list.add(another_book, another_chapter, another_verse, version, copyright_, permission) + # THEN: the current index should be 1 self.assertEqual(reference_list.current_index, 1, 'The current index should be 1') @@ -87,17 +115,18 @@ class TestVerseReferenceList(TestCase): # GIVEN: version, copyright and permission reference_list = VerseReferenceList() version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' # WHEN: a not existing version will be added - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # THEN: the data will be appended to the list self.assertEqual(len(reference_list.version_list), 1, 'The version data should be appended') - self.assertEqual(reference_list.version_list[0], {'version': version, 'copyright': copyright, 'permission': permission}, + self.assertEqual(reference_list.version_list[0], + {'version': version, 'copyright': copyright_, 'permission': permission}, 'The version data should be appended') - + def add_existing_version_test(self): """ Test the addition of an existing version to the list @@ -105,12 +134,12 @@ class TestVerseReferenceList(TestCase): # GIVEN: version, copyright and permission, added to the version list reference_list = VerseReferenceList() version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # WHEN: an existing version will be added - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # THEN: the data will not be appended to the list self.assertEqual(len(reference_list.version_list), 1, 'The version data should not be appended') diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index 4090d4e9e..f4d0dc30d 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -1,16 +1,40 @@ # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Images plugin. """ - from unittest import TestCase -from mock import MagicMock, patch - from openlp.core.lib import Registry from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.mediaitem import ImageMediaItem +from tests.functional import MagicMock, patch class TestImageMediaItem(TestCase): @@ -24,11 +48,10 @@ class TestImageMediaItem(TestCase): Registry().register('service_list', MagicMock()) Registry().register('main_window', self.mocked_main_window) Registry().register('live_controller', MagicMock()) - mocked_parent = MagicMock() mocked_plugin = MagicMock() - with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init: - mocked_init.return_value = None - self.media_item = ImageMediaItem(mocked_parent, mocked_plugin) + with patch('openlp.plugins.images.lib.mediaitem.MediaManagerItem._setup'), \ + patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.setup_item'): + self.media_item = ImageMediaItem(None, mocked_plugin) def save_new_images_list_empty_list_test(self): """ @@ -36,7 +59,7 @@ class TestImageMediaItem(TestCase): """ # GIVEN: An empty image_list image_list = [] - with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: + with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list'): self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with the empty list @@ -50,8 +73,8 @@ class TestImageMediaItem(TestCase): """ Test that the save_new_images_list() calls load_full_list() when reload_list is set to True """ - # GIVEN: A list with 1 image - image_list = [ 'test_image.jpg' ] + # GIVEN: A list with 1 image and a mocked out manager + image_list = ['test_image.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: ImageFilenames.filename = '' self.media_item.manager = MagicMock() @@ -69,8 +92,8 @@ class TestImageMediaItem(TestCase): """ Test that the save_new_images_list() doesn't call load_full_list() when reload_list is set to False """ - # GIVEN: A list with 1 image - image_list = [ 'test_image.jpg' ] + # GIVEN: A list with 1 image and a mocked out manager + image_list = ['test_image.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() @@ -126,9 +149,35 @@ class TestImageMediaItem(TestCase): self.media_item.reset_action.setVisible.assert_called_with(False) self.media_item.live_controller.display.reset_image.assert_called_with() + def recursively_delete_group_test(self): + """ + Test that recursively_delete_group() works + """ + # GIVEN: An ImageGroups object and mocked functions + with patch('openlp.core.utils.delete_file') as mocked_delete_file: + ImageFilenames.group_id = 1 + ImageGroups.parent_id = 1 + self.media_item.manager = MagicMock() + self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect + self.media_item.service_path = "" + test_group = ImageGroups() + test_group.id = 1 + + # WHEN: recursively_delete_group() is called + self.media_item.recursively_delete_group(test_group) + + # THEN: + assert mocked_delete_file.call_count == 0, 'delete_file() should not be called' + assert self.media_item.manager.delete_object.call_count == 7, \ + 'manager.delete_object() should be called exactly 7 times' + + # CLEANUP: Remove added attribute from ImageFilenames and ImageGroups + delattr(ImageFilenames, 'group_id') + delattr(ImageGroups, 'parent_id') + def _recursively_delete_group_side_effect(*args, **kwargs): """ - Side effect method that creates custom retun values for the recursively_delete_group method + Side effect method that creates custom return values for the recursively_delete_group method """ if args[1] == ImageFilenames and args[2]: # Create some fake objects that should be removed @@ -150,29 +199,3 @@ class TestImageMediaItem(TestCase): returned_object1.id = 1 return [returned_object1] return [] - - def recursively_delete_group_test(self): - """ - Test that recursively_delete_group() works - """ - # GIVEN: An ImageGroups object and mocked functions - with patch('openlp.core.utils.delete_file') as mocked_delete_file: - ImageFilenames.group_id = 1 - ImageGroups.parent_id = 1 - self.media_item.manager = MagicMock() - self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect - self.media_item.servicePath = "" - test_group = ImageGroups() - test_group.id = 1 - - # WHEN: recursively_delete_group() is called - self.media_item.recursively_delete_group(test_group) - - # THEN: - assert mocked_delete_file.call_count == 0, 'delete_file() should not be called' - assert self.media_item.manager.delete_object.call_count == 7, \ - 'manager.delete_object() should be called exactly 7 times' - - # CLEANUP: Remove added attribute from ImageFilenames and ImageGroups - delattr(ImageFilenames, 'group_id') - delattr(ImageGroups, 'parent_id') diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 95fea2bdb..5b5c99e78 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -1,17 +1,41 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Presentations plugin. """ -import os -from tempfile import mkstemp from unittest import TestCase -from mock import patch, MagicMock - from PyQt4 import QtGui from openlp.core.lib import Registry - from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem +from tests.functional import patch, MagicMock class TestMediaItem(TestCase): @@ -25,11 +49,9 @@ class TestMediaItem(TestCase): Registry.create() Registry().register('service_manager', MagicMock()) Registry().register('main_window', MagicMock()) - - with patch('openlp.plugins.presentations.lib.mediaitem.PresentationMediaItem.__init__') as mocked_init: - mocked_init.return_value = None - self.media_item = PresentationMediaItem(MagicMock(), MagicMock, MagicMock(), MagicMock()) - + with patch('openlp.plugins.presentations.lib.mediaitem.MediaManagerItem._setup'), \ + patch('openlp.plugins.presentations.lib.mediaitem.PresentationMediaItem.setup_item'): + self.media_item = PresentationMediaItem(None, MagicMock, MagicMock()) self.application = QtGui.QApplication.instance() def tearDown(self): @@ -65,7 +87,8 @@ class TestMediaItem(TestCase): mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate self.media_item.build_file_mask_string() - # THEN: The file mask should be generated. - assert self.media_item.on_new_file_masks == 'Presentations (*.odp *.ppt )', \ - 'The file mask should contain the odp and ppt extensions' - + # THEN: The file mask should be generated correctly + self.assertIn('*.odp', self.media_item.on_new_file_masks, + 'The file mask should contain the odp extension') + self.assertIn('*.ppt', self.media_item.on_new_file_masks, + 'The file mask should contain the ppt extension') diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index e683699cd..067c5cff1 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -1,18 +1,45 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Remotes plugin. """ import os import re - from unittest import TestCase from tempfile import mkstemp -from mock import patch - -from openlp.core.lib import Settings -from openlp.plugins.remotes.lib.remotetab import RemoteTab from PyQt4 import QtGui +from openlp.core.common import Settings +from openlp.plugins.remotes.lib.remotetab import RemoteTab +from tests.functional import patch + __default_settings__ = { 'remotes/twelve hour': True, 'remotes/port': 4316, @@ -23,9 +50,7 @@ __default_settings__ = { 'remotes/authentication enabled': False, 'remotes/ip address': '0.0.0.0' } - ZERO_URL = '0.0.0.0' - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) @@ -60,7 +85,8 @@ class TestRemoteTab(TestCase): # WHEN: the default ip address is given ip_address = self.form.get_ip_address(ZERO_URL) # THEN: the default ip address will be returned - self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address') + self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), + 'The return value should be a valid ip address') def get_ip_address_with_ip_test(self): """ @@ -79,10 +105,10 @@ class TestRemoteTab(TestCase): Test the set_urls function with standard defaults """ # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + with patch('openlp.core.common.Settings') as mocked_class, \ + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False @@ -96,7 +122,7 @@ class TestRemoteTab(TestCase): # THEN: the following screen values should be set self.assertEqual(self.form.address_edit.text(), ZERO_URL, 'The default URL should be set on the screen') self.assertEqual(self.form.https_settings_group_box.isEnabled(), False, - 'The Https box should not be enabled') + 'The Https box should not be enabled') self.assertEqual(self.form.https_settings_group_box.isChecked(), False, 'The Https checked box should note be Checked') self.assertEqual(self.form.user_login_group_box.isChecked(), False, @@ -107,10 +133,10 @@ class TestRemoteTab(TestCase): Test the set_urls function with certificate available """ # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + with patch('openlp.core.common.Settings') as mocked_class, \ + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 0e154a1a1..a9ba16bf8 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -1,16 +1,44 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Remotes plugin. """ import os - from unittest import TestCase from tempfile import mkstemp -from mock import MagicMock -from openlp.core.lib import Settings -from openlp.plugins.remotes.lib.httpserver import HttpRouter from PyQt4 import QtGui +from openlp.core.common import Settings +from openlp.plugins.remotes.lib.httpserver import HttpRouter +from tests.functional import MagicMock + __default_settings__ = { 'remotes/twelve hour': True, 'remotes/port': 4316, diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 3f1735a3c..b0ab77850 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -1,13 +1,39 @@ # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the EasyWorship song importer. """ import os from unittest import TestCase -from mock import patch, MagicMock + +from tests.functional import MagicMock, patch from openlp.plugins.songs.lib.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType @@ -43,6 +69,7 @@ SONG_TEST_DATA = [ 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], 'verse_order_list': []}] + class EasyWorshipSongImportLogger(EasyWorshipSongImport): """ This class logs changes in the title instance variable @@ -60,6 +87,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport): def title(self, title): self._title_assignment_list.append(title) + class TestFieldDesc: def __init__(self, name, field_type, size): self.name = name diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index 200f20aa6..05e07c4cf 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -32,9 +32,8 @@ This module contains tests for the SongShow Plus song importer. import os from unittest import TestCase -from mock import patch, MagicMock +from tests.functional import patch, MagicMock -from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter TEST_PATH = os.path.abspath( @@ -192,4 +191,4 @@ class TestFoilPresenter(TestCase): # THEN: _process_lyrics should return None and the song_import logError method should have been called once self.assertIsNone(result) self.mocked_song_import.logError.assert_called_once_with('Element Text', 'Translated String') - self.process_lyrics_patcher.start() \ No newline at end of file + self.process_lyrics_patcher.start() diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index a9e64b5c9..327bf68e8 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -1,13 +1,39 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the lib submodule of the Songs plugin. """ - from unittest import TestCase -from mock import patch, MagicMock - from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf from openlp.plugins.songs.lib.songcompare import songs_probably_equal, _remove_typos, _op_length +from tests.functional import patch, MagicMock class TestLib(TestCase): @@ -68,10 +94,10 @@ class TestLib(TestCase): # GIVEN: Two equal songs. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.full_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -82,10 +108,10 @@ class TestLib(TestCase): # GIVEN: A song and a short version of the same song. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.short_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -96,10 +122,10 @@ class TestLib(TestCase): # GIVEN: A song and the same song with lots of errors. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.error_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -110,10 +136,10 @@ class TestLib(TestCase): # GIVEN: Two different songs. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.different_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be False. assert result == False, 'The result should be False' diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 47940911d..45c62469c 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -5,13 +5,12 @@ import os from tempfile import mkstemp from unittest import TestCase -from mock import patch, MagicMock - from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, ServiceItem, Settings - +from openlp.core.common import Settings +from openlp.core.lib import Registry, ServiceItem from openlp.plugins.songs.lib.mediaitem import SongMediaItem +from tests.functional import patch, MagicMock class TestMediaItem(TestCase): @@ -25,9 +24,9 @@ class TestMediaItem(TestCase): Registry.create() Registry().register('service_list', MagicMock()) Registry().register('main_window', MagicMock()) - with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \ + with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \ patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): - self.media_item = SongMediaItem(MagicMock(), MagicMock()) + self.media_item = SongMediaItem(None, MagicMock()) fd, self.ini_file = mkstemp('.ini') Settings().set_filename(self.ini_file) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py new file mode 100644 index 000000000..37d4a1223 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +This module contains tests for the Songbeamer song importer. +""" + +import os +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'resources', 'songbeamersongs')) +SONG_TEST_DATA = {'Lobsinget dem Herrn.sng': + {'title': 'GL 1 - Lobsinget dem Herrn', + 'verses': + [('1. Lobsinget dem Herrn,\no preiset Ihn gern!\n' + 'Anbetung und Lob Ihm gebühret.\n', 'v'), + ('2. Lobsingt Seiner Lieb´,\ndie einzig ihn trieb,\n' + 'zu sterben für unsere Sünden!\n', 'v'), + ('3. Lobsingt Seiner Macht!\nSein Werk ist vollbracht:\n' + 'Er sitzet zur Rechten des Vaters.\n', 'v'), + ('4. Lobsingt seiner Treu´,\ndie immerdar neu,\n' + 'bis Er uns zur Herrlichket führet!\n\n', 'v')], + 'song_book_name': 'Glaubenslieder I', + 'song_number': "1"} + } + +class TestSongBeamerImport(TestCase): + """ + Test the functions in the :mod:`songbeamerimport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the SongBeamer file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = SongBeamerImport(mocked_manager) + + # THEN: The importer object should not be None + self.assertIsNotNone(importer, 'Import should not be none') + + def invalid_import_source_test(self): + """ + Test SongBeamerImport.doImport handles different invalid import_source values + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = SongBeamerImport(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = True + + # WHEN: Import source is not a list + for source in ['not a list', 0]: + importer.import_source = source + + # THEN: doImport should return none and the progress bar maximum should not be set. + self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list') + self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, + 'setMaxium on import_wizard.progress_bar should not have been called') + + def valid_import_source_test(self): + """ + Test SongBeamerImport.doImport handles different invalid import_source values + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = SongBeamerImport(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = True + + # WHEN: Import source is a list + importer.import_source = ['List', 'of', 'files'] + + # THEN: doImport should return none and the progress bar setMaximum should be called with the length of + # import_source. + self.assertIsNone(importer.doImport(), + 'doImport should return None when import_source is a list and stop_import_flag is True') + mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source)) + + def file_import_test(self): + """ + Test the actual import of real song files and check that the imported data is correct. + """ + + # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", + # and mocked out "author", "add_copyright", "add_verse", "finish" methods. + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + for song_file in SONG_TEST_DATA: + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_finish.return_value = True + importer = SongBeamerImport(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = False + importer.addVerse = mocked_add_verse + importer.finish = mocked_finish + + # WHEN: Importing each file + importer.import_source = [os.path.join(TEST_PATH, song_file)] + title = SONG_TEST_DATA[song_file]['title'] + add_verse_calls = SONG_TEST_DATA[song_file]['verses'] + song_book_name = SONG_TEST_DATA[song_file]['song_book_name'] + song_number = SONG_TEST_DATA[song_file]['song_number'] + + # THEN: doImport should return none, the song data should be as expected, and finish should have been + # called. + self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed') + self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title)) + for verse_text, verse_tag in add_verse_calls: + mocked_add_verse.assert_any_call(verse_text, verse_tag) + if song_book_name: + self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"' + % (song_file, song_book_name)) + if song_number: + self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s' + % (song_file, song_number)) + mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 2276784e0..aa0779f5c 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -1,55 +1,57 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the SongShow Plus song importer. """ import os from unittest import TestCase -from mock import patch, MagicMock +from tests.helpers.songfileimport import SongImportTestHelper from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport +from tests.functional import patch, MagicMock -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../resources/songshowplussongs')) -SONG_TEST_DATA = {'Amazing Grace.sbsong': - {'title': 'Amazing Grace (Demonstration)', - 'authors': ['John Newton', 'Edwin Excell', 'John P. Rees'], - 'copyright': 'Public Domain ', - 'ccli_number': 22025, - 'verses': - [('Amazing grace! How sweet the sound!\r\nThat saved a wretch like me!\r\n' - 'I once was lost, but now am found;\r\nWas blind, but now I see.', 'v1'), - ('\'Twas grace that taught my heart to fear,\r\nAnd grace my fears relieved.\r\n' - 'How precious did that grace appear,\r\nThe hour I first believed.', 'v2'), - ('The Lord has promised good to me,\r\nHis Word my hope secures.\r\n' - 'He will my shield and portion be\r\nAs long as life endures.', 'v3'), - ('Thro\' many dangers, toils and snares\r\nI have already come.\r\n' - '\'Tis grace that brought me safe thus far,\r\nAnd grace will lead me home.', 'v4'), - ('When we\'ve been there ten thousand years,\r\nBright shining as the sun,\r\n' - 'We\'ve no less days to sing God\'s praise,\r\nThan when we first begun.', 'v5')], - 'topics': ['Assurance', 'Grace', 'Praise', 'Salvation'], - 'comments': '\n\n\n', - 'song_book_name': 'Demonstration Songs', - 'song_number': 0, - 'verse_order_list': []}, - 'Beautiful Garden Of Prayer.sbsong': - {'title': 'Beautiful Garden Of Prayer (Demonstration)', - 'authors': ['Eleanor Allen Schroll', 'James H. Fillmore'], - 'copyright': 'Public Domain ', - 'ccli_number': 60252, - 'verses': - [('There\'s a garden where Jesus is waiting,\r\nThere\'s a place that is wondrously fair.\r\n' - 'For it glows with the light of His presence,\r\n\'Tis the beautiful garden of prayer.', 'v1'), - ('There\'s a garden where Jesus is waiting,\r\nAnd I go with my burden and care.\r\n' - 'Just to learn from His lips, words of comfort,\r\nIn the beautiful garden of prayer.', 'v2'), - ('There\'s a garden where Jesus is waiting,\r\nAnd He bids you to come meet Him there,\r\n' - 'Just to bow and receive a new blessing,\r\nIn the beautiful garden of prayer.', 'v3'), - ('O the beautiful garden, the garden of prayer,\r\nO the beautiful garden of prayer.\r\n' - 'There my Savior awaits, and He opens the gates\r\nTo the beautiful garden of prayer.', 'c1')], - 'topics': ['Devotion', 'Prayer'], - 'comments': '', - 'song_book_name': '', - 'song_number': 0, - 'verse_order_list': []}} +TEST_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'songshowplussongs')) + +class TestSongShowPlusFileImport(SongImportTestHelper): + def __init__(self, *args, **kwargs): + self.importer_class_name = 'SongShowPlusImport' + self.importer_module_name = 'songshowplusimport' + super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs) + + def test_song_import(self): + test_import = self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.sbsong'), + self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) + test_import = self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'), + self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) class TestSongShowPlusImport(TestCase): @@ -89,7 +91,7 @@ class TestSongShowPlusImport(TestCase): # THEN: doImport should return none and the progress bar maximum should not be set. self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list') self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, - 'setMaxium on import_wizard.progress_bar should not have been called') + 'setMaximum on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): """ @@ -166,70 +168,3 @@ class TestSongShowPlusImport(TestCase): self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % (openlp_tag, original_tag)) - - def file_import_test(self): - """ - Test the actual import of real song files and check that the imported data is correct. - """ - - # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", - # and mocked out "author", "add_copyright", "add_verse", "finish" methods. - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): - for song_file in SONG_TEST_DATA: - mocked_manager = MagicMock() - mocked_import_wizard = MagicMock() - mocked_parse_author = MagicMock() - mocked_add_copyright = MagicMock() - mocked_add_verse = MagicMock() - mocked_finish = MagicMock() - mocked_finish.return_value = True - importer = SongShowPlusImport(mocked_manager) - importer.import_wizard = mocked_import_wizard - importer.stop_import_flag = False - importer.parse_author = mocked_parse_author - importer.addCopyright = mocked_add_copyright - importer.addVerse = mocked_add_verse - importer.finish = mocked_finish - importer.topics = [] - - # WHEN: Importing each file - importer.import_source = [os.path.join(TEST_PATH, song_file)] - title = SONG_TEST_DATA[song_file]['title'] - author_calls = SONG_TEST_DATA[song_file]['authors'] - song_copyright = SONG_TEST_DATA[song_file]['copyright'] - ccli_number = SONG_TEST_DATA[song_file]['ccli_number'] - add_verse_calls = SONG_TEST_DATA[song_file]['verses'] - topics = SONG_TEST_DATA[song_file]['topics'] - comments = SONG_TEST_DATA[song_file]['comments'] - song_book_name = SONG_TEST_DATA[song_file]['song_book_name'] - song_number = SONG_TEST_DATA[song_file]['song_number'] - verse_order_list = SONG_TEST_DATA[song_file]['verse_order_list'] - - # THEN: doImport should return none, the song data should be as expected, and finish should have been - # called. - self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed') - self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title)) - for author in author_calls: - mocked_parse_author.assert_any_call(author) - if song_copyright: - mocked_add_copyright.assert_called_with(song_copyright) - if ccli_number: - self.assertEquals(importer.ccliNumber, ccli_number, 'ccliNumber for %s should be %s' - % (song_file, ccli_number)) - for verse_text, verse_tag in add_verse_calls: - mocked_add_verse.assert_any_call(verse_text, verse_tag) - if topics: - self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (song_file, topics)) - if comments: - self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' - % (song_file, comments)) - if song_book_name: - self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"' - % (song_file, song_book_name)) - if song_number: - self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s' - % (song_file, song_number)) - if verse_order_list: - self.assertEquals(importer.verseOrderList, [], 'verseOrderList for %s should be %s' - % (song_file, verse_order_list)) - mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 7c9b80056..836c5340b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -1,15 +1,45 @@ # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### """ This module contains tests for the WorshipCenter Pro song importer. """ +import os +from unittest import TestCase, SkipTest + +if os.name != 'nt': + raise SkipTest('Not Windows, skipping test') -from unittest import TestCase -from mock import patch, MagicMock import pyodbc from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport +from tests.functional import patch, MagicMock + class TestRecord(object): """ @@ -23,6 +53,7 @@ class TestRecord(object): self.Field = field self.Value = value + class WorshipCenterProImportLogger(WorshipCenterProImport): """ This class logs changes in the title instance variable @@ -189,4 +220,4 @@ class TestWorshipCenterProSongImport(TestCase): for call in verse_calls: mocked_add_verse.assert_any_call(call) self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, - 'Incorrect number of calls made to addVerse') \ No newline at end of file + 'Incorrect number of calls made to addVerse') diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py new file mode 100644 index 000000000..faf18999f --- /dev/null +++ b/tests/helpers/songfileimport.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +The :mod:`songfileimporthelper` modules provides a helper class and methods to easily enable testing the import of +song files from third party applications. +""" +import json +from unittest import TestCase + +from tests.functional import patch, MagicMock + +class SongImportTestHelper(TestCase): + """ + This class is designed to be a helper class to reduce repition when testing the import of song files. + """ + def __init__(self, *args, **kwargs): + super(SongImportTestHelper, self).__init__(*args, **kwargs) + self.importer_module = __import__( + 'openlp.plugins.songs.lib.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) + self.importer_class = getattr(self.importer_module, self.importer_class_name) + + def setUp(self): + """ + Patch and set up the mocks required. + """ + self.add_copyright_patcher = patch( + 'openlp.plugins.songs.lib.%s.%s.addCopyright' % (self.importer_module_name, self.importer_class_name)) + self.add_verse_patcher = patch( + 'openlp.plugins.songs.lib.%s.%s.addVerse' % (self.importer_module_name, self.importer_class_name)) + self.finish_patcher = patch( + 'openlp.plugins.songs.lib.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) + self.parse_author_patcher = patch( + 'openlp.plugins.songs.lib.%s.%s.parse_author' % (self.importer_module_name, self.importer_class_name)) + self.song_import_patcher = patch('openlp.plugins.songs.lib.%s.SongImport' % self.importer_module_name) + self.mocked_add_copyright = self.add_copyright_patcher.start() + self.mocked_add_verse = self.add_verse_patcher.start() + self.mocked_finish = self.finish_patcher.start() + self.mocked_parse_author = self.parse_author_patcher.start() + self.mocked_song_importer = self.song_import_patcher.start() + self.mocked_manager = MagicMock() + self.mocked_import_wizard = MagicMock() + self.mocked_finish.return_value = True + + def tearDown(self): + """ + Clean up + """ + self.add_copyright_patcher.stop() + self.add_verse_patcher.stop() + self.finish_patcher.stop() + self.parse_author_patcher.stop() + self.song_import_patcher.stop() + + def load_external_result_data(self, file_name): + """ + A method to load and return an object containing the song data from an external file. + """ + result_file = open(file_name, 'rb') + return json.loads(result_file.read().decode()) + + def file_import(self, source_file_name, result_data): + """ + Import the given file and check that it has imported correctly + """ + importer = self.importer_class(self.mocked_manager) + importer.import_wizard = self.mocked_import_wizard + importer.stop_import_flag = False + importer.topics = [] + + # WHEN: Importing the source file + importer.import_source = [source_file_name] + add_verse_calls = self._get_data(result_data, 'verses') + author_calls = self._get_data(result_data, 'authors') + ccli_number = self._get_data(result_data, 'ccli_number') + comments = self._get_data(result_data, 'comments') + song_book_name = self._get_data(result_data, 'song_book_name') + song_copyright = self._get_data(result_data, 'copyright') + song_number = self._get_data(result_data, 'song_number') + title = self._get_data(result_data, 'title') + topics = self._get_data(result_data, 'topics') + verse_order_list = self._get_data(result_data, 'verse_order_list') + + # THEN: doImport should return none, the song data should be as expected, and finish should have been + # called. + self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed') + self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title)) + for author in author_calls: + self.mocked_parse_author.assert_any_call(author) + if song_copyright: + self.mocked_add_copyright.assert_called_with(song_copyright) + if ccli_number: + self.assertEquals(importer.ccliNumber, ccli_number, 'ccliNumber for %s should be %s' + % (source_file_name, ccli_number)) + for verse_text, verse_tag in add_verse_calls: + self.mocked_add_verse.assert_any_call(verse_text, verse_tag) + if topics: + self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) + if comments: + self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' + % (source_file_name, comments)) + if song_book_name: + self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"' + % (source_file_name, song_book_name)) + if song_number: + self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s' + % (source_file_name, song_number)) + if verse_order_list: + self.assertEquals(importer.verseOrderList, [], 'verseOrderList for %s should be %s' + % (source_file_name, verse_order_list)) + self.mocked_finish.assert_called_with() + + def _get_data(self, data, key): + if key in data: + return data[key] + return '' + diff --git a/tests/interfaces/__init__.py b/tests/interfaces/__init__.py index 61c17dee3..7ba934446 100644 --- a/tests/interfaces/__init__.py +++ b/tests/interfaces/__init__.py @@ -1,3 +1,4 @@ + import sip sip.setapi('QDate', 2) sip.setapi('QDateTime', 2) @@ -7,7 +8,9 @@ sip.setapi('QTime', 2) sip.setapi('QUrl', 2) sip.setapi('QVariant', 2) -#from PyQt4 import QtGui +import sys -# Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" an QApplication. -#application = QtGui.QApplication([]) +if sys.version_info[1] >= 3: + from unittest.mock import patch, MagicMock +else: + from mock import patch, MagicMock diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index dcff55e19..e0ee8bfec 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -7,11 +7,12 @@ import shutil from tempfile import mkstemp, mkdtemp from unittest import TestCase -from mock import MagicMock from PyQt4 import QtGui +from openlp.core.common import Settings from openlp.core.lib.pluginmanager import PluginManager -from openlp.core.lib import Registry, Settings +from openlp.core.lib import Registry +from tests.interfaces import MagicMock class TestPluginManager(TestCase): diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index 2c5948705..5e151da77 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -3,10 +3,11 @@ """ from unittest import TestCase -from mock import MagicMock, patch +from PyQt4 import QtGui, QtTest + from openlp.core.lib import Registry from openlp.core.ui import filerenameform -from PyQt4 import QtGui, QtTest +from tests.interfaces import MagicMock, patch class TestStartFileRenameForm(TestCase): diff --git a/tests/interfaces/openlp_core_ui/test_listpreviewwidget.py b/tests/interfaces/openlp_core_ui/test_listpreviewwidget.py index 4d7044424..49d376797 100644 --- a/tests/interfaces/openlp_core_ui/test_listpreviewwidget.py +++ b/tests/interfaces/openlp_core_ui/test_listpreviewwidget.py @@ -3,12 +3,12 @@ """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtGui from openlp.core.lib import Registry, ServiceItem from openlp.core.ui import listpreviewwidget +from tests.interfaces import MagicMock, patch from tests.utils.osdinteraction import read_service_from_file diff --git a/tests/interfaces/openlp_core_ui/test_mainwindow.py b/tests/interfaces/openlp_core_ui/test_mainwindow.py index 299f5db6d..0a5c77361 100644 --- a/tests/interfaces/openlp_core_ui/test_mainwindow.py +++ b/tests/interfaces/openlp_core_ui/test_mainwindow.py @@ -2,12 +2,12 @@ Package to test the openlp.core.ui.mainwindow package. """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtGui from openlp.core.lib import Registry from openlp.core.ui.mainwindow import MainWindow +from tests.interfaces import MagicMock, patch class TestMainWindow(TestCase): diff --git a/tests/interfaces/openlp_core_ui/test_servicemanager.py b/tests/interfaces/openlp_core_ui/test_servicemanager.py index dbd49102c..e8b6df94f 100644 --- a/tests/interfaces/openlp_core_ui/test_servicemanager.py +++ b/tests/interfaces/openlp_core_ui/test_servicemanager.py @@ -3,12 +3,12 @@ """ from unittest import TestCase -from mock import MagicMock, Mock, patch from PyQt4 import QtGui from openlp.core.lib import Registry, ScreenList, ServiceItem from openlp.core.ui.mainwindow import MainWindow +from tests.interfaces import MagicMock, patch class TestServiceManager(TestCase): @@ -41,7 +41,7 @@ class TestServiceManager(TestCase): # WHEN I have an empty display # THEN the count of items should be zero self.assertEqual(self.service_manager.service_manager_list.topLevelItemCount(), 0, - 'The service manager list should be empty ') + 'The service manager list should be empty ') def context_menu_test(self): """ @@ -49,8 +49,8 @@ class TestServiceManager(TestCase): """ # GIVEN: A service item added with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \ - patch('PyQt4.QtGui.QWidget.mapToGlobal') as mocked_map_to_global, \ - patch('PyQt4.QtGui.QMenu.exec_') as mocked_exec: + patch('PyQt4.QtGui.QWidget.mapToGlobal'), \ + patch('PyQt4.QtGui.QMenu.exec_'): mocked_item = MagicMock() mocked_item.parent.return_value = None mocked_item_at_method.return_value = mocked_item @@ -61,12 +61,12 @@ class TestServiceManager(TestCase): self.service_manager.service_items = [{'service_item': service_item}] q_point = None # Mocked actions. - self.service_manager.edit_action.setVisible = Mock() - self.service_manager.create_custom_action.setVisible = Mock() - self.service_manager.maintain_action.setVisible = Mock() - self.service_manager.notes_action.setVisible = Mock() - self.service_manager.time_action.setVisible = Mock() - self.service_manager.auto_start_action.setVisible = Mock() + self.service_manager.edit_action.setVisible = MagicMock() + self.service_manager.create_custom_action.setVisible = MagicMock() + self.service_manager.maintain_action.setVisible = MagicMock() + self.service_manager.notes_action.setVisible = MagicMock() + self.service_manager.time_action.setVisible = MagicMock() + self.service_manager.auto_start_action.setVisible = MagicMock() # WHEN: Show the context menu. self.service_manager.context_menu(q_point) diff --git a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py index 64e7ca722..336c94655 100644 --- a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py +++ b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py @@ -2,12 +2,12 @@ Package to test the openlp.core.ui package. """ from unittest import TestCase -from mock import patch from PyQt4 import QtCore, QtGui, QtTest from openlp.core.lib import Registry from openlp.core.ui import servicenoteform +from tests.interfaces import patch class TestStartNoteDialog(TestCase): diff --git a/tests/interfaces/openlp_core_ui/test_settings_form.py b/tests/interfaces/openlp_core_ui/test_settings_form.py index 7dd368e84..b9f032182 100644 --- a/tests/interfaces/openlp_core_ui/test_settings_form.py +++ b/tests/interfaces/openlp_core_ui/test_settings_form.py @@ -3,12 +3,11 @@ Package to test the openlp.core.lib.settingsform package. """ from unittest import TestCase -from mock import MagicMock, patch - from PyQt4 import QtCore, QtTest, QtGui from openlp.core.ui import settingsform from openlp.core.lib import Registry, ScreenList +from tests.interfaces import MagicMock, patch SCREEN = { @@ -168,4 +167,4 @@ class TestSettingsForm(TestCase): # THEN: Images_regenerate should have been added. assert self.dummy1.call_count == 1, 'dummy1 should have been called once' assert self.dummy2.call_count == 1, 'dummy2 should have been called once' - assert self.dummy3.call_count == 1, 'dummy3 should have been called once' \ No newline at end of file + assert self.dummy3.call_count == 1, 'dummy3 should have been called once' diff --git a/tests/interfaces/openlp_core_ui/test_starttimedialog.py b/tests/interfaces/openlp_core_ui/test_starttimedialog.py index 6542cc02e..de3d87910 100644 --- a/tests/interfaces/openlp_core_ui/test_starttimedialog.py +++ b/tests/interfaces/openlp_core_ui/test_starttimedialog.py @@ -2,13 +2,12 @@ Package to test the openlp.core.ui package. """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtCore, QtGui, QtTest from openlp.core.lib import Registry from openlp.core.ui import starttimeform - +from tests.interfaces import MagicMock, patch class TestStartTimeDialog(TestCase): diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index bd645f1ff..54ad3bec1 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -1,12 +1,11 @@ """ Package to test the openlp.plugin.bible.lib.https package. """ - from unittest import TestCase -from mock import MagicMock from openlp.core.lib import Registry from openlp.plugins.bibles.lib.http import BGExtract, CWExtract +from tests.interfaces import MagicMock class TestBibleHTTP(TestCase): diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py index 61dbb822c..ffa3ec884 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py @@ -2,7 +2,6 @@ Module to test the EditCustomForm. """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtGui, QtTest, QtCore @@ -10,6 +9,7 @@ from openlp.core.lib import Registry # Import needed due to import problems. from openlp.plugins.custom.lib.mediaitem import CustomMediaItem from openlp.plugins.custom.forms.editcustomform import EditCustomForm +from tests.interfaces import MagicMock, patch class TestEditCustomForm(TestCase): diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py index 0ed70ab62..406b7e8a4 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py @@ -2,12 +2,12 @@ Module to test the EditCustomSlideForm. """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtGui from openlp.core.lib import Registry from openlp.plugins.custom.forms.editcustomslideform import EditCustomSlideForm +from tests.interfaces import MagicMock, patch class TestEditCustomSlideForm(TestCase): diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py b/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py index 8ed953048..75907e3c8 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py @@ -1,13 +1,13 @@ """ Package to test the openlp.plugins.songs.forms.editsongform package. """ -from mock import MagicMock from unittest import TestCase from PyQt4 import QtGui from openlp.core.lib import Registry from openlp.plugins.songs.forms.editsongform import EditSongForm +from tests.interfaces import MagicMock class TestEditSongForm(TestCase): @@ -55,9 +55,9 @@ class TestEditSongForm(TestCase): self.form.verse_list_widget.rowCount = MagicMock(return_value=2) # Mock out the verse. first_verse = MagicMock() - first_verse.data = MagicMock(return_value='V1') + first_verse.data.return_value = 'V1' second_verse = MagicMock() - second_verse.data = MagicMock(return_value= 'V2') + second_verse.data.return_value = 'V2' self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse]) self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split()) @@ -76,9 +76,9 @@ class TestEditSongForm(TestCase): self.form.verse_list_widget.rowCount = MagicMock(return_value=2) # Mock out the verse. first_verse = MagicMock() - first_verse.data = MagicMock(return_value='V1') + first_verse.data.return_value = 'V1' second_verse = MagicMock() - second_verse.data = MagicMock(return_value= 'V2') + second_verse.data.return_value = 'V2' self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse]) self.form._extract_verse_order = MagicMock(return_value=[given_verse_order]) @@ -98,7 +98,7 @@ class TestEditSongForm(TestCase): self.form.verse_list_widget.rowCount = MagicMock(return_value=1) # Mock out the verse. (We want a verse type to be returned). mocked_verse = MagicMock() - mocked_verse.data = MagicMock(return_value='V1') + mocked_verse.data.return_value = 'V1' self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse) self.form._extract_verse_order = MagicMock(return_value=[]) self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order) diff --git a/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng b/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng new file mode 100644 index 000000000..fbc9aa9fc --- /dev/null +++ b/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng @@ -0,0 +1,25 @@ +#LangCount=1 +#Title=GL 1 - Lobsinget dem Herrn +#Editor=SongBeamer 4.20 +#Version=3 +#Format=F/K// +#TitleFormat=U +#ChurchSongID=0001 +#Songbook=Glaubenslieder I / 1 +--- +1. Lobsinget dem Herrn, +o preiset Ihn gern! +Anbetung und Lob Ihm gebühret. + --- +2. Lobsingt Seiner Lieb´, +die einzig ihn trieb, +zu sterben für unsere Sünden! + --- +3. Lobsingt Seiner Macht! +Sein Werk ist vollbracht: +Er sitzet zur Rechten des Vaters. + --- +4. Lobsingt seiner Treu´, +die immerdar neu, +bis Er uns zur Herrlichket führet! + diff --git a/tests/resources/songshowplussongs/Amazing Grace.json b/tests/resources/songshowplussongs/Amazing Grace.json new file mode 100644 index 000000000..878132881 --- /dev/null +++ b/tests/resources/songshowplussongs/Amazing Grace.json @@ -0,0 +1,42 @@ +{ + "authors": [ + "John Newton", + "Edwin Excell", + "John P. Rees" + ], + "ccli_number": 22025, + "comments": "\n\n\n", + "copyright": "Public Domain ", + "song_book_name": "Demonstration Songs", + "song_number": 0, + "title": "Amazing Grace (Demonstration)", + "topics": [ + "Assurance", + "Grace", + "Praise", + "Salvation" + ], + "verse_order_list": [], + "verses": [ + [ + "Amazing grace! How sweet the sound!\r\nThat saved a wretch like me!\r\nI once was lost, but now am found;\r\nWas blind, but now I see.", + "v1" + ], + [ + "'Twas grace that taught my heart to fear,\r\nAnd grace my fears relieved.\r\nHow precious did that grace appear,\r\nThe hour I first believed.", + "v2" + ], + [ + "The Lord has promised good to me,\r\nHis Word my hope secures.\r\nHe will my shield and portion be\r\nAs long as life endures.", + "v3" + ], + [ + "Thro' many dangers, toils and snares\r\nI have already come.\r\n'Tis grace that brought me safe thus far,\r\nAnd grace will lead me home.", + "v4" + ], + [ + "When we've been there ten thousand years,\r\nBright shining as the sun,\r\nWe've no less days to sing God's praise,\r\nThan when we first begun.", + "v5" + ] + ] +} \ No newline at end of file diff --git a/tests/resources/songshowplussongs/Beautiful Garden Of Prayer.json b/tests/resources/songshowplussongs/Beautiful Garden Of Prayer.json new file mode 100644 index 000000000..651af9100 --- /dev/null +++ b/tests/resources/songshowplussongs/Beautiful Garden Of Prayer.json @@ -0,0 +1,35 @@ +{ + "authors": [ + "Eleanor Allen Schroll", + "James H. Fillmore" + ], + "ccli_number": 60252, + "comments": "", + "copyright": "Public Domain ", + "song_book_name": "", + "song_number": 0, + "title": "Beautiful Garden Of Prayer (Demonstration)", + "topics": [ + "Devotion", + "Prayer" + ], + "verse_order_list": [], + "verses": [ + [ + "There's a garden where Jesus is waiting,\r\nThere's a place that is wondrously fair.\r\nFor it glows with the light of His presence,\r\n'Tis the beautiful garden of prayer.", + "v1" + ], + [ + "There's a garden where Jesus is waiting,\r\nAnd I go with my burden and care.\r\nJust to learn from His lips, words of comfort,\r\nIn the beautiful garden of prayer.", + "v2" + ], + [ + "There's a garden where Jesus is waiting,\r\nAnd He bids you to come meet Him there,\r\nJust to bow and receive a new blessing,\r\nIn the beautiful garden of prayer.", + "v3" + ], + [ + "O the beautiful garden, the garden of prayer,\r\nO the beautiful garden of prayer.\r\nThere my Savior awaits, and He opens the gates\r\nTo the beautiful garden of prayer.", + "c1" + ] + ] +} \ No newline at end of file diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index e69de29bb..d983f1b6e 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# 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, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 json + + +def assert_length(expected, iterable, msg=None): + if len(iterable) != expected: + if not msg: + msg = 'Expected length %s, got %s' % (expected, len(iterable)) + raise AssertionError(msg) + + +def convert_file_service_item(test_path, name, row=0): + service_file = os.path.join(test_path, name) + open_file = open(service_file, 'r') + try: + items = json.load(open_file) + first_line = items[row] + except IOError: + first_line = '' + finally: + open_file.close() + return first_line +