QGCUASFileView.cc 9.78 KB
Newer Older
Don Gagne's avatar
Don Gagne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*=====================================================================
 
 QGroundControl Open Source Ground Control Station
 
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 
 This file is part of the QGROUNDCONTROL project
 
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
 
 ======================================================================*/

24
#include "QGCUASFileView.h"
Don Gagne's avatar
Don Gagne committed
25
#include "FileManager.h"
Don Gagne's avatar
Don Gagne committed
26
#include "QGCFileDialog.h"
Don Gagne's avatar
Don Gagne committed
27
#include "UAS.h"
28

Lorenz Meier's avatar
Lorenz Meier committed
29 30
#include <QFileDialog>
#include <QDir>
31
#include <QDebug>
Lorenz Meier's avatar
Lorenz Meier committed
32

Don Gagne's avatar
Don Gagne committed
33 34 35 36
QGCUASFileView::QGCUASFileView(QWidget *parent, Vehicle* vehicle)
    : QWidget(parent)
    , _manager(vehicle->uas()->getFileManager())
    , _currentCommand(commandNone)
37
{
Don Gagne's avatar
Don Gagne committed
38
    _ui.setupUi(this);
Don Gagne's avatar
Don Gagne committed
39

Don Gagne's avatar
Don Gagne committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    if (vehicle->px4Firmware()) {
        _ui.progressBar->reset();

        // Connect UI signals
        connect(_ui.listFilesButton,    &QPushButton::clicked,              this, &QGCUASFileView::_refreshTree);
        connect(_ui.downloadButton,     &QPushButton::clicked,              this, &QGCUASFileView::_downloadFile);
        connect(_ui.uploadButton,       &QPushButton::clicked,              this, &QGCUASFileView::_uploadFile);
        connect(_ui.treeWidget,         &QTreeWidget::currentItemChanged,   this, &QGCUASFileView::_currentItemChanged);

        // Connect signals from FileManager
        connect(_manager, &FileManager::commandProgress,    this, &QGCUASFileView::_commandProgress);
        connect(_manager, &FileManager::commandComplete,    this, &QGCUASFileView::_commandComplete);
        connect(_manager, &FileManager::commandError,       this, &QGCUASFileView::_commandError);
        connect(_manager, &FileManager::listEntry,  this, &QGCUASFileView::_listEntryReceived);
    } else {
        _setAllButtonsEnabled(false);
        _ui.statusText->setText(QStringLiteral("Onboard Files not supported by this Vehicle"));
    }
Don Gagne's avatar
Don Gagne committed
58
}
Lorenz Meier's avatar
Lorenz Meier committed
59

