PlanView.qml 34.8 KB
Newer Older
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
Don Gagne's avatar
Don Gagne committed
9 10


11 12
import QtQuick          2.3
import QtQuick.Controls 1.2
Don Gagne's avatar
Don Gagne committed
13
import QtQuick.Dialogs  1.2
14 15 16
import QtLocation       5.3
import QtPositioning    5.3
import QtQuick.Layouts  1.2
17
import QtQuick.Window   2.2
Don Gagne's avatar
Don Gagne committed
18

19
import QGroundControl               1.0
Don Gagne's avatar
Don Gagne committed
20 21 22
import QGroundControl.FlightMap     1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0
23
import QGroundControl.FactSystem    1.0
24
import QGroundControl.FactControls  1.0
Don Gagne's avatar
Don Gagne committed
25
import QGroundControl.Palette       1.0
26
import QGroundControl.Controllers   1.0
Don Gagne's avatar
Don Gagne committed
27 28

/// Mission Editor
Don Gagne's avatar
Don Gagne committed
29

Don Gagne's avatar
Don Gagne committed
30
QGCView {
31
    id:         _qgcView
32
    viewPanel:  panel
33
    z:          QGroundControl.zOrderTopMost
34

35 36 37 38 39 40 41 42
    readonly property int   _decimalPlaces:             8
    readonly property real  _horizontalMargin:          ScreenTools.defaultFontPixelWidth  / 2
    readonly property real  _margin:                    ScreenTools.defaultFontPixelHeight * 0.5
    readonly property var   _activeVehicle:             QGroundControl.multiVehicleManager.activeVehicle
    readonly property real  _rightPanelWidth:           Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30)
    readonly property real  _toolButtonTopMargin:       parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2)
    readonly property var   _defaultVehicleCoordinate:  QtPositioning.coordinate(37.803784, -122.462276)
    readonly property bool  _waypointsOnlyMode:         QGroundControl.corePlugin.options.missionWaypointsOnly
43

44 45 46 47 48
    property var    _planMasterController:      masterController
    property var    _missionController:         _planMasterController.missionController
    property var    _geoFenceController:        _planMasterController.geoFenceController
    property var    _rallyPointController:      _planMasterController.rallyPointController
    property var    _visualItems:               _missionController.visualItems
49 50
    property bool   _lightWidgetBorders:        editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:        false
51
    property bool   _addROIOnClick:             false
52
    property bool   _singleComplexItem:         _missionController.complexMissionItemNames.length === 1
53 54
    property real   _toolbarHeight:             _qgcView.height - ScreenTools.availableHeight
    property int    _editingLayer:              _layerMission
55
    property int    _toolStripBottom:           toolStrip.height + toolStrip.y
56

57 58 59 60
    readonly property int       _layerMission:              1
    readonly property int       _layerGeoFence:             2
    readonly property int       _layerRallyPoints:          3
    readonly property string    _armedVehicleUploadPrompt:  qsTr("Vehicle is currently armed. Do you want to upload the mission to the vehicle?")
61

62
    Component.onCompleted: {
63
        toolbar.planMasterController =  Qt.binding(function () { return _planMasterController })
64
        toolbar.currentMissionItem =    Qt.binding(function () { return _missionController.currentPlanViewItem })
65 66
    }

67 68 69 70 71
    function addComplexItem(complexItemName) {
        var coordinate = editorMap.center
        coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
        coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
72
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
73 74 75
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
76
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
77
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
78 79
    }

80 81 82 83 84
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

85
    MapFitFunctions {
86
        id:                         mapFitFunctions  // The name for this id cannot be changed without breaking references outside of this code. Beware!
87 88
        map:                        editorMap
        usePlannedHomePosition:     true
89
        planMasterController:       _planMasterController
90 91
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
    Connections {
        target: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude

        onRawValueChanged: {
            if (_visualItems.count > 1) {
                _qgcView.showDialog(applyNewAltitude, qsTr("Apply new alititude"), showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
            }
        }
    }

    Component {
        id: applyNewAltitude

        QGCViewMessage {
            message:    qsTr("You have changed the default altitude for mission items. Would you like to apply that altitude to all the items in the current mission?")

            function accept() {
                hideDialog()
110
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
111 112 113 114
            }
        }
    }

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    Component {
        id: activeMissionUploadDialogComponent

        QGCViewDialog {

            Column {
                anchors.fill:   parent
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("Your vehicle is currently flying a mission. In order to upload a new or modified mission the current mission will be paused.")
                }

                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("After the mission is uploaded you can adjust the current waypoint and start the mission.")
                }

                QGCButton {
                    text:       qsTr("Pause and Upload")
                    onClicked: {
                        _activeVehicle.flightMode = _activeVehicle.pauseFlightMode
140
                        _planMasterController.sendToVehicle()
141
                        hideDialog()
142 143 144 145 146 147
                    }
                }
            }
        }
    }

