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 302 303 304 305 306 307
            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)
                }
            }
308
            close()
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 368
    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
                }
            }
        }
    }

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

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

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

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

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

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

417 418
            // 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)
419

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

423
            readonly property real animationDuration: 500
424

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

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

435 436 437 438 439 440 441
            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: {
442 443 444
                    // Take focus to close any previous editing
                    editorMap.focus = true

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

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

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

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

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

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

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

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

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

519 520 521 522 523 524 525 526 527
            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
528 529 530 531 532
                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 ]
533 534 535 536 537 538
                maxHeight:          mapScale.y - toolStrip.y

                property bool _showZoom: !ScreenTools.isMobile

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

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

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

        Item {
            anchors.fill:   rightPanel
613 614 615 616

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

                readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75

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

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

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

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

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

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

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

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

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

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

            // Rally Point Editor

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

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

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

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

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

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

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

819 820 821 822 823 824 825 826 827 828 829
    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()
            }
        }
    }

830 831 832 833 834 835 836 837 838 839 840
    //- ToolStrip DropPanel Components

    Component {
        id: centerMapDropPanel

        CenterMapDropPanel {
            map:            editorMap
            fitFunctions:   mapFitFunctions
        }
    }

841 842 843 844 845 846 847 848 849
    Component {
        id: patternDropPanel

        ColumnLayout {
            spacing:    ScreenTools.defaultFontPixelWidth * 0.5

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

            Repeater {
850
                model: _missionController.complexMissionItemNames
851 852 853 854 855 856

                QGCButton {
                    text:               modelData
                    Layout.fillWidth:   true

                    onClicked: {
857
                        addComplexItem(modelData)
858 859 860 861 862 863
                        dropPanel.hide()
                    }
                }
            }
        } // Column
    }
864 865

    Component {
866
        id: syncDropPanel
867

868 869 870
        Column {
            id:         columnHolder
            spacing:    _margin
871

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

874 875 876
            QGCLabel {
                width:      sendSaveGrid.width
                wrapMode:   Text.WordWrap
877
                text:       masterController.dirty ?
878 879 880 881 882
                                (_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:")
883 884
            }

885 886 887 888 889 890
            GridLayout {
                id:                 sendSaveGrid
                columns:            2
                anchors.margins:    _margin
                rowSpacing:         _margin
                columnSpacing:      ScreenTools.defaultFontPixelWidth
891

892
                QGCButton {
893
                    text:               qsTr("New...")
894
                    Layout.fillWidth:   true
895 896
                    enabled:            _visualItems.count > 1
                    onClicked:  {
897
                        dropPanel.hide()
898
                        _qgcView.showDialog(removeAllPromptDialog, qsTr("New Plan"), _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No)
899 900 901 902
                    }
                }

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

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

                QGCButton {
                    text:               qsTr("Save As...")
                    Layout.fillWidth:   true
933
                    enabled:            !masterController.syncInProgress && _visualItems.count > 1
934 935 936 937 938 939
                    onClicked: {
                        dropPanel.hide()
                        masterController.saveToSelectedFile()
                    }
                }

940
                QGCButton {
941
                    text:               qsTr("Load KML...")
942
                    Layout.fillWidth:   true
943 944
                    enabled:            !masterController.syncInProgress
                    onClicked: {
945
                        dropPanel.hide()
946
                        masterController.loadKmlFromSelectedFile()
947 948
                    }
                }
949

950

951 952 953
                QGCButton {
                    text:               qsTr("Save KML...")
                    Layout.fillWidth:   true
954
                    enabled:            !masterController.syncInProgress && _visualItems.count > 1
955
                    onClicked: {
956
                        // First point does not count
957 958 959 960
                        if (_visualItems.count < 2) {
                            _qgcView.showDialog(noItemForKML, qsTr("KML"), _qgcView.showDialogDefaultWidth, StandardButton.Cancel)
                            return
                        }
961 962 963 964
                        dropPanel.hide()
                        masterController.saveKmlToSelectedFile()
                    }
                }
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 1005 1006 1007 1008 1009
                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()
1010
                        _qgcView.showDialog(clearVehicleMissionDialog, text, _qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel)
1011 1012 1013
                    }
                }

1014
            }
1015 1016
        }
    }
Don Gagne's avatar
Don Gagne committed
1017
} // QGCVIew