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


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 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      _rightPanelOpacity:     0.8
    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

52 53 54 55 56 57 58
    /// 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
    property int _editingLayer: _layerMission

59
    onActiveVehiclePositionChanged: updateMapToVehiclePosition()
60

61
    Connections {
62
        target: QGroundControl.multiVehicleManager
63 64 65 66 67 68

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

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

78 79 80 81 82 83 84 85 86 87
    function normalizeLat(lat) {
        // Normalize latitude to range: 0 to 180, S to N
        return lat + 90.0
    }

    function normalizeLon(lon) {
        // Normalize longitude to range: 0 to 360, W to E
        return lon  + 180.0
    }

88
    /// Fix the map viewport to the current mission items.
89
    function fitViewportToMissionItems() {
90 91
        if (_visualItems.count == 1) {
            editorMap.center = _visualItems.get(0).coordinate
92
        } else {
93
            var missionItem = _visualItems.get(0)
94 95 96 97 98
            var north = normalizeLat(missionItem.coordinate.latitude)
            var south = north
            var east = normalizeLon(missionItem.coordinate.longitude)
            var west = east

99 100
            for (var i=1; i<_visualItems.count; i++) {
                missionItem = _visualItems.get(i)
101

102
                if (missionItem.specifiesCoordinate && !missionItem.isStandaloneCoordinate) {
103 104 105 106 107 108 109 110
                    var lat = normalizeLat(missionItem.coordinate.latitude)
                    var lon = normalizeLon(missionItem.coordinate.longitude)

                    north = Math.max(north, lat)
                    south = Math.min(south, lat)
                    east = Math.max(east, lon)
                    west = Math.min(west, lon)
                }
111
            }
112
            editorMap.visibleRegion = QtPositioning.rectangle(QtPositioning.coordinate(north - 90.0, west - 180.0), QtPositioning.coordinate(south - 90.0, east - 180.0))
113 114 115
        }
    }

116
    MissionController {
117
        id: missionController
118

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

124 125
        function loadFromSelectedFile() {
            if (ScreenTools.isMobile) {
126
                qgcView.showDialog(mobileFilePicker, qsTr("Select Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
127 128 129 130 131 132 133 134 135
            } else {
                missionController.loadFromFilePicker()
                fitViewportToMissionItems()
                _currentMissionItem = _visualItems.get(0)
            }
        }

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

        onVisualItemsChanged: {
            itemDragger.clearItem()
        }
145

146 147
        onNewItemsFromVehicle: {
            fitViewportToMissionItems()
148
            setCurrentItem(0)
149 150
        }
    }
151

152 153
    GeoFenceController {
        id: geoFenceController
154

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

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        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()
            }
        }

173 174 175 176 177
        onFenceSupportedChanged: {
            if (!fenceSupported && _editingLayer == _layerGeoFence) {
                _editingLayer = _layerMission
            }
        }
178 179 180 181 182 183

        onBreachReturnPointChanged: {
            if (polygon.count() > 3) {
                sendToVehicle()
            }
        }
184
    }
185

186
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
187

188 189 190 191 192 193
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

    ExclusiveGroup {
        id: _dropButtonsExclusiveGroup
194 195
    }

196
    function setCurrentItem(sequenceNumber) {
197
        editorMap.polygonDraw.cancelPolygonEdit()
Don Gagne's avatar
Don Gagne committed
198
        _currentMissionItem = undefined
199
        for (var i=0; i<_visualItems.count; i++) {
200 201 202
            var visualItem = _visualItems.get(i)
            if (visualItem.sequenceNumber == sequenceNumber) {
                _currentMissionItem = visualItem
Don Gagne's avatar
Don Gagne committed
203
                _currentMissionItem.isCurrentItem = true
204
                _currentMissionIndex = i
Don Gagne's avatar
Don Gagne committed
205
            } else {
206
                visualItem.isCurrentItem = false
Don Gagne's avatar
Don Gagne committed
207
            }
208 209 210
        }
    }

211 212
    property int _moveDialogMissionItemIndex

213 214 215
    Component {
        id: mobileFilePicker

Don Gagne's avatar
Don Gagne committed
216
        QGCMobileFileDialog {
217
            openDialog:         true
218
            fileExtension:      _syncDropDownController == geoFenceController ? QGroundControl.fenceFileExtension : QGroundControl.missionFileExtension
219
            onFilenameReturned: _syncDropDownController.loadFromfile(filename)
220 221 222 223 224 225
        }
    }

    Component {
        id: mobileFileSaver

Don Gagne's avatar
Don Gagne committed
226
        QGCMobileFileDialog {
227
            openDialog:         false
228
            fileExtension:      _syncDropDownController == geoFenceController ? QGroundControl.fenceFileExtension : QGroundControl.missionFileExtension
229
            onFilenameReturned: _syncDropDownController.saveToFile()
230 231 232
        }
    }

233 234 235 236 237 238 239 240 241 242
    Component {
        id: moveDialog

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

                if (toIndex == 0) {
                    toIndex = 1
                }
243
                missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
244 245 246 247 248 249 250 251 252 253 254 255
                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
256
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
257 258 259 260
                }

                QGCComboBox {
                    id:             toCombo
261
                    model:          _visualItems.count
262 263 264 265 266 267
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
268 269
    QGCViewPanel {
        id:             panel
270 271 272 273
        height:         ScreenTools.availableHeight
        anchors.bottom: parent.bottom
        anchors.left:   parent.left
        anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
274

Don Gagne's avatar
Don Gagne committed
275
        Item {
Don Gagne's avatar
Don Gagne committed
276 277
            anchors.fill: parent

Don Gagne's avatar
Don Gagne committed
278 279
            FlightMap {
                id:             editorMap
280
                height:         qgcView.height
281 282 283
                anchors.bottom: parent.bottom
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
284
                mapName:        "MissionEditor"
285

286 287
                readonly property real animationDuration: 500

288 289 290
                // Initial map position duplicates Fly view position
                Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition

291 292 293 294 295 296 297
                Behavior on zoomLevel {
                    NumberAnimation {
                        duration:       editorMap.animationDuration
                        easing.type:    Easing.InOutQuad
                    }
                }

298 299
                QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

Don Gagne's avatar
Don Gagne committed
300
                MouseArea {
301 302
                    //-- 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
303 304
                    anchors.fill: parent
                    onClicked: {
305 306
                        //-- Don't pay attention to items beneath the toolbar.
                        var topLimit = parent.height - ScreenTools.availableHeight
307 308 309 310 311 312 313 314 315 316 317
                        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:
318
                            if (addMissionItemsButton.checked) {
319
                                var sequenceNumber = missionController.insertSimpleMissionItem(coordinate, missionController.visualItems.count)
320 321
                                setCurrentItem(sequenceNumber)
                            }
322 323
                            break
                        case _layerGeoFence:
324
                            console.log("Updating breach return point", coordinate)
325 326
                            geoFenceController.breachReturnPoint = coordinate
                            break
327
                        }
Don Gagne's avatar
Don Gagne committed
328
                    }
Don Gagne's avatar
Don Gagne committed
329
                }
Don Gagne's avatar
Don Gagne committed
330

331
                // We use this item to support dragging since dragging a MapQuickItem just doesn't seem to work
Don Gagne's avatar
Don Gagne committed
332 333 334 335
                Rectangle {
                    id:             itemDragger
                    x:              missionItemIndicator ? (missionItemIndicator.x + missionItemIndicator.anchorPoint.x - (itemDragger.width / 2)) : 100
                    y:              missionItemIndicator ? (missionItemIndicator.y + missionItemIndicator.anchorPoint.y - (itemDragger.height / 2)) : 100
336 337
                    width:          ScreenTools.defaultFontPixelHeight * 2
                    height:         ScreenTools.defaultFontPixelHeight * 2
Don Gagne's avatar
Don Gagne committed
338 339 340
                    color:          "transparent"
                    visible:        false
                    z:              QGroundControl.zOrderMapItems + 1    // Above item icons
341 342 343

                    property var    missionItem
                    property var    missionItemIndicator
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
                    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)
                            coordinate.altitude = itemDragger.missionItem.coordinate.altitude
                            itemDragger.preventCoordinateBindingLoop = true
                            itemDragger.missionItem.coordinate = coordinate
                            itemDragger.preventCoordinateBindingLoop = false
                        }
                    }
Don Gagne's avatar
Don Gagne committed
359

360
                    function clearItem() {
Don Gagne's avatar
Don Gagne committed
361 362 363 364 365
                        itemDragger.visible = false
                        itemDragger.missionItem = undefined
                        itemDragger.missionItemIndicator = undefined
                    }

366 367 368 369 370 371 372 373
                    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
374 375 376 377
                        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
378
                    }
379
                }
