QGCUASFileView.cc 9.53 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"
27

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

32
QGCUASFileView::QGCUASFileView(QWidget *parent, FileManager *manager) :
33
    QWidget(parent),
34
    _manager(manager),
Don Gagne's avatar
Don Gagne committed
35
    _currentCommand(commandNone)
36
{
Don Gagne's avatar
Don Gagne committed
37
    _ui.setupUi(this);
38
    
Don Gagne's avatar
Don Gagne committed
39
    _ui.progressBar->reset();
40 41
    
    // Connect UI signals
Don Gagne's avatar
Don Gagne committed
42 43 44 45 46 47 48 49 50 51
    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);
Don Gagne's avatar
Don Gagne committed
52
}
Lorenz Meier's avatar
Lorenz Meier committed
53

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

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

    _ui.statusText->clear();

107
    // get and check directory from list view
108 109 110 111 112
    QTreeWidgetItem* item = _ui.treeWidget->currentItem();
    if (item && item->type() != _typeDir) {
        return;
    }

113 114 115 116 117 118 119
    // 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);
120

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

Don Gagne's avatar
Don Gagne committed
123 124
    _ui.statusText->setText(QString("Uploading: %1").arg(uploadFromHere));
                            
125
    qDebug() << "Upload: " << uploadFromHere << "to path" << path;
Don Gagne's avatar
Don Gagne committed
126 127 128
    
    _setAllButtonsEnabled(false);
    _currentCommand = commandUpload;
129

130
    _manager->uploadPath(path, uploadFromHere);
131 132
}

133
/// @brief Called to update the progress of the download.
Don Gagne's avatar
Don Gagne committed
134 135
///     @param value Progress bar value
void QGCUASFileView::_commandProgress(int value)
136
{
Don Gagne's avatar
Don Gagne committed
137
    _ui.progressBar->setValue(value);
138 139 140 141
}

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

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

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

Don Gagne's avatar
Don Gagne committed
168
    _requestDirectoryList("/");
169
}
Lorenz Meier's avatar
Lorenz Meier committed
170

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

    QTreeWidgetItem* item;
193
    item = new QTreeWidgetItem(_walkItemStack.last(), type);
Don Gagne's avatar
Don Gagne committed
194 195
    Q_CHECK_PTR(item);
    
196
    item->setText(0, entry.right(entry.size() - 1));
Lorenz Meier's avatar
Lorenz Meier committed
197 198
}

Don Gagne's avatar
Don Gagne committed
199 200
/// @brief Called when a command completes successfully
void QGCUASFileView::_commandComplete(void)
Lorenz Meier's avatar
Lorenz Meier committed
201
{
Don Gagne's avatar
Don Gagne committed
202 203 204 205 206 207 208 209 210 211 212 213
    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();
214
    }
Don Gagne's avatar
Don Gagne committed
215 216 217
    
    _ui.statusText->setText(statusText);
    _ui.progressBar->reset();
Don Gagne's avatar
Don Gagne committed
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 246 247 248 249 250 251 252
}

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
253
        _requestDirectoryList(dir);
Don Gagne's avatar
Don Gagne committed
254 255 256 257 258 259 260
    } 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
261 262
            _setAllButtonsEnabled(true);
            _currentCommand = commandNone;
Don Gagne's avatar
Don Gagne committed
263 264 265 266 267 268 269
        }
    }
}

void QGCUASFileView::_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
{
    Q_UNUSED(previous);
Don Gagne's avatar
Don Gagne committed
270
    
271
    _ui.downloadButton->setEnabled(current ? (current->type() == _typeFile) : false);
272
    _ui.uploadButton->setEnabled(current ? (current->type() == _typeDir) : false);
Lorenz Meier's avatar
Lorenz Meier committed
273
}
Don Gagne's avatar
Don Gagne committed
274

275
void QGCUASFileView::_requestDirectoryList(const QString& dir)
Don Gagne's avatar
Don Gagne committed
276
{
277
    _manager->listDirectory(dir);
Don Gagne's avatar
Don Gagne committed
278 279
}

Don Gagne's avatar
Don Gagne committed
280
void QGCUASFileView::_setAllButtonsEnabled(bool enabled)
Don Gagne's avatar
Don Gagne committed
281
{
Don Gagne's avatar
Don Gagne committed
282 283 284 285
    _ui.treeWidget->setEnabled(enabled);
    _ui.downloadButton->setEnabled(enabled);
    _ui.listFilesButton->setEnabled(enabled);
    _ui.uploadButton->setEnabled(enabled);
286
    
Don Gagne's avatar
Don Gagne committed
287 288 289
    if (enabled) {
        _currentItemChanged(_ui.treeWidget->currentItem(), NULL);
    }
Don Gagne's avatar
Don Gagne committed
290
}