changeset 226:f784b5b1914c

settings: add library page
author Paper <mrpapersonic@gmail.com>
date Mon, 08 Jan 2024 21:23:00 -0500
parents 56ea2bdc6724
children c4f03f83b252
files CMakeLists.txt include/core/config.h include/core/strings.h include/gui/dialog/settings.h src/core/config.cc src/core/strings.cc src/gui/dialog/settings.cc src/gui/dialog/settings/library.cc src/gui/window.cc
diffstat 9 files changed, 257 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Jan 08 17:07:01 2024 -0500
+++ b/CMakeLists.txt	Mon Jan 08 21:23:00 2024 -0500
@@ -89,6 +89,7 @@
 	src/gui/dialog/settings/services.cc
 	src/gui/dialog/settings/torrents.cc
 	src/gui/dialog/settings/recognition.cc
+	src/gui/dialog/settings/library.cc
 
 	# Translate
 	src/gui/translate/anime.cc
--- a/include/core/config.h	Mon Jan 08 17:07:01 2024 -0500
+++ b/include/core/config.h	Mon Jan 08 21:23:00 2024 -0500
@@ -9,6 +9,7 @@
 
 #include <string>
 #include <vector>
+#include <set>
 #include <utility>
 
 struct MediaPlayer {
@@ -54,7 +55,8 @@
 		} torrents;
 
 		struct {
-			std::vector<std::string> paths;
+			bool real_time_monitor;
+			std::set<std::string> paths;
 		} library;
 };
 
--- a/include/core/strings.h	Mon Jan 08 17:07:01 2024 -0500
+++ b/include/core/strings.h	Mon Jan 08 21:23:00 2024 -0500
@@ -3,6 +3,7 @@
 
 #include <string>
 #include <vector>
+#include <set>
 #include <sstream>
 
 #include <cstdint>
@@ -16,6 +17,7 @@
  * into a string, separated by delimiters.
 */
 std::string Implode(const std::vector<std::string>& vector, const std::string& delimiter);
+std::string Implode(const std::set<std::string>& set, const std::string& delimiter);
 std::vector<std::string> Split(const std::string &text, const std::string& delimiter);
 
 /* Substring removal functions */
--- a/include/gui/dialog/settings.h	Mon Jan 08 17:07:01 2024 -0500
+++ b/include/gui/dialog/settings.h	Mon Jan 08 21:23:00 2024 -0500
@@ -86,6 +86,19 @@
 		decltype(session.config.recognition.players) players;
 };
 
+class SettingsPageLibrary final : public SettingsPage {
+		Q_OBJECT
+
+	public:
+		SettingsPageLibrary(QWidget* parent = nullptr);
+		void SaveInfo() override;
+
+	private:
+		QWidget* CreateFoldersWidget();
+		decltype(session.config.library.paths) paths;
+		decltype(session.config.library.real_time_monitor) real_time_monitor;
+};
+
 class SettingsDialog final : public QDialog {
 		Q_OBJECT
 
--- a/src/core/config.cc	Mon Jan 08 17:07:01 2024 -0500
+++ b/src/core/config.cc	Mon Jan 08 21:23:00 2024 -0500
@@ -93,7 +93,13 @@
 
 	theme.SetTheme(Translate::ToTheme(INI::GetIniValue<std::string>(ini, "Appearance", "Theme", "Default")));
 
-	library.paths = Strings::Split(INI::GetIniValue<std::string>(ini, "Library", "Folders", ""), ";");
+	{
+		std::vector<std::string> v = Strings::Split(INI::GetIniValue<std::string>(ini, "Library", "Folders", ""), ";");
+		library.paths = std::set(std::make_move_iterator(v.begin()),
+								 std::make_move_iterator(v.end()));
+	}
+
+	library.real_time_monitor = INI::GetIniValue<bool>(ini, "Library", "Real-time monitor", true);
 
 	return 0;
 }
@@ -137,6 +143,7 @@
 	}
 
 	INI::SetIniValue(ini, "Library", "Folders", Strings::Implode(library.paths, ";"));
+	INI::SetIniValue(ini, "Library", "Real-time monitor", library.real_time_monitor);
 
 	file.write(ini);
 
--- a/src/core/strings.cc	Mon Jan 08 17:07:01 2024 -0500
+++ b/src/core/strings.cc	Mon Jan 08 21:23:00 2024 -0500
@@ -35,6 +35,21 @@
 	return out;
 }
 
