Cura.qml 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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 a placehoder for adding a pattern in the header
  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. Toolbar
  172. {
  173. // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by
  174. // plugins)
  175. id: toolbar
  176. property int mouseX: base.mouseX
  177. property int mouseY: base.mouseY
  178. anchors
  179. {
  180. verticalCenter: parent.verticalCenter
  181. left: parent.left
  182. }
  183. visible: CuraApplication.platformActivity && !PrintInformation.preSliced
  184. }
  185. ObjectsList
  186. {
  187. id: objectsList
  188. visible: UM.Preferences.getValue("cura/use_multi_build_plate")
  189. anchors
  190. {
  191. bottom: viewOrientationControls.top
  192. left: toolbar.right
  193. margins: UM.Theme.getSize("default_margin").width
  194. }
  195. }
  196. JobSpecs
  197. {
  198. id: jobSpecs
  199. visible: CuraApplication.platformActivity
  200. anchors
  201. {
  202. left: parent.left
  203. bottom: viewOrientationControls.top
  204. margins: UM.Theme.getSize("default_margin").width
  205. bottomMargin: UM.Theme.getSize("thin_margin").width
  206. }
  207. }
  208. ViewOrientationControls
  209. {
  210. id: viewOrientationControls
  211. anchors
  212. {
  213. left: parent.left
  214. bottom: parent.bottom
  215. margins: UM.Theme.getSize("default_margin").width
  216. }
  217. }
  218. Cura.ActionPanelWidget
  219. {
  220. id: actionPanelWidget
  221. anchors.right: parent.right
  222. anchors.bottom: parent.bottom
  223. anchors.rightMargin: UM.Theme.getSize("thick_margin").width
  224. anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
  225. /*
  226. Show this panel only if there is something on the build plate, and there is NOT an opaque item in front of the build plate.
  227. This cannot be solved by Z indexing! If you want to try solving this, please increase this counter when you're done:
  228. Number of people having tried to fix this by z-indexing: 2
  229. The problem arises from the following render order requirements:
  230. - The stage menu must be rendered above the stage main.
  231. - The stage main must be rendered above the action panel (because the monitor page must be rendered above the action panel).
  232. - The action panel must be rendered above the expandable components drop-down.
  233. However since the expandable components drop-downs are child elements of the stage menu,
  234. they can't be rendered lower than elements that are lower than the stage menu.
  235. Therefore we opted to forego the second requirement and hide the action panel instead when something obscures it (except the expandable components).
  236. We assume that QQuickRectangles are always opaque and any other item is not.
  237. */
  238. visible: CuraApplication.platformActivity && (main.item == null || !qmlTypeOf(main.item, "QQuickRectangle"))
  239. }
  240. Item
  241. {
  242. id: additionalComponents
  243. width: childrenRect.width
  244. anchors.right: actionPanelWidget.left
  245. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  246. anchors.bottom: actionPanelWidget.bottom
  247. anchors.bottomMargin: UM.Theme.getSize("thick_margin").height * 2
  248. visible: actionPanelWidget.visible
  249. Row
  250. {
  251. id: additionalComponentsRow
  252. anchors.verticalCenter: parent.verticalCenter
  253. spacing: UM.Theme.getSize("default_margin").width
  254. }
  255. }
  256. Component.onCompleted: contentItem.addAdditionalComponents()
  257. Connections
  258. {
  259. target: CuraApplication
  260. onAdditionalComponentsChanged: contentItem.addAdditionalComponents("saveButton")
  261. }
  262. function addAdditionalComponents()
  263. {
  264. for (var component in CuraApplication.additionalComponents["saveButton"])
  265. {
  266. CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
  267. }
  268. }
  269. Loader
  270. {
  271. // A stage can control this area. If nothing is set, it will therefore show the 3D view.
  272. id: main
  273. anchors
  274. {
  275. // Align to the top of the stageMenu since the stageMenu may not exist
  276. top: stageMenu.source ? stageMenu.verticalCenter : parent.top
  277. left: parent.left
  278. right: parent.right
  279. bottom: parent.bottom
  280. }
  281. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : ""
  282. }
  283. Loader
  284. {
  285. // The stage menu is, as the name implies, a menu that is defined by the active stage.
  286. // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage
  287. // without this menu!
  288. id: stageMenu
  289. anchors
  290. {
  291. left: parent.left
  292. right: parent.right
  293. top: parent.top
  294. }
  295. height: UM.Theme.getSize("stage_menu").height
  296. source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : ""
  297. // HACK: This is to ensure that the parent never gets set to null, as this wreaks havoc on the focus.
  298. function onParentDestroyed()
  299. {
  300. printSetupSelector.parent = stageMenu
  301. printSetupSelector.visible = false
  302. }
  303. property Item oldParent: null
  304. // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated
  305. // Every time the stage is changed.
  306. property var printSetupSelector: Cura.PrintSetupSelector
  307. {
  308. width: UM.Theme.getSize("print_setup_widget").width
  309. height: UM.Theme.getSize("stage_menu").height
  310. headerCornerSide: RoundedRectangle.Direction.Right
  311. onParentChanged:
  312. {
  313. if(stageMenu.oldParent !=null)
  314. {
  315. stageMenu.oldParent.Component.destruction.disconnect(stageMenu.onParentDestroyed)
  316. }
  317. stageMenu.oldParent = parent
  318. visible = parent != stageMenu
  319. parent.Component.destruction.connect(stageMenu.onParentDestroyed)
  320. }
  321. }
  322. }
  323. UM.MessageStack
  324. {
  325. anchors
  326. {
  327. horizontalCenter: parent.horizontalCenter
  328. top: parent.verticalCenter
  329. bottom: parent.bottom
  330. bottomMargin: UM.Theme.getSize("default_margin").height
  331. }
  332. }
  333. }
  334. PrintSetupTooltip
  335. {
  336. id: tooltip
  337. }
  338. }
  339. UM.PreferencesDialog
  340. {
  341. id: preferences
  342. Component.onCompleted:
  343. {
  344. //; Remove & re-add the general page as we want to use our own instead of uranium standard.
  345. removePage(0);
  346. insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml"));
  347. removePage(1);
  348. insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml"));
  349. insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml"));
  350. insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/Materials/MaterialsPage.qml"));
  351. insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml"));
  352. // Remove plug-ins page because we will use the shiny new plugin browser:
  353. removePage(5);
  354. //Force refresh
  355. setPage(0);
  356. }
  357. onVisibleChanged:
  358. {
  359. // When the dialog closes, switch to the General page.
  360. // This prevents us from having a heavy page like Setting Visiblity active in the background.
  361. setPage(0);
  362. }
  363. }
  364. Connections
  365. {
  366. target: Cura.Actions.preferences
  367. onTriggered: preferences.visible = true
  368. }
  369. Connections
  370. {
  371. target: CuraApplication
  372. onShowPreferencesWindow: preferences.visible = true
  373. }
  374. Connections
  375. {
  376. target: Cura.Actions.addProfile
  377. onTriggered:
  378. {
  379. preferences.show();
  380. preferences.setPage(4);
  381. // Create a new profile after a very short delay so the preference page has time to initiate
  382. createProfileTimer.start();
  383. }
  384. }
  385. Connections
  386. {
  387. target: Cura.Actions.configureMachines
  388. onTriggered:
  389. {
  390. preferences.visible = true;
  391. preferences.setPage(2);
  392. }
  393. }
  394. Connections
  395. {
  396. target: Cura.Actions.manageProfiles
  397. onTriggered:
  398. {
  399. preferences.visible = true;
  400. preferences.setPage(4);
  401. }
  402. }
  403. Connections
  404. {
  405. target: Cura.Actions.manageMaterials
  406. onTriggered:
  407. {
  408. preferences.visible = true;
  409. preferences.setPage(3)
  410. }
  411. }
  412. Connections
  413. {
  414. target: Cura.Actions.configureSettingVisibility
  415. onTriggered:
  416. {
  417. preferences.visible = true;
  418. preferences.setPage(1);
  419. if(source && source.key)
  420. {
  421. preferences.getCurrentItem().scrollToSection(source.key);
  422. }
  423. }
  424. }
  425. Timer
  426. {
  427. id: createProfileTimer
  428. repeat: false
  429. interval: 1
  430. onTriggered: preferences.getCurrentItem().createProfile()
  431. }
  432. // BlurSettings is a way to force the focus away from any of the setting items.
  433. // We need to do this in order to keep the bindings intact.
  434. Connections
  435. {
  436. target: Cura.MachineManager
  437. onBlurSettings:
  438. {
  439. contentItem.forceActiveFocus()
  440. }
  441. }
  442. ContextMenu
  443. {
  444. id: contextMenu
  445. }
  446. onPreClosing:
  447. {
  448. close.accepted = CuraApplication.getIsAllChecksPassed();
  449. if (!close.accepted)
  450. {
  451. CuraApplication.checkAndExitApplication();
  452. }
  453. }
  454. MessageDialog
  455. {
  456. id: exitConfirmationDialog
  457. title: catalog.i18nc("@title:window", "Closing Cura")
  458. text: catalog.i18nc("@label", "Are you sure you want to exit Cura?")
  459. icon: StandardIcon.Question
  460. modality: Qt.ApplicationModal
  461. standardButtons: StandardButton.Yes | StandardButton.No
  462. onYes: CuraApplication.callConfirmExitDialogCallback(true)
  463. onNo: CuraApplication.callConfirmExitDialogCallback(false)
  464. onRejected: CuraApplication.callConfirmExitDialogCallback(false)
  465. onVisibilityChanged:
  466. {
  467. if (!visible)
  468. {
  469. // reset the text to default because other modules may change the message text.
  470. text = catalog.i18nc("@label", "Are you sure you want to exit Cura?");
  471. }
  472. }
  473. }
  474. Connections
  475. {
  476. target: CuraApplication
  477. onShowConfirmExitDialog:
  478. {
  479. exitConfirmationDialog.text = message;
  480. exitConfirmationDialog.open();
  481. }
  482. }
  483. Connections
  484. {
  485. target: Cura.Actions.quit
  486. onTriggered: CuraApplication.checkAndExitApplication();
  487. }
  488. Connections
  489. {
  490. target: Cura.Actions.toggleFullScreen
  491. onTriggered: base.toggleFullscreen();
  492. }
  493. FileDialog
  494. {
  495. id: openDialog;
  496. //: File open dialog title
  497. title: catalog.i18nc("@title:window","Open file(s)")
  498. modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
  499. selectMultiple: true
  500. nameFilters: UM.MeshFileHandler.supportedReadFileTypes;
  501. folder: CuraApplication.getDefaultPath("dialog_load_path")
  502. onAccepted:
  503. {
  504. // Because several implementations of the file dialog only update the folder
  505. // when it is explicitly set.
  506. var f = folder;
  507. folder = f;
  508. CuraApplication.setDefaultPath("dialog_load_path", folder);
  509. handleOpenFileUrls(fileUrls);
  510. }
  511. // Yeah... I know... it is a mess to put all those things here.
  512. // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there,
  513. // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic
  514. // and view here may require more effort but make things more difficult to understand.
  515. function handleOpenFileUrls(fileUrlList)
  516. {
  517. // look for valid project files
  518. var projectFileUrlList = [];
  519. var hasGcode = false;
  520. var nonGcodeFileList = [];
  521. for (var i in fileUrlList)
  522. {
  523. var endsWithG = /\.g$/;
  524. var endsWithGcode = /\.gcode$/;
  525. if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i]))
  526. {
  527. continue;
  528. }
  529. else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i]))
  530. {
  531. projectFileUrlList.push(fileUrlList[i]);
  532. }
  533. nonGcodeFileList.push(fileUrlList[i]);
  534. }
  535. hasGcode = nonGcodeFileList.length < fileUrlList.length;
  536. // show a warning if selected multiple files together with Gcode
  537. var hasProjectFile = projectFileUrlList.length > 0;
  538. var selectedMultipleFiles = fileUrlList.length > 1;
  539. if (selectedMultipleFiles && hasGcode)
  540. {
  541. infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles;
  542. infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile;
  543. infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice();
  544. infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice();
  545. infoMultipleFilesWithGcodeDialog.open();
  546. }
  547. else
  548. {
  549. handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList);
  550. }
  551. }
  552. function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList)
  553. {
  554. // we only allow opening one project file
  555. if (selectedMultipleFiles && hasProjectFile)
  556. {
  557. openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice();
  558. openFilesIncludingProjectsDialog.show();
  559. return;
  560. }
  561. if (hasProjectFile)
  562. {
  563. var projectFile = projectFileUrlList[0];
  564. // check preference
  565. var choice = UM.Preferences.getValue("cura/choice_on_open_project");
  566. if (choice == "open_as_project")
  567. {
  568. openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
  569. }
  570. else if (choice == "open_as_model")
  571. {
  572. openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
  573. }
  574. else // always ask
  575. {
  576. // ask whether to open as project or as models
  577. askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
  578. askOpenAsProjectOrModelsDialog.show();
  579. }
  580. }
  581. else
  582. {
  583. openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice());
  584. }
  585. }
  586. }
  587. MessageDialog
  588. {
  589. id: packageInstallDialog
  590. title: catalog.i18nc("@window:title", "Install Package");
  591. standardButtons: StandardButton.Ok
  592. modality: Qt.ApplicationModal
  593. }
  594. MessageDialog
  595. {
  596. id: infoMultipleFilesWithGcodeDialog
  597. title: catalog.i18nc("@title:window", "Open File(s)")
  598. icon: StandardIcon.Information
  599. standardButtons: StandardButton.Ok
  600. 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.")
  601. property var selectedMultipleFiles
  602. property var hasProjectFile
  603. property var fileUrls
  604. property var projectFileUrlList
  605. onAccepted:
  606. {
  607. openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
  608. }
  609. }
  610. Connections
  611. {
  612. target: Cura.Actions.open
  613. onTriggered: openDialog.open()
  614. }
  615. OpenFilesIncludingProjectsDialog
  616. {
  617. id: openFilesIncludingProjectsDialog
  618. }
  619. AskOpenAsProjectOrModelsDialog
  620. {
  621. id: askOpenAsProjectOrModelsDialog
  622. }
  623. Connections
  624. {
  625. target: CuraApplication
  626. onOpenProjectFile:
  627. {
  628. askOpenAsProjectOrModelsDialog.fileUrl = project_file;
  629. askOpenAsProjectOrModelsDialog.show();
  630. }
  631. }
  632. Connections
  633. {
  634. target: Cura.Actions.showProfileFolder
  635. onTriggered:
  636. {
  637. var path = UM.Resources.getPath(UM.Resources.Preferences, "");
  638. if(Qt.platform.os == "windows") {
  639. path = path.replace(/\\/g,"/");
  640. }
  641. Qt.openUrlExternally(path);
  642. if(Qt.platform.os == "linux") {
  643. Qt.openUrlExternally(UM.Resources.getPath(UM.Resources.Resources, ""));
  644. }
  645. }
  646. }
  647. AddMachineDialog
  648. {
  649. id: addMachineDialog
  650. onMachineAdded:
  651. {
  652. machineActionsWizard.firstRun = addMachineDialog.firstRun
  653. machineActionsWizard.start(id)
  654. }
  655. }
  656. // Dialog to handle first run machine actions
  657. UM.Wizard
  658. {
  659. id: machineActionsWizard;
  660. title: catalog.i18nc("@title:window", "Add Printer")
  661. property var machine;
  662. function start(id)
  663. {
  664. var actions = Cura.MachineActionManager.getFirstStartActions(id)
  665. resetPages() // Remove previous pages
  666. for (var i = 0; i < actions.length; i++)
  667. {
  668. actions[i].displayItem.reset()
  669. machineActionsWizard.appendPage(actions[i].displayItem, catalog.i18nc("@title", actions[i].label));
  670. }
  671. //Only start if there are actions to perform.
  672. if (actions.length > 0)
  673. {
  674. machineActionsWizard.currentPage = 0;
  675. show()
  676. }
  677. }
  678. }
  679. MessageDialog
  680. {
  681. id: messageDialog
  682. modality: Qt.ApplicationModal
  683. onAccepted: CuraApplication.messageBoxClosed(clickedButton)
  684. onApply: CuraApplication.messageBoxClosed(clickedButton)
  685. onDiscard: CuraApplication.messageBoxClosed(clickedButton)
  686. onHelp: CuraApplication.messageBoxClosed(clickedButton)
  687. onNo: CuraApplication.messageBoxClosed(clickedButton)
  688. onRejected: CuraApplication.messageBoxClosed(clickedButton)
  689. onReset: CuraApplication.messageBoxClosed(clickedButton)
  690. onYes: CuraApplication.messageBoxClosed(clickedButton)
  691. }
  692. Connections
  693. {
  694. target: CuraApplication
  695. onShowMessageBox:
  696. {
  697. messageDialog.title = title
  698. messageDialog.text = text
  699. messageDialog.informativeText = informativeText
  700. messageDialog.detailedText = detailedText
  701. messageDialog.standardButtons = buttons
  702. messageDialog.icon = icon
  703. messageDialog.visible = true
  704. }
  705. }
  706. DiscardOrKeepProfileChangesDialog
  707. {
  708. id: discardOrKeepProfileChangesDialog
  709. }
  710. Connections
  711. {
  712. target: CuraApplication
  713. onShowDiscardOrKeepProfileChanges:
  714. {
  715. discardOrKeepProfileChangesDialog.show()
  716. }
  717. }
  718. Connections
  719. {
  720. target: Cura.Actions.addMachine
  721. onTriggered: addMachineDialog.visible = true;
  722. }
  723. AboutDialog
  724. {
  725. id: aboutDialog
  726. }
  727. Connections
  728. {
  729. target: Cura.Actions.about
  730. onTriggered: aboutDialog.visible = true;
  731. }
  732. Connections
  733. {
  734. target: CuraApplication
  735. onRequestAddPrinter:
  736. {
  737. addMachineDialog.visible = true
  738. addMachineDialog.firstRun = false
  739. }
  740. }
  741. Timer
  742. {
  743. id: startupTimer;
  744. interval: 100;
  745. repeat: false;
  746. running: true;
  747. onTriggered:
  748. {
  749. if(!base.visible)
  750. {
  751. base.visible = true;
  752. }
  753. // check later if the user agreement dialog has been closed
  754. if (CuraApplication.needToShowUserAgreement)
  755. {
  756. restart();
  757. }
  758. else if(Cura.MachineManager.activeMachine == null)
  759. {
  760. addMachineDialog.open();
  761. }
  762. }
  763. }
  764. /**
  765. * Function to check whether a QML object has a certain type.
  766. * Taken from StackOverflow: https://stackoverflow.com/a/28384228 and
  767. * adapted to our code style.
  768. * Licensed under CC BY-SA 3.0.
  769. * \param obj The QtObject to get the name of.
  770. * \param class_name (str) The name of the class to check against. Has to be
  771. * the QtObject class name, not the QML entity name.
  772. */
  773. function qmlTypeOf(obj, class_name)
  774. {
  775. //className plus "(" is the class instance without modification.
  776. //className plus "_QML" is the class instance with user-defined properties.
  777. var str = obj.toString();
  778. return str.indexOf(class_name + "(") == 0 || str.indexOf(class_name + "_QML") == 0;
  779. }
  780. }