ProfilesPage.qml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. // Copyright (c) 2018 Ultimaker B.V.
  2. // Uranium is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 1.4
  5. import QtQuick.Layouts 1.3
  6. import QtQuick.Dialogs 1.2
  7. import UM 1.2 as UM
  8. import Cura 1.0 as Cura
  9. Item
  10. {
  11. id: base
  12. property QtObject qualityManager: CuraApplication.getQualityManager()
  13. property var resetEnabled: false // Keep PreferencesDialog happy
  14. property var extrudersModel: Cura.ExtrudersModel {}
  15. UM.I18nCatalog { id: catalog; name: "cura"; }
  16. Cura.QualityManagementModel {
  17. id: qualitiesModel
  18. }
  19. Label {
  20. id: titleLabel
  21. anchors {
  22. top: parent.top
  23. left: parent.left
  24. right: parent.right
  25. margins: 5 * screenScaleFactor
  26. }
  27. font.pointSize: 18
  28. text: catalog.i18nc("@title:tab", "Profiles")
  29. }
  30. property var hasCurrentItem: base.currentItem != null
  31. property var currentItem: {
  32. var current_index = qualityListView.currentIndex;
  33. return (current_index == -1) ? null : qualitiesModel.getItem(current_index);
  34. }
  35. property var currentItemName: hasCurrentItem ? base.currentItem.name : ""
  36. property var isCurrentItemActivated: {
  37. if (!base.currentItem) {
  38. return false;
  39. }
  40. return base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName;
  41. }
  42. property var canCreateProfile: {
  43. return isCurrentItemActivated && Cura.MachineManager.hasUserSettings;
  44. }
  45. Row // Button Row
  46. {
  47. id: buttonRow
  48. anchors {
  49. left: parent.left
  50. right: parent.right
  51. top: titleLabel.bottom
  52. }
  53. height: childrenRect.height
  54. // Activate button
  55. Button
  56. {
  57. text: catalog.i18nc("@action:button", "Activate")
  58. iconName: "list-activate"
  59. enabled: !isCurrentItemActivated
  60. onClicked: {
  61. if (base.currentItem.is_read_only) {
  62. Cura.MachineManager.setQualityGroup(base.currentItem.quality_group);
  63. } else {
  64. Cura.MachineManager.setQualityChangesGroup(base.currentItem.quality_changes_group);
  65. }
  66. }
  67. }
  68. // Create button
  69. Button
  70. {
  71. text: catalog.i18nc("@label", "Create")
  72. iconName: "list-add"
  73. enabled: base.canCreateProfile && !Cura.MachineManager.stacksHaveErrors
  74. visible: base.canCreateProfile
  75. onClicked: {
  76. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  77. createQualityDialog.open();
  78. createQualityDialog.selectText();
  79. }
  80. }
  81. // Duplicate button
  82. Button
  83. {
  84. text: catalog.i18nc("@label", "Duplicate")
  85. iconName: "list-add"
  86. enabled: !base.canCreateProfile
  87. visible: !base.canCreateProfile
  88. onClicked: {
  89. duplicateQualityDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
  90. duplicateQualityDialog.open();
  91. duplicateQualityDialog.selectText();
  92. }
  93. }
  94. // Remove button
  95. Button
  96. {
  97. text: catalog.i18nc("@action:button", "Remove")
  98. iconName: "list-remove"
  99. enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated
  100. onClicked: {
  101. forceActiveFocus();
  102. confirmRemoveQualityDialog.open();
  103. }
  104. }
  105. // Rename button
  106. Button
  107. {
  108. text: catalog.i18nc("@action:button", "Rename")
  109. iconName: "edit-rename"
  110. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  111. onClicked: {
  112. renameQualityDialog.object = base.currentItem.name;
  113. renameQualityDialog.open();
  114. renameQualityDialog.selectText();
  115. }
  116. }
  117. // Import button
  118. Button
  119. {
  120. text: catalog.i18nc("@action:button", "Import")
  121. iconName: "document-import"
  122. onClicked: {
  123. importDialog.open();
  124. }
  125. }
  126. // Export button
  127. Button
  128. {
  129. text: catalog.i18nc("@action:button", "Export")
  130. iconName: "document-export"
  131. enabled: base.hasCurrentItem && !base.currentItem.is_read_only
  132. onClicked: {
  133. exportDialog.open();
  134. }
  135. }
  136. }
  137. // Click create profile from ... in Profile context menu
  138. signal createProfile()
  139. onCreateProfile:
  140. {
  141. createQualityDialog.object = Cura.ContainerManager.makeUniqueName(Cura.MachineManager.activeQualityOrQualityChangesName);
  142. createQualityDialog.open();
  143. createQualityDialog.selectText();
  144. }
  145. // Dialog to request a name when creating a new profile
  146. UM.RenameDialog
  147. {
  148. id: createQualityDialog
  149. title: catalog.i18nc("@title:window", "Create Profile")
  150. object: "<new name>"
  151. onAccepted:
  152. {
  153. base.newQualityNameToSelect = newName; // We want to switch to the new profile once it's created
  154. base.toActivateNewQuality = true;
  155. base.qualityManager.createQualityChanges(newName);
  156. }
  157. }
  158. property string newQualityNameToSelect: ""
  159. property bool toActivateNewQuality: false
  160. // This connection makes sure that we will switch to the correct quality after the model gets updated
  161. Connections
  162. {
  163. target: qualitiesModel
  164. onItemsChanged: {
  165. var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name;
  166. if (newQualityNameToSelect != "") {
  167. toSelectItemName = newQualityNameToSelect;
  168. }
  169. var newIdx = -1; // Default to nothing if nothing can be found
  170. if (toSelectItemName != "") {
  171. // Select the required quality name if given
  172. for (var idx = 0; idx < qualitiesModel.rowCount(); ++idx) {
  173. var item = qualitiesModel.getItem(idx);
  174. if (item.name == toSelectItemName) {
  175. // Switch to the newly created profile if needed
  176. newIdx = idx;
  177. if (base.toActivateNewQuality) {
  178. // Activate this custom quality if required
  179. Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group);
  180. }
  181. break;
  182. }
  183. }
  184. }
  185. qualityListView.currentIndex = newIdx;
  186. // Reset states
  187. base.newQualityNameToSelect = "";
  188. base.toActivateNewQuality = false;
  189. }
  190. }
  191. // Dialog to request a name when duplicating a new profile
  192. UM.RenameDialog
  193. {
  194. id: duplicateQualityDialog
  195. title: catalog.i18nc("@title:window", "Duplicate Profile")
  196. object: "<new name>"
  197. onAccepted:
  198. {
  199. base.qualityManager.duplicateQualityChanges(newName, base.currentItem);
  200. }
  201. }
  202. // Confirmation dialog for removing a profile
  203. MessageDialog
  204. {
  205. id: confirmRemoveQualityDialog
  206. icon: StandardIcon.Question;
  207. title: catalog.i18nc("@title:window", "Confirm Remove")
  208. text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName)
  209. standardButtons: StandardButton.Yes | StandardButton.No
  210. modality: Qt.ApplicationModal
  211. onYes:
  212. {
  213. base.qualityManager.removeQualityChangesGroup(base.currentItem.quality_changes_group);
  214. // reset current item to the first if available
  215. qualityListView.currentIndex = -1; // Reset selection.
  216. }
  217. }
  218. // Dialog to rename a quality profile
  219. UM.RenameDialog
  220. {
  221. id: renameQualityDialog
  222. title: catalog.i18nc("@title:window", "Rename Profile")
  223. object: "<new name>"
  224. onAccepted:
  225. {
  226. var actualNewName = base.qualityManager.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName);
  227. base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated
  228. }
  229. }
  230. // Dialog for importing a quality profile
  231. FileDialog
  232. {
  233. id: importDialog
  234. title: catalog.i18nc("@title:window", "Import Profile")
  235. selectExisting: true
  236. nameFilters: qualitiesModel.getFileNameFilters("profile_reader")
  237. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  238. onAccepted:
  239. {
  240. var result = Cura.ContainerManager.importProfile(fileUrl);
  241. messageDialog.text = result.message;
  242. if (result.status == "ok") {
  243. messageDialog.icon = StandardIcon.Information;
  244. }
  245. else if (result.status == "duplicate") {
  246. messageDialog.icon = StandardIcon.Warning;
  247. }
  248. else {
  249. messageDialog.icon = StandardIcon.Critical;
  250. }
  251. messageDialog.open();
  252. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  253. }
  254. }
  255. // Dialog for exporting a quality profile
  256. FileDialog
  257. {
  258. id: exportDialog
  259. title: catalog.i18nc("@title:window", "Export Profile")
  260. selectExisting: false
  261. nameFilters: qualitiesModel.getFileNameFilters("profile_writer")
  262. folder: CuraApplication.getDefaultPath("dialog_profile_path")
  263. onAccepted:
  264. {
  265. var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group,
  266. fileUrl, selectedNameFilter);
  267. if (result && result.status == "error") {
  268. messageDialog.icon = StandardIcon.Critical;
  269. messageDialog.text = result.message;
  270. messageDialog.open();
  271. }
  272. // else pop-up Message thing from python code
  273. CuraApplication.setDefaultPath("dialog_profile_path", folder);
  274. }
  275. }
  276. Item {
  277. id: contentsItem
  278. anchors {
  279. top: titleLabel.bottom
  280. left: parent.left
  281. right: parent.right
  282. bottom: parent.bottom
  283. margins: 5 * screenScaleFactor
  284. bottomMargin: 0
  285. }
  286. clip: true
  287. }
  288. Item
  289. {
  290. anchors {
  291. top: buttonRow.bottom
  292. topMargin: UM.Theme.getSize("default_margin").height
  293. left: parent.left
  294. right: parent.right
  295. bottom: parent.bottom
  296. }
  297. SystemPalette { id: palette }
  298. Label
  299. {
  300. id: captionLabel
  301. anchors {
  302. top: parent.top
  303. left: parent.left
  304. }
  305. visible: text != ""
  306. text: catalog.i18nc("@label %1 is printer name", "Printer: %1").arg(Cura.MachineManager.activeMachineName)
  307. width: profileScrollView.width
  308. elide: Text.ElideRight
  309. }
  310. ScrollView
  311. {
  312. id: profileScrollView
  313. anchors {
  314. top: captionLabel.visible ? captionLabel.bottom : parent.top
  315. topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
  316. bottom: parent.bottom
  317. left: parent.left
  318. }
  319. Rectangle {
  320. parent: viewport
  321. anchors.fill: parent
  322. color: palette.light
  323. }
  324. width: true ? (parent.width * 0.4) | 0 : parent.width
  325. frameVisible: true
  326. ListView
  327. {
  328. id: qualityListView
  329. model: qualitiesModel
  330. section.property: "is_read_only"
  331. section.delegate: Rectangle
  332. {
  333. height: childrenRect.height
  334. Label
  335. {
  336. anchors.left: parent.left
  337. anchors.leftMargin: UM.Theme.getSize("default_lining").width
  338. text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles")
  339. font.bold: true
  340. }
  341. }
  342. delegate: Rectangle
  343. {
  344. width: profileScrollView.width
  345. height: childrenRect.height
  346. color: ListView.isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
  347. Row
  348. {
  349. spacing: (UM.Theme.getSize("default_margin").width / 2) | 0
  350. anchors.left: parent.left
  351. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  352. anchors.right: parent.right
  353. Label
  354. {
  355. width: Math.floor((parent.width * 0.8))
  356. text: model.name
  357. elide: Text.ElideRight
  358. font.italic: model.name == Cura.MachineManager.activeQualityOrQualityChangesName
  359. color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text
  360. }
  361. }
  362. MouseArea
  363. {
  364. anchors.fill: parent
  365. onClicked: {
  366. parent.ListView.view.currentIndex = model.index;
  367. }
  368. }
  369. }
  370. }
  371. }
  372. // details panel on the right
  373. Item
  374. {
  375. id: detailsPanel
  376. anchors {
  377. left: profileScrollView.right
  378. leftMargin: UM.Theme.getSize("default_margin").width
  379. top: parent.top
  380. bottom: parent.bottom
  381. right: parent.right
  382. }
  383. Item
  384. {
  385. anchors.fill: parent
  386. visible: base.currentItem != null
  387. Item // Profile title Label
  388. {
  389. id: profileName
  390. width: parent.width
  391. height: childrenRect.height
  392. Label {
  393. text: base.currentItemName
  394. font: UM.Theme.getFont("large")
  395. }
  396. }
  397. Flow {
  398. id: currentSettingsActions
  399. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName
  400. anchors.left: parent.left
  401. anchors.right: parent.right
  402. anchors.top: profileName.bottom
  403. anchors.topMargin: UM.Theme.getSize("default_margin").height
  404. Button
  405. {
  406. text: catalog.i18nc("@action:button", "Update profile with current settings/overrides")
  407. enabled: Cura.MachineManager.hasUserSettings && !base.currentItem.is_read_only
  408. onClicked: Cura.ContainerManager.updateQualityChanges()
  409. }
  410. Button
  411. {
  412. text: catalog.i18nc("@action:button", "Discard current changes");
  413. enabled: Cura.MachineManager.hasUserSettings
  414. onClicked: Cura.ContainerManager.clearUserContainers();
  415. }
  416. }
  417. Column {
  418. id: profileNotices
  419. anchors.top: currentSettingsActions.visible ? currentSettingsActions.bottom : currentSettingsActions.anchors.top
  420. anchors.topMargin: UM.Theme.getSize("default_margin").height
  421. anchors.left: parent.left
  422. anchors.right: parent.right
  423. spacing: UM.Theme.getSize("default_margin").height
  424. Label {
  425. id: defaultsMessage
  426. visible: false
  427. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  428. wrapMode: Text.WordWrap
  429. width: parent.width
  430. }
  431. Label {
  432. id: noCurrentSettingsMessage
  433. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  434. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  435. wrapMode: Text.WordWrap
  436. width: parent.width
  437. }
  438. }
  439. TabView
  440. {
  441. anchors.left: parent.left
  442. anchors.top: profileNotices.visible ? profileNotices.bottom : profileNotices.anchors.top
  443. anchors.topMargin: UM.Theme.getSize("default_margin").height
  444. anchors.right: parent.right
  445. anchors.bottom: parent.bottom
  446. currentIndex: 0
  447. ProfileTab
  448. {
  449. title: catalog.i18nc("@title:tab", "Global Settings")
  450. qualityItem: base.currentItem
  451. }
  452. Repeater
  453. {
  454. model: base.extrudersModel
  455. ProfileTab
  456. {
  457. title: model.name
  458. extruderPosition: model.index
  459. qualityItem: base.currentItem
  460. }
  461. }
  462. }
  463. }
  464. }
  465. }
  466. }