+std::string Implode(const std::set<std::string>& set, const std::string& delimiter) {
+	if (set.size() < 1)
+		return "-";
+
+	std::string out;
+
+	for (auto it = set.cbegin(); it != set.cend(); it++) {
+		out.append(*it);
+		if (it != std::prev(set.cend(), 1))
+			out.append(delimiter);
+	}
+
+	return out;
+}
+
 std::vector<std::string> Split(const std::string &text, const std::string& delimiter) {
 	std::vector<std::string> tokens;
 
--- a/src/gui/dialog/settings.cc	Mon Jan 08 17:07:01 2024 -0500
+++ b/src/gui/dialog/settings.cc	Mon Jan 08 21:23:00 2024 -0500
@@ -88,7 +88,7 @@
 			sidebar = new SideBar(widget);
 
 			sidebar->setCurrentItem(sidebar->AddItem(tr("Services"), SideBar::CreateIcon(":/icons/24x24/globe.png")));
-			// sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png"));
+			sidebar->AddItem(tr("Library"), SideBar::CreateIcon(":/icons/24x24/inbox-film.png"));
 			sidebar->AddItem(tr("Application"), SideBar::CreateIcon(":/icons/24x24/application-sidebar-list.png"));
 			sidebar->AddItem(tr("Recognition"), SideBar::CreateIcon(":/icons/24x24/question.png"));
 			// sidebar->AddItem(tr("Sharing"), SideBar::CreateIcon(":/icons/24x24/megaphone.png"));
@@ -109,6 +109,7 @@
 		{
 			stacked = new QStackedWidget(widget);
 			stacked->addWidget(new SettingsPageServices(stacked));
+			stacked->addWidget(new SettingsPageLibrary(stacked));
 			stacked->addWidget(new SettingsPageApplication(stacked));
 			stacked->addWidget(new SettingsPageRecognition(stacked));
 			stacked->addWidget(new SettingsPageTorrents(stacked));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/dialog/settings/library.cc	Mon Jan 08 21:23:00 2024 -0500
@@ -0,0 +1,207 @@
+#include "core/session.h"
+#include "core/strings.h"
+#include "gui/dialog/settings.h"
+
+#include <QListWidget>
+#include <QListWidgetItem>
+#include <QGroupBox>
+#include <QCheckBox>
+#include <QLabel>
+#include <QSizePolicy>
+#include <QVBoxLayout>
+#include <QDir>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QPushButton>
+#include <QDropEvent>
+#include <QMimeData>
+
+#include <algorithm>
+#include <iostream>
+
+class DroppableListWidget : public QListWidget {
+	Q_OBJECT
+
+public:
+	explicit DroppableListWidget(QWidget* parent);
+
+signals:
+	void FilesDropped(QStringList list);
+
+protected:
+	void dragEnterEvent(QDragEnterEvent* event) override;
+	void dragMoveEvent(QDragMoveEvent* event) override;
+	void dropEvent(QDropEvent* event) override;
+};
+
+DroppableListWidget::DroppableListWidget(QWidget* parent) : QListWidget(parent) {
+	setAcceptDrops(true);
+}
+
+void DroppableListWidget::dragMoveEvent(QDragMoveEvent* event) {
+    if (event->mimeData()->hasUrls())
+        event->acceptProposedAction();
+}
+
+void DroppableListWidget::dragEnterEvent(QDragEnterEvent* event) {
+    if (event->mimeData()->hasUrls())
+        event->acceptProposedAction();
+}
+
+void DroppableListWidget::dropEvent(QDropEvent* event) {
+	const QMimeData *mime_data = event->mimeData();
+
+	if (!mime_data->hasUrls())
+		return;
+
+	QStringList path_list;
+	QList<QUrl> url_list = mime_data->urls();
+
+	for (const auto& url : url_list) {
+		if (!url.isLocalFile())
+			continue;
+
+		const QString file = url.toLocalFile();
+		const QFileInfo fileinfo(file);
+		if (fileinfo.exists() && fileinfo.isDir())
+			path_list.append(file);
+	}
+
+	if (!path_list.isEmpty())
+		emit FilesDropped(path_list);
+
+	event->acceptProposedAction();
+}
+
+QWidget* SettingsPageLibrary::CreateFoldersWidget() {
+	QWidget* result = new QWidget(this);
+	result->setAutoFillBackground(true);
+	result->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+	QVBoxLayout* full_layout = new QVBoxLayout(result);
+
+	{
+		QGroupBox* group_box = new QGroupBox(tr("Library folders"), result);
+		group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QVBoxLayout* group_box_layout = new QVBoxLayout(group_box);
+
+		{
+			QLabel* label = new QLabel(tr("These folders will be scanned and monitored for new episodes."), group_box);
+			group_box_layout->addWidget(label);
+		}
+
+		{
+			DroppableListWidget* listwidget = new DroppableListWidget(group_box);
+			listwidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+			for (const auto& path : paths) {
+				QListWidgetItem* item = new QListWidgetItem(listwidget);
+				item->setText(Strings::ToQString(path));
+				/* add icons as well soon */
+			}
+
+			connect(listwidget, &DroppableListWidget::FilesDropped, this, [this, listwidget](QStringList list){
+				for (const auto& dir : list) {
+					paths.insert(Strings::ToUtf8String(dir));
+					QListWidgetItem* item = new QListWidgetItem(listwidget);
+					item->setText(dir);
+				}
+			});
+
+			group_box_layout->addWidget(listwidget);
+
+			{
+				QWidget* widget = new QWidget(group_box);
+				QHBoxLayout* widget_layout = new QHBoxLayout(widget);
+
+				{
+					QLabel* label = new QLabel(tr("Tip: You can drag and drop folders here."), widget);
+					widget_layout->addWidget(label);
+				}
+
+				widget_layout->addStretch();
+
+				{
+					QPushButton* button = new QPushButton(tr("Add new..."), widget);
+
+					connect(button, &QPushButton::clicked, this, [this, listwidget]{
+						const QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
+																			QDir::homePath(),
+																			QFileDialog::ShowDirsOnly
+																			| QFileDialog::DontResolveSymlinks);
+						if (dir.isEmpty())
+							return;
+						paths.insert(Strings::ToUtf8String(dir));
+						QListWidgetItem* item = new QListWidgetItem(listwidget);
+						item->setText(dir);
+					});
+
+					widget_layout->addWidget(button);
+				}
+
+				{
+					QPushButton* button = new QPushButton(tr("Remove"), widget);
+
+					connect(listwidget, &QListWidget::itemSelectionChanged, this, [button, listwidget]{
+						QList<QListWidgetItem*> selection = listwidget->selectedItems();
+						button->setEnabled(selection.size() > 0);
+					});
+
+					connect(button, &QPushButton::clicked, this, [this, listwidget]{
+						QList<QListWidgetItem*> selection = listwidget->selectedItems();
+						for (const auto& item : selection) {
+							paths.erase(Strings::ToUtf8String(item->text()));
+							delete item;
+						}
+					});
+
+					widget_layout->addWidget(button);
+				}
+
+				group_box_layout->addWidget(widget);
+			}
+		}
+
+		full_layout->addWidget(group_box);
+	}
+
+	{
+		QGroupBox* group_box = new QGroupBox(tr("Real-time monitor"), result);
+		group_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+
+		QVBoxLayout* group_box_layout = new QVBoxLayout(group_box);
+
+		{
+			QCheckBox* checkbox = new QCheckBox(tr("Detect new files and folders under library folders"), group_box);
+			checkbox->setCheckState(real_time_monitor ? Qt::Checked : Qt::Unchecked);
+
+			connect(checkbox, &QCheckBox::stateChanged, this, [this](int state) {
+				real_time_monitor = (state != Qt::Unchecked);
+			});
+
+			group_box_layout->addWidget(checkbox);
+		}
+
+		full_layout->addWidget(group_box);
+	}
+
+	full_layout->setSpacing(10);
+	full_layout->addStretch();
+
+	return result;
+}
+
+void SettingsPageLibrary::SaveInfo() {
+	session.config.library.paths = paths;
+	session.config.library.real_time_monitor = real_time_monitor;
+}
+
+SettingsPageLibrary::SettingsPageLibrary(QWidget* parent)
+	: SettingsPage(parent, tr("Library")),
+	  paths(session.config.library.paths) {
+	real_time_monitor = session.config.library.real_time_monitor;
+	AddTab(CreateFoldersWidget(), tr("Folder"));
+}
+
+#include "gui/dialog/settings/library.moc"
--- a/src/gui/window.cc	Mon Jan 08 17:07:01 2024 -0500
+++ b/src/gui/window.cc	Mon Jan 08 21:23:00 2024 -0500
@@ -168,13 +168,14 @@
 			folder_menu = menu->addMenu(tr("&Library folders"));
 
 			/* add in all of our existing folders... */
-			for (std::size_t i = 0; i < session.config.library.paths.size(); i++) {
-				const QString folder = Strings::ToQString(session.config.library.paths[i]);
+			std::size_t i = 0;
+			for (const auto& path : session.config.library.paths) {
+				const QString folder = Strings::ToQString(path);
 				QAction* action = folder_menu->addAction(folder, [folder]{
 					QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
 				});
 				if (i < 9)
-					action->setShortcut(QKeySequence(Qt::ALT | (Qt::Key_1 + i)));
+					action->setShortcut(QKeySequence(Qt::ALT | (Qt::Key_1 + i++)));
 				else if (i == 9)
 					action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_0));
 			}
@@ -184,12 +185,12 @@
 			{
 				folder_menu->addAction(tr("&Add new folder..."), [this]{
 					const QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
-																		  "/home",
+																		  QDir::homePath(),
 																		  QFileDialog::ShowDirsOnly
 																		  | QFileDialog::DontResolveSymlinks);
 					if (dir.isEmpty())
 						return;
-					session.config.library.paths.push_back(Strings::ToUtf8String(dir));
+					session.config.library.paths.insert(Strings::ToUtf8String(dir));
 					/* we have to recreate the menu bar to add the new folder */
 					CreateBars();
 				});