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

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

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

30
Item {
31

Gus Grubba's avatar
Gus Grubba committed
32 33
    property bool planControlColapsed: false

34
    readonly property int   _decimalPlaces:             8
Gus Grubba's avatar
Gus Grubba committed
35
    readonly property real  _horizontalMargin:          ScreenTools.defaultFontPixelWidth  * 0.5
36
    readonly property real  _margin:                    ScreenTools.defaultFontPixelHeight * 0.5
Gus Grubba's avatar
Gus Grubba committed
37
    readonly property real  _radius:                    ScreenTools.defaultFontPixelWidth  * 0.5
38
    readonly property real  _rightPanelWidth:           Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30)
Gus Grubba's avatar
Gus Grubba committed
39
    readonly property real  _toolButtonTopMargin:       ScreenTools.defaultFontPixelHeight * 0.5
40 41
    readonly property var   _defaultVehicleCoordinate:  QtPositioning.coordinate(37.803784, -122.462276)
    readonly property bool  _waypointsOnlyMode:         QGroundControl.corePlugin.options.missionWaypointsOnly
42

Gus Grubba's avatar
Gus Grubba committed
43
    property bool   _airspaceEnabled:                    QGroundControl.airmapSupported ? (QGroundControl.settingsManager.airMapSettings.enableAirMap.rawValue && QGroundControl.airspaceManager.connected): false
Gus Grubba's avatar
Gus Grubba committed
44 45 46 47 48 49 50 51
    property var    _missionController:                 _planMasterController.missionController
    property var    _geoFenceController:                _planMasterController.geoFenceController
    property var    _rallyPointController:              _planMasterController.rallyPointController
    property var    _visualItems:                       _missionController.visualItems
    property bool   _lightWidgetBorders:                editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:                false
    property bool   _addROIOnClick:                     false
    property bool   _singleComplexItem:                 _missionController.complexMissionItemNames.length === 1
Gus Grubba's avatar
Gus Grubba committed
52
    property int    _editingLayer:                      bar.currentIndex ? _layers[bar.currentIndex] : _layerMission
Gus Grubba's avatar
Gus Grubba committed
53
    property int    _toolStripBottom:                   toolStrip.height + toolStrip.y
54
    property var    _appSettings:                       QGroundControl.settingsManager.appSettings
55

Gus Grubba's avatar
Gus Grubba committed
56 57
    readonly property var       _layers:                [_layerMission, _layerGeoFence, _layerRallyPoints]

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

63 64
    function addComplexItem(complexItemName) {
        var coordinate = editorMap.center
65
        coordinate.latitude  = coordinate.latitude.toFixed(_decimalPlaces)
66
        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
67
        coordinate.altitude  = coordinate.altitude.toFixed(_decimalPlaces)
68
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
69 70 71
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
72
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
73
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
74 75
    }

76 77
    function insertComplexMissionItemFromKMLOrSHP(complexItemName, file, index) {
        var sequenceNumber = _missionController.insertComplexMissionItemFromKMLOrSHP(complexItemName, file, index)
78 79 80
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
    }

81
    function updateAirspace(reset) {
82 83 84 85
        if(_airspaceEnabled) {
            var coordinateNW = editorMap.toCoordinate(Qt.point(0,0), false /* clipToViewPort */)
            var coordinateSE = editorMap.toCoordinate(Qt.point(width,height), false /* clipToViewPort */)
            if(coordinateNW.isValid && coordinateSE.isValid) {
86
                QGroundControl.airspaceManager.setROI(coordinateNW, coordinateSE, true /*planView*/, reset)
87 88 89 90
            }
        }
    }

91 92 93 94 95
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

96
    MapFitFunctions {
97
        id:                         mapFitFunctions  // The name for this id cannot be changed without breaking references outside of this code. Beware!
98 99
        map:                        editorMap
        usePlannedHomePosition:     true
100
        planMasterController:       _planMasterController
101 102
    }

