/* Copyright 2022 WobLight
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see .
*/
import QtQuick
import GitAddonsManager.engine
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
ApplicationWindow {
visible: true
width: 640
height: 480
id: window
property int availableUpdates: 0
property bool addonsReady: false
Component.onCompleted: {
availableUpdates = Qt.binding(function(){
if (!(Engine.status == Engine.Ready && addonsReady))
return -1;
var count = 0
for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i]
if (addon.gitStatus & Addon.Behind)
count = count + 1
}
return count
})
addonsReady = Qt.binding(checkAddonsReady)
}
function checkAddonsReady(){
for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i]
if (addon.status == Addon.Status.Busy)
return false
}
return true
}
function updateAll() {
for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i]
if (addon.gitStatus & Addon.Behind)
addon.update()
}
}
onAvailableUpdatesChanged: {
if (availableUpdates == -1)
return;
if (availableUpdates > 0) {
tray.showMessage("Addons updates available", "You have " + availableUpdates + " addons waiting for update.", 1, 5000)
} else {
tray.showMessage("Addons up to date", "All addons are updated", 1)
}
}
onClosing: function(close) {
if (Engine.minimizeToTray == Engine.MinimizeToTrayAsk) {
close.accepted = false
closeDialog.visible = true
} else if (Engine.minimizeToTray == Engine.MinimizeToTrayNo) {
Qt.quit()
}
else
{
close.accepted = false
window.visible = false
}
}
Action {
id: refreshAction
icon.name: "view-refresh"
onTriggered: Engine.scanForAddons()
shortcut: StandardKey.Refresh
enabled: Engine.status == Engine.Ready && addonsReady
}
Action {
id: addAction
icon.name: "list-add"
onTriggered: addDialog.visible = true
shortcut: StandardKey.New
}
Action {
id: exportList
icon.name: "export-symbolic"
onTriggered: {
exportListDialog.targetPath = null
exportListDialog.visible = true
}
enabled: Engine.status == Engine.Ready && addonsReady
shortcut: StandardKey.SaveAs
}
Action {
id: updateAllAction
onTriggered: updateAll()
enabled: availableUpdates > 0
}
Page {
anchors.fill: parent
footer: ToolBar {
height: messageLabel.implicitHeight
RowLayout
{
anchors.fill: parent
Label {
id: messageLabel
text: Engine.statusMessage
}
Label {
id: logButton
text: qsTr("[View log]")
MouseArea {
onClicked: logWindow.visible = !logWindow.visible
anchors.fill: parent
}
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight
}
}
}
header: TabBar {
id: bar
TabButton {
text: qsTr("Addons")
}
TabButton {
text: qsTr("Options")
}
TabButton {
text: qsTr("About")
}
}
contentItem: StackLayout {
currentIndex: bar.currentIndex
AddonsPanel {
}
Options {
}
ColumnLayout {
Label {
text: "Author: woblight - minor update by Zae"
}
Label {
text: "Source code: GitHub"
onLinkActivated: Qt.openUrlExternally(link)
textFormat: Text.RichText
}
Label {
text: "Report an issue: GitHub"
onLinkActivated: Qt.openUrlExternally(link)
textFormat: Text.RichText
}
Label {
text: "Original Website: woblight.gitlab.io"
onLinkActivated: Qt.openUrlExternally(link)
textFormat: Text.RichText
}
RowLayout {
Label {
text: "Version:"
}
TextArea {
readOnly: true
text: Engine.version
selectByMouse: true
onFocusChanged: if (focus) selectAll()
}
Button {
id: updateButton
visible: Engine.selfUpdates
contentItem: RowLayout {
BusyIndicator {
implicitHeight: parent.height
implicitWidth: implicitHeight
running: !updateButton.enabled
visible: running
}
Label {
text:
Engine.updateStatus === Engine.NoUpdate ?
qsTr("Check for update") :
Engine.updateStatus === Engine.CheckingForUpdate ?
qsTr("Checking for update...") :
Engine.updateStatus === Engine.UpdateAvailable ?
qsTr("Download update") :
Engine.updateStatus === Engine.DownloadingUpdate ?
qsTr("Downloading update...") :
Engine.updateStatus === Engine.UpdateReady ?
qsTr("Unpacking update") : qsTr("error")
}
}
onClicked: Engine.updateStatus === Engine.NoUpdate ?
Engine.checkForUpdates() :
Engine.updateStatus === Engine.UpdateAvailable ?
Engine.downloadUpdate() :
Engine.updateStatus === Engine.UpdateReady ?
Engine.executeUpdate() : {}
enabled: Engine.updateStatus === Engine.NoUpdate || Engine.updateStatus === Engine.UpdateAvailable || Engine.updateStatus === Engine.UpdateReady
}
}
}
}
}
FolderDialog {
id: fileDialog
property int selector: -1
title: qsTr("Choose addons directory")
onAccepted: Engine.setAddonsPath(selector, fileDialog.selectedFolder.toString().replace("file://",""))
visible: Engine.addonsPaths.length == 0
}
Dialog {
parent: ApplicationWindow.overlay
id: cloneHelpDialog
modal: true
visible: cloneHelpBtn.checked
x: (parent.width - width) / 2
y: (parent.height - height) / 2
standardButtons: Dialog.Close
title: qsTr("Clone URL Help")
ColumnLayout {
anchors.fill: parent
Label {
text: "Look for \"clone url\" on the repository webpage, it usually ends in .git"
}
Label { text: "Gitlab" }
Image {
source: "qrc:/gitlab_clone_url.jpg"
fillMode: Image.PreserveAspectCrop
Layout.fillHeight: true
Layout.fillWidth: true
}
Label { text: "Github" }
Image {
source: "qrc:/github_clone_url.jpg"
fillMode: Image.PreserveAspectCrop
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
Dialog {
width: window.width
x: (parent.width - width) / 2
y: (parent.height - height) / 2
id: addDialog
title: "Clone Repository"
standardButtons: Dialog.Ok | Dialog.Cancel
ColumnLayout {
anchors.fill: parent
RowLayout {
TextField {
Layout.fillWidth: true
id: addField
placeholderText: "Git clone url"
onAccepted: addDialog.accept()
focus: visible
}
ToolButton {
id: cloneHelpBtn
icon.name: "help-whatsthis"
onClicked: cloneHelpDialog.visible = true
}
}
ComboBox {
id: pathChooser
visible: Engine.addonsPaths.length > 1
model: Engine.addonsPaths
currentIndex: 0
Layout.fillWidth: true
}
}
focus: visible
modal: true
onAccepted: {
Engine.clone(addField.text, pathChooser.currentIndex)
addField.clear()
}
onRejected: addField.clear()
}
Dialog {
x: (parent.width - implicitWidth) / 2
y: (parent.height - implicitHeight) / 2
title: Engine.statusMessage
id: cloningDialong
ProgressBar {
anchors.fill: parent
id: cloningBar
to: Engine.total
value: Engine.progress
indeterminate: to == 0
}
visible: Engine.status == Engine.Busy
modal: true
closePolicy: Popup.NoAutoClose
}
Dialog {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
title: "An error occourred"
id: errorDialog
Label {
text: Engine.statusMessage
}
visible: Engine.status == Engine.Error
modal: true
closePolicy: Popup.NoAutoClose
standardButtons: Dialog.Ok
onClosed: Engine.status = Engine.Ready
}
Tray {
id: tray
}
Dialog {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
property var invalidPaths: {
var l = []
for (var i = 0; i < Engine.addonsPaths.length; i++)
if (Engine.addonsPaths[i] != "" && !(Engine.addonsPaths[i].toLowerCase().match("interface/addons$") || Engine.addonsPaths[i].toLowerCase().match("/run/user/\\d+/doc/\\w+/addons$")))
l.push(Engine.addonsPaths[i])
return l
}
id: invalidDialog
visible: invalidPaths.length > 0
title: qsTr("Warning")
standardButtons: Dialog.Close
Label {
anchors.fill: parent
text: {
var m = qsTr("Following paths don't appears to be valid. Please make sure it is set to the Interface/AddOns folder inside your World of Warcraft folder.", "", invalidDialog.invalidPaths.length)
for (var i = 0; i < invalidDialog.invalidPaths.length; i++)
m += "
" + invalidDialog.invalidPaths[i]+""
return m
}
textFormat: Text.RichText
wrapMode: Text.WordWrap
}
modal: true
}
Dialog {
id: closeDialog
x: (parent.width - width) / 2
y: (parent.height - height) / 2
closePolicy: Popup.NoAutoClose
modal: true
title: qsTr("Choose closing behaviour")
Label {
text: qsTr("Your choise will be saved and be changed any time by the Options panel")
}
footer: DialogButtonBox {
Button {
text: "Close Application"
onClicked: {
Engine.minimizeToTray = Engine.MinimizeToTrayNo
Qt.quit()
}
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
Button {
text: "Minimize in System Tray"
onClicked: {
Engine.minimizeToTray = Engine.MinimizeToTrayYes
close()
}
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
}
}
Dialog {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
visible: Engine.firstBoot
onClosed: Engine.firstBoot = false
title: qsTr("Note on GitAddonsManager")
Label {
anchors.fill: parent
text: qsTr("Greetings,
"+
"This application will manage only addons cloned with git, not regularly downloaded ones. If you don't know what that means, probably none of your current addons will be listed and only addons added through this aplication will be managed.
"+
"With this application is only possible to clone (download) addons from git repositories (e.g. Gitlab and Github).
Finally, this is an alpha version things might not go as intended.")
textFormat: Text.RichText
wrapMode: Text.WordWrap
}
modal: true
standardButtons: Dialog.Close
}
Dialog {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
visible: Engine.updateStatus === Engine.UpdateAvailable || Engine.updateStatus === Engine.DownloadingUpdate || Engine.updateStatus === Engine.UpdateReady
id: updateDialog
ColumnLayout {
anchors.fill: parent
Label {
Layout.fillWidth: true
id: updateMessage
text: Engine.updateStatus === Engine.UpdateAvailable ?
qsTr("
A newer version of GitAddonsManager is available
") :
Engine.updateStatus === Engine.DownloadingUpdate ?
qsTr("Downloading update...") : qsTr("Download finished.
To complete the update click \"Close & Update\", then extract GitAddonsManager.zip and replace old files.")
textFormat: Text.RichText
wrapMode: Text.WordWrap
}
ProgressBar {
Layout.fillWidth: true
id: updateProgress
to: Engine.total
value: Engine.progress
indeterminate: to == -1
visible: Engine.updateStatus === Engine.DownloadingUpdate
}
}
modal: true
footer: DialogButtonBox {
Button {
visible: Engine.updateStatus === Engine.UpdateAvailable
text: qsTr("Download")
onClicked: Engine.downloadUpdate()
}
Button {
visible: Engine.updateStatus === Engine.UpdateReady
text: qsTr("Close && Update")
onClicked: Engine.executeUpdate()
}
}
closePolicy: Engine.updateStatus !== Engine.DownloadingUpdate ? Dialog.CloseOnPressOutside | Dialog.CloseOnEscape : Dialog.NoAutoClose
standardButtons: Engine.updateStatus !== Engine.DownloadingUpdate ? Dialog.Close : Dialog.NoButton
}
FileDialog {
id: exportListDialog
property string targetPath
onAccepted: Engine.exportAddonList(selectedFile, targetPath)
fileMode: FileDialog.SaveFile
defaultSuffix: "txt"
}
LogWindow {
id: logWindow
}
}