PlanView.qml 41.4 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")
DonLakeFlyer's avatar
DonLakeFlyer committed
188
            fileDialog.planFiles =      true
189
            fileDialog.selectExisting = true
190
            fileDialog.nameFilters =    masterController.loadNameFilters
191 192
            fileDialog.fileExtension =  QGroundControl.settingsManager.appSettings.planFileExtension
            fileDialog.fileExtension2 = QGroundControl.settingsManager.appSettings.missionFileExtension
193
            fileDialog.openForLoad()
194 195 196
        }

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

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

214 215 216 217 218 219 220 221 222 223
        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()
        }

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

239 240
    Connections {
        target: _missionController
241

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

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

252 253 254 255
    ExclusiveGroup {
        id: _mapTypeButtonsExclusiveGroup
    }

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

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

274 275
    property int _moveDialogMissionItemIndex

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

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

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

292
        onAcceptedForLoad: {
293 294 295 296 297 298 299 300 301
            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) {
Don Gagne's avatar
Don Gagne committed
302 303 304 305 306 307 308
                    var editVehicle = _activeVehicle ? _activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle
                    if (editVehicle.fixedWing) {
                        insertComplexMissionItemFromKML(_missionController.surveyComplexItemName, file, -1)
                    } else {
                        kmlPolygonSelectDialogKMLFile = file
                        _qgcView.showDialog(kmlPolygonSelectDialog, fileDialog.title, _qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
                    }
309 310 311 312
                } else if (retList[0] == KMLFileHelper.Polyline) {
                    insertComplexMissionItemFromKML(_missionController.corridorScanComplexItemName, file, -1)
                }
            }
313
            close()
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
    property string kmlPolygonSelectDialogKMLFile
    Component {
        id: kmlPolygonSelectDialog

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

            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
                }
            }
        }
    }

364 365 366 367 368 369 370
    Component {
        id: moveDialog

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

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

                QGCComboBox {
                    id:             toCombo
392
                    model:          _visualItems.count
393 394 395 396 397 398
                    currentIndex:   _moveDialogMissionItemIndex
                }
            }
        }
    }