148 149 150 151 152 153 154 155
    Component {
        id: noItemForKML

        QGCViewMessage {
            message:    qsTr("You need at least one item to create a KML.")
        }
    }

156
    PlanMasterController {
157
        id: masterController
158

159 160
        Component.onCompleted: {
            start(true /* editMode */)
161
            _missionController.setCurrentPlanViewIndex(0, true)
162 163
        }

164 165 166 167
        function waitingOnDataMessage() {
            _qgcView.showMessage(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."), StandardButton.Ok)
        }

168
        function upload() {
169 170 171 172
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
173
            if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
174
                _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
175
            } else {
176 177
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
178 179
        }

180
        function loadFromSelectedFile() {
181
            fileDialog.title =          qsTr("Select Plan File")
182
            fileDialog.selectExisting = true
183
            fileDialog.nameFilters =    masterController.loadNameFilters
184 185
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
186
            fileDialog.openForLoad()
187 188 189
        }

        function saveToSelectedFile() {
190 191 192 193
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
194
            fileDialog.title =          qsTr("Save Plan")
195
            fileDialog.plan =           true
196
            fileDialog.selectExisting = false
197
            fileDialog.nameFilters =    masterController.saveNameFilters
198 199
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
200
            fileDialog.openForSave()
201 202
        }

203
        function fitViewportToItems() {
204
            mapFitFunctions.fitMapViewportToMissionItems()
205
        }
206 207

        function saveKmlToSelectedFile() {
208 209 210 211
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
212 213 214 215
            fileDialog.title =          qsTr("Save KML")
            fileDialog.plan =           false
            fileDialog.selectExisting = false
            fileDialog.nameFilters =    masterController.saveKmlFilters
216 217
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.kmlFileExtension
            fileDialog.fileExtension2 = ""
218 219
            fileDialog.openForSave()
        }
220
    }
221

222 223
    Connections {
        target: _missionController
224

225
        onNewItemsFromVehicle: {
226 227 228
            if (_visualItems && _visualItems.count != 1) {
                mapFitFunctions.fitMapViewportToMissionItems()
            }
229
            _missionController.setCurrentPlanViewIndex(0, true)
230 231
        }
    }
232

233
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
234

235 236 237 238
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

239 240 241 242
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
243
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
244
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
245 246
    }

247 248 249 250 251 252 253 254 255 256
    /// Inserts a new ROI mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertROIMissionItem(coordinate, index) {
        var sequenceNumber = _missionController.insertROIMissionItem(coordinate, index)
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
        _addROIOnClick = false
        toolStrip.uncheckAll()
    }

257 258
    property int _moveDialogMissionItemIndex

259 260 261
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
262
        property bool plan: true
263
        folder:         QGroundControl.settingsManager.appSettings.missionSavePath
264

265
        onAcceptedForSave: {
266
            plan ? masterController.saveToFile(file) : masterController.saveToKml(file)
267
            close()
268 269
        }

270
        onAcceptedForLoad: {
271 272
            masterController.loadFromFile(file)
            masterController.fitViewportToItems()
273
            _missionController.setCurrentPlanViewIndex(0, true)
274
            close()
275 276 277
        }
    }

