diff --git a/openlp/core/api/endpoint/controller.py b/openlp/core/api/endpoint/controller.py
index 4e897f370..3523568aa 100644
--- a/openlp/core/api/endpoint/controller.py
+++ b/openlp/core/api/endpoint/controller.py
@@ -91,6 +91,7 @@ def controller_text(request):
item['text'] = str(frame['title'])
item['html'] = str(frame['title'])
item['selected'] = (live_controller.selected_row == index)
+ item['title'] = current_item.title
data.append(item)
json_data = {'results': {'slides': data}}
if current_item:
@@ -117,12 +118,11 @@ def controller_set(request):
return {'results': {'success': True}}
-@controller_endpoint.route('{action:next|previous}')
+@controller_endpoint.route('{controller}/{action:next|previous}')
@requires_auth
def controller_direction(request, controller, action):
"""
Handles requests for setting service items in the slide controller
-11
:param request: The http request object.
:param controller: the controller slides forward or backward.
:param action: the controller slides forward or backward.
@@ -137,7 +137,7 @@ def controller_direction(request, controller, action):
def controller_direction_api(request, controller, action):
"""
Handles requests for setting service items in the slide controller
-11
+
:param request: The http request object.
:param controller: the controller slides forward or backward.
:param action: the controller slides forward or backward.
diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py
index d1f62fb8b..8d939a97f 100644
--- a/openlp/core/common/__init__.py
+++ b/openlp/core/common/__init__.py
@@ -62,7 +62,7 @@ def get_local_ip4():
"""
# Get the local IPv4 active address(es) that are NOT localhost (lo or '127.0.0.1')
log.debug('Getting local IPv4 interface(es) information')
- MY_IP4 = {}
+ my_ip4 = {}
for iface in QNetworkInterface.allInterfaces():
if not iface.isValid() or not (iface.flags() & (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)):
continue
@@ -70,25 +70,25 @@ def get_local_ip4():
ip = address.ip()
# NOTE: Next line will skip if interface is localhost - keep for now until we decide about it later
# if (ip.protocol() == QAbstractSocket.IPv4Protocol) and (ip != QHostAddress.LocalHost):
- if (ip.protocol() == QAbstractSocket.IPv4Protocol):
- MY_IP4[iface.name()] = {'ip': ip.toString(),
+ if ip.protocol() == QAbstractSocket.IPv4Protocol:
+ my_ip4[iface.name()] = {'ip': ip.toString(),
'broadcast': address.broadcast().toString(),
'netmask': address.netmask().toString(),
'prefix': address.prefixLength(),
'localnet': QHostAddress(address.netmask().toIPv4Address() &
- ip.toIPv4Address()).toString()
+ ip.toIPv4Address()).toString()
}
log.debug('Adding {iface} to active list'.format(iface=iface.name()))
- if len(MY_IP4) == 1:
- if 'lo' in MY_IP4:
+ if len(my_ip4) == 1:
+ if 'lo' in my_ip4:
# No active interfaces - so leave localhost in there
log.warning('No active IPv4 interfaces found except localhost')
else:
# Since we have a valid IP4 interface, remove localhost
log.debug('Found at least one IPv4 interface, removing localhost')
- MY_IP4.pop('lo')
+ my_ip4.pop('lo')
- return MY_IP4
+ return my_ip4
def trace_error_handler(logger):
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index 94a0e19f1..570c6d843 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -121,6 +121,9 @@ class ItemCapabilities(object):
``HasThumbnails``
The item has related thumbnails available
+
+ ``HasMetaData``
+ The item has Meta Data about item
"""
CanPreview = 1
CanEdit = 2
@@ -143,6 +146,7 @@ class ItemCapabilities(object):
HasDisplayTitle = 19
HasNotes = 20
HasThumbnails = 21
+ HasMetaData = 22
class ServiceItem(RegistryProperties):
@@ -200,6 +204,7 @@ class ServiceItem(RegistryProperties):
self.will_auto_start = False
self.has_original_files = True
self._new_item()
+ self.metadata = []
def _new_item(self):
"""
@@ -375,7 +380,8 @@ class ServiceItem(RegistryProperties):
'background_audio': self.background_audio,
'theme_overwritten': self.theme_overwritten,
'will_auto_start': self.will_auto_start,
- 'processor': self.processor
+ 'processor': self.processor,
+ 'metadata': self.metadata
}
service_data = []
if self.service_item_type == ServiceItemType.Text:
@@ -426,6 +432,7 @@ class ServiceItem(RegistryProperties):
self.will_auto_start = header.get('will_auto_start', False)
self.processor = header.get('processor', None)
self.has_original_files = True
+ self.metadata = header.get('item_meta_data', [])
if 'background_audio' in header:
self.background_audio = []
for file_path in header['background_audio']:
diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py
index faa481455..1d8ce5f01 100644
--- a/openlp/core/ui/firsttimewizard.py
+++ b/openlp/core/ui/firsttimewizard.py
@@ -127,9 +127,6 @@ class UiFirstTimeWizard(object):
self.media_check_box.setChecked(True)
self.media_check_box.setObjectName('media_check_box')
self.plugin_layout.addWidget(self.media_check_box)
- self.remote_check_box = QtWidgets.QCheckBox(self.plugin_page)
- self.remote_check_box.setObjectName('remote_check_box')
- self.plugin_layout.addWidget(self.remote_check_box)
self.song_usage_check_box = QtWidgets.QCheckBox(self.plugin_page)
self.song_usage_check_box.setChecked(True)
self.song_usage_check_box.setObjectName('song_usage_check_box')
@@ -138,13 +135,6 @@ class UiFirstTimeWizard(object):
self.alert_check_box.setChecked(True)
self.alert_check_box.setObjectName('alert_check_box')
self.plugin_layout.addWidget(self.alert_check_box)
- self.projectors_check_box = QtWidgets.QCheckBox(self.plugin_page)
- # If visibility setting for projector panel is True, check the box.
- if Settings().value('projector/show after wizard'):
- self.projectors_check_box.setChecked(True)
- self.projectors_check_box.setObjectName('projectors_check_box')
- self.projectors_check_box.clicked.connect(self.on_projectors_check_box_clicked)
- self.plugin_layout.addWidget(self.projectors_check_box)
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
# The song samples page
self.songs_page = QtWidgets.QWizardPage()
@@ -256,13 +246,9 @@ class UiFirstTimeWizard(object):
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard',
'Presentations – Show .ppt, .odp and .pdf files'))
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media – Playback of Audio and Video files'))
- self.remote_check_box.setText(str(UiStrings().WebDownloadText))
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor'))
self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard',
'Alerts – Display informative messages while showing other slides'))
- self.projectors_check_box.setText(translate('OpenLP.FirstTimeWizard',
- 'Projector Controller – Control PJLink compatible projects on your'
- ' network from OpenLP'))
self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
self.no_internet_page.setSubTitle(
translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py
index ed593dd88..1368be39d 100644
--- a/openlp/core/ui/media/mediacontroller.py
+++ b/openlp/core/ui/media/mediacontroller.py
@@ -179,7 +179,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
Check to see if we have any media Player's available.
"""
- log.debug('_check_available_media_players')
controller_dir = os.path.join('core', 'ui', 'media')
# Find all files that do not begin with '.' (lp:#1738047) and end with player.py
glob_pattern = os.path.join(controller_dir, '[!.]*player.py')
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 5969f7bda..2e5d29e4d 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -751,7 +751,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def context_menu(self, point):
"""
- The Right click context menu from the Serviceitem list
+ The Right click context menu from the Service item list
:param point: The location of the cursor.
"""
@@ -1136,7 +1136,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_delete_from_service(self):
"""
Remove the current ServiceItem from the list.
- :param field:
"""
item = self.find_service_item()[0]
if item != -1:
@@ -1205,6 +1204,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
tips.append('{text1}: {text2}'.format(text1=text1, text2=text2))
if item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
tips.append(item['service_item'].get_media_time())
+ if item['service_item'].is_capable(ItemCapabilities.HasMetaData):
+ for meta in item['service_item'].metadata:
+ tips.append(meta)
tree_widget_item.setToolTip(0, '
'.join(tips))
tree_widget_item.setData(0, QtCore.Qt.UserRole, item['order'])
tree_widget_item.setSelected(item['selected'])
@@ -1362,7 +1364,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def make_preview(self):
"""
Send the current item to the Preview slide controller
- :param field:
"""
self.application.set_busy_cursor()
item, child = self.find_service_item()
@@ -1387,7 +1388,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_double_click_live(self):
"""
Send the current item to the Live slide controller but triggered by a tablewidget click event.
- :param field:
"""
self.list_double_clicked = True
self.make_live()
@@ -1396,7 +1396,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
"""
If single click previewing is enabled, and triggered by a tablewidget click event,
start a timeout to verify a double-click hasn't triggered.
- :param field:
"""
if Settings().value('advanced/single click service preview'):
if not self.list_double_clicked:
@@ -1407,7 +1406,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_single_click_preview_timeout(self):
"""
If a single click ok, but double click not triggered, send the current item to the Preview slide controller.
- :param field:
"""
if self.list_double_clicked:
# If a double click has registered, clear it.
@@ -1447,7 +1445,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def remote_edit(self):
"""
Triggers a remote edit to a plugin to allow item to be edited.
- :param field:
"""
item = self.find_service_item()[0]
if self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEdit):
@@ -1459,8 +1456,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_service_item_rename(self):
"""
Opens a dialog to rename the service item.
-
- :param field: Not used, but PyQt needs this.
"""
item = self.find_service_item()[0]
if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle):
@@ -1477,7 +1472,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def create_custom(self):
"""
Saves the current text item as a custom slide
- :param field:
"""
item = self.find_service_item()[0]
Registry().execute('custom_create_from_service', self.service_items[item]['service_item'])
@@ -1597,8 +1591,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_theme_change_action(self):
"""
Handles theme change events
-
- :param field:
"""
theme = self.sender().objectName()
# No object name means that the "Default" theme is supposed to be used.
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 68c1b2e53..581e01253 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -318,6 +318,10 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
tooltip=translate('OpenLP.SlideController',
'Edit and reload song preview.'),
triggers=self.on_edit_song)
+ self.toolbar.add_toolbar_action('clear', icon=':/general/general_delete.png',
+ tooltip=translate('OpenLP.SlideController',
+ 'Clear'),
+ triggers=self.on_clear)
self.controller_layout.addWidget(self.toolbar)
# Build the Media Toolbar
self.media_controller.register_controller(self)
@@ -356,7 +360,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.audio_time_label.setObjectName('audio_time_label')
self.toolbar.add_toolbar_widget(self.audio_time_label)
self.toolbar.set_widget_visible(AUDIO_LIST, False)
- self.toolbar.set_widget_visible(['song_menu'], False)
+ self.toolbar.set_widget_visible('song_menu', False)
# Screen preview area
self.preview_frame = QtWidgets.QFrame(self.splitter)
self.preview_frame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio))
@@ -427,7 +431,8 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.__add_actions_to_widget(self.controller)
else:
self.preview_widget.doubleClicked.connect(self.on_preview_double_click)
- self.toolbar.set_widget_visible(['editSong'], False)
+ self.toolbar.set_widget_visible('editSong', False)
+ self.toolbar.set_widget_visible('clear', False)
self.controller.addActions([self.next_item, self.previous_item])
Registry().register_function('slidecontroller_{text}_stop_loop'.format(text=self.type_prefix),
self.on_stop_loop)
@@ -726,7 +731,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
self.mediabar.hide()
self.song_menu.hide()
self.toolbar.set_widget_visible(LOOP_LIST, False)
- self.toolbar.set_widget_visible(['song_menu'], False)
+ self.toolbar.set_widget_visible('song_menu', False)
# Reset the button
self.play_slides_once.setChecked(False)
self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))
@@ -737,7 +742,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
if item.is_text():
if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and
not self.song_menu.menu().isEmpty()):
- self.toolbar.set_widget_visible(['song_menu'], True)
+ self.toolbar.set_widget_visible('song_menu', True)
if item.is_capable(ItemCapabilities.CanLoop) and len(item.get_frames()) > 1:
self.toolbar.set_widget_visible(LOOP_LIST)
if item.is_media():
@@ -762,9 +767,10 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
# See bug #791050
self.toolbar.hide()
self.mediabar.hide()
- self.toolbar.set_widget_visible(['editSong'], False)
+ self.toolbar.set_widget_visible('editSong', False)
+ self.toolbar.set_widget_visible('clear', True)
if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin:
- self.toolbar.set_widget_visible(['editSong'])
+ self.toolbar.set_widget_visible('editSong')
elif item.is_media():
self.mediabar.show()
self.previous_item.setVisible(not item.is_media())
@@ -1381,6 +1387,14 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
if new_item:
self.add_service_item(new_item)
+ def on_clear(self):
+ """
+ Clear the preview bar.
+ """
+ self.preview_widget.clear_list()
+ self.toolbar.set_widget_visible('editSong', False)
+ self.toolbar.set_widget_visible('clear', False)
+
def on_preview_add_to_service(self):
"""
From the preview display request the Item to be added to service
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index 4a544b11d..a1b1d7a55 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -428,8 +428,8 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
self.log_exception('Export Theme Failed')
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
translate('OpenLP.ThemeManager',
- 'The theme_name export failed because this error occurred: {err}')
- .format(err=ose.strerror))
+ 'The {theme_name} export failed because this error occurred: {err}')
+ .format(theme_name=theme_name, err=ose.strerror))
if theme_path.exists():
theme_path.rmtree(ignore_errors=True)
return False
diff --git a/openlp/core/widgets/toolbar.py b/openlp/core/widgets/toolbar.py
index 758a62605..e0ba4e301 100644
--- a/openlp/core/widgets/toolbar.py
+++ b/openlp/core/widgets/toolbar.py
@@ -67,14 +67,20 @@ class OpenLPToolbar(QtWidgets.QToolBar):
"""
Set the visibility for a widget or a list of widgets.
- :param widgets: A list of string with widget object names.
+ :param widgets: A list of strings or individual string with widget object names.
:param visible: The new state as bool.
"""
- for handle in widgets:
- if handle in self.actions:
- self.actions[handle].setVisible(visible)
+ if isinstance(widgets, list):
+ for handle in widgets:
+ if handle in self.actions:
+ self.actions[handle].setVisible(visible)
+ else:
+ log.warning('No handle "%s" in actions list.', str(handle))
+ else:
+ if widgets in self.actions:
+ self.actions[widgets].setVisible(visible)
else:
- log.warning('No handle "%s" in actions list.', str(handle))
+ log.warning('No handle "%s" in actions list.', str(widgets))
def set_widget_enabled(self, widgets, enabled=True):
"""
diff --git a/openlp/core/widgets/views.py b/openlp/core/widgets/views.py
index 5adec3e27..bcd96edb9 100644
--- a/openlp/core/widgets/views.py
+++ b/openlp/core/widgets/views.py
@@ -146,6 +146,14 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
self.screen_ratio = screen_ratio
self.__recalculate_layout()
+ def clear_list(self):
+ """
+ Clear the preview list
+ :return:
+ """
+ self.setRowCount(0)
+ self.clear()
+
def replace_service_item(self, service_item, width, slide_number):
"""
Replace the current preview items with the ones in service_item and display the given slide
@@ -156,8 +164,7 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
"""
self.service_item = service_item
self.setRowCount(0)
- self.clear()
- self.setColumnWidth(0, width)
+ self.clear_list()
row = 0
text = []
for frame_number, frame in enumerate(self.service_item.get_frames()):
diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py
index d4e7f8cfd..95076221a 100644
--- a/openlp/plugins/custom/lib/mediaitem.py
+++ b/openlp/plugins/custom/lib/mediaitem.py
@@ -30,6 +30,7 @@ from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus, \
check_item_selected
+from openlp.core.lib.ui import create_widget_action
from openlp.plugins.custom.forms.editcustomform import EditCustomForm
from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder
from openlp.plugins.custom.lib.db import CustomSlide
@@ -84,6 +85,12 @@ class CustomMediaItem(MediaManagerItem):
Registry().register_function('custom_preview', self.on_preview_click)
Registry().register_function('custom_create_from_service', self.create_from_service_item)
+ def add_custom_context_actions(self):
+ create_widget_action(self.list_view, separator=True)
+ create_widget_action(
+ self.list_view, text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
+ triggers=self.on_clone_click)
+
def config_update(self):
"""
Config has been updated so reload values
@@ -243,6 +250,23 @@ class CustomMediaItem(MediaManagerItem):
service_item.raw_footer.append('')
return True
+ def on_clone_click(self):
+ """
+ Clone the selected Custom item
+ """
+ item = self.list_view.currentItem()
+ item_id = item.data(QtCore.Qt.UserRole)
+ old_custom_slide = self.plugin.db_manager.get_object(CustomSlide, item_id)
+ new_custom_slide = CustomSlide()
+ new_custom_slide.title = '{title} <{text}>'.format(title=old_custom_slide.title,
+ text=translate('CustomPlugin.MediaItem',
+ 'copy', 'For item cloning'))
+ new_custom_slide.text = old_custom_slide.text
+ new_custom_slide.credits = old_custom_slide.credits
+ new_custom_slide.theme_name = old_custom_slide.theme_name
+ self.plugin.db_manager.save_object(new_custom_slide)
+ self.on_search_text_button_clicked()
+
def on_search_text_button_clicked(self):
"""
Search the plugin database
diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py
index bf78d8d55..fef160010 100644
--- a/openlp/plugins/songs/lib/db.py
+++ b/openlp/plugins/songs/lib/db.py
@@ -164,7 +164,7 @@ class Song(BaseModel):
"""
Add a Songbook Entry to the song if it not yet exists
- :param songbook_name: Name of the Songbook.
+ :param songbook: Name of the Songbook.
:param entry: Entry in the Songbook (usually a number)
"""
for songbook_entry in self.songbook_entries:
diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py
index a99dd0a13..b3888fceb 100644
--- a/openlp/plugins/songs/lib/mediaitem.py
+++ b/openlp/plugins/songs/lib/mediaitem.py
@@ -572,10 +572,19 @@ class SongMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem)
service_item.add_capability(ItemCapabilities.CanSoftBreak)
+ service_item.add_capability(ItemCapabilities.HasMetaData)
song = self.plugin.manager.get_object(Song, item_id)
service_item.theme = song.theme_name
service_item.edit_id = item_id
verse_list = SongXML().get_verses(song.lyrics)
+ if Settings().value('songs/add songbook slide') and song.songbook_entries:
+ first_slide = '\n'
+ for songbook_entry in song.songbook_entries:
+ first_slide = first_slide + '{book}/{num}/{pub}\n\n'.format(book=songbook_entry.songbook.name,
+ num=songbook_entry.entry,
+ pub=songbook_entry.songbook.publisher)
+
+ service_item.add_from_text(first_slide, 'O1')
# no verse list or only 1 space (in error)
verse_tags_translated = False
if VerseType.from_translated_string(str(verse_list[0][0]['type'])) is not None:
@@ -622,6 +631,9 @@ class SongMediaItem(MediaManagerItem):
if song.media_files:
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
service_item.background_audio = [m.file_path for m in song.media_files]
+ item.metadata.append('{label}: {media}'.
+ format(label=translate('SongsPlugin.MediaItem', 'Media'),
+ media=service_item.background_audio))
return True
def generate_footer(self, item, song):
@@ -685,6 +697,23 @@ class SongMediaItem(MediaManagerItem):
if Settings().value('core/ccli number'):
item.raw_footer.append(translate('SongsPlugin.MediaItem',
'CCLI License: ') + Settings().value('core/ccli number'))
+ item.metadata.append('{label}: {title}'.format(label=translate('SongsPlugin.MediaItem', 'Title'),
+ title=song.title))
+ if song.alternate_title:
+ item.metadata.append('{label}: {title}'.
+ format(label=translate('SongsPlugin.MediaItem', 'Alt Title'),
+ title=song.alternate_title))
+ if song.songbook_entries:
+ for songbook_entry in song.songbook_entries:
+ item.metadata.append('{label}: {book}/{num}/{pub}'.
+ format(label=translate('SongsPlugin.MediaItem', 'Songbook'),
+ book=songbook_entry.songbook.name,
+ num=songbook_entry.entry,
+ pub=songbook_entry.songbook.publisher))
+ if song.topics:
+ for topics in song.topics:
+ item.metadata.append('{label}: {topic}'.
+ format(label=translate('SongsPlugin.MediaItem', 'Topic'), topic=topics.name))
return authors_all
def service_load(self, item):
diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py
index 173957b82..37335f16f 100644
--- a/openlp/plugins/songs/lib/songstab.py
+++ b/openlp/plugins/songs/lib/songstab.py
@@ -51,6 +51,9 @@ class SongsTab(SettingsTab):
self.add_from_service_check_box = QtWidgets.QCheckBox(self.mode_group_box)
self.add_from_service_check_box.setObjectName('add_from_service_check_box')
self.mode_layout.addWidget(self.add_from_service_check_box)
+ self.songbook_slide_check_box = QtWidgets.QCheckBox(self.mode_group_box)
+ self.songbook_slide_check_box.setObjectName('songbook_slide_check_box')
+ self.mode_layout.addWidget(self.songbook_slide_check_box)
self.display_songbook_check_box = QtWidgets.QCheckBox(self.mode_group_box)
self.display_songbook_check_box.setObjectName('songbook_check_box')
self.mode_layout.addWidget(self.display_songbook_check_box)
@@ -95,6 +98,7 @@ class SongsTab(SettingsTab):
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.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
+ self.songbook_slide_check_box.stateChanged.connect(self.on_songbook_slide_check_box_changed)
self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed)
self.display_written_by_check_box.stateChanged.connect(self.on_written_by_check_box_changed)
self.display_copyright_check_box.stateChanged.connect(self.on_copyright_check_box_changed)
@@ -111,6 +115,8 @@ class SongsTab(SettingsTab):
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',
'Import missing songs from Service files'))
+ self.songbook_slide_check_box.setText(translate('SongsPlugin.SongsTab',
+ 'Add Songbooks as first side'))
self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer'))
self.display_written_by_check_box.setText(translate(
'SongsPlugin.SongsTab', 'Show "Written by:" in footer for unspecified authors'))
@@ -141,6 +147,9 @@ class SongsTab(SettingsTab):
def on_add_from_service_check_box_changed(self, check_state):
self.update_load = (check_state == QtCore.Qt.Checked)
+ def on_songbook_slide_check_box_changed(self, check_state):
+ self.songbook_slide = (check_state == QtCore.Qt.Checked)
+
def on_songbook_check_box_changed(self, check_state):
self.display_songbook = (check_state == QtCore.Qt.Checked)
@@ -171,6 +180,7 @@ class SongsTab(SettingsTab):
self.tool_bar = settings.value('display songbar')
self.update_edit = settings.value('update service on edit')
self.update_load = settings.value('add song from service')
+ self.songbook_slide = settings.value('add songbook slide')
self.display_songbook = settings.value('display songbook')
self.display_written_by = settings.value('display written by')
self.display_copyright_symbol = settings.value('display copyright symbol')
@@ -208,6 +218,7 @@ class SongsTab(SettingsTab):
settings.setValue('mainview chords', self.mainview_chords)
settings.setValue('disable chords import', self.disable_chords_import)
settings.setValue('chord notation', self.chord_notation)
+ settings.setValue('add songbook slide', self.songbook_slide)
settings.endGroup()
if self.tab_visited:
self.settings_form.register_post_process('songs_config_updated')
diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py
index e8fff3f2a..31f239063 100644
--- a/openlp/plugins/songs/songsplugin.py
+++ b/openlp/plugins/songs/songsplugin.py
@@ -61,6 +61,7 @@ __default_settings__ = {
'songs/last import type': SongFormat.OpenLyrics,
'songs/update service on edit': False,
'songs/add song from service': True,
+ 'songs/add songbook slide': False,
'songs/display songbar': True,
'songs/display songbook': False,
'songs/display written by': True,
diff --git a/scripts/websocket_client.py b/scripts/websocket_client.py
index 03a208a6f..753825edf 100755
--- a/scripts/websocket_client.py
+++ b/scripts/websocket_client.py
@@ -25,6 +25,7 @@ import asyncio
import websockets
import random
+
async def tester():
async with websockets.connect('ws://localhost:4317/poll') as websocket:
diff --git a/tests/functional/openlp_core/api/endpoint/test_controller.py b/tests/functional/openlp_core/api/endpoint/test_controller.py
index 08b1f5d69..90aba6e03 100644
--- a/tests/functional/openlp_core/api/endpoint/test_controller.py
+++ b/tests/functional/openlp_core/api/endpoint/test_controller.py
@@ -20,10 +20,10 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from unittest import TestCase
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock
from openlp.core.common.registry import Registry
-from openlp.core.api.endpoint.controller import controller_text
+from openlp.core.api.endpoint.controller import controller_text, controller_direction
class TestController(TestCase):
@@ -42,7 +42,7 @@ class TestController(TestCase):
def test_controller_text(self):
"""
- Remote Deploy tests - test the dummy zip file is processed correctly
+ Remote API Tests : test the controller text method can be called
"""
# GIVEN: A mocked service with a dummy service item
self.mocked_live_controller.service_item = MagicMock()
@@ -52,3 +52,25 @@ class TestController(TestCase):
results = ret['results']
assert isinstance(results['item'], MagicMock)
assert len(results['slides']) == 0
+
+ def test_controller_direction_next(self):
+ """
+ Text the live next method is triggered
+ """
+ # GIVEN: A mocked service with a dummy service item
+ self.mocked_live_controller.service_item = MagicMock()
+ # WHEN: I trigger the method
+ controller_direction(None, 'live', 'next')
+ # THEN: The correct method is called
+ self.mocked_live_controller.slidecontroller_live_next.emit.assert_called_once_with()
+
+ def test_controller_direction_previous(self):
+ """
+ Text the live next method is triggered
+ """
+ # GIVEN: A mocked service with a dummy service item
+ self.mocked_live_controller.service_item = MagicMock()
+ # WHEN: I trigger the method
+ controller_direction(None, 'live', 'previous')
+ # THEN: The correct method is called
+ self.mocked_live_controller.slidecontroller_live_previous.emit.assert_called_once_with()
diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py
index 9af310e26..afe8b370b 100644
--- a/tests/functional/openlp_plugins/songs/test_mediaitem.py
+++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py
@@ -431,14 +431,16 @@ class TestMediaItem(TestCase, TestMixin):
# GIVEN: A Song and a Service Item
song = Song()
song.title = 'My Song'
+ song.alternate_title = ''
song.copyright = 'My copyright'
song.authors_songs = []
song.songbook_entries = []
song.ccli_number = ''
+ song.topics = None
book1 = MagicMock()
- book1.name = "My songbook"
+ book1.name = 'My songbook'
book2 = MagicMock()
- book2.name = "Thy songbook"
+ book2.name = 'Thy songbook'
song.songbookentries = []
song.add_songbook_entry(book1, '12')
song.add_songbook_entry(book2, '502A')