Don Gagne's avatar
Don Gagne committed
399 400
    QGCViewPanel {
        id:             panel
401
        anchors.fill:   parent
Don Gagne's avatar
Don Gagne committed
402

403
        FlightMap {
404 405 406 407 408
            id:                         editorMap
            anchors.fill:               parent
            mapName:                    "MissionEditor"
            allowGCSLocationCenter:     true
            allowVehicleLocationCenter: true
409
            planView:                   true
410
            qgcView:                    _qgcView
Don Gagne's avatar
Don Gagne committed
411

412 413
            // 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)
414

415 416
            property real _leftToolWidth:   toolStrip.x + toolStrip.width
            property real _statusHeight:    waypointValuesDisplay.visible ? editorMap.height - waypointValuesDisplay.y : 0
417

418
            readonly property real animationDuration: 500
419

420 421
            // Initial map position duplicates Fly view position
            Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
422

423 424 425 426 427 428
            Behavior on zoomLevel {
                NumberAnimation {
                    duration:       editorMap.animationDuration
                    easing.type:    Easing.InOutQuad
                }
            }
429

430 431 432 433 434 435 436
            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: {
437 438 439
                    // Take focus to close any previous editing
                    editorMap.focus = true

440 441 442 443
                    //-- Don't pay attention to items beneath the toolbar.
                    var topLimit = parent.height - ScreenTools.availableHeight
                    if(mouse.y < topLimit) {
                        return
444 445
                    }

446 447 448 449
                    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)
450

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

469 470
            // Add the mission item visuals to the map
            Repeater {
471
                model: _editingLayer == _layerMission ? _missionController.visualItems : undefined
472

473 474
                delegate: MissionItemMapVisual {
                    map:        editorMap
DonLakeFlyer's avatar
DonLakeFlyer committed
475
                    qgcView:    _qgcView
476
                    onClicked:  _missionController.setCurrentPlanViewIndex(sequenceNumber, false)
477
                    visible:    _editingLayer == _layerMission
478
                }
479
            }
480

481 482
            // Add lines between waypoints
            MissionLineView {
483
                model: _editingLayer == _layerMission ? _missionController.waypointLines : undefined
484
            }
485

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

499 500
            GeoFenceMapVisuals {
                map:                    editorMap
501
                myGeoFenceController:   _geoFenceController
502
                interactive:            _editingLayer == _layerGeoFence
503
                homePosition:           _missionController.plannedHomePosition
504 505
                planView:               true
            }
506

507 508
            RallyPointMapVisuals {
                map:                    editorMap
509
                myRallyPointController: _rallyPointController
510 511
                interactive:            _editingLayer == _layerRallyPoints
                planView:               true
512
            }
513

514 515 516 517 518 519 520 521 522
            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
523 524 525 526 527
                showAlternateIcon:  [ masterController.dirty, false, false, false, false, false, false ]
                rotateImage:        [ masterController.syncInProgress, false, false, false, false, false, false ]
                animateImage:       [ masterController.dirty, false, false, false, false, false, false ]
                buttonEnabled:      [ !masterController.syncInProgress, true, true, true, true, true, true ]
                buttonVisible:      [ true, true, _waypointsOnlyMode, true, true, _showZoom, _showZoom ]
528 529 530 531 532 533
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

                model: [
                    {
534
                        name:                   qsTr("File"),
535 536 537 538 539
                        iconSource:             "/qmlimages/MapSync.svg",
                        alternateIconSource:    "/qmlimages/MapSyncChanged.svg",
                        dropPanelComponent:     syncDropPanel
                    },
                    {
540
                        name:                   qsTr("Waypoint"),
541 542
                        iconSource:             "/qmlimages/MapAddMission.svg",
                        toggle:                 true
543
                    },
544
                    {
545
                        name:                   qsTr("ROI"),
546 547
                        iconSource:             "/qmlimages/MapAddMission.svg",
                        toggle:                 true
548
                    },
549
                    {
550
                        name:               _singleComplexItem ? _missionController.complexMissionItemNames[0] : qsTr("Pattern"),
551 552 553 554
                        iconSource:         "/qmlimages/MapDrawShape.svg",
                        dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
                    },
                    {
555
                        name:               qsTr("Center"),
556 557 558 559
                        iconSource:         "/qmlimages/MapCenter.svg",
                        dropPanelComponent: centerMapDropPanel
                    },
                    {
560
                        name:               qsTr("In"),
561 562 563
                        iconSource:         "/qmlimages/ZoomPlus.svg"
                    },
                    {
564
                        name:               qsTr("Out"),
565 566 567 568 569 570
                        iconSource:         "/qmlimages/ZoomMinus.svg"
                    }
                ]

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

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

        Item {
            anchors.fill:   rightPanel
608 609 610 611

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

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

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

638 639 640 641 642 643 644 645
                QGCRadioButton {
                    id:             planElementMission
                    exclusiveGroup: planElementSelectorGroup
                    text:           qsTr("Mission")
                    checked:        true
                    color:          mapPal.text
                    textStyle:      Text.Outline
                    textStyleColor: mapPal.textOutline
646 647
                }

648 649 650 651 652 653 654 655 656
                Item { height: 1; width: 1 }

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

                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
674
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
675 676 677 678 679 680 681 682 683 684 685
                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
686
                    model:          _missionController.visualItems
687 688
                    cacheBuffer:    Math.max(height * 2, 0)
                    clip:           true
689
                    currentIndex:   _missionController.currentPlanViewIndex
690 691 692
                    highlightMoveDuration: 250

                    delegate: MissionItemEditor {
693
                        map:                editorMap
694
                        masterController:  _planMasterController
695 696 697 698
                        missionItem:        object
                        width:              parent.width
                        readOnly:           false
                        rootQgcView:        _qgcView
699

700
                        onClicked:  _missionController.setCurrentPlanViewIndex(object.sequenceNumber, false)
701 702 703

                        onRemove: {
                            var removeIndex = index
704 705
                            _missionController.removeMissionItem(removeIndex)
                            if (removeIndex >= _missionController.visualItems.count) {
706 707
                                removeIndex--
                            }
708
                            _missionController.setCurrentPlanViewIndex(removeIndex, true)
709 710
                        }

711 712
                        onInsertWaypoint:       insertSimpleMissionItem(editorMap.center, index)
                        onInsertComplexItem:    insertComplexMissionItem(complexItemName, editorMap.center, index)
713 714 715 716 717
                    }
                } // QGCListView
            } // Item - Mission Item editor

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

            // Rally Point Editor

            RallyPointEditorHeader {
                id:                 rallyPointHeader
733 734
                anchors.topMargin:  ScreenTools.defaultFontPixelHeight / 2
                anchors.top:        planElementSelectorRow.bottom
735 736 737
                anchors.left:       parent.left
                anchors.right:      parent.right
                visible:            _editingLayer == _layerRallyPoints
738
                controller:         _rallyPointController
739 740 741 742
            }

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

        MapScale {
            id:                 mapScale
            anchors.margins:    ScreenTools.defaultFontPixelHeight * (0.66)
            anchors.bottom:     waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom
            anchors.left:       parent.left
            mapControl:         editorMap
759
            visible:            _toolStripBottom < y
760 761 762 763 764 765
        }

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

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

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

798 799 800
    Component {
        id: removeAllPromptDialog
        QGCViewMessage {
801
            message: qsTr("Are you sure you want to remove all items and create a new plan? ") +
802
                     (_planMasterController.offline ? "" : qsTr("This will also remove all items from the vehicle."))
803
            function accept() {
804 805 806 807 808
                if (_planMasterController.offline) {
                    masterController.removeAll()
                } else {
                    masterController.removeAllFromVehicle()
                }
809 810 811 812 813
                hideDialog()
            }
        }
    }

814 815 816 817 818 819 820 821 822 823 824
    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() {
                masterController.removeAllFromVehicle()
                hideDialog()
            }
        }
    }

