MissionEditor.qml 39.3 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:         _root

31
    viewPanel:          panel
Don Gagne's avatar
Don Gagne committed
32

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

36 37 38 39 40 41 42 43
    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)
44
    readonly property var       _defaultVehicleCoordinate:   QtPositioning.coordinate(37.803784, -122.462276)
45

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

53 54 55 56 57 58 59
    /// 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

60
    onActiveVehiclePositionChanged: updateMapToVehiclePosition()
61

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

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

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

79 80 81 82 83 84 85 86 87 88
    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
    }

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

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

103
                if (missionItem.specifiesCoordinate && !missionItem.isStandaloneCoordinate) {
104 105 106 107 108 109 110 111
                    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)
                }
112
            }
113
            editorMap.visibleRegion = QtPositioning.rectangle(QtPositioning.coordinate(north - 90.0, west - 180.0), QtPositioning.coordinate(south - 90.0, east - 180.0))
114 115 116
        }
    }

117
    MissionController {
118
        id: missionController
119

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

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
        function loadFromSelectedFile() {
            if (ScreenTools.isMobile) {
                _root.showDialog(mobileFilePicker, qsTr("Select Mission File"), _root.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
            } else {
                missionController.loadFromFilePicker()
                fitViewportToMissionItems()
                _currentMissionItem = _visualItems.get(0)
            }
        }

        function saveToSelectedFile() {
            if (ScreenTools.isMobile) {
                _root.showDialog(mobileFileSaver, qsTr("Save Mission File"), _root.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
            } else {
                missionController.saveToFilePicker()
            }
        }

        onVisualItemsChanged: {
            itemDragger.clearItem()
        }
146

147 148 149 150 151
        onNewItemsFromVehicle: {
            fitViewportToMissionItems()
            _currentMissionItem = _visualItems.get(0)
        }
    }
152

153 154
    GeoFenceController {
        id: geoFenceController
155

156
        Component.onCompleted: start(true /* editMode */)
157
    }
158

159
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
160

161 162 163 164 165 166
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

    ExclusiveGroup {
        id: _dropButtonsExclusiveGroup
167 168
    }

169
    function setCurrentItem(sequenceNumber) {
Don Gagne's avatar
Don Gagne committed
170
        _currentMissionItem = undefined
171
        for (var i=0; i<_visualItems.count; i++) {
172 173 174
            var visualItem = _visualItems.get(i)
            if (visualItem.sequenceNumber == sequenceNumber) {
                _currentMissionItem = visualItem
Don Gagne's avatar
Don Gagne committed
175
                _currentMissionItem.isCurrentItem = true
176
                _currentMissionIndex = i
Don Gagne's avatar
Don Gagne committed
177
            } else {
178
                visualItem.isCurrentItem = false
Don Gagne's avatar
Don Gagne committed
179
            }
180 181 182
        }
    }

183 184
    property int _moveDialogMissionItemIndex

185 186 187
    Component {
        id: mobileFilePicker

Don Gagne's avatar
Don Gagne committed
188
        QGCMobileFileDialog {
189 190 191
            openDialog:         true
            fileExtension:      QGroundControl.missionFileExtension
            onFilenameReturned: _syncDropDownController.loadFromfile(filename)
192 193 194 195 196 197
        }
    }

    Component {
        id: mobileFileSaver

Don Gagne's avatar
Don Gagne committed
198
        QGCMobileFileDialog {
199 200 201
            openDialog:         false
            fileExtension:      QGroundControl.missionFileExtension
            onFilenameReturned: _syncDropDownController.saveToFile()
202 203 204
        }
    }

205 206 207 208 209 210 211 212 213 214
    Component {
        id: moveDialog

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

                if (toIndex == 0) {
                    toIndex = 1
                }
215
                missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
216 217 218 219 220 221 222 223 224 225 226 227
                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
228
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
229 230 231 232
                }

                QGCComboBox {
                    id:             toCombo
233
                    model:          _visualItems.count
234 235 236 237 238 239
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
240 241
    QGCViewPanel {
        id:             panel
242 243 244 245
        height:         ScreenTools.availableHeight
        anchors.bottom: parent.bottom
        anchors.left:   parent.left
        anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
246

Don Gagne's avatar
Don Gagne committed
247
        Item {
Don Gagne's avatar
Don Gagne committed
248 249
            anchors.fill: parent

Don Gagne's avatar
Don Gagne committed
250 251
            FlightMap {
                id:             editorMap
252 253 254 255
                height:         _root.height
                anchors.bottom: parent.bottom
                anchors.left:   parent.left
                anchors.right:  parent.right
Don Gagne's avatar
Don Gagne committed
256
                mapName:        "MissionEditor"
257

258 259
                readonly property real animationDuration: 500

260 261 262
                // Initial map position duplicates Fly view position
                Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition

263 264 265 266 267 268 269
                Behavior on zoomLevel {
                    NumberAnimation {
                        duration:       editorMap.animationDuration
                        easing.type:    Easing.InOutQuad
                    }
                }

270 271
                QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

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

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

                    property var    missionItem
                    property var    missionItemIndicator
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
                    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
330

331
                    function clearItem() {
Don Gagne's avatar
Don Gagne committed
332 333 334 335 336
                        itemDragger.visible = false
                        itemDragger.missionItem = undefined
                        itemDragger.missionItemIndicator = undefined
                    }

337 338 339 340 341 342 343 344
                    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
345 346 347 348
                        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
349
                    }
350
                }
351

352
                // Add the complex mission item polygon to the map
353
                MapItemView {
354 355
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined

Don Gagne's avatar
Don Gagne committed
356
                    delegate: MapPolygon {
357 358 359 360 361 362
                        color:      'green'
                        path:       object.polygonPath
                        opacity:    0.5
                    }
                }

363 364
                // Add the complex mission item grid to the map
                MapItemView {
365
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
366 367 368

                    delegate: MapPolyline {
                        line.color: "white"
369
                        line.width: 2
370 371 372 373
                        path:       object.gridPoints
                    }
                }

374 375
                // Add the complex mission item exit coordinates
                MapItemView {
376
                    model: _editingLayer == _layerMission ? missionController.complexVisualItems : undefined
377 378 379 380 381 382 383 384 385 386 387 388 389
                    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
390 391
                }

392 393
                // Add the simple mission items to the map
                MapItemView {
394
                    model:      _editingLayer == _layerMission ? missionController.visualItems : undefined
395 396 397
                    delegate:   missionItemComponent
                }

Don Gagne's avatar
Don Gagne committed
398
                Component {
399
                    id: missionItemComponent
Don Gagne's avatar
Don Gagne committed
400 401 402 403

                    MissionItemIndicator {
                        id:             itemIndicator
                        coordinate:     object.coordinate
404
                        visible:        object.specifiesCoordinate
Don Gagne's avatar
Don Gagne committed
405
                        z:              QGroundControl.zOrderMapItems
406
                        missionItem:    object
407
                        sequenceNumber: object.sequenceNumber
Don Gagne's avatar
Don Gagne committed
408

409 410 411
                        //-- 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
412 413
                        onClicked: setCurrentItem(object.sequenceNumber)

414 415
                        function updateItemIndicator() {
                            if (object.isCurrentItem && itemIndicator.visible && object.specifiesCoordinate && object.isSimpleItem) {
416 417 418 419
                                // 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
420 421 422
                            }
                        }

423 424 425
                        Connections {
                            target: object

426 427
                            onIsCurrentItemChanged:         updateItemIndicator()
                            onSpecifiesCoordinateChanged:   updateItemIndicator()
428 429
                        }

Don Gagne's avatar
Don Gagne committed
430 431 432 433 434 435 436 437 438
                        // 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 {
439
                                    label:          object.abbreviation
Don Gagne's avatar
Don Gagne committed
440 441 442 443 444 445 446 447
                                    isCurrentItem:  object.isCurrentItem
                                    z:              2

                                    onClicked: setCurrentItem(object.sequenceNumber)
                                }
                            }
                        }
                    }
448 449 450
                }

                // Add lines between waypoints
451
                MissionLineView {
452
                    model:      _editingLayer == _layerMission ? missionController.waypointLines : undefined
Don Gagne's avatar
Don Gagne committed
453 454
                }

Don Gagne's avatar
Don Gagne committed
455 456
                // Add the vehicles to the map
                MapItemView {
457
                    model: QGroundControl.multiVehicleManager.vehicles
Don Gagne's avatar
Don Gagne committed
458 459 460 461 462 463 464 465 466 467
                    delegate:
                        VehicleMapItem {
                                vehicle:        object
                                coordinate:     object.coordinate
                                isSatellite:    editorMap.isSatelliteMap
                                size:           ScreenTools.defaultFontPixelHeight * 5
                                z:              QGroundControl.zOrderMapItems - 1
                        }
                }

468
                // Mission Item Editor
Don Gagne's avatar
Don Gagne committed
469
                Item {
470
                    id:             missionItemEditor
471
                    height:         ScreenTools.availableHeight
472 473 474 475
                    anchors.bottom: parent.bottom
                    anchors.right:  parent.right
                    width:          _rightPanelWidth
                    opacity:        _rightPanelOpacity
Gus Grubba's avatar
Gus Grubba committed
476
                    z:              QGroundControl.zOrderTopMost
477
                    visible:        _editingLayer == _layerMission
478

Don Gagne's avatar
Don Gagne committed
479 480 481 482 483 484 485
                    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
                     }

486
                    ListView {
487 488 489 490
                        id:             editorListView
                        anchors.left:   parent.left
                        anchors.right:  parent.right
                        anchors.top:    parent.top
491
                        height:         parent.height
492 493
                        spacing:        _margin / 2
                        orientation:    ListView.Vertical
494
                        model:          missionController.visualItems
495
                        cacheBuffer:    height * 2
496
                        clip:           true
497
                        currentIndex:   _currentMissionIndex
498 499
                        highlightMoveDuration: 250

500
                        delegate: MissionItemEditor {
501 502
                            missionItem:    object
                            width:          parent.width
Don Gagne's avatar
Don Gagne committed
503
                            qgcView:        _root
504
                            readOnly:       false
505 506 507 508

                            onClicked:  setCurrentItem(object.sequenceNumber)

                            onRemove: {
509
                                itemDragger.clearItem()
510
                                missionController.removeMissionItem(index)
511 512
                            }

513
                            onInsert: {
514
                                var sequenceNumber = missionController.insertSimpleMissionItem(editorMap.center, insertAfterIndex)
515 516 517
                                setCurrentItem(sequenceNumber)
                            }

518
                            onMoveHomeToMapCenter: missionController.visualItems.get(0).coordinate = editorMap.center
519
                        }
520 521 522
                    } // ListView
                } // Item - Mission Item editor

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
                // GeoFence Editor
                Loader {
                    anchors.topMargin:  parent.height - ScreenTools.availableHeight
                    anchors.top:        parent.top
                    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
                    path:           geoFenceController.polygon.path
                }

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