103
    on_AirspaceEnabledChanged: {
104
        if(QGroundControl.airmapSupported) {
105 106
            if(_airspaceEnabled) {
                planControlColapsed = QGroundControl.airspaceManager.airspaceVisible
107
                updateAirspace(true)
108 109 110
            } else {
                planControlColapsed = false
            }
111
        } else {
Gus Grubba's avatar
Gus Grubba committed
112 113 114 115
            planControlColapsed = false
        }
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
116
    Connections {
Gus Grubba's avatar
Gus Grubba committed
117
        target: _appSettings ? _appSettings.defaultMissionItemAltitude : null
DonLakeFlyer's avatar
DonLakeFlyer committed
118 119
        onRawValueChanged: {
            if (_visualItems.count > 1) {
120
                mainWindow.showComponentDialog(applyNewAltitude, qsTr("Apply new alititude"), mainWindow.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
DonLakeFlyer's avatar
DonLakeFlyer committed
121 122 123 124 125 126 127 128 129 130
            }
        }
    }

    Component {
        id: applyNewAltitude
        QGCViewMessage {
            message:    qsTr("You have changed the default altitude for mission items. Would you like to apply that altitude to all the items in the current mission?")
            function accept() {
                hideDialog()
131
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
132 133 134 135
            }
        }
    }

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    Component {
        id: activeMissionUploadDialogComponent
        QGCViewDialog {
            Column {
                anchors.fill:   parent
                spacing:        ScreenTools.defaultFontPixelHeight
                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("Your vehicle is currently flying a mission. In order to upload a new or modified mission the current mission will be paused.")
                }
                QGCLabel {
                    width:      parent.width
                    wrapMode:   Text.WordWrap
                    text:       qsTr("After the mission is uploaded you can adjust the current waypoint and start the mission.")
                }
                QGCButton {
                    text:       qsTr("Pause and Upload")
                    onClicked: {
Gus Grubba's avatar
Gus Grubba committed
155
                        activeVehicle.flightMode = activeVehicle.pauseFlightMode
156
                        _planMasterController.sendToVehicle()
157
                        hideDialog()
158 159 160 161 162 163
                    }
                }
            }
        }
    }

Gus Grubba's avatar
Gus Grubba committed
164
    Connections {
165
        target: QGroundControl.airspaceManager
166
        onAirspaceVisibleChanged: {
167
            planControlColapsed = QGroundControl.airspaceManager.airspaceVisible
Gus Grubba's avatar
Gus Grubba committed
168 169 170
        }
    }

171 172 173 174 175 176 177
    Component {
        id: noItemForKML
        QGCViewMessage {
            message:    qsTr("You need at least one item to create a KML.")
        }
    }

178
    PlanMasterController {
Gus Grubba's avatar
Gus Grubba committed
179
        id: _planMasterController
180

181
        Component.onCompleted: {
182
            _planMasterController.start(false /* flyView */)
183
            _missionController.setCurrentPlanViewIndex(0, true)
Gus Grubba's avatar
Gus Grubba committed
184
            mainWindow.planMasterControllerPlan = _planMasterController
185 186
        }

187
        function waitingOnDataMessage() {
188
            mainWindow.showMessageDialog(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."))
189 190
        }

191
        function upload() {
192 193 194 195
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
Gus Grubba's avatar
Gus Grubba committed
196
            if (activeVehicle && activeVehicle.armed && activeVehicle.flightMode === activeVehicle.missionFlightMode) {
197
                mainWindow.showComponentDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel)
198
            } else {
199 200
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
201 202
        }

203
        function loadFromSelectedFile() {
204
            fileDialog.title =          qsTr("Select Plan File")
DonLakeFlyer's avatar
DonLakeFlyer committed
205
            fileDialog.planFiles =      true
206
            fileDialog.selectExisting = true
Gus Grubba's avatar
Gus Grubba committed
207
            fileDialog.nameFilters =    _planMasterController.loadNameFilters
208 209
            fileDialog.fileExtension =  _appSettings.planFileExtension
            fileDialog.fileExtension2 = _appSettings.missionFileExtension
210
            fileDialog.openForLoad()
211 212 213
        }

        function saveToSelectedFile() {
214 215 216 217
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
218
            fileDialog.title =          qsTr("Save Plan")
219
            fileDialog.planFiles =      true
220
            fileDialog.selectExisting = false
Gus Grubba's avatar
Gus Grubba committed
221
            fileDialog.nameFilters =    _planMasterController.saveNameFilters
222 223
            fileDialog.fileExtension =  _appSettings.planFileExtension
            fileDialog.fileExtension2 = _appSettings.missionFileExtension
224
            fileDialog.openForSave()
225 226
        }

227
        function fitViewportToItems() {
228
            mapFitFunctions.fitMapViewportToMissionItems()
229
        }
230

231 232
        function loadShapeFromSelectedFile() {
            fileDialog.title =          qsTr("Load Shape")
233 234
            fileDialog.planFiles =      false
            fileDialog.selectExisting = true
235 236 237
            fileDialog.nameFilters =    ShapeFileHelper.fileDialogKMLOrSHPFilters
            fileDialog.fileExtension =  _appSettings.kmlFileExtension
            fileDialog.fileExtension2 = _appSettings.shpFileExtension
238 239 240
            fileDialog.openForLoad()
        }

241
        function saveKmlToSelectedFile() {
242 243 244 245
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
246
            fileDialog.title =          qsTr("Save KML")
247
            fileDialog.planFiles =      false
248
            fileDialog.selectExisting = false
249 250
            fileDialog.nameFilters =    ShapeFileHelper.fileDialogKMLFilters
            fileDialog.fileExtension =  _appSettings.kmlFileExtension
251
            fileDialog.fileExtension2 = ""
252 253
            fileDialog.openForSave()
        }
254
    }
255

256 257
    Connections {
        target: _missionController
258

259
        onNewItemsFromVehicle: {
Gus Grubba's avatar
Gus Grubba committed
260
            if (_visualItems && _visualItems.count !== 1) {
261 262
                mapFitFunctions.fitMapViewportToMissionItems()
            }
263
            _missionController.setCurrentPlanViewIndex(0, true)
264 265
        }
    }
266

267 268 269 270
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
271
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
272
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
273 274
    }

275 276 277 278 279 280 281
    /// Inserts a new ROI mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertROIMissionItem(coordinate, index) {
        var sequenceNumber = _missionController.insertROIMissionItem(coordinate, index)
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
        _addROIOnClick = false
282
        toolStrip.lastClickedButton.checked = false
283 284
    }