60 61
/// @brief Downloads the file currently selected in the tree view
void QGCUASFileView::_downloadFile(void)
Don Gagne's avatar
Don Gagne committed
62
{
Don Gagne's avatar
Don Gagne committed
63 64 65 66
    if (_currentCommand != commandNone) {
        qWarning() << QString("Download attempted while another command was in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
67 68 69
    
    _ui.statusText->clear();
    
Don Gagne's avatar
Don Gagne committed
70 71 72 73
    QString downloadToHere = QGCFileDialog::getExistingDirectory(this,
                                                                 "Download Directory",
                                                                 QDir::homePath(),
                                                                 QGCFileDialog::ShowDirsOnly | QGCFileDialog::DontResolveSymlinks);
74
    
Don Gagne's avatar
Don Gagne committed
75
    // And now download to this location
Don Gagne's avatar
Don Gagne committed
76
    
Don Gagne's avatar
Don Gagne committed
77
    QString path;
Don Gagne's avatar
Don Gagne committed
78 79
    QString downloadFilename;
    
Don Gagne's avatar
Don Gagne committed
80 81 82
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() == _typeFile) {
        do {
83
            QString name = item->text(0).split("\t")[0];    // Strip off file sizes
84 85
            
            // If this is the file name and not a directory keep track of the download file name
Don Gagne's avatar
Don Gagne committed
86 87
            if (downloadFilename.isEmpty()) {
                downloadFilename = name;
88 89
            }
            
90
            path.prepend("/" + name);
Don Gagne's avatar
Don Gagne committed
91 92 93
            item = item->parent();
        } while (item);
        
Don Gagne's avatar
Don Gagne committed
94 95
        _setAllButtonsEnabled(false);
        _currentCommand = commandDownload;
96
        
Don Gagne's avatar
Don Gagne committed
97 98
        _ui.statusText->setText(QString("Downloading: %1").arg(downloadFilename));
                                
99
        _manager->streamPath(path, QDir(downloadToHere));
100 101 102
    }
}

103 104 105
/// @brief uploads a file into the currently selected directory the tree view
void QGCUASFileView::_uploadFile(void)
{
Don Gagne's avatar
Don Gagne committed
106 107 108 109
    if (_currentCommand != commandNone) {
        qWarning() << QString("Upload attempted while another command was in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
110 111 112

    _ui.statusText->clear();

113
    // get and check directory from list view
114 115 116 117 118
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() != _typeDir) {
        return;
    }

119 120 121 122 123 124 125
    // Find complete path for upload directory
    QString path;
    do {
        QString name = item->text(0).split("\t")[0];    // Strip off file sizes
        path.prepend("/" + name);
        item = item->parent();
    } while (item);
126

Don Gagne's avatar
Don Gagne committed
127
    QString uploadFromHere = QGCFileDialog::getOpenFileName(this, "Upload File", QDir::homePath());
128

Don Gagne's avatar
Don Gagne committed
129 130
    _ui.statusText->setText(QString("Uploading: %1").arg(uploadFromHere));
                            
131
    qDebug() << "Upload: " << uploadFromHere << "to path" << path;
Don Gagne's avatar
Don Gagne committed
132 133 134
    
    _setAllButtonsEnabled(false);
    _currentCommand = commandUpload;
135

136
    _manager->uploadPath(path, uploadFromHere);
137 138
}

139
/// @brief Called to update the progress of the download.
Don Gagne's avatar
Don Gagne committed
140 141
///     @param value Progress bar value
void QGCUASFileView::_commandProgress(int value)
142
{
Don Gagne's avatar
Don Gagne committed
143
    _ui.progressBar->setValue(value);
144 145 146 147
}

/// @brief Called when an error occurs during a download.
///     @param msg Error message
Don Gagne's avatar
Don Gagne committed
148
void QGCUASFileView::_commandError(const QString& msg)
149
{
Don Gagne's avatar
Don Gagne committed
150 151 152
    _setAllButtonsEnabled(true);
    _currentCommand = commandNone;
    _ui.statusText->setText(QString("Error: %1").arg(msg));
153 154
}

155
/// @brief Refreshes the directory list tree.
Don Gagne's avatar
Don Gagne committed
156
void QGCUASFileView::_refreshTree(void)
157
{
Don Gagne's avatar
Don Gagne committed
158 159 160 161
    if (_currentCommand != commandNone) {
        qWarning() << QString("List attempted while another command was in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
162
    
163
    _ui.treeWidget->clear();
164
    _ui.statusText->clear();
Don Gagne's avatar
Don Gagne committed
165 166 167 168 169 170

    _walkIndexStack.clear();
    _walkItemStack.clear();
    _walkIndexStack.append(0);
    _walkItemStack.append(_ui.treeWidget->invisibleRootItem());
    
Don Gagne's avatar
Don Gagne committed
171 172
    _setAllButtonsEnabled(false);
    _currentCommand = commandList;
Don Gagne's avatar
Don Gagne committed
173

Don Gagne's avatar
Don Gagne committed
174
    _requestDirectoryList("/");
175
}
Lorenz Meier's avatar
Lorenz Meier committed
176

177 178
/// @brief Adds the specified directory entry to the tree view.
void QGCUASFileView::_listEntryReceived(const QString& entry)
Lorenz Meier's avatar
Lorenz Meier committed
179
{
Don Gagne's avatar
Don Gagne committed
180 181 182 183
    if (_currentCommand != commandList) {
        qWarning() << QString("List entry received while no list command in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
184
    
Don Gagne's avatar
Don Gagne committed
185
    int type;
186
    if (entry.startsWith("F")) {
Don Gagne's avatar
Don Gagne committed
187
        type = _typeFile;
188
    } else if (entry.startsWith("D")) {
Don Gagne's avatar
Don Gagne committed
189
        type = _typeDir;
190
        if (entry == "D." || entry == "D..") {
Don Gagne's avatar
Don Gagne committed
191 192 193 194
            return;
        }
    } else {
        Q_ASSERT(false);
195
        return; // Silence maybe-unitialized on type
Don Gagne's avatar
Don Gagne committed
196 197 198
    }

    QTreeWidgetItem* item;
199
    item = new QTreeWidgetItem(_walkItemStack.last(), type);
Don Gagne's avatar
Don Gagne committed
200 201
    Q_CHECK_PTR(item);
    
202
    item->setText(0, entry.right(entry.size() - 1));
Lorenz Meier's avatar
Lorenz Meier committed
203 204
}

Don Gagne's avatar
Don Gagne committed
205 206
/// @brief Called when a command completes successfully
void QGCUASFileView::_commandComplete(void)
Lorenz Meier's avatar
Lorenz Meier committed
207
{
Don Gagne's avatar
Don Gagne committed
208 209 210 211 212 213 214 215 216 217 218 219
    QString statusText;
    
    if (_currentCommand == commandDownload) {
        _currentCommand = commandNone;
        _setAllButtonsEnabled(true);
        statusText = "Download complete";
    } else if (_currentCommand == commandDownload) {
        _currentCommand = commandNone;
        _setAllButtonsEnabled(true);
        statusText = "Upload complete";
    } else if (_currentCommand == commandList) {
        _listComplete();
220
    }
Don Gagne's avatar
Don Gagne committed
221 222 223
    
    _ui.statusText->setText(statusText);
    _ui.progressBar->reset();
Don Gagne's avatar
Don Gagne committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
}

void QGCUASFileView::_listComplete(void)
{
    // Walk the current items, traversing down into directories
    
Again:
    int walkIndex = _walkIndexStack.last();
    QTreeWidgetItem* parentItem = _walkItemStack.last();
    QTreeWidgetItem* childItem = parentItem->child(walkIndex);

    // Loop until we hit a directory
    while (childItem && childItem->type() != _typeDir) {
        // Move to next index at current level
        _walkIndexStack.last() = ++walkIndex;
        childItem = parentItem->child(walkIndex);
    }
    
    if (childItem) {
        // Process this item
        QString text = childItem->text(0);
        
        // Move to the next item for processing at this level
        _walkIndexStack.last() = ++walkIndex;
        
        // Push this new directory on the stack
        _walkItemStack.append(childItem);
        _walkIndexStack.append(0);
        
        // Ask for the directory list
        QString dir;
        for (int i=1; i<_walkItemStack.count(); i++) {
            QTreeWidgetItem* item = _walkItemStack[i];
            dir.append("/" + item->text(0));
        }
Don Gagne's avatar
Don Gagne committed
259
        _requestDirectoryList(dir);
Don Gagne's avatar
Don Gagne committed
260 261 262 263 264 265 266
    } else {
        // We have run out of items at the this level, pop the stack and keep going at that level
        _walkIndexStack.removeLast();
        _walkItemStack.removeLast();
        if (_walkIndexStack.count() != 0) {
            goto Again;
        } else {
Don Gagne's avatar
Don Gagne committed
267 268
            _setAllButtonsEnabled(true);
            _currentCommand = commandNone;
Don Gagne's avatar
Don Gagne committed
269 270 271 272 273 274 275
        }
    }
}

void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
    Q_UNUSED(previous);
Don Gagne's avatar
Don Gagne committed
276
    
277
    _ui.downloadButton->setEnabled(current ? (current->type() == _typeFile) : false);
278
    _ui.uploadButton->setEnabled(current ? (current->type() == _typeDir) : false);
Lorenz Meier's avatar
Lorenz Meier committed
279
}
Don Gagne's avatar
Don Gagne committed
280

281
void QGCUASFileView::_requestDirectoryList(const QString& dir)
Don Gagne's avatar
Don Gagne committed
282
{
283
    _manager->listDirectory(dir);
Don Gagne's avatar
Don Gagne committed
284 285
}

Don Gagne's avatar
Don Gagne committed
286
void QGCUASFileView::_setAllButtonsEnabled(bool enabled)
Don Gagne's avatar
Don Gagne committed
287
{
Don Gagne's avatar
Don Gagne committed
288 289 290 291
    _ui.treeWidget->setEnabled(enabled);
    _ui.downloadButton->setEnabled(enabled);
    _ui.listFilesButton->setEnabled(enabled);
    _ui.uploadButton->setEnabled(enabled);
292
    
Don Gagne's avatar
Don Gagne committed
293 294 295
    if (enabled) {
        _currentItemChanged(_ui.treeWidget->currentItem(), NULL);
    }
Don Gagne's avatar
Don Gagne committed
296
}