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

        onFenceSupportedChanged: {
            if (!fenceSupported && _editingLayer == _layerGeoFence) {
                _editingLayer = _layerMission
            }
        }
162
    }
163

164
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
165

166 167 168 169 170 171
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

    ExclusiveGroup {
        id: _dropButtonsExclusiveGroup
172 173
    }

174
    function setCurrentItem(sequenceNumber) {
175
        editorMap.polygonDraw.cancelPolygonEdit()
Don Gagne's avatar
Don Gagne committed
176
        _currentMissionItem = undefined
177
        for (var i=0; i<_visualItems.count; i++) {
178 179 180
            var visualItem = _visualItems.get(i)
            if (visualItem.sequenceNumber == sequenceNumber) {
                _currentMissionItem = visualItem
Don Gagne's avatar
Don Gagne committed
181
                _currentMissionItem.isCurrentItem = true
182
                _currentMissionIndex = i
Don Gagne's avatar
Don Gagne committed
183
            } else {
184
                visualItem.isCurrentItem = false
Don Gagne's avatar
Don Gagne committed
185
            }
186 187 188
        }
    }

189 190
    property int _moveDialogMissionItemIndex

191 192 193
    Component {
        id: mobileFilePicker

Don Gagne's avatar
Don Gagne committed
194
        QGCMobileFileDialog {
195 196 197
            openDialog:         true
            fileExtension:      QGroundControl.missionFileExtension
            onFilenameReturned: _syncDropDownController.loadFromfile(filename)
198 199 200 201 202 203
        }
    }

    Component {
        id: mobileFileSaver

Don Gagne's avatar
Don Gagne committed
204
        QGCMobileFileDialog {
205 206 207
            openDialog:         false
            fileExtension:      QGroundControl.missionFileExtension
            onFilenameReturned: _syncDropDownController.saveToFile()
208 209 210
        }
    }