552 553 554 555 556 557 558 559 560 561 562
                //-- Dismiss Drop Down (if any)
                MouseArea {
                    anchors.fill:   parent
                    enabled:        _dropButtonsExclusiveGroup.current != null
                    onClicked: {
                        if(_dropButtonsExclusiveGroup.current)
                            _dropButtonsExclusiveGroup.current.checked = false
                        _dropButtonsExclusiveGroup.current = null
                    }
                }

563 564 565 566 567 568 569 570 571 572
                QGCLabel {
                    id:         planLabel
                    text:       qsTr("Plan")
                    color:      mapPal.text
                    visible:    !ScreenTools.isShortScreen
                    anchors.topMargin:          _toolButtonTopMargin
                    anchors.horizontalCenter:   toolColumn.horizontalCenter
                    anchors.top:                parent.top
                }

573 574
                //-- Vertical Tool Buttons
                Column {
575
                    id:                 toolColumn
576 577
                    anchors.topMargin:  ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2
                    anchors.leftMargin: ScreenTools.defaultFontPixelHeight
578
                    anchors.left:       parent.left
579
                    anchors.top:        ScreenTools.isShortScreen ? parent.top : planLabel.bottom
580
                    spacing:            ScreenTools.defaultFontPixelHeight
Don Gagne's avatar
Don Gagne committed
581
                    z:                  QGroundControl.zOrderWidgets
582

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
                    DropButton {
                        id:                 layerButton
                        dropDirection:      dropRight
                        //buttonImage:        "/qmlimages/MapCenter.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
                        lightBorders:       _lightWidgetBorders

                        dropDownComponent: Component {
                            Column {
                                spacing: ScreenTools.defaultFontPixelWidth * 0.5

                                QGCLabel { text: qsTr("Editing Layer:") }

                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth

                                    QGCButton {
                                        text: qsTr("Mission")

                                        onClicked: {
                                            layerButton.hideDropDown()
                                            _editingLayer = _layerMission
                                            _syncDropDownController = missionController
                                        }
                                    }

                                    QGCButton {
                                        text: qsTr("GeoFence")

                                        onClicked: {
                                            layerButton.hideDropDown()
                                            _editingLayer = _layerGeoFence
                                            _syncDropDownController = geoFenceController
                                        }
                                    }
                                }
                            }
                        }
                    }

