diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro
index 7550048..e7e8974 100644
--- a/kiwix-desktop.pro
+++ b/kiwix-desktop.pro
@@ -44,7 +44,7 @@ SOURCES += \
src/blobbuffer.cpp \
src/library.cpp \
src/settingsmanager.cpp \
- src/settingsmanagerview.cpp \
+ src/settingsview.cpp \
src/topwidget.cpp \
src/urlschemehandler.cpp \
src/webview.cpp \
@@ -76,7 +76,7 @@ HEADERS += \
src/blobbuffer.h \
src/library.h \
src/settingsmanager.h \
- src/settingsmanagerview.h \
+ src/settingsview.h \
src/topwidget.h \
src/kconstants.h \
src/urlschemehandler.h \
@@ -105,7 +105,8 @@ FORMS += \
ui/about.ui \
src/contentmanagerside.ui \
src/readinglistbar.ui \
- ui/localkiwixserver.ui
+ ui/localkiwixserver.ui \
+ ui/settings.ui
include(subprojects/QtSingleApplication/src/qtsingleapplication.pri)
CODECFORSRC = UTF-8
diff --git a/resources/css/_settingsManager.css b/resources/css/_settingsManager.css
index 1321b52..6452da3 100644
--- a/resources/css/_settingsManager.css
+++ b/resources/css/_settingsManager.css
@@ -1,88 +1,40 @@
-html, body {
- padding: 0;
- margin: auto;
- height: 100%;
- position: relative;
- width: 80%;
- overflow: hidden;
+QWidget#widget {
+ max-width: 500;
}
-#settings {
- height: 100%;
- position: relative;
+QLabel,
+QPushButton,
+QSpinBox {
+ font-size: 16px;
+ line-height: 24px;
+ padding: 8px;
}
-#header {
- display: flex;
- flex-direction: row;
- flex-wrap: nowrap;
- justify-content: space-between;
+QFrame[frameShape="4"] {
+ /*frameShape 4 is HLine */
+ border: 1px solid #eaecf0;
+ min-height: 0pt;
+ max-height: 1pt;
}
-.row {
- display: flex;
- flex-direction: row;
- flex-wrap: nowrap;
- justify-content: space-between;
- align-items: center;
- border-top: 1px solid grey;
- height: 40px;
-}
-
-
-input {
- margin: 5px;
-}
-
-#download_dir {
- flex: 1;
- margin: 5px;
- overflow: hidden;
- direction: rtl;
-}
-
-#download_dir > span {
- display: block;
- float: right;
- direction: ltr;
- min-width: 100%;
- font-size: 0.9em;
-}
-
-input[type=number] {
- width: 80px;
-}
-
-input[type=button] {
- color: blue;
+QLabel#settingsLabel {
+ font-size: 32px;
+ line-height: 44px;
font-weight: bold;
- font-size: 14px;
- border: 0;
- background: transparent;
- border-radius: 2px;
+}
+QSpinBox,
+QLabel#downloadDirPath {
+ font: bold 12pt "Cantarell";
+}
+QPushButton {
+ background-color: white;
+ color: #3366cc;
+ padding: 4px;
+ font: bold;
+ border-radius: 4px;
}
-input[type=button]:hover {
+QPushButton:hover {
+ background-color: #3366cc;
color: white;
- background: blue;
-}
-
-input[type=number]::-webkit-inner-spin-button,
-input[type=number]::-webkit-outer-spin-button {
- opacity: 1;
-}
-
-div.percentage-symbol {
- position: relative;
-}
-
-div.percentage-symbol:before {
- position: absolute;
- content: "%";
- top: 0.4em;
- right: 1.5em;
-}
-
-div.percentage-symbol input {
- text-align: left;
}
diff --git a/resources/js/_settingsManager.js b/resources/js/_settingsManager.js
deleted file mode 100644
index 954cdf0..0000000
--- a/resources/js/_settingsManager.js
+++ /dev/null
@@ -1,61 +0,0 @@
-function onDownloadDirChanged (downloadDir) {
- app.downloadDir = downloadDir;
-}
-
-function validPort (port) {
- return /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/.test(port);
-}
-
-function setTranslations(translations) {
- app.translations = createDict(TRANSLATION_KEYS, translations);
-}
-
-const TRANSLATION_KEYS = ["settings",
- "port-for-local-kiwix-server-setting",
- "zoom-level-setting",
- "download-directory-setting",
- "reset",
- "browse",
- "invalid-port"];
-
-function init() {
- new QWebChannel(qt.webChannelTransport, function(channel) {
- settingsManager = channel.objects.settingsManager;
- app = new Vue({
- el: "#settings",
- data: {
- settingsManager: settingsManager,
- kiwixServerPort: settingsManager.kiwixServerPort,
- zoomFactor: Math.floor(settingsManager.zoomFactor * 100),
- downloadDir: settingsManager.downloadDir,
- translations:{}
- },
- methods: {
- gt : function(key) {
- return this.translations[key];
- },
- setPort : function() {
- if (!validPort(this.kiwixServerPort)) {
- alert(this.gt("invalid-port"));
- this.kiwixServerPort = settingsManager.kiwixServerPort;
- return;
- }
- settingsManager.setKiwixServerPort(this.kiwixServerPort);
- },
- setZoomFactor : function() {
- this.zoomFactor = (this.zoomFactor < 30) ? 30 : this.zoomFactor;
- this.zoomFactor = (this.zoomFactor > 500) ? 500 : this.zoomFactor;
- settingsManager.setZoomFactor(this.zoomFactor / 100);
- },
- resetDownloadDir : function() {
- settingsManager.resetDownloadDir();
- },
- browseDownloadDir : function() {
- settingsManager.browseDownloadDir();
- }
- }
- });
- settingsManager.downloadDirChanged.connect(onDownloadDirChanged)
- settingsManager.getTranslations(TRANSLATION_KEYS, setTranslations);
- });
-}
\ No newline at end of file
diff --git a/resources/settingsmanager.qrc b/resources/settingsmanager.qrc
index 5d32dec..92722e9 100644
--- a/resources/settingsmanager.qrc
+++ b/resources/settingsmanager.qrc
@@ -1,7 +1,5 @@
- texts/_settingsManager.html
css/_settingsManager.css
- js/_settingsManager.js
diff --git a/resources/texts/_settingsManager.html b/resources/texts/_settingsManager.html
deleted file mode 100644
index 32c0594..0000000
--- a/resources/texts/_settingsManager.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/kiwixapp.cpp b/src/kiwixapp.cpp
index 3772fc4..47b7f68 100644
--- a/src/kiwixapp.cpp
+++ b/src/kiwixapp.cpp
@@ -17,7 +17,6 @@
KiwixApp::KiwixApp(int& argc, char *argv[])
: QtSingleApplication("kiwix-desktop", argc, argv),
- m_settingsManager(),
m_profile(),
m_libraryDirectory(findLibraryDirectory()),
m_library(m_libraryDirectory),
diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp
index 9412c41..ef1ff01 100644
--- a/src/settingsmanager.cpp
+++ b/src/settingsmanager.cpp
@@ -4,23 +4,23 @@
#include
#include
#include
-
SettingsManager::SettingsManager(QObject *parent)
: QObject(parent),
m_settings("Kiwix", "Kiwix-desktop"),
- m_settingsViewDisplayed(false)
+ m_view(nullptr)
{
initSettings();
}
-SettingsManagerView* SettingsManager::getView()
+SettingsView* SettingsManager::getView()
{
- auto view = new SettingsManagerView();
- view->registerObject("settingsManager", this);
- view->setHtml();
- connect(view, &QObject::destroyed, this, [=]() { m_settingsViewDisplayed = false; });
- m_settingsViewDisplayed = true;
- return view;
+ if (m_view == nullptr) {
+ auto view = new SettingsView();
+ view->init(m_kiwixServerPort, m_zoomFactor * 100, m_downloadDir);
+ connect(view, &QObject::destroyed, this, [=]() { m_view = nullptr; });
+ m_view = view;
+ }
+ return m_view;
}
void SettingsManager::setSettings(const QString &key, const QVariant &value)
@@ -64,69 +64,17 @@ void SettingsManager::setZoomFactor(qreal zoomFactor)
{
m_zoomFactor = zoomFactor;
m_settings.setValue("view/zoomFactor", zoomFactor);
+ emit(zoomChanged(zoomFactor));
}
bool SettingsManager::setDownloadDir(QString downloadDir)
{
m_downloadDir = downloadDir;
m_settings.setValue("download/dir", downloadDir);
+ emit(downloadDirChanged(downloadDir));
return true;
}
-bool SettingsManager::confirmDialogDownloadDir(const QString& dir)
-{
- auto text = gt("download-dir-dialog-msg");
- text = text.replace("{{DIRECTORY}}", dir);
- QMessageBox msgBox(
- QMessageBox::Question, //Icon
- gt("download-dir-dialog-title"), //Title
- text, //Text
- QMessageBox::Ok | QMessageBox::Cancel //Buttons
- );
- msgBox.setDefaultButton(QMessageBox::Ok);
-
- int ret = msgBox.exec();
- return (ret == QMessageBox::Ok);
-}
-
-void SettingsManager::resetDownloadDir()
-{
- auto dir = QString::fromStdString(kiwix::getDataDirectory());
- if (dir == m_downloadDir) {
- return;
- }
- if (confirmDialogDownloadDir(dir)) {
- setDownloadDir(dir);
- emit(downloadDirChanged(dir));
- }
-}
-
-void SettingsManager::browseDownloadDir()
-{
- QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(),
- gt("browse-directory"),
- m_downloadDir,
- QFileDialog::ShowDirsOnly);
- if (dir == m_downloadDir || dir.isEmpty()) {
- return;
- }
-
- if (confirmDialogDownloadDir(dir)) {
- setDownloadDir(dir);
- emit(downloadDirChanged(dir));
- }
-}
-
-QStringList SettingsManager::getTranslations(const QStringList &keys)
-{
- QStringList translations;
-
- for(auto& key: keys) {
- translations.append(KiwixApp::instance()->getText(key));
- }
- return translations;
-}
-
void SettingsManager::initSettings()
{
m_kiwixServerPort = m_settings.value("localKiwixServer/port", 8181).toInt();
diff --git a/src/settingsmanager.h b/src/settingsmanager.h
index 0cfaf48..1940df1 100644
--- a/src/settingsmanager.h
+++ b/src/settingsmanager.h
@@ -3,21 +3,20 @@
#include
#include
-#include "settingsmanagerview.h"
+#include "settingsview.h"
class SettingsManager : public QObject
{
Q_OBJECT
Q_PROPERTY(int kiwixServerPort READ getKiwixServerPort NOTIFY portChanged)
- Q_PROPERTY(qreal zoomFactor READ getZoomFactor NOTIFY zoomChanged)
- Q_PROPERTY(QString downloadDir READ getDownloadDir NOTIFY downloadDirChanged)
+ Q_PROPERTY(qreal zoomFactor MEMBER m_zoomFactor WRITE setZoomFactor NOTIFY zoomChanged)
+ Q_PROPERTY(QString downloadDir MEMBER m_downloadDir WRITE setDownloadDir NOTIFY downloadDirChanged)
public:
explicit SettingsManager(QObject *parent = nullptr);
virtual ~SettingsManager() {};
- SettingsManagerView* getView();
- bool isSettingsViewdisplayed() { return m_settingsViewDisplayed; };
+ SettingsView* getView();
void setSettings(const QString &key, const QVariant &value);
void deleteSettings(const QString &key);
bool settingsExists(const QString &key);
@@ -25,19 +24,14 @@ public:
qreal getZoomFactorByZimId(const QString &id);
public slots:
- QStringList getTranslations(const QStringList &keys);
void setKiwixServerPort(int port);
int getKiwixServerPort() { return m_kiwixServerPort; };
void setZoomFactor(qreal zoomFactor);
qreal getZoomFactor() { return m_zoomFactor; };
bool setDownloadDir(QString downloadDir);
QString getDownloadDir() { return m_downloadDir; }
- void resetDownloadDir();
- void browseDownloadDir();
-
private:
void initSettings();
- bool confirmDialogDownloadDir(const QString& dir);
signals:
void portChanged(int port);
@@ -46,7 +40,7 @@ signals:
private:
QSettings m_settings;
- bool m_settingsViewDisplayed;
+ SettingsView *m_view;
int m_kiwixServerPort;
qreal m_zoomFactor;
QString m_downloadDir;
diff --git a/src/settingsmanagerview.cpp b/src/settingsmanagerview.cpp
deleted file mode 100644
index 2082c7d..0000000
--- a/src/settingsmanagerview.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "settingsmanagerview.h"
-#include "kiwixapp.h"
-#include
-
-SettingsManagerView::SettingsManagerView(QWidget *parent) : QWebEngineView(parent)
-{
- page()->setWebChannel(&m_webChannel);
- setContextMenuPolicy( Qt::NoContextMenu );
-}
-
-void SettingsManagerView::registerObject(const QString& id, QObject* object)
-{
- m_webChannel.registerObject(id, object);
-}
-
-void SettingsManagerView::setHtml()
-{
- QFile contentFile(":texts/_settingsManager.html");
- contentFile.open(QIODevice::ReadOnly);
- auto byteContent = contentFile.readAll();
- contentFile.close();
- QWebEngineView::setHtml(byteContent);
-}
diff --git a/src/settingsmanagerview.h b/src/settingsmanagerview.h
deleted file mode 100644
index de5dfc0..0000000
--- a/src/settingsmanagerview.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef SETTINGSMANAGERVIEW_H
-#define SETTINGSMANAGERVIEW_H
-
-#include
-#include
-
-class SettingsManagerView : public QWebEngineView
-{
- Q_OBJECT
-public:
- SettingsManagerView(QWidget *parent = nullptr);
- void registerObject(const QString &id, QObject *object);
- void setHtml();
-
-private:
- QWebChannel m_webChannel;
-};
-
-#endif // SETTINGSMANAGERVIEW_H
diff --git a/src/settingsview.cpp b/src/settingsview.cpp
new file mode 100644
index 0000000..23719f8
--- /dev/null
+++ b/src/settingsview.cpp
@@ -0,0 +1,105 @@
+#include "settingsview.h"
+#include "ui_settings.h"
+#include "kiwixapp.h"
+#include
+#include
+#include
+SettingsView::SettingsView(QWidget *parent)
+ : QWidget(parent)
+ , ui(new Ui::Settings)
+{
+ ui->setupUi(this);
+ QFile file(QString::fromUtf8(":/css/_settingsManager.css"));
+ file.open(QFile::ReadOnly);
+ QString styleSheet = QString(file.readAll());
+ ui->widget->setStyleSheet(styleSheet);
+ connect(ui->serverPortSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &SettingsView::setKiwixServerPort);
+ connect(ui->zoomPercentSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &SettingsView::setZoom);
+ connect(ui->browseButton, &QPushButton::clicked, this, &SettingsView::browseDownloadDir);
+ connect(ui->resetButton, &QPushButton::clicked, this, &SettingsView::resetDownloadDir);
+ connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::downloadDirChanged, this, &SettingsView::onDownloadDirChanged);
+ connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::zoomChanged, this, &SettingsView::onZoomChanged);
+ connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::portChanged, this, &SettingsView::onServerPortChanged);
+ ui->settingsLabel->setText(gt("settings"));
+ ui->serverPortLabel->setText(gt("port-for-local-kiwix-server-setting"));
+ ui->zoomPercentLabel->setText(gt("zoom-level-setting"));
+ ui->downloadDirLabel->setText(gt("download-directory-setting"));
+ ui->resetButton->setText(gt("reset"));
+ ui->browseButton->setText(gt("browse"));
+}
+void SettingsView::init(int port, int zoomPercent, const QString &dir)
+{
+ ui->serverPortSpinBox->setValue(port);
+ ui->zoomPercentSpinBox->setValue(zoomPercent);
+ ui->downloadDirPath->setText(dir);
+}
+bool SettingsView::confirmDialogDownloadDir(const QString& dir)
+{
+ auto text = gt("download-dir-dialog-msg");
+ text = text.replace("{{DIRECTORY}}", dir);
+ QMessageBox msgBox(
+ QMessageBox::Question, //Icon
+ gt("download-dir-dialog-title"), //Title
+ text, //Text
+ QMessageBox::Ok | QMessageBox::Cancel //Buttons
+ );
+ msgBox.setDefaultButton(QMessageBox::Ok);
+
+ int ret = msgBox.exec();
+ return (ret == QMessageBox::Ok);
+}
+
+void SettingsView::resetDownloadDir()
+{
+ auto dir = QString::fromStdString(kiwix::getDataDirectory());
+ const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir();
+ if (dir == downloadDir) {
+ return;
+ }
+ if (confirmDialogDownloadDir(dir)) {
+ KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir);
+ }
+}
+
+void SettingsView::browseDownloadDir()
+{
+ const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir();
+ QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(),
+ gt("browse-directory"),
+ downloadDir,
+ QFileDialog::ShowDirsOnly);
+ if (dir == downloadDir || dir.isEmpty()) {
+ return;
+ }
+
+ if (confirmDialogDownloadDir(dir)) {
+ KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir);
+ }
+}
+
+void SettingsView::setZoom(int zoomPercent)
+{
+ qreal zoomFactor = (qreal) zoomPercent/100;
+ KiwixApp::instance()->getSettingsManager()->setZoomFactor(zoomFactor);
+}
+
+void SettingsView::setKiwixServerPort(int port)
+{
+ KiwixApp::instance()->getSettingsManager()->setKiwixServerPort(port);
+}
+
+void SettingsView::onDownloadDirChanged(const QString &dir)
+{
+ ui->downloadDirPath->setText(dir);
+}
+
+void SettingsView::onZoomChanged(qreal zoomFactor)
+{
+ qreal zoomPercent = zoomFactor * 100;
+ ui->zoomPercentSpinBox->setValue(zoomPercent);
+}
+
+void SettingsView::onServerPortChanged(int port)
+{
+ ui->serverPortSpinBox->setValue(port);
+}
diff --git a/src/settingsview.h b/src/settingsview.h
new file mode 100644
index 0000000..0e80c22
--- /dev/null
+++ b/src/settingsview.h
@@ -0,0 +1,29 @@
+#ifndef SETTINGSVIEW_H
+#define SETTINGSVIEW_H
+
+#include
+namespace Ui {
+class Settings;
+}
+class SettingsView : public QWidget
+{
+ Q_OBJECT
+public:
+ SettingsView(QWidget *parent = nullptr);
+ ~SettingsView(){};
+ void init(int port, int zoomPercent, const QString &dir);
+public Q_SLOTS:
+ void resetDownloadDir();
+ void browseDownloadDir();
+ void setZoom(int zoomPercent);
+ void onDownloadDirChanged(const QString &dir);
+ void onZoomChanged(qreal zoomFactor);
+ void onServerPortChanged(int port);
+ void setKiwixServerPort(int port);
+private:
+ bool confirmDialogDownloadDir(const QString& dir);
+
+ Ui::Settings *ui;
+};
+
+#endif // SETTINGSVIEW_H
diff --git a/src/tabbar.cpp b/src/tabbar.cpp
index dc7e188..6f8c483 100644
--- a/src/tabbar.cpp
+++ b/src/tabbar.cpp
@@ -8,7 +8,6 @@
#include
#include
#include
-
#define QUITIFNULL(VIEW) if (nullptr==(VIEW)) { return; }
#define CURRENTIFNULL(VIEW) if(nullptr==VIEW) { VIEW = currentZimView();}
@@ -55,16 +54,17 @@ TabBar::TabBar(QWidget *parent) :
});
connect(app->getAction(KiwixApp::SettingAction), &QAction::triggered,
this, [=]() {
- for (int i = 0 ; i < (mp_stackedWidget->count() - 1) ; i++) {
- if (qobject_cast(mp_stackedWidget->widget(i))) {
+ SettingsView* view = KiwixApp::instance()->getSettingsManager()->getView();
+ for (int i = 0 ; i < mp_stackedWidget->count(); i++) {
+ if (mp_stackedWidget->widget(i) == view) {
setCurrentIndex(i);
return;
}
}
int index = currentIndex() + 1;
- SettingsManagerView* view = KiwixApp::instance()->getSettingsManager()->getView();
mp_stackedWidget->insertWidget(index, view);
insertTab(index,QIcon(":/icons/settings.svg"), gt("settings"));
+ KiwixApp::instance()->setSideBar(KiwixApp::SideBarType::NONE);
QToolButton *tb = new QToolButton(this);
tb->setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::CloseTabAction));
setTabButton(index, QTabBar::RightSide, tb);
@@ -294,7 +294,7 @@ void TabBar::onCurrentChanged(int index)
QWidget *w = mp_stackedWidget->widget(index);
- if (qobject_cast(w)) {
+ if (qobject_cast(w)) {
emit webActionEnabledChanged(QWebEnginePage::Back, false);
emit webActionEnabledChanged(QWebEnginePage::Forward, false);
emit libraryPageDisplayed(false);
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 3e89d7f..6505e0c 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -79,7 +79,11 @@
-
-
+
+
+ background-color: rgb(255, 255, 255);
+
+
diff --git a/ui/settings.ui b/ui/settings.ui
new file mode 100644
index 0000000..178e720
--- /dev/null
+++ b/ui/settings.ui
@@ -0,0 +1,280 @@
+
+
+ Settings
+
+
+
+ 0
+ 0
+ 1029
+ 580
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 600
+ 0
+
+
+
+ Form
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Settings
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Port for local kiwix server:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+
+ 1
+
+
+ 65535
+
+
+
+
+
+ -
+
+
+ true
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Zoom level:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+ %
+
+
+ 30
+
+
+ 500
+
+
+ 10
+
+
+ 100
+
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Download directory:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ true
+
+
+ true
+
+
+
+
+
+ Reset
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+ Browse
+
+
+ true
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+