211 212 213 214 215 216 217 218 219 220
    Component {
        id: moveDialog

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

                if (toIndex == 0) {
                    toIndex = 1
                }
221
                missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
222 223 224 225 226 227 228 229 230 231 232 233
                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
234
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
235 236 237 238
                }

                QGCComboBox {
                    id:             toCombo
239
                    model:          _visualItems.count
240 241 242 243 244 245
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
246 247
    QGCViewPanel {
        id:             panel
248 249 250 251
        height:         ScreenTools.availableHeight
        anchors.bottom: parent.bottom
        anchors.left:   parent.left
        anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
252

Don Gagne's avatar
Don Gagne committed
253
        Item {
Don Gagne's avatar
Don Gagne committed
254 255
            anchors.fill: parent

Don Gagne's avatar
Don Gagne committed
256 257
            FlightMap {
                id:             editorMap
258
                height:         qgcView.height
259 260 261
                anchors.bottom: parent.bottom
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
262
                mapName:        "MissionEditor"
263

264 265
                readonly property real animationDuration: 500

266 267 268
                // Initial map position duplicates Fly view position
                Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition

269 270 271 272 273 274 275
                Behavior on zoomLevel {
                    NumberAnimation {
                        duration:       editorMap.animationDuration
                        easing.type:    Easing.InOutQuad
                    }
                }

276 277
                QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

Don Gagne's avatar
Don Gagne committed
278
                MouseArea {
279 280
                    //-- 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
281 282
                    anchors.fill: parent
                    onClicked: {
283 284
                        //-- Don't pay attention to items beneath the toolbar.
                        var topLimit = parent.height - ScreenTools.availableHeight
285 286 287 288 289 290 291 292 293 294 295
                        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:
296
                            if (addMissionItemsButton.checked) {
297
                                var sequenceNumber = missionController.insertSimpleMissionItem(coordinate, missionController.visualItems.count)
298 299
                                setCurrentItem(sequenceNumber)
                            }
300 301 302 303
                            break
                        case _layerGeoFence:
                            geoFenceController.breachReturnPoint = coordinate
                            break
304
                        }
Don Gagne's avatar
Don Gagne committed
305
                    }
Don Gagne's avatar
Don Gagne committed
306
                }
Don Gagne's avatar
Don Gagne committed
307

308
                // We use this item to support dragging since dragging a MapQuickItem just doesn't seem to work
Don Gagne's avatar
Don Gagne committed
309 310 311 312
                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
313 314
                    width:          ScreenTools.defaultFontPixelHeight * 2
                    height:         ScreenTools.defaultFontPixelHeight * 2
Don Gagne's avatar
Don Gagne committed
315 316 317
                    color:          "transparent"
                    visible:        false
                    z:              QGroundControl.zOrderMapItems + 1    // Above item icons
318 319 320

                    property var    missionItem
                    property var    missionItemIndicator
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
                    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
336

337
                    function clearItem() {
Don Gagne's avatar
Don Gagne committed
338 339 340 341 342
                        itemDragger.visible = false
                        itemDragger.missionItem = undefined
                        itemDragger.missionItemIndicator = undefined
                    }

343 344 345 346 347 348 349 350
                    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
351 352 353 354
                        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
355
                    }
356
                }
357

358
                // Add the complex mission item polygon to the map
359
                MapItemView {
360 361
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined

Don Gagne's avatar
Don Gagne committed
362
                    delegate: MapPolygon {
363 364 365 366 367 368
                        color:      'green'
                        path:       object.polygonPath
                        opacity:    0.5
                    }
                }

369 370
                // Add the complex mission item grid to the map
                MapItemView {
371
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
372 373 374

                    delegate: MapPolyline {
                        line.color: "white"
375
                        line.width: 2
376 377 378 379
                        path:       object.gridPoints
                    }
                }

380 381
                // Add the complex mission item exit coordinates
                MapItemView {
382
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
383 384 385 386 387 388 389 390 391 392 393 394 395
                    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
396 397
                }

398 399
                // Add the simple mission items to the map
                MapItemView {
400
                    model:      _editingLayer == _layerMission ? missionController.visualItems : undefined
401 402 403
                    delegate:   missionItemComponent
                }

Don Gagne's avatar
Don Gagne committed
404
                Component {
405
                    id: missionItemComponent
Don Gagne's avatar
Don Gagne committed
406 407 408 409

                    MissionItemIndicator {
                        id:             itemIndicator
                        coordinate:     object.coordinate
410
                        visible:        object.specifiesCoordinate
Don Gagne's avatar
Don Gagne committed
411
                        z:              QGroundControl.zOrderMapItems
412
                        missionItem:    object
413
                        sequenceNumber: object.sequenceNumber
Don Gagne's avatar
Don Gagne committed
414

415 416 417
                        //-- 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
418 419
                        onClicked: setCurrentItem(object.sequenceNumber)

420 421
                        function updateItemIndicator() {
                            if (object.isCurrentItem && itemIndicator.visible && object.specifiesCoordinate && object.isSimpleItem) {
422 423 424 425
                                // 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
426 427 428
                            }
                        }

429 430 431
                        Connections {
                            target: object

432 433
                            onIsCurrentItemChanged:         updateItemIndicator()
                            onSpecifiesCoordinateChanged:   updateItemIndicator()
434 435
                        }

Don Gagne's avatar
Don Gagne committed
436 437 438 439 440 441 442 443 444
                        // 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 {
445
                                    label:          object.abbreviation
Don Gagne's avatar
Don Gagne committed
446 447 448 449 450 451 452 453
                                    isCurrentItem:  object.isCurrentItem
                                    z:              2

                                    onClicked: setCurrentItem(object.sequenceNumber)
                                }
                            }
                        }
                    }
454 455 456
                }

                // Add lines between waypoints
457
                MissionLineView {
458
                    model:      _editingLayer == _layerMission ? missionController.waypointLines : undefined
Don Gagne's avatar
Don Gagne committed
459 460
                }

Don Gagne's avatar
Don Gagne committed
461 462
                // Add the vehicles to the map
                MapItemView {
463
                    model: QGroundControl.multiVehicleManager.vehicles
Don Gagne's avatar
Don Gagne committed
464 465 466 467 468 469 470 471 472 473
                    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
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
                // 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
                        }
                    }
                }