624
                    RoundButton {
625 626 627
                        id:             addMissionItemsButton
                        buttonImage:    "/qmlimages/MapAddMission.svg"
                        lightBorders:   _lightWidgetBorders
628
                        visible:        _editingLayer == _layerMission
629 630
                    }

631
                    RoundButton {
632 633 634
                        id:             addShapeButton
                        buttonImage:    "/qmlimages/MapDrawShape.svg"
                        lightBorders:   _lightWidgetBorders
635
                        visible:        _editingLayer == _layerMission
636 637 638 639 640 641

                        onClicked: {
                            var coordinate = editorMap.center
                            coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                            coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                            coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
642
                            var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count)
643
                            setCurrentItem(sequenceNumber)
644
                            checked = false
645
                            addMissionItemsButton.checked = false
646 647 648
                        }
                    }

649 650 651
                    DropButton {
                        id:                 syncButton
                        dropDirection:      dropRight
652
                        buttonImage:        _syncDropDownController.dirty ? "/qmlimages/MapSyncChanged.svg" : "/qmlimages/MapSync.svg"
653 654 655
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
                        dropDownComponent:  syncDropDownComponent
656 657
                        enabled:            !_syncDropDownController.syncInProgress
                        rotateImage:        _syncDropDownController.syncInProgress
658
                        lightBorders:       _lightWidgetBorders
659 660
                    }

