KCM.qml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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 1.3
  21. import QtQuick.Layouts 1.2
  22. import QtQuick.Dialogs 1.2
  23. import org.kde.kirigami 2.4 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 var locale: Qt.locale()
  32. property real textWidth: 0
  33. property var pwmFans: pwmFanModel.fans
  34. id: root
  35. implicitWidth: 1024
  36. implicitHeight: 768
  37. Connections {
  38. target: Fancontrol.Base
  39. onNeedsApplyChanged: kcm.needsSave = Fancontrol.Base.needsApply
  40. }
  41. Connections {
  42. target: kcm
  43. onAboutToSave: {
  44. Fancontrol.Base.apply();
  45. }
  46. onAboutToLoad: {
  47. Fancontrol.Base.load();
  48. enabledBox.checked = systemdCom.serviceActive;
  49. }
  50. onAboutToDefault: enabledBox.checked = false
  51. }
  52. ColumnLayout {
  53. id: noFansInfo
  54. width: parent.width
  55. anchors.verticalCenter: parent.verticalCenter
  56. spacing: Kirigami.Units.smallSpacing * 2
  57. visible: pwmFans.length === 0
  58. Label {
  59. Layout.alignment: Qt.AlignCenter
  60. text: i18n("There are no pwm capable fans in your system.")
  61. font.pointSize: 14
  62. font.bold: true
  63. }
  64. Button {
  65. Layout.alignment: Qt.AlignCenter
  66. text: loader.sensorsDetected ? i18n("Detect fans again") : i18n("Detect fans")
  67. iconName: kcm.needsAuthorization ? "dialog-password" : ""
  68. onClicked: loader.detectSensors()
  69. }
  70. }
  71. CheckBox {
  72. id: enabledBox
  73. anchors.top: parent.top
  74. visible: pwmFans.length > 0
  75. text: i18n("Control fans manually")
  76. checked: systemdCom.serviceEnabled && systemdCom.serviceActive;
  77. onCheckedChanged: {
  78. systemdCom.serviceActive = enabledBox.checked;
  79. loader.restartServiceAfterTesting = checked;
  80. autostartBox.checked = true;
  81. }
  82. Connections {
  83. target: systemdCom
  84. onServiceActiveChanged: if (systemdCom.serviceActive != enabledBox.checked) enabledBox.checked = systemdCom.serviceActive
  85. }
  86. }
  87. ColumnLayout {
  88. id: bodyLayout
  89. width: parent.width
  90. anchors.bottom: advancedButton.top
  91. anchors.top: enabledBox.bottom
  92. anchors.bottomMargin: advancedButton.height / 4
  93. visible: enabledBox.checked
  94. CheckBox {
  95. id: autostartBox
  96. text: i18n("Enable service at boot")
  97. checked: systemdCom.serviceEnabled
  98. onCheckedChanged: systemdCom.serviceEnabled = checked
  99. Connections {
  100. target: systemdCom
  101. onServiceEnabledChanged: if (systemdCom.serviceEnabled != autostartBox.checked) autostartBox.checked = systemdCom.serviceEnabled
  102. }
  103. }
  104. RowLayout {
  105. visible: enabledBox.checked && pwmFans.length > 0
  106. Label {
  107. text: i18n("Fan:")
  108. Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
  109. renderType: Text.NativeRendering
  110. }
  111. ComboBox {
  112. id: fanComboBox
  113. model: pwmFanModel
  114. textRole: "display"
  115. Layout.fillWidth: true
  116. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  117. }
  118. Button {
  119. Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
  120. text: i18n("Detect fans")
  121. iconName: kcm.needsAuthorization ? "dialog-password" : ""
  122. onClicked: loader.detectSensors()
  123. }
  124. }
  125. Loader {
  126. Layout.fillWidth: true
  127. Layout.fillHeight: true
  128. active: pwmFans.length > fanComboBox.currentIndex && fanComboBox.currentIndex >= 0
  129. sourceComponent: Fancontrol.FanItem {
  130. unit: Fancontrol.Base.unit
  131. fan: pwmFans[fanComboBox.currentIndex]
  132. systemdCom: root.systemdCom
  133. tempModel: root.tempModel
  134. minTemp: Fancontrol.Base.minTemp
  135. maxTemp: Fancontrol.Base.maxTemp
  136. }
  137. }
  138. }
  139. Item {
  140. id: advancedButton
  141. property bool expanded: false
  142. anchors.bottom: settingsArea.top
  143. width: parent.width
  144. height: advancedArrow.height
  145. visible: enabledBox.checked
  146. Image {
  147. id: advancedArrow
  148. source: parent.expanded ? "image://icon/go-down" : "image://icon/go-next"
  149. fillMode: Image.PreserveAspectFit
  150. height: advancedLabel.implicitHeight
  151. }
  152. Label {
  153. id: advancedLabel
  154. anchors.left: advancedArrow.right
  155. text: i18n("Advanced settings")
  156. font.bold: true
  157. }
  158. MouseArea {
  159. anchors.fill: parent
  160. onClicked: parent.expanded = !parent.expanded
  161. }
  162. }
  163. ColumnLayout {
  164. id: settingsArea
  165. visible: enabledBox.checked && parent.height - enabledBox.height - bodyLayout.height - advancedButton.height > height
  166. width: parent.width
  167. anchors.bottom: parent.bottom
  168. clip: true
  169. state: advancedButton.expanded ? "VISIBLE" : "HIDDEN"
  170. states: [
  171. State {
  172. name: "VISIBLE"
  173. PropertyChanges {
  174. target: settingsArea
  175. height: settingsArea.implicitHeight
  176. }
  177. },
  178. State {
  179. name: "HIDDEN"
  180. PropertyChanges {
  181. target: settingsArea
  182. height: 0
  183. }
  184. }
  185. ]
  186. transitions: Transition {
  187. NumberAnimation {
  188. properties: "height"
  189. easing.type: Easing.InOutQuad
  190. }
  191. }
  192. RowLayout {
  193. width: parent.width
  194. Label {
  195. Layout.preferredWidth: root.textWidth
  196. clip: true
  197. text: i18n("Interval:")
  198. horizontalAlignment: Text.AlignRight
  199. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  200. }
  201. SpinBox {
  202. Layout.minimumWidth: implicitWidth
  203. Layout.fillWidth: true
  204. value: loader.interval
  205. suffix: " " + i18np("second", "seconds", loader.interval)
  206. minimumValue: 1.0
  207. onValueChanged: loader.interval = value
  208. }
  209. }
  210. RowLayout {
  211. width: parent.width
  212. Label {
  213. Layout.preferredWidth: root.textWidth
  214. clip: true
  215. text: i18n("Minimum temperature for fan graphs:")
  216. horizontalAlignment: Text.AlignRight
  217. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  218. }
  219. SpinBox {
  220. id: minTempBox
  221. Layout.minimumWidth: implicitWidth
  222. Layout.fillWidth: true
  223. decimals: 2
  224. maximumValue: maxTempBox.value
  225. minimumValue: Units.fromKelvin(0, Fancontrol.Base.unit)
  226. value: Units.fromCelsius(Fancontrol.Base.minTemp, Fancontrol.Base.unit)
  227. suffix: Fancontrol.Base.unit
  228. onValueChanged: Fancontrol.Base.minTemp = Units.toCelsius(value, Fancontrol.Base.unit)
  229. }
  230. }
  231. RowLayout {
  232. width: parent.width
  233. Label {
  234. Layout.preferredWidth: root.textWidth
  235. clip: true
  236. text: i18n("Maximum temperature for fan graphs:")
  237. horizontalAlignment: Text.AlignRight
  238. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  239. }
  240. SpinBox {
  241. id: maxTempBox
  242. Layout.minimumWidth: implicitWidth
  243. Layout.fillWidth: true
  244. decimals: 2
  245. maximumValue: Number.POSITIVE_INFINITY
  246. minimumValue: minTempBox.value
  247. value: Units.fromCelsius(Fancontrol.Base.maxTemp, Fancontrol.Base.unit)
  248. suffix: Fancontrol.Base.unit
  249. onValueChanged: Fancontrol.Base.maxTemp = Units.toCelsius(value, Fancontrol.Base.unit)
  250. }
  251. }
  252. RowLayout {
  253. width: parent.width
  254. Label {
  255. Layout.preferredWidth: root.textWidth
  256. clip: true
  257. text: i18n("Name of the fancontrol systemd service:")
  258. horizontalAlignment: Text.AlignRight
  259. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  260. }
  261. Fancontrol.OptionInput {
  262. Layout.minimumWidth: implicitWidth
  263. Layout.fillWidth: true
  264. color: systemdCom.serviceExists ? "green" : "red"
  265. value: Fancontrol.Base.serviceName
  266. onTextChanged: Fancontrol.Base.serviceName = text
  267. }
  268. }
  269. RowLayout {
  270. width: parent.width
  271. Label {
  272. Layout.preferredWidth: root.textWidth
  273. clip: true
  274. text: i18n("Path to the fancontrol config file:")
  275. horizontalAlignment: Text.AlignRight
  276. Component.onCompleted: root.textWidth = Math.max(root.textWidth, contentWidth)
  277. }
  278. Fancontrol.OptionInput {
  279. Layout.minimumWidth: implicitWidth
  280. Layout.fillWidth: true
  281. value: Fancontrol.Base.configUrl.toString().replace("file://", "")
  282. color: Fancontrol.Base.configValid ? "green" : "red"
  283. onTextChanged: Fancontrol.Base.configUrl = text
  284. }
  285. Button {
  286. action: loadAction
  287. }
  288. }
  289. }
  290. Action {
  291. id: loadAction
  292. iconName: "document-open"
  293. onTriggered: openFileDialog.open()
  294. tooltip: i18n("Load configuration file")
  295. shortcut: StandardKey.Open
  296. }
  297. FileDialog {
  298. id: openFileDialog
  299. title: i18n("Please choose a configuration file")
  300. folder: "file:///etc"
  301. selectExisting: true
  302. selectMultiple: false
  303. onAccepted: Fancontrol.Base.configUrl = fileUrl;
  304. }
  305. Fancontrol.ErrorDialog {
  306. id: errorDialog
  307. modality: Qt.ApplicationModal
  308. }
  309. }