515
                // Mission Item Editor
Don Gagne's avatar
Don Gagne committed
516
                Item {
Don Gagne's avatar
Don Gagne committed
517 518 519 520 521 522 523 524 525
                    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
526

Don Gagne's avatar
Don Gagne committed
527 528 529 530 531 532 533
                    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
                     }

534
                    ListView {
535 536 537 538
                        id:             editorListView
                        anchors.left:   parent.left
                        anchors.right:  parent.right
                        anchors.top:    parent.top
539
                        height:         parent.height
540 541
                        spacing:        _margin / 2
                        orientation:    ListView.Vertical
542
                        model:          missionController.visualItems
543
                        cacheBuffer:    height * 2
544
                        clip:           true
545
                        currentIndex:   _currentMissionIndex
546 547
                        highlightMoveDuration: 250

548
                        delegate: MissionItemEditor {
549 550
                            missionItem:    object
                            width:          parent.width
551
                            readOnly:       false
552 553 554 555

                            onClicked:  setCurrentItem(object.sequenceNumber)

                            onRemove: {
556
                                itemDragger.clearItem()
557
                                missionController.removeMissionItem(index)
558
                                editorMap.polygonDraw.cancelPolygonEdit()
559 560
                            }

561
                            onInsert: {
562
                                var sequenceNumber = missionController.insertSimpleMissionItem(editorMap.center, insertAfterIndex)
563 564 565
                                setCurrentItem(sequenceNumber)
                            }

566
                            onMoveHomeToMapCenter: _visualItems.get(0).coordinate = editorMap.center
567
                        }
568 569 570
                    } // ListView
                } // Item - Mission Item editor

571 572
                // GeoFence Editor
                Loader {
Don Gagne's avatar
Don Gagne committed
573 574
                    anchors.topMargin:  _margin
                    anchors.top:        planElementSelector.bottom
575 576 577 578 579 580 581 582 583 584 585 586 587
                    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
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
                    path:           geoFenceController.polygonSupported ? geoFenceController.polygon.path : undefined
                }

                // GeoFence circle
                MapCircle {
                    border.color:   "#80FF0000"
                    border.width:   3
                    center:         missionController.plannedHomePosition
                    radius:         geoFenceController.circleSupported ? geoFenceController.circleRadius : 0
                }

                // GeoFence circle
                MapCircle {
                    border.color:   "#80FF0000"
                    border.width:   3
                    center:         missionController.plannedHomePosition
                    radius:         geoFenceController.circleSupported ? geoFenceController.circleRadius : 0
605 606 607 608 609 610
                }

                // GeoFence breach return point
                MapQuickItem {
                    anchorPoint:    Qt.point(sourceItem.width / 2, sourceItem.height / 2)
                    coordinate:     geoFenceController.breachReturnPoint
611 612
                    visible:        geoFenceController.breachReturnSupported
                    sourceItem:     MissionItemIndexLabel { label: "F" }
613 614
                }

615 616 617 618 619 620 621 622 623 624 625
                //-- Dismiss Drop Down (if any)
                MouseArea {
                    anchors.fill:   parent
                    enabled:        _dropButtonsExclusiveGroup.current != null
                    onClicked: {
                        if(_dropButtonsExclusiveGroup.current)
                            _dropButtonsExclusiveGroup.current.checked = false
                        _dropButtonsExclusiveGroup.current = null
                    }
                }

626 627 628 629 630 631 632 633 634 635
                QGCLabel {
                    id:         planLabel
                    text:       qsTr("Plan")
                    color:      mapPal.text
                    visible:    !ScreenTools.isShortScreen
                    anchors.topMargin:          _toolButtonTopMargin
                    anchors.horizontalCenter:   toolColumn.horizontalCenter
                    anchors.top:                parent.top
                }