285 286
    property int _moveDialogMissionItemIndex

287 288
    QGCFileDialog {
        id:             fileDialog
Gus Grubba's avatar
Gus Grubba committed
289
        folder:         _appSettings ? _appSettings.missionSavePath : ""
290

291 292
        property bool planFiles: true    ///< true: working with plan files, false: working with kml file

293
        onAcceptedForSave: {
294
            if (planFiles) {
Gus Grubba's avatar
Gus Grubba committed
295
                _planMasterController.saveToFile(file)
296
            } else {
Gus Grubba's avatar
Gus Grubba committed
297
                _planMasterController.saveToKml(file)
298
            }
299
            close()
300 301
        }

302
        onAcceptedForLoad: {
303
            if (planFiles) {
Gus Grubba's avatar
Gus Grubba committed
304 305
                _planMasterController.loadFromFile(file)
                _planMasterController.fitViewportToItems()
306 307
                _missionController.setCurrentPlanViewIndex(0, true)
            } else {
308 309
                var retList = ShapeFileHelper.determineShapeType(file)
                if (retList[0] == ShapeFileHelper.Error) {
310
                    mainWindow.showMessageDialog("Error", retList[1])
311
                } else if (retList[0] == ShapeFileHelper.Polygon) {
Gus Grubba's avatar
Gus Grubba committed
312
                     var editVehicle = activeVehicle ? activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
313 314 315 316
                    if (editVehicle.fixedWing) {
                        insertComplexMissionItemFromKMLOrSHP(_missionController.surveyComplexItemName, file, -1)
                    } else {
                        polygonSelectPatternFile = file
317
                        mainWindow.showComponentDialog(patternPolygonSelectDialog, fileDialog.title, mainWindow.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
318
                    }
319 320
                } else if (retList[0] == ShapeFileHelper.Polyline) {
                    insertComplexMissionItemFromKMLOrSHP(_missionController.corridorScanComplexItemName, file, -1)
321 322
                }
            }
323
            close()
324 325 326
        }
    }

327
    property string polygonSelectPatternFile
328
    Component {
329
        id: patternPolygonSelectDialog
330 331 332 333 334 335 336 337
        QGCViewDialog {
            function accept() {
                var complexItemName
                if (surveyRadio.checked) {
                    complexItemName = _missionController.surveyComplexItemName
                } else {
                    complexItemName = _missionController.structureScanComplexItemName
                }
338
                insertComplexMissionItemFromKMLOrSHP(complexItemName, polygonSelectPatternFile, -1)
339 340 341 342 343 344 345 346 347 348
                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
349
                    text:           qsTr("Create which pattern type?")
350 351 352 353 354 355 356 357 358 359 360 361 362
                }
                QGCRadioButton {
                    id:             surveyRadio
                    text:           qsTr("Survey")
                    checked:        true
                }
                QGCRadioButton {
                    text:           qsTr("Structure Scan")
                }
            }
        }
    }

