diff --git a/lisp/application.py b/lisp/application.py
index babb50d47..07969fb25 100644
--- a/lisp/application.py
+++ b/lisp/application.py
@@ -206,6 +206,9 @@ def _load_from_file(self, session_file):
self.__session.update_properties(session_dict["session"])
self.__session.session_file = session_file
+ for plugin_name, plugin_config in session_dict['session']['plugins'].items():
+ self.__session.set_plugin_session_config(plugin_name, plugin_config)
+
# Load cues
for cues_dict in session_dict.get("cues", {}):
cue_type = cues_dict.pop("_type_", "Undefined")
diff --git a/lisp/core/plugin.py b/lisp/core/plugin.py
index f2f07a3a0..429182d7d 100644
--- a/lisp/core/plugin.py
+++ b/lisp/core/plugin.py
@@ -17,7 +17,10 @@
# You should have received a copy of the GNU General Public License
# along with Linux Show Player. If not, see .
-from lisp.core.configuration import DummyConfiguration
+import inspect
+from os import path
+
+from lisp.core.configuration import DummyConfiguration, JSONFileConfiguration
# TODO: add possible additional metadata (Icon, Version, ...)
@@ -42,3 +45,32 @@ def app(self):
def finalize(self):
"""Called when the application is getting closed."""
+
+ @property
+ def SessionConfig(self):
+ """Returns the plugin's session-specific config.
+
+ Fallsback to a "session.json" file (much like the app-level config "default.json" file)
+ or, if that doesn't exist, returns the plugin's app-level config file.
+
+ This second fallback is for plugins that might wish to set defaults on an app-level,
+ but also permit the user to override those defaults in/for specific sessions.
+ """
+ plugin_name = self.__module__.split('.')[-1]
+ if plugin_name in self.__app.session.plugins:
+ return self.__app.session.plugins[plugin_name]
+
+ plugin_path = path.join(
+ path.split(inspect.getfile(self.__class__))[0],
+ 'session.json')
+ if path.exists(plugin_path):
+ return JSONFileConfiguration._read_json(plugin_path)
+
+ return self.Config
+
+ def WriteSessionConfig(self, config):
+ """Writes the plugin's session-specific config to the active session.
+
+ When the user saves the showfile, it will then be written to disk.
+ """
+ self.__app.session.set_plugin_session_config(self.__module__.split('.')[-1], config)
diff --git a/lisp/core/session.py b/lisp/core/session.py
index b1f1e2cd1..456e16883 100644
--- a/lisp/core/session.py
+++ b/lisp/core/session.py
@@ -29,6 +29,7 @@ class Session(HasInstanceProperties):
layout = Property(default={})
session_file = Property(default="")
+ plugins = Property(default={})
def __init__(self, layout):
super().__init__()
@@ -60,6 +61,9 @@ def abs_path(self, rel_path):
return rel_path
+ def set_plugin_session_config(self, plugin_name, plugin_session_config):
+ self.plugins[plugin_name] = plugin_session_config
+
def rel_path(self, abs_path):
"""Return a relative (to the session-file) version of the given path."""
return os.path.relpath(abs_path, start=self.path())
diff --git a/lisp/plugins/__init__.py b/lisp/plugins/__init__.py
index db70e7906..db5795c80 100644
--- a/lisp/plugins/__init__.py
+++ b/lisp/plugins/__init__.py
@@ -52,7 +52,7 @@ def load_plugins(application):
else:
default_config_path = FALLBACK_CONFIG_PATH
- # Load plugin configuration
+ # Load plugin (app-level) configuration
config = JSONFileConfiguration(
path.join(USER_DIR, mod_name + ".json"), default_config_path
)
diff --git a/lisp/ui/mainwindow.py b/lisp/ui/mainwindow.py
index d68c96e4e..b4f14ce85 100644
--- a/lisp/ui/mainwindow.py
+++ b/lisp/ui/mainwindow.py
@@ -46,6 +46,7 @@
from lisp.ui.logging.status import LogStatusView
from lisp.ui.logging.viewer import LogViewer
from lisp.ui.settings.app_configuration import AppConfigurationDialog
+from lisp.ui.settings.session_configuration import SessionConfigurationDialog
from lisp.ui.ui_utils import translate
@@ -150,6 +151,12 @@ def __init__(self, conf, title="Linux Show Player", **kwargs):
self.menuEdit.addSeparator()
self.menuEdit.addAction(self.multiEdit)
+ # menuTools
+ self.sessionPreferences = QAction(self)
+ self.sessionPreferences.triggered.connect(self._show_session_preferences)
+ self.menuTools.addAction(self.sessionPreferences)
+ self.menuTools.addSeparator()
+
# menuAbout
self.actionAbout = QAction(self)
self.actionAbout.triggered.connect(self._show_about)
@@ -225,6 +232,7 @@ def retranslateUi(self):
# menuTools
self.menuTools.setTitle(translate("MainWindow", "&Tools"))
self.multiEdit.setText(translate("MainWindow", "Edit selection"))
+ self.sessionPreferences.setText(translate("MainWindow", "Session Preferences"))
# menuAbout
self.menuAbout.setTitle(translate("MainWindow", "&About"))
self.actionAbout.setText(translate("MainWindow", "About"))
@@ -310,6 +318,10 @@ def _show_preferences(self):
prefUi = AppConfigurationDialog(parent=self)
prefUi.exec_()
+ def _show_session_preferences(self):
+ prefUi = SessionConfigurationDialog(parent=self)
+ prefUi.exec_()
+
def _load_from_file(self):
if self._check_saved():
path, _ = QFileDialog.getOpenFileName(
diff --git a/lisp/ui/settings/session_configuration.py b/lisp/ui/settings/session_configuration.py
new file mode 100644
index 000000000..3e497ba08
--- /dev/null
+++ b/lisp/ui/settings/session_configuration.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Linux Show Player
+#
+# Copyright 2012-2016 Francesco Ceruti
+#
+# Linux Show Player 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linux Show Player 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 Linux Show Player. If not, see .
+
+import logging
+from collections import namedtuple
+
+from PyQt5 import QtCore
+from PyQt5.QtCore import QModelIndex
+from PyQt5.QtWidgets import QVBoxLayout, QDialogButtonBox, QDialog
+
+from lisp.core.dicttree import DictNode
+from lisp.core.util import typename
+from lisp.ui.settings.pages import ConfigurationPage, TreeMultiConfigurationWidget
+from lisp.ui.settings.pages_tree_model import PagesTreeModel
+from lisp.ui.ui_utils import translate
+
+logger = logging.getLogger(__name__)
+
+PageEntry = namedtuple('PageEntry', ('page', 'plugin'))
+
+
+class SessionConfigurationDialog(QDialog):
+ PagesRegistry = DictNode()
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.setWindowTitle(translate('SessionConfiguration', 'Session preferences'))
+ self.setWindowModality(QtCore.Qt.WindowModal)
+ self.setMaximumSize(800, 510)
+ self.setMinimumSize(800, 510)
+ self.resize(800, 510)
+ self.setLayout(QVBoxLayout())
+
+ self.model = PagesTreeModel()
+ for r_node in SessionConfigurationDialog.PagesRegistry.children:
+ self._populateModel(QModelIndex(), r_node)
+
+ self.mainPage = TreeMultiConfigurationWidget(self.model)
+ if len(self.PagesRegistry.children):
+ self.mainPage.selectFirst()
+ self.layout().addWidget(self.mainPage)
+
+ self.dialogButtons = QDialogButtonBox(self)
+ self.dialogButtons.setStandardButtons(
+ QDialogButtonBox.Cancel |
+ QDialogButtonBox.Apply |
+ QDialogButtonBox.Ok
+ )
+ self.layout().addWidget(self.dialogButtons)
+
+ self.dialogButtons.button(QDialogButtonBox.Cancel).clicked.connect(
+ self.reject)
+ self.dialogButtons.button(QDialogButtonBox.Apply).clicked.connect(
+ self.applySettings)
+ self.dialogButtons.button(QDialogButtonBox.Ok).clicked.connect(
+ self.__onOk)
+
+ def applySettings(self):
+ self.mainPage.applySettings()
+
+ def _populateModel(self, m_parent, r_parent):
+ if r_parent.value is not None:
+ page = r_parent.value.page
+ config = r_parent.value.plugin.SessionConfig
+ else:
+ page = None
+ config = None
+
+ try:
+ if page is None:
+ # The current node have no page, use the parent model-index
+ # as parent for it's children
+ mod_index = m_parent
+ elif issubclass(page, ConfigurationPage):
+ mod_index = self.model.addPage(page(config), parent=m_parent)
+ else:
+ mod_index = self.model.addPage(page(), parent=m_parent)
+ except Exception:
+ if not isinstance(page, type):
+ page_name = 'NoPage'
+ elif issubclass(page, ConfigurationPage):
+ page_name = page.Name
+ else:
+ page_name = page.__name__
+
+ logger.warning(
+ 'Cannot load configuration page: "{}" ({})'.format(
+ page_name, r_parent.path()), exc_info=True)
+ else:
+ for r_node in r_parent.children:
+ self._populateModel(mod_index, r_node)
+
+ def __onOk(self):
+ self.applySettings()
+ self.accept()
+
+ @staticmethod
+ def registerSettingsPage(path, page, plugin):
+ """
+ :param path: indicate the page "position": 'category.sub.key'
+ :type path: str
+ :type page: Type[lisp.ui.settings.settings_page.ConfigurationPage]
+ #:type config: lisp.core.configuration.Configuration
+ """
+ if issubclass(page, ConfigurationPage):
+ SessionConfigurationDialog.PagesRegistry.set(
+ path, PageEntry(page=page, plugin=plugin))
+ else:
+ raise TypeError(
+ 'SessionConfiguration pages must be ConfigurationPage(s), not {}'
+ .format(typename(page))
+ )
+
+ @staticmethod
+ def unregisterSettingsPage(path):
+ """
+ :param path: indicate the page "position": 'category.sub.key'
+ :type path: str
+ """
+ SessionConfigurationDialog.PagesRegistry.pop(path)