636 637
                //-- Vertical Tool Buttons
                Column {
638
                    id:                 toolColumn
639 640
                    anchors.topMargin:  ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2
                    anchors.leftMargin: ScreenTools.defaultFontPixelHeight
641
                    anchors.left:       parent.left
642
                    anchors.top:        ScreenTools.isShortScreen ? parent.top : planLabel.bottom
643
                    spacing:            ScreenTools.defaultFontPixelHeight
Don Gagne's avatar
Don Gagne committed
644
                    z:                  QGroundControl.zOrderWidgets
645 646

                    RoundButton {
647 648 649
                        id:             addMissionItemsButton
                        buttonImage:    "/qmlimages/MapAddMission.svg"
                        lightBorders:   _lightWidgetBorders
650
                        visible:        _editingLayer == _layerMission
651 652
                    }

653
                    RoundButton {
654 655 656
                        id:             addShapeButton
                        buttonImage:    "/qmlimages/MapDrawShape.svg"
                        lightBorders:   _lightWidgetBorders
657
                        visible:        _editingLayer == _layerMission
658 659 660 661 662 663

                        onClicked: {
                            var coordinate = editorMap.center
                            coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                            coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                            coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
664
                            var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count)
665
                            setCurrentItem(sequenceNumber)
666
                            checked = false
667
                            addMissionItemsButton.checked = false
668 669 670
                        }
                    }

671 672 673
                    DropButton {
                        id:                 syncButton
                        dropDirection:      dropRight
674
                        buttonImage:        _syncDropDownController.dirty ? "/qmlimages/MapSyncChanged.svg" : "/qmlimages/MapSync.svg"
675 676 677
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
                        dropDownComponent:  syncDropDownComponent
678 679
                        enabled:            !_syncDropDownController.syncInProgress
                        rotateImage:        _syncDropDownController.syncInProgress
680
                        lightBorders:       _lightWidgetBorders
681 682
                    }

683 684 685 686 687 688
                    DropButton {
                        id:                 centerMapButton
                        dropDirection:      dropRight
                        buttonImage:        "/qmlimages/MapCenter.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
689
                        lightBorders:       _lightWidgetBorders
690

691 692
                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
693
                                spacing: ScreenTools.defaultFontPixelWidth * 0.5
694
                                QGCLabel { text: qsTr("Center map:") }
695 696 697
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    QGCButton {
698
                                        text: qsTr("Home")
dogmaphobic's avatar
dogmaphobic committed
699
                                        width:  ScreenTools.defaultFontPixelWidth * 10
700 701
                                        onClicked: {
                                            centerMapButton.hideDropDown()
702
                                            editorMap.center = missionController.visualItems.get(0).coordinate
703
                                        }
704
                                    }
705
                                    QGCButton {
706
                                        text: qsTr("Mission")
dogmaphobic's avatar
dogmaphobic committed
707
                                        width:  ScreenTools.defaultFontPixelWidth * 10
708 709 710 711
                                        onClicked: {
                                            centerMapButton.hideDropDown()
                                            fitViewportToMissionItems()
                                        }
712
                                    }
713
                                    QGCButton {
714
                                        text:       qsTr("Vehicle")
dogmaphobic's avatar
dogmaphobic committed
715
                                        width:      ScreenTools.defaultFontPixelWidth * 10
716
                                        enabled:    activeVehicle && activeVehicle.latitude != 0 && activeVehicle.longitude != 0
717
                                        property var activeVehicle: _activeVehicle
718 719
                                        onClicked: {
                                            centerMapButton.hideDropDown()
720
                                            editorMap.center = activeVehicle.coordinate
721
                                        }
722 723 724 725 726 727
                                    }
                                }
                            }
                        }
                    }

728 729 730 731 732 733
                    DropButton {
                        id:                 mapTypeButton
                        dropDirection:      dropRight
                        buttonImage:        "/qmlimages/MapType.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
734
                        lightBorders:       _lightWidgetBorders
735 736 737

                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
738
                                spacing: _margin
739
                                QGCLabel { text: qsTr("Map type:") }
740 741 742 743
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    Repeater {
                                        model: QGroundControl.flightMapSettings.mapTypes
744

745 746
                                        QGCButton {
                                            checkable:      true
747
                                            checked:        QGroundControl.flightMapSettings.mapType === text
748 749 750
                                            text:           modelData
                                            exclusiveGroup: _mapTypeButtonsExclusiveGroup
                                            onClicked: {
751
                                                QGroundControl.flightMapSettings.mapType = text
752 753 754
                                                checked = true
                                                mapTypeButton.hideDropDown()
                                            }
755 756 757 758 759 760 761
                                        }
                                    }
                                }
                            }
                        }
                    }