363 364 365 366 367
    Component {
        id: moveDialog
        QGCViewDialog {
            function accept() {
                var toIndex = toCombo.currentIndex
Gus Grubba's avatar
Gus Grubba committed
368
                if (toIndex === 0) {
369 370
                    toIndex = 1
                }
371
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
372 373 374 375 376 377 378 379 380 381 382
                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
383
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
384 385 386 387
                }

                QGCComboBox {
                    id:             toCombo
388
                    model:          _visualItems.count
389 390 391 392 393 394
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

395
    Item {
Don Gagne's avatar
Don Gagne committed
396
        id:             panel
397
        anchors.fill:   parent
Don Gagne's avatar
Don Gagne committed
398

399
        FlightMap {
400 401 402 403 404
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
405
            planView:                   true
Don Gagne's avatar
Don Gagne committed
406

407
            // This is the center rectangle of the map which is not obscured by tools
408
            property rect centerViewport:   Qt.rect(_leftToolWidth, 0, editorMap.width - _leftToolWidth - _rightPanelWidth, editorMap.height - _statusHeight)
409

410 411
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
412

413
            readonly property real animationDuration: 500
414

415 416
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
417

418 419 420 421 422 423
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
424

425 426
            QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

427 428
            onZoomLevelChanged: updateAirspace(false)
            onCenterChanged:    updateAirspace(false)
429

430 431 432
            MouseArea {
                anchors.fill: parent
                onClicked: {
433 434
                    // Take focus to close any previous editing
                    editorMap.focus = true
435 436 437 438
                    var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
                    coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
                    coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
                    coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
439

440 441 442
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
443
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
444 445 446
                        } else if (_addROIOnClick) {
                            _addROIOnClick = false
                            insertROIMissionItem(coordinate, _missionController.visualItems.count)
447
                        }
448 449
                        break
                    case _layerRallyPoints:
Gus Grubba's avatar
Gus Grubba committed
450
                        if (_rallyPointController.supported && _addWaypointOnClick) {
451
                            _rallyPointController.addPoint(coordinate)
452
                        }
453
                        break
Don Gagne's avatar
Don Gagne committed
454
                    }
Don Gagne's avatar
Don Gagne committed
455
                }
456
            }
Don Gagne's avatar
Don Gagne committed
457

458 459
            // Add the mission item visuals to the map
            Repeater {
460
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
461 462
                delegate: MissionItemMapVisual {
                    map:        editorMap
463
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
464
                    visible:    _editingLayer == _layerMission
465
                }
466
            }
467

468 469
            // Add lines between waypoints
            MissionLineView {
470
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
471
            }
472

473 474 475 476 477 478
            MapItemView {
                model: _editingLayer == _layerMission ? _missionController.directionArrows : undefined

                delegate: MapLineArrow {
                    fromCoord:      object ? object.coordinate1 : undefined
                    toCoord:        object ? object.coordinate2 : undefined
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
                    arrowPosition:  3
                    z:              QGroundControl.zOrderWaypointLines + 1
                }
            }

            // UI for splitting the current segment
            MapQuickItem {
                id:             splitSegmentItem
                anchorPoint.x:  sourceItem.width / 2
                anchorPoint.y:  sourceItem.height / 2
                z:              QGroundControl.zOrderWaypointLines + 1

                sourceItem: SplitIndicator {
                    onClicked:  insertSimpleMissionItem(splitSegmentItem.coordinate, _missionController.currentPlanViewIndex)
                }

                function _updateSplitCoord() {
                    if (_missionController.splitSegment) {
                        var distance = _missionController.splitSegment.coordinate1.distanceTo(_missionController.splitSegment.coordinate2)
                        var azimuth = _missionController.splitSegment.coordinate1.azimuthTo(_missionController.splitSegment.coordinate2)
                        splitSegmentItem.coordinate = _missionController.splitSegment.coordinate1.atDistanceAndAzimuth(distance / 2, azimuth)
                    } else {
                        coordinate = QtPositioning.coordinate()
                    }
                }

                Connections {
                    target:                 _missionController
                    onSplitSegmentChanged:  splitSegmentItem._updateSplitCoord()
                }

                Connections {
                    target:                 _missionController.splitSegment
                    onCoordinate1Changed:   splitSegmentItem._updateSplitCoord()
                    onCoordinate2Changed:   splitSegmentItem._updateSplitCoord()
514 515 516
                }
            }

517 518 519 520 521 522 523
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
524
                    map:            editorMap
525 526
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
527
                }
528
            }
529

530 531
            GeoFenceMapVisuals {
                map:                    editorMap
532
                myGeoFenceController:   _geoFenceController
533
                interactive:            _editingLayer == _layerGeoFence
534
                homePosition:           _missionController.plannedHomePosition
535 536
                planView:               true
            }
537

538 539
            RallyPointMapVisuals {
                map:                    editorMap
540
                myRallyPointController: _rallyPointController
541 542
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
543
            }
544

545 546
            // Airspace overlap support
            MapItemView {
547
                model:              _airspaceEnabled && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.circles : []
548 549 550
                delegate: MapCircle {
                    center:         object.center
                    radius:         object.radius
551
                    color:          object.color
Gus Grubba's avatar
Gus Grubba committed
552 553
                    border.color:   object.lineColor
                    border.width:   object.lineWidth
554 555 556 557
                }
            }

            MapItemView {
558
                model:              _airspaceEnabled && QGroundControl.airspaceManager.airspaceVisible ? QGroundControl.airspaceManager.airspaces.polygons : []
559 560
                delegate: MapPolygon {
                    path:           object.polygon
561
                    color:          object.color
Gus Grubba's avatar
Gus Grubba committed
562 563
                    border.color:   object.lineColor
                    border.width:   object.lineWidth
564 565
                }
            }
566
        }
567

568 569
        //-----------------------------------------------------------
        // Left tool strip
