MissionEditor.qml 45.4 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


Don Gagne's avatar
Don Gagne committed
11 12 13 14 15
import QtQuick          2.4
import QtQuick.Controls 1.3
import QtQuick.Dialogs  1.2
import QtLocation       5.3
import QtPositioning    5.3
dogmaphobic's avatar
dogmaphobic committed
16
import QtQuick.Layouts  1.2
Don Gagne's avatar
Don Gagne committed
17

18
import QGroundControl               1.0
Don Gagne's avatar
Don Gagne committed
19 20 21 22
import QGroundControl.FlightMap     1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0
import QGroundControl.Palette       1.0
Don Gagne's avatar
Don Gagne committed
23
import QGroundControl.Mavlink       1.0
24
import QGroundControl.Controllers   1.0
Don Gagne's avatar
Don Gagne committed
25 26

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

Don Gagne's avatar
Don Gagne committed
28
QGCView {
29 30
    id:         qgcView
    viewPanel:  panel
Don Gagne's avatar
Don Gagne committed
31

32
    // zOrder comes from the Loader in MainWindow.qml
Gus Grubba's avatar
Gus Grubba committed
33
    z: QGroundControl.zOrderTopMost
34

35 36 37 38 39
    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)
Gus Grubba's avatar
Gus Grubba committed
40
    readonly property real      _rightPanelOpacity:     1
41 42
    readonly property int       _toolButtonCount:       6
    readonly property real      _toolButtonTopMargin:   parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2)
43
    readonly property var       _defaultVehicleCoordinate:   QtPositioning.coordinate(37.803784, -122.462276)
44

45
    property var    _visualItems:           missionController.visualItems
Don Gagne's avatar
Don Gagne committed
46
    property var    _currentMissionItem
47
    property int    _currentMissionIndex:   0
48 49
    property bool   _firstVehiclePosition:  true
    property var    activeVehiclePosition:  _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate()
50
    property bool   _lightWidgetBorders:    editorMap.isSatelliteMap
51
    property bool   _addWaypointOnClick:    false
52

53 54 55 56 57
    /// The controller which should be called for load/save, send to/from vehicle calls
    property var _syncDropDownController: missionController

    readonly property int _layerMission:        1
    readonly property int _layerGeoFence:       2
58
    readonly property int _layerRallyPoints:    3
59 60
    property int _editingLayer: _layerMission

61
    onActiveVehiclePositionChanged: updateMapToVehiclePosition()
62

63
    Connections {
64
        target: QGroundControl.multiVehicleManager
65 66 67 68 69 70

        onActiveVehicleChanged: {
            // When the active vehicle changes we need to allow the first vehicle position to move the map again
            _firstVehiclePosition = true
            updateMapToVehiclePosition()
        }
71
    }
72 73

    function updateMapToVehiclePosition() {
74
        if (_activeVehicle && _activeVehicle.coordinateValid && _activeVehicle.coordinate.isValid && _firstVehiclePosition) {
75 76
            _firstVehiclePosition = false
            editorMap.center = _activeVehicle.coordinate
77 78 79
        }
    }

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

    function checkFirstLoadComplete() {
        if (!_firstLoadComplete && _firstMissionLoadComplete && _firstRallyLoadComplete && _firstFenceLoadComplete) {
            _firstLoadComplete = true
88
            mapFitFunctions.fitMapViewportToAllItems()
89 90 91
        }
    }

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    function addSurveyItem() {
            var coordinate = editorMap.center
            coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
            coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
            coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
            var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count)
            setCurrentItem(sequenceNumber)
    }

    MapFitFunctions {
        id:                         mapFitFunctions
        map:                        editorMap
        mapFitViewport:             Qt.rect(leftToolWidth, toolbarHeight, editorMap.width - leftToolWidth - rightPanelWidth, editorMap.height - toolbarHeight)
        usePlannedHomePosition:     true
        mapGeoFenceController:      geoFenceController
        mapMissionController:       missionController
        mapRallyPointController:    rallyPointController

        property real toolbarHeight:    qgcView.height - ScreenTools.availableHeight
        property real rightPanelWidth:  _rightPanelWidth
        property real leftToolWidth:    mapFitFunctions.x + mapFitFunctions.width
    }

