Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 1 | |
| 2 | /* |
| 3 | * Copyright (C) 2020 by Savoir-faire Linux |
| 4 | * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 3 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 18 | */ |
| 19 | import QtQuick 2.14 |
| 20 | import QtQuick.Window 2.14 |
| 21 | import QtQuick.Controls 2.14 |
| 22 | import QtQuick.Layouts 1.14 |
| 23 | import QtQuick.Controls.Universal 2.12 |
| 24 | import QtGraphicalEffects 1.14 |
| 25 | import net.jami.Models 1.0 |
| 26 | |
| 27 | |
| 28 | /* |
| 29 | * Import qml component files. |
| 30 | */ |
| 31 | import "components" |
| 32 | import "../settingsview" |
| 33 | |
| 34 | Window { |
| 35 | id: mainViewWindow |
| 36 | |
| 37 | property int minWidth: sidePanelViewStackPreferedWidth |
| 38 | property int minHeight: aboutPopUpDialog.contentHeight |
| 39 | |
| 40 | property int mainViewWindowPreferedWidth: 650 |
| 41 | property int mainViewWindowPreferedHeight: 600 |
| 42 | property int sidePanelViewStackPreferedWidth: 250 |
| 43 | property int welcomePageGroupPreferedWidth: 250 |
| 44 | property int aboutPopUpPreferedWidth: 250 |
| 45 | |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 46 | property int savedSidePanelViewMinWidth: 0 |
| 47 | property int savedSidePanelViewMaxWidth: 0 |
| 48 | property int savedWelcomeViewMinWidth: 0 |
| 49 | property int savedWelcomeViewMaxWidth: 0 |
| 50 | property bool hiddenView: false |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 51 | |
| 52 | /* |
| 53 | * To calculate tab bar bottom border hidden rect left margin. |
| 54 | */ |
| 55 | property int tabBarLeftMargin: 8 |
| 56 | property int tabButtonShrinkSize: 8 |
| 57 | |
| 58 | signal noAccountIsAvailable |
| 59 | signal needToAddNewAccount |
| 60 | signal closeApp |
| 61 | |
| 62 | function newAccountAdded(index) { |
| 63 | mainViewWindowSidePanel.refreshAccountComboBox(index) |
| 64 | } |
| 65 | |
| 66 | function recursionStackViewItemMove(stackOne, stackTwo) { |
| 67 | |
| 68 | |
| 69 | /* |
| 70 | * Move all items (expect the bottom item) to stacktwo by the same order in stackone. |
| 71 | */ |
| 72 | if (stackOne.depth === 1) { |
| 73 | return |
| 74 | } |
| 75 | |
| 76 | var tempItem = stackOne.pop(StackView.Immediate) |
| 77 | recursionStackViewItemMove(stackOne, stackTwo) |
| 78 | stackTwo.push(tempItem, StackView.Immediate) |
| 79 | } |
| 80 | |
| 81 | title: "Jami" |
| 82 | visible: true |
| 83 | width: mainViewWindowPreferedWidth |
| 84 | height: mainViewWindowPreferedHeight |
| 85 | minimumWidth: minWidth |
| 86 | minimumHeight: minHeight |
| 87 | |
| 88 | Connections { |
| 89 | target: CallAdapter |
| 90 | |
| 91 | function onShowCallStack(accountId, convUid, forceReset) { |
| 92 | if (forceReset) { |
| 93 | callStackView.responsibleAccountId = accountId |
| 94 | callStackView.responsibleConvUid = convUid |
| 95 | } |
| 96 | |
| 97 | |
| 98 | /* |
| 99 | * Check if it is coming from the current responsible call, |
| 100 | * and push views onto the correct stackview |
| 101 | */ |
| 102 | if (callStackView.responsibleAccountId === accountId |
| 103 | && callStackView.responsibleConvUid === convUid) { |
| 104 | if (welcomeViewStack.visible) { |
| 105 | welcomeViewStack.pop(null, StackView.Immediate) |
| 106 | welcomeViewStack.push(callStackView, StackView.Immediate) |
| 107 | } else { |
| 108 | sidePanelViewStack.pop(null, StackView.Immediate) |
| 109 | sidePanelViewStack.push(callStackView, StackView.Immediate) |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | function onCloseCallStack(accountId, convUid) { |
| 115 | |
Sébastien Blin | 8940f3c | 2020-07-23 17:03:11 -0400 | [diff] [blame] | 116 | var responsibleCallId = ClientWrapper.utilsAdaptor.getCallId( |
| 117 | callStackView.responsibleAccountId, callStackView.responsibleConvUid) |
| 118 | var callId = ClientWrapper.utilsAdaptor.getCallId( |
| 119 | callStackView.responsibleAccountId, convUid) |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 120 | /* |
| 121 | * Check if call stack view is on any of the stackview. |
| 122 | */ |
Sébastien Blin | 8940f3c | 2020-07-23 17:03:11 -0400 | [diff] [blame] | 123 | if (responsibleCallId === callId || responsibleCallId.length === 0) { |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 124 | if (welcomeViewStack.find(function (item, index) { |
| 125 | return item.objectName === "callStackViewObject" |
| 126 | }) || sidePanelViewStack.find(function (item, index) { |
| 127 | return item.objectName === "callStackViewObject" |
| 128 | })) { |
| 129 | callStackView.needToCloseInCallConversationAndPotentialWindow() |
| 130 | if (welcomeViewStack.visible) { |
| 131 | welcomeViewStack.pop(null, StackView.Immediate) |
| 132 | welcomeViewStack.push(communicationPageMessageWebView, |
| 133 | StackView.Immediate) |
| 134 | } else { |
| 135 | sidePanelViewStack.pop(null, StackView.Immediate) |
| 136 | sidePanelViewStack.push( |
| 137 | communicationPageMessageWebView, |
| 138 | StackView.Immediate) |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | function onIncomingCallNeedToSetupMainView(accountId, convUid) { |
| 145 | |
| 146 | |
| 147 | /* |
| 148 | * Set up the call stack view that is needed by call overlay. |
| 149 | */ |
| 150 | welcomeViewStack.pop(null, StackView.Immediate) |
| 151 | sidePanelViewStack.pop(null, StackView.Immediate) |
| 152 | |
| 153 | var index = ClientWrapper.utilsAdaptor.getCurrAccList().indexOf(accountId) |
| 154 | var name = ClientWrapper.utilsAdaptor.getBestName(accountId, convUid) |
| 155 | var id = ClientWrapper.utilsAdaptor.getBestId(accountId, convUid) |
| 156 | |
| 157 | communicationPageMessageWebView.headerUserAliasLabelText = name |
| 158 | communicationPageMessageWebView.headerUserUserNameLabelText = (name !== id) ? id : "" |
| 159 | |
| 160 | callStackView.needToCloseInCallConversationAndPotentialWindow() |
Sébastien Blin | 8940f3c | 2020-07-23 17:03:11 -0400 | [diff] [blame] | 161 | callStackView.setLinkedWebview( |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 162 | communicationPageMessageWebView) |
| 163 | |
| 164 | callStackView.responsibleAccountId = accountId |
| 165 | callStackView.responsibleConvUid = convUid |
| 166 | callStackView.updateCorrspondingUI() |
| 167 | |
| 168 | mainViewWindowSidePanel.needToChangeToAccount(accountId, index) |
| 169 | ConversationsAdapter.selectConversation(accountId, convUid) |
| 170 | |
| 171 | MessagesAdapter.setupChatView(convUid) |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | StackLayout { |
| 176 | id: mainViewStackLayout |
| 177 | |
| 178 | anchors.fill: parent |
| 179 | |
| 180 | currentIndex: 0 |
| 181 | |
| 182 | SplitView { |
| 183 | id: splitView |
| 184 | |
| 185 | Layout.fillWidth: true |
| 186 | Layout.fillHeight: true |
| 187 | |
| 188 | width: mainViewWindow.width |
| 189 | height: mainViewWindow.height |
| 190 | |
| 191 | handle: Rectangle { |
| 192 | implicitWidth: JamiTheme.splitViewHandlePreferedWidth |
| 193 | implicitHeight: splitView.height |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 194 | color:"transparent" |
| 195 | Rectangle { |
| 196 | implicitWidth: 1 |
| 197 | implicitHeight: splitView.height |
| 198 | color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor) |
| 199 | } |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | StackView { |
| 203 | id: sidePanelViewStack |
| 204 | |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 205 | initialItem: mainViewWindowSidePanel |
| 206 | |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 207 | SplitView.maximumWidth: splitView.width - sidePanelViewStackPreferedWidth |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 208 | SplitView.minimumWidth: sidePanelViewStackPreferedWidth |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 209 | SplitView.fillHeight: true |
| 210 | |
| 211 | clip: true |
| 212 | } |
| 213 | |
| 214 | StackView { |
| 215 | id: welcomeViewStack |
| 216 | |
| 217 | initialItem: welcomePage |
| 218 | |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 219 | SplitView.maximumWidth: hiddenView ? splitView.width : splitView.width - sidePanelViewStackPreferedWidth |
| 220 | SplitView.minimumWidth: sidePanelViewStackPreferedWidth |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 221 | SplitView.fillHeight: true |
| 222 | |
| 223 | clip: true |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | SettingsView { |
| 228 | id: settingsView |
| 229 | |
| 230 | Layout.fillWidth: true |
| 231 | Layout.fillHeight: true |
| 232 | |
| 233 | onSettingsViewWindowNeedToShowMainViewWindow: { |
| 234 | mainViewWindowSidePanel.refreshAccountComboBox( |
| 235 | accountDeleted ? 0 : -1) |
| 236 | mainViewStackLayout.currentIndex = 0 |
| 237 | } |
| 238 | |
| 239 | onSettingsViewWindowNeedToShowNewWizardWindow: { |
| 240 | mainViewWindow.noAccountIsAvailable() |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | AccountListModel { |
| 246 | id: accountListModel |
| 247 | } |
| 248 | |
| 249 | SidePanel { |
| 250 | id: mainViewWindowSidePanel |
| 251 | |
| 252 | onSettingBtnClicked_AccountComboBox: { |
| 253 | mainViewStackLayout.currentIndex = 1 |
| 254 | } |
| 255 | |
| 256 | onConversationSmartListNeedToAccessMessageWebView: { |
| 257 | |
| 258 | communicationPageMessageWebView.headerUserAliasLabelText = currentUserAlias |
| 259 | communicationPageMessageWebView.headerUserUserNameLabelText = currentUserDisplayName |
| 260 | |
| 261 | callStackView.needToCloseInCallConversationAndPotentialWindow() |
| 262 | callStackView.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId() |
| 263 | callStackView.responsibleConvUid = currentUID |
| 264 | callStackView.updateCorrspondingUI() |
| 265 | |
| 266 | if (callStackViewShouldShow) { |
| 267 | if (callStateStr == "Talking" || callStateStr == "Hold") { |
| 268 | ClientWrapper.utilsAdaptor.setCurrentCall( |
| 269 | ClientWrapper.utilsAdaptor.getCurrAccId(), |
| 270 | currentUID) |
| 271 | if (isAudioOnly) |
| 272 | callStackView.showAudioCallPage() |
| 273 | else |
| 274 | callStackView.showVideoCallPage( |
| 275 | ClientWrapper.utilsAdaptor.getCallId( |
| 276 | callStackView.responsibleAccountId, |
| 277 | callStackView.responsibleConvUid)) |
| 278 | } else { |
| 279 | callStackView.showOutgoingCallPage(callStateStr) |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | |
| 284 | /* |
| 285 | * Set up chatview. |
| 286 | */ |
| 287 | MessagesAdapter.setupChatView(currentUID) |
Sébastien Blin | 8940f3c | 2020-07-23 17:03:11 -0400 | [diff] [blame] | 288 | callStackView.setLinkedWebview( |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 289 | communicationPageMessageWebView) |
| 290 | |
| 291 | if (welcomeViewStack.find(function (item, index) { |
| 292 | return item.objectName === "communicationPageMessageWebView" |
| 293 | }) || sidePanelViewStack.find(function (item, index) { |
| 294 | return item.objectName === "communicationPageMessageWebView" |
| 295 | })) { |
| 296 | if (!callStackViewShouldShow) |
| 297 | return |
| 298 | } |
| 299 | |
| 300 | |
| 301 | /* |
| 302 | * Push messageWebView or callStackView onto the correct stackview |
| 303 | */ |
| 304 | welcomeViewStack.pop(null, StackView.Immediate) |
| 305 | sidePanelViewStack.pop(null, StackView.Immediate) |
| 306 | |
| 307 | if (sidePanelViewStack.visible && welcomeViewStack.visible) { |
| 308 | if (callStackViewShouldShow) { |
| 309 | welcomeViewStack.push(callStackView) |
| 310 | } else { |
| 311 | welcomeViewStack.push(communicationPageMessageWebView) |
| 312 | } |
| 313 | } else if (sidePanelViewStack.visible |
| 314 | && !welcomeViewStack.visible) { |
| 315 | if (callStackViewShouldShow) { |
| 316 | sidePanelViewStack.push(callStackView) |
| 317 | } else { |
| 318 | sidePanelViewStack.push(communicationPageMessageWebView) |
| 319 | } |
| 320 | } else if (!sidePanelViewStack.visible |
| 321 | && !welcomeViewStack.visible) { |
| 322 | if (callStackViewShouldShow) { |
| 323 | sidePanelViewStack.push(callStackView) |
| 324 | } else { |
| 325 | sidePanelViewStack.push(communicationPageMessageWebView) |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | onAccountComboBoxNeedToShowWelcomePage: { |
| 331 | |
| 332 | |
| 333 | /* |
| 334 | * If the item argument is specified, all items down to (but not including) item will be popped. |
| 335 | */ |
| 336 | welcomeViewStack.pop(welcomePage) |
| 337 | welcomePage.currentAccountIndex = index |
| 338 | qrDialog.currentAccountIndex = index |
| 339 | } |
| 340 | |
| 341 | onConversationSmartListViewNeedToShowWelcomePage: { |
| 342 | welcomeViewStack.pop(welcomePage) |
| 343 | welcomePage.currentAccountIndex = 0 |
| 344 | qrDialog.currentAccountIndex = 0 |
| 345 | } |
| 346 | |
| 347 | onAccountSignalsReconnect: { |
| 348 | MessagesAdapter.accountChangedSetUp(accountId) |
| 349 | } |
| 350 | |
| 351 | onNeedToUpdateConversationForAddedContact: { |
| 352 | MessagesAdapter.updateConversationForAddedContact() |
| 353 | mainViewWindowSidePanel.clearContactSearchBar() |
| 354 | mainViewWindowSidePanel.forceReselectConversationSmartListCurrentIndex() |
| 355 | } |
| 356 | |
| 357 | onNeedToAddNewAccount: { |
| 358 | mainViewWindow.needToAddNewAccount() |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | CallStackView { |
| 363 | id: callStackView |
| 364 | |
| 365 | visible: false |
| 366 | |
| 367 | objectName: "callStackViewObject" |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | WelcomePage { |
| 371 | id: welcomePage |
| 372 | visible: false |
| 373 | } |
| 374 | |
| 375 | MessageWebView { |
| 376 | id: communicationPageMessageWebView |
| 377 | |
| 378 | objectName: "communicationPageMessageWebView" |
| 379 | |
| 380 | signal toSendMessageContentSaved(string arg) |
| 381 | signal toMessagesCleared |
| 382 | signal toMessagesLoaded |
| 383 | |
| 384 | visible: false |
| 385 | |
| 386 | Connections { |
| 387 | target: MessagesAdapter |
| 388 | |
| 389 | function onNeedToUpdateSmartList() { |
| 390 | mainViewWindowSidePanel.forceUpdateConversationSmartListView() |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | onNeedToGoBackToWelcomeView: { |
| 395 | mainViewWindowSidePanel.deselectConversationSmartList() |
| 396 | if (communicationPageMessageWebView.visible |
| 397 | && !welcomeViewStack.visible) { |
| 398 | sidePanelViewStack.pop() |
| 399 | } else if (communicationPageMessageWebView.visible |
| 400 | && welcomeViewStack.visible) { |
| 401 | welcomeViewStack.pop() |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | Component.onCompleted: { |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 406 | sidePanelViewStack.SplitView.maximumWidth = Qt.binding(function() { |
| 407 | return (hiddenView ? splitView.width : splitView.width - sidePanelViewStackPreferedWidth) |
| 408 | }) |
| 409 | |
| 410 | recordBox.x = Qt.binding(function() { |
| 411 | var i = (welcomeViewStack.width > 1000 ? Math.round((welcomeViewStack.width-1000)*0.5) : 0) |
| 412 | return sidePanelViewStack.width + recordBox.x_offset + i |
| 413 | }) |
| 414 | |
| 415 | recordBox.y = Qt.binding(function() { |
| 416 | return sidePanelViewStack.height + recordBox.y_offset |
| 417 | }) |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 418 | |
| 419 | |
| 420 | /* |
| 421 | * Set qml MessageWebView object pointer to c++. |
| 422 | */ |
| 423 | MessagesAdapter.setQmlObject(this) |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | onWidthChanged: { |
| 428 | |
| 429 | |
| 430 | /* |
| 431 | * Hide unnecessary stackview when width is changed. |
| 432 | */ |
| 433 | if (mainViewWindow.width < sidePanelViewStackPreferedWidth |
| 434 | + welcomePageGroupPreferedWidth - 5 |
| 435 | && welcomeViewStack.visible) { |
| 436 | welcomeViewStack.visible = false |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 437 | hiddenView = true |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 438 | |
| 439 | /* |
| 440 | * The find callback function is called for each item in the stack. |
| 441 | */ |
| 442 | var inWelcomeViewStack = welcomeViewStack.find( |
| 443 | function (item, index) { |
| 444 | return index > 0 |
| 445 | }) |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 446 | |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 447 | if (inWelcomeViewStack) { |
| 448 | recursionStackViewItemMove(welcomeViewStack, sidePanelViewStack) |
| 449 | } |
| 450 | |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 451 | mainViewWindow.update() |
| 452 | } else if (mainViewWindow.width >= sidePanelViewStackPreferedWidth |
| 453 | + welcomePageGroupPreferedWidth + 5 |
| 454 | && !welcomeViewStack.visible) { |
| 455 | welcomeViewStack.visible = true |
ababi | df651a2 | 2020-07-30 13:38:57 +0200 | [diff] [blame] | 456 | hiddenView = false |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 457 | |
| 458 | var inSidePanelViewStack = sidePanelViewStack.find( |
| 459 | function (item, index) { |
| 460 | return index > 0 |
| 461 | }) |
| 462 | if (inSidePanelViewStack) { |
| 463 | recursionStackViewItemMove(sidePanelViewStack, welcomeViewStack) |
| 464 | } |
| 465 | |
Sébastien Blin | 1f91576 | 2020-08-03 13:27:42 -0400 | [diff] [blame] | 466 | mainViewWindow.update() |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | AboutPopUp { |
| 471 | id: aboutPopUpDialog |
| 472 | |
| 473 | x: Math.round((mainViewWindow.width - width) / 2) |
| 474 | y: Math.round((mainViewWindow.height - height) / 2) |
| 475 | width: Math.max(mainViewWindow.width / 2, aboutPopUpPreferedWidth) |
| 476 | height: aboutPopUpDialog.contentHeight |
| 477 | } |
| 478 | |
| 479 | WelcomePageQrDialog { |
| 480 | id: qrDialog |
| 481 | |
| 482 | x: Math.round((mainViewWindow.width - width) / 2) |
| 483 | y: Math.round((mainViewWindow.height - height) / 2) |
| 484 | width: qrDialog.contentHeight |
| 485 | height: qrDialog.contentHeight |
| 486 | } |
| 487 | |
| 488 | RecordBox{ |
| 489 | id: recordBox |
| 490 | visible: false |
| 491 | } |
| 492 | |
| 493 | UserProfile { |
| 494 | id: userProfile |
| 495 | |
| 496 | x: Math.round((mainViewWindow.width - width) / 2) |
| 497 | y: Math.round((mainViewWindow.height - height) / 2) |
| 498 | width: Math.max(mainViewWindow.width / 2, aboutPopUpPreferedWidth) |
| 499 | height: userProfile.contentHeight |
| 500 | } |
| 501 | |
| 502 | Component.onCompleted: { |
| 503 | CallAdapter.initQmlObject() |
| 504 | } |
| 505 | |
| 506 | onClosing: { |
| 507 | close.accepted = false |
| 508 | mainViewWindow.hide() |
| 509 | mainViewWindow.closeApp() |
| 510 | } |
| 511 | } |