Gus Grubba's avatar
Gus Grubba committed
570
        ToolStrip {
571
            id:                 toolStrip
572
            anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2
573 574 575 576
            anchors.left:       parent.left
            anchors.topMargin:  ScreenTools.defaultFontPixelHeight * 0.5
            anchors.top:        parent.top
            z:                  QGroundControl.zOrderWidgets
Gus Grubba's avatar
Gus Grubba committed
577
            maxHeight:          mapScale.y - toolStrip.y
578

Gus Grubba's avatar
Gus Grubba committed
579
            property bool _isRally:     _editingLayer == _layerRallyPoints
580

Gus Grubba's avatar
Gus Grubba committed
581 582 583 584 585 586
            model: [
                {
                    name:               qsTr("Fly"),
                    iconSource:         "/qmlimages/PaperPlane.svg",
                    buttonEnabled:      true,
                    buttonVisible:      true,
587
                },
588
                {
Gus Grubba's avatar
Gus Grubba committed
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 624 625 626
                    name:               qsTr("File"),
                    iconSource:         "/qmlimages/MapSync.svg",
                    buttonEnabled:      !_planMasterController.syncInProgress,
                    buttonVisible:      true,
                    showAlternateIcon:  _planMasterController.dirty,
                    alternateIconSource:"/qmlimages/MapSyncChanged.svg",
                    dropPanelComponent: syncDropPanel
                },
                {
                    name:               _editingLayer == _layerRallyPoints ? qsTr("Rally Point") : qsTr("Waypoint"),
                    iconSource:         "/qmlimages/MapAddMission.svg",
                    buttonEnabled:      true,
                    buttonVisible:      true,
                    toggle:             true,
                    checked:            _addWaypointOnClick
                },
                {
                    name:               qsTr("ROI"),
                    iconSource:         "/qmlimages/MapAddMission.svg",
                    buttonEnabled:      true,
                    buttonVisible:      !_isRally && _waypointsOnlyMode,
                    toggle:             true
                },
                {
                    name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : qsTr("Pattern"),
                    iconSource:         "/qmlimages/MapDrawShape.svg",
                    buttonEnabled:      true,
                    buttonVisible:      !_isRally,
                    dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                },
                {
                    name:               qsTr("Center"),
                    iconSource:         "/qmlimages/MapCenter.svg",
                    buttonEnabled:      true,
                    buttonVisible:      true,
                    dropPanelComponent: centerMapDropPanel
                }
            ]
627

Gus Grubba's avatar
Gus Grubba committed
628 629
            onClicked: {
                switch (index) {
630 631 632 633
                case 0:
                    mainWindow.showFlyView()
                    break;
                case 2:
Gus Grubba's avatar
Gus Grubba committed
634 635 636 637 638 639
                    if(_addWaypointOnClick) {
                        //-- Toggle it off
                        _addWaypointOnClick = false
                        _addROIOnClick = false
                        setChecked(index, false)
                    } else {
640
                        _addWaypointOnClick = checked
641
                        _addROIOnClick = false
Gus Grubba's avatar
Gus Grubba committed
642 643
                    }
                    break
644
                case 3:
Gus Grubba's avatar
Gus Grubba committed
645 646 647
                    _addROIOnClick = checked
                    _addWaypointOnClick = false
                    break
648
                case 4:
Gus Grubba's avatar
Gus Grubba committed
649 650 651 652
                    if (_singleComplexItem) {
                        addComplexItem(_missionController.complexMissionItemNames[0])
                    }
                    break
653 654
                }
            }
Gus Grubba's avatar
Gus Grubba committed
655
        }
656

Gus Grubba's avatar
Gus Grubba committed
657
        //-----------------------------------------------------------
658 659 660
        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
661
            height:             parent.height
662 663
            width:              _rightPanelWidth
            color:              qgcPal.window
664
            opacity:            planExpanded.visible ? 0.2 : 0
Gus Grubba's avatar
Gus Grubba committed
665 666 667
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
            anchors.rightMargin: ScreenTools.defaultFontPixelWidth
668
        }
Gus Grubba's avatar
Gus Grubba committed
669 670
        //-------------------------------------------------------
        // Right Panel Controls
