Cura.qml 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. // Copyright (c) 2018 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. UM.MainWindow
  15. {
  16. id: base
  17. // Cura application window title
  18. title: catalog.i18nc("@title:window", "Ultimaker Cura")
  19. backgroundColor: UM.Theme.getColor("viewport_background")
  20. UM.I18nCatalog
  21. {
  22. id: catalog
  23. name: "cura"
  24. }
  25. function showTooltip(item, position, text)
  26. {
  27. tooltip.text = text;
  28. position = item.mapToItem(backgroundItem, position.x - UM.Theme.getSize("default_arrow").width, position.y);
  29. tooltip.show(position);
  30. }
  31. function hideTooltip()
  32. {
  33. tooltip.hide();
  34. }
  35. Component.onCompleted:
  36. {
  37. CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
  38. // Workaround silly issues with QML Action's shortcut property.
  39. //
  40. // Currently, there is no way to define shortcuts as "Application Shortcut".
  41. // This means that all Actions are "Window Shortcuts". The code for this
  42. // implements a rather naive check that just checks if any of the action's parents
  43. // are a window. Since the "Actions" object is a singleton it has no parent by
  44. // default. If we set its parent to something contained in this window, the
  45. // shortcut will activate properly because one of its parents is a window.
  46. //
  47. // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property.
  48. Cura.Actions.parent = backgroundItem
  49. CuraApplication.purgeWindows()
  50. }
  51. Item
  52. {
  53. id: backgroundItem
  54. anchors.fill: parent
  55. signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml
  56. function getMeshName(path)
  57. {
  58. //takes the path the complete path of the meshname and returns only the filebase
  59. var fileName = path.slice(path.lastIndexOf("/") + 1)
  60. var fileBase = fileName.slice(0, fileName.indexOf("."))
  61. return fileBase
  62. }
  63. //DeleteSelection on the keypress backspace event
  64. Keys.onPressed:
  65. {
  66. if (event.key == Qt.Key_Backspace)
  67. {
  68. Cura.Actions.deleteSelection.trigger()
  69. }
  70. }
  71. ApplicationMenu
  72. {
  73. id: applicationMenu
  74. window: base
  75. }
  76. MainWindowHeader
  77. {
  78. id: mainWindowHeader
  79. anchors
  80. {
  81. left: parent.left
  82. right: parent.right
  83. top: applicationMenu.bottom
  84. }
  85. }
  86. Item
  87. {
  88. id: contentItem
  89. anchors
  90. {
  91. top: mainWindowHeader.bottom
  92. bottom: parent.bottom
  93. left: parent.left
  94. right: parent.right
  95. }
  96. Keys.forwardTo: applicationMenu
  97. DropArea
  98. {
  99. // The drop area is here to handle files being dropped onto Cura.
  100. anchors.fill: parent
  101. onDropped:
  102. {
  103. if (drop.urls.length > 0)
  104. {
  105. var nonPackages = [];
  106. for (var i = 0; i < drop.urls.length; i++)
  107. {
  108. var filename = drop.urls[i];
  109. if (filename.endsWith(".curapackage"))
  110. {
  111. // Try to install plugin & close.
  112. CuraApplication.getPackageManager().installPackageViaDragAndDrop(filename);
  113. packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting.");
  114. packageInstallDialog.icon = StandardIcon.Information;
  115. packageInstallDialog.open();
  116. }
  117. else
  118. {
  119. nonPackages.push(filename);
  120. }
  121. }
  122. openDialog.handleOpenFileUrls(nonPackages);
  123. }
  124. }
  125. }
  126. Rectangle
  127. {
  128. id: stageMenuBackground
  129. anchors
  130. {
  131. left: parent.left
  132. right: parent.right
  133. top: parent.top
  134. }
  135. visible: stageMenu.source != ""
  136. height: visible ? Math.round(UM.Theme.getSize("stage_menu").height / 2) : 0
  137. LinearGradient
  138. {
  139. anchors.fill: parent
  140. start: Qt.point(0, 0)
  141. end: Qt.point(parent.width, 0)
  142. gradient: Gradient
  143. {
  144. GradientStop
  145. {
  146. position: 0.0
  147. color: UM.Theme.getColor("main_window_header_background")
  148. }
  149. GradientStop
  150. {
  151. position: 0.5
  152. color: UM.Theme.getColor("main_window_header_background_gradient")
  153. }
  154. GradientStop
  155. {
  156. position: 1.0
  157. color: UM.Theme.getColor("main_window_header_background")
  158. }
  159. }
  160. }
  161. }
  162. Connections
  163. {
  164. target: stageMenu.item
  165. onShowTooltip: base.showTooltip(item, location, text)
  166. onHideTooltip: base.hideTooltip()
  167. }
  168. JobSpecs
  169. {
  170. id: jobSpecs
  171. anchors
  172. {
  173. bottom: parent.bottom
  174. bottomMargin: UM.Theme.getSize("default_margin").height
  175. }
  176. }
  177. Toolbar
  178. {
  179. // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by
  180. // plugins)
  181. id: toolbar
  182. property int mouseX: base.mouseX
  183. property int mouseY: base.mouseY
  184. anchors
  185. {
  186. verticalCenter: parent.verticalCenter
  187. left: parent.left
  188. }
  189. visible: CuraApplication.platformActivity && !PrintInformation.preSliced
  190. }
  191. ObjectsList
  192. {
  193. id: objectsList
  194. visible: UM.Preferences.getValue("cura/use_multi_build_plate")
  195. anchors
  196. {
  197. bottom: viewOrientationControls.top
  198. left: toolbar.right
  199. margins: UM.Theme.getSize("default_margin").width
  200. }
  201. }
  202. ViewOrientationControls
  203. {
  204. id: viewOrientationControls
  205. anchors
  206. {
  207. left: parent.left
  208. margins: UM.Theme.getSize("default_margin").width
  209. bottom: parent.bottom
  210. }
  211. }
  212. Cura.ActionPanelWidget
  213. {
  214. anchors.right: parent.right
  215. anchors.bottom: parent.bottom
  216. anchors.rightMargin: UM.Theme.getSize("thick_margin").width
  217. anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
  218. visible: CuraApplication.platformActivity
  219. }
  220. Loader
  221. {
  222. // A stage can control this area. If nothing is set, it will therefore show the 3D view.
  223. id: main
  224. anchors
  225. {
  226. top: stageMenuBackground.bottom
  227. left: parent.left
  228. right: parent.right
  229. bottom: parent.bottom
  230. }
  231. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : ""
  232. }
  233. Loader
  234. {
  235. // The stage menu is, as the name implies, a menu that is defined by the active stage.
  236. // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage
  237. // without this menu!
  238. id: stageMenu
  239. anchors
  240. {
  241. left: parent.left
  242. right: parent.right
  243. top: parent.top
  244. }
  245. height: UM.Theme.getSize("stage_menu").height
  246. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : ""
  247. // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated
  248. // Every time the stage is changed.
  249. property var printSetupSelector: Cura.PrintSetupSelector
  250. {
  251. onShowTooltip: base.showTooltip(item, location, text)
  252. onHideTooltip: base.hideTooltip()
  253. width: UM.Theme.getSize("print_setup_widget").width
  254. height: UM.Theme.getSize("stage_menu").height
  255. headerCornerSide: RoundedRectangle.Direction.Right
  256. }
  257. }
  258. UM.MessageStack
  259. {
  260. anchors
  261. {
  262. horizontalCenter: parent.horizontalCenter
  263. top: parent.verticalCenter
  264. bottom: parent.bottom
  265. bottomMargin: UM.Theme.getSize("default_margin").height
  266. }
  267. }
  268. }
  269. SidebarTooltip
  270. {
  271. id: tooltip
  272. }
  273. }
  274. UM.PreferencesDialog
  275. {
  276. id: preferences
  277. Component.onCompleted:
  278. {
  279. //; Remove & re-add the general page as we want to use our own instead of uranium standard.
  280. removePage(0);
  281. insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml"));
  282. removePage(1);
  283. insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml"));
  284. insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml"));
  285. insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/Materials/MaterialsPage.qml"));
  286. insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml"));
  287. // Remove plug-ins page because we will use the shiny new plugin browser:
  288. removePage(5);
  289. //Force refresh
  290. setPage(0);
  291. }
  292. onVisibleChanged:
  293. {
  294. // When the dialog closes, switch to the General page.
  295. // This prevents us from having a heavy page like Setting Visiblity active in the background.
  296. setPage(0);
  297. }
  298. }
  299. Connections
  300. {
  301. target: Cura.Actions.preferences
  302. onTriggered: preferences.visible = true
  303. }
  304. Connections
  305. {
  306. target: CuraApplication
  307. onShowPreferencesWindow: preferences.visible = true
  308. }
  309. Connections
  310. {
  311. target: Cura.Actions.addProfile
  312. onTriggered:
  313. {
  314. preferences.show();
  315. preferences.setPage(4);
  316. // Create a new profile after a very short delay so the preference page has time to initiate
  317. createProfileTimer.start();
  318. }
  319. }
  320. Connections
  321. {
  322. target: Cura.Actions.configureMachines
  323. onTriggered:
  324. {
  325. preferences.visible = true;
  326. preferences.setPage(2);
  327. }
  328. }
  329. Connections
  330. {
  331. target: Cura.Actions.manageProfiles
  332. onTriggered:
  333. {
  334. preferences.visible = true;
  335. preferences.setPage(4);
  336. }
  337. }
  338. Connections
  339. {
  340. target: Cura.Actions.manageMaterials
  341. onTriggered:
  342. {
  343. preferences.visible = true;
  344. preferences.setPage(3)
  345. }
  346. }
  347. Connections
  348. {
  349. target: Cura.Actions.configureSettingVisibility
  350. onTriggered:
  351. {
  352. preferences.visible = true;
  353. preferences.setPage(1);
  354. if(source && source.key)
  355. {
  356. preferences.getCurrentItem().scrollToSection(source.key);
  357. }
  358. }
  359. }
  360. Timer
  361. {
  362. id: createProfileTimer
  363. repeat: false
  364. interval: 1
  365. onTriggered: preferences.getCurrentItem().createProfile()
  366. }
  367. // BlurSettings is a way to force the focus away from any of the setting items.
  368. // We need to do this in order to keep the bindings intact.
  369. Connections
  370. {
  371. target: Cura.MachineManager
  372. onBlurSettings:
  373. {
  374. contentItem.forceActiveFocus()
  375. }
  376. }
  377. ContextMenu
  378. {
  379. id: contextMenu
  380. }
  381. onPreClosing:
  382. {
  383. close.accepted = CuraApplication.getIsAllChecksPassed();
  384. if (!close.accepted)
  385. {
  386. CuraApplication.checkAndExitApplication();
  387. }
  388. }
  389. MessageDialog
  390. {
  391. id: exitConfirmationDialog
  392. title: catalog.i18nc("@title:window", "Closing Cura")
  393. text: catalog.i18nc("@label", "Are you sure you want to exit Cura?")
  394. icon: StandardIcon.Question
  395. modality: Qt.ApplicationModal
  396. standardButtons: StandardButton.Yes | StandardButton.No
  397. onYes: CuraApplication.callConfirmExitDialogCallback(true)
  398. onNo: CuraApplication.callConfirmExitDialogCallback(false)
  399. onRejected: CuraApplication.callConfirmExitDialogCallback(false)
  400. onVisibilityChanged:
  401. {
  402. if (!visible)
  403. {
  404. // reset the text to default because other modules may change the message text.
  405. text = catalog.i18nc("@label", "Are you sure you want to exit Cura?");
  406. }
  407. }
  408. }
  409. Connections
  410. {
  411. target: CuraApplication
  412. onShowConfirmExitDialog:
  413. {
  414. exitConfirmationDialog.text = message;
  415. exitConfirmationDialog.open();
  416. }
  417. }
  418. Connections
  419. {
  420. target: Cura.Actions.quit
  421. onTriggered: CuraApplication.checkAndExitApplication();
  422. }
  423. Connections
  424. {
  425. target: Cura.Actions.toggleFullScreen
  426. onTriggered: base.toggleFullscreen();
  427. }
  428. FileDialog
  429. {
  430. id: openDialog;
  431. //: File open dialog title
  432. title: catalog.i18nc("@title:window","Open file(s)")
  433. modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
  434. selectMultiple: true
  435. nameFilters: UM.MeshFileHandler.supportedReadFileTypes;
  436. folder: CuraApplication.getDefaultPath("dialog_load_path")
  437. onAccepted:
  438. {
  439. // Because several implementations of the file dialog only update the folder
  440. // when it is explicitly set.
  441. var f = folder;
  442. folder = f;
  443. CuraApplication.setDefaultPath("dialog_load_path", folder);
  444. handleOpenFileUrls(fileUrls);
  445. }
  446. // Yeah... I know... it is a mess to put all those things here.
  447. // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there,
  448. // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic
  449. // and view here may require more effort but make things more difficult to understand.
  450. function handleOpenFileUrls(fileUrlList)
  451. {
  452. // look for valid project files
  453. var projectFileUrlList = [];
  454. var hasGcode = false;
  455. var nonGcodeFileList = [];
  456. for (var i in fileUrlList)
  457. {
  458. var endsWithG = /\.g$/;
  459. var endsWithGcode = /\.gcode$/;
  460. if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i]))
  461. {
  462. continue;
  463. }
  464. else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i]))
  465. {
  466. projectFileUrlList.push(fileUrlList[i]);
  467. }
  468. nonGcodeFileList.push(fileUrlList[i]);
  469. }
  470. hasGcode = nonGcodeFileList.length < fileUrlList.length;
  471. // show a warning if selected multiple files together with Gcode
  472. var hasProjectFile = projectFileUrlList.length > 0;
  473. var selectedMultipleFiles = fileUrlList.length > 1;
  474. if (selectedMultipleFiles && hasGcode)
  475. {
  476. infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles;
  477. infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile;
  478. infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice();
  479. infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice();
  480. infoMultipleFilesWithGcodeDialog.open();
  481. }
  482. else
  483. {
  484. handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList);
  485. }
  486. }
  487. function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList)
  488. {
  489. // we only allow opening one project file
  490. if (selectedMultipleFiles && hasProjectFile)
  491. {
  492. openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice();
  493. openFilesIncludingProjectsDialog.show();
  494. return;
  495. }
  496. if (hasProjectFile)
  497. {
  498. var projectFile = projectFileUrlList[0];
  499. // check preference
  500. var choice = UM.Preferences.getValue("cura/choice_on_open_project");
  501. if (choice == "open_as_project")
  502. {
  503. openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
  504. }
  505. else if (choice == "open_as_model")
  506. {
  507. openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
  508. }
  509. else // always ask
  510. {
  511. // ask whether to open as project or as models
  512. askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
  513. askOpenAsProjectOrModelsDialog.show();
  514. }
  515. }
  516. else
  517. {
  518. openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice());
  519. }
  520. }
  521. }
  522. MessageDialog
  523. {
  524. id: packageInstallDialog
  525. title: catalog.i18nc("@window:title", "Install Package");
  526. standardButtons: StandardButton.Ok
  527. modality: Qt.ApplicationModal
  528. }
  529. MessageDialog
  530. {
  531. id: infoMultipleFilesWithGcodeDialog
  532. title: catalog.i18nc("@title:window", "Open File(s)")
  533. icon: StandardIcon.Information
  534. standardButtons: StandardButton.Ok
  535. 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.")
  536. property var selectedMultipleFiles
  537. property var hasProjectFile
  538. property var fileUrls
  539. property var projectFileUrlList
  540. onAccepted:
  541. {
  542. openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
  543. }
  544. }
  545. Connections
  546. {
  547. target: Cura.Actions.open
  548. onTriggered: openDialog.open()
  549. }
  550. OpenFilesIncludingProjectsDialog
  551. {
  552. id: openFilesIncludingProjectsDialog
  553. }
  554. AskOpenAsProjectOrModelsDialog
  555. {
  556. id: askOpenAsProjectOrModelsDialog
  557. }
  558. Connections
  559. {
  560. target: CuraApplication
  561. onOpenProjectFile:
  562. {
  563. askOpenAsProjectOrModelsDialog.fileUrl = project_file;
  564. askOpenAsProjectOrModelsDialog.show();
  565. }
  566. }
  567. Connections
  568. {
  569. target: Cura.Actions.showProfileFolder
  570. onTriggered:
  571. {
  572. var path = UM.Resources.getPath(UM.Resources.Preferences, "");
  573. if(Qt.platform.os == "windows") {
  574. path = path.replace(/\\/g,"/");
  575. }
  576. Qt.openUrlExternally(path);
  577. if(Qt.platform.os == "linux") {
  578. Qt.openUrlExternally(UM.Resources.getPath(UM.Resources.Resources, ""));
  579. }
  580. }
  581. }
  582. AddMachineDialog
  583. {
  584. id: addMachineDialog
  585. onMachineAdded:
  586. {
  587. machineActionsWizard.firstRun = addMachineDialog.firstRun
  588. machineActionsWizard.start(id)
  589. }
  590. }
  591. // Dialog to handle first run machine actions
  592. UM.Wizard
  593. {
  594. id: machineActionsWizard;
  595. title: catalog.i18nc("@title:window", "Add Printer")
  596. property var machine;
  597. function start(id)
  598. {
  599. var actions = Cura.MachineActionManager.getFirstStartActions(id)
  600. resetPages() // Remove previous pages
  601. for (var i = 0; i < actions.length; i++)
  602. {
  603. actions[i].displayItem.reset()
  604. machineActionsWizard.appendPage(actions[i].displayItem, catalog.i18nc("@title", actions[i].label));
  605. }
  606. //Only start if there are actions to perform.
  607. if (actions.length > 0)
  608. {
  609. machineActionsWizard.currentPage = 0;
  610. show()
  611. }
  612. }
  613. }
  614. MessageDialog
  615. {
  616. id: messageDialog
  617. modality: Qt.ApplicationModal
  618. onAccepted: CuraApplication.messageBoxClosed(clickedButton)
  619. onApply: CuraApplication.messageBoxClosed(clickedButton)
  620. onDiscard: CuraApplication.messageBoxClosed(clickedButton)
  621. onHelp: CuraApplication.messageBoxClosed(clickedButton)
  622. onNo: CuraApplication.messageBoxClosed(clickedButton)
  623. onRejected: CuraApplication.messageBoxClosed(clickedButton)
  624. onReset: CuraApplication.messageBoxClosed(clickedButton)
  625. onYes: CuraApplication.messageBoxClosed(clickedButton)
  626. }
  627. Connections
  628. {
  629. target: CuraApplication
  630. onShowMessageBox:
  631. {
  632. messageDialog.title = title
  633. messageDialog.text = text
  634. messageDialog.informativeText = informativeText
  635. messageDialog.detailedText = detailedText
  636. messageDialog.standardButtons = buttons
  637. messageDialog.icon = icon
  638. messageDialog.visible = true
  639. }
  640. }
  641. DiscardOrKeepProfileChangesDialog
  642. {
  643. id: discardOrKeepProfileChangesDialog
  644. }
  645. Connections
  646. {
  647. target: CuraApplication
  648. onShowDiscardOrKeepProfileChanges:
  649. {
  650. discardOrKeepProfileChangesDialog.show()
  651. }
  652. }
  653. Connections
  654. {
  655. target: Cura.Actions.addMachine
  656. onTriggered: addMachineDialog.visible = true;
  657. }
  658. AboutDialog
  659. {
  660. id: aboutDialog
  661. }
  662. Connections
  663. {
  664. target: Cura.Actions.about
  665. onTriggered: aboutDialog.visible = true;
  666. }
  667. Connections
  668. {
  669. target: CuraApplication
  670. onRequestAddPrinter:
  671. {
  672. addMachineDialog.visible = true
  673. addMachineDialog.firstRun = false
  674. }
  675. }
  676. Timer
  677. {
  678. id: startupTimer;
  679. interval: 100;
  680. repeat: false;
  681. running: true;
  682. onTriggered:
  683. {
  684. if(!base.visible)
  685. {
  686. base.visible = true;
  687. }
  688. // check later if the user agreement dialog has been closed
  689. if (CuraApplication.needToShowUserAgreement)
  690. {
  691. restart();
  692. }
  693. else if(Cura.MachineManager.activeMachine == null)
  694. {
  695. addMachineDialog.open();
  696. }
  697. }
  698. }
  699. }