ProfilesPage.qml 18 KB

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