661 662 663 664 665 666
                    DropButton {
                        id:                 centerMapButton
                        dropDirection:      dropRight
                        buttonImage:        "/qmlimages/MapCenter.svg"
                        viewportMargins:    ScreenTools.defaultFontPixelWidth / 2
                        exclusiveGroup:     _dropButtonsExclusiveGroup
667
                        lightBorders:       _lightWidgetBorders
668

669 670
                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
671
                                spacing: ScreenTools.defaultFontPixelWidth * 0.5
672
                                QGCLabel { text: qsTr("Center map:") }
673 674 675
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    QGCButton {
676
                                        text: qsTr("Home")
dogmaphobic's avatar
dogmaphobic committed
677
                                        width:  ScreenTools.defaultFontPixelWidth * 10
678 679
                                        onClicked: {
                                            centerMapButton.hideDropDown()
680
                                            editorMap.center = missionController.visualItems.get(0).coordinate
681
                                        }
682
                                    }
683
                                    QGCButton {
684
                                        text: qsTr("Mission")
dogmaphobic's avatar
dogmaphobic committed
685
                                        width:  ScreenTools.defaultFontPixelWidth * 10
686 687 688 689
                                        onClicked: {
                                            centerMapButton.hideDropDown()
                                            fitViewportToMissionItems()
                                        }
690
                                    }
691
                                    QGCButton {
692
                                        text:       qsTr("Vehicle")
dogmaphobic's avatar
dogmaphobic committed
693
                                        width:      ScreenTools.defaultFontPixelWidth * 10
694
                                        enabled:    activeVehicle && activeVehicle.latitude != 0 && activeVehicle.longitude != 0
695
                                        property var activeVehicle: _activeVehicle
696 697
                                        onClicked: {
                                            centerMapButton.hideDropDown()
698
                                            editorMap.center = activeVehicle.coordinate
699
                                        }
700 701 702 703 704 705
                                    }
                                }
                            }
                        }
                    }

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

                        dropDownComponent: Component {
                            Column {
dogmaphobic's avatar
dogmaphobic committed
716
                                spacing: _margin
717
                                QGCLabel { text: qsTr("Map type:") }
718 719 720 721
                                Row {
                                    spacing: ScreenTools.defaultFontPixelWidth
                                    Repeater {
                                        model: QGroundControl.flightMapSettings.mapTypes
722

723 724
                                        QGCButton {
                                            checkable:      true
725
                                            checked:        QGroundControl.flightMapSettings.mapType === text
726 727 728
                                            text:           modelData
                                            exclusiveGroup: _mapTypeButtonsExclusiveGroup
                                            onClicked: {
729
                                                QGroundControl.flightMapSettings.mapType = text
730 731 732
                                                checked = true
                                                mapTypeButton.hideDropDown()
                                            }
733 734 735 736 737 738 739
                                        }
                                    }
                                }
                            }
                        }
                    }

740 741
                    //-- Zoom Map In
                    RoundButton {
742 743 744 745 746
                        id:             mapZoomPlus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomPlus.svg"
                        lightBorders:   _lightWidgetBorders

747 748 749 750 751 752 753 754 755
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel += 0.5
                            checked = false
                        }
                    }

                    //-- Zoom Map Out
                    RoundButton {
756 757 758 759
                        id:             mapZoomMinus
                        visible:        !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen
                        buttonImage:    "/qmlimages/ZoomMinus.svg"
                        lightBorders:   _lightWidgetBorders
760 761 762 763 764 765
                        onClicked: {
                            if(editorMap)
                                editorMap.zoomLevel -= 0.5
                            checked = false
                        }
                    }