825 826 827 828 829 830 831 832 833 834 835
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

836 837 838 839 840 841 842 843 844
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
845
                model: _missionController.complexMissionItemNames
846 847 848 849 850 851

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
852
                        addComplexItem(modelData)
853 854 855 856 857 858
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
859 860

    Component {
861
        id: syncDropPanel
862

863 864 865
        Column {
            id:         columnHolder
            spacing:    _margin
866

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

869 870 871
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
872
                text:       masterController.dirty ?
873 874 875 876 877
                                (_activeVehicle ?
                                     qsTr("You have unsaved changes. You should upload to your vehicle, or save to a file:") :
                                     qsTr("You have unsaved changes.")
                                ) :
                                qsTr("Plan File:")
878 879
            }

880 881 882 883 884 885
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
886

887
                QGCButton {
888
                    text:               qsTr("New...")
889
                    Layout.fillWidth:   true
890 891
                    enabled:            _visualItems.count > 1
                    onClicked:  {
892
                        dropPanel.hide()
893
                        _qgcView.showDialog(removeAllPromptDialog, qsTr("New Plan"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
894 895 896 897
                    }
                }

                QGCButton {
898
                    text:               qsTr("Open...")
899
                    Layout.fillWidth:   true
900
                    enabled:            !masterController.syncInProgress
901 902
                    onClicked: {
                        dropPanel.hide()
903
                        if (masterController.dirty) {
904 905
                            _qgcView.showDialog(syncLoadFromFileOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
906
                            masterController.loadFromSelectedFile()
907 908 909 910
                        }
                    }
                }

911 912 913
                QGCButton {
                    text:               qsTr("Save")
                    Layout.fillWidth:   true
914
                    enabled:            !masterController.syncInProgress && masterController.currentPlanFile !== ""
915 916 917 918 919 920 921 922 923 924 925 926 927
                    onClicked: {
                        dropPanel.hide()
                        if(masterController.currentPlanFile !== "") {
                            masterController.saveToCurrent()
                        } else {
                            masterController.saveToSelectedFile()
                        }
                    }
                }

                QGCButton {
                    text:               qsTr("Save As...")
                    Layout.fillWidth:   true
928
                    enabled:            !masterController.syncInProgress && _visualItems.count > 1
929 930 931 932 933 934
                    onClicked: {
                        dropPanel.hide()
                        masterController.saveToSelectedFile()
                    }
                }

935
                QGCButton {
936
                    text:               qsTr("Load KML...")
937
                    Layout.fillWidth:   true
938 939
                    enabled:            !masterController.syncInProgress
                    onClicked: {
940
                        dropPanel.hide()
941
                        masterController.loadKmlFromSelectedFile()
942 943
                    }
                }
944

945

946 947 948
                QGCButton {
                    text:               qsTr("Save KML...")
                    Layout.fillWidth:   true
949
                    enabled:            !masterController.syncInProgress && _visualItems.count > 1
950
                    onClicked: {
951
                        // First point does not count
952 953 954 955
                        if (_visualItems.count < 2) {
                            _qgcView.showDialog(noItemForKML, qsTr("KML"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
                            return
                        }
956 957 958 959
                        dropPanel.hide()
                        masterController.saveKmlToSelectedFile()
                    }
                }
960

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
                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
                }

                QGCButton {
                    text:               qsTr("Upload")
                    Layout.fillWidth:   true
                    enabled:            !masterController.offline && !masterController.syncInProgress && _visualItems.count > 1
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
                        dropPanel.hide()
                        masterController.upload()
                    }
                }

                QGCButton {
                    text:               qsTr("Download")
                    Layout.fillWidth:   true
                    enabled:            !masterController.offline && !masterController.syncInProgress
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
                        dropPanel.hide()
                        if (masterController.dirty) {
                            _qgcView.showDialog(syncLoadFromVehicleOverwrite, columnHolder._overwriteText, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
                        } else {
                            masterController.loadFromVehicle()
                        }
                    }
                }

                QGCButton {
                    text:               qsTr("Clear Vehicle Mission")
                    Layout.fillWidth:   true
                    Layout.columnSpan:  2
                    enabled:            !masterController.offline && !masterController.syncInProgress
                    visible:            !QGroundControl.corePlugin.options.disableVehicleConnection
                    onClicked: {
                        dropPanel.hide()
1005
                        _qgcView.showDialog(clearVehicleMissionDialog, text, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1006 1007 1008
                    }
                }

1009
            }
1010 1011
        }
    }
Don Gagne's avatar
Don Gagne committed
1012
} // QGCVIew