278 279 280 281 282 283 284
    Component {
        id: moveDialog

        QGCViewDialog {
            function accept() {
                var toIndex = toCombo.currentIndex

285
                if (toIndex === 0) {
286 287
                    toIndex = 1
                }
288
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
289 290 291 292 293 294 295 296 297 298 299 300
                hideDialog()
            }

            Column {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    wrapMode:       Text.WordWrap
301
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
302 303 304 305
                }

                QGCComboBox {
                    id:             toCombo
306
                    model:          _visualItems.count
307 308 309 310 311 312
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
313 314
    QGCViewPanel {
        id:             panel
315
        anchors.fill:   parent
Don Gagne's avatar
Don Gagne committed
316

317
        FlightMap {
318 319 320 321 322
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
323
            planView:                   true
Don Gagne's avatar
Don Gagne committed
324

325 326
            // This is the center rectangle of the map which is not obscured by tools
            property rect centerViewport: Qt.rect(_leftToolWidth, _toolbarHeight, editorMap.width - _leftToolWidth - _rightPanelWidth, editorMap.height - _statusHeight - _toolbarHeight)
327

328 329
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
330

331
            readonly property real animationDuration: 500
332

333 334
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
335

336 337 338 339 340 341
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
342

343 344 345 346 347 348 349
            QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

            MouseArea {
                //-- It's a whole lot faster to just fill parent and deal with top offset below
                //   than computing the coordinate offset.
                anchors.fill: parent
                onClicked: {
350 351 352
                    // Take focus to close any previous editing
                    editorMap.focus = true

353 354 355 356
                    //-- Don't pay attention to items beneath the toolbar.
                    var topLimit = parent.height - ScreenTools.availableHeight
                    if(mouse.y < topLimit) {
                        return
357 358
                    }

359 360 361 362
                    var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
                    coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                    coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                    coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
363

364 365 366
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
367
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
368 369 370
                        } else if (_addROIOnClick) {
                            _addROIOnClick = false
                            insertROIMissionItem(coordinate, _missionController.visualItems.count)
371
                        }
372 373
                        break
                    case _layerRallyPoints:
374
                        if (_rallyPointController.supported) {
375
                            _rallyPointController.addPoint(coordinate)
376
                        }
377
                        break
Don Gagne's avatar
Don Gagne committed
378
                    }
Don Gagne's avatar
Don Gagne committed
379
                }
380
            }
Don Gagne's avatar
Don Gagne committed
381

382 383
            // Add the mission item visuals to the map
            Repeater {
384
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
385

386 387
                delegate: MissionItemMapVisual {
                    map:        editorMap
DonLakeFlyer's avatar
DonLakeFlyer committed
388
                    qgcView:    _qgcView
389
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
390
                    visible:    _editingLayer == _layerMission
391
                }
392
            }
393

394 395
            // Add lines between waypoints
            MissionLineView {
396
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
397
            }
398

399 400 401 402 403 404 405
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
406
                    map:            editorMap
407 408
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
409
                }
410
            }
411

412 413
            GeoFenceMapVisuals {
                map:                    editorMap
414
                myGeoFenceController:   _geoFenceController
415
                interactive:            _editingLayer == _layerGeoFence
416
                homePosition:           _missionController.plannedHomePosition
417 418
                planView:               true
            }
419

420 421
            RallyPointMapVisuals {
                map:                    editorMap
422
                myRallyPointController: _rallyPointController
423 424
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
425
            }
426

427 428 429 430 431 432 433 434 435
            ToolStrip {
                id:                 toolStrip
                anchors.leftMargin: ScreenTools.defaultFontPixelWidth
                anchors.left:       parent.left
                anchors.topMargin:  _toolButtonTopMargin
                anchors.top:        parent.top
                color:              qgcPal.window
                title:              qsTr("Plan")
                z:                  QGroundControl.zOrderWidgets
436 437 438 439 440
                showAlternateIcon:  [ false, false, false, masterController.dirty, false, false, false ]
                rotateImage:        [ false, false, false, masterController.syncInProgress, false, false, false ]
                animateImage:       [ false, false, false, masterController.dirty, false, false, false ]
                buttonEnabled:      [ true, true, true, !masterController.syncInProgress, true, true, true ]
                buttonVisible:      [ true, _waypointsOnlyMode, true, true, true, _showZoom, _showZoom ]
441 442 443 444 445 446 447 448 449 450
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
                        name:       "Waypoint",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
451 452 453 454 455
                    {
                        name:       "ROI",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
456
                    {
457
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : "Pattern",
458 459 460
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                    },
461 462 463 464 465 466
                    {
                        name:                   "Sync",
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
                    },
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
                    {
                        name:               "Center",
                        iconSource:         "/qmlimages/MapCenter.svg",
                        dropPanelComponent: centerMapDropPanel
                    },
                    {
                        name:               "In",
                        iconSource:         "/qmlimages/ZoomPlus.svg"
                    },
                    {
                        name:               "Out",
                        iconSource:         "/qmlimages/ZoomMinus.svg"
                    }
                ]

                onClicked: {
                    switch (index) {
                    case 0:
                        _addWaypointOnClick = checked
486
                        _addROIOnClick = false
487 488
                        break
                    case 1:
489 490 491 492
                        _addROIOnClick = checked
                        _addWaypointOnClick = false
                        break
                    case 2:
493
                        if (_singleComplexItem) {
494
                            addComplexItem(_missionController.complexMissionItemNames[0])
495
                        }
496
                        break
497
                    case 5:
498 499
                        editorMap.zoomLevel += 0.5
                        break
500
                    case 6:
501 502 503 504 505 506 507 508 509 510 511 512
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
        } // FlightMap

        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
513
            height:             ScreenTools.availableHeight
514 515
            width:              _rightPanelWidth
            color:              qgcPal.window
516 517 518 519 520
            opacity:            0.2
        }

        Item {
            anchors.fill:   rightPanel
521 522 523 524

            // Plan Element selector (Mission/Fence/Rally)
            Row {
                id:                 planElementSelectorRow
525
                anchors.topMargin:  Math.round(ScreenTools.defaultFontPixelHeight / 3)
526 527 528 529
                anchors.top:        parent.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                spacing:            _horizontalMargin
530
                visible:            QGroundControl.corePlugin.options.enablePlanViewSelector
531 532 533 534 535 536 537 538 539

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

                ExclusiveGroup {
                    id: planElementSelectorGroup
                    onCurrentChanged: {
                        switch (current) {
                        case planElementMission:
                            _editingLayer = _layerMission
540
                            break
541 542
                        case planElementGeoFence:
                            _editingLayer = _layerGeoFence
Don Gagne's avatar
Don Gagne committed
543
                            break
544 545
                        case planElementRallyPoints:
                            _editingLayer = _layerRallyPoints
Don Gagne's avatar
Don Gagne committed
546
                            break
547 548 549 550
                        }
                    }
                }

551 552 553 554 555 556 557 558
                QGCRadioButton {
                    id:             planElementMission
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Mission")
                    checked:        true
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
559 560
                }

561 562 563 564 565 566 567 568 569
                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementGeoFence
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Fence")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
570
                }
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586

                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementRallyPoints
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Rally")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
                }
            } // Row - Plan Element Selector

            // Mission Item Editor
            Item {
                id:                 missionItemEditor
587
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
588 589 590 591 592 593 594 595 596 597 598
                anchors.top:        planElementSelectorRow.visible ? planElementSelectorRow.bottom : planElementSelectorRow.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                anchors.bottom:     parent.bottom
                visible:            _editingLayer == _layerMission

                QGCListView {
                    id:             missionItemEditorListView
                    anchors.fill:   parent
                    spacing:        _margin / 2
                    orientation:    ListView.Vertical
599
                    model:          _missionController.visualItems
600 601
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
602
                    currentIndex:   _missionController.currentPlanViewIndex
603 604 605
                    highlightMoveDuration: 250

                    delegate: MissionItemEditor {
606
                        map:                editorMap
607
                        masterController:  _planMasterController
608 609 610 611
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
612

613
                        onClicked:  _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
614 615 616

                        onRemove: {
                            var removeIndex = index
617 618
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
619 620
                                removeIndex--
                            }
621
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
622 623
                        }

624 625
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
626 627 628 629 630
                    }
                } // QGCListView
            } // Item - Mission Item editor

            // GeoFence Editor
