Cura.qml 24 KB

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