671
        Item {
Gus Grubba's avatar
Gus Grubba committed
672
            anchors.fill:           rightPanel
Gus Grubba's avatar
Gus Grubba committed
673
            anchors.topMargin:      _toolButtonTopMargin
Gus Grubba's avatar
Gus Grubba committed
674 675 676
            DeadMouseArea {
                anchors.fill:   parent
            }
Gus Grubba's avatar
Gus Grubba committed
677 678
            Column {
                id:                 rightControls
Gus Grubba's avatar
Gus Grubba committed
679
                spacing:            ScreenTools.defaultFontPixelHeight * 0.5
680 681
                anchors.left:       parent.left
                anchors.right:      parent.right
Gus Grubba's avatar
Gus Grubba committed
682 683 684 685
                anchors.top:        parent.top
                //-------------------------------------------------------
                // Airmap Airspace Control
                AirspaceControl {
Gus Grubba's avatar
Gus Grubba committed
686 687
                    id:             airspaceControl
                    width:          parent.width
688
                    visible:        _airspaceEnabled
689
                    planView:       true
690
                    showColapse:    true
691
                }
Gus Grubba's avatar
Gus Grubba committed
692 693 694 695
                //-------------------------------------------------------
                // Mission Controls (Colapsed)
                Rectangle {
                    width:      parent.width
Gus Grubba's avatar
Gus Grubba committed
696
                    height:     planControlColapsed ? colapsedRow.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
697 698
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
699
                    visible:    planControlColapsed && _airspaceEnabled
Gus Grubba's avatar
Gus Grubba committed
700 701 702 703 704 705 706
                    Row {
                        id:                     colapsedRow
                        spacing:                ScreenTools.defaultFontPixelWidth
                        anchors.left:           parent.left
                        anchors.leftMargin:     ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                        QGCColoredImage {
707 708 709 710 711
                            width:              height
                            height:             ScreenTools.defaultFontPixelWidth * 2.5
                            sourceSize.height:  height
                            source:             "qrc:/res/waypoint.svg"
                            color:              qgcPal.text
Gus Grubba's avatar
Gus Grubba committed
712 713 714
                            anchors.verticalCenter: parent.verticalCenter
                        }
                        QGCLabel {
715 716
                            text:               qsTr("Plan")
                            color:              qgcPal.text
Gus Grubba's avatar
Gus Grubba committed
717
                            anchors.verticalCenter: parent.verticalCenter
718 719
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
720 721 722 723
                    QGCColoredImage {
                        width:                  height
                        height:                 ScreenTools.defaultFontPixelWidth * 2.5
                        sourceSize.height:      height
724
                        source:                 QGroundControl.airmapSupported ? "qrc:/airmap/expand.svg" : ""
725
                        color:                  "white"
726
                        visible:                QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
727 728 729 730 731 732
                        anchors.right:          parent.right
                        anchors.rightMargin:    ScreenTools.defaultFontPixelWidth
                        anchors.verticalCenter: parent.verticalCenter
                    }
                    MouseArea {
                        anchors.fill:   parent
733
                        enabled:        QGroundControl.airmapSupported
Gus Grubba's avatar
Gus Grubba committed
734
                        onClicked: {
735
                            QGroundControl.airspaceManager.airspaceVisible = false
Gus Grubba's avatar
Gus Grubba committed
736 737
                        }
                    }
Gus Grubba's avatar
Gus Grubba committed
738
                }
Gus Grubba's avatar
Gus Grubba committed
739 740 741 742 743
                //-------------------------------------------------------
                // Mission Controls (Expanded)
                Rectangle {
                    id:         planExpanded
                    width:      parent.width
Gus Grubba's avatar
Gus Grubba committed
744
                    height:     (!planControlColapsed || !_airspaceEnabled) ? bar.height + ScreenTools.defaultFontPixelHeight : 0
Gus Grubba's avatar
Gus Grubba committed
745 746
                    color:      qgcPal.missionItemEditor
                    radius:     _radius
Gus Grubba's avatar
Gus Grubba committed
747
                    visible:    (!planControlColapsed || !_airspaceEnabled) && QGroundControl.corePlugin.options.enablePlanViewSelector
Gus Grubba's avatar
Gus Grubba committed
748
                    Item {
Gus Grubba's avatar
Gus Grubba committed
749
                        height:             bar.height
Gus Grubba's avatar
Gus Grubba committed
750 751
                        anchors.left:       parent.left
                        anchors.right:      parent.right
Gus Grubba's avatar
Gus Grubba committed
752
                        anchors.margins:    ScreenTools.defaultFontPixelWidth
Gus Grubba's avatar
Gus Grubba committed
753
                        anchors.verticalCenter: parent.verticalCenter
Gus Grubba's avatar
Gus Grubba committed
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
                        QGCTabBar {
                            id:             bar
                            width:          parent.width
                            anchors.centerIn: parent
                            Component.onCompleted: {
                                currentIndex = 0
                            }
                            QGCTabButton {
                                text:       qsTr("Mission")
                            }
                            QGCTabButton {
                                text:       qsTr("Fence")
                                enabled:    _geoFenceController.supported
                            }
                            QGCTabButton {
                                text:       qsTr("Rally")
                                enabled:    _rallyPointController.supported
Gus Grubba's avatar
Gus Grubba committed
771 772 773 774 775 776 777 778 779 780 781 782
                            }
                        }
                    }
                }
            }
            //-------------------------------------------------------
            // Mission Item Editor
            Item {
                id:                     missionItemEditor
                anchors.left:           parent.left
                anchors.right:          parent.right
                anchors.top:            rightControls.bottom
Gus Grubba's avatar
Gus Grubba committed
783
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.25
Gus Grubba's avatar
Gus Grubba committed
784 785 786 787
                anchors.bottom:         parent.bottom
                anchors.bottomMargin:   ScreenTools.defaultFontPixelHeight * 0.25
                visible:                _editingLayer == _layerMission && !planControlColapsed
                QGCListView {
Gus Grubba's avatar
Gus Grubba committed
788 789 790 791 792 793 794 795
                    id:                 missionItemEditorListView
                    anchors.fill:       parent
                    spacing:            ScreenTools.defaultFontPixelHeight / 4
                    orientation:        ListView.Vertical
                    model:              _missionController.visualItems
                    cacheBuffer:        Math.max(height * 2, 0)
                    clip:               true
                    currentIndex:       _missionController.currentPlanViewIndex
Gus Grubba's avatar
Gus Grubba committed
796
                    highlightMoveDuration: 250
Gus Grubba's avatar
Gus Grubba committed
797
                    visible:            _editingLayer == _layerMission && !planControlColapsed
Gus Grubba's avatar
Gus Grubba committed
798 799
                    //-- List Elements
                    delegate: MissionItemEditor {
Gus Grubba's avatar
Gus Grubba committed
800
                        map:            editorMap
Gus Grubba's avatar
Gus Grubba committed
801
                        masterController:  _planMasterController
Gus Grubba's avatar
Gus Grubba committed
802 803 804 805
                        missionItem:    object
                        width:          parent.width
                        readOnly:       false
                        onClicked:      _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
Gus Grubba's avatar
Gus Grubba committed
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
                        onRemove: {
                            var removeIndex = index
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
                                removeIndex--
                            }
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
                        }
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
                    }
                }
            }
            // GeoFence Editor
            GeoFenceEditor {
                anchors.top:            rightControls.bottom
Gus Grubba's avatar
Gus Grubba committed
822
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.25
Don Gagne's avatar
Don Gagne committed
823
                anchors.bottom:         parent.bottom
Gus Grubba's avatar
Gus Grubba committed
824 825 826 827 828 829 830 831 832 833
                anchors.left:           parent.left
                anchors.right:          parent.right
                myGeoFenceController:   _geoFenceController
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
            }
            // Rally Point Editor
            RallyPointEditorHeader {
                id:                     rallyPointHeader
                anchors.top:            rightControls.bottom
Gus Grubba's avatar
Gus Grubba committed
834
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.25
Gus Grubba's avatar
Gus Grubba committed
835 836 837 838 839 840 841 842
                anchors.left:           parent.left
                anchors.right:          parent.right
                visible:                _editingLayer == _layerRallyPoints
                controller:             _rallyPointController
            }
            RallyPointItemEditor {
                id:                     rallyPointEditor
                anchors.top:            rallyPointHeader.bottom
Gus Grubba's avatar
Gus Grubba committed
843
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight * 0.25
Gus Grubba's avatar
Gus Grubba committed
844 845 846 847 848
                anchors.left:           parent.left
                anchors.right:          parent.right
                visible:                _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:             _rallyPointController.currentRallyPoint
                controller:             _rallyPointController
849
            }
Gus Grubba's avatar
Gus Grubba committed
850
        }
851 852

        MapScale {
853 854 855 856 857 858 859 860 861 862 863
            id:                     mapScale
            anchors.margins:        ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:         waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:           parent.left
            mapControl:             editorMap
            buttonsOnLeft:          true
            terrainButtonVisible:   true
            visible:                _toolStripBottom < y && _editingLayer === _layerMission
            terrainButtonChecked:   waypointValuesDisplay.visible

            onTerrainButtonClicked: waypointValuesDisplay.toggleVisible()
864 865 866 867 868 869
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
870
            height:             ScreenTools.defaultFontPixelHeight * 7
871
            maxWidth:           parent.width - rightPanel.width - x
872
            anchors.bottom:     parent.bottom
873
            missionItems:       _missionController.visualItems
874 875 876 877 878 879 880
            visible:            _internalVisible && _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus

            property bool _internalVisible: false

            function toggleVisible() {
                _internalVisible = !_internalVisible
            }
881
        }
Gus Grubba's avatar
Gus Grubba committed
882
    }