380

381
                // Add the complex mission item polygon to the map
382
                MapItemView {
383 384
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined

Don Gagne's avatar
Don Gagne committed
385
                    delegate: MapPolygon {
386 387 388 389 390 391
                        color:      'green'
                        path:       object.polygonPath
                        opacity:    0.5
                    }
                }

392 393
                // Add the complex mission item grid to the map
                MapItemView {
394
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
395 396 397

                    delegate: MapPolyline {
                        line.color: "white"
398
                        line.width: 2
399 400 401 402
                        path:       object.gridPoints
                    }
                }

403 404
                // Add the complex mission item exit coordinates
                MapItemView {
405
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
406 407 408 409 410 411 412 413 414 415 416 417 418
                    delegate:   exitCoordinateComponent
                }

                Component {
                    id: exitCoordinateComponent

                    MissionItemIndicator {
                        coordinate:     object.exitCoordinate
                        z:              QGroundControl.zOrderMapItems
                        missionItem:    object
                        sequenceNumber: object.lastSequenceNumber
                        visible:        object.specifiesCoordinate
                    }
Don Gagne's avatar
Don Gagne committed
419 420
                }

421 422
                // Add the simple mission items to the map
                MapItemView {
423
                    model:      _editingLayer == _layerMission ? missionController.visualItems : undefined
424 425 426
                    delegate:   missionItemComponent
                }