115
    MissionController {
116
        id: missionController
117

118 119
        Component.onCompleted: {
            start(true /* editMode */)
120
            setCurrentItem(0)
121 122
        }

123 124
        function loadFromSelectedFile() {
            if (ScreenTools.isMobile) {
Don Gagne's avatar
Don Gagne committed
125
                qgcView.showDialog(mobileFilePicker, qsTr("Select Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Cancel)
126 127
            } else {
                missionController.loadFromFilePicker()
128
                mapFitFunctions.fitMapViewportToMissionItems()
129 130 131 132 133 134
                _currentMissionItem = _visualItems.get(0)
            }
        }

        function saveToSelectedFile() {
            if (ScreenTools.isMobile) {
135
                qgcView.showDialog(mobileFileSaver, qsTr("Save Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
136 137 138 139 140
            } else {
                missionController.saveToFilePicker()
            }
        }

141
        function fitViewportToItems() {
142
            mapFitFunctions.fitMapViewportToMissionItems()
143 144
        }

145 146 147
        onVisualItemsChanged: {
            itemDragger.clearItem()
        }
148

149
        onNewItemsFromVehicle: {
150
            mapFitFunctions.fitMapViewportToMissionItems()
151
            setCurrentItem(0)
152 153
            _firstMissionLoadComplete = true
            checkFirstLoadComplete()
154 155
        }
    }
156

157 158
    GeoFenceController {
        id: geoFenceController
159

160
        Component.onCompleted: start(true /* editMode */)
161

162 163 164 165 166 167 168 169 170 171 172 173 174
        function saveToSelectedFile() {
            if (ScreenTools.isMobile) {
                qgcView.showDialog(mobileFileSaver, qsTr("Save Fence File"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
            } else {
                geoFenceController.saveToFilePicker()
            }
        }

        function loadFromSelectedFile() {
            if (ScreenTools.isMobile) {
                qgcView.showDialog(mobileFilePicker, qsTr("Select Fence File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
            } else {
                geoFenceController.loadFromFilePicker()
175
                mapFitFunctions.fitMapViewportToFenceItems()
176 177 178
            }
        }

179 180 181 182 183 184 185 186
        function validateBreachReturn() {
            if (geoFenceController.polygon.path.length > 0) {
                if (!geoFenceController.polygon.containsCoordinate(geoFenceController.breachReturnPoint)) {
                    geoFenceController.breachReturnPoint = geoFenceController.polygon.center()
                }
                if (!geoFenceController.polygon.containsCoordinate(geoFenceController.breachReturnPoint)) {
                    geoFenceController.breachReturnPoint = geoFenceController.polygon.path[0]
                }
187 188
            }
        }
189 190

        function fitViewportToItems() {
191
            mapFitFunctions.fitMapViewportToFenceItems()
192 193 194 195 196 197
        }

        onLoadComplete: {
            _firstFenceLoadComplete = true
            switch (_syncDropDownController) {
            case geoFenceController:
198
                mapFitFunctions.fitMapViewportToFenceItems()
199 200 201 202 203 204
                break
            case missionController:
                checkFirstLoadComplete()
                break
            }
        }
205
    }
206

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    RallyPointController {
        id: rallyPointController

        onCurrentRallyPointChanged: {
            if (_editingLayer == _layerRallyPoints && !currentRallyPoint) {
                itemDragger.visible = false
                itemDragger.coordinateItem = undefined
                itemDragger.mapCoordinateIndicator = undefined
            }
        }

        Component.onCompleted: start(true /* editMode */)

        function saveToSelectedFile() {
            if (ScreenTools.isMobile) {
                qgcView.showDialog(mobileFileSaver, qsTr("Save Rally Point File"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
            } else {
                rallyPointController.saveToFilePicker()
            }
        }

        function loadFromSelectedFile() {
            if (ScreenTools.isMobile) {
                qgcView.showDialog(mobileFilePicker, qsTr("Select Rally Point File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
            } else {
                rallyPointController.loadFromFilePicker()
233
                mapFitFunctions.fitMapViewportToRallyItems()
234 235 236 237
            }
        }

        function fitViewportToItems() {
238
            mapFitFunctions.fitMapViewportToRallyItems()
239 240 241 242 243 244
        }

        onLoadComplete: {
            _firstRallyLoadComplete = true
            switch (_syncDropDownController) {
            case rallyPointController:
245
                mapFitFunctions.fitMapViewportToRallyItems()
246 247 248 249
                break
            case missionController:
                checkFirstLoadComplete()
                break
250 251 252 253
            }
        }
    }

254
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
255

256 257 258 259 260 261
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

    ExclusiveGroup {
        id: _dropButtonsExclusiveGroup
262 263
    }

264
    function setCurrentItem(sequenceNumber) {
265
        editorMap.polygonDraw.cancelPolygonEdit()
Don Gagne's avatar
Don Gagne committed
266
        _currentMissionItem = undefined
267
        for (var i=0; i<_visualItems.count; i++) {
268 269 270
            var visualItem = _visualItems.get(i)
            if (visualItem.sequenceNumber == sequenceNumber) {
                _currentMissionItem = visualItem
Don Gagne's avatar
Don Gagne committed
271
                _currentMissionItem.isCurrentItem = true
272
                _currentMissionIndex = i
Don Gagne's avatar
Don Gagne committed
273
            } else {
274
                visualItem.isCurrentItem = false
Don Gagne's avatar
Don Gagne committed
275
            }
276 277 278
        }
    }

279 280
    property int _moveDialogMissionItemIndex

281 282 283
    Component {
        id: mobileFilePicker

Don Gagne's avatar
Don Gagne committed
284 285
        QGCMobileFileOpenDialog {
            fileExtension: _syncDropDownController.fileExtension
286 287 288 289
            onFilenameReturned: {
                _syncDropDownController.loadFromFile(filename)
                _syncDropDownController.fitViewportToItems()
            }
290 291 292 293 294 295
        }
    }

    Component {
        id: mobileFileSaver

Don Gagne's avatar
Don Gagne committed
296
        QGCMobileFileSaveDialog {
297
            fileExtension:      _syncDropDownController.fileExtension
298
            onFilenameReturned: _syncDropDownController.saveToFile(filename)
299 300 301
        }
    }

302 303 304 305 306 307 308 309 310 311
    Component {
        id: moveDialog

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

                if (toIndex == 0) {
                    toIndex = 1
                }
312
                missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
313 314 315 316 317 318 319 320 321 322 323 324
                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
325
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
326 327 328 329
                }

                QGCComboBox {
                    id:             toCombo
330
                    model:          _visualItems.count
331 332 333 334 335 336
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
337 338
    QGCViewPanel {
        id:             panel
339 340 341 342
        height:         ScreenTools.availableHeight
        anchors.bottom: parent.bottom
        anchors.left:   parent.left
        anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
343

Don Gagne's avatar
Don Gagne committed
344
        Item {
Don Gagne's avatar
Don Gagne committed
345 346
            anchors.fill: parent

Don Gagne's avatar
Don Gagne committed
347 348
            FlightMap {
                id:             editorMap
349
                height:         qgcView.height
350 351 352
                anchors.bottom: parent.bottom
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
353
                mapName:        "MissionEditor"
354

355 356
                readonly property real animationDuration: 500

357 358 359
                // Initial map position duplicates Fly view position
                Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition

360 361 362 363 364 365 366
                Behavior on zoomLevel {
                    NumberAnimation {
                        duration:       editorMap.animationDuration
                        easing.type:    Easing.InOutQuad
                    }
                }

367 368
                QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

Don Gagne's avatar
Don Gagne committed
369
                MouseArea {
370 371
                    //-- It's a whole lot faster to just fill parent and deal with top offset below
                    //   than computing the coordinate offset.
Don Gagne's avatar
Don Gagne committed
372 373
                    anchors.fill: parent
                    onClicked: {
374 375
                        //-- Don't pay attention to items beneath the toolbar.
                        var topLimit = parent.height - ScreenTools.availableHeight
376 377 378 379 380 381 382 383 384 385 386
                        if(mouse.y < topLimit) {
                            return
                        }

                        var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y))
                        coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                        coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)

                        switch (_editingLayer) {
                        case _layerMission:
387
                            if (_addWaypointOnClick) {
388
                                var sequenceNumber = missionController.insertSimpleMissionItem(coordinate, missionController.visualItems.count)
389 390
                                setCurrentItem(sequenceNumber)
                            }
391 392
                            break
                        case _layerGeoFence:
393
                            if (geoFenceController.breachReturnEnabled) {
394 395 396 397 398 399 400 401
                                geoFenceController.breachReturnPoint = coordinate
                                geoFenceController.validateBreachReturn()
                            }
                            break
                        case _layerRallyPoints:
                            if (rallyPointController.rallyPointsSupported) {
                                rallyPointController.addPoint(coordinate)
                            }
402
                            break
403
                        }
Don Gagne's avatar
Don Gagne committed
404
                    }
Don Gagne's avatar
Don Gagne committed
405
                }
Don Gagne's avatar
Don Gagne committed
406

407
                // We use this item to support dragging since dragging a MapQuickItem just doesn't seem to work
Don Gagne's avatar
Don Gagne committed
408 409
                Rectangle {
                    id:             itemDragger
410 411
                    x:              mapCoordinateIndicator ? (mapCoordinateIndicator.x + mapCoordinateIndicator.anchorPoint.x - (itemDragger.width / 2)) : 100
                    y:              mapCoordinateIndicator ? (mapCoordinateIndicator.y + mapCoordinateIndicator.anchorPoint.y - (itemDragger.height / 2)) : 100
412 413
                    width:          ScreenTools.defaultFontPixelHeight * 2
                    height:         ScreenTools.defaultFontPixelHeight * 2
Don Gagne's avatar
Don Gagne committed
414 415 416
                    color:          "transparent"
                    visible:        false
                    z:              QGroundControl.zOrderMapItems + 1    // Above item icons
417

418 419
                    property var    coordinateItem
                    property var    mapCoordinateIndicator
420 421 422 423 424 425 426 427 428
                    property bool   preventCoordinateBindingLoop: false

                    onXChanged: liveDrag()
                    onYChanged: liveDrag()

                    function liveDrag() {
                        if (!itemDragger.preventCoordinateBindingLoop && Drag.active) {
                            var point = Qt.point(itemDragger.x + (itemDragger.width  / 2), itemDragger.y + (itemDragger.height / 2))
                            var coordinate = editorMap.toCoordinate(point)
429
                            coordinate.altitude = itemDragger.coordinateItem.coordinate.altitude
430
                            itemDragger.preventCoordinateBindingLoop = true
431
                            itemDragger.coordinateItem.coordinate = coordinate
432 433 434
                            itemDragger.preventCoordinateBindingLoop = false
                        }
                    }
Don Gagne's avatar
Don Gagne committed
435

436
                    function clearItem() {
Don Gagne's avatar
Don Gagne committed
437
                        itemDragger.visible = false
438 439
                        itemDragger.coordinateItem = undefined
                        itemDragger.mapCoordinateIndicator = undefined
Don Gagne's avatar
Don Gagne committed
440 441
                    }

442 443 444 445 446 447 448 449
                    Drag.active:    itemDrag.drag.active
                    Drag.hotSpot.x: width  / 2
                    Drag.hotSpot.y: height / 2

                    MouseArea {
                        id:             itemDrag
                        anchors.fill:   parent
                        drag.target:    parent
Don Gagne's avatar
Don Gagne committed
450 451 452 453
                        drag.minimumX:  0
                        drag.minimumY:  0
                        drag.maximumX:  itemDragger.parent.width - parent.width
                        drag.maximumY:  itemDragger.parent.height - parent.height
Don Gagne's avatar
Don Gagne committed
454
                    }
455
                }
456

457
                // Add the complex mission item polygon to the map
458
                MapItemView {
459
                    model: missionController.complexVisualItems
460

Don Gagne's avatar
Don Gagne committed
461
                    delegate: MapPolygon {
462 463 464 465 466 467
                        color:      'green'
                        path:       object.polygonPath
                        opacity:    0.5
                    }
                }

468 469
                // Add the complex mission item grid to the map
                MapItemView {
470
                    model: missionController.complexVisualItems
471 472 473

                    delegate: MapPolyline {
                        line.color: "white"
474
                        line.width: 2
475 476 477 478
                        path:       object.gridPoints
                    }
                }

479 480
                // Add the complex mission item exit coordinates
                MapItemView {
481
                    model: missionController.complexVisualItems
482 483 484 485 486 487 488 489 490 491 492 493
                    delegate:   exitCoordinateComponent
                }

                Component {
                    id: exitCoordinateComponent

                    MissionItemIndicator {
                        coordinate:     object.exitCoordinate
                        z:              QGroundControl.zOrderMapItems
                        missionItem:    object
                        sequenceNumber: object.lastSequenceNumber
                        visible:        object.specifiesCoordinate
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

                        // These are the non-coordinate child mission items attached to this item
                        Row {
                            anchors.top:    parent.top
                            anchors.left:   parent.right

                            Repeater {
                                model: !object.isSimpleItem ? object.childItems : 0

                                delegate: MissionItemIndexLabel {
                                    label:      object.abbreviation
                                    checked:    object.isCurrentItem
                                    z:          2

                                    onClicked: setCurrentItem(object.sequenceNumber)
                                }
                            }
                        }
512
                    }
Don Gagne's avatar
Don Gagne committed
513 514
                }

515 516
                // Add the simple mission items to the map
                MapItemView {
517
                    model:      missionController.visualItems
518 519 520
                    delegate:   missionItemComponent
                }

Don Gagne's avatar
Don Gagne committed
521
                Component {
522
                    id: missionItemComponent
Don Gagne's avatar
Don Gagne committed
523 524 525 526

                    MissionItemIndicator {
                        id:             itemIndicator
                        coordinate:     object.coordinate
527
                        visible:        object.specifiesCoordinate
Don Gagne's avatar
Don Gagne committed
528
                        z:              QGroundControl.zOrderMapItems
529
                        missionItem:    object
530
                        sequenceNumber: object.sequenceNumber
Don Gagne's avatar
Don Gagne committed
531

532 533 534
                        //-- If you don't want to allow selecting items beneath the
                        //   toolbar, the code below has to check and see if mouse.y
                        //   is greater than (map.height - ScreenTools.availableHeight)
Don Gagne's avatar
Don Gagne committed
535 536
                        onClicked: setCurrentItem(object.sequenceNumber)

537 538
                        function updateItemIndicator() {
                            if (object.isCurrentItem && itemIndicator.visible && object.specifiesCoordinate && object.isSimpleItem) {
539 540
                                // Setup our drag item
                                itemDragger.visible = true
541 542
                                itemDragger.coordinateItem = Qt.binding(function() { return object })
                                itemDragger.mapCoordinateIndicator = Qt.binding(function() { return itemIndicator })
Don Gagne's avatar
Don Gagne committed
543 544 545
                            }
                        }

546 547 548
                        Connections {
                            target: object

549 550
                            onIsCurrentItemChanged:         updateItemIndicator()
                            onSpecifiesCoordinateChanged:   updateItemIndicator()
551 552
                        }

Don Gagne's avatar
Don Gagne committed
553 554 555 556 557 558
                        // These are the non-coordinate child mission items attached to this item
                        Row {
                            anchors.top:    parent.top
                            anchors.left:   parent.right

                            Repeater {
559
                                model: object.isSimpleItem ? object.childItems : 0
Don Gagne's avatar
Don Gagne committed
560 561

                                delegate: MissionItemIndexLabel {
562 563 564
                                    label:      object.abbreviation
                                    checked:    object.isCurrentItem
                                    z:          2
Don Gagne's avatar
Don Gagne committed
565 566 567 568 569 570

                                    onClicked: setCurrentItem(object.sequenceNumber)
                                }
                            }
                        }
                    }
571 572 573
                }

                // Add lines between waypoints
574
                MissionLineView {
575
                    model:      _editingLayer == _layerMission ? missionController.waypointLines : undefined
Don Gagne's avatar
Don Gagne committed
576 577
                }

Don Gagne's avatar
Don Gagne committed
578 579
                // Add the vehicles to the map
                MapItemView {
580
                    model: QGroundControl.multiVehicleManager.vehicles
Don Gagne's avatar
Don Gagne committed
581 582
                    delegate:
                        VehicleMapItem {
583 584 585 586 587 588
                        vehicle:        object
                        coordinate:     object.coordinate
                        isSatellite:    editorMap.isSatelliteMap
                        size:           ScreenTools.defaultFontPixelHeight * 5
                        z:              QGroundControl.zOrderMapItems - 1
                    }
Don Gagne's avatar
Don Gagne committed
589 590
                }

591 592 593
                // Plan Element selector (Mission/Fence/Rally)
                Row {
                    id:                 planElementSelectorRow
Don Gagne's avatar
Don Gagne committed
594 595 596 597
                    anchors.topMargin:  parent.height - ScreenTools.availableHeight + _margin
                    anchors.top:        parent.top
                    anchors.leftMargin: parent.width - _rightPanelWidth
                    anchors.left:       parent.left
598
                    spacing:            _horizontalMargin
599
                    visible:            QGroundControl.corePlugin.options.enablePlanViewSelector
600 601

                    readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75
Don Gagne's avatar
Don Gagne committed
602 603 604 605

                    ExclusiveGroup {
                        id: planElementSelectorGroup
                        onCurrentChanged: {
606 607 608 609 610 611 612 613 614 615 616 617 618 619
                            switch (current) {
                            case planElementMission:
                                _editingLayer = _layerMission
                                _syncDropDownController = missionController
                                break
                            case planElementGeoFence:
                                _editingLayer = _layerGeoFence
                                _syncDropDownController = geoFenceController
                                break
                            case planElementRallyPoints:
                                _editingLayer = _layerRallyPoints
                                _syncDropDownController = rallyPointController
                                break
                            }
620
                            _syncDropDownController.fitViewportToItems()
Don Gagne's avatar
Don Gagne committed
621 622 623
                        }
                    }

624
                    QGCRadioButton {
625 626
                        id:             planElementMission
                        exclusiveGroup: planElementSelectorGroup
627
                        text:           qsTr("Mission")
628
                        checked:        true
Don Gagne's avatar
Don Gagne committed
629
                        color:          mapPal.text
630 631
                        textStyle:      Text.Outline
                        textStyleColor: mapPal.textOutline
632 633 634 635
                    }

                    Item { height: 1; width: 1 }

636
                    QGCRadioButton {
637 638
                        id:             planElementGeoFence
                        exclusiveGroup: planElementSelectorGroup
639
                        text:           qsTr("Fence")
Don Gagne's avatar
Don Gagne committed
640
                        color:          mapPal.text
641 642
                        textStyle:      Text.Outline
                        textStyleColor: mapPal.textOutline
Don Gagne's avatar
Don Gagne committed
643
                    }
644 645 646

                    Item { height: 1; width: 1 }

647
                    QGCRadioButton {
648 649
                        id:             planElementRallyPoints
                        exclusiveGroup: planElementSelectorGroup
650
                        text:           qsTr("Rally")
Don Gagne's avatar
Don Gagne committed
651
                        color:          mapPal.text
652 653
                        textStyle:      Text.Outline
                        textStyleColor: mapPal.textOutline
654 655
                    }
                } // Row - Plan Element Selector
Don Gagne's avatar
Don Gagne committed
656

657
                // Mission Item Editor
Don Gagne's avatar
Don Gagne committed
658
                Item {
Don Gagne's avatar
Don Gagne committed
659 660
                    id:                 missionItemEditor
                    anchors.topMargin:  _margin
661
                    anchors.top:        planElementSelectorRow.visible ? planElementSelectorRow.bottom : planElementSelectorRow.top
Don Gagne's avatar
Don Gagne committed
662 663 664 665 666 667
                    anchors.bottom:     parent.bottom
                    anchors.right:      parent.right
                    width:              _rightPanelWidth
                    opacity:            _rightPanelOpacity
                    z:                  QGroundControl.zOrderTopMost
                    visible:            _editingLayer == _layerMission
668

Don Gagne's avatar
Don Gagne committed
669
                    MouseArea {
670 671 672 673 674
                        // This MouseArea prevents the Map below it from getting Mouse events. Without this
                        // things like mousewheel will scroll the Flickable and then scroll the map as well.
                        anchors.fill:       missionItemEditorListView
                        onWheel:            wheel.accepted = true
                    }
Don Gagne's avatar
Don Gagne committed
675

Don Gagne's avatar
Don Gagne committed
676
                    QGCListView {
677
                        id:             missionItemEditorListView
678 679 680
                        anchors.left:   parent.left
                        anchors.right:  parent.right
                        anchors.top:    parent.top
681
                        height:         parent.height
682 683
                        spacing:        _margin / 2
                        orientation:    ListView.Vertical
684
                        model:          missionController.visualItems
685
                        cacheBuffer:    height * 2
686
                        clip:           true
687
                        currentIndex:   _currentMissionIndex
688 689
                        highlightMoveDuration: 250

690
                        delegate: MissionItemEditor {
691 692
                            missionItem:    object
                            width:          parent.width
693
                            readOnly:       false
694 695 696 697

                            onClicked:  setCurrentItem(object.sequenceNumber)

                            onRemove: {
698
                                itemDragger.clearItem()
699
                                missionController.removeMissionItem(index)
700
                                editorMap.polygonDraw.cancelPolygonEdit()
701 702
                            }

703
                            onInsert: {
Adyasha Dash's avatar
Adyasha Dash committed
704
                                var sequenceNumber = missionController.insertSimpleMissionItem(editorMap.center, index)
705 706 707
                                setCurrentItem(sequenceNumber)
                            }

708
                            onMoveHomeToMapCenter: _visualItems.get(0).coordinate = editorMap.center
709
                        }
Don Gagne's avatar
Don Gagne committed
710
                    } // QGCListView
711 712
                } // Item - Mission Item editor

713 714
                // GeoFence Editor
                Loader {
Don Gagne's avatar
Don Gagne committed
715
                    anchors.topMargin:  _margin
716
                    anchors.top:        planElementSelectorRow.bottom
717 718 719 720 721 722 723 724 725 726 727 728 729
                    anchors.right:      parent.right
                    opacity:            _rightPanelOpacity
                    z:                  QGroundControl.zOrderTopMost
                    source:             _editingLayer == _layerGeoFence ? "qrc:/qml/GeoFenceEditor.qml" : ""

                    property real availableWidth:   _rightPanelWidth
                    property real availableHeight:  ScreenTools.availableHeight
                }

                // GeoFence polygon
                MapPolygon {
                    border.color:   "#80FF0000"
                    border.width:   3
730
                    path:           geoFenceController.polygon.path
731
                    z:              QGroundControl.zOrderMapItems
732
                    visible:        geoFenceController.polygonEnabled
733 734 735 736 737 738 739
                }

                // GeoFence circle
                MapCircle {
                    border.color:   "#80FF0000"
                    border.width:   3
                    center:         missionController.plannedHomePosition
740
                    radius:         geoFenceController.circleRadius
741
                    z:              QGroundControl.zOrderMapItems
742
                    visible:        geoFenceController.circleEnabled
743 744 745 746 747 748
                }

                // GeoFence breach return point
                MapQuickItem {
                    anchorPoint:    Qt.point(sourceItem.width / 2, sourceItem.height / 2)
                    coordinate:     geoFenceController.breachReturnPoint
749
                    visible:        geoFenceController.breachReturnEnabled
750
                    sourceItem:     MissionItemIndexLabel { label: "F" }
751
                    z:              QGroundControl.zOrderMapItems
752 753
                }

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
                // Rally Point Editor

                RallyPointEditorHeader {
                    id:                 rallyPointHeader
                    anchors.topMargin:  _margin
                    anchors.top:        planElementSelectorRow.bottom
                    anchors.right:      parent.right
                    width:              _rightPanelWidth
                    opacity:            _rightPanelOpacity
                    z:                  QGroundControl.zOrderTopMost
                    visible:            _editingLayer == _layerRallyPoints
                    controller:         rallyPointController
                }

                RallyPointItemEditor {
                    id:                 rallyPointEditor
                    anchors.topMargin:  _margin
                    anchors.top:        rallyPointHeader.bottom
                    anchors.right:      parent.right
                    width:              _rightPanelWidth
                    opacity:            _rightPanelOpacity
                    z:                  QGroundControl.zOrderTopMost
                    visible:            _editingLayer == _layerRallyPoints && rallyPointController.points.count
                    rallyPoint:         rallyPointController.currentRallyPoint
                    controller:         rallyPointController
                }

                // Rally points on map

                MapItemView {
                    model: rallyPointController.points

                    delegate: MapQuickItem {
                        id:             itemIndicator
                        anchorPoint:    Qt.point(sourceItem.width / 2, sourceItem.height / 2)
                        coordinate:     object.coordinate
                        z:              QGroundControl.zOrderMapItems

                        sourceItem: MissionItemIndexLabel {
                            id:         itemIndexLabel
                            label:      qsTr("R", "rally point map item label")
                            checked:    _editingLayer == _layerRallyPoints ? object == rallyPointController.currentRallyPoint : false

                            onClicked: rallyPointController.currentRallyPoint = object

                            onCheckedChanged: {
                                if (checked) {
                                    // Setup our drag item
                                    itemDragger.visible = true
                                    itemDragger.coordinateItem = Qt.binding(function() { return object })
                                    itemDragger.mapCoordinateIndicator = Qt.binding(function() { return itemIndicator })
                                }
                            }
                        }
                    }
                }

811 812 813 814 815 816 817 818 819 820 821
                //-- Dismiss Drop Down (if any)
                MouseArea {
                    anchors.fill:   parent
                    enabled:        _dropButtonsExclusiveGroup.current != null
                    onClicked: {
                        if(_dropButtonsExclusiveGroup.current)
                            _dropButtonsExclusiveGroup.current.checked = false
                        _dropButtonsExclusiveGroup.current = null
                    }
                }

822
                /*
823

824
                  FIXME: Need to put these back into ToolStrip
825

Don Gagne's avatar
Don Gagne committed
826 827 828 829 830 831 832 833 834 835 836 837 838 839
                //-- Zoom Map In
                RoundButton {
                    id:                 mapZoomPlus
                    anchors.topMargin:  ScreenTools.defaultFontPixelHeight
                    anchors.top:        mapTypeButton.bottom
                    anchors.left:       mapTypeButton.left
                    visible:            !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                    buttonImage:        "/qmlimages/ZoomPlus.svg"
                    lightBorders:   _lightWidgetBorders

                    onClicked: {
                        if(editorMap)
                            editorMap.zoomLevel += 0.5
                        checked = false
840
                    }
Don Gagne's avatar
Don Gagne committed
841
                }
842

Don Gagne's avatar
Don Gagne committed
843 844 845 846 847 848 849 850 851 852 853 854 855
                //-- Zoom Map Out
                RoundButton {
                    id:                 mapZoomMinus
                    anchors.topMargin:  ScreenTools.defaultFontPixelHeight
                    anchors.top:        mapZoomPlus.bottom
                    anchors.left:       mapZoomPlus.left
                    visible:            !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                    buttonImage:        "/qmlimages/ZoomMinus.svg"
                    lightBorders:       _lightWidgetBorders
                    onClicked: {
                        if(editorMap)
                            editorMap.zoomLevel -= 0.5
                        checked = false
856
                    }
857
                }
858

859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
                */

                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
                    showAlternateIcon:  [ false, false, _syncDropDownController.dirty, false, false ]
                    rotateImage:        [ false, false, _syncDropDownController.syncInProgress, false, false ]
                    buttonEnabled:      [ true, true, !_syncDropDownController.syncInProgress, true, true ]

                    model: [
                        {
                            name:       "Waypoint",
                            iconSource: "/qmlimages/MapAddMission.svg",
                            toggle:     true
                        },
                        {
                            name:       "Pattern",
                            iconSource: "/qmlimages/MapDrawShape.svg"
                        },
                        {
                            name:                   "Sync",
                            iconSource:             "/qmlimages/MapSync.svg",
                            alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                            dropPanelComponent:     syncDropPanel
                        },
                        {
                            name:               "Center",
                            iconSource:         "/qmlimages/MapCenter.svg",
                            dropPanelComponent: centerMapDropPanel
                        },
                        {
                            name:               "Map",
                            iconSource:         "/qmlimages/MapType.svg",
                            dropPanelComponent: mapTypeDropPanel
                        }
                    ]

                    onClicked: {
                        switch (index) {
                        case 0:
                            _addWaypointOnClick = checked
                            break
                        case 1:
                            addSurveyItem()
                            break
                        }
                    }
                }

913 914 915 916 917 918 919 920
                MapScale {
                    anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
                    anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
                    anchors.left:       parent.left
                    mapControl:         editorMap
                    visible:            !ScreenTools.isTinyScreen
                }

921
                MissionItemStatus {
922 923 924 925 926 927 928 929 930
                    id:                     waypointValuesDisplay
                    anchors.margins:        ScreenTools.defaultFontPixelWidth
                    anchors.left:           parent.left
                    anchors.bottom:         parent.bottom
                    z:                      QGroundControl.zOrderTopMost
                    currentMissionItem:     _currentMissionItem
                    missionItems:           missionController.visualItems
                    expandedWidth:          missionItemEditor.x - (ScreenTools.defaultFontPixelWidth * 2)
                    missionDistance:        missionController.missionDistance
931
                    missionTime:            missionController.missionTime
932 933
                    missionMaxTelemetry:    missionController.missionMaxTelemetry
                    visible:                _editingLayer == _layerMission && !ScreenTools.isShortScreen
934
                }
935
            } // FlightMap
Don Gagne's avatar
Don Gagne committed
936 937
        } // Item - split view container
    } // QGCViewPanel
938

939 940 941 942
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
943
            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?")
944 945
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
946
                _syncDropDownController.loadFromVehicle()
947 948 949 950 951 952 953 954
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
955
            message:   qsTr("You have unsaved/unsent changes. Loading a from a file will lose these changes. Are you sure you want to load from a file?")
956 957
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
958
                _syncDropDownController.loadFromSelectedFile()
959 960 961 962
            }
        }
    }

963 964 965
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
Don Gagne's avatar
Don Gagne committed
966
            message: qsTr("Are you sure you want to remove all items?")
967 968
            function accept() {
                itemDragger.clearItem()
Don Gagne's avatar
Don Gagne committed
969
                _syncDropDownController.removeAll()
970 971 972 973 974
                hideDialog()
            }
        }
    }

975 976
    //- ToolStrip DropPanel Components

977
    Component {
978
        id: syncDropPanel
979

980 981 982
        Column {
            id:         columnHolder
            spacing:    _margin
983

984
            property string _overwriteText: (_editingLayer == _layerMission) ? qsTr("Mission overwrite") : ((_editingLayer == _layerGeoFence) ? qsTr("GeoFence overwrite") : qsTr("Rally Points overwrite"))
Don Gagne's avatar
Don Gagne committed
985

986
            QGCLabel {
dogmaphobic's avatar
dogmaphobic committed
987
                width:      sendSaveGrid.width
988
                wrapMode:   Text.WordWrap
989
                text:       _syncDropDownController.dirty ?
Don Gagne's avatar
Don Gagne committed
990
                                qsTr("You have unsaved changes. You should send to your vehicle, or save to a file:") :
991
                                qsTr("Sync:")
992
            }
993

dogmaphobic's avatar
dogmaphobic committed
994 995 996 997 998 999
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
1000

1001
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
1002 1003
                    text:               qsTr("Send To Vehicle")
                    Layout.fillWidth:   true
1004
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
1005
                    onClicked: {
1006
                        dropPanel.hide()
1007
                        _syncDropDownController.sendToVehicle()
1008 1009
                    }
                }
1010

1011
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
1012 1013
                    text:               qsTr("Load From Vehicle")
                    Layout.fillWidth:   true
1014
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
1015
                    onClicked: {
1016
                        dropPanel.hide()
1017
                        if (_syncDropDownController.dirty) {
1018
                            qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1019
                        } else {
1020
                            _syncDropDownController.loadFromVehicle()
1021
                        }
1022 1023
                    }
                }
1024

1025
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
1026 1027
                    text:               qsTr("Save To File...")
                    Layout.fillWidth:   true
1028
                    enabled:            !_syncDropDownController.syncInProgress
1029
                    onClicked: {
1030
                        dropPanel.hide()
1031
                        _syncDropDownController.saveToSelectedFile()
1032 1033
                    }
                }
1034

1035
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
1036 1037
                    text:               qsTr("Load From File...")
                    Layout.fillWidth:   true
1038
                    enabled:            !_syncDropDownController.syncInProgress
1039
                    onClicked: {
1040
                        dropPanel.hide()
1041
                        if (_syncDropDownController.dirty) {
1042
                            qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1043
                        } else {
1044
                            _syncDropDownController.loadFromSelectedFile()
1045
                        }
1046 1047
                    }
                }
1048

dogmaphobic's avatar
dogmaphobic committed
1049 1050 1051 1052
                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
1053
                        dropPanel.hide()
1054
                        qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
dogmaphobic's avatar
dogmaphobic committed
1055
                    }
1056 1057
                }
            }
1058 1059
        }
    }
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

    Component {
        id: mapTypeDropPanel

        Column {
            spacing: _margin

            QGCLabel { text: qsTr("Map type:") }
            Row {
                spacing: ScreenTools.defaultFontPixelWidth
                Repeater {
                    model: QGroundControl.flightMapSettings.mapTypes

                    QGCButton {
                        checkable:      true
                        checked:        QGroundControl.flightMapSettings.mapType === text
                        text:           modelData
                        exclusiveGroup: _mapTypeButtonsExclusiveGroup
                        onClicked: {
                            QGroundControl.flightMapSettings.mapType = text
                            dropPanel.hide()
                        }
                    }
                }
            }
        }
    }
Don Gagne's avatar
Don Gagne committed
1096
} // QGCVIew