Add support for multiple directories
This commit is contained in:
+12
-8
@@ -41,13 +41,6 @@ Page {
|
||||
ToolTip.text: "Upgrade all addons"
|
||||
Material.foreground: Material.background
|
||||
}
|
||||
ToolButton {
|
||||
hoverEnabled: true
|
||||
action: openAddonsFolder
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: "Open addons folder externally"
|
||||
Material.foreground: Material.background
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
@@ -75,8 +68,19 @@ Page {
|
||||
model: Engine.addons
|
||||
id: listView
|
||||
|
||||
section.property: "modelData.path"
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate:
|
||||
Button {
|
||||
text: section
|
||||
icon.name: "go-parent-folder"
|
||||
width: Math.min(listView.width, implicitWidth)
|
||||
onClicked: Qt.openUrlExternally((Qt.platform.os == "windows" ? "file:///" : "file://")+section)
|
||||
display: AbstractButton.TextBesideIcon
|
||||
}
|
||||
|
||||
delegate: MouseArea {
|
||||
property Addon addon: Engine.addons[index]
|
||||
property Addon addon: Engine.addons[index]
|
||||
width: parent.width
|
||||
implicitHeight: row.height
|
||||
hoverEnabled: true
|
||||
|
||||
+140
-128
@@ -22,142 +22,154 @@ import QtQuick.Controls.Material 2.2
|
||||
import QtQuick.Controls.Universal 2.2
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
id: path
|
||||
text: Engine.addonsPath
|
||||
onAccepted: Engine.addonsPath = text
|
||||
placeholderText: qsTr("Path to AddOns folder")
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Path to the folder containing the addons")
|
||||
enabled: Control.status == Control.Ready && addonsReady
|
||||
Pane {
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
Repeater {
|
||||
model: Engine.addonsPaths.length
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
id: path
|
||||
text: Engine.addonsPaths[index]
|
||||
onAccepted: Engine.addonsPath = text
|
||||
placeholderText: qsTr("Path to AddOns folder")
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Path to the folder containing the addons")
|
||||
enabled: Control.status == Control.Ready && addonsReady
|
||||
}
|
||||
Button {
|
||||
enabled: Control.status == Control.Ready && addonsReady
|
||||
text: qsTr("Browse")
|
||||
onClicked: {fileDialog.selector = index; fileDialog.folder = "file://" + Engine.addonsPaths[index]; fileDialog.visible = true}
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Choose the folder containing the addons")
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
enabled: Control.status == Control.Ready && addonsReady
|
||||
text: qsTr("Browse")
|
||||
onClicked: fileDialog.visible = true
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Choose the folder containing the addons")
|
||||
text: qsTr("Add directory")
|
||||
Layout.alignment: Qt.AlignRight
|
||||
onClicked: {fileDialog.selector = -1; fileDialog.visible = true}
|
||||
}
|
||||
}
|
||||
CheckBox {
|
||||
Layout.fillWidth: true
|
||||
tristate: Engine.minimizeToTray == Engine.MinimizeToTrayAsk
|
||||
onCheckedChanged: Engine.minimizeToTray = checked ? Engine.MinimizeToTrayYes : Engine.MinimizeToTrayNo
|
||||
checkState: Engine.minimizeToTray == Engine.MinimizeToTrayAsk ?
|
||||
Qt.PartiallyChecked :
|
||||
Engine.minimizeToTray == Engine.MinimizeToTrayYes ?
|
||||
Qt.Checked : Qt.Unchecked
|
||||
text: qsTr("Minimize to System Tray")
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Minimize to System Tray when the main window is closed.")
|
||||
}
|
||||
GroupBox {
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Style")
|
||||
Flow {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
ComboBox {
|
||||
model: Engine.availableStyles
|
||||
currentIndex: Engine.availableStyles.indexOf(Engine.style)
|
||||
onActivated: {
|
||||
Engine.style = Engine.availableStyles[currentIndex]
|
||||
restartDialog.visible = true
|
||||
}
|
||||
ToolTip.text: qsTr("Requires restart")
|
||||
ToolTip.visible: hovered
|
||||
|
||||
CheckBox {
|
||||
Layout.fillWidth: true
|
||||
tristate: Engine.minimizeToTray == Engine.MinimizeToTrayAsk
|
||||
onCheckedChanged: Engine.minimizeToTray = checked ? Engine.MinimizeToTrayYes : Engine.MinimizeToTrayNo
|
||||
checkState: Engine.minimizeToTray == Engine.MinimizeToTrayAsk ?
|
||||
Qt.PartiallyChecked :
|
||||
Engine.minimizeToTray == Engine.MinimizeToTrayYes ?
|
||||
Qt.Checked : Qt.Unchecked
|
||||
text: qsTr("Minimize to System Tray")
|
||||
hoverEnabled: true
|
||||
}
|
||||
ComboBox {
|
||||
id: materialTheme
|
||||
visible: Engine.style == "Material"
|
||||
model: ["Light", "Dark"]
|
||||
onCurrentIndexChanged: window.Material.theme = currentIndex
|
||||
}
|
||||
ComboBox {
|
||||
id: universalTheme
|
||||
visible: Engine.style == "Universal"
|
||||
model: ["Light", "Dark", "System"]
|
||||
onActivated: window.Universal.theme = currentIndex
|
||||
}
|
||||
ListModel {
|
||||
id: materialColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 19; i++)
|
||||
append({"color": Material.color(i)})}
|
||||
}
|
||||
ListModel {
|
||||
id: materialDarkColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 19; i++)
|
||||
append({"color": Material.color(i,Material.Shade200)})}
|
||||
}
|
||||
ListModel {
|
||||
id: universalColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 20; i++)
|
||||
append({"color": Universal.color(i)})}
|
||||
}
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Minimize to System Tray when the main window is closed.")
|
||||
}
|
||||
GroupBox {
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Style")
|
||||
Flow {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
ComboBox {
|
||||
model: Engine.availableStyles
|
||||
currentIndex: Engine.availableStyles.indexOf(Engine.style)
|
||||
onActivated: {
|
||||
Engine.style = Engine.availableStyles[currentIndex]
|
||||
restartDialog.visible = true
|
||||
}
|
||||
ToolTip.text: qsTr("Requires restart")
|
||||
ToolTip.visible: hovered
|
||||
hoverEnabled: true
|
||||
}
|
||||
ComboBox {
|
||||
id: materialTheme
|
||||
visible: Engine.style == "Material"
|
||||
model: ["Light", "Dark"]
|
||||
onCurrentIndexChanged: window.Material.theme = currentIndex
|
||||
}
|
||||
ComboBox {
|
||||
id: universalTheme
|
||||
visible: Engine.style == "Universal"
|
||||
model: ["Light", "Dark", "System"]
|
||||
onActivated: window.Universal.theme = currentIndex
|
||||
}
|
||||
ListModel {
|
||||
id: materialColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 19; i++)
|
||||
append({"color": Material.color(i)})}
|
||||
}
|
||||
ListModel {
|
||||
id: materialDarkColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 19; i++)
|
||||
append({"color": Material.color(i,Material.Shade200)})}
|
||||
}
|
||||
ListModel {
|
||||
id: universalColors
|
||||
dynamicRoles: true
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < 20; i++)
|
||||
append({"color": Universal.color(i)})}
|
||||
}
|
||||
|
||||
ColorPicker {
|
||||
id: materialPrimary
|
||||
visible: Engine.style == "Material"
|
||||
text: qsTr("Primary:")
|
||||
model: materialTheme.currentIndex === Material.Dark ? materialDarkColors : materialColors
|
||||
currentIndex: Material.Indigo
|
||||
onColorChanged: window.Material.primary = color
|
||||
}
|
||||
ColorPicker {
|
||||
id: materialAccent
|
||||
visible: Engine.style == "Material"
|
||||
text: qsTr("Accent:")
|
||||
model: materialTheme.currentIndex === Material.Dark ? materialDarkColors : materialColors
|
||||
currentIndex: Material.Pink
|
||||
onColorChanged: window.Material.accent = color
|
||||
}
|
||||
ColorPicker {
|
||||
id: universalAccent
|
||||
visible: Engine.style == "Universal"
|
||||
text: qsTr("Accent:")
|
||||
model: universalColors
|
||||
currentIndex: Universal.Cobalt
|
||||
onColorChanged: window.Universal.accent = color
|
||||
}
|
||||
ColorPicker {
|
||||
id: materialPrimary
|
||||
visible: Engine.style == "Material"
|
||||
text: qsTr("Primary:")
|
||||
model: materialTheme.currentIndex === Material.Dark ? materialDarkColors : materialColors
|
||||
currentIndex: Material.Indigo
|
||||
onColorChanged: window.Material.primary = color
|
||||
}
|
||||
ColorPicker {
|
||||
id: materialAccent
|
||||
visible: Engine.style == "Material"
|
||||
text: qsTr("Accent:")
|
||||
model: materialTheme.currentIndex === Material.Dark ? materialDarkColors : materialColors
|
||||
currentIndex: Material.Pink
|
||||
onColorChanged: window.Material.accent = color
|
||||
}
|
||||
ColorPicker {
|
||||
id: universalAccent
|
||||
visible: Engine.style == "Universal"
|
||||
text: qsTr("Accent:")
|
||||
model: universalColors
|
||||
currentIndex: Universal.Cobalt
|
||||
onColorChanged: window.Universal.accent = color
|
||||
}
|
||||
|
||||
Settings {
|
||||
category: "Material"
|
||||
property alias primary: materialPrimary.currentIndex
|
||||
property alias accent: materialAccent.currentIndex
|
||||
property alias theme: materialTheme.currentIndex
|
||||
}
|
||||
Settings {
|
||||
category: "Universal"
|
||||
property alias accent: universalAccent.currentIndex
|
||||
property alias theme: universalTheme.currentIndex
|
||||
Settings {
|
||||
category: "Material"
|
||||
property alias primary: materialPrimary.currentIndex
|
||||
property alias accent: materialAccent.currentIndex
|
||||
property alias theme: materialTheme.currentIndex
|
||||
}
|
||||
Settings {
|
||||
category: "Universal"
|
||||
property alias accent: universalAccent.currentIndex
|
||||
property alias theme: universalTheme.currentIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Dialog {
|
||||
id: restartDialog
|
||||
parent: overlay
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
text: qsTr("The application must be restarted for this change to take effect.\nRestart now?")
|
||||
wrapMode: Text.WordWrap
|
||||
Dialog {
|
||||
id: restartDialog
|
||||
parent: overlay
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
text: qsTr("The application must be restarted for this change to take effect.\nRestart now?")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
onAccepted: Qt.exit(1)
|
||||
}
|
||||
onAccepted: Qt.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
Addon::Addon(QString name, git_repository *repo, QObject *parent) : QObject(parent),
|
||||
m_name(name), m_repo(repo, &git_repository_free), m_progress(0), m_total(0), m_status(Status::Ready), m_gitStatus(GitStatusFlag::UpToDate), m_pool(new QThreadPool(this))
|
||||
Addon::Addon(QString name, git_repository *repo, QString path, QObject *parent) : QObject(parent),
|
||||
m_name(name), m_repo(repo, &git_repository_free), m_progress(0), m_total(0), m_status(Status::Ready), m_gitStatus(GitStatusFlag::UpToDate), m_pool(new QThreadPool(this)), m_path(path)
|
||||
{
|
||||
connect(this, &Addon::remoteChanged, this, &Addon::fetchRemote);
|
||||
connect(this, &Addon::currentBranchChanged, this, &Addon::updateGitStatus);
|
||||
@@ -337,7 +337,7 @@ void Addon::uninstall()
|
||||
removeSubfolders();
|
||||
closeRepo();
|
||||
delegate("Removing addon folder", [this](){
|
||||
removeFolder(Control::instance()->addonsPath() + "/" + m_name);
|
||||
removeFolder(m_path + "/" + m_name);
|
||||
}, [](){Control::instance()->scanForAddons();});
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ void Addon::removeSubfolders()
|
||||
{
|
||||
delegate("Removing Subfolders", [this](){
|
||||
QStringList paths(m_subfolders);
|
||||
std::transform(paths.begin(), paths.end(), paths.begin(), [](auto p){return Control::instance()->addonsPath() + "/" + p;});
|
||||
std::transform(paths.begin(), paths.end(), paths.begin(), [this](auto p){return m_path + "/" + p;});
|
||||
removeFolders(paths);
|
||||
});
|
||||
}
|
||||
@@ -412,7 +412,7 @@ void Addon::removeSubfolders()
|
||||
void Addon::unpackSubfolders(){
|
||||
delegate("Unpacking Subfolders", [this](){
|
||||
QStringList errors;
|
||||
QDir addonsDir(Control::instance()->addonsPath());
|
||||
QDir addonsDir(m_path);
|
||||
if (m_subfolders.contains(m_name, Qt::CaseInsensitive)) {
|
||||
closeRepo();
|
||||
QString oldName = m_name;
|
||||
@@ -513,9 +513,9 @@ void Addon::reclone()
|
||||
check_git_return(git_branch_name(&remote, remoteRefForBranch(m_currentBranch)));
|
||||
QString oldRemote(remote);
|
||||
closeRepo();
|
||||
if (removeFolder(Control::instance()->addonsPath() + "/" + name() + "/.git")){
|
||||
if (removeFolder(m_path + "/" + name() + "/.git")){
|
||||
git_repository *repo = nullptr;
|
||||
check_git_return(git_repository_init(&repo, (Control::instance()->addonsPath() + "/" + m_name).toLocal8Bit(), false));
|
||||
check_git_return(git_repository_init(&repo, (m_path + "/" + m_name).toLocal8Bit(), false));
|
||||
m_repo.reset(repo);
|
||||
for (size_t i = 0; i < remotes.count; i++) {
|
||||
AutoPtr remote(&git_remote_free);
|
||||
@@ -542,9 +542,9 @@ void Addon::reclone()
|
||||
void Addon::loadReadme()
|
||||
{
|
||||
delegate("Readme Loading",[this](){
|
||||
QFileInfo md(Control::instance()->addonsPath() + "/" + m_name + "/README.md");
|
||||
QFileInfo md(m_path + "/" + m_name + "/README.md");
|
||||
if (!md.exists() && !md.isFile()) {
|
||||
QDir repodir(Control::instance()->addonsPath() + "/" + m_name);
|
||||
QDir repodir(m_path + "/" + m_name);
|
||||
auto a = repodir.entryInfoList({"readme*"});
|
||||
if (a.size() > 0)
|
||||
md = a[0];
|
||||
@@ -624,6 +624,11 @@ QString Addon::readme() const
|
||||
return m_readme;
|
||||
}
|
||||
|
||||
QString Addon::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void Addon::closeRepo()
|
||||
{
|
||||
m_repo.reset();
|
||||
@@ -632,7 +637,7 @@ void Addon::closeRepo()
|
||||
void Addon::openRepo()
|
||||
{
|
||||
git_repository *repo = nullptr;
|
||||
git_repository_open(&repo, (Control::instance()->addonsPath() + "/" + m_name).toLocal8Bit());
|
||||
git_repository_open(&repo, (m_path + "/" + m_name).toLocal8Bit());
|
||||
m_repo.reset(repo);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ class Addon : public QObject
|
||||
|
||||
int m_total;
|
||||
|
||||
QString m_path;
|
||||
|
||||
QMutex m_mutex;
|
||||
QWaitCondition m_wait;
|
||||
void *m_result;
|
||||
@@ -67,6 +69,7 @@ public:
|
||||
Q_PROPERTY(QStringList subfolders READ subfolders WRITE setSubfolders NOTIFY subfoldersChanged)
|
||||
Q_PROPERTY(QString filesToRemove READ filesToRemove WRITE setFilesToRemove NOTIFY filesToRemoveChanged)
|
||||
Q_PROPERTY(QString readme READ readme WRITE setReadme NOTIFY readmeChanged)
|
||||
Q_PROPERTY(QString path READ path)
|
||||
|
||||
enum class Status {
|
||||
Error = -1,
|
||||
@@ -92,7 +95,7 @@ public:
|
||||
Q_PROPERTY(GitStatus gitStatus READ gitStatus WRITE setGitStatus NOTIFY gitStatusChanged)
|
||||
Q_FLAGS(GitStatus)
|
||||
|
||||
explicit Addon(QString name, git_repository *repo, QObject *parent = nullptr);
|
||||
explicit Addon(QString name, git_repository *repo, QString path, QObject *parent = nullptr);
|
||||
|
||||
QString name() const;
|
||||
|
||||
@@ -118,6 +121,8 @@ public:
|
||||
|
||||
QString readme() const;
|
||||
|
||||
QString path() const;
|
||||
|
||||
private:
|
||||
|
||||
Status m_status;
|
||||
@@ -169,6 +174,8 @@ signals:
|
||||
|
||||
void readmeChanged(QString readme);
|
||||
|
||||
void pathChanged(QString path);
|
||||
|
||||
public slots:
|
||||
void setName(QString name);
|
||||
void update();
|
||||
|
||||
+75
-29
@@ -41,19 +41,31 @@ Control::Control(QObject *parent) : QObject(parent),
|
||||
m_style(QQuickStyle::name()),m_availableStyles(QQuickStyle::availableStyles())
|
||||
{
|
||||
connect(this, &Control::addonsPathChanged, this, &Control::scanForAddons);
|
||||
connect(this, &Control::addonsPathChanged, this, &Control::saveAddonsPath);
|
||||
connect(this, &Control::addonsPathsChanged, this, &Control::saveAddonsPaths);
|
||||
}
|
||||
|
||||
void Control::init() {
|
||||
git_libgit2_init();
|
||||
QSettings settings;
|
||||
setAddonsPath(settings.value("addonsPath").toString());
|
||||
if (settings.value("addonsPaths").isNull()) {
|
||||
setAddonsPath(-1, settings.value("addonsPath").toString());
|
||||
settings.remove("addonsPath");
|
||||
} else {
|
||||
auto paths = settings.value("addonsPaths").toStringList();
|
||||
for (int i = 0; i < paths.length(); i++)
|
||||
setAddonsPath(-1, paths[i]);
|
||||
}
|
||||
setFirstBoot(settings.value("firstBoot",true).toBool());
|
||||
setMinimizeToTray((MinimizeToTray)settings.value("minimizeToTray",(int)MinimizeToTrayAsk).toInt());
|
||||
nam = new QNetworkAccessManager(this);
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
QStringList Control::addonsPaths() const
|
||||
{
|
||||
return m_addonsPaths;
|
||||
}
|
||||
|
||||
void Control::delegate(QString taskname, auto work, auto callback)
|
||||
{
|
||||
Q_ASSERT_X(QThread::currentThread() == thread(), "delegate", "Attempt to delegate from another thread.");
|
||||
@@ -108,11 +120,6 @@ QList<QObject *> Control::addons() const
|
||||
return m_addons;
|
||||
}
|
||||
|
||||
QString Control::addonsPath() const
|
||||
{
|
||||
return m_addonsPath;
|
||||
}
|
||||
|
||||
Control *Control::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
@@ -265,28 +272,61 @@ void Control::setAddons(QList<QObject *> addons)
|
||||
emit addonsChanged(m_addons);
|
||||
}
|
||||
|
||||
void Control::setAddonsPath(QString addonsPath)
|
||||
void Control::setAddonsPath(int index, QString addonsPath)
|
||||
{
|
||||
addonsPath = QUrl::fromLocalFile(addonsPath).adjusted(QUrl::NormalizePathSegments|QUrl::StripTrailingSlash).toLocalFile();
|
||||
if (m_addonsPath == addonsPath)
|
||||
if (addonsPath.isNull()) {
|
||||
for (int i = 0; i < m_addons.size();) {
|
||||
int s = m_addons.size();
|
||||
if (static_cast<Addon*>(m_addons[i])->path() == m_addonsPaths[index])
|
||||
delete m_addons.takeAt(i);
|
||||
if (s != m_addons.size())
|
||||
emit addonsChanged(m_addons);
|
||||
else i++;
|
||||
}
|
||||
m_addonsPaths.removeAt(index);
|
||||
emit addonsPathsChanged(m_addonsPaths);
|
||||
return;
|
||||
}
|
||||
addonsPath = QUrl::fromLocalFile(addonsPath).adjusted(QUrl::NormalizePathSegments|QUrl::StripTrailingSlash).toLocalFile();
|
||||
if (index == -1) {
|
||||
m_addonsPaths.append(addonsPath);
|
||||
index = m_addonsPaths.size() -1;
|
||||
} else {
|
||||
if (m_addonsPaths[index] == addonsPath)
|
||||
return;
|
||||
for (int i = 0; i < m_addons.size();) {
|
||||
if (static_cast<Addon*>(m_addons[i])->path() == m_addonsPaths[index])
|
||||
delete m_addons.takeAt(i);
|
||||
else i++;
|
||||
}
|
||||
m_addonsPaths[index] = addonsPath;
|
||||
}
|
||||
|
||||
m_addonsPath = addonsPath;
|
||||
emit addonsPathChanged(m_addonsPath);
|
||||
emit addonsPathChanged(index, m_addonsPaths[index]);
|
||||
emit addonsPathsChanged(m_addonsPaths);
|
||||
}
|
||||
|
||||
void Control::saveAddonsPath()
|
||||
void Control::saveAddonsPaths()
|
||||
{
|
||||
QSettings settings;
|
||||
settings.setValue("addonsPath", m_addonsPath);
|
||||
settings.setValue("addonsPaths", m_addonsPaths);
|
||||
}
|
||||
|
||||
void Control::scanForAddons()
|
||||
void Control::scanForAddons(int i)
|
||||
{
|
||||
if (i == -1) {
|
||||
foreach (QObject *addon, m_addons)
|
||||
addon->deleteLater();
|
||||
QObjectList ol;
|
||||
setAddons(ol);
|
||||
for (int k = 0; k < m_addonsPaths.size(); ++k)
|
||||
scanForAddons(k);
|
||||
return;
|
||||
}
|
||||
using init_t = QPair<QString, git_repository*>;
|
||||
delegate("Scanning addons folder...", [this](){
|
||||
delegate("Scanning addons folder...", [this, i](){
|
||||
QList<init_t> list;
|
||||
QDir addonsFolder(m_addonsPath);
|
||||
QDir addonsFolder(m_addonsPaths[i]);
|
||||
foreach (QFileInfo dirInfo, addonsFolder.entryInfoList(QDir::AllDirs)) {
|
||||
QDir gitdir(dirInfo.canonicalFilePath() + "/.git");
|
||||
if (!gitdir.exists()) continue;
|
||||
@@ -299,13 +339,10 @@ void Control::scanForAddons()
|
||||
list << init_t(dirInfo.fileName(), repo);
|
||||
}
|
||||
return list;
|
||||
}, [this](auto list){
|
||||
QObjectList ol;
|
||||
}, [this, k=i](auto list){
|
||||
foreach (init_t i, list)
|
||||
ol << new Addon(i.first, i.second);
|
||||
foreach (QObject *addon, m_addons)
|
||||
addon->deleteLater();
|
||||
setAddons(ol);
|
||||
m_addons << new Addon(i.first, i.second, m_addonsPaths[k]);
|
||||
emit addonsChanged(m_addons);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,9 +353,9 @@ int clone_progress_cb(const git_transfer_progress *stats, void *payload) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Control::clone(QUrl url)
|
||||
void Control::clone(QUrl url, int i)
|
||||
{
|
||||
delegate("Cloning addon...",[this, url]() {
|
||||
delegate("Cloning addon...",[this, url, i]() {
|
||||
struct {
|
||||
QString name;
|
||||
git_repository *repo = nullptr;
|
||||
@@ -327,14 +364,14 @@ void Control::clone(QUrl url)
|
||||
data.name = url.fileName().section('.',0,0);
|
||||
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
|
||||
opts.fetch_opts.callbacks.transfer_progress = &clone_progress_cb;
|
||||
int error = git_clone(&data.repo, url.toString().toLocal8Bit(), (m_addonsPath + "/" + data.name).toLocal8Bit(), &opts);
|
||||
int error = git_clone(&data.repo, url.toString().toLocal8Bit(), (m_addonsPaths[i] + "/" + data.name).toLocal8Bit(), &opts);
|
||||
if (error < 0) {
|
||||
const git_error *e = giterr_last();
|
||||
data.error = QString::asprintf("Error %d/%d: %s", error, e->klass, e->message);
|
||||
return data;
|
||||
}
|
||||
|
||||
QDir dir(m_addonsPath);
|
||||
QDir dir(m_addonsPaths[i]);
|
||||
dir.cd(data.name);
|
||||
QStringList toc = dir.entryList({"*.toc"});
|
||||
if (toc.isEmpty()) {
|
||||
@@ -348,9 +385,9 @@ void Control::clone(QUrl url)
|
||||
git_repository_open(&data.repo, dir.canonicalPath().toLocal8Bit());
|
||||
}
|
||||
return data;
|
||||
},[this](auto ret){
|
||||
},[this, i](auto ret){
|
||||
if (ret.error.isEmpty()) {
|
||||
auto add = new Addon(ret.name, ret.repo);
|
||||
auto add = new Addon(ret.name, ret.repo, m_addonsPaths[i]);
|
||||
m_addons.prepend(add);
|
||||
addonsChanged(m_addons);
|
||||
add->unpackSubfolders();
|
||||
@@ -536,6 +573,15 @@ void Control::setUpdateStatus(Control::UpdateStatus updateStatus)
|
||||
emit updateStatusChanged(m_updateStatus);
|
||||
}
|
||||
|
||||
void Control::setAddonsPaths(QStringList addonsPaths)
|
||||
{
|
||||
if (m_addonsPaths == addonsPaths)
|
||||
return;
|
||||
|
||||
m_addonsPaths = addonsPaths;
|
||||
emit addonsPathsChanged(m_addonsPaths);
|
||||
}
|
||||
|
||||
GitException::GitException(int code) : m_code(code)
|
||||
{
|
||||
const git_error *e = giterr_last();
|
||||
|
||||
@@ -52,17 +52,15 @@ class Control : public QObject
|
||||
|
||||
QList<QObject *> m_addons;
|
||||
|
||||
QString m_addonsPath;
|
||||
|
||||
public:
|
||||
Q_PROPERTY(QList<QObject *> addons READ addons WRITE setAddons NOTIFY addonsChanged)
|
||||
Q_PROPERTY(QString addonsPath READ addonsPath WRITE setAddonsPath NOTIFY addonsPathChanged)
|
||||
Q_PROPERTY(int progress READ progress WRITE setProgress NOTIFY progressChanged)
|
||||
Q_PROPERTY(int total READ total WRITE setTotal NOTIFY totalChanged)
|
||||
Q_PROPERTY(QString statusMessage READ statusMessage WRITE setStatusMessage NOTIFY statusMessageChanged)
|
||||
Q_PROPERTY(bool firstBoot READ firstBoot WRITE setFirstBoot NOTIFY firstBootChanged)
|
||||
Q_PROPERTY(QString style READ style WRITE setStyle NOTIFY styleChanged)
|
||||
Q_PROPERTY(QStringList availableStyles READ availableStyles WRITE setAvailableStyles NOTIFY availableStylesChanged)
|
||||
Q_PROPERTY(QStringList addonsPaths READ addonsPaths WRITE setAddonsPaths NOTIFY addonsPathsChanged)
|
||||
|
||||
~Control();
|
||||
|
||||
@@ -95,8 +93,6 @@ public:
|
||||
Q_PROPERTY(UpdateStatus updateStatus READ updateStatus WRITE setUpdateStatus NOTIFY updateStatusChanged)
|
||||
QList<QObject *> addons() const;
|
||||
|
||||
QString addonsPath() const;
|
||||
|
||||
static Control *instance();
|
||||
|
||||
int progress() const;
|
||||
@@ -121,6 +117,8 @@ public:
|
||||
|
||||
void init();
|
||||
|
||||
QStringList addonsPaths() const;
|
||||
|
||||
private:
|
||||
static Control *m_instance;
|
||||
explicit Control(QObject *parent = nullptr);
|
||||
@@ -148,11 +146,11 @@ private:
|
||||
|
||||
UpdateStatus m_updateStatus = UpdateStatus::NoUpdate;
|
||||
|
||||
QStringList m_addonsPaths;
|
||||
|
||||
signals:
|
||||
void addonsChanged(QList<QObject *> addons);
|
||||
|
||||
void addonsPathChanged(QString addonsPath);
|
||||
|
||||
void progressChanged(int progress);
|
||||
|
||||
void totalChanged(int total);
|
||||
@@ -171,12 +169,15 @@ signals:
|
||||
|
||||
void updateStatusChanged(UpdateStatus updateStatus);
|
||||
|
||||
void addonsPathsChanged(QStringList addonsPaths);
|
||||
|
||||
void addonsPathChanged(int i, QString path);
|
||||
|
||||
public slots:
|
||||
void setAddons(QList<QObject *> addons);
|
||||
void setAddonsPath(QString addonsPath);
|
||||
void saveAddonsPath();
|
||||
void scanForAddons();
|
||||
void clone(QUrl url);
|
||||
void saveAddonsPaths();
|
||||
void scanForAddons(int i = -1);
|
||||
void clone(QUrl url, int i);
|
||||
void setProgress(int progress);
|
||||
void setTotal(int total);
|
||||
void setStatus(Status status);
|
||||
@@ -189,6 +190,8 @@ public slots:
|
||||
void downloadUpdate();
|
||||
void executeUpdate();
|
||||
void setUpdateStatus(UpdateStatus updateStatus);
|
||||
void setAddonsPaths(QStringList addonsPaths);
|
||||
void setAddonsPath(int i, QString path = QString());
|
||||
};
|
||||
Q_DECLARE_METATYPE(Control::MinimizeToTray)
|
||||
|
||||
|
||||
@@ -93,11 +93,6 @@ ApplicationWindow {
|
||||
onTriggered: updateAll()
|
||||
enabled: availableUpdates > 0
|
||||
}
|
||||
Action {
|
||||
id: openAddonsFolder
|
||||
icon.name: "go-parent-folder"
|
||||
onTriggered: Qt.openUrlExternally((Qt.platform.os == "windows" ? "file:///" : "file://")+Engine.addonsPath)
|
||||
}
|
||||
|
||||
ColumnLayout{
|
||||
anchors.fill: parent
|
||||
@@ -186,11 +181,11 @@ ApplicationWindow {
|
||||
}
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
property int selector: -1
|
||||
title: qsTr("Choose addons directory")
|
||||
folder: Engine.addonsPath != "" ? Engine.addonsPath : shortcuts.home
|
||||
onAccepted: Engine.addonsPath = fileUrl.toString().replace("file://","")
|
||||
onAccepted: Engine.setAddonsPath(selector, fileUrl.toString().replace("file://",""))
|
||||
selectFolder: true
|
||||
visible: Engine.addonsPath == ""
|
||||
visible: Engine.addonsPaths.length == 0
|
||||
}
|
||||
Dialog {
|
||||
parent: ApplicationWindow.overlay
|
||||
@@ -290,7 +285,7 @@ ApplicationWindow {
|
||||
Dialog {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
visible: Engine.addonsPath != "" && !Engine.addonsPath.toLowerCase().match("interface/addons$")
|
||||
visible: Engine.addonsPaths[0] != "" && !Engine.addonsPaths[0].toLowerCase().match("interface/addons$")
|
||||
title: qsTr("Warning")
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
|
||||
Reference in New Issue
Block a user