ProfilesPage.qml 21 KB

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