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"
13
#include "QGCQFileDialog.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();
    
57
    QString downloadToHere = QGCQFileDialog::getExistingDirectory(this,
DonLakeFlyer's avatar
DonLakeFlyer committed
58
                                                                 tr("Download Directory"),
Don Gagne's avatar
Don Gagne committed
59
                                                                 QDir::homePath(),
60
                                                                 QGCQFileDialog::ShowDirsOnly | QGCQFileDialog::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 {
Don Gagne's avatar
Don Gagne committed
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
            }
            
Don Gagne's avatar
Don Gagne committed
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
        
DonLakeFlyer's avatar
DonLakeFlyer committed
84
        _ui.statusText->setText(tr("Downloading: %1").arg(downloadFilename));
Don Gagne's avatar
Don Gagne committed
85
                                
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

DonLakeFlyer's avatar
DonLakeFlyer committed
114
    QString uploadFromHere = QGCQFileDialog::getOpenFileName(this, tr("Upload File"), QDir::homePath());
115

DonLakeFlyer's avatar
DonLakeFlyer committed
116
    _ui.statusText->setText(tr("Uploading: %1").arg(uploadFromHere));
Don Gagne's avatar
Don Gagne committed
117
                            
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
    _setAllButtonsEnabled(true);
    _currentCommand = commandNone;
DonLakeFlyer's avatar
DonLakeFlyer committed
139
    _ui.statusText->setText(tr("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
    QString statusText;
    
    if (_currentCommand == commandDownload) {
        _currentCommand = commandNone;
        _setAllButtonsEnabled(true);
        statusText = "Download complete";
201
    } else if (_currentCommand == commandUpload) {
Don Gagne's avatar
Don Gagne committed
202
203
204
205
206
        _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
}