forked from openlp/openlp
1699 lines
82 KiB
Python
1699 lines
82 KiB
Python
# -*- coding: utf-8 -*-
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
|
|
###############################################################################
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
# --------------------------------------------------------------------------- #
|
|
# Copyright (c) 2008-2016 OpenLP Developers #
|
|
# --------------------------------------------------------------------------- #
|
|
# 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 service manager sets up, loads, saves and manages services.
|
|
"""
|
|
import html
|
|
import json
|
|
import os
|
|
import shutil
|
|
import zipfile
|
|
from datetime import datetime, timedelta
|
|
from tempfile import mkstemp
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
|
|
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \
|
|
RegistryMixin, check_directory_exists, UiStrings, translate
|
|
from openlp.core.common.actions import ActionList, CategoryOrder
|
|
from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, 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.utils import delete_file, split_filename, format_time
|
|
|
|
|
|
class ServiceManagerList(QtWidgets.QTreeWidget):
|
|
"""
|
|
Set up key bindings and mouse behaviour for the service list
|
|
"""
|
|
def __init__(self, service_manager, parent=None):
|
|
"""
|
|
Constructor
|
|
"""
|
|
super(ServiceManagerList, self).__init__(parent)
|
|
self.service_manager = service_manager
|
|
|
|
def keyPressEvent(self, event):
|
|
"""
|
|
Capture Key press and respond accordingly.
|
|
:param event:
|
|
"""
|
|
if isinstance(event, QtGui.QKeyEvent):
|
|
# here accept the event and do something
|
|
if event.key() == QtCore.Qt.Key_Up:
|
|
self.service_manager.on_move_selection_up()
|
|
event.accept()
|
|
elif event.key() == QtCore.Qt.Key_Down:
|
|
self.service_manager.on_move_selection_down()
|
|
event.accept()
|
|
elif event.key() == QtCore.Qt.Key_Delete:
|
|
self.service_manager.on_delete_from_service()
|
|
event.accept()
|
|
event.ignore()
|
|
else:
|
|
event.ignore()
|
|
|
|
def mouseMoveEvent(self, event):
|
|
"""
|
|
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
|
move just tell it what plugin to call
|
|
:param event:
|
|
"""
|
|
if event.buttons() != QtCore.Qt.LeftButton:
|
|
event.ignore()
|
|
return
|
|
if not self.itemAt(self.mapFromGlobal(QtGui.QCursor.pos())):
|
|
event.ignore()
|
|
return
|
|
drag = QtGui.QDrag(self)
|
|
mime_data = QtCore.QMimeData()
|
|
drag.setMimeData(mime_data)
|
|
mime_data.setText('ServiceManager')
|
|
drag.exec(QtCore.Qt.CopyAction)
|
|
|
|
|
|
class Ui_ServiceManager(object):
|
|
"""
|
|
UI part of the Service Manager
|
|
"""
|
|
def setup_ui(self, widget):
|
|
"""
|
|
Define the UI
|
|
:param widget:
|
|
"""
|
|
# start with the layout
|
|
self.layout = QtWidgets.QVBoxLayout(widget)
|
|
self.layout.setSpacing(0)
|
|
self.layout.setContentsMargins(0, 0, 0, 0)
|
|
# Create the top toolbar
|
|
self.toolbar = OpenLPToolbar(widget)
|
|
self.toolbar.add_toolbar_action('newService', text=UiStrings().NewService, icon=':/general/general_new.png',
|
|
tooltip=UiStrings().CreateService, triggers=self.on_new_service_clicked)
|
|
self.toolbar.add_toolbar_action('openService', text=UiStrings().OpenService,
|
|
icon=':/general/general_open.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Load an existing service.'),
|
|
triggers=self.on_load_service_clicked)
|
|
self.toolbar.add_toolbar_action('saveService', text=UiStrings().SaveService,
|
|
icon=':/general/general_save.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
|
|
triggers=self.decide_save_method)
|
|
self.toolbar.addSeparator()
|
|
self.theme_label = QtWidgets.QLabel('%s:' % UiStrings().Theme, widget)
|
|
self.theme_label.setContentsMargins(3, 3, 3, 3)
|
|
self.theme_label.setObjectName('theme_label')
|
|
self.toolbar.add_toolbar_widget(self.theme_label)
|
|
self.theme_combo_box = QtWidgets.QComboBox(self.toolbar)
|
|
self.theme_combo_box.setToolTip(translate('OpenLP.ServiceManager', 'Select a theme for the service.'))
|
|
self.theme_combo_box.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
|
self.theme_combo_box.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
|
self.theme_combo_box.setObjectName('theme_combo_box')
|
|
self.toolbar.add_toolbar_widget(self.theme_combo_box)
|
|
self.toolbar.setObjectName('toolbar')
|
|
self.layout.addWidget(self.toolbar)
|
|
# Create the service manager list
|
|
self.service_manager_list = ServiceManagerList(widget)
|
|
self.service_manager_list.setEditTriggers(
|
|
QtWidgets.QAbstractItemView.CurrentChanged |
|
|
QtWidgets.QAbstractItemView.DoubleClicked |
|
|
QtWidgets.QAbstractItemView.EditKeyPressed)
|
|
self.service_manager_list.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
|
|
self.service_manager_list.setAlternatingRowColors(True)
|
|
self.service_manager_list.setHeaderHidden(True)
|
|
self.service_manager_list.setExpandsOnDoubleClick(False)
|
|
self.service_manager_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
self.service_manager_list.customContextMenuRequested.connect(self.context_menu)
|
|
self.service_manager_list.setObjectName('service_manager_list')
|
|
# enable drop
|
|
self.service_manager_list.__class__.dragEnterEvent = lambda x, event: event.accept()
|
|
self.service_manager_list.__class__.dragMoveEvent = lambda x, event: event.accept()
|
|
self.service_manager_list.__class__.dropEvent = self.drop_event
|
|
self.layout.addWidget(self.service_manager_list)
|
|
# Add the bottom toolbar
|
|
self.order_toolbar = OpenLPToolbar(widget)
|
|
action_list = ActionList.get_instance()
|
|
action_list.add_category(UiStrings().Service, CategoryOrder.standard_toolbar)
|
|
self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action(
|
|
'moveTop',
|
|
text=translate('OpenLP.ServiceManager', 'Move to &top'), icon=':/services/service_top.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Move item to the top of the service.'),
|
|
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_top)
|
|
self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action(
|
|
'moveUp',
|
|
text=translate('OpenLP.ServiceManager', 'Move &up'), icon=':/services/service_up.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Move item up one position in the service.'),
|
|
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_up)
|
|
self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action(
|
|
'moveDown',
|
|
text=translate('OpenLP.ServiceManager', 'Move &down'), icon=':/services/service_down.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Move item down one position in the service.'),
|
|
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_down)
|
|
self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action(
|
|
'moveBottom',
|
|
text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
|
|
can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
|
|
self.service_manager_list.down = self.order_toolbar.add_toolbar_action(
|
|
'down',
|
|
text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
|
|
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
|
|
triggers=self.on_move_selection_down)
|
|
action_list.add_action(self.service_manager_list.down)
|
|
self.service_manager_list.up = self.order_toolbar.add_toolbar_action(
|
|
'up',
|
|
text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
|
|
tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
|
|
triggers=self.on_move_selection_up)
|
|
action_list.add_action(self.service_manager_list.up)
|
|
self.order_toolbar.addSeparator()
|
|
self.service_manager_list.delete = self.order_toolbar.add_toolbar_action(
|
|
'delete', can_shortcuts=True,
|
|
text=translate('OpenLP.ServiceManager', '&Delete From Service'), icon=':/general/general_delete.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Delete the selected item from the service.'),
|
|
triggers=self.on_delete_from_service)
|
|
self.order_toolbar.addSeparator()
|
|
self.service_manager_list.expand = self.order_toolbar.add_toolbar_action(
|
|
'expand', can_shortcuts=True,
|
|
text=translate('OpenLP.ServiceManager', '&Expand all'), icon=':/services/service_expand_all.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Expand all the service items.'),
|
|
category=UiStrings().Service, triggers=self.on_expand_all)
|
|
self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action(
|
|
'collapse', can_shortcuts=True,
|
|
text=translate('OpenLP.ServiceManager', '&Collapse all'), icon=':/services/service_collapse_all.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Collapse all the service items.'),
|
|
category=UiStrings().Service, triggers=self.on_collapse_all)
|
|
self.order_toolbar.addSeparator()
|
|
self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action(
|
|
'make_live', can_shortcuts=True,
|
|
text=translate('OpenLP.ServiceManager', 'Go Live'), icon=':/general/general_live.png',
|
|
tooltip=translate('OpenLP.ServiceManager', 'Send the selected item to Live.'),
|
|
category=UiStrings().Service,
|
|
triggers=self.make_live)
|
|
self.layout.addWidget(self.order_toolbar)
|
|
# Connect up our signals and slots
|
|
self.theme_combo_box.activated.connect(self.on_theme_combo_box_selected)
|
|
self.service_manager_list.doubleClicked.connect(self.on_double_click_live)
|
|
self.service_manager_list.clicked.connect(self.on_single_click_preview)
|
|
self.service_manager_list.itemCollapsed.connect(self.collapsed)
|
|
self.service_manager_list.itemExpanded.connect(self.expanded)
|
|
# Last little bits of setting up
|
|
self.service_theme = Settings().value(self.main_window.service_manager_settings_section + '/service theme')
|
|
self.service_path = AppLocation.get_section_data_path('servicemanager')
|
|
# build the drag and drop context menu
|
|
self.dnd_menu = QtWidgets.QMenu()
|
|
self.new_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add New Item'))
|
|
self.new_action.setIcon(build_icon(':/general/general_edit.png'))
|
|
self.add_to_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add to Selected Item'))
|
|
self.add_to_action.setIcon(build_icon(':/general/general_edit.png'))
|
|
# build the context menu
|
|
self.menu = QtWidgets.QMenu()
|
|
self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'),
|
|
icon=':/general/general_edit.png', triggers=self.remote_edit)
|
|
self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'),
|
|
icon=':/general/general_edit.png',
|
|
triggers=self.on_service_item_rename)
|
|
self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'),
|
|
icon=':/general/general_edit.png',
|
|
triggers=self.on_service_item_edit_form)
|
|
self.notes_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Notes'),
|
|
icon=':/services/service_notes.png',
|
|
triggers=self.on_service_item_note_form)
|
|
self.time_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Start Time'),
|
|
icon=':/media/media_time.png', triggers=self.on_start_time_form)
|
|
self.auto_start_action = create_widget_action(self.menu, text='',
|
|
icon=':/media/auto-start_active.png',
|
|
triggers=self.on_auto_start)
|
|
# Add already existing delete action to the menu.
|
|
self.menu.addAction(self.service_manager_list.delete)
|
|
self.create_custom_action = create_widget_action(self.menu,
|
|
text=translate('OpenLP.ServiceManager', 'Create New &Custom '
|
|
'Slide'),
|
|
icon=':/general/general_edit.png',
|
|
triggers=self.create_custom)
|
|
self.menu.addSeparator()
|
|
# Add AutoPlay menu actions
|
|
self.auto_play_slides_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Auto play slides'))
|
|
self.menu.addMenu(self.auto_play_slides_menu)
|
|
auto_play_slides_group = QtWidgets.QActionGroup(self.auto_play_slides_menu)
|
|
auto_play_slides_group.setExclusive(True)
|
|
self.auto_play_slides_loop = create_widget_action(self.auto_play_slides_menu,
|
|
text=translate('OpenLP.ServiceManager', 'Auto play slides '
|
|
'&Loop'),
|
|
checked=False, triggers=self.toggle_auto_play_slides_loop)
|
|
auto_play_slides_group.addAction(self.auto_play_slides_loop)
|
|
self.auto_play_slides_once = create_widget_action(self.auto_play_slides_menu,
|
|
text=translate('OpenLP.ServiceManager', 'Auto play slides '
|
|
'&Once'),
|
|
checked=False, triggers=self.toggle_auto_play_slides_once)
|
|
auto_play_slides_group.addAction(self.auto_play_slides_once)
|
|
self.auto_play_slides_menu.addSeparator()
|
|
self.timed_slide_interval = create_widget_action(self.auto_play_slides_menu,
|
|
text=translate('OpenLP.ServiceManager', '&Delay between '
|
|
'slides'),
|
|
triggers=self.on_timed_slide_interval)
|
|
self.menu.addSeparator()
|
|
self.preview_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
|
icon=':/general/general_preview.png', triggers=self.make_preview)
|
|
# Add already existing make live action to the menu.
|
|
self.menu.addAction(self.service_manager_list.make_live)
|
|
self.menu.addSeparator()
|
|
self.theme_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Change Item Theme'))
|
|
self.menu.addMenu(self.theme_menu)
|
|
self.service_manager_list.addActions(
|
|
[self.service_manager_list.move_down,
|
|
self.service_manager_list.move_up,
|
|
self.service_manager_list.make_live,
|
|
self.service_manager_list.move_top,
|
|
self.service_manager_list.move_bottom,
|
|
self.service_manager_list.up,
|
|
self.service_manager_list.down,
|
|
self.service_manager_list.expand,
|
|
self.service_manager_list.collapse
|
|
])
|
|
Registry().register_function('theme_update_list', self.update_theme_list)
|
|
Registry().register_function('config_screen_changed', self.regenerate_service_items)
|
|
Registry().register_function('theme_update_global', self.theme_change)
|
|
Registry().register_function('mediaitem_suffix_reset', self.reset_supported_suffixes)
|
|
|
|
|
|
class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceManager, RegistryProperties):
|
|
"""
|
|
Manages the services. This involves taking text strings from plugins and adding them to the service. This service
|
|
can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
|
|
Also handles the UI tasks of moving things up and down etc.
|
|
"""
|
|
servicemanager_set_item = QtCore.pyqtSignal(int)
|
|
servicemanager_next_item = QtCore.pyqtSignal()
|
|
servicemanager_previous_item = QtCore.pyqtSignal()
|
|
|
|
def __init__(self, parent=None):
|
|
"""
|
|
Sets up the service manager, toolbars, list view, et al.
|
|
"""
|
|
super(ServiceManager, self).__init__(parent)
|
|
self.active = build_icon(':/media/auto-start_active.png')
|
|
self.inactive = build_icon(':/media/auto-start_inactive.png')
|
|
self.service_items = []
|
|
self.suffixes = []
|
|
self.drop_position = -1
|
|
self.service_id = 0
|
|
# is a new service and has not been saved
|
|
self._modified = False
|
|
self._file_name = ''
|
|
self.service_has_all_original_files = True
|
|
self.list_double_clicked = False
|
|
|
|
def bootstrap_initialise(self):
|
|
"""
|
|
To be called as part of initialisation
|
|
"""
|
|
self.setup_ui(self)
|
|
# Need to use event as called across threads and UI is updated
|
|
self.servicemanager_set_item.connect(self.on_set_item)
|
|
self.servicemanager_next_item.connect(self.next_item)
|
|
self.servicemanager_previous_item.connect(self.previous_item)
|
|
|
|
def bootstrap_post_set_up(self):
|
|
"""
|
|
Can be set up as a late setup
|
|
"""
|
|
self.service_note_form = ServiceNoteForm()
|
|
self.service_item_edit_form = ServiceItemEditForm()
|
|
self.start_time_form = StartTimeForm()
|
|
|
|
def set_modified(self, modified=True):
|
|
"""
|
|
Setter for property "modified". Sets whether or not the current service has been modified.
|
|
|
|
:param modified: Indicates if the service has new or removed items. Used to trigger a remote update.
|
|
"""
|
|
if modified:
|
|
self.service_id += 1
|
|
self._modified = modified
|
|
service_file = self.short_file_name() or translate('OpenLP.ServiceManager', 'Untitled Service')
|
|
self.main_window.set_service_modified(modified, service_file)
|
|
|
|
def is_modified(self):
|
|
"""
|
|
Getter for boolean property "modified".
|
|
"""
|
|
return self._modified
|
|
|
|
def set_file_name(self, file_name):
|
|
"""
|
|
Setter for service file.
|
|
|
|
:param file_name: The service file name
|
|
"""
|
|
self._file_name = str(file_name)
|
|
self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
|
|
Settings().setValue('servicemanager/last file', file_name)
|
|
self._save_lite = self._file_name.endswith('.oszl')
|
|
|
|
def file_name(self):
|
|
"""
|
|
Return the current file name including path.
|
|
"""
|
|
return self._file_name
|
|
|
|
def short_file_name(self):
|
|
"""
|
|
Return the current file name, excluding the path.
|
|
"""
|
|
return split_filename(self._file_name)[1]
|
|
|
|
def reset_supported_suffixes(self):
|
|
"""
|
|
Resets the Suffixes list.
|
|
|
|
"""
|
|
self.suffixes = []
|
|
|
|
def supported_suffixes(self, suffix_list):
|
|
"""
|
|
Adds Suffixes supported to the master list. Called from Plugins.
|
|
|
|
:param suffix_list: New Suffix's to be supported
|
|
"""
|
|
if isinstance(suffix_list, str):
|
|
self.suffixes.append(suffix_list)
|
|
else:
|
|
for suffix in suffix_list:
|
|
if suffix not in self.suffixes:
|
|
self.suffixes.append(suffix)
|
|
|
|
def on_new_service_clicked(self, field=None):
|
|
"""
|
|
Create a new service.
|
|
:param field:
|
|
"""
|
|
if self.is_modified():
|
|
result = self.save_modified_service()
|
|
if result == QtWidgets.QMessageBox.Cancel:
|
|
return False
|
|
elif result == QtWidgets.QMessageBox.Save:
|
|
if not self.decide_save_method():
|
|
return False
|
|
self.new_file()
|
|
|
|
def on_load_service_clicked(self, load_file=None):
|
|
"""
|
|
Loads the service file and saves the existing one it there is one unchanged.
|
|
|
|
:param load_file: The service file to the loaded. Will be None is from menu so selection will be required.
|
|
"""
|
|
if self.is_modified():
|
|
result = self.save_modified_service()
|
|
if result == QtWidgets.QMessageBox.Cancel:
|
|
return False
|
|
elif result == QtWidgets.QMessageBox.Save:
|
|
self.decide_save_method()
|
|
if not load_file:
|
|
file_name, filter_used = QtWidgets.QFileDialog.getOpenFileName(
|
|
self.main_window,
|
|
translate('OpenLP.ServiceManager', 'Open File'),
|
|
Settings().value(self.main_window.service_manager_settings_section + '/last directory'),
|
|
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)'))
|
|
if not file_name:
|
|
return False
|
|
else:
|
|
file_name = load_file
|
|
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory',
|
|
split_filename(file_name)[0])
|
|
self.load_file(file_name)
|
|
|
|
def save_modified_service(self):
|
|
"""
|
|
Check to see if a service needs to be saved.
|
|
"""
|
|
return QtWidgets.QMessageBox.question(self.main_window,
|
|
translate('OpenLP.ServiceManager', 'Modified Service'),
|
|
translate('OpenLP.ServiceManager',
|
|
'The current service has been modified. Would you like to save '
|
|
'this service?'),
|
|
QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Discard |
|
|
QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Save)
|
|
|
|
def on_recent_service_clicked(self, field=None):
|
|
"""
|
|
Load a recent file as the service triggered by mainwindow recent service list.
|
|
:param field:
|
|
"""
|
|
sender = self.sender()
|
|
self.load_file(sender.data())
|
|
|
|
def new_file(self):
|
|
"""
|
|
Create a blank new service file.
|
|
"""
|
|
self.service_manager_list.clear()
|
|
self.service_items = []
|
|
self.set_file_name('')
|
|
self.service_id += 1
|
|
self.set_modified(False)
|
|
Settings().setValue('servicemanager/last file', '')
|
|
self.plugin_manager.new_service_created()
|
|
|
|
def create_basic_service(self):
|
|
"""
|
|
Create the initial service array with the base items to be saved.
|
|
|
|
:return: service array
|
|
"""
|
|
service = []
|
|
core = {'lite-service': self._save_lite,
|
|
'service-theme': self.service_theme
|
|
}
|
|
service.append({'openlp_core': core})
|
|
return service
|
|
|
|
def save_file(self, field=None):
|
|
"""
|
|
Save the current service file.
|
|
|
|
A temporary file is created so that we don't overwrite the existing one and leave a mangled service file should
|
|
there be an error when saving. Audio files are also copied into the service manager directory, and then packaged
|
|
into the zip file.
|
|
"""
|
|
if not self.file_name():
|
|
return self.save_file_as()
|
|
temp_file, temp_file_name = mkstemp('.osz', 'openlp_')
|
|
# We don't need the file handle.
|
|
os.close(temp_file)
|
|
self.log_debug(temp_file_name)
|
|
path_file_name = str(self.file_name())
|
|
path, file_name = os.path.split(path_file_name)
|
|
base_name = os.path.splitext(file_name)[0]
|
|
service_file_name = '%s.osj' % base_name
|
|
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
|
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
|
service = self.create_basic_service()
|
|
write_list = []
|
|
missing_list = []
|
|
audio_files = []
|
|
total_size = 0
|
|
self.application.set_busy_cursor()
|
|
# Number of items + 1 to zip it
|
|
self.main_window.display_progress_bar(len(self.service_items) + 1)
|
|
# Get list of missing files, and list of files to write
|
|
for item in self.service_items:
|
|
if not item['service_item'].uses_file():
|
|
continue
|
|
for frame in item['service_item'].get_frames():
|
|
path_from = item['service_item'].get_frame_path(frame=frame)
|
|
if path_from in write_list or path_from in missing_list:
|
|
continue
|
|
if not os.path.exists(path_from):
|
|
missing_list.append(path_from)
|
|
else:
|
|
write_list.append(path_from)
|
|
if missing_list:
|
|
self.application.set_normal_cursor()
|
|
title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
|
|
message = translate('OpenLP.ServiceManager',
|
|
'The following file(s) in the service are missing: %s\n\n'
|
|
'These files will be removed if you continue to save.') % "\n\t".join(missing_list)
|
|
answer = QtWidgets.QMessageBox.critical(self, title, message,
|
|
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok |
|
|
QtWidgets.QMessageBox.Cancel))
|
|
if answer == QtWidgets.QMessageBox.Cancel:
|
|
self.main_window.finished_progress_bar()
|
|
return False
|
|
# Check if item contains a missing file.
|
|
for item in list(self.service_items):
|
|
self.main_window.increment_progress_bar()
|
|
item['service_item'].remove_invalid_frames(missing_list)
|
|
if item['service_item'].missing_frames():
|
|
self.service_items.remove(item)
|
|
else:
|
|
service_item = item['service_item'].get_service_repr(self._save_lite)
|
|
if service_item['header']['background_audio']:
|
|
for i, file_name in enumerate(service_item['header']['background_audio']):
|
|
new_file = os.path.join('audio', item['service_item'].unique_identifier, file_name)
|
|
audio_files.append((file_name, new_file))
|
|
service_item['header']['background_audio'][i] = new_file
|
|
# Add the service item to the service.
|
|
service.append({'serviceitem': service_item})
|
|
self.repaint_service_list(-1, -1)
|
|
for file_item in write_list:
|
|
file_size = os.path.getsize(file_item)
|
|
total_size += file_size
|
|
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
|
|
service_content = json.dumps(service)
|
|
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
|
|
allow_zip_64 = (total_size > 2147483648 + len(service_content))
|
|
self.log_debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
|
|
zip_file = None
|
|
success = True
|
|
self.main_window.increment_progress_bar()
|
|
try:
|
|
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, allow_zip_64)
|
|
# First we add service contents..
|
|
zip_file.writestr(service_file_name, service_content)
|
|
# Finally add all the listed media files.
|
|
for write_from in write_list:
|
|
zip_file.write(write_from, write_from)
|
|
for audio_from, audio_to in audio_files:
|
|
if audio_from.startswith('audio'):
|
|
# When items are saved, they get new unique_identifier. Let's copy the file to the new location.
|
|
# Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit.
|
|
audio_from = os.path.join(self.service_path, audio_from)
|
|
save_file = os.path.join(self.service_path, audio_to)
|
|
save_path = os.path.split(save_file)[0]
|
|
check_directory_exists(save_path)
|
|
if not os.path.exists(save_file):
|
|
shutil.copy(audio_from, save_file)
|
|
zip_file.write(audio_from, audio_to)
|
|
except IOError:
|
|
self.log_exception('Failed to save service to disk: %s' % temp_file_name)
|
|
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
|
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
|
success = False
|
|
finally:
|
|
if zip_file:
|
|
zip_file.close()
|
|
self.main_window.finished_progress_bar()
|
|
self.application.set_normal_cursor()
|
|
if success:
|
|
try:
|
|
shutil.copy(temp_file_name, path_file_name)
|
|
except shutil.Error:
|
|
return self.save_file_as()
|
|
except OSError as ose:
|
|
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
|
|
translate('OpenLP.ServiceManager', 'An error occurred while writing the '
|
|
'service file: %s') % ose.strerror,
|
|
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
|
success = False
|
|
self.main_window.add_recent_file(path_file_name)
|
|
self.set_modified(False)
|
|
delete_file(temp_file_name)
|
|
return success
|
|
|
|
def save_local_file(self):
|
|
"""
|
|
Save the current service file but leave all the file references alone to point to the current machine.
|
|
This format is not transportable as it will not contain any files.
|
|
"""
|
|
if not self.file_name():
|
|
return self.save_file_as()
|
|
temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
|
|
# We don't need the file handle.
|
|
os.close(temp_file)
|
|
self.log_debug(temp_file_name)
|
|
path_file_name = str(self.file_name())
|
|
path, file_name = os.path.split(path_file_name)
|
|
base_name = os.path.splitext(file_name)[0]
|
|
service_file_name = '%s.osj' % base_name
|
|
self.log_debug('ServiceManager.save_file - %s' % path_file_name)
|
|
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
|
|
service = self.create_basic_service()
|
|
self.application.set_busy_cursor()
|
|
# Number of items + 1 to zip it
|
|
self.main_window.display_progress_bar(len(self.service_items) + 1)
|
|
for item in self.service_items:
|
|
self.main_window.increment_progress_bar()
|
|
service_item = item['service_item'].get_service_repr(self._save_lite)
|
|
# TODO: check for file item on save.
|
|
service.append({'serviceitem': service_item})
|
|
self.main_window.increment_progress_bar()
|
|
service_content = json.dumps(service)
|
|
zip_file = None
|
|
success = True
|
|
self.main_window.increment_progress_bar()
|
|
try:
|
|
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)
|
|
# First we add service contents.
|
|
zip_file.writestr(service_file_name, service_content)
|
|
except IOError:
|
|
self.log_exception('Failed to save service to disk: %s', temp_file_name)
|
|
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
|
|
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
|
|
success = False
|
|
finally:
|
|
if zip_file:
|
|
zip_file.close()
|
|
self.main_window.finished_progress_bar()
|
|
self.application.set_normal_cursor()
|
|
if success:
|
|
try:
|
|
shutil.copy(temp_file_name, path_file_name)
|
|
except shutil.Error:
|
|
return self.save_file_as()
|
|
self.main_window.add_recent_file(path_file_name)
|
|
self.set_modified(False)
|
|
delete_file(temp_file_name)
|
|
return success
|
|
|
|
def save_file_as(self, field=None):
|
|
"""
|
|
Get a file name and then call :func:`ServiceManager.save_file` to save the file.
|
|
"""
|
|
default_service_enabled = Settings().value('advanced/default service enabled')
|
|
if default_service_enabled:
|
|
service_day = Settings().value('advanced/default service day')
|
|
if service_day == 7:
|
|
local_time = datetime.now()
|
|
else:
|
|
service_hour = Settings().value('advanced/default service hour')
|
|
service_minute = Settings().value('advanced/default service minute')
|
|
now = datetime.now()
|
|
day_delta = service_day - now.weekday()
|
|
if day_delta < 0:
|
|
day_delta += 7
|
|
time = now + timedelta(days=day_delta)
|
|
local_time = time.replace(hour=service_hour, minute=service_minute)
|
|
default_pattern = Settings().value('advanced/default service name')
|
|
default_file_name = format_time(default_pattern, local_time)
|
|
else:
|
|
default_file_name = ''
|
|
directory = Settings().value(self.main_window.service_manager_settings_section + '/last directory')
|
|
path = os.path.join(directory, default_file_name)
|
|
# SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
|
|
# the long term.
|
|
if self._file_name.endswith('oszl') or self.service_has_all_original_files:
|
|
file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
|
|
self.main_window, UiStrings().SaveService, path,
|
|
translate('OpenLP.ServiceManager',
|
|
'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
|
|
else:
|
|
file_name, filter_uesd = QtWidgets.QFileDialog.getSaveFileName(
|
|
self.main_window, UiStrings().SaveService, path,
|
|
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz);;'))
|
|
if not file_name:
|
|
return False
|
|
if os.path.splitext(file_name)[1] == '':
|
|
file_name += '.osz'
|
|
else:
|
|
ext = os.path.splitext(file_name)[1]
|
|
file_name.replace(ext, '.osz')
|
|
self.set_file_name(file_name)
|
|
self.decide_save_method()
|
|
|
|
def decide_save_method(self, field=None):
|
|
"""
|
|
Determine which type of save method to use.
|
|
:param field:
|
|
"""
|
|
if not self.file_name():
|
|
return self.save_file_as()
|
|
if self._save_lite:
|
|
return self.save_local_file()
|
|
else:
|
|
return self.save_file()
|
|
|
|
def load_file(self, file_name):
|
|
"""
|
|
Load an existing service file
|
|
:param file_name:
|
|
"""
|
|
if not file_name:
|
|
return False
|
|
file_name = str(file_name)
|
|
if not os.path.exists(file_name):
|
|
return False
|
|
zip_file = None
|
|
file_to = None
|
|
self.application.set_busy_cursor()
|
|
try:
|
|
zip_file = zipfile.ZipFile(file_name)
|
|
for zip_info in zip_file.infolist():
|
|
try:
|
|
ucs_file = zip_info.filename
|
|
except UnicodeDecodeError:
|
|
self.log_exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
|
|
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
|
'File is not a valid service.\n The content encoding is not UTF-8.'))
|
|
continue
|
|
os_file = ucs_file.replace('/', os.path.sep)
|
|
os_file = os.path.basename(os_file)
|
|
self.log_debug('Extract file: %s' % os_file)
|
|
zip_info.filename = os_file
|
|
zip_file.extract(zip_info, self.service_path)
|
|
if os_file.endswith('osj') or os_file.endswith('osd'):
|
|
p_file = os.path.join(self.service_path, os_file)
|
|
if 'p_file' in locals():
|
|
file_to = open(p_file, 'r')
|
|
if p_file.endswith('osj'):
|
|
items = json.load(file_to)
|
|
else:
|
|
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
|
'The service file you are trying to open is in an old '
|
|
'format.\n Please save it using OpenLP 2.0.2 or '
|
|
'greater.'))
|
|
return
|
|
file_to.close()
|
|
self.new_file()
|
|
self.set_file_name(file_name)
|
|
self.main_window.display_progress_bar(len(items))
|
|
self.process_service_items(items)
|
|
delete_file(p_file)
|
|
self.main_window.add_recent_file(file_name)
|
|
self.set_modified(False)
|
|
Settings().setValue('servicemanager/last file', file_name)
|
|
else:
|
|
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
|
|
self.log_error('File contains no service data')
|
|
except (IOError, NameError, zipfile.BadZipfile):
|
|
self.log_exception('Problem loading service file %s' % file_name)
|
|
critical_error_message_box(message=translate('OpenLP.ServiceManager',
|
|
'File could not be opened because it is corrupt.'))
|
|
except zipfile.BadZipfile:
|
|
if os.path.getsize(file_name) == 0:
|
|
self.log_exception('Service file is zero sized: %s' % file_name)
|
|
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
|
|
translate('OpenLP.ServiceManager',
|
|
'This service file does not contain '
|
|
'any data.'))
|
|
else:
|
|
self.log_exception('Service file is cannot be extracted as zip: %s' % file_name)
|
|
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
|
|
translate('OpenLP.ServiceManager',
|
|
'This file is either corrupt or it is not an OpenLP 2 '
|
|
'service file.'))
|
|
self.application.set_normal_cursor()
|
|
return
|
|
finally:
|
|
if file_to:
|
|
file_to.close()
|
|
if zip_file:
|
|
zip_file.close()
|
|
self.main_window.finished_progress_bar()
|
|
self.application.set_normal_cursor()
|
|
self.repaint_service_list(-1, -1)
|
|
|
|
def process_service_items(self, service_items):
|
|
"""
|
|
Process all the array of service items loaded from the saved service
|
|
|
|
:param service_items: list of service_items
|
|
"""
|
|
for item in service_items:
|
|
self.main_window.increment_progress_bar()
|
|
service_item = ServiceItem()
|
|
if 'openlp_core' in item:
|
|
item = item.get('openlp_core')
|
|
theme = item.get('service-theme', None)
|
|
if theme:
|
|
find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
|
|
if theme == self.theme_combo_box.currentText():
|
|
self.renderer.set_service_theme(theme)
|
|
else:
|
|
if self._save_lite:
|
|
service_item.set_from_service(item)
|
|
else:
|
|
service_item.set_from_service(item, self.service_path)
|
|
service_item.validate_item(self.suffixes)
|
|
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
|
new_item = Registry().get(service_item.name).service_load(service_item)
|
|
if new_item:
|
|
service_item = new_item
|
|
self.add_service_item(service_item, repaint=False)
|
|
|
|
def load_last_file(self):
|
|
"""
|
|
Load the last service item from the service manager when the service was last closed. Can be blank if there was
|
|
no service present.
|
|
"""
|
|
file_name = Settings().value('servicemanager/last file')
|
|
if file_name:
|
|
self.load_file(file_name)
|
|
|
|
def context_menu(self, point):
|
|
"""
|
|
The Right click context menu from the Serviceitem list
|
|
|
|
:param point: The location of the cursor.
|
|
"""
|
|
item = self.service_manager_list.itemAt(point)
|
|
if item is None:
|
|
return
|
|
if item.parent():
|
|
pos = item.parent().data(0, QtCore.Qt.UserRole)
|
|
else:
|
|
pos = item.data(0, QtCore.Qt.UserRole)
|
|
service_item = self.service_items[pos - 1]
|
|
self.edit_action.setVisible(False)
|
|
self.rename_action.setVisible(False)
|
|
self.create_custom_action.setVisible(False)
|
|
self.maintain_action.setVisible(False)
|
|
self.notes_action.setVisible(False)
|
|
self.time_action.setVisible(False)
|
|
self.auto_start_action.setVisible(False)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.CanEdit) and service_item['service_item'].edit_id:
|
|
self.edit_action.setVisible(True)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.CanEditTitle):
|
|
self.rename_action.setVisible(True)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.CanMaintain):
|
|
self.maintain_action.setVisible(True)
|
|
if item.parent() is None:
|
|
self.notes_action.setVisible(True)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.CanLoop) and \
|
|
len(service_item['service_item'].get_frames()) > 1:
|
|
self.auto_play_slides_menu.menuAction().setVisible(True)
|
|
self.auto_play_slides_once.setChecked(service_item['service_item'].auto_play_slides_once)
|
|
self.auto_play_slides_loop.setChecked(service_item['service_item'].auto_play_slides_loop)
|
|
self.timed_slide_interval.setChecked(service_item['service_item'].timed_slide_interval > 0)
|
|
if service_item['service_item'].timed_slide_interval > 0:
|
|
delay_suffix = ' %s s' % str(service_item['service_item'].timed_slide_interval)
|
|
else:
|
|
delay_suffix = ' ...'
|
|
self.timed_slide_interval.setText(
|
|
translate('OpenLP.ServiceManager', '&Delay between slides') + delay_suffix)
|
|
# TODO for future: make group explains itself more visually
|
|
else:
|
|
self.auto_play_slides_menu.menuAction().setVisible(False)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime) and \
|
|
not service_item['service_item'].is_capable(ItemCapabilities.IsOptical):
|
|
self.time_action.setVisible(True)
|
|
if service_item['service_item'].is_capable(ItemCapabilities.CanAutoStartForLive):
|
|
self.auto_start_action.setVisible(True)
|
|
self.auto_start_action.setIcon(self.inactive)
|
|
self.auto_start_action.setText(translate('OpenLP.ServiceManager', '&Auto Start - inactive'))
|
|
if service_item['service_item'].will_auto_start:
|
|
self.auto_start_action.setText(translate('OpenLP.ServiceManager', '&Auto Start - active'))
|
|
self.auto_start_action.setIcon(self.active)
|
|
if service_item['service_item'].is_text():
|
|
for plugin in self.plugin_manager.plugins:
|
|
if plugin.name == 'custom' and plugin.status == PluginStatus.Active:
|
|
self.create_custom_action.setVisible(True)
|
|
break
|
|
self.theme_menu.menuAction().setVisible(False)
|
|
# Set up the theme menu.
|
|
if service_item['service_item'].is_text() and self.renderer.theme_level == ThemeLevel.Song:
|
|
self.theme_menu.menuAction().setVisible(True)
|
|
# The service item does not have a theme, check the "Default".
|
|
if service_item['service_item'].theme is None:
|
|
theme_action = self.theme_menu.defaultAction()
|
|
else:
|
|
theme_action = self.theme_menu.findChild(QtWidgets.QAction, service_item['service_item'].theme)
|
|
if theme_action is not None:
|
|
theme_action.setChecked(True)
|
|
self.menu.exec(self.service_manager_list.mapToGlobal(point))
|
|
|
|
def on_service_item_note_form(self, field=None):
|
|
"""
|
|
Allow the service note to be edited
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
self.service_note_form.text_edit.setPlainText(self.service_items[item]['service_item'].notes)
|
|
if self.service_note_form.exec():
|
|
self.service_items[item]['service_item'].notes = self.service_note_form.text_edit.toPlainText()
|
|
self.repaint_service_list(item, -1)
|
|
self.set_modified()
|
|
|
|
def on_start_time_form(self, field=None):
|
|
"""
|
|
Opens a dialog to type in service item notes.
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
self.start_time_form.item = self.service_items[item]
|
|
if self.start_time_form.exec():
|
|
self.repaint_service_list(item, -1)
|
|
|
|
def toggle_auto_play_slides_once(self, field=None):
|
|
"""
|
|
Toggle Auto play slide once. Inverts auto play once option for the item
|
|
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
service_item = self.service_items[item]['service_item']
|
|
service_item.auto_play_slides_once = not service_item.auto_play_slides_once
|
|
if service_item.auto_play_slides_once:
|
|
service_item.auto_play_slides_loop = False
|
|
self.auto_play_slides_loop.setChecked(False)
|
|
if service_item.auto_play_slides_once and service_item.timed_slide_interval == 0:
|
|
service_item.timed_slide_interval = Settings().value(
|
|
self.main_window.general_settings_section + '/loop delay')
|
|
self.set_modified()
|
|
|
|
def toggle_auto_play_slides_loop(self, field=None):
|
|
"""
|
|
Toggle Auto play slide loop.
|
|
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
service_item = self.service_items[item]['service_item']
|
|
service_item.auto_play_slides_loop = not service_item.auto_play_slides_loop
|
|
if service_item.auto_play_slides_loop:
|
|
service_item.auto_play_slides_once = False
|
|
self.auto_play_slides_once.setChecked(False)
|
|
if service_item.auto_play_slides_loop and service_item.timed_slide_interval == 0:
|
|
service_item.timed_slide_interval = Settings().value(
|
|
self.main_window.general_settings_section + '/loop delay')
|
|
self.set_modified()
|
|
|
|
def on_timed_slide_interval(self, field=None):
|
|
"""
|
|
Shows input dialog for enter interval in seconds for delay
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
service_item = self.service_items[item]['service_item']
|
|
if service_item.timed_slide_interval == 0:
|
|
timed_slide_interval = Settings().value(self.main_window.general_settings_section + '/loop delay')
|
|
else:
|
|
timed_slide_interval = service_item.timed_slide_interval
|
|
timed_slide_interval, ok = QtWidgets.QInputDialog.getInt(self, translate('OpenLP.ServiceManager',
|
|
'Input delay'),
|
|
translate('OpenLP.ServiceManager',
|
|
'Delay between slides in seconds.'),
|
|
timed_slide_interval, 0, 180, 1)
|
|
if ok:
|
|
service_item.timed_slide_interval = timed_slide_interval
|
|
if service_item.timed_slide_interval != 0 and not service_item.auto_play_slides_loop \
|
|
and not service_item.auto_play_slides_once:
|
|
service_item.auto_play_slides_loop = True
|
|
elif service_item.timed_slide_interval == 0:
|
|
service_item.auto_play_slides_loop = False
|
|
service_item.auto_play_slides_once = False
|
|
self.set_modified()
|
|
|
|
def on_auto_start(self, field=None):
|
|
"""
|
|
Toggles to Auto Start Setting.
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
self.service_items[item]['service_item'].will_auto_start = \
|
|
not self.service_items[item]['service_item'].will_auto_start
|
|
|
|
def on_service_item_edit_form(self, field=None):
|
|
"""
|
|
Opens a dialog to edit the service item and update the service display if changes are saved.
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
self.service_item_edit_form.set_service_item(self.service_items[item]['service_item'])
|
|
if self.service_item_edit_form.exec():
|
|
self.add_service_item(self.service_item_edit_form.get_service_item(),
|
|
replace=True, expand=self.service_items[item]['expanded'])
|
|
|
|
def preview_live(self, unique_identifier, row):
|
|
"""
|
|
Called by the SlideController to request a preview item be made live and allows the next preview to be updated
|
|
if relevant.
|
|
|
|
:param unique_identifier: Reference to the service_item
|
|
:param row: individual row number
|
|
"""
|
|
for sitem in self.service_items:
|
|
if sitem['service_item'].unique_identifier == unique_identifier:
|
|
item = self.service_manager_list.topLevelItem(sitem['order'] - 1)
|
|
self.service_manager_list.setCurrentItem(item)
|
|
self.make_live(int(row))
|
|
return
|
|
|
|
def next_item(self):
|
|
"""
|
|
Called by the SlideController to select the next service item.
|
|
"""
|
|
if not self.service_manager_list.selectedItems():
|
|
return
|
|
selected = self.service_manager_list.selectedItems()[0]
|
|
look_for = 0
|
|
service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
|
|
while service_iterator.value():
|
|
if look_for == 1 and service_iterator.value().parent() is None:
|
|
self.service_manager_list.setCurrentItem(service_iterator.value())
|
|
self.make_live()
|
|
return
|
|
if service_iterator.value() == selected:
|
|
look_for = 1
|
|
service_iterator += 1
|
|
|
|
def previous_item(self, last_slide=False):
|
|
"""
|
|
Called by the SlideController to select the previous service item.
|
|
|
|
:param last_slide: Is this the last slide in the service_item.
|
|
"""
|
|
if not self.service_manager_list.selectedItems():
|
|
return
|
|
selected = self.service_manager_list.selectedItems()[0]
|
|
prev_item = None
|
|
prev_item_last_slide = None
|
|
service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
|
|
while service_iterator.value():
|
|
if service_iterator.value() == selected:
|
|
if last_slide and prev_item_last_slide:
|
|
pos = prev_item.data(0, QtCore.Qt.UserRole)
|
|
check_expanded = self.service_items[pos - 1]['expanded']
|
|
self.service_manager_list.setCurrentItem(prev_item_last_slide)
|
|
if not check_expanded:
|
|
self.service_manager_list.collapseItem(prev_item)
|
|
self.make_live()
|
|
self.service_manager_list.setCurrentItem(prev_item)
|
|
elif prev_item:
|
|
self.service_manager_list.setCurrentItem(prev_item)
|
|
self.make_live()
|
|
return
|
|
if service_iterator.value().parent() is None:
|
|
prev_item = service_iterator.value()
|
|
if service_iterator.value().parent() is prev_item:
|
|
prev_item_last_slide = service_iterator.value()
|
|
service_iterator += 1
|
|
|
|
def on_set_item(self, message, field=None):
|
|
"""
|
|
Called by a signal to select a specific item and make it live usually from remote.
|
|
|
|
:param field:
|
|
:param message: The data passed in from a remove message
|
|
"""
|
|
self.log_debug(message)
|
|
self.set_item(int(message))
|
|
|
|
def set_item(self, index):
|
|
"""
|
|
Makes a specific item in the service live.
|
|
|
|
:param index: The index of the service item list to be actioned.
|
|
"""
|
|
if 0 <= index < self.service_manager_list.topLevelItemCount():
|
|
item = self.service_manager_list.topLevelItem(index)
|
|
self.service_manager_list.setCurrentItem(item)
|
|
self.make_live()
|
|
|
|
def on_move_selection_up(self):
|
|
"""
|
|
Moves the cursor selection up the window. Called by the up arrow.
|
|
"""
|
|
item = self.service_manager_list.currentItem()
|
|
item_before = self.service_manager_list.itemAbove(item)
|
|
if item_before is None:
|
|
return
|
|
self.service_manager_list.setCurrentItem(item_before)
|
|
|
|
def on_move_selection_down(self):
|
|
"""
|
|
Moves the cursor selection down the window. Called by the down arrow.
|
|
"""
|
|
item = self.service_manager_list.currentItem()
|
|
item_after = self.service_manager_list.itemBelow(item)
|
|
if item_after is None:
|
|
return
|
|
self.service_manager_list.setCurrentItem(item_after)
|
|
|
|
def on_collapse_all(self, field=None):
|
|
"""
|
|
Collapse all the service items.
|
|
:param field:
|
|
"""
|
|
for item in self.service_items:
|
|
item['expanded'] = False
|
|
self.service_manager_list.collapseAll()
|
|
|
|
def collapsed(self, item):
|
|
"""
|
|
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
|
|
|
:param item: The service item to be checked
|
|
"""
|
|
pos = item.data(0, QtCore.Qt.UserRole)
|
|
# Only set root items as collapsed, and since we only have 2 levels we find them by checking for children
|
|
if item.childCount():
|
|
self.service_items[pos - 1]['expanded'] = False
|
|
|
|
def on_expand_all(self, field=None):
|
|
"""
|
|
Collapse all the service items.
|
|
:param field:
|
|
"""
|
|
for item in self.service_items:
|
|
item['expanded'] = True
|
|
self.service_manager_list.expandAll()
|
|
|
|
def expanded(self, item):
|
|
"""
|
|
Record if an item is collapsed. Used when repainting the list to get the correct state.
|
|
|
|
:param item: The service item to be checked
|
|
"""
|
|
pos = item.data(0, QtCore.Qt.UserRole)
|
|
# Only set root items as expanded, and since we only have 2 levels we find them by checking for children
|
|
if item.childCount():
|
|
self.service_items[pos - 1]['expanded'] = True
|
|
|
|
def on_service_top(self, field=None):
|
|
"""
|
|
Move the current ServiceItem to the top of the list.
|
|
:param field:
|
|
"""
|
|
item, child = self.find_service_item()
|
|
if item < len(self.service_items) and item != -1:
|
|
temp = self.service_items[item]
|
|
self.service_items.remove(self.service_items[item])
|
|
self.service_items.insert(0, temp)
|
|
self.repaint_service_list(0, child)
|
|
self.set_modified()
|
|
|
|
def on_service_up(self, field=None):
|
|
"""
|
|
Move the current ServiceItem one position up in the list.
|
|
:param field:
|
|
"""
|
|
item, child = self.find_service_item()
|
|
if item > 0:
|
|
temp = self.service_items[item]
|
|
self.service_items.remove(self.service_items[item])
|
|
self.service_items.insert(item - 1, temp)
|
|
self.repaint_service_list(item - 1, child)
|
|
self.set_modified()
|
|
|
|
def on_service_down(self, field=None):
|
|
"""
|
|
Move the current ServiceItem one position down in the list.
|
|
:param field:
|
|
"""
|
|
item, child = self.find_service_item()
|
|
if item < len(self.service_items) and item != -1:
|
|
temp = self.service_items[item]
|
|
self.service_items.remove(self.service_items[item])
|
|
self.service_items.insert(item + 1, temp)
|
|
self.repaint_service_list(item + 1, child)
|
|
self.set_modified()
|
|
|
|
def on_service_end(self, field=None):
|
|
"""
|
|
Move the current ServiceItem to the bottom of the list.
|
|
:param field:
|
|
"""
|
|
item, child = self.find_service_item()
|
|
if item < len(self.service_items) and item != -1:
|
|
temp = self.service_items[item]
|
|
self.service_items.remove(self.service_items[item])
|
|
self.service_items.insert(len(self.service_items), temp)
|
|
self.repaint_service_list(len(self.service_items) - 1, child)
|
|
self.set_modified()
|
|
|
|
def on_delete_from_service(self, field=None):
|
|
"""
|
|
Remove the current ServiceItem from the list.
|
|
:param field:
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
if item != -1:
|
|
self.service_items.remove(self.service_items[item])
|
|
self.repaint_service_list(item - 1, -1)
|
|
self.set_modified()
|
|
|
|
def repaint_service_list(self, service_item, service_item_child):
|
|
"""
|
|
Clear the existing service list and prepaint all the items. This is used when moving items as the move takes
|
|
place in a supporting list, and when regenerating all the items due to theme changes.
|
|
|
|
:param service_item: The item which changed. (int)
|
|
:param service_item_child: The child of the ``service_item``, which will be selected. (int)
|
|
"""
|
|
# Correct order of items in array
|
|
count = 1
|
|
self.service_has_all_original_files = True
|
|
for item in self.service_items:
|
|
item['order'] = count
|
|
count += 1
|
|
if not item['service_item'].has_original_files:
|
|
self.service_has_all_original_files = False
|
|
# Repaint the screen
|
|
self.service_manager_list.clear()
|
|
self.service_manager_list.clearSelection()
|
|
for item_count, item in enumerate(self.service_items):
|
|
service_item_from_item = item['service_item']
|
|
tree_widget_item = QtWidgets.QTreeWidgetItem(self.service_manager_list)
|
|
if service_item_from_item.is_valid:
|
|
if service_item_from_item.notes:
|
|
icon = QtGui.QImage(service_item_from_item.icon)
|
|
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
|
overlay = QtGui.QImage(':/services/service_item_notes.png')
|
|
overlay = overlay.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
|
painter = QtGui.QPainter(icon)
|
|
painter.drawImage(0, 0, overlay)
|
|
painter.end()
|
|
tree_widget_item.setIcon(0, build_icon(icon))
|
|
elif service_item_from_item.temporary_edit:
|
|
icon = QtGui.QImage(service_item_from_item.icon)
|
|
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
|
overlay = QtGui.QImage(':/general/general_export.png')
|
|
overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
|
painter = QtGui.QPainter(icon)
|
|
painter.drawImage(40, 0, overlay)
|
|
painter.end()
|
|
tree_widget_item.setIcon(0, build_icon(icon))
|
|
else:
|
|
tree_widget_item.setIcon(0, service_item_from_item.iconic_representation)
|
|
else:
|
|
tree_widget_item.setIcon(0, build_icon(':/general/general_delete.png'))
|
|
tree_widget_item.setText(0, service_item_from_item.get_display_title())
|
|
tips = []
|
|
if service_item_from_item.temporary_edit:
|
|
tips.append('<strong>%s:</strong> <em>%s</em>' % (translate('OpenLP.ServiceManager', 'Edit'),
|
|
(translate('OpenLP.ServiceManager', 'Service copy only'))))
|
|
if service_item_from_item.theme and service_item_from_item.theme != -1:
|
|
tips.append('<strong>%s:</strong> <em>%s</em>' %
|
|
(translate('OpenLP.ServiceManager', 'Slide theme'), service_item_from_item.theme))
|
|
if service_item_from_item.notes:
|
|
tips.append('<strong>%s: </strong> %s' %
|
|
(translate('OpenLP.ServiceManager', 'Notes'), html.escape(service_item_from_item.notes)))
|
|
if item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
|
|
tips.append(item['service_item'].get_media_time())
|
|
tree_widget_item.setToolTip(0, '<br>'.join(tips))
|
|
tree_widget_item.setData(0, QtCore.Qt.UserRole, item['order'])
|
|
tree_widget_item.setSelected(item['selected'])
|
|
# Add the children to their parent tree_widget_item.
|
|
for count, frame in enumerate(service_item_from_item.get_frames()):
|
|
child = QtWidgets.QTreeWidgetItem(tree_widget_item)
|
|
# prefer to use a display_title
|
|
if service_item_from_item.is_capable(ItemCapabilities.HasDisplayTitle):
|
|
text = frame['display_title'].replace('\n', ' ')
|
|
else:
|
|
text = frame['title'].replace('\n', ' ')
|
|
child.setText(0, text[:40])
|
|
child.setData(0, QtCore.Qt.UserRole, count)
|
|
if service_item == item_count:
|
|
if item['expanded'] and service_item_child == count:
|
|
self.service_manager_list.setCurrentItem(child)
|
|
elif service_item_child == -1:
|
|
self.service_manager_list.setCurrentItem(tree_widget_item)
|
|
tree_widget_item.setExpanded(item['expanded'])
|
|
|
|
def clean_up(self):
|
|
"""
|
|
Empties the service_path of temporary files on system exit.
|
|
"""
|
|
for file_name in os.listdir(self.service_path):
|
|
file_path = os.path.join(self.service_path, file_name)
|
|
delete_file(file_path)
|
|
if os.path.exists(os.path.join(self.service_path, 'audio')):
|
|
shutil.rmtree(os.path.join(self.service_path, 'audio'), True)
|
|
|
|
def on_theme_combo_box_selected(self, current_index):
|
|
"""
|
|
Set the theme for the current service.
|
|
|
|
:param current_index: The combo box index for the selected item
|
|
"""
|
|
self.service_theme = self.theme_combo_box.currentText()
|
|
self.renderer.set_service_theme(self.service_theme)
|
|
Settings().setValue(self.main_window.service_manager_settings_section + '/service theme', self.service_theme)
|
|
self.regenerate_service_items(True)
|
|
|
|
def theme_change(self):
|
|
"""
|
|
The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
|
|
"""
|
|
visible = self.renderer.theme_level == ThemeLevel.Global
|
|
self.theme_label.setVisible(visible)
|
|
self.theme_combo_box.setVisible(visible)
|
|
|
|
def regenerate_service_items(self, changed=False):
|
|
"""
|
|
Rebuild the service list as things have changed and a repaint is the easiest way to do this.
|
|
|
|
:param changed: True if the list has changed for new / removed items. False for a theme change.
|
|
"""
|
|
self.application.set_busy_cursor()
|
|
# force reset of renderer as theme data has changed
|
|
self.service_has_all_original_files = True
|
|
if self.service_items:
|
|
for item in self.service_items:
|
|
item['selected'] = False
|
|
service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
|
|
selected_item = None
|
|
while service_iterator.value():
|
|
if service_iterator.value().isSelected():
|
|
selected_item = service_iterator.value()
|
|
service_iterator += 1
|
|
if selected_item is not None:
|
|
if selected_item.parent() is None:
|
|
pos = selected_item.data(0, QtCore.Qt.UserRole)
|
|
else:
|
|
pos = selected_item.parent().data(0, QtCore.Qt.UserRole)
|
|
self.service_items[pos - 1]['selected'] = True
|
|
temp_service_items = self.service_items
|
|
self.service_manager_list.clear()
|
|
self.service_items = []
|
|
self.is_new = True
|
|
for item in temp_service_items:
|
|
self.add_service_item(item['service_item'], False, expand=item['expanded'], repaint=False,
|
|
selected=item['selected'])
|
|
# Set to False as items may have changed rendering does not impact the saved song so True may also be valid
|
|
if changed:
|
|
self.set_modified()
|
|
# Repaint it once only at the end
|
|
self.repaint_service_list(-1, -1)
|
|
self.application.set_normal_cursor()
|
|
|
|
def replace_service_item(self, new_item):
|
|
"""
|
|
Using the service item passed replace the one with the same edit id if found.
|
|
|
|
:param new_item: a new service item to up date an existing one.
|
|
"""
|
|
for item_count, item in enumerate(self.service_items):
|
|
if item['service_item'].edit_id == new_item.edit_id and item['service_item'].name == new_item.name:
|
|
new_item.render()
|
|
new_item.merge(item['service_item'])
|
|
item['service_item'] = new_item
|
|
self.repaint_service_list(item_count + 1, 0)
|
|
self.live_controller.replace_service_manager_item(new_item)
|
|
self.set_modified()
|
|
|
|
def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False,
|
|
position=-1):
|
|
"""
|
|
Add a Service item to the list
|
|
|
|
:param item: Service Item to be added
|
|
:param rebuild: Do we need to rebuild the live display (Default False)
|
|
:param expand: Override the default expand settings. (Tristate)
|
|
:param replace: Is the service item a replacement (Default False)
|
|
:param repaint: Do we need to repaint the service item list (Default True)
|
|
:param selected: Has the item been selected (Default False)
|
|
:param position: The position where the item is dropped (Default -1)
|
|
"""
|
|
# if not passed set to config value
|
|
if expand is None:
|
|
expand = Settings().value('advanced/expand service item')
|
|
item.from_service = True
|
|
if position != -1:
|
|
self.drop_position = position
|
|
if replace:
|
|
s_item, child = self.find_service_item()
|
|
item.merge(self.service_items[s_item]['service_item'])
|
|
self.service_items[s_item]['service_item'] = item
|
|
self.repaint_service_list(s_item, child)
|
|
self.live_controller.replace_service_manager_item(item)
|
|
else:
|
|
item.render()
|
|
# nothing selected for dnd
|
|
if self.drop_position == -1:
|
|
if isinstance(item, list):
|
|
for ind_item in item:
|
|
self.service_items.append({'service_item': ind_item,
|
|
'order': len(self.service_items) + 1,
|
|
'expanded': expand, 'selected': selected})
|
|
else:
|
|
self.service_items.append({'service_item': item,
|
|
'order': len(self.service_items) + 1,
|
|
'expanded': expand, 'selected': selected})
|
|
if repaint:
|
|
self.repaint_service_list(len(self.service_items) - 1, -1)
|
|
else:
|
|
self.service_items.insert(self.drop_position,
|
|
{'service_item': item, 'order': self.drop_position,
|
|
'expanded': expand, 'selected': selected})
|
|
self.repaint_service_list(self.drop_position, -1)
|
|
# if rebuilding list make sure live is fixed.
|
|
if rebuild:
|
|
self.live_controller.replace_service_manager_item(item)
|
|
self.drop_position = -1
|
|
self.set_modified()
|
|
|
|
def make_preview(self, field=None):
|
|
"""
|
|
Send the current item to the Preview slide controller
|
|
:param field:
|
|
"""
|
|
self.application.set_busy_cursor()
|
|
item, child = self.find_service_item()
|
|
if self.service_items[item]['service_item'].is_valid:
|
|
self.preview_controller.add_service_manager_item(self.service_items[item]['service_item'], child)
|
|
else:
|
|
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
|
translate('OpenLP.ServiceManager',
|
|
'Your item cannot be displayed as there is no handler to display it'))
|
|
self.application.set_normal_cursor()
|
|
|
|
def get_service_item(self):
|
|
"""
|
|
Send the current item to the Preview slide controller
|
|
"""
|
|
item = self.find_service_item()[0]
|
|
if item == -1:
|
|
return False
|
|
else:
|
|
return self.service_items[item]['service_item']
|
|
|
|
def on_double_click_live(self, field=None):
|
|
"""
|
|
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()
|
|
|
|
def on_single_click_preview(self, field=None):
|
|
"""
|
|
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:
|
|
# If a double click has not registered start a timer, otherwise wait for the existing timer to finish.
|
|
QtCore.QTimer.singleShot(QtWidgets.QApplication.instance().doubleClickInterval(),
|
|
self.on_single_click_preview_timeout)
|
|
|
|
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.
|
|
self.list_double_clicked = False
|
|
else:
|
|
# Otherwise preview the item.
|
|
self.make_preview()
|
|
|
|
def make_live(self, row=-1):
|
|
"""
|
|
Send the current item to the Live slide controller
|
|
|
|
:param row: Row number to be displayed if from preview. -1 is passed if the value is not set
|
|
"""
|
|
item, child = self.find_service_item()
|
|
# No items in service
|
|
if item == -1:
|
|
return
|
|
if row != -1:
|
|
child = row
|
|
self.application.set_busy_cursor()
|
|
if self.service_items[item]['service_item'].is_valid:
|
|
self.live_controller.add_service_manager_item(self.service_items[item]['service_item'], child)
|
|
if Settings().value(self.main_window.general_settings_section + '/auto preview'):
|
|
item += 1
|
|
if self.service_items and item < len(self.service_items) and \
|
|
self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanPreview):
|
|
self.preview_controller.add_service_manager_item(self.service_items[item]['service_item'], 0)
|
|
self.live_controller.preview_widget.setFocus()
|
|
else:
|
|
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
|
translate('OpenLP.ServiceManager',
|
|
'Your item cannot be displayed as the plugin required to display it '
|
|
'is missing or inactive'))
|
|
self.application.set_normal_cursor()
|
|
|
|
def remote_edit(self, field=None):
|
|
"""
|
|
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):
|
|
new_item = Registry().get(self.service_items[item]['service_item'].name). \
|
|
on_remote_edit(self.service_items[item]['service_item'].edit_id)
|
|
if new_item:
|
|
self.add_service_item(new_item, replace=True)
|
|
|
|
def on_service_item_rename(self, field=None):
|
|
"""
|
|
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):
|
|
return
|
|
title = self.service_items[item]['service_item'].title
|
|
title, ok = QtWidgets.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'),
|
|
translate('OpenLP.ServiceManager', 'Title:'),
|
|
QtWidgets.QLineEdit.Normal, self.tr(title))
|
|
if ok:
|
|
self.service_items[item]['service_item'].title = title
|
|
self.repaint_service_list(item, -1)
|
|
self.set_modified()
|
|
|
|
def create_custom(self, field=None):
|
|
"""
|
|
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'])
|
|
|
|
def find_service_item(self):
|
|
"""
|
|
Finds the first selected ServiceItem in the list and returns the position of the service_item_from_item and its
|
|
selected child item. For example, if the third child item (in the Slidecontroller known as slide) in the
|
|
second service item is selected this will return::
|
|
|
|
(1, 2)
|
|
"""
|
|
items = self.service_manager_list.selectedItems()
|
|
service_item = -1
|
|
service_item_child = -1
|
|
for item in items:
|
|
parent_item = item.parent()
|
|
if parent_item is None:
|
|
service_item = item.data(0, QtCore.Qt.UserRole)
|
|
else:
|
|
service_item = parent_item.data(0, QtCore.Qt.UserRole)
|
|
service_item_child = item.data(0, QtCore.Qt.UserRole)
|
|
# Adjust for zero based arrays.
|
|
service_item -= 1
|
|
# Only process the first item on the list for this method.
|
|
break
|
|
return service_item, service_item_child
|
|
|
|
def drop_event(self, event):
|
|
"""
|
|
Receive drop event and trigger an internal event to get the plugins to build and push the correct service item.
|
|
The drag event payload carries the plugin name
|
|
|
|
:param event: Handle of the event passed
|
|
"""
|
|
link = event.mimeData()
|
|
if link.hasUrls():
|
|
event.setDropAction(QtCore.Qt.CopyAction)
|
|
event.accept()
|
|
for url in link.urls():
|
|
file_name = url.toLocalFile()
|
|
if file_name.endswith('.osz'):
|
|
self.on_load_service_clicked(file_name)
|
|
elif file_name.endswith('.oszl'):
|
|
# todo correct
|
|
self.on_load_service_clicked(file_name)
|
|
elif link.hasText():
|
|
plugin = link.text()
|
|
item = self.service_manager_list.itemAt(event.pos())
|
|
# ServiceManager started the drag and drop
|
|
if plugin == 'ServiceManager':
|
|
start_pos, child = self.find_service_item()
|
|
# If no items selected
|
|
if start_pos == -1:
|
|
return
|
|
if item is None:
|
|
end_pos = len(self.service_items)
|
|
else:
|
|
end_pos = get_parent_item_data(item) - 1
|
|
service_item = self.service_items[start_pos]
|
|
self.service_items.remove(service_item)
|
|
self.service_items.insert(end_pos, service_item)
|
|
self.repaint_service_list(end_pos, child)
|
|
self.set_modified()
|
|
else:
|
|
# we are not over anything so drop
|
|
replace = False
|
|
if item is None:
|
|
self.drop_position = len(self.service_items)
|
|
else:
|
|
# we are over something so lets investigate
|
|
pos = get_parent_item_data(item) - 1
|
|
service_item = self.service_items[pos]
|
|
if (plugin == service_item['service_item'].name and
|
|
service_item['service_item'].is_capable(ItemCapabilities.CanAppend)):
|
|
action = self.dnd_menu.exec(QtGui.QCursor.pos())
|
|
# New action required
|
|
if action == self.new_action:
|
|
self.drop_position = get_parent_item_data(item)
|
|
# Append to existing action
|
|
if action == self.add_to_action:
|
|
self.drop_position = get_parent_item_data(item)
|
|
item.setSelected(True)
|
|
replace = True
|
|
else:
|
|
self.drop_position = get_parent_item_data(item) - 1
|
|
Registry().execute('%s_add_service_item' % plugin, replace)
|
|
|
|
def update_theme_list(self, theme_list):
|
|
"""
|
|
Called from ThemeManager when the Themes have changed
|
|
|
|
:param theme_list: A list of current themes to be displayed
|
|
"""
|
|
self.theme_combo_box.clear()
|
|
self.theme_menu.clear()
|
|
self.theme_combo_box.addItem('')
|
|
theme_group = QtWidgets.QActionGroup(self.theme_menu)
|
|
theme_group.setExclusive(True)
|
|
theme_group.setObjectName('theme_group')
|
|
# Create a "Default" theme, which allows the user to reset the item's theme to the service theme or global
|
|
# theme.
|
|
default_theme = create_widget_action(self.theme_menu, text=UiStrings().Default, checked=False,
|
|
triggers=self.on_theme_change_action)
|
|
self.theme_menu.setDefaultAction(default_theme)
|
|
theme_group.addAction(default_theme)
|
|
self.theme_menu.addSeparator()
|
|
for theme in theme_list:
|
|
self.theme_combo_box.addItem(theme)
|
|
theme_group.addAction(create_widget_action(self.theme_menu, theme, text=theme, checked=False,
|
|
triggers=self.on_theme_change_action))
|
|
find_and_set_in_combo_box(self.theme_combo_box, self.service_theme)
|
|
self.renderer.set_service_theme(self.service_theme)
|
|
self.regenerate_service_items()
|
|
|
|
def on_theme_change_action(self, field=None):
|
|
"""
|
|
Handles theme change events
|
|
|
|
:param field:
|
|
"""
|
|
theme = self.sender().objectName()
|
|
# No object name means that the "Default" theme is supposed to be used.
|
|
if not theme:
|
|
theme = None
|
|
item = self.find_service_item()[0]
|
|
self.service_items[item]['service_item'].update_theme(theme)
|
|
self.regenerate_service_items(True)
|
|
|
|
def get_drop_position(self):
|
|
"""
|
|
Getter for drop_position. Used in: MediaManagerItem
|
|
"""
|
|
return self.drop_position
|
|
|
|
|
|
def get_parent_item_data(item):
|
|
"""
|
|
Finds and returns the parent item for any item
|
|
|
|
:param item: The service item list item to be checked.
|
|
"""
|
|
parent_item = item.parent()
|
|
if parent_item is None:
|
|
return item.data(0, QtCore.Qt.UserRole)
|
|
else:
|
|
return parent_item.data(0, QtCore.Qt.UserRole)
|