KCM.qml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Copyright (C) 2015 Malte Veerman <malte.veerman@gmail.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. *
  18. */
  19. import QtQuick 2.4
  20. import QtQuick.Controls 2.3
  21. import QtQuick.Layouts 1.10
  22. import QtQuick.Dialogs 1.2
  23. import org.kde.kirigami 2.0 as Kirigami
  24. import org.kde.kcm 1.0
  25. import Fancontrol.Qml 1.0 as Fancontrol
  26. Item {
  27. property QtObject loader: Fancontrol.Base.loader
  28. property QtObject systemdCom: Fancontrol.Base.systemdCom
  29. property QtObject pwmFanModel: Fancontrol.Base.pwmFanModel
  30. property QtObject tempModel: Fancontrol.Base.tempModel
  31. property QtObject profileModel: Fancontrol.Base.profileModel
  32. property var locale: Qt.locale()
  33. property real textWidth: 0
  34. property var pwmFans: pwmFanModel.fans
  35. id: root
  36. implicitWidth: 1024
  37. implicitHeight: 768
  38. Connections {
  39. target: Fancontrol.Base
  40. onNeedsApplyChanged: kcm.needsSave = Fancontrol.Base.needsApply
  41. }
  42. Connections {
  43. target: kcm
  44. onAboutToSave: {
  45. Fancontrol.Base.apply();
  46. }
  47. onAboutToLoad: {
  48. Fancontrol.Base.load();
  49. enabledBox.checked = systemdCom.serviceActive;
  50. }
  51. onAboutToDefault: enabledBox.checked = false
  52. }
  53. ColumnLayout {
  54. id: noFansInfo
  55. width: parent.width
  56. anchors.verticalCenter: parent.verticalCenter
  57. spacing: Kirigami.Units.smallSpacing * 2
  58. visible: pwmFans.length === 0
  59. Label {
  60. Layout.alignment: Qt.AlignCenter
  61. text: i18n("There are no pwm capable fans in your system.")
  62. font.pointSize: 14
  63. font.bold: true
  64. }
  65. Button {
  66. Layout.alignment: Qt.AlignCenter
  67. text: loader.sensorsDetected ? i18n("Detect fans again") : i18n("Detect fans")
  68. icon.name: kcm.needsAuthorization ? "dialog-password" : ""
  69. onClicked: loader.detectSensors()
  70. }
  71. }
  72. CheckBox {
  73. id: enabledBox
  74. anchors.top: parent.top
  75. visible: pwmFans.length > 0
  76. text: i18n("Control fans manually")
  77. checked: systemdCom.serviceEnabled && systemdCom.serviceActive;
  78. onCheckedChanged: {
  79. systemdCom.serviceActive = enabledBox.checked;
  80. loader.restartServiceAfterTesting = checked;
  81. autostartBox.checked = true;
  82. }
  83. Connections {
  84. target: systemdCom
  85. onServiceActiveChanged: if (systemdCom.serviceActive != enabledBox.checked) enabledBox.checked = systemdCom.serviceActive
  86. }
  87. }
  88. ColumnLayout {
  89. id: bodyLayout
  90. width: parent.width
  91. anchors.bottom: advancedButton.top
  92. anchors.top: enabledBox.bottom
  93. anchors.bottomMargin: advancedButton.height / 4
  94. visible: enabledBox.checked
  95. CheckBox {
  96. id: autostartBox
  97. text: i18n("Enable service at boot")
  98. checked: systemdCom.serviceEnabled
  99. onCheckedChanged: systemdCom.serviceEnabled = checked
  100. Connections {
  101. target: systemdCom
  102. onServiceEnabledChanged: if (systemdCom.serviceEnabled != autostartBox.checked) autostartBox.checked = systemdCom.serviceEnabled
  103. }
  104. }
  105. RowLayout {
  106. id: profileRow
  107. Label {
  108. text: i18n("Profile:")
  109. Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
  110. renderType: Text.NativeRendering
  111. }
  112. ComboBox {
  113. id: profileComboBox
  114. property string saveText: editText.length > 0 ? editText : currentText
  115. editable: true
  116. model: profileModel
  117. textRole: "display"
  118. Layout.fillWidth: true
  119. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  120. onActivated: Fancontrol.Base.applyProfile(index)
  121. }
  122. Button {
  123. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  124. action: saveProfileAction
  125. }
  126. Button {
  127. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  128. action: deleteProfileAction
  129. }
  130. }
  131. RowLayout {
  132. visible: enabledBox.checked && pwmFans.length > 0
  133. Label {
  134. text: i18n("Fan:")
  135. Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
  136. renderType: Text.NativeRendering
  137. }
  138. ComboBox {
  139. id: fanComboBox
  140. model: pwmFanModel
  141. textRole: "display"
  142. Layout.fillWidth: true
  143. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  144. }
  145. Button {
  146. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  147. text: i18n("Detect fans")
  148. icon.name: kcm.needsAuthorization ? "dialog-password" : ""
  149. onClicked: loader.detectSensors()
  150. }
  151. }
  152. Loader {
  153. Layout.fillWidth: true
  154. Layout.fillHeight: true
  155. active: pwmFans.length > fanComboBox.currentIndex && fanComboBox.currentIndex >= 0
  156. sourceComponent: Fancontrol.FanItem {
  157. unit: Fancontrol.Base.unit
  158. fan: pwmFans[fanComboBox.currentIndex]
  159. systemdCom: root.systemdCom
  160. tempModel: root.tempModel
  161. minTemp: Fancontrol.Base.minTemp
  162. maxTemp: Fancontrol.Base.maxTemp
  163. }
  164. }
  165. }
  166. Item {
  167. id: advancedButton
  168. property bool expanded: false
  169. anchors.bottom: settingsArea.top
  170. width: parent.width
  171. height: advancedArrow.height
  172. visible: enabledBox.checked
  173. Image {
  174. id: advancedArrow
  175. source: parent.expanded ? "image://icon/go-down" : "image://icon/go-next"
  176. fillMode: Image.PreserveAspectFit
  177. height: advancedLabel.implicitHeight
  178. }
  179. Label {
  180. id: advancedLabel
  181. anchors.left: advancedArrow.right
  182. text: i18n("Advanced settings")
  183. font.bold: true
  184. }
  185. MouseArea {
  186. anchors.fill: parent
  187. onClicked: parent.expanded = !parent.expanded
  188. }
  189. }
  190. ColumnLayout {
  191. id: settingsArea
  192. visible: enabledBox.checked && parent.height - enabledBox.height - bodyLayout.height - advancedButton.height > height
  193. width: parent.width
  194. anchors.bottom: parent.bottom
  195. clip: true
  196. state: advancedButton.expanded ? "VISIBLE" : "HIDDEN"
  197. states: [
  198. State {
  199. name: "VISIBLE"
  200. PropertyChanges {
  201. target: settingsArea
  202. height: settingsArea.implicitHeight
  203. }
  204. },
  205. State {
  206. name: "HIDDEN"
  207. PropertyChanges {
  208. target: settingsArea
  209. height: 0
  210. }
  211. }
  212. ]
  213. transitions: Transition {
  214. NumberAnimation {
  215. properties: "height"
  216. easing.type: Easing.InOutQuad
  217. }
  218. }
  219. RowLayout {
  220. width: parent.width
  221. Label {
  222. Layout.preferredWidth: root.textWidth
  223. clip: true
  224. text: i18n("Interval:")
  225. horizontalAlignment: Text.AlignRight
  226. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  227. }
  228. SpinBox {
  229. Layout.minimumWidth: implicitWidth
  230. Layout.fillWidth: true
  231. value: loader.interval
  232. editable: true
  233. textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0) + ' ' + i18np("second", "seconds", value) }
  234. from: 1.0
  235. onValueModified: loader.interval = value
  236. }
  237. }
  238. RowLayout {
  239. width: parent.width
  240. Label {
  241. Layout.preferredWidth: root.textWidth
  242. clip: true
  243. text: i18n("Minimum temperature for fan graphs:")
  244. horizontalAlignment: Text.AlignRight
  245. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  246. }
  247. SpinBox {
  248. id: minTempBox
  249. Layout.minimumWidth: implicitWidth
  250. Layout.fillWidth: true
  251. to: maxTempBox.value
  252. from: Units.fromKelvin(0, Fancontrol.Base.unit)
  253. value: Units.fromCelsius(Fancontrol.Base.minTemp, Fancontrol.Base.unit)
  254. textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 2) + Fancontrol.Base.unit }
  255. onValueChanged: Fancontrol.Base.minTemp = Units.toCelsius(value, Fancontrol.Base.unit)
  256. }
  257. }
  258. RowLayout {
  259. width: parent.width
  260. Label {
  261. Layout.preferredWidth: root.textWidth
  262. clip: true
  263. text: i18n("Maximum temperature for fan graphs:")
  264. horizontalAlignment: Text.AlignRight
  265. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  266. }
  267. SpinBox {
  268. id: maxTempBox
  269. Layout.minimumWidth: implicitWidth
  270. Layout.fillWidth: true
  271. from: minTempBox.value
  272. value: Units.fromCelsius(Fancontrol.Base.maxTemp, Fancontrol.Base.unit)
  273. editable: true
  274. textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 2) + Fancontrol.Base.unit }
  275. onValueChanged: Fancontrol.Base.maxTemp = Units.toCelsius(value, Fancontrol.Base.unit)
  276. }
  277. }
  278. RowLayout {
  279. width: parent.width
  280. Label {
  281. Layout.preferredWidth: root.textWidth
  282. clip: true
  283. text: i18n("Name of the fancontrol systemd service:")
  284. horizontalAlignment: Text.AlignRight
  285. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  286. }
  287. Fancontrol.OptionInput {
  288. Layout.minimumWidth: implicitWidth
  289. Layout.fillWidth: true
  290. color: systemdCom.serviceExists ? "green" : "red"
  291. value: Fancontrol.Base.serviceName
  292. onTextChanged: Fancontrol.Base.serviceName = text
  293. }
  294. }
  295. RowLayout {
  296. width: parent.width
  297. Label {
  298. Layout.preferredWidth: root.textWidth
  299. clip: true
  300. text: i18n("Path to the fancontrol config file:")
  301. horizontalAlignment: Text.AlignRight
  302. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  303. }
  304. Fancontrol.OptionInput {
  305. Layout.minimumWidth: implicitWidth
  306. Layout.fillWidth: true
  307. value: Fancontrol.Base.configUrl.toString().replace("file://", "")
  308. color: Fancontrol.Base.configValid ? "green" : "red"
  309. onTextChanged: Fancontrol.Base.configUrl = text
  310. }
  311. Button {
  312. action: loadAction
  313. }
  314. }
  315. }
  316. Action {
  317. id: saveProfileAction
  318. text: i18n("Save profile")
  319. icon.name: "document-save"
  320. onTriggered: Fancontrol.Base.saveProfile(profileComboBox.saveText)
  321. }
  322. Action {
  323. id: deleteProfileAction
  324. text: i18n("Delete profile")
  325. icon.name: "edit-delete"
  326. onTriggered: Fancontrol.Base.deleteProfile(profileComboBox.currentIndex)
  327. }
  328. Action {
  329. id: loadAction
  330. icon.name: "document-open"
  331. onTriggered: openFileDialog.open()
  332. // tooltip: i18n("Load configuration file")
  333. shortcut: StandardKey.Open
  334. }
  335. FileDialog {
  336. id: openFileDialog
  337. title: i18n("Please choose a configuration file")
  338. folder: "file:///etc"
  339. selectExisting: true
  340. selectMultiple: false
  341. onAccepted: Fancontrol.Base.configUrl = fileUrl;
  342. }
  343. Fancontrol.ErrorDialog {
  344. id: errorDialog
  345. modal: true
  346. }
  347. }