766
                }
767

768 769 770 771 772 773 774 775 776
                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
                }

777
                MissionItemStatus {
778
                    id:                 waypointValuesDisplay
779
                    anchors.margins:    ScreenTools.defaultFontPixelWidth
780 781
                    anchors.left:       parent.left
                    anchors.bottom:     parent.bottom
782 783
                    z:                  QGroundControl.zOrderTopMost
                    currentMissionItem: _currentMissionItem
784
                    missionItems:       missionController.visualItems
785
                    expandedWidth:      missionItemEditor.x - (ScreenTools.defaultFontPixelWidth * 2)
786 787 788 789 790
                    missionDistance:    missionController.missionDistance
                    missionMaxTelemetry: missionController.missionMaxTelemetry
                    cruiseDistance:     missionController.cruiseDistance
                    hoverDistance:      missionController.hoverDistance
                    visible:            _editingLayer == _layerMission && !ScreenTools.isShortScreen
791
                }
792
            } // FlightMap
Don Gagne's avatar
Don Gagne committed
793 794
        } // Item - split view container
    } // QGCViewPanel
795

796 797 798 799
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
800
            message:   qsTr("You have unsaved/unsent mission changes. Loading the mission from the Vehicle will lose these changes. Are you sure you want to load the mission from the Vehicle?")
801 802
            function accept() {
                hideDialog()
803
                loadFromVehicle()
804 805 806 807 808 809 810 811
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
812
            message:   qsTr("You have unsaved/unsent mission changes. Loading a mission from a file will lose these changes. Are you sure you want to load a mission from a file?")
813 814
            function accept() {
                hideDialog()
815
                loadFromFile()
816 817 818 819
            }
        }
    }

820 821 822
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
823
            message: qsTr("Are you sure you want to delete all mission items?")
824 825
            function accept() {
                itemDragger.clearItem()
826
                missionController.removeAll()
827 828 829 830 831
                hideDialog()
            }
        }
    }

832 833
    Component {
        id: syncDropDownComponent
834

835 836 837
        Column {
            id:         columnHolder
            spacing:    _margin
838

839
            QGCLabel {
dogmaphobic's avatar
dogmaphobic committed
840
                width:      sendSaveGrid.width
841
                wrapMode:   Text.WordWrap
842
                text:       _syncDropDownController.dirty ?
843
                                qsTr("You have unsaved changes to your mission. You should send to your vehicle, or save to a file:") :
844
                                qsTr("Sync:")
845
            }
846

dogmaphobic's avatar
dogmaphobic committed
847 848 849 850 851 852
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
853

854
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
855 856
                    text:               qsTr("Send To Vehicle")
                    Layout.fillWidth:   true
857
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
858 859
                    onClicked: {
                        syncButton.hideDropDown()
860
                        _syncDropDownController.sendToVehicle()
861 862
                    }
                }
863

864
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
865 866
                    text:               qsTr("Load From Vehicle")
                    Layout.fillWidth:   true
867
                    enabled:            _activeVehicle && !_syncDropDownController.syncInProgress
868 869
                    onClicked: {
                        syncButton.hideDropDown()
870
                        if (_syncDropDownController.dirty) {
871
                            _root.showDialog(syncLoadFromVehicleOverwrite, qsTr("Mission overwrite"), _root.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
872
                        } else {
873
                            _syncDropDownController.loadFromVehicle()
874
                        }
875 876
                    }
                }
877

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

888
                QGCButton {
dogmaphobic's avatar
dogmaphobic committed
889 890
                    text:               qsTr("Load From File...")
                    Layout.fillWidth:   true
891
                    enabled:            !_syncDropDownController.syncInProgress
892 893
                    onClicked: {
                        syncButton.hideDropDown()
894
                        if (_syncDropDownController.dirty) {
895
                            _root.showDialog(syncLoadFromFileOverwrite, qsTr("Mission overwrite"), _root.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
896
                        } else {
897
                            _syncDropDownController.loadFromSelectedFile()
898
                        }
899 900
                    }
                }
901

dogmaphobic's avatar
dogmaphobic committed
902 903 904 905 906
                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        syncButton.hideDropDown()
907
                        _syncDropDownController.removeAll()
dogmaphobic's avatar
dogmaphobic committed
908 909
                        _root.showDialog(removeAllPromptDialog, qsTr("Delete all"), _root.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
                    }
910 911
                }
            }
912 913
        }
    }
Don Gagne's avatar
Don Gagne committed
914
} // QGCVIew