Don Gagne's avatar
Don Gagne committed
427
                Component {
428
                    id: missionItemComponent
Don Gagne's avatar
Don Gagne committed
429 430 431 432

                    MissionItemIndicator {
                        id:             itemIndicator
                        coordinate:     object.coordinate
433
                        visible:        object.specifiesCoordinate
Don Gagne's avatar
Don Gagne committed
434
                        z:              QGroundControl.zOrderMapItems
435
                        missionItem:    object
436
                        sequenceNumber: object.sequenceNumber
Don Gagne's avatar
Don Gagne committed
437

438 439 440
                        //-- 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
441 442
                        onClicked: setCurrentItem(object.sequenceNumber)

443 444
                        function updateItemIndicator() {
                            if (object.isCurrentItem && itemIndicator.visible && object.specifiesCoordinate && object.isSimpleItem) {
445 446 447 448
                                // Setup our drag item
                                itemDragger.visible = true
                                itemDragger.missionItem = Qt.binding(function() { return object })
                                itemDragger.missionItemIndicator = Qt.binding(function() { return itemIndicator })
Don Gagne's avatar
Don Gagne committed
449 450 451
                            }
                        }

452 453 454
                        Connections {
                            target: object

455 456
                            onIsCurrentItemChanged:         updateItemIndicator()
                            onSpecifiesCoordinateChanged:   updateItemIndicator()
457 458
                        }

Don Gagne's avatar
Don Gagne committed
459 460 461 462 463 464 465 466 467
                        // These are the non-coordinate child mission items attached to this item
                        Row {
                            anchors.top:    parent.top
                            anchors.left:   parent.right

                            Repeater {
                                model: object.childItems

                                delegate: MissionItemIndexLabel {
468
                                    label:          object.abbreviation
Don Gagne's avatar
Don Gagne committed
469 470 471 472 473 474 475 476
                                    isCurrentItem:  object.isCurrentItem
                                    z:              2

                                    onClicked: setCurrentItem(object.sequenceNumber)
                                }
                            }
                        }
                    }
477 478 479
                }

                // Add lines between waypoints
