ProfilesPage.qml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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. Component.onCompleted:
  331. {
  332. var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName;
  333. // Select the required quality name if given
  334. for (var idx = 0; idx < qualitiesModel.rowCount(); idx++) {
  335. var item = qualitiesModel.getItem(idx);
  336. if (item.name == selectedItemName) {
  337. currentIndex = idx;
  338. break;
  339. }
  340. }
  341. }
  342. section.property: "is_read_only"
  343. section.delegate: Rectangle
  344. {
  345. height: childrenRect.height
  346. Label
  347. {
  348. anchors.left: parent.left
  349. anchors.leftMargin: UM.Theme.getSize("default_lining").width
  350. text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles")
  351. font.bold: true
  352. }
  353. }
  354. delegate: Rectangle
  355. {
  356. width: profileScrollView.width
  357. height: childrenRect.height
  358. property bool isCurrentItem: ListView.isCurrentItem
  359. color: isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
  360. Label
  361. {
  362. anchors.left: parent.left
  363. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  364. anchors.right: parent.right
  365. width: Math.floor((parent.width * 0.8))
  366. text: model.name
  367. elide: Text.ElideRight
  368. font.italic: model.name == Cura.MachineManager.activeQualityOrQualityChangesName
  369. color: parent.isCurrentItem ? palette.highlightedText : palette.text
  370. }
  371. MouseArea
  372. {
  373. anchors.fill: parent
  374. onClicked: {
  375. parent.ListView.view.currentIndex = model.index;
  376. }
  377. }
  378. }
  379. }
  380. }
  381. // details panel on the right
  382. Item
  383. {
  384. id: detailsPanel
  385. anchors {
  386. left: profileScrollView.right
  387. leftMargin: UM.Theme.getSize("default_margin").width
  388. top: parent.top
  389. bottom: parent.bottom
  390. right: parent.right
  391. }
  392. Item
  393. {
  394. anchors.fill: parent
  395. visible: base.currentItem != null
  396. Item // Profile title Label
  397. {
  398. id: profileName
  399. width: parent.width
  400. height: childrenRect.height
  401. Label {
  402. text: base.currentItemName
  403. font: UM.Theme.getFont("large")
  404. }
  405. }
  406. Flow {
  407. id: currentSettingsActions
  408. visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName
  409. anchors.left: parent.left
  410. anchors.right: parent.right
  411. anchors.top: profileName.bottom
  412. anchors.topMargin: UM.Theme.getSize("default_margin").height
  413. Button
  414. {
  415. text: catalog.i18nc("@action:button", "Update profile with current settings/overrides")
  416. enabled: Cura.MachineManager.hasUserSettings && !base.currentItem.is_read_only
  417. onClicked: Cura.ContainerManager.updateQualityChanges()
  418. }
  419. Button
  420. {
  421. text: catalog.i18nc("@action:button", "Discard current changes");
  422. enabled: Cura.MachineManager.hasUserSettings
  423. onClicked: Cura.ContainerManager.clearUserContainers();
  424. }
  425. }
  426. Column {
  427. id: profileNotices
  428. anchors.top: currentSettingsActions.visible ? currentSettingsActions.bottom : currentSettingsActions.anchors.top
  429. anchors.topMargin: UM.Theme.getSize("default_margin").height
  430. anchors.left: parent.left
  431. anchors.right: parent.right
  432. spacing: UM.Theme.getSize("default_margin").height
  433. Label {
  434. id: defaultsMessage
  435. visible: false
  436. text: catalog.i18nc("@action:label", "This profile uses the defaults specified by the printer, so it has no settings/overrides in the list below.")
  437. wrapMode: Text.WordWrap
  438. width: parent.width
  439. }
  440. Label {
  441. id: noCurrentSettingsMessage
  442. visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings
  443. text: catalog.i18nc("@action:label", "Your current settings match the selected profile.")
  444. wrapMode: Text.WordWrap
  445. width: parent.width
  446. }
  447. }
  448. TabView
  449. {
  450. anchors.left: parent.left
  451. anchors.top: profileNotices.visible ? profileNotices.bottom : profileNotices.anchors.top
  452. anchors.topMargin: UM.Theme.getSize("default_margin").height
  453. anchors.right: parent.right
  454. anchors.bottom: parent.bottom
  455. currentIndex: 0
  456. ProfileTab
  457. {
  458. title: catalog.i18nc("@title:tab", "Global Settings")
  459. qualityItem: base.currentItem
  460. }
  461. Repeater
  462. {
  463. model: base.extrudersModel
  464. ProfileTab
  465. {
  466. title: model.name
  467. extruderPosition: model.index
  468. qualityItem: base.currentItem
  469. }
  470. }
  471. }
  472. }
  473. }
  474. }
  475. }