Cura.qml 32 KB

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