480
                MissionLineView {
481
                    model:      _editingLayer == _layerMission ? missionController.waypointLines : undefined
Don Gagne's avatar
Don Gagne committed
482 483
                }

Don Gagne's avatar
Don Gagne committed
484 485
                // Add the vehicles to the map
                MapItemView {
486
                    model: QGroundControl.multiVehicleManager.vehicles
Don Gagne's avatar
Don Gagne committed
487 488 489 490 491 492 493 494 495 496
                    delegate:
                        VehicleMapItem {
                                vehicle:        object
                                coordinate:     object.coordinate
                                isSatellite:    editorMap.isSatelliteMap
                                size:           ScreenTools.defaultFontPixelHeight * 5
                                z:              QGroundControl.zOrderMapItems - 1
                        }
                }

Don Gagne's avatar
Don Gagne committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
                // Mission/GeoFence selector
                Item {
                    id:                 planElementSelector
                    anchors.topMargin:  parent.height - ScreenTools.availableHeight + _margin
                    anchors.top:        parent.top
                    anchors.leftMargin: parent.width - _rightPanelWidth
                    anchors.left:       parent.left
                    width:              planElementSelectorRow.width
                    height:             geoFenceController.fenceSupported ? planElementSelectorRow.height : 0
                    visible:            geoFenceController.fenceSupported

                    ExclusiveGroup {
                        id: planElementSelectorGroup
                        onCurrentChanged: {
                            var layerIsMission = current == planElementMission
                            _editingLayer = layerIsMission ? _layerMission : _layerGeoFence
                            _syncDropDownController = layerIsMission ? missionController : geoFenceController
                        }
                    }

                    Row {
                        id:     planElementSelectorRow
                        spacing: _horizontalMargin

                        QGCRadioButton {
                            id:             planElementMission
                            text:           qsTr("Mission")
                            checked:        true
                            exclusiveGroup: planElementSelectorGroup
                            color:          mapPal.text
                        }

                        QGCRadioButton {
                            id:             planElementGeoFence
                            text:           qsTr("GeoFence")
                            exclusiveGroup: planElementSelectorGroup
                            color:          mapPal.text
                        }
                    }
                }

538
                // Mission Item Editor
Don Gagne's avatar
Don Gagne committed
539
                Item {
Don Gagne's avatar
Don Gagne committed
540 541 542 543 544 545 546 547 548
                    id:                 missionItemEditor
                    anchors.topMargin:  _margin
                    anchors.top:        planElementSelector.bottom
                    anchors.bottom:     parent.bottom
                    anchors.right:      parent.right
                    width:              _rightPanelWidth
                    opacity:            _rightPanelOpacity
                    z:                  QGroundControl.zOrderTopMost
                    visible:            _editingLayer == _layerMission
549

Don Gagne's avatar
Don Gagne committed
550 551 552 553 554 555 556
                    MouseArea {
                         // 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:       editorListView
                         onWheel:            wheel.accepted = true
                     }

557
                    ListView {
558 559 560 561
                        id:             editorListView
                        anchors.left:   parent.left
                        anchors.right:  parent.right
                        anchors.top:    parent.top
562
                        height:         parent.height
563 564
                        spacing:        _margin / 2
                        orientation:    ListView.Vertical
565
                        model:          missionController.visualItems
566
                        cacheBuffer:    height * 2
567
                        clip:           true
568
                        currentIndex:   _currentMissionIndex
569 570
                        highlightMoveDuration: 250

571
                        delegate: MissionItemEditor {
572 573
                            missionItem:    object
                            width:          parent.width
574
                            readOnly:       false
575 576 577 578

                            onClicked:  setCurrentItem(object.sequenceNumber)

                            onRemove: {
579
                                itemDragger.clearItem()
580
                                missionController.removeMissionItem(index)
581
                                editorMap.polygonDraw.cancelPolygonEdit()
582 583
                            }

584
                            onInsert: {
585
                                var sequenceNumber = missionController.insertSimpleMissionItem(editorMap.center, insertAfterIndex)
586 587 588
                                setCurrentItem(sequenceNumber)
                            }

589
                            onMoveHomeToMapCenter: _visualItems.get(0).coordinate = editorMap.center
590
                        }
591 592 593
                    } // ListView
                } // Item - Mission Item editor

