PlanView.qml 38.7 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 12
import QtQuick          2.3
import QtQuick.Controls 1.2
Don Gagne's avatar
Don Gagne committed
13
import QtQuick.Dialogs  1.2
14 15 16
import QtLocation       5.3
import QtPositioning    5.3
import QtQuick.Layouts  1.2
17
import QtQuick.Window   2.2
Don Gagne's avatar
Don Gagne committed
18

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

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

Don Gagne's avatar
Don Gagne committed
31
QGCView {
32
    id:         _qgcView
33
    viewPanel:  panel
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  _toolButtonTopMargin:       parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2)
    readonly property var   _defaultVehicleCoordinate:  QtPositioning.coordinate(37.803784, -122.462276)
    readonly property bool  _waypointsOnlyMode:         QGroundControl.corePlugin.options.missionWaypointsOnly
44

45 46 47 48 49
    property var    _planMasterController:      masterController
    property var    _missionController:         _planMasterController.missionController
    property var    _geoFenceController:        _planMasterController.geoFenceController
    property var    _rallyPointController:      _planMasterController.rallyPointController
    property var    _visualItems:               _missionController.visualItems
50 51
    property bool   _lightWidgetBorders:        editorMap.isSatelliteMap
    property bool   _addWaypointOnClick:        false
52
    property bool   _addROIOnClick:             false
53
    property bool   _singleComplexItem:         _missionController.complexMissionItemNames.length === 1
54 55
    property real   _toolbarHeight:             _qgcView.height - ScreenTools.availableHeight
    property int    _editingLayer:              _layerMission
56
    property int    _toolStripBottom:           toolStrip.height + toolStrip.y
57

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
    Component.onCompleted: {
64
        toolbar.planMasterController =  Qt.binding(function () { return _planMasterController })
65
        toolbar.currentMissionItem =    Qt.binding(function () { return _missionController.currentPlanViewItem })
66 67
    }

68 69 70 71 72
    function addComplexItem(complexItemName) {
        var coordinate = editorMap.center
        coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
        coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
        coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
73
        insertComplexMissionItem(complexItemName, coordinate, _missionController.visualItems.count)
74 75 76
    }

    function insertComplexMissionItem(complexItemName, coordinate, index) {
77
        var sequenceNumber = _missionController.insertComplexMissionItem(complexItemName, coordinate, index)
78
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
79 80
    }

81 82 83 84 85
    function insertComplexMissionItemFromKML(complexItemName, kmlFile, index) {
        var sequenceNumber = _missionController.insertComplexMissionItemFromKML(complexItemName, kmlFile, index)
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
    }

86 87 88 89 90
    property bool _firstMissionLoadComplete:    false
    property bool _firstFenceLoadComplete:      false
    property bool _firstRallyLoadComplete:      false
    property bool _firstLoadComplete:           false

91
    MapFitFunctions {
92
        id:                         mapFitFunctions  // The name for this id cannot be changed without breaking references outside of this code. Beware!
93 94
        map:                        editorMap
        usePlannedHomePosition:     true
95
        planMasterController:       _planMasterController
96 97
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    Connections {
        target: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude

        onRawValueChanged: {
            if (_visualItems.count > 1) {
                _qgcView.showDialog(applyNewAltitude, qsTr("Apply new alititude"), showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
            }
        }
    }

    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()
116
                _missionController.applyDefaultMissionAltitude()
DonLakeFlyer's avatar
DonLakeFlyer committed
117 118 119 120
            }
        }
    }

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
    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: {
                        _activeVehicle.flightMode = _activeVehicle.pauseFlightMode
146
                        _planMasterController.sendToVehicle()
147
                        hideDialog()
148 149 150 151 152 153
                    }
                }
            }
        }
    }

154 155 156 157 158 159 160 161
    Component {
        id: noItemForKML

        QGCViewMessage {
            message:    qsTr("You need at least one item to create a KML.")
        }
    }

