QGCUASFileView.cc 9.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (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
10

11
#include "QGCUASFileView.h"
Don Gagne's avatar
Don Gagne committed
12
#include "FileManager.h"
Don Gagne's avatar
Don Gagne committed
13
#include "QGCFileDialog.h"
Don Gagne's avatar
Don Gagne committed
14
#include "UAS.h"
15

Lorenz Meier's avatar
Lorenz Meier committed
16 17
#include <QFileDialog>
#include <QDir>
18
#include <QDebug>
Lorenz Meier's avatar
Lorenz Meier committed
19

Don Gagne's avatar
Don Gagne committed
20 21 22 23
QGCUASFileView::QGCUASFileView(QWidget *parent, Vehicle* vehicle)
    : QWidget(parent)
    , _manager(vehicle->uas()->getFileManager())
    , _currentCommand(commandNone)
24
{
Don Gagne's avatar
Don Gagne committed
25
    _ui.setupUi(this);
Don Gagne's avatar
Don Gagne committed
26

Don Gagne's avatar
Don Gagne committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
    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
45
}
Lorenz Meier's avatar
Lorenz Meier committed
46

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

90 91 92
/// @brief uploads a file into the currently selected directory the tree view
void QGCUASFileView::_uploadFile(void)
{
Don Gagne's avatar
Don Gagne committed
93 94 95 96
    if (_currentCommand != commandNone) {
        qWarning() << QString("Upload attempted while another command was in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
97 98 99

    _ui.statusText->clear();

100
    // get and check directory from list view
101 102 103 104 105
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() != _typeDir) {
        return;
    }

106 107 108 109 110 111 112
    // 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);
113

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

Don Gagne's avatar
Don Gagne committed
116 117
    _ui.statusText->setText(QString("Uploading: %1").arg(uploadFromHere));
                            
118
    qDebug() << "Upload: " << uploadFromHere << "to path" << path;
Don Gagne's avatar
Don Gagne committed
119 120 121
    
    _setAllButtonsEnabled(false);
    _currentCommand = commandUpload;
122

123
    _manager->uploadPath(path, uploadFromHere);
124 125
}

126
/// @brief Called to update the progress of the download.
Don Gagne's avatar
Don Gagne committed
127 128
///     @param value Progress bar value
void QGCUASFileView::_commandProgress(int value)
129
{
Don Gagne's avatar
Don Gagne committed
130
    _ui.progressBar->setValue(value);
131 132 133 134
}

/// @brief Called when an error occurs during a download.
///     @param msg Error message
Don Gagne's avatar
Don Gagne committed
135
void QGCUASFileView::_commandError(const QString& msg)
136
{
Don Gagne's avatar
Don Gagne committed
137 138 139
    _setAllButtonsEnabled(true);
    _currentCommand = commandNone;
    _ui.statusText->setText(QString("Error: %1").arg(msg));
140 141
}

142
/// @brief Refreshes the directory list tree.
Don Gagne's avatar
Don Gagne committed
143
void QGCUASFileView::_refreshTree(void)
144
{
Don Gagne's avatar
Don Gagne committed
145 146 147 148
    if (_currentCommand != commandNone) {
        qWarning() << QString("List attempted while another command was in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
149
    
150
    _ui.treeWidget->clear();
151
    _ui.statusText->clear();
Don Gagne's avatar
Don Gagne committed
152 153 154 155 156 157

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

Don Gagne's avatar
Don Gagne committed
161
    _requestDirectoryList("/");
162
}
Lorenz Meier's avatar
Lorenz Meier committed
163

164 165
/// @brief Adds the specified directory entry to the tree view.
void QGCUASFileView::_listEntryReceived(const QString& entry)
Lorenz Meier's avatar
Lorenz Meier committed
166
{
Don Gagne's avatar
Don Gagne committed
167 168 169 170
    if (_currentCommand != commandList) {
        qWarning() << QString("List entry received while no list command in progress: _currentCommand(%1)").arg(_currentCommand);
        return;
    }
171
    
Don Gagne's avatar
Don Gagne committed
172
    int type;
173
    if (entry.startsWith("F")) {
Don Gagne's avatar
Don Gagne committed
174
        type = _typeFile;
175
    } else if (entry.startsWith("D")) {
Don Gagne's avatar
Don Gagne committed
176
        type = _typeDir;
177
        if (entry == "D." || entry == "D..") {
Don Gagne's avatar
Don Gagne committed
178 179 180 181
            return;
        }
    } else {
        Q_ASSERT(false);
182
        return; // Silence maybe-unitialized on type
Don Gagne's avatar
Don Gagne committed
183 184 185
    }

    QTreeWidgetItem* item;
186
    item = new QTreeWidgetItem(_walkItemStack.last(), type);
Don Gagne's avatar
Don Gagne committed
187 188
    Q_CHECK_PTR(item);
    
189
    item->setText(0, entry.right(entry.size() - 1));
Lorenz Meier's avatar
Lorenz Meier committed
190 191
}

Don Gagne's avatar
Don Gagne committed
192 193
/// @brief Called when a command completes successfully
void QGCUASFileView::_commandComplete(void)
Lorenz Meier's avatar
Lorenz Meier committed
194
{
Don Gagne's avatar
Don Gagne committed
195 196 197 198 199 200 201 202 203 204 205 206
    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();
207
    }
Don Gagne's avatar
Don Gagne committed
208 209 210
    
    _ui.statusText->setText(statusText);
    _ui.progressBar->reset();
Don Gagne's avatar
Don Gagne committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
}

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
246
        _requestDirectoryList(dir);
Don Gagne's avatar
Don Gagne committed
247 248 249 250 251 252 253
    } 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
254 255
            _setAllButtonsEnabled(true);
            _currentCommand = commandNone;
Don Gagne's avatar
Don Gagne committed
256 257 258 259 260 261 262
        }
    }
}

void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
    Q_UNUSED(previous);
Don Gagne's avatar
Don Gagne committed
263
    
264
    _ui.downloadButton->setEnabled(current ? (current->type() == _typeFile) : false);
265
    _ui.uploadButton->setEnabled(current ? (current->type() == _typeDir) : false);
Lorenz Meier's avatar
Lorenz Meier committed
266
}
Don Gagne's avatar
Don Gagne committed
267

268
void QGCUASFileView::_requestDirectoryList(const QString& dir)
Don Gagne's avatar
Don Gagne committed
269
{
270
    _manager->listDirectory(dir);
Don Gagne's avatar
Don Gagne committed
271 272
}

Don Gagne's avatar
Don Gagne committed
273
void QGCUASFileView::_setAllButtonsEnabled(bool enabled)
Don Gagne's avatar
Don Gagne committed
274
{
Don Gagne's avatar
Don Gagne committed
275 276 277 278
    _ui.treeWidget->setEnabled(enabled);
    _ui.downloadButton->setEnabled(enabled);
    _ui.listFilesButton->setEnabled(enabled);
    _ui.uploadButton->setEnabled(enabled);
279
    
Don Gagne's avatar
Don Gagne committed
280 281 282
    if (enabled) {
        _currentItemChanged(_ui.treeWidget->currentItem(), NULL);
    }
Don Gagne's avatar
Don Gagne committed
283
}