Cura.qml 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. // Copyright (c) 2022 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 2.15
  5. import QtQuick.Dialogs
  6. import UM 1.5 as UM
  7. import Cura 1.1 as Cura
  8. import "Dialogs"
  9. import "Menus"
  10. import "MainWindow"
  11. import "WelcomePages"
  12. UM.MainWindow
  13. {
  14. id: base
  15. // Cura application window title
  16. title:
  17. {
  18. let result = "";
  19. if(PrintInformation !== null && PrintInformation.jobName != "")
  20. {
  21. result += PrintInformation.jobName + " - ";
  22. }
  23. result += CuraApplication.applicationDisplayName;
  24. return result;
  25. }
  26. backgroundColor: UM.Theme.getColor("viewport_background")
  27. UM.I18nCatalog
  28. {
  29. id: catalog
  30. name: "cura"
  31. }
  32. function showTooltip(item, position, text)
  33. {
  34. tooltip.text = text;
  35. position = item.mapToItem(backgroundItem, position.x - UM.Theme.getSize("default_arrow").width, position.y);
  36. tooltip.show(position);
  37. }
  38. function hideTooltip()
  39. {
  40. tooltip.hide();
  41. }
  42. MouseArea
  43. {
  44. // Hack introduced when switching to qt6
  45. // We used to be able to let the main window's default handlers control this, but something seems to be changed
  46. // for qt6 in the ordering. TODO; We should find out what changed and have a less hacky fix for that.
  47. enabled: parent.visible
  48. anchors.fill: parent
  49. hoverEnabled: true
  50. acceptedButtons: Qt.AllButtons
  51. onPositionChanged: (mouse) => {base.mouseMoved(mouse);}
  52. onPressed: (mouse) => { base.mousePressed(mouse);}
  53. onReleased: (mouse) => { base.mouseReleased(mouse);}
  54. onWheel: (wheel) => {base.wheel(wheel)}
  55. }
  56. Rectangle
  57. {
  58. id: greyOutBackground
  59. anchors.fill: parent
  60. visible: welcomeDialogItem.visible
  61. color: UM.Theme.getColor("window_disabled_background")
  62. opacity: 0.7
  63. z: stageMenu.z + 1
  64. MouseArea
  65. {
  66. // Prevent all mouse events from passing through.
  67. enabled: parent.visible
  68. anchors.fill: parent
  69. hoverEnabled: true
  70. acceptedButtons: Qt.AllButtons
  71. }
  72. }
  73. WelcomeDialogItem
  74. {
  75. id: welcomeDialogItem
  76. visible: false
  77. z: greyOutBackground.z + 1
  78. }
  79. Component.onCompleted:
  80. {
  81. CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
  82. CuraApplication.purgeWindows()
  83. }
  84. Connections
  85. {
  86. // This connection is used when there is no ActiveMachine and the user is logged in
  87. target: CuraApplication
  88. function onShowAddPrintersUncancellableDialog()
  89. {
  90. Cura.Actions.parent = backgroundItem
  91. // Reuse the welcome dialog item to show "Add a printer" only.
  92. welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel()
  93. welcomeDialogItem.progressBarVisible = false
  94. welcomeDialogItem.visible = true
  95. }
  96. }
  97. Connections
  98. {
  99. target: CuraApplication
  100. function onInitializationFinished()
  101. {
  102. // Workaround silly issues with QML Action's shortcut property.
  103. //
  104. // Currently, there is no way to define shortcuts as "Application Shortcut".
  105. // This means that all Actions are "Window Shortcuts". The code for this
  106. // implements a rather naive check that just checks if any of the action's parents
  107. // are a window. Since the "Actions" object is a singleton it has no parent by
  108. // default. If we set its parent to something contained in this window, the
  109. // shortcut will activate properly because one of its parents is a window.
  110. //
  111. // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property.
  112. Cura.Actions.parent = backgroundItem
  113. if (CuraApplication.shouldShowWelcomeDialog())
  114. {
  115. welcomeDialogItem.visible = true
  116. }
  117. else
  118. {
  119. welcomeDialogItem.visible = false
  120. }
  121. // Reuse the welcome dialog item to show "What's New" only.
  122. if (CuraApplication.shouldShowWhatsNewDialog())
  123. {
  124. welcomeDialogItem.model = CuraApplication.getWhatsNewPagesModel()
  125. welcomeDialogItem.progressBarVisible = false
  126. welcomeDialogItem.visible = true
  127. }
  128. // Reuse the welcome dialog item to show the "Add printers" dialog. Triggered when there is no active
  129. // machine and the user is logged in.
  130. if (!Cura.MachineManager.activeMachine && Cura.API.account.isLoggedIn)
  131. {
  132. welcomeDialogItem.model = CuraApplication.getAddPrinterPagesModelWithoutCancel()
  133. welcomeDialogItem.progressBarVisible = false
  134. welcomeDialogItem.visible = true
  135. }
  136. }
  137. }
  138. Item
  139. {
  140. id: backgroundItem
  141. anchors.fill: parent
  142. //DeleteSelection on the keypress backspace event
  143. Keys.onPressed: (event) =>
  144. {
  145. if (event.key == Qt.Key_Backspace)
  146. {
  147. Cura.Actions.deleteSelection.trigger()
  148. }
  149. }
  150. ApplicationMenu
  151. {
  152. id: applicationMenu
  153. }
  154. Item
  155. {
  156. id: headerBackground
  157. anchors
  158. {
  159. top: applicationMenu.bottom
  160. left: parent.left
  161. right: parent.right
  162. }
  163. height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height
  164. Rectangle
  165. {
  166. anchors.fill: parent
  167. color: UM.Theme.getColor("main_window_header_background")
  168. }
  169. // This is a placeholder for adding a pattern in the header
  170. Image
  171. {
  172. id: backgroundPattern
  173. anchors.fill: parent
  174. fillMode: Image.Tile
  175. source: UM.Theme.getImage("header_pattern")
  176. horizontalAlignment: Image.AlignLeft
  177. verticalAlignment: Image.AlignTop
  178. }
  179. }
  180. MainWindowHeader
  181. {
  182. id: mainWindowHeader
  183. anchors
  184. {
  185. left: parent.left
  186. right: parent.right
  187. top: applicationMenu.bottom
  188. }
  189. }
  190. Item
  191. {
  192. id: contentItem
  193. anchors
  194. {
  195. top: mainWindowHeader.bottom
  196. bottom: parent.bottom
  197. left: parent.left
  198. right: parent.right
  199. }
  200. Keys.forwardTo: applicationMenu
  201. DropArea
  202. {
  203. // The drop area is here to handle files being dropped onto Cura.
  204. anchors.fill: parent
  205. onDropped: (drop) =>
  206. {
  207. if (drop.urls.length > 0)
  208. {
  209. var nonPackages = [];
  210. for (var i = 0; i < drop.urls.length; i++)
  211. {
  212. var filename = drop.urls[i];
  213. if (filename.toString().toLowerCase().endsWith(".curapackage"))
  214. {
  215. // Try to install plugin & close.
  216. CuraApplication.installPackageViaDragAndDrop(filename);
  217. packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting.");
  218. packageInstallDialog.open();
  219. }
  220. else
  221. {
  222. nonPackages.push(filename);
  223. }
  224. }
  225. openDialog.handleOpenFileUrls(nonPackages);
  226. }
  227. }
  228. }
  229. ObjectSelector
  230. {
  231. id: objectSelector
  232. visible: CuraApplication.platformActivity
  233. anchors
  234. {
  235. bottom: jobSpecs.top
  236. left: toolbar.right
  237. leftMargin: UM.Theme.getSize("default_margin").width
  238. rightMargin: UM.Theme.getSize("default_margin").width
  239. bottomMargin: UM.Theme.getSize("narrow_margin").height
  240. }
  241. }
  242. JobSpecs
  243. {
  244. id: jobSpecs
  245. visible: CuraApplication.platformActivity
  246. anchors
  247. {
  248. left: toolbar.right
  249. bottom: viewOrientationControls.top
  250. leftMargin: UM.Theme.getSize("default_margin").width
  251. rightMargin: UM.Theme.getSize("default_margin").width
  252. bottomMargin: UM.Theme.getSize("thin_margin").width
  253. topMargin: UM.Theme.getSize("thin_margin").width
  254. }
  255. }
  256. ViewOrientationControls
  257. {
  258. id: viewOrientationControls
  259. anchors
  260. {
  261. left: toolbar.right
  262. bottom: parent.bottom
  263. margins: UM.Theme.getSize("default_margin").width
  264. }
  265. }
  266. Toolbar
  267. {
  268. // The toolbar is the left bar that is populated by all the tools
  269. // (which are dynamically populated by plugins)
  270. id: toolbar
  271. property int mouseX: base.mouseX
  272. property int mouseY: base.mouseY
  273. property bool tallerThanParent: height > parent.height
  274. anchors
  275. {
  276. verticalCenter: tallerThanParent ? undefined : parent.verticalCenter
  277. left: parent.left
  278. }
  279. visible: CuraApplication.platformActivity && !PrintInformation.preSliced
  280. }
  281. // A hint for the loaded content view. Overlay items / controls can safely be placed in this area
  282. Item {
  283. id: mainSafeArea
  284. anchors.left: viewOrientationControls.right
  285. anchors.right: main.right
  286. anchors.top: main.top
  287. anchors.bottom: main.bottom
  288. }
  289. Loader
  290. {
  291. // A stage can control this area. If nothing is set, it will therefore show the 3D view.
  292. id: main
  293. anchors
  294. {
  295. // Align to the top of the stageMenu since the stageMenu may not exist
  296. top: stageMenu.source ? stageMenu.verticalCenter : parent.top
  297. left: parent.left
  298. right: parent.right
  299. bottom: parent.bottom
  300. }
  301. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : ""
  302. onLoaded: {
  303. if (main.item.safeArea !== undefined){
  304. main.item.safeArea = Qt.binding(function() { return mainSafeArea });
  305. }
  306. }
  307. }
  308. Loader
  309. {
  310. // The stage menu is, as the name implies, a menu that is defined by the active stage.
  311. // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage
  312. // without this menu!
  313. id: stageMenu
  314. anchors
  315. {
  316. left: parent.left
  317. right: parent.right
  318. top: parent.top
  319. }
  320. height: UM.Theme.getSize("stage_menu").height
  321. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : ""
  322. // HACK: This is to ensure that the parent never gets set to null, as this wreaks havoc on the focus.
  323. function onParentDestroyed()
  324. {
  325. printSetupSelector.parent = stageMenu
  326. printSetupSelector.visible = false
  327. }
  328. property Item oldParent: null
  329. // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated
  330. // Every time the stage is changed.
  331. property var printSetupSelector: Cura.PrintSetupSelector
  332. {
  333. width: UM.Theme.getSize("print_setup_widget").width
  334. height: UM.Theme.getSize("stage_menu").height
  335. headerCornerSide: RoundedRectangle.Direction.Right
  336. onParentChanged:
  337. {
  338. if(stageMenu.oldParent !=null)
  339. {
  340. stageMenu.oldParent.Component.destruction.disconnect(stageMenu.onParentDestroyed)
  341. }
  342. stageMenu.oldParent = parent
  343. visible = parent != stageMenu
  344. parent.Component.destruction.connect(stageMenu.onParentDestroyed)
  345. }
  346. }
  347. }
  348. UM.MessageStack
  349. {
  350. anchors
  351. {
  352. horizontalCenter: parent.horizontalCenter
  353. top: parent.verticalCenter
  354. bottom: parent.bottom
  355. bottomMargin: UM.Theme.getSize("default_margin").height
  356. }
  357. primaryButton: Component
  358. {
  359. Cura.PrimaryButton
  360. {
  361. text: model.name
  362. iconSource: UM.Theme.getIcon(model.icon)
  363. height: UM.Theme.getSize("message_action_button").height
  364. }
  365. }
  366. secondaryButton: Component
  367. {
  368. Cura.SecondaryButton
  369. {
  370. text: model.name
  371. iconSource: UM.Theme.getIcon(model.icon)
  372. height: UM.Theme.getSize("message_action_button").height
  373. }
  374. }
  375. link: Component
  376. {
  377. Cura.TertiaryButton
  378. {
  379. text: model.name
  380. iconSource:
  381. {
  382. if (model.icon == null || model.icon == "")
  383. {
  384. return UM.Theme.getIcon("LinkExternal")
  385. }
  386. return UM.Theme.getIcon(model.icon)
  387. }
  388. height: UM.Theme.getSize("message_action_button").height
  389. }
  390. }
  391. }
  392. }
  393. PrintSetupTooltip
  394. {
  395. id: tooltip
  396. sourceWidth: UM.Theme.getSize("print_setup_widget").width
  397. }
  398. }
  399. UM.PreferencesDialog
  400. {
  401. id: preferences
  402. Component.onCompleted:
  403. {
  404. //; Remove & re-add the general page as we want to use our own instead of uranium standard.
  405. removePage(0);
  406. insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml"));
  407. removePage(1);
  408. insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml"));
  409. insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml"));
  410. insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/Materials/MaterialsPage.qml"));
  411. insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml"));
  412. currentPage = 0;
  413. }
  414. onVisibleChanged:
  415. {
  416. // When the dialog closes, switch to the General page.
  417. // This prevents us from having a heavy page like Setting Visibility active in the background.
  418. setPage(0);
  419. }
  420. }
  421. Connections
  422. {
  423. target: Cura.Actions.preferences
  424. function onTriggered() { preferences.visible = true }
  425. }
  426. Connections
  427. {
  428. target: CuraApplication
  429. function onShowPreferencesWindow() { preferences.visible = true }
  430. }
  431. Connections
  432. {
  433. target: Cura.Actions.addProfile
  434. function onTriggered()
  435. {
  436. createNewQualityDialog.visible = true;
  437. }
  438. }
  439. Connections
  440. {
  441. target: Cura.Actions.configureMachines
  442. function onTriggered()
  443. {
  444. preferences.visible = true;
  445. preferences.setPage(2);
  446. }
  447. }
  448. Connections
  449. {
  450. target: Cura.Actions.manageProfiles
  451. function onTriggered()
  452. {
  453. preferences.visible = true;
  454. preferences.setPage(4);
  455. }
  456. }
  457. Connections
  458. {
  459. target: Cura.Actions.manageMaterials
  460. function onTriggered()
  461. {
  462. preferences.visible = true;
  463. preferences.setPage(3)
  464. }
  465. }
  466. Connections
  467. {
  468. target: Cura.Actions.configureSettingVisibility
  469. function onTriggered(source)
  470. {
  471. preferences.visible = true;
  472. preferences.setPage(1);
  473. if(source && source.key)
  474. {
  475. preferences.getCurrentItem().scrollToSection(source.key);
  476. }
  477. }
  478. }
  479. // BlurSettings is a way to force the focus away from any of the setting items.
  480. // We need to do this in order to keep the bindings intact.
  481. Connections
  482. {
  483. target: Cura.MachineManager
  484. function onBlurSettings()
  485. {
  486. contentItem.forceActiveFocus()
  487. }
  488. }
  489. ContextMenu
  490. {
  491. id: contextMenu
  492. }
  493. onPreClosing: (close) =>
  494. {
  495. close.accepted = CuraApplication.getIsAllChecksPassed();
  496. if (!close.accepted)
  497. {
  498. CuraApplication.checkAndExitApplication();
  499. }
  500. }
  501. Cura.MessageDialog
  502. {
  503. id: exitConfirmationDialog
  504. title: catalog.i18nc("@title:window %1 is the application name", "Closing %1").arg(CuraApplication.applicationDisplayName)
  505. text: catalog.i18nc("@label %1 is the application name", "Are you sure you want to exit %1?").arg(CuraApplication.applicationDisplayName)
  506. standardButtons: Dialog.Yes | Dialog.No
  507. onAccepted: CuraApplication.callConfirmExitDialogCallback(true)
  508. onRejected: CuraApplication.callConfirmExitDialogCallback(false)
  509. onClosed:
  510. {
  511. if (!visible)
  512. {
  513. // reset the text to default because other modules may change the message text.
  514. text = catalog.i18nc("@label %1 is the application name", "Are you sure you want to exit %1?").arg(CuraApplication.applicationDisplayName);
  515. }
  516. }
  517. }
  518. Connections
  519. {
  520. target: CuraApplication
  521. function onShowConfirmExitDialog(message)
  522. {
  523. exitConfirmationDialog.text = message;
  524. exitConfirmationDialog.open();
  525. }
  526. }
  527. Connections
  528. {
  529. target: Cura.Actions.quit
  530. function onTriggered() { CuraApplication.checkAndExitApplication(); }
  531. }
  532. Connections
  533. {
  534. target: Cura.Actions.toggleFullScreen
  535. function onTriggered() { base.toggleFullscreen() }
  536. }
  537. Connections
  538. {
  539. target: Cura.Actions.exitFullScreen
  540. function onTriggered() { base.exitFullscreen() }
  541. }
  542. FileDialog
  543. {
  544. id: openDialog;
  545. //: File open dialog title
  546. title: catalog.i18nc("@title:window","Open file(s)")
  547. modality: Qt.WindowModal
  548. fileMode: FileDialog.OpenFiles
  549. nameFilters: UM.MeshFileHandler.supportedReadFileTypes;
  550. currentFolder: CuraApplication.getDefaultPath("dialog_load_path")
  551. onAccepted:
  552. {
  553. // Because several implementations of the file dialog only update the folder
  554. // when it is explicitly set.
  555. var f = currentFolder;
  556. currentFolder = f;
  557. CuraApplication.setDefaultPath("dialog_load_path", currentFolder);
  558. handleOpenFileUrls(selectedFiles);
  559. }
  560. // Yeah... I know... it is a mess to put all those things here.
  561. // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there,
  562. // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic
  563. // and view here may require more effort but make things more difficult to understand.
  564. function handleOpenFileUrls(fileUrlList)
  565. {
  566. // look for valid project files
  567. var projectFileUrlList = [];
  568. var hasGcode = false;
  569. var nonGcodeFileList = [];
  570. for (var i in fileUrlList)
  571. {
  572. var endsWithG = /\.g$/;
  573. var endsWithGcode = /\.gcode$/;
  574. if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i]))
  575. {
  576. continue;
  577. }
  578. else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i]))
  579. {
  580. projectFileUrlList.push(fileUrlList[i]);
  581. }
  582. nonGcodeFileList.push(fileUrlList[i]);
  583. }
  584. hasGcode = nonGcodeFileList.length < fileUrlList.length;
  585. // show a warning if selected multiple files together with Gcode
  586. var hasProjectFile = projectFileUrlList.length > 0;
  587. var selectedMultipleFiles = fileUrlList.length > 1;
  588. if (selectedMultipleFiles && hasGcode)
  589. {
  590. infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles;
  591. infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile;
  592. infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice();
  593. infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice();
  594. infoMultipleFilesWithGcodeDialog.open();
  595. }
  596. else
  597. {
  598. handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList);
  599. }
  600. }
  601. function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList)
  602. {
  603. // Make sure the files opened through the openFilesIncludingProjectDialog are added to the recent files list
  604. openFilesIncludingProjectsDialog.addToRecent = true;
  605. // we only allow opening one project file
  606. if (selectedMultipleFiles && hasProjectFile)
  607. {
  608. openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice();
  609. openFilesIncludingProjectsDialog.show();
  610. return;
  611. }
  612. if (hasProjectFile)
  613. {
  614. var projectFile = projectFileUrlList[0];
  615. // check preference
  616. var choice = UM.Preferences.getValue("cura/choice_on_open_project");
  617. if (choice == "open_as_project")
  618. {
  619. openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
  620. }
  621. else if (choice == "open_as_model")
  622. {
  623. openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
  624. }
  625. else // always ask
  626. {
  627. // ask whether to open as project or as models
  628. askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
  629. askOpenAsProjectOrModelsDialog.addToRecent = true;
  630. askOpenAsProjectOrModelsDialog.show();
  631. }
  632. }
  633. else
  634. {
  635. openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice());
  636. }
  637. }
  638. }
  639. Cura.MessageDialog
  640. {
  641. id: packageInstallDialog
  642. title: catalog.i18nc("@window:title", "Install Package")
  643. standardButtons: Dialog.Ok
  644. }
  645. Cura.MessageDialog
  646. {
  647. id: infoMultipleFilesWithGcodeDialog
  648. title: catalog.i18nc("@title:window", "Open File(s)")
  649. standardButtons: Dialog.Ok
  650. text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.")
  651. property var selectedMultipleFiles
  652. property var hasProjectFile
  653. property var fileUrls
  654. property var projectFileUrlList
  655. onAccepted:
  656. {
  657. openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
  658. }
  659. }
  660. Connections
  661. {
  662. target: Cura.Actions.open
  663. function onTriggered() { openDialog.open() }
  664. }
  665. OpenFilesIncludingProjectsDialog
  666. {
  667. id: openFilesIncludingProjectsDialog
  668. }
  669. AskOpenAsProjectOrModelsDialog
  670. {
  671. id: askOpenAsProjectOrModelsDialog
  672. }
  673. Connections
  674. {
  675. target: CuraApplication
  676. function onOpenProjectFile(project_file, add_to_recent_files)
  677. {
  678. askOpenAsProjectOrModelsDialog.fileUrl = project_file;
  679. askOpenAsProjectOrModelsDialog.addToRecent = add_to_recent_files;
  680. askOpenAsProjectOrModelsDialog.show();
  681. }
  682. }
  683. Connections
  684. {
  685. target: Cura.Actions.showProfileFolder
  686. function onTriggered()
  687. {
  688. var path = UM.Resources.getPath(UM.Resources.Preferences, "");
  689. if(Qt.platform.os == "windows")
  690. {
  691. path = path.replace(/\\/g,"/");
  692. }
  693. Qt.openUrlExternally(path);
  694. if(Qt.platform.os == "linux")
  695. {
  696. Qt.openUrlExternally(UM.Resources.getPath(UM.Resources.Resources, ""));
  697. }
  698. }
  699. }
  700. Component
  701. {
  702. id: discardOrKeepProfileChangesDialogComponent
  703. DiscardOrKeepProfileChangesDialog { }
  704. }
  705. Loader
  706. {
  707. id: discardOrKeepProfileChangesDialogLoader
  708. }
  709. Connections
  710. {
  711. target: CuraApplication
  712. function onShowCompareAndSaveProfileChanges(profileState)
  713. {
  714. discardOrKeepProfileChangesDialogLoader.sourceComponent = discardOrKeepProfileChangesDialogComponent
  715. discardOrKeepProfileChangesDialogLoader.item.buttonState = profileState
  716. discardOrKeepProfileChangesDialogLoader.item.show()
  717. }
  718. function onShowDiscardOrKeepProfileChanges()
  719. {
  720. onShowCompareAndSaveProfileChanges(DiscardOrKeepProfileChangesDialog.ButtonsType.DiscardOrKeep)
  721. }
  722. }
  723. Cura.WizardDialog
  724. {
  725. id: addMachineDialog
  726. title: catalog.i18nc("@title:window", "Add Printer")
  727. model: CuraApplication.getAddPrinterPagesModel()
  728. progressBarVisible: false
  729. }
  730. Cura.WizardDialog
  731. {
  732. id: whatsNewDialog
  733. title: catalog.i18nc("@title:window", "What's New")
  734. minimumWidth: UM.Theme.getSize("welcome_wizard_window").width
  735. minimumHeight: UM.Theme.getSize("welcome_wizard_window").height
  736. model: CuraApplication.getWhatsNewPagesModel()
  737. progressBarVisible: false
  738. visible: false
  739. }
  740. Connections
  741. {
  742. target: Cura.Actions.whatsNew
  743. function onTriggered() { whatsNewDialog.show() }
  744. }
  745. Connections
  746. {
  747. target: Cura.Actions.addMachine
  748. function onTriggered()
  749. {
  750. // Make sure to show from the first page when the dialog shows up.
  751. addMachineDialog.resetModelState()
  752. addMachineDialog.show()
  753. }
  754. }
  755. AboutDialog
  756. {
  757. id: aboutDialog
  758. }
  759. Connections
  760. {
  761. target: Cura.Actions.about
  762. function onTriggered() { aboutDialog.visible = true; }
  763. }
  764. Timer
  765. {
  766. id: startupTimer
  767. interval: 100
  768. repeat: false
  769. running: true
  770. onTriggered:
  771. {
  772. if (!base.visible)
  773. {
  774. base.visible = true
  775. }
  776. }
  777. }
  778. Cura.RenameDialog
  779. {
  780. id: createNewQualityDialog
  781. title: catalog.i18nc("@title:window", "Save Custom Profile")
  782. objectPlaceholder: catalog.i18nc("@textfield:placeholder", "New Custom Profile")
  783. explanation: catalog.i18nc("@info", "Custom profile name:")
  784. extraInfo:
  785. [
  786. UM.ColorImage
  787. {
  788. width: UM.Theme.getSize("message_type_icon").width
  789. height: UM.Theme.getSize("message_type_icon").height
  790. source: UM.Theme.getIcon("Information")
  791. color: UM.Theme.getColor("text")
  792. },
  793. Column
  794. {
  795. UM.Label
  796. {
  797. text: catalog.i18nc
  798. (
  799. "@label %i will be replaced with a profile name",
  800. "<b>Only user changed settings will be saved in the custom profile.</b><br/>" +
  801. "For materials that support it, the new custom profile will inherit properties from <b>%1</b>."
  802. ).arg(Cura.MachineManager.activeQualityOrQualityChangesName)
  803. wrapMode: Text.WordWrap
  804. width: parent.parent.width - 2 * UM.Theme.getSize("message_type_icon").width
  805. }
  806. Cura.TertiaryButton
  807. {
  808. text: catalog.i18nc("@action:button", "Learn more about Cura print profiles")
  809. iconSource: UM.Theme.getIcon("LinkExternal")
  810. isIconOnRightSide: true
  811. leftPadding: 0
  812. rightPadding: 0
  813. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/s/article/1667337576882")
  814. }
  815. }
  816. ]
  817. okButtonText: catalog.i18nc("@button", "Save new profile")
  818. onAccepted: CuraApplication.getQualityManagementModel().createQualityChanges(newName, true);
  819. }
  820. /**
  821. * Function to check whether a QML object has a certain type.
  822. * Taken from StackOverflow: https://stackoverflow.com/a/28384228 and
  823. * adapted to our code style.
  824. * Licensed under CC BY-SA 3.0.
  825. * \param obj The QtObject to get the name of.
  826. * \param class_name (str) The name of the class to check against. Has to be
  827. * the QtObject class name, not the QML entity name.
  828. */
  829. function qmlTypeOf(obj, class_name)
  830. {
  831. //className plus "(" is the class instance without modification.
  832. //className plus "_QML" is the class instance with user-defined properties.
  833. var str = obj.toString();
  834. return str.indexOf(class_name + "(") == 0 || str.indexOf(class_name + "_QML") == 0;
  835. }
  836. }