ProfilesPage.qml 19 KB

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