883

884 885 886 887
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
888
            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?")
889 890
            function accept() {
                hideDialog()
Gus Grubba's avatar
Gus Grubba committed
891
                _planMasterController.loadFromVehicle()
892 893 894 895 896 897 898 899
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
900
            message:   qsTr("You have unsaved/unsent changes. Loading from a file will lose these changes. Are you sure you want to load from a file?")
901 902
            function accept() {
                hideDialog()
Gus Grubba's avatar
Gus Grubba committed
903
                _planMasterController.loadFromSelectedFile()
904 905 906 907
            }
        }
    }

908 909 910
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
911
            message: qsTr("Are you sure you want to remove all items and create a new plan? ") +
912
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
913
            function accept() {
914
                if (_planMasterController.offline) {
Gus Grubba's avatar
Gus Grubba committed
915
                    _planMasterController.removeAll()
916
                } else {
Gus Grubba's avatar
Gus Grubba committed
917
                    _planMasterController.removeAllFromVehicle()
918
                }
919 920 921 922 923
                hideDialog()
            }
        }
    }

924 925 926 927 928
    Component {
        id: clearVehicleMissionDialog
        QGCViewMessage {
            message: qsTr("Are you sure you want to remove all mission items and clear the mission from the vehicle?")
            function accept() {
Gus Grubba's avatar
Gus Grubba committed
929
                _planMasterController.removeAllFromVehicle()
930 931 932 933 934
                hideDialog()
            }
        }
    }

935 936 937 938 939 940 941 942 943 944 945
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

946 947 948 949 950 951 952 953 954
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
955
                model: _missionController.complexMissionItemNames
956 957 958 959 960 961

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
962
                        addComplexItem(modelData)
963 964 965 966
                        dropPanel.hide()
                    }
                }
            }
967 968 969 970 971 972 973 974 975 976 977 978 979

            Rectangle {
                width:              parent.width * 0.8
                height:             1
                color:              qgcPal.text
                opacity:            0.5
                Layout.fillWidth:   true
                Layout.columnSpan:  2
            }

            QGCButton {
                text:               qsTr("Load KML/SHP...")
                Layout.fillWidth:   true
Gus Grubba's avatar
Gus Grubba committed
980
                enabled:            !_planMasterController.syncInProgress
981
                onClicked: {
Gus Grubba's avatar
Gus Grubba committed
982
                    _planMasterController.loadShapeFromSelectedFile()
983 984 985
                    dropPanel.hide()
                }
            }