594 595
                // GeoFence Editor
                Loader {
Don Gagne's avatar
Don Gagne committed
596 597
                    anchors.topMargin:  _margin
                    anchors.top:        planElementSelector.bottom
598 599 600 601 602 603 604 605 606 607 608 609 610
                    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
611
                    path:           geoFenceController.polygonSupported ? geoFenceController.polygon.path : undefined
612
                    z:              QGroundControl.zOrderMapItems
613 614 615 616 617 618 619 620
                }

                // GeoFence circle
                MapCircle {
                    border.color:   "#80FF0000"
                    border.width:   3
                    center:         missionController.plannedHomePosition
                    radius:         geoFenceController.circleSupported ? geoFenceController.circleRadius : 0
621
                    z:              QGroundControl.zOrderMapItems
622 623 624 625 626 627
                }

                // GeoFence breach return point
                MapQuickItem {
                    anchorPoint:    Qt.point(sourceItem.width / 2, sourceItem.height / 2)
                    coordinate:     geoFenceController.breachReturnPoint
628 629
                    visible:        geoFenceController.breachReturnSupported
                    sourceItem:     MissionItemIndexLabel { label: "F" }
630 631 632 633 634 635 636 637
                    z:              QGroundControl.zOrderMapItems

                    Connections {
                        target: geoFenceController
                        onBreachReturnPointChanged: console.log("breachreturn changed inside", geoFenceController.breachReturnPoint)
                    }

                    onCoordinateChanged: console.log("MqpQuickItem coodinateChanged", coordinate)
638 639
                }

640 641 642 643 644 645 646 647 648 649 650
                //-- Dismiss Drop Down (if any)
                MouseArea {
                    anchors.fill:   parent
                    enabled:        _dropButtonsExclusiveGroup.current != null
                    onClicked: {
                        if(_dropButtonsExclusiveGroup.current)
                            _dropButtonsExclusiveGroup.current.checked = false
                        _dropButtonsExclusiveGroup.current = null
                    }
                }

651 652 653 654 655 656 657 658 659 660
                QGCLabel {
                    id:         planLabel
                    text:       qsTr("Plan")
                    color:      mapPal.text
                    visible:    !ScreenTools.isShortScreen
                    anchors.topMargin:          _toolButtonTopMargin
                    anchors.horizontalCenter:   toolColumn.horizontalCenter
                    anchors.top:                parent.top
                }

661 662
                //-- Vertical Tool Buttons
                Column {
663
                    id:                 toolColumn
664 665
                    anchors.topMargin:  ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2
                    anchors.leftMargin: ScreenTools.defaultFontPixelHeight
666
                    anchors.left:       parent.left
667
                    anchors.top:        ScreenTools.isShortScreen ? parent.top : planLabel.bottom
668
                    spacing:            ScreenTools.defaultFontPixelHeight
Don Gagne's avatar
Don Gagne committed
669
                    z:                  QGroundControl.zOrderWidgets
670 671

                    RoundButton {
672 673 674
                        id:             addMissionItemsButton
                        buttonImage:    "/qmlimages/MapAddMission.svg"
                        lightBorders:   _lightWidgetBorders
675
                        visible:        _editingLayer == _layerMission
676 677
                    }

678
                    RoundButton {
679 680 681
                        id:             addShapeButton
                        buttonImage:    "/qmlimages/MapDrawShape.svg"
                        lightBorders:   _lightWidgetBorders
682
                        visible:        _editingLayer == _layerMission
683 684 685 686 687 688

                        onClicked: {
                            var coordinate = editorMap.center
                            coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                            coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                            coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
689
                            var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count)
690
                            setCurrentItem(sequenceNumber)
691
                            checked = false
692
                            addMissionItemsButton.checked = false
693 694 695
                        }
                    }

