Cura.qml 29 KB

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