forked from openlp/openlp
Merge trunk
This commit is contained in:
commit
1804b690bd
@ -5,6 +5,8 @@ recursive-include openlp *.html
|
|||||||
recursive-include openlp *.js
|
recursive-include openlp *.js
|
||||||
recursive-include openlp *.css
|
recursive-include openlp *.css
|
||||||
recursive-include openlp *.png
|
recursive-include openlp *.png
|
||||||
|
recursive-include openlp *.ps
|
||||||
|
recursive-include openlp *.json
|
||||||
recursive-include documentation *
|
recursive-include documentation *
|
||||||
recursive-include resources *
|
recursive-include resources *
|
||||||
recursive-include scripts *
|
recursive-include scripts *
|
||||||
|
@ -320,14 +320,14 @@ class Ui_MainWindow(object):
|
|||||||
# i18n add Language Actions
|
# i18n add Language Actions
|
||||||
add_actions(self.settings_language_menu, (self.auto_language_item, None))
|
add_actions(self.settings_language_menu, (self.auto_language_item, None))
|
||||||
add_actions(self.settings_language_menu, self.language_group.actions())
|
add_actions(self.settings_language_menu, self.language_group.actions())
|
||||||
# Order things differently in OS X so that Preferences menu item in the
|
# Qt on OS X looks for keywords in the menu items title to determine which menu items get added to the main
|
||||||
# app menu is correct (this gets picked up automatically by Qt).
|
# menu. If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the
|
||||||
|
# main menu need to be marked as such with QAction.NoRole.
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
|
self.settings_shortcuts_item.setMenuRole(QtGui.QAction.NoRole)
|
||||||
None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item))
|
self.formatting_tag_item.setMenuRole(QtGui.QAction.NoRole)
|
||||||
else:
|
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
|
||||||
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
|
None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
|
||||||
None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
|
|
||||||
add_actions(self.tools_menu, (self.tools_add_tool_item, None))
|
add_actions(self.tools_menu, (self.tools_add_tool_item, None))
|
||||||
add_actions(self.tools_menu, (self.tools_open_data_folder, None))
|
add_actions(self.tools_menu, (self.tools_open_data_folder, None))
|
||||||
add_actions(self.tools_menu, (self.tools_first_time_wizard, None))
|
add_actions(self.tools_menu, (self.tools_first_time_wizard, None))
|
||||||
|
@ -34,6 +34,7 @@ from distutils.version import LooseVersion
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
|
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
@ -207,7 +208,7 @@ class VlcPlayer(MediaPlayer):
|
|||||||
start_time = 0
|
start_time = 0
|
||||||
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
|
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
|
||||||
start_time = controller.media_info.start_time
|
start_time = controller.media_info.start_time
|
||||||
display.vlc_media_player.play()
|
threading.Thread(target=display.vlc_media_player.play).start()
|
||||||
if not self.media_state_wait(display, vlc.State.Playing):
|
if not self.media_state_wait(display, vlc.State.Playing):
|
||||||
return False
|
return False
|
||||||
self.volume(display, controller.media_info.volume)
|
self.volume(display, controller.media_info.volume)
|
||||||
@ -233,7 +234,7 @@ class VlcPlayer(MediaPlayer):
|
|||||||
"""
|
"""
|
||||||
Stop the current item
|
Stop the current item
|
||||||
"""
|
"""
|
||||||
display.vlc_media_player.stop()
|
threading.Thread(target=display.vlc_media_player.stop).start()
|
||||||
self.state = MediaState.Stopped
|
self.state = MediaState.Stopped
|
||||||
|
|
||||||
def volume(self, display, vol):
|
def volume(self, display, vol):
|
||||||
|
@ -353,7 +353,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = create_thumb(imageFile.filename, thumb)
|
icon = create_thumb(imageFile.filename, thumb)
|
||||||
item_name = QtGui.QTreeWidgetItem(filename)
|
item_name = QtGui.QTreeWidgetItem([filename])
|
||||||
item_name.setText(0, filename)
|
item_name.setText(0, filename)
|
||||||
item_name.setIcon(0, icon)
|
item_name.setIcon(0, icon)
|
||||||
item_name.setToolTip(0, imageFile.filename)
|
item_name.setToolTip(0, imageFile.filename)
|
||||||
|
@ -229,7 +229,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||||
check_directory_exists(self.service_path)
|
check_directory_exists(self.service_path)
|
||||||
self.load_list(Settings().value(self.settings_section + '/media files'))
|
self.load_list(Settings().value(self.settings_section + '/media files'))
|
||||||
self.populate_display_types()
|
self.rebuild_players()
|
||||||
|
|
||||||
def rebuild_players(self):
|
def rebuild_players(self):
|
||||||
"""
|
"""
|
||||||
|
@ -74,6 +74,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
super(EasyWorshipSongImport, self).__init__(manager, **kwargs)
|
super(EasyWorshipSongImport, self).__init__(manager, **kwargs)
|
||||||
|
self.entry_error_log = ''
|
||||||
|
|
||||||
def do_import(self):
|
def do_import(self):
|
||||||
"""
|
"""
|
||||||
@ -183,7 +184,12 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
self.set_song_import_object(authors, inflated_content)
|
self.set_song_import_object(authors, inflated_content)
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
break
|
break
|
||||||
if not self.finish():
|
if self.entry_error_log:
|
||||||
|
self.log_error(self.import_source,
|
||||||
|
translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s')
|
||||||
|
% (self.title, self.entry_error_log))
|
||||||
|
self.entry_error_log = ''
|
||||||
|
elif not self.finish():
|
||||||
self.log_error(self.import_source)
|
self.log_error(self.import_source)
|
||||||
# Set file_pos for next entry
|
# Set file_pos for next entry
|
||||||
file_pos += entry_length
|
file_pos += entry_length
|
||||||
@ -281,7 +287,7 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
raw_record = db_file.read(record_size)
|
raw_record = db_file.read(record_size)
|
||||||
self.fields = self.record_structure.unpack(raw_record)
|
self.fields = self.record_structure.unpack(raw_record)
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
self.title = self.get_field(fi_title).decode()
|
self.title = self.get_field(fi_title).decode('unicode-escape')
|
||||||
# Get remaining fields.
|
# Get remaining fields.
|
||||||
copy = self.get_field(fi_copy)
|
copy = self.get_field(fi_copy)
|
||||||
admin = self.get_field(fi_admin)
|
admin = self.get_field(fi_admin)
|
||||||
@ -289,23 +295,28 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
authors = self.get_field(fi_author)
|
authors = self.get_field(fi_author)
|
||||||
words = self.get_field(fi_words)
|
words = self.get_field(fi_words)
|
||||||
if copy:
|
if copy:
|
||||||
self.copyright = copy.decode()
|
self.copyright = copy.decode('unicode-escape')
|
||||||
if admin:
|
if admin:
|
||||||
if copy:
|
if copy:
|
||||||
self.copyright += ', '
|
self.copyright += ', '
|
||||||
self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
|
self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
'Administered by %s') % admin.decode()
|
'Administered by %s') % admin.decode('unicode-escape')
|
||||||
if ccli:
|
if ccli:
|
||||||
self.ccli_number = ccli.decode()
|
self.ccli_number = ccli.decode('unicode-escape')
|
||||||
if authors:
|
if authors:
|
||||||
authors = authors.decode()
|
authors = authors.decode('unicode-escape')
|
||||||
else:
|
else:
|
||||||
authors = ''
|
authors = ''
|
||||||
# Set the SongImport object members.
|
# Set the SongImport object members.
|
||||||
self.set_song_import_object(authors, words)
|
self.set_song_import_object(authors, words)
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
break
|
break
|
||||||
if not self.finish():
|
if self.entry_error_log:
|
||||||
|
self.log_error(self.import_source,
|
||||||
|
translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s')
|
||||||
|
% (self.title, self.entry_error_log))
|
||||||
|
self.entry_error_log = ''
|
||||||
|
elif not self.finish():
|
||||||
self.log_error(self.import_source)
|
self.log_error(self.import_source)
|
||||||
db_file.close()
|
db_file.close()
|
||||||
self.memo_file.close()
|
self.memo_file.close()
|
||||||
@ -328,8 +339,19 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
self.add_author(author_name.strip())
|
self.add_author(author_name.strip())
|
||||||
if words:
|
if words:
|
||||||
# Format the lyrics
|
# Format the lyrics
|
||||||
result = strip_rtf(words.decode(), self.encoding)
|
result = None
|
||||||
|
decoded_words = None
|
||||||
|
try:
|
||||||
|
decoded_words = words.decode()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
# The unicode chars in the rtf was not escaped in the expected manor
|
||||||
|
self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
|
'Unexpected data formatting.')
|
||||||
|
return
|
||||||
|
result = strip_rtf(decoded_words, self.encoding)
|
||||||
if result is None:
|
if result is None:
|
||||||
|
self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport',
|
||||||
|
'No song text found.')
|
||||||
return
|
return
|
||||||
words, self.encoding = result
|
words, self.encoding = result
|
||||||
verse_type = VerseType.tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
|
@ -36,8 +36,8 @@ from PyQt4 import QtCore, QtGui
|
|||||||
from sqlalchemy.sql import or_
|
from sqlalchemy.sql import or_
|
||||||
|
|
||||||
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
|
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
|
||||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, check_item_selected, \
|
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItem, ServiceItemContext, \
|
||||||
create_separated_list
|
check_item_selected, create_separated_list
|
||||||
from openlp.core.lib.ui import create_widget_action
|
from openlp.core.lib.ui import create_widget_action
|
||||||
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
||||||
from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
|
from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
|
||||||
@ -124,7 +124,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
log.debug('config_updated')
|
log.debug('config_updated')
|
||||||
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
|
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
|
||||||
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
|
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
|
||||||
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',)
|
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service')
|
||||||
|
self.display_songbook = Settings().value(self.settings_section + '/display songbook')
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.search_text_label.setText('%s:' % UiStrings().Search)
|
self.search_text_label.setText('%s:' % UiStrings().Search)
|
||||||
@ -506,6 +507,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation],
|
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation],
|
||||||
create_separated_list(authors_translation)))
|
create_separated_list(authors_translation)))
|
||||||
item.raw_footer.append(song.copyright)
|
item.raw_footer.append(song.copyright)
|
||||||
|
if self.display_songbook and song.book:
|
||||||
|
item.raw_footer.append("%s #%s" % (song.book.name, song.song_number))
|
||||||
if Settings().value('core/ccli number'):
|
if Settings().value('core/ccli number'):
|
||||||
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
||||||
'CCLI License: ') + Settings().value('core/ccli number'))
|
'CCLI License: ') + Settings().value('core/ccli number'))
|
||||||
|
@ -121,7 +121,7 @@ class SongShowPlusImport(SongImport):
|
|||||||
null, verse_no, = struct.unpack("BB", song_data.read(2))
|
null, verse_no, = struct.unpack("BB", song_data.read(2))
|
||||||
elif block_key == CUSTOM_VERSE:
|
elif block_key == CUSTOM_VERSE:
|
||||||
null, verse_name_length, = struct.unpack("BB", song_data.read(2))
|
null, verse_name_length, = struct.unpack("BB", song_data.read(2))
|
||||||
verse_name = song_data.read(verse_name_length)
|
verse_name = self.decode(song_data.read(verse_name_length))
|
||||||
length_descriptor_size, = struct.unpack("B", song_data.read(1))
|
length_descriptor_size, = struct.unpack("B", song_data.read(1))
|
||||||
log.debug(length_descriptor_size)
|
log.debug(length_descriptor_size)
|
||||||
# Detect if/how long the length descriptor is
|
# Detect if/how long the length descriptor is
|
||||||
@ -147,7 +147,12 @@ class SongShowPlusImport(SongImport):
|
|||||||
elif block_key == COPYRIGHT:
|
elif block_key == COPYRIGHT:
|
||||||
self.add_copyright(self.decode(data))
|
self.add_copyright(self.decode(data))
|
||||||
elif block_key == CCLI_NO:
|
elif block_key == CCLI_NO:
|
||||||
self.ccli_number = int(data)
|
# Try to get the CCLI number even if the field contains additional text
|
||||||
|
match = re.search(r'\d+', self.decode(data))
|
||||||
|
if match:
|
||||||
|
self.ccli_number = int(match.group())
|
||||||
|
else:
|
||||||
|
log.warn("Can't parse CCLI Number from string: %s" % self.decode(data))
|
||||||
elif block_key == VERSE:
|
elif block_key == VERSE:
|
||||||
self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no))
|
self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no))
|
||||||
elif block_key == CHORUS:
|
elif block_key == CHORUS:
|
||||||
|
@ -59,6 +59,9 @@ class SongsTab(SettingsTab):
|
|||||||
self.add_from_service_check_box = QtGui.QCheckBox(self.mode_group_box)
|
self.add_from_service_check_box = QtGui.QCheckBox(self.mode_group_box)
|
||||||
self.add_from_service_check_box.setObjectName('add_from_service_check_box')
|
self.add_from_service_check_box.setObjectName('add_from_service_check_box')
|
||||||
self.mode_layout.addWidget(self.add_from_service_check_box)
|
self.mode_layout.addWidget(self.add_from_service_check_box)
|
||||||
|
self.display_songbook_check_box = QtGui.QCheckBox(self.mode_group_box)
|
||||||
|
self.display_songbook_check_box.setObjectName('songbook_check_box')
|
||||||
|
self.mode_layout.addWidget(self.display_songbook_check_box)
|
||||||
self.left_layout.addWidget(self.mode_group_box)
|
self.left_layout.addWidget(self.mode_group_box)
|
||||||
self.left_layout.addStretch()
|
self.left_layout.addStretch()
|
||||||
self.right_layout.addStretch()
|
self.right_layout.addStretch()
|
||||||
@ -66,6 +69,7 @@ class SongsTab(SettingsTab):
|
|||||||
self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed)
|
self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed)
|
||||||
self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed)
|
self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed)
|
||||||
self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
|
self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
|
||||||
|
self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode'))
|
self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode'))
|
||||||
@ -75,6 +79,7 @@ class SongsTab(SettingsTab):
|
|||||||
self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit'))
|
self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit'))
|
||||||
self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab',
|
self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab',
|
||||||
'Import missing songs from service files'))
|
'Import missing songs from service files'))
|
||||||
|
self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer'))
|
||||||
|
|
||||||
def on_search_as_type_check_box_changed(self, check_state):
|
def on_search_as_type_check_box_changed(self, check_state):
|
||||||
self.song_search = (check_state == QtCore.Qt.Checked)
|
self.song_search = (check_state == QtCore.Qt.Checked)
|
||||||
@ -88,6 +93,9 @@ class SongsTab(SettingsTab):
|
|||||||
def on_add_from_service_check_box_changed(self, check_state):
|
def on_add_from_service_check_box_changed(self, check_state):
|
||||||
self.update_load = (check_state == QtCore.Qt.Checked)
|
self.update_load = (check_state == QtCore.Qt.Checked)
|
||||||
|
|
||||||
|
def on_songbook_check_box_changed(self, check_state):
|
||||||
|
self.display_songbook = (check_state == QtCore.Qt.Checked)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
settings.beginGroup(self.settings_section)
|
settings.beginGroup(self.settings_section)
|
||||||
@ -95,10 +103,12 @@ class SongsTab(SettingsTab):
|
|||||||
self.tool_bar = settings.value('display songbar')
|
self.tool_bar = settings.value('display songbar')
|
||||||
self.update_edit = settings.value('update service on edit')
|
self.update_edit = settings.value('update service on edit')
|
||||||
self.update_load = settings.value('add song from service')
|
self.update_load = settings.value('add song from service')
|
||||||
|
self.display_songbook = settings.value('display songbook')
|
||||||
self.search_as_type_check_box.setChecked(self.song_search)
|
self.search_as_type_check_box.setChecked(self.song_search)
|
||||||
self.tool_bar_active_check_box.setChecked(self.tool_bar)
|
self.tool_bar_active_check_box.setChecked(self.tool_bar)
|
||||||
self.update_on_edit_check_box.setChecked(self.update_edit)
|
self.update_on_edit_check_box.setChecked(self.update_edit)
|
||||||
self.add_from_service_check_box.setChecked(self.update_load)
|
self.add_from_service_check_box.setChecked(self.update_load)
|
||||||
|
self.display_songbook_check_box.setChecked(self.display_songbook)
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
@ -108,6 +118,7 @@ class SongsTab(SettingsTab):
|
|||||||
settings.setValue('display songbar', self.tool_bar)
|
settings.setValue('display songbar', self.tool_bar)
|
||||||
settings.setValue('update service on edit', self.update_edit)
|
settings.setValue('update service on edit', self.update_edit)
|
||||||
settings.setValue('add song from service', self.update_load)
|
settings.setValue('add song from service', self.update_load)
|
||||||
|
settings.setValue('display songbook', self.display_songbook)
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
if self.tab_visited:
|
if self.tab_visited:
|
||||||
self.settings_form.register_post_process('songs_config_updated')
|
self.settings_form.register_post_process('songs_config_updated')
|
||||||
|
@ -63,6 +63,7 @@ __default_settings__ = {
|
|||||||
'songs/search as type': False,
|
'songs/search as type': False,
|
||||||
'songs/add song from service': True,
|
'songs/add song from service': True,
|
||||||
'songs/display songbar': True,
|
'songs/display songbar': True,
|
||||||
|
'songs/display songbook': False,
|
||||||
'songs/last directory import': '',
|
'songs/last directory import': '',
|
||||||
'songs/last directory export': '',
|
'songs/last directory export': '',
|
||||||
'songs/songselect username': '',
|
'songs/songselect username': '',
|
||||||
|
@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin):
|
|||||||
self.lock.release()
|
self.lock.release()
|
||||||
# The sleep time is adjusted in the test case.
|
# The sleep time is adjusted in the test case.
|
||||||
time.sleep(self.sleep_time)
|
time.sleep(self.sleep_time)
|
||||||
return ''
|
return ''
|
||||||
|
@ -31,12 +31,12 @@ Package to test the openlp.core.ui.firsttimeform package.
|
|||||||
"""
|
"""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from tests.functional import MagicMock
|
|
||||||
|
|
||||||
from tests.helpers.testmixin import TestMixin
|
|
||||||
from openlp.core.common import Registry
|
from openlp.core.common import Registry
|
||||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||||
|
|
||||||
|
from tests.functional import MagicMock
|
||||||
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
|
|
||||||
class TestFirstTimeForm(TestCase, TestMixin):
|
class TestFirstTimeForm(TestCase, TestMixin):
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import os
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.ui.mainwindow import MainWindow
|
from openlp.core.ui.mainwindow import MainWindow
|
||||||
|
from openlp.core.lib.ui import UiStrings
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
@ -95,3 +96,41 @@ class TestMainWindow(TestCase, TestMixin):
|
|||||||
|
|
||||||
# THEN the file should not be opened
|
# THEN the file should not be opened
|
||||||
assert not mocked_load_path.called, 'load_path should not have been called'
|
assert not mocked_load_path.called, 'load_path should not have been called'
|
||||||
|
|
||||||
|
def main_window_title_test(self):
|
||||||
|
"""
|
||||||
|
Test that running a new instance of OpenLP set the window title correctly
|
||||||
|
"""
|
||||||
|
# GIVEN a newly opened OpenLP instance
|
||||||
|
|
||||||
|
# WHEN no changes are made to the service
|
||||||
|
|
||||||
|
# THEN the main window's title shoud be the same as the OLPV2x string in the UiStrings class
|
||||||
|
self.assertEqual(self.main_window.windowTitle(), UiStrings().OLPV2x,
|
||||||
|
'The main window\'s title should be the same as the OLPV2x string in UiStrings class')
|
||||||
|
|
||||||
|
def set_service_modifed_test(self):
|
||||||
|
"""
|
||||||
|
Test that when setting the service's title the main window's title is set correctly
|
||||||
|
"""
|
||||||
|
# GIVEN a newly opened OpenLP instance
|
||||||
|
|
||||||
|
# WHEN set_service_modified is called with with the modified flag set true and a file name
|
||||||
|
self.main_window.set_service_modified(True, 'test.osz')
|
||||||
|
|
||||||
|
# THEN the main window's title should be set to the
|
||||||
|
self.assertEqual(self.main_window.windowTitle(), '%s - %s*' % (UiStrings().OLPV2x, 'test.osz'),
|
||||||
|
'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - test.osz*"')
|
||||||
|
|
||||||
|
def set_service_unmodified_test(self):
|
||||||
|
"""
|
||||||
|
Test that when setting the service's title the main window's title is set correctly
|
||||||
|
"""
|
||||||
|
# GIVEN a newly opened OpenLP instance
|
||||||
|
|
||||||
|
# WHEN set_service_modified is called with with the modified flag set False and a file name
|
||||||
|
self.main_window.set_service_modified(False, 'test.osz')
|
||||||
|
|
||||||
|
# THEN the main window's title should be set to the
|
||||||
|
self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'),
|
||||||
|
'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - test.osz"')
|
||||||
|
@ -125,4 +125,4 @@ class TestMedia(TestCase, TestMixin):
|
|||||||
|
|
||||||
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
||||||
self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct')
|
self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct')
|
||||||
self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players')
|
self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players')
|
||||||
|
@ -33,7 +33,7 @@ from unittest import TestCase
|
|||||||
|
|
||||||
from openlp.core.ui import SlideController
|
from openlp.core.ui import SlideController
|
||||||
|
|
||||||
from tests.interfaces import MagicMock, patch
|
from tests.interfaces import MagicMock
|
||||||
|
|
||||||
|
|
||||||
class TestSlideController(TestCase):
|
class TestSlideController(TestCase):
|
||||||
|
@ -32,7 +32,7 @@ This module contains tests for the lib submodule of the Remotes plugin.
|
|||||||
import os
|
import os
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.common import Settings
|
from openlp.core.common import Settings, Registry
|
||||||
from openlp.plugins.remotes.lib.httpserver import HttpRouter
|
from openlp.plugins.remotes.lib.httpserver import HttpRouter
|
||||||
from tests.functional import MagicMock, patch, mock_open
|
from tests.functional import MagicMock, patch, mock_open
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
@ -92,15 +92,14 @@ class TestRouter(TestCase, TestMixin):
|
|||||||
Test the router control functionality
|
Test the router control functionality
|
||||||
"""
|
"""
|
||||||
# GIVEN: A testing set of Routes
|
# GIVEN: A testing set of Routes
|
||||||
router = HttpRouter()
|
|
||||||
mocked_function = MagicMock()
|
mocked_function = MagicMock()
|
||||||
test_route = [
|
test_route = [
|
||||||
(r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}),
|
(r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}),
|
||||||
]
|
]
|
||||||
router.routes = test_route
|
self.router.routes = test_route
|
||||||
|
|
||||||
# WHEN: called with a poll route
|
# WHEN: called with a poll route
|
||||||
function, args = router.process_http_request('/stage/api/poll', None)
|
function, args = self.router.process_http_request('/stage/api/poll', None)
|
||||||
|
|
||||||
# THEN: the function should have been called only once
|
# THEN: the function should have been called only once
|
||||||
self.assertEqual(mocked_function, function['function'], 'The mocked function should match defined value.')
|
self.assertEqual(mocked_function, function['function'], 'The mocked function should match defined value.')
|
||||||
@ -126,6 +125,25 @@ class TestRouter(TestCase, TestMixin):
|
|||||||
# THEN: all types should match
|
# THEN: all types should match
|
||||||
self.assertEqual(content_type, header[1], 'Mismatch of content type')
|
self.assertEqual(content_type, header[1], 'Mismatch of content type')
|
||||||
|
|
||||||
|
def main_poll_test(self):
|
||||||
|
"""
|
||||||
|
Test the main poll logic
|
||||||
|
"""
|
||||||
|
# GIVEN: a defined router with two slides
|
||||||
|
Registry().register('live_controller', MagicMock)
|
||||||
|
router = HttpRouter()
|
||||||
|
router.send_response = MagicMock()
|
||||||
|
router.send_header = MagicMock()
|
||||||
|
router.end_headers = MagicMock()
|
||||||
|
router.live_controller.slide_count = 2
|
||||||
|
|
||||||
|
# WHEN: main poll called
|
||||||
|
results = router.main_poll()
|
||||||
|
|
||||||
|
# THEN: the correct response should be returned
|
||||||
|
self.assertEqual(results.decode('utf-8'), '{"results": {"slide_count": 2}}',
|
||||||
|
'The resulting json strings should match')
|
||||||
|
|
||||||
def serve_file_without_params_test(self):
|
def serve_file_without_params_test(self):
|
||||||
"""
|
"""
|
||||||
Test the serve_file method without params
|
Test the serve_file method without params
|
||||||
|
@ -67,7 +67,21 @@ SONG_TEST_DATA = [
|
|||||||
'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', 'v2'),
|
'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', 'v2'),
|
||||||
('There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n'
|
('There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n'
|
||||||
'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')],
|
'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')],
|
||||||
'verse_order_list': []}]
|
'verse_order_list': []},
|
||||||
|
{'title': 'Vi pløjed og vi så\'de',
|
||||||
|
'authors': ['Matthias Claudius'],
|
||||||
|
'copyright': 'Public Domain',
|
||||||
|
'ccli_number': 0,
|
||||||
|
'verses':
|
||||||
|
[('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n'
|
||||||
|
'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.',
|
||||||
|
'v1'),
|
||||||
|
('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'),
|
||||||
|
('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n'
|
||||||
|
'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'),
|
||||||
|
('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n'
|
||||||
|
'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', 'v3')],
|
||||||
|
'verse_order_list': []}]
|
||||||
|
|
||||||
EWS_SONG_TEST_DATA =\
|
EWS_SONG_TEST_DATA =\
|
||||||
{'title': 'Vi pløjed og vi så\'de',
|
{'title': 'Vi pløjed og vi så\'de',
|
||||||
@ -139,6 +153,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
"""
|
"""
|
||||||
Test the functions in the :mod:`ewimport` module.
|
Test the functions in the :mod:`ewimport` module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def create_field_desc_entry_test(self):
|
def create_field_desc_entry_test(self):
|
||||||
"""
|
"""
|
||||||
Test creating an instance of the :class`FieldDescEntry` class.
|
Test creating an instance of the :class`FieldDescEntry` class.
|
||||||
@ -467,3 +482,22 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']:
|
for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']:
|
||||||
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
mocked_finish.assert_called_with()
|
mocked_finish.assert_called_with()
|
||||||
|
|
||||||
|
def import_rtf_unescaped_unicode_test(self):
|
||||||
|
"""
|
||||||
|
Test import of rtf without the expected escaping of unicode
|
||||||
|
"""
|
||||||
|
|
||||||
|
# GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method.
|
||||||
|
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
mocked_add_author = MagicMock()
|
||||||
|
importer = EasyWorshipSongImportLogger(mocked_manager)
|
||||||
|
importer.add_author = mocked_add_author
|
||||||
|
importer.encoding = 'cp1252'
|
||||||
|
|
||||||
|
# WHEN: running set_song_import_object on a verse string without the needed escaping
|
||||||
|
importer.set_song_import_object('Test Author', b'Det som var fr\x86n begynnelsen')
|
||||||
|
|
||||||
|
# THEN: The import should fail
|
||||||
|
self.assertEquals(importer.entry_error_log, 'Unexpected data formatting.', 'Import should fail')
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
This module contains tests for the lib submodule of the Songs plugin.
|
This module contains tests for the lib submodule of the Songs plugin.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
from tempfile import mkstemp
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
@ -29,6 +27,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
|
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
|
||||||
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
|
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
|
||||||
self.media_item = SongMediaItem(None, MagicMock())
|
self.media_item = SongMediaItem(None, MagicMock())
|
||||||
|
self.media_item.display_songbook = False
|
||||||
self.get_application()
|
self.get_application()
|
||||||
self.build_settings()
|
self.build_settings()
|
||||||
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
|
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
|
||||||
@ -129,6 +128,32 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'],
|
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'],
|
||||||
'The array should be returned correctly with a song, an author, copyright and amended ccli')
|
'The array should be returned correctly with a song, an author, copyright and amended ccli')
|
||||||
|
|
||||||
|
def build_song_footer_base_songbook_test(self):
|
||||||
|
"""
|
||||||
|
Test build songs footer with basic song and a songbook
|
||||||
|
"""
|
||||||
|
# GIVEN: A Song and a Service Item
|
||||||
|
mock_song = MagicMock()
|
||||||
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.copyright = 'My copyright'
|
||||||
|
mock_song.book = MagicMock()
|
||||||
|
mock_song.book.name = "My songbook"
|
||||||
|
mock_song.song_number = 12
|
||||||
|
service_item = ServiceItem(None)
|
||||||
|
|
||||||
|
# WHEN: I generate the Footer with default settings
|
||||||
|
self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
|
# THEN: The songbook should not be in the footer
|
||||||
|
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright'])
|
||||||
|
|
||||||
|
# WHEN: I activate the "display songbook" option
|
||||||
|
self.media_item.display_songbook = True
|
||||||
|
self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
|
# THEN: The songbook should be in the footer
|
||||||
|
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12'])
|
||||||
|
|
||||||
def match_authors_test(self):
|
def match_authors_test(self):
|
||||||
"""
|
"""
|
||||||
Test the author matching when importing a song from a service
|
Test the author matching when importing a song from a service
|
||||||
|
@ -57,6 +57,8 @@ class TestSongShowPlusFileImport(SongImportTestHelper):
|
|||||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
|
||||||
self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'),
|
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')))
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
|
||||||
|
self.file_import(os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong'),
|
||||||
|
self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json')))
|
||||||
|
|
||||||
|
|
||||||
class TestSongShowPlusImport(TestCase):
|
class TestSongShowPlusImport(TestCase):
|
||||||
|
@ -62,4 +62,4 @@ class TestHistoryComboBox(TestCase, TestMixin):
|
|||||||
self.combo.addItem('test2')
|
self.combo.addItem('test2')
|
||||||
|
|
||||||
# THEN: The list of items should contain both strings.
|
# THEN: The list of items should contain both strings.
|
||||||
self.assertEqual(self.combo.getItems(), ['test1', 'test2'])
|
self.assertEqual(self.combo.getItems(), ['test1', 'test2'])
|
||||||
|
@ -75,4 +75,4 @@ class TestShortcutform(TestCase, TestMixin):
|
|||||||
# THEN: The button should be changed.
|
# THEN: The button should be changed.
|
||||||
self.assertEqual(button.text(), text, "The text should match.")
|
self.assertEqual(button.text(), text, "The text should match.")
|
||||||
mocked_check_method.assert_called_once_with(True)
|
mocked_check_method.assert_called_once_with(True)
|
||||||
self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.")
|
self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.")
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"Martin Luther"
|
||||||
|
],
|
||||||
|
"ccli_number": 12456,
|
||||||
|
"comments": "",
|
||||||
|
"copyright": "Public Domain",
|
||||||
|
"song_number": 0,
|
||||||
|
"title": "A Mighty Fortress is our God",
|
||||||
|
"topics": [],
|
||||||
|
"verse_order_list": [],
|
||||||
|
"verses": [
|
||||||
|
[
|
||||||
|
"A mighty fortress is our God, a bulwark never failing;\r\nOur helper He, amid the flood of mortal ills prevailing:\r\nFor still our ancient foe doth seek to work us woe;\r\nHis craft and power are great, and, armed with cruel hate,\r\nOn earth is not his equal.\r\n",
|
||||||
|
"v1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Did we in our own strength confide, our striving would be losing;\r\nWere not the right Man on our side, the Man of Godâs own choosing:\r\nDost ask who that may be? Christ Jesus, it is He;\r\nLord Sabaoth, His Name, from age to age the same,\r\nAnd He must win the battle.\r\n",
|
||||||
|
"v2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"And though this world, with devils filled, should threaten to undo us,\r\nWe will not fear, for God hath willed His truth to triumph through us:\r\nThe Prince of Darkness grim, we tremble not for him;\r\nHis rage we can endure, for lo, his doom is sure,\r\nOne little word shall fell him.\r\n",
|
||||||
|
"v3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"That word above all earthly powers, no thanks to them, abideth;\r\nThe Spirit and the gifts are ours through Him Who with us sideth:\r\nLet goods and kindred go, this mortal life also;\r\nThe body they may kill: Godâs truth abideth still,\r\nHis kingdom is forever.\r\n",
|
||||||
|
"v4"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user