696 697 698
                    DropButton {
                        id:                 syncButton
                        dropDirection:      dropRight
699
                        buttonImage:        _syncDropDownController.dirty ? "/qmlimages/MapSyncChanged.svg" : "/qmlimages/MapSync.svg"
700 701 702
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
                        dropDownComponent:  syncDropDownComponent
703 704
                        enabled:            !_syncDropDownController.syncInProgress
                        rotateImage:        _syncDropDownController.syncInProgress
705
                        lightBorders:       _lightWidgetBorders
706 707
                    }

708 709 710 711 712 713
                    DropButton {
                        id:                 centerMapButton
                        dropDirection:      dropRight
                        buttonImage:        "/qmlimages/MapCenter.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
714
                        lightBorders:       _lightWidgetBorders
715

716 717
                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
718
                                spacing: ScreenTools.defaultFontPixelWidth * 0.5
719
                                QGCLabel { text: qsTr("Center map:") }
720 721 722
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    QGCButton {
723
                                        text: qsTr("Home")
dogmaphobic's avatar
dogmaphobic committed
724
                                        width:  ScreenTools.defaultFontPixelWidth * 10
725 726
                                        onClicked: {
                                            centerMapButton.hideDropDown()
727
                                            editorMap.center = missionController.visualItems.get(0).coordinate
728
                                        }
729
                                    }
730
                                    QGCButton {
731
                                        text: qsTr("Mission")
dogmaphobic's avatar
dogmaphobic committed
732
                                        width:  ScreenTools.defaultFontPixelWidth * 10
733 734 735 736
                                        onClicked: {
                                            centerMapButton.hideDropDown()
                                            fitViewportToMissionItems()
                                        }
737
                                    }
738
                                    QGCButton {
739
                                        text:       qsTr("Vehicle")
dogmaphobic's avatar
dogmaphobic committed
740
                                        width:      ScreenTools.defaultFontPixelWidth * 10
741
                                        enabled:    activeVehicle && activeVehicle.latitude != 0 && activeVehicle.longitude != 0
742
                                        property var activeVehicle: _activeVehicle
743 744
                                        onClicked: {
                                            centerMapButton.hideDropDown()
745
                                            editorMap.center = activeVehicle.coordinate
746
                                        }
747 748 749 750 751 752
                                    }
                                }
                            }
                        }
                    }

753 754 755 756 757 758
                    DropButton {
                        id:                 mapTypeButton
                        dropDirection:      dropRight
                        buttonImage:        "/qmlimages/MapType.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
759
                        lightBorders:       _lightWidgetBorders
760 761 762

                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
763
                                spacing: _margin
764
                                QGCLabel { text: qsTr("Map type:") }
765 766 767 768
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    Repeater {
                                        model: QGroundControl.flightMapSettings.mapTypes
769

770 771
                                        QGCButton {
                                            checkable:      true
772
                                            checked:        QGroundControl.flightMapSettings.mapType === text
773 774 775
                                            text:           modelData
                                            exclusiveGroup: _mapTypeButtonsExclusiveGroup
                                            onClicked: {
776
                                                QGroundControl.flightMapSettings.mapType = text
777 778 779
                                                checked = true
                                                mapTypeButton.hideDropDown()
                                            }
780 781 782 783 784 785 786
                                        }
                                    }
                                }
                            }
                        }
                    }

787 788
                    //-- Zoom Map In
                    RoundButton {
789 790 791 792 793
                        id:             mapZoomPlus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomPlus.svg"
                        lightBorders:   _lightWidgetBorders

794 795 796 797 798 799 800 801 802
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel += 0.5
                            checked = false
                        }
                    }

                    //-- Zoom Map Out
                    RoundButton {
803 804 805 806
                        id:             mapZoomMinus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomMinus.svg"
                        lightBorders:   _lightWidgetBorders
807 808 809 810 811 812
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel -= 0.5
                            checked = false
                        }
                    }
813
                }
814