986 987
        } // Column
    }
988 989

    Component {
990
        id: syncDropPanel
991

992 993 994
        Column {
            id:         columnHolder
            spacing:    _margin
995

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

998 999 1000
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
Gus Grubba's avatar
Gus Grubba committed
1001
                text:       _planMasterController.dirty ?
Gus Grubba's avatar
Gus Grubba committed
1002
                                (activeVehicle ?
1003 1004 1005 1006
                                     qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                     qsTr("You have unsaved changes.")
                                ) :
                                qsTr("Plan File:")
1007 1008
            }

1009 1010 1011 1012 1013 1014
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
1015

1016
                QGCButton {
1017
                    text:               qsTr("New...")
1018
                    Layout.fillWidth:   true
1019
                    enabled:            _planMasterController.containsItems
1020
                    onClicked:  {
1021
                        dropPanel.hide()
1022
                        mainWindow.showComponentDialog(removeAllPromptDialog, qsTr("New Plan"), mainWindow.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
1023 1024
                    }
                }
1025

1026
                QGCButton {
1027
                    text:               qsTr("Open...")
1028
                    Layout.fillWidth:   true
Gus Grubba's avatar
Gus Grubba committed
1029
                    enabled:            !_planMasterController.syncInProgress
1030 1031
                    onClicked: {
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1032
                        if (_planMasterController.dirty) {
1033
                            mainWindow.showComponentDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, mainWindow.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1034
                        } else {
Gus Grubba's avatar
Gus Grubba committed
1035
                            _planMasterController.loadFromSelectedFile()
1036 1037 1038
                        }
                    }
                }
1039

1040
                QGCButton {
1041
                    text:               qsTr("Save")
1042
                    Layout.fillWidth:   true
Gus Grubba's avatar
Gus Grubba committed
1043
                    enabled:            !_planMasterController.syncInProgress && _planMasterController.currentPlanFile !== ""
1044 1045
                    onClicked: {
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1046 1047
                        if(_planMasterController.currentPlanFile !== "") {
                            _planMasterController.saveToCurrent()
1048
                        } else {
Gus Grubba's avatar
Gus Grubba committed
1049
                            _planMasterController.saveToSelectedFile()
1050
                        }
1051 1052 1053 1054
                    }
                }

                QGCButton {
1055
                    text:               qsTr("Save As...")
1056
                    Layout.fillWidth:   true
1057
                    enabled:            !_planMasterController.syncInProgress && _planMasterController.containsItems
1058 1059
                    onClicked: {
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1060
                        _planMasterController.saveToSelectedFile()
1061 1062 1063 1064
                    }
                }

                QGCButton {
1065
                    text:               qsTr("Save Mission Waypoints As KML...")
1066
                    Layout.columnSpan:  2
Gus Grubba's avatar
Gus Grubba committed
1067
                    enabled:            !_planMasterController.syncInProgress && _visualItems.count > 1
1068
                    onClicked: {
1069
                        // First point does not count
1070
                        if (_visualItems.count < 2) {
1071
                            mainWindow.showComponentDialog(noItemForKML, qsTr("KML"), mainWindow.showDialogDefaultWidth, StandardButton.Cancel)
1072 1073
                            return
                        }
1074
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1075
                        _planMasterController.saveKmlToSelectedFile()
1076 1077
                    }
                }
1078

1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
                Rectangle {
                    width:              parent.width * 0.8
                    height:             1
                    color:              qgcPal.text
                    opacity:            0.5
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    Layout.fillWidth:   true
                    Layout.columnSpan:  2
                }

1089
                QGCButton {
1090
                    text:               qsTr("Upload")
1091
                    Layout.fillWidth:   true
1092
                    enabled:            !_planMasterController.offline && !_planMasterController.syncInProgress && _planMasterController.containsItems
1093 1094
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
1095
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1096
                        _planMasterController.upload()
1097 1098
                    }
                }
1099 1100 1101 1102

                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
Gus Grubba's avatar
Gus Grubba committed
1103
                    enabled:            !_planMasterController.offline && !_planMasterController.syncInProgress
1104 1105 1106
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
                        dropPanel.hide()
Gus Grubba's avatar
Gus Grubba committed
1107
                        if (_planMasterController.dirty) {
1108
                            mainWindow.showComponentDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, mainWindow.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1109
                        } else {
Gus Grubba's avatar
Gus Grubba committed
1110
                            _planMasterController.loadFromVehicle()
1111 1112 1113 1114 1115 1116 1117 1118
                        }
                    }
                }

                QGCButton {
                    text:               qsTr("Clear Vehicle Mission")
                    Layout.fillWidth:   true
                    Layout.columnSpan:  2
Gus Grubba's avatar
Gus Grubba committed
1119
                    enabled:            !_planMasterController.offline && !_planMasterController.syncInProgress
1120 1121 1122
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
                        dropPanel.hide()
1123
                        mainWindow.showComponentDialog(clearVehicleMissionDialog, text, mainWindow.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1124 1125 1126
                    }
                }

1127
            }
1128 1129
        }
    }
Gus Grubba's avatar
Gus Grubba committed
1130
}