631 632 633 634 635 636
            GeoFenceEditor {
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight / 2
                anchors.top:            planElementSelectorRow.bottom
                anchors.left:           parent.left
                anchors.right:          parent.right
                availableHeight:        ScreenTools.availableHeight
637
                myGeoFenceController:   _geoFenceController
638 639
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
640 641 642 643 644 645
            }

            // Rally Point Editor

            RallyPointEditorHeader {
                id:                 rallyPointHeader
646 647
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        planElementSelectorRow.bottom
648 649 650
                anchors.left:       parent.left
                anchors.right:      parent.right
                visible:            _editingLayer == _layerRallyPoints
651
                controller:         _rallyPointController
652 653 654 655
            }

            RallyPointItemEditor {
                id:                 rallyPointEditor
656 657
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        rallyPointHeader.bottom
658 659
                anchors.left:       parent.left
                anchors.right:      parent.right
660 661 662
                visible:            _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:         _rallyPointController.currentRallyPoint
                controller:         _rallyPointController
663 664
            }
        } // Right panel
665 666 667 668 669 670 671

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
672
            visible:            _toolStripBottom < y
673 674 675 676 677 678
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
679
            height:             ScreenTools.defaultFontPixelHeight * 7
680
            maxWidth:           parent.width - rightPanel.width - x