815 816 817 818 819 820 821 822 823
                MapScale {
                    anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
                    anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
                    anchors.left:       parent.left
                    z:                  QGroundControl.zOrderWidgets
                    mapControl:         editorMap
                    visible:            !ScreenTools.isTinyScreen
                }

824
                MissionItemStatus {
825 826 827 828 829 830 831 832 833 834 835 836 837
                    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
                    missionMaxTelemetry:    missionController.missionMaxTelemetry
                    cruiseDistance:         missionController.cruiseDistance
                    hoverDistance:          missionController.hoverDistance
                    visible:                _editingLayer == _layerMission && !ScreenTools.isShortScreen
838
                }
839
            } // FlightMap
Don Gagne's avatar
Don Gagne committed
840 841
        } // Item - split view container
    } // QGCViewPanel
842

843 844 845 846
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
847
            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?")
848 849
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
850
                _syncDropDownController.loadFromVehicle()
851 852 853 854 855 856 857 858
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
859
            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?")
860 861
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
862
                _syncDropDownController.loadFromSelectedFile()
863 864 865 866
            }
        }
    }

867 868 869
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
Don Gagne's avatar
Don Gagne committed
870
            message: qsTr("Are you sure you want to remove all items?")
871 872
            function accept() {
                itemDragger.clearItem()
Don Gagne's avatar
Don Gagne committed
873
                _syncDropDownController.removeAll()
874 875 876 877 878
                hideDialog()
            }
        }
    }

879 880
    Component {
        id: syncDropDownComponent
881

882 883 884
        Column {
            id:         columnHolder
            spacing:    _margin
885

Don Gagne's avatar
Don Gagne committed
886 887
            property string _overwriteText: (_editingLayer == _layerMission) ? qsTr("Mission overwrite") : qsTr("GeoFence overwrite")

888
            QGCLabel {
dogmaphobic's avatar
dogmaphobic committed
889
                width:      sendSaveGrid.width
890
                wrapMode:   Text.WordWrap
891
                text:       _syncDropDownController.dirty ?
Don Gagne's avatar
Don Gagne committed
892
                                qsTr("You have unsaved changes. You should send to your vehicle, or save to a file:") :
893
                                qsTr("Sync:")
894
            }
895

dogmaphobic's avatar
dogmaphobic committed
896 897 898 899 900 901
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
902

903
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
904 905
                    text:               qsTr("Send To Vehicle")
                    Layout.fillWidth:   true
906
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
907 908
                    onClicked: {
                        syncButton.hideDropDown()
909
                        _syncDropDownController.sendToVehicle()
910 911
                    }
                }
912

913
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
914 915
                    text:               qsTr("Load From Vehicle")
                    Layout.fillWidth:   true
916
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
917 918
                    onClicked: {
                        syncButton.hideDropDown()
919
                        if (_syncDropDownController.dirty) {
920
                            qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
921
                        } else {
922
                            _syncDropDownController.loadFromVehicle()
923
                        }
924 925
                    }
                }
926

927
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
928 929
                    text:               qsTr("Save To File...")
                    Layout.fillWidth:   true
930
                    enabled:            !_syncDropDownController.syncInProgress
931 932
                    onClicked: {
                        syncButton.hideDropDown()
933
                        _syncDropDownController.saveToSelectedFile()
934 935
                    }
                }
936

937
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
938 939
                    text:               qsTr("Load From File...")
                    Layout.fillWidth:   true
940
                    enabled:            !_syncDropDownController.syncInProgress
941 942
                    onClicked: {
                        syncButton.hideDropDown()
943
                        if (_syncDropDownController.dirty) {
944
                            qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
945
                        } else {
946
                            _syncDropDownController.loadFromSelectedFile()
947
                        }
948 949
                    }
                }
950

dogmaphobic's avatar
dogmaphobic committed
951 952 953 954 955
                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        syncButton.hideDropDown()
956
                        _syncDropDownController.removeAll()
957
                        qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
dogmaphobic's avatar
dogmaphobic committed
958
                    }
959 960
                }
            }
961 962
        }
    }
Don Gagne's avatar
Don Gagne committed
963
} // QGCVIew