import QtQuick 2.15; import QtQuick.Window 2.0; import Qt.labs.platform 1.1 import istamon.internal 1.0 import org.kde.kirigami 2.10 as Kirigami import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.1 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import QtQuick.Controls 2.15 as QQC2 import org.kde.kitemmodels 1.0 import QtQuick.Dialogs 1.0 Kirigami.ApplicationWindow { id: window; title: "Istamon" width: Kirigami.Units.gridUnit * 28 // intial position at the center, 1/3 from the top Component.onCompleted: { x = Screen.width / 2 - width / 2 y = height / 4 } WindowIcon { window: window icon_name: istamon.status_icon_name } globalDrawer: Kirigami.GlobalDrawer { isMenu: true actions: [ Kirigami.Action { iconName: 'configure' text: 'Configuration' onTriggered: { applicationWindow().pageStack.replace(startPage) } }, Kirigami.Action { icon.name: "application-exit" text: 'Quit' shortcut: StandardKey.Quit onTriggered: Qt.quit() } ] } // Minimize on Escape and Close Shortcut { sequences: ["Escape"] onActivated: window.showMinimized() } flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowMinimizeButtonHint pageStack.initialPage: startPage Component { id: startPage Kirigami.ScrollablePage { title: 'Configuration' id: configPage Kirigami.Action { id: showServicesAction onTriggered: { if (cfg.isConfigured) { applicationWindow().pageStack.replace(istamonPage) } } } FileDialog { id: fileDialog title: "Please choose a file" folder: shortcuts.home onAccepted: { caCertText.text = IstamonUtil.urlFileName(fileDialog.fileUrl) } } Kirigami.Action { id: updateCfgAction onTriggered: { if (cfg.isConfigured) { let c = cfg.getCfg(); urlText.text = c.url if (c.hasCredentials) { wantCredentials.checked = true userText.text = c.user passwordText.text = c.password if (c.savePasswordChoice == 'dontsave') { passwordDontSave.checked = true } else if (c.savePasswordChoice == 'encrypted') { passwordEncrypted.checked = true } else if (c.savePasswordChoice == 'unencrypted') { passwordUnencrypted.checked = true } } if (c.hasCaCert) { wantCaCert.checked = true caCertText.text = c.caCert } } } } Kirigami.Action { id: putCfgAction onTriggered: { let c = cfg.getCfg() c.url = urlText.text c.hasCredentials = wantCredentials.checked c.user = userText.text c.password = passwordText.text if (passwordDontSave.checked) { c.savePasswordChoice = "dontsave" } else if (passwordEncrypted.checked) { c.savePasswordChoice = "encrypted" } else if (passwordUnencrypted.checked) { c.savePasswordChoice = "unencrypted" } c.hasCaCert = wantCaCert.checked c.caCert = caCertText.text cfg.setCfg(c); } } Connections { target: cfg function onCfgUpdated() { updateCfgAction.trigger() showServicesAction.trigger() } } Component.onCompleted: { updateCfgAction.trigger() } Kirigami.FormLayout { Layout.fillHeight: true // this item defines the preferred width of the entire form Item { Layout.preferredWidth: configPage.width * 0.7 } QQC2.TextField { id: urlText placeholderText: "https://example.com:5665" Kirigami.FormData.label: "Server URL:" } Kirigami.Separator { Kirigami.FormData.isSection: true } QQC2.CheckBox { id: wantCredentials Kirigami.FormData.label: 'Credentials' } QQC2.TextField { id: userText enabled: wantCredentials.checked Kirigami.FormData.label: "User:" } Kirigami.ActionTextField { id: passwordText enabled: wantCredentials.checked echoMode: TextInput.Password rightActions: [ Kirigami.Action { id: passwordTextShowAction icon.name: "password-show-on" onTriggered: { if (passwordText.echoMode == TextInput.Password) { passwordText.echoMode = TextInput.Normal passwordTextShowAction.icon.name = 'password-show-off' } else { passwordText.echoMode = TextInput.Password passwordTextShowAction.icon.name = 'password-show-on' } } } ] Kirigami.FormData.label: "Password: " } ColumnLayout { Layout.rowSpan: 3 Kirigami.FormData.buddyFor: passwordUnencrypted QQC2.RadioButton { id: passwordDontSave enabled: wantCredentials.checked checked: true text: "Don't save" } QQC2.RadioButton { id: passwordEncrypted enabled: cfg.hasPasswordManager() && wantCredentials.checked text: "Save encrypted" } QQC2.RadioButton { id: passwordUnencrypted enabled: wantCredentials.checked text: "Save unencrypted" } } Kirigami.Separator { Kirigami.FormData.isSection: true } QQC2.CheckBox { id: wantCaCert Kirigami.FormData.label: "CA-Cert" } Kirigami.ActionTextField { id: caCertText // The label is not shown, but the layout breaks when it is missing. // this is probably a bug in Kirigami Kirigami.FormData.label: "CA-Cert Path: " enabled: wantCaCert.checked placeholderText: "/path/to/ca-cert.pem" rightActions: [ Kirigami.Action { icon.name: "document-open-folder" onTriggered: { fileDialog.visible = true; } } ] } } footer: ColumnLayout { RowLayout { Layout.alignment: Qt.AlignRight Layout.margins: Kirigami.Units.largeSpacing QQC2.Button { text: 'Apply' onClicked: { putCfgAction.trigger() } } QQC2.Button { icon.name: 'document-save' text: 'Apply and Save' onClicked: { putCfgAction.trigger() cfg.saveCfg() } } } } } } IstamonCfg { id: cfg istamon: istamon Component.onCompleted: { if (isConfigured) { applicationWindow().pageStack.initialPage = istamonPage } else { showConfigErrors() applicationWindow().pageStack.initialPage = startPage } } onNotification: msg => { showPassiveNotification(msg) } } Component { id: istamonPage Kirigami.ScrollablePage { id: page title: cfg.title contextualActions : [ showHandledToggleAction ] header: RowLayout { // FIXME: does this have any effect? spacing: Kirigami.Units.largeSpacing QQC2.Label { Layout.fillWidth: true Layout.alignment: Qt.AlignLeft horizontalAlignment: Qt.AlignRight // FIXME: somehow this text determines the width of the whole ColumnLayout... why? text: `DOWN: ${istamon.host_down_count - istamon.host_down_handled_count} (${istamon.host_down_handled_count})` } QQC2.Label { Layout.fillWidth: true Layout.alignment: Qt.AlignRight // FIXME: just a cludge Layout.leftMargin: Kirigami.Units.gridUnit text: `PROBLEMS: ${istamon.critical_count + istamon.warning_count + istamon.unknown_count - istamon.critical_handled_count - istamon.warning_handled_count - istamon.unknown_handled_count} (${istamon.critical_handled_count + istamon.warning_handled_count + istamon.unknown_handled_count })` } QQC2.Label { Layout.fillWidth: true Layout.alignment: Qt.AlignRight // FIXME: just a cludge Layout.leftMargin: Kirigami.Units.gridUnit * 2 horizontalAlignment: Qt.AlignRight // FIXME: somehow this text determines the width of the whole ColumnLayout... why? text: `${istamon.last_run}` } } ListView { id: listServices Layout.fillWidth: true model: servicesSearchSortFilterModel delegate: Kirigami.AbstractListItem { backgroundColor: Kirigami.Theme.backgroundColor RowLayout { Layout.fillWidth: true Layout.fillHeight: true Rectangle { width: Kirigami.Units.gridUnit * 2 Layout.preferredHeight: width Layout.alignment: Qt.AlignVCenter|Qt.AlignLeft border.width:1 radius: Kirigami.Units.smallSpacing / 2 opacity: 0.7 color: { if (isService) { if (itemState === 2) { return '#f56'; } else if (itemState === 1){ return '#fa4'; } else if (itemState === 0){ return '#4b7'; } else { return "#a4f"; } } else { // host if (itemState === 1) { return '#f56'; } else if (itemState === 0) { return '#4b7'; } else { return "#a4f"; } } } } ColumnLayout { Kirigami.Heading { Layout.fillWidth: true text: { if (isService) { return `${handled?'*':''}${hostName}: ${displayName}` } else { return `${handled?'*':''}Host ${hostName}` } } level: 3 elide: Text.ElideRight } QQC2.Label { text: lastCheckOutput visible: isService font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8 } } } } } } } // Note: Just for reference... if we want to re-enable the systray icon at // some point. // // SystemTrayIcon { // visible: true // icon.source: `qrc:///icons/${istamon.status_icon_name}` // tooltip: "istamon" // // onActivated: { // window.show() // window.raise() // window.requestActivate() // } // // } // Kirigami.Action { id: showHandledToggleAction checkable: true onCheckedChanged: servicesSearchSortFilterModel.invalidateFilter() text: 'Show Handled' } IstamonListModel { id: _istamonListModel } IstamonContainer { id: istamon list: _istamonListModel } KSortFilterProxyModel { id: servicesSearchSortFilterModel sourceModel: istamon.list filterRowCallback: function(source_row, source_parent) { let state = sourceModel.data(sourceModel.index(source_row, 0), istamon.get_role("itemState")); let handled = sourceModel.data(sourceModel.index(source_row, 0), istamon.get_role("handled")) let show = showHandledToggleAction.checked || !handled return state != 0 && show; } } }