162
    PlanMasterController {
163
        id: masterController
164

165
        Component.onCompleted: {
166
            start(false /* flyView */)
167
            _missionController.setCurrentPlanViewIndex(0, true)
168 169
        }

170 171 172 173
        function waitingOnDataMessage() {
            _qgcView.showMessage(qsTr("Unable to Save/Upload"), qsTr("Plan is waiting on terrain data from server for correct altitude values."), StandardButton.Ok)
        }

174
        function upload() {
175 176 177 178
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
179
            if (_activeVehicle && _activeVehicle.armed && _activeVehicle.flightMode === _activeVehicle.missionFlightMode) {
180
                _qgcView.showDialog(activeMissionUploadDialogComponent, qsTr("Plan Upload"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
181
            } else {
182 183
                sendToVehicle()
            }
DonLakeFlyer's avatar
DonLakeFlyer committed
184 185
        }

186
        function loadFromSelectedFile() {
187
            fileDialog.title =          qsTr("Select Plan File")
188
            fileDialog.selectExisting = true
189
            fileDialog.nameFilters =    masterController.loadNameFilters
190 191
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
192
            fileDialog.openForLoad()
193 194 195
        }

        function saveToSelectedFile() {
196 197 198 199
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
200
            fileDialog.title =          qsTr("Save Plan")
201
            fileDialog.planFiles =      true
202
            fileDialog.selectExisting = false
203
            fileDialog.nameFilters =    masterController.saveNameFilters
204 205
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
206
            fileDialog.openForSave()
207 208
        }

209
        function fitViewportToItems() {
210
            mapFitFunctions.fitMapViewportToMissionItems()
211
        }
212

213 214 215 216 217 218 219 220 221 222
        function loadKmlFromSelectedFile() {
            fileDialog.title =          qsTr("Load KML")
            fileDialog.planFiles =      false
            fileDialog.selectExisting = true
            fileDialog.nameFilters =    masterController.fileKmlFilters
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.kmlFileExtension
            fileDialog.fileExtension2 = ""
            fileDialog.openForLoad()
        }

223
        function saveKmlToSelectedFile() {
224 225 226 227
            if (!readyForSaveSend()) {
                waitingOnDataMessage()
                return
            }
228
            fileDialog.title =          qsTr("Save KML")
229
            fileDialog.planFiles =      false
230
            fileDialog.selectExisting = false
231
            fileDialog.nameFilters =    masterController.fileKmlFilters
232 233
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.kmlFileExtension
            fileDialog.fileExtension2 = ""
234 235
            fileDialog.openForSave()
        }
236
    }
237

238 239
    Connections {
        target: _missionController
240

241
        onNewItemsFromVehicle: {
242 243 244
            if (_visualItems && _visualItems.count != 1) {
                mapFitFunctions.fitMapViewportToMissionItems()
            }
245
            _missionController.setCurrentPlanViewIndex(0, true)
246 247
        }
    }
248

249
    QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
Don Gagne's avatar
Don Gagne committed
250

251 252 253 254
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

255 256 257 258
    /// Inserts a new simple mission item
    ///     @param coordinate Location to insert item
    ///     @param index Insert item at this index
    function insertSimpleMissionItem(coordinate, index) {
259
        var sequenceNumber = _missionController.insertSimpleMissionItem(coordinate, index)
260
        _missionController.setCurrentPlanViewIndex(sequenceNumber, true)
261 262
    }

263 264 265 266 267 268 269 270 271 272
    /// 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
        toolStrip.uncheckAll()
    }

273 274
    property int _moveDialogMissionItemIndex

275 276 277 278
    QGCFileDialog {
        id:             fileDialog
        qgcView:        _qgcView
        folder:         QGroundControl.settingsManager.appSettings.missionSavePath
279

280 281
        property bool planFiles: true    ///< true: working with plan files, false: working with kml file

282
        onAcceptedForSave: {
283 284 285 286 287
            if (planFiles) {
                masterController.saveToFile(file)
            } else {
                masterController.saveToKml(file)
            }
288
            close()
289 290
        }

291
        onAcceptedForLoad: {
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
            if (planFiles) {
                masterController.loadFromFile(file)
                masterController.fitViewportToItems()
                _missionController.setCurrentPlanViewIndex(0, true)
            } else {
                var retList = KMLFileHelper.determineFileContents(file)
                if (retList[0] == KMLFileHelper.Error) {
                    _qgcView.showMessage("Error", retList[1], StandardButton.Ok)
                } else if (retList[0] == KMLFileHelper.Polygon) {
                    kmlPolygonSelectDialogKMLFile = file
                    _qgcView.showDialog(kmlPolygonSelectDialog, fileDialog.title, _qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
                } else if (retList[0] == KMLFileHelper.Polyline) {
                    insertComplexMissionItemFromKML(_missionController.corridorScanComplexItemName, file, -1)
                }
            }
307
            close()
308 309 310
        }
    }

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    property string kmlPolygonSelectDialogKMLFile
    Component {
        id: kmlPolygonSelectDialog

        QGCViewDialog {
            property var editVehicle: _activeVehicle ? _activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle

            function accept() {
                var complexItemName
                if (surveyRadio.checked) {
                    complexItemName = _missionController.surveyComplexItemName
                } else {
                    complexItemName = _missionController.structureScanComplexItemName
                }
                insertComplexMissionItemFromKML(complexItemName, kmlPolygonSelectDialogKMLFile, -1)
                hideDialog()
            }

            Component.onCompleted: {
                if (editVehicle.fixedWing) {
                    // Only Survey available
                    accept()
                }
            }

            ExclusiveGroup {
                id: radioGroup
            }

            Column {
                anchors.left:   parent.left
                anchors.right:  parent.right
                spacing:        ScreenTools.defaultFontPixelHeight

                QGCLabel {
                    anchors.left:   parent.left
                    anchors.right:  parent.right
                    wrapMode:       Text.WordWrap
                    text:           qsTr("What would you like to create from the polygon specified by the KML file?")
                }

                QGCRadioButton {
                    id:             surveyRadio
                    text:           qsTr("Survey")
                    checked:        true
                    exclusiveGroup: radioGroup
                }

                QGCRadioButton {
                    text:           qsTr("Structure Scan")
                    exclusiveGroup: radioGroup
                    visible:        !editVehicle.fixedWing
                }
            }
        }
    }

368 369 370 371 372 373 374
    Component {
        id: moveDialog

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

375
                if (toIndex === 0) {
376 377
                    toIndex = 1
                }
378
                _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex)
379 380 381 382 383 384 385 386 387 388 389 390
                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
391
                    text:           qsTr("Move the selected mission item to the be after following mission item:")
392 393 394 395
                }

                QGCComboBox {
                    id:             toCombo
396
                    model:          _visualItems.count
397 398 399 400 401 402
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
403 404
    QGCViewPanel {
        id:             panel
405
        anchors.fill:   parent
Don Gagne's avatar
Don Gagne committed
406

407
        FlightMap {
408 409 410 411 412
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
413
            planView:                   true
414
            qgcView:                    _qgcView
Don Gagne's avatar
Don Gagne committed
415

416 417
            // This is the center rectangle of the map which is not obscured by tools
            property rect centerViewport: Qt.rect(_leftToolWidth, _toolbarHeight, editorMap.width - _leftToolWidth - _rightPanelWidth, editorMap.height - _statusHeight - _toolbarHeight)
418

419 420
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
421

422
            readonly property real animationDuration: 500
423

424 425
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
426

427 428 429 430 431 432
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
433

434 435 436 437 438 439 440
            QGCMapPalette { id: mapPal; lightColors: editorMap.isSatelliteMap }

            MouseArea {
                //-- It's a whole lot faster to just fill parent and deal with top offset below
                //   than computing the coordinate offset.
                anchors.fill: parent
                onClicked: {
441 442 443
                    // Take focus to close any previous editing
                    editorMap.focus = true

444 445 446 447
                    //-- Don't pay attention to items beneath the toolbar.
                    var topLimit = parent.height - ScreenTools.availableHeight
                    if(mouse.y < topLimit) {
                        return
448 449
                    }

450 451 452 453
                    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)
454

455 456 457
                    switch (_editingLayer) {
                    case _layerMission:
                        if (_addWaypointOnClick) {
458
                            insertSimpleMissionItem(coordinate, _missionController.visualItems.count)
459 460 461
                        } else if (_addROIOnClick) {
                            _addROIOnClick = false
                            insertROIMissionItem(coordinate, _missionController.visualItems.count)
462
                        }
463 464
                        break
                    case _layerRallyPoints:
465
                        if (_rallyPointController.supported) {
466
                            _rallyPointController.addPoint(coordinate)
467
                        }
468
                        break
Don Gagne's avatar
Don Gagne committed
469
                    }
Don Gagne's avatar
Don Gagne committed
470
                }
471
            }
Don Gagne's avatar
Don Gagne committed
472

473 474
            // Add the mission item visuals to the map
            Repeater {
475
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
476

477 478
                delegate: MissionItemMapVisual {
                    map:        editorMap
DonLakeFlyer's avatar
DonLakeFlyer committed
479
                    qgcView:    _qgcView
480
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
481
                    visible:    _editingLayer == _layerMission
482
                }
483
            }
484

485 486
            // Add lines between waypoints
            MissionLineView {
487
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
488
            }
489

490 491 492 493 494 495 496
            // Add the vehicles to the map
            MapItemView {
                model: QGroundControl.multiVehicleManager.vehicles
                delegate:
                    VehicleMapItem {
                    vehicle:        object
                    coordinate:     object.coordinate
497
                    map:            editorMap
498 499
                    size:           ScreenTools.defaultFontPixelHeight * 3
                    z:              QGroundControl.zOrderMapItems - 1
500
                }
501
            }
502

503 504
            GeoFenceMapVisuals {
                map:                    editorMap
505
                myGeoFenceController:   _geoFenceController
506
                interactive:            _editingLayer == _layerGeoFence
507
                homePosition:           _missionController.plannedHomePosition
508 509
                planView:               true
            }
510

511 512
            RallyPointMapVisuals {
                map:                    editorMap
513
                myRallyPointController: _rallyPointController
514 515
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
516
            }
517

518 519 520 521 522 523 524 525 526
            ToolStrip {
                id:                 toolStrip
                anchors.leftMargin: ScreenTools.defaultFontPixelWidth
                anchors.left:       parent.left
                anchors.topMargin:  _toolButtonTopMargin
                anchors.top:        parent.top
                color:              qgcPal.window
                title:              qsTr("Plan")
                z:                  QGroundControl.zOrderWidgets
527 528 529 530 531
                showAlternateIcon:  [ false, false, false, masterController.dirty, false, false, false ]
                rotateImage:        [ false, false, false, masterController.syncInProgress, false, false, false ]
                animateImage:       [ false, false, false, masterController.dirty, false, false, false ]
                buttonEnabled:      [ true, true, true, !masterController.syncInProgress, true, true, true ]
                buttonVisible:      [ true, _waypointsOnlyMode, true, true, true, _showZoom, _showZoom ]
532 533 534 535 536 537 538 539 540 541
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
                        name:       "Waypoint",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
542 543 544 545 546
                    {
                        name:       "ROI",
                        iconSource: "/qmlimages/MapAddMission.svg",
                        toggle:     true
                    },
547
                    {
548
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : "Pattern",
549 550 551
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                    },
552 553 554 555 556 557
                    {
                        name:                   "Sync",
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
                    },
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
                    {
                        name:               "Center",
                        iconSource:         "/qmlimages/MapCenter.svg",
                        dropPanelComponent: centerMapDropPanel
                    },
                    {
                        name:               "In",
                        iconSource:         "/qmlimages/ZoomPlus.svg"
                    },
                    {
                        name:               "Out",
                        iconSource:         "/qmlimages/ZoomMinus.svg"
                    }
                ]

                onClicked: {
                    switch (index) {
                    case 0:
                        _addWaypointOnClick = checked
577
                        _addROIOnClick = false
578 579
                        break
                    case 1:
580 581 582 583
                        _addROIOnClick = checked
                        _addWaypointOnClick = false
                        break
                    case 2:
584
                        if (_singleComplexItem) {
585
                            addComplexItem(_missionController.complexMissionItemNames[0])
586
                        }
587
                        break
588
                    case 5:
589 590
                        editorMap.zoomLevel += 0.5
                        break
591
                    case 6:
592 593 594 595 596 597 598 599 600 601 602 603
                        editorMap.zoomLevel -= 0.5
                        break
                    }
                }
            }
        } // FlightMap

        // Right pane for mission editing controls
        Rectangle {
            id:                 rightPanel
            anchors.bottom:     parent.bottom
            anchors.right:      parent.right
604
            height:             ScreenTools.availableHeight
605 606
            width:              _rightPanelWidth
            color:              qgcPal.window
607 608 609 610 611
            opacity:            0.2
        }

        Item {
            anchors.fill:   rightPanel
612 613 614 615

            // Plan Element selector (Mission/Fence/Rally)
            Row {
                id:                 planElementSelectorRow
616
                anchors.topMargin:  Math.round(ScreenTools.defaultFontPixelHeight / 3)
617 618 619 620
                anchors.top:        parent.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                spacing:            _horizontalMargin
621
                visible:            QGroundControl.corePlugin.options.enablePlanViewSelector
622 623 624 625 626 627 628 629 630

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

                ExclusiveGroup {
                    id: planElementSelectorGroup
                    onCurrentChanged: {
                        switch (current) {
                        case planElementMission:
                            _editingLayer = _layerMission
631
                            break
632 633
                        case planElementGeoFence:
                            _editingLayer = _layerGeoFence
Don Gagne's avatar
Don Gagne committed
634
                            break
635 636
                        case planElementRallyPoints:
                            _editingLayer = _layerRallyPoints
Don Gagne's avatar
Don Gagne committed
637
                            break
638 639 640 641
                        }
                    }
                }

642 643 644 645 646 647 648 649
                QGCRadioButton {
                    id:             planElementMission
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Mission")
                    checked:        true
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
650 651
                }

652 653 654 655 656 657 658 659 660
                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementGeoFence
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Fence")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
661
                }
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677

                Item { height: 1; width: 1 }

                QGCRadioButton {
                    id:             planElementRallyPoints
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Rally")
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
                }
            } // Row - Plan Element Selector

            // Mission Item Editor
            Item {
                id:                 missionItemEditor
678
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
679 680 681 682 683 684 685 686 687 688 689
                anchors.top:        planElementSelectorRow.visible ? planElementSelectorRow.bottom : planElementSelectorRow.top
                anchors.left:       parent.left
                anchors.right:      parent.right
                anchors.bottom:     parent.bottom
                visible:            _editingLayer == _layerMission

                QGCListView {
                    id:             missionItemEditorListView
                    anchors.fill:   parent
                    spacing:        _margin / 2
                    orientation:    ListView.Vertical
690
                    model:          _missionController.visualItems
691 692
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
693
                    currentIndex:   _missionController.currentPlanViewIndex
694 695 696
                    highlightMoveDuration: 250

                    delegate: MissionItemEditor {
697
                        map:                editorMap
698
                        masterController:  _planMasterController
699 700 701 702
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
703

704
                        onClicked:  _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
705 706 707

                        onRemove: {
                            var removeIndex = index
708 709
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
710 711
                                removeIndex--
                            }
712
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
713 714
                        }

715 716
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
717 718 719 720 721
                    }
                } // QGCListView
            } // Item - Mission Item editor

            // GeoFence Editor