762 763
                    //-- Zoom Map In
                    RoundButton {
764 765 766 767 768
                        id:             mapZoomPlus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomPlus.svg"
                        lightBorders:   _lightWidgetBorders

769 770 771 772 773 774 775 776 777
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel += 0.5
                            checked = false
                        }
                    }

                    //-- Zoom Map Out
                    RoundButton {
778 779 780 781
                        id:             mapZoomMinus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomMinus.svg"
                        lightBorders:   _lightWidgetBorders
782 783 784 785 786 787
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel -= 0.5
                            checked = false
                        }
                    }
788
                }
789

790 791 792 793 794 795 796 797 798
                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
                }

799
                MissionItemStatus {
800 801 802 803 804 805 806 807 808 809 810 811 812
                    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
813
                }
814
            } // FlightMap
Don Gagne's avatar
Don Gagne committed
815 816
        } // Item - split view container
    } // QGCViewPanel
817

818 819 820 821
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
822
            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?")
823 824
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
825
                _syncDropDownController.loadFromVehicle()
826 827 828 829 830 831 832 833
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
834
            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?")
835 836
            function accept() {
                hideDialog()
Don Gagne's avatar
Don Gagne committed
837
                _syncDropDownController.loadFromSelectedFile()
838 839 840 841
            }
        }
    }

842 843 844
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
Don Gagne's avatar
Don Gagne committed
845
            message: qsTr("Are you sure you want to remove all items?")
846 847
            function accept() {
                itemDragger.clearItem()
Don Gagne's avatar
Don Gagne committed
848
                _syncDropDownController.removeAll()
849 850 851 852 853
                hideDialog()
            }
        }
    }

854 855
    Component {
        id: syncDropDownComponent
856

857 858 859
        Column {
            id:         columnHolder
            spacing:    _margin
860

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

863
            QGCLabel {
dogmaphobic's avatar
dogmaphobic committed
864
                width:      sendSaveGrid.width
865
                wrapMode:   Text.WordWrap
866
                text:       _syncDropDownController.dirty ?
Don Gagne's avatar
Don Gagne committed
867
                                qsTr("You have unsaved changes. You should send to your vehicle, or save to a file:") :
868
                                qsTr("Sync:")
869
            }
870

dogmaphobic's avatar
dogmaphobic committed
871 872 873 874 875 876
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
877

878
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
879 880
                    text:               qsTr("Send To Vehicle")
                    Layout.fillWidth:   true
881
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
882 883
                    onClicked: {
                        syncButton.hideDropDown()
884
                        _syncDropDownController.sendToVehicle()
885 886
                    }
                }
887

888
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
889 890
                    text:               qsTr("Load From Vehicle")
                    Layout.fillWidth:   true
891
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
892 893
                    onClicked: {
                        syncButton.hideDropDown()
894
                        if (_syncDropDownController.dirty) {
895
                            qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
896
                        } else {
897
                            _syncDropDownController.loadFromVehicle()
898
                        }
899 900
                    }
                }
901

902
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
903 904
                    text:               qsTr("Save To File...")
                    Layout.fillWidth:   true
905
                    enabled:            !_syncDropDownController.syncInProgress
906 907
                    onClicked: {
                        syncButton.hideDropDown()
908
                        _syncDropDownController.saveToSelectedFile()
909 910
                    }
                }
911

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

dogmaphobic's avatar
dogmaphobic committed
926 927 928 929 930
                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        syncButton.hideDropDown()
931
                        _syncDropDownController.removeAll()
932
                        qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
dogmaphobic's avatar
dogmaphobic committed
933
                    }
934 935
                }
            }
936 937
        }
    }
Don Gagne's avatar
Don Gagne committed
938
} // QGCVIew