681
            anchors.bottom:     parent.bottom
682
            missionItems:       _missionController.visualItems
683
            visible:            _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus
684
        }
Don Gagne's avatar
Don Gagne committed
685
    } // QGCViewPanel
686

687 688 689 690
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
691
            message:   qsTr("You have unsaved/unsent changes. Loading from the Vehicle will lose these changes. Are you sure you want to load from the Vehicle?")
692 693
            function accept() {
                hideDialog()
694
                masterController.loadFromVehicle()
695 696 697 698 699 700 701 702
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
703
            message:   qsTr("You have unsaved/unsent changes. Loading from a file will lose these changes. Are you sure you want to load from a file?")
704 705
            function accept() {
                hideDialog()
706
                masterController.loadFromSelectedFile()
707 708 709 710
            }
        }
    }

711 712 713
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
714 715
            message: qsTr("Are you sure you want to remove all items? ") +
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
716
            function accept() {
717 718 719 720 721
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
722 723 724 725 726
                hideDialog()
            }
        }
    }

727 728 729 730 731 732 733 734 735 736 737
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

738 739 740 741 742 743 744 745 746
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

            QGCLabel { text: qsTr("Create complex pattern:") }

            Repeater {
747
                model: _missionController.complexMissionItemNames
748 749 750 751 752 753

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
754
                        addComplexItem(modelData)
755 756 757 758 759 760
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
761 762

    Component {
763
        id: syncDropPanel
764

765 766 767
        Column {
            id:         columnHolder
            spacing:    _margin
768

769
            property string _overwriteText: (_editingLayer == _layerMission) ? qsTr("Mission overwrite") : ((_editingLayer == _layerGeoFence) ? qsTr("GeoFence overwrite") : qsTr("Rally Points overwrite"))
770

771 772 773
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
774
                text:       masterController.dirty ?
775 776
                                qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                qsTr("Sync:")
777 778
            }

779 780 781 782 783 784
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
785

786 787 788
                QGCButton {
                    text:               qsTr("Upload")
                    Layout.fillWidth:   true
789
                    enabled:            !masterController.offline && !masterController.syncInProgress
790 791
                    onClicked: {
                        dropPanel.hide()
792
                        masterController.upload()
793 794
                    }
                }
795

796 797 798
                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
799
                    enabled:            !masterController.offline && !masterController.syncInProgress
800 801
                    onClicked: {
                        dropPanel.hide()
802
                        if (masterController.dirty) {
803 804
                            _qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
805
                            masterController.loadFromVehicle()
806 807 808
                        }
                    }
                }
809

810 811 812
                QGCButton {
                    text:               qsTr("Save To File...")
                    Layout.fillWidth:   true
813
                    enabled:            !masterController.syncInProgress
814 815
                    onClicked: {
                        dropPanel.hide()
816
                        masterController.saveToSelectedFile()
817 818 819 820 821 822
                    }
                }

                QGCButton {
                    text:               qsTr("Load From File...")
                    Layout.fillWidth:   true
823
                    enabled:            !masterController.syncInProgress
824 825
                    onClicked: {
                        dropPanel.hide()
826
                        if (masterController.dirty) {
827 828
                            _qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
829
                            masterController.loadFromSelectedFile()
830 831 832 833 834 835 836 837 838 839 840 841
                        }
                    }
                }

                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        dropPanel.hide()
                        _qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
                    }
                }
842 843 844 845 846 847

                QGCButton {
                    text:               qsTr("Save KML...")
                    Layout.fillWidth:   true
                    enabled:            !masterController.syncInProgress
                    onClicked: {
848 849 850 851 852
                        // First point do not count
                        if (_visualItems.count < 2) {
                            _qgcView.showDialog(noItemForKML, qsTr("KML"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
                            return
                        }
853 854 855 856
                        dropPanel.hide()
                        masterController.saveKmlToSelectedFile()
                    }
                }
857
            }
858 859
        }
    }
Don Gagne's avatar
Don Gagne committed
860
} // QGCVIew