722 723 724 725 726 727
            GeoFenceEditor {
                anchors.topMargin:      ScreenTools.defaultFontPixelHeight / 2
                anchors.top:            planElementSelectorRow.bottom
                anchors.left:           parent.left
                anchors.right:          parent.right
                availableHeight:        ScreenTools.availableHeight
728
                myGeoFenceController:   _geoFenceController
729 730
                flightMap:              editorMap
                visible:                _editingLayer == _layerGeoFence
731 732 733 734 735 736
            }

            // Rally Point Editor

            RallyPointEditorHeader {
                id:                 rallyPointHeader
737 738
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        planElementSelectorRow.bottom
739 740 741
                anchors.left:       parent.left
                anchors.right:      parent.right
                visible:            _editingLayer == _layerRallyPoints
742
                controller:         _rallyPointController
743 744 745 746
            }

            RallyPointItemEditor {
                id:                 rallyPointEditor
747 748
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        rallyPointHeader.bottom
749 750
                anchors.left:       parent.left
                anchors.right:      parent.right
751 752 753
                visible:            _editingLayer == _layerRallyPoints && _rallyPointController.points.count
                rallyPoint:         _rallyPointController.currentRallyPoint
                controller:         _rallyPointController
754 755
            }
        } // Right panel
756 757 758 759 760 761 762

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
763
            visible:            _toolStripBottom < y
