KCM.qml 10.0 KB

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