764 765 766 767 768 769
        }

        MissionItemStatus {
            id:                 waypointValuesDisplay
            anchors.margins:    ScreenTools.defaultFontPixelWidth
            anchors.left:       parent.left
770
            height:             ScreenTools.defaultFontPixelHeight * 7
771
            maxWidth:           parent.width - rightPanel.width - x
772
            anchors.bottom:     parent.bottom
773
            missionItems:       _missionController.visualItems
774
            visible:            _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus
775
        }
Don Gagne's avatar
Don Gagne committed
776
    } // QGCViewPanel
777

778 779 780 781
    Component {
        id: syncLoadFromVehicleOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
Don Gagne's avatar
Don Gagne committed
782
            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?")
783 784
            function accept() {
                hideDialog()
785
                masterController.loadFromVehicle()
786 787 788 789 790 791 792 793
            }
        }
    }

    Component {
        id: syncLoadFromFileOverwrite
        QGCViewMessage {
            id:         syncLoadFromVehicleCheck
DonLakeFlyer's avatar
DonLakeFlyer committed
794
            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?")
795 796
            function accept() {
                hideDialog()
797
                masterController.loadFromSelectedFile()
798 799 800 801
            }
        }
    }

802 803 804
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
805 806
            message: qsTr("Are you sure you want to remove all items? ") +
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
807
            function accept() {
808 809 810 811 812
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
813 814 815 816 817
                hideDialog()
            }
        }
    }

818 819 820 821 822 823 824 825 826 827 828
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

829 830 831 832 833 834 835 836 837
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
838
                model: _missionController.complexMissionItemNames
839 840 841 842 843 844

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
845
                        addComplexItem(modelData)
846 847 848 849 850 851
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
852 853

    Component {
854
        id: syncDropPanel
855

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

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

862 863 864
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
865
                text:       masterController.dirty ?
866 867
                                qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                qsTr("Sync:")
868 869
            }

870 871 872 873 874 875
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
876

877 878 879
                QGCButton {
                    text:               qsTr("Upload")
                    Layout.fillWidth:   true
880
                    enabled:            !masterController.offline && !masterController.syncInProgress
881 882
                    onClicked: {
                        dropPanel.hide()
883
                        masterController.upload()
884 885
                    }
                }
886

887 888 889
                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
890
                    enabled:            !masterController.offline && !masterController.syncInProgress
891 892
                    onClicked: {
                        dropPanel.hide()
893
                        if (masterController.dirty) {
894 895
                            _qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
896
                            masterController.loadFromVehicle()
897 898 899
                        }
                    }
                }
900

901
                QGCButton {
902
                    text:               qsTr("Save Plan...")
903
                    Layout.fillWidth:   true
904
                    enabled:            !masterController.syncInProgress
905 906
                    onClicked: {
                        dropPanel.hide()
907
                        masterController.saveToSelectedFile()
908 909 910 911
                    }
                }

                QGCButton {
912
                    text:               qsTr("Load Plan...")
913
                    Layout.fillWidth:   true
914
                    enabled:            !masterController.syncInProgress
915 916
                    onClicked: {
                        dropPanel.hide()
917
                        if (masterController.dirty) {
918 919
                            _qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
920
                            masterController.loadFromSelectedFile()
921 922 923 924 925
                        }
                    }
                }

                QGCButton {
926
                    text:               qsTr("Load KML...")
927
                    Layout.fillWidth:   true
928 929
                    enabled:            !masterController.syncInProgress
                    onClicked: {
930
                        dropPanel.hide()
931
                        masterController.loadKmlFromSelectedFile()
932 933
                    }
                }
934

935

936 937 938 939 940
                QGCButton {
                    text:               qsTr("Save KML...")
                    Layout.fillWidth:   true
                    enabled:            !masterController.syncInProgress
                    onClicked: {
941 942 943 944 945
                        // First point do not count
                        if (_visualItems.count < 2) {
                            _qgcView.showDialog(noItemForKML, qsTr("KML"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
                            return
                        }
946 947 948 949
                        dropPanel.hide()
                        masterController.saveKmlToSelectedFile()
                    }
                }
950 951 952 953 954 955 956 957 958

                QGCButton {
                    text:               qsTr("Remove All")
                    Layout.fillWidth:   true
                    onClicked:  {
                        dropPanel.hide()
                        _qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
                    }
                }
959
            }
960 961
        }
    }
Don Gagne's avatar
Don Gagne committed
962
} // QGCVIew