1 #include <QMessageBox>
2 #include <QLabel>
3 #include <QVBoxLayout>
4 #include <QHBoxLayout>
5 #include <QProgressBar>
6 #include <QPushButton>
7 #include <QDesktopServices>
8 #include <QDebug>
9 #include <climits>
10
11 #include "utils/utils.h"
12 #include "progress-dialog.h"
13 #include "seafile-applet.h"
14 #include "transfer-mgr.h"
15
16 namespace
17 {
18 const char* kQueryIndexUrl = "idx_progress";
19 } // namespace
20
21
FileBrowserProgressDialog(FileNetworkTask * task,QWidget * parent)22 FileBrowserProgressDialog::FileBrowserProgressDialog(FileNetworkTask *task, QWidget *parent)
23 : QProgressDialog(parent),
24 task_(task),
25 progress_request_(NULL),
26 index_progress_timer_(new QTimer(this)),
27 task_finished_(false)
28 {
29 initUI();
30 initTaskInfo();
31
32 connect(index_progress_timer_, SIGNAL(timeout()), this, SLOT(onQueryUpdate()));
33
34 connect(task_, SIGNAL(progressUpdate(qint64, qint64)),
35 this, SLOT(onProgressUpdate(qint64, qint64)));
36 connect(task_, SIGNAL(nameUpdate(QString)),
37 this, SLOT(onCurrentNameUpdate(QString)));
38 connect(task_, SIGNAL(finished(bool)), this, SLOT(onTaskFinished(bool)));
39 connect(task_, SIGNAL(retried(int)), this, SLOT(initTaskInfo()));
40 connect(this, SIGNAL(canceled()), this, SLOT(cancel()));
41
42 if (task_->type() == FileNetworkTask::Upload) {
43 FileUploadTask *upload_task = (FileUploadTask *)task_;
44 connect(upload_task, SIGNAL(oneFileFailed(const QString&, bool)),
45 this, SLOT(onOneFileUploadFailed(const QString&, bool)));
46 }
47 }
48
initUI()49 void FileBrowserProgressDialog::initUI()
50 {
51 setWindowModality(Qt::NonModal);
52 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
53 setWindowIcon(QIcon(":/images/seafile.png"));
54
55 QVBoxLayout *layout_ = new QVBoxLayout;
56 progress_bar_ = new QProgressBar;
57 description_label_ = new QLabel;
58
59 layout_->addWidget(description_label_);
60 layout_->addWidget(progress_bar_);
61
62 QHBoxLayout *hlayout_ = new QHBoxLayout;
63 more_details_label_ = new QLabel;
64 more_details_label_->setText(tr("Pending"));
65 cancel_button_ = new QPushButton(tr("Cancel"));
66 QWidget *spacer = new QWidget;
67 spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
68
69 hlayout_->addWidget(more_details_label_);
70 hlayout_->addWidget(spacer);
71 hlayout_->addWidget(cancel_button_);
72 hlayout_->setContentsMargins(-1, 0, -1, 6);
73 layout_->setContentsMargins(-1, 0, -1, 6);
74 layout_->addLayout(hlayout_);
75
76 setLayout(layout_);
77 setLabel(description_label_);
78 setBar(progress_bar_);
79 setCancelButton(cancel_button_);
80 }
81
~FileBrowserProgressDialog()82 FileBrowserProgressDialog::~FileBrowserProgressDialog()
83 {
84 if (progress_request_ != NULL)
85 progress_request_->deleteLater();
86 }
87
initTaskInfo()88 void FileBrowserProgressDialog::initTaskInfo()
89 {
90 task_finished_ = false;
91 if (task_->canceled()) {
92 return;
93 }
94 QString title, label;
95 if (task_->type() == FileNetworkTask::Upload) {
96 title = tr("Upload");
97 label = tr("Uploading %1");
98 } else {
99 title = tr("Download");
100 label = tr("Downloading %1");
101 }
102 setWindowTitle(title);
103 setLabelText(label.arg(QFileInfo(task_->localFilePath()).fileName()));
104
105 more_details_label_->setText("");
106
107 setMaximum(0);
108 setValue(0);
109 }
110
onCurrentNameUpdate(QString current_name)111 void FileBrowserProgressDialog::onCurrentNameUpdate(QString current_name)
112 {
113 QString label = tr("Uploading %1");
114 setLabelText(label.arg(current_name));
115 }
116
onProgressUpdate(qint64 processed_bytes,qint64 total_bytes)117 void FileBrowserProgressDialog::onProgressUpdate(qint64 processed_bytes, qint64 total_bytes)
118 {
119 // Skip the updates if the task has been cancelled, because we may already
120 // have already rejected this dialog.
121 if (task_->canceled()) {
122 return;
123 }
124 // if the value is less than the maxmium, this dialog will close itself
125 // add this guard for safety
126 if (processed_bytes >= total_bytes)
127 total_bytes = processed_bytes + 1;
128
129 if (total_bytes > INT_MAX) {
130 if (maximum() != INT_MAX)
131 setMaximum(INT_MAX);
132
133 // Avoid overflow
134 double progress = double(processed_bytes) * INT_MAX / total_bytes;
135 setValue((int)progress);
136 } else {
137 if (maximum() != total_bytes)
138 setMaximum(total_bytes);
139
140 setValue(processed_bytes);
141 }
142
143 more_details_label_->setText(tr("%1 of %2")
144 .arg(::readableFileSizeV2(processed_bytes))
145 .arg(::readableFileSizeV2(total_bytes)));
146 }
147
onTaskFinished(bool success)148 void FileBrowserProgressDialog::onTaskFinished(bool success)
149 {
150 task_finished_ = true;
151 // printf ("FileBrowserProgressDialog: onTaskFinished\n");
152 if (task_->canceled()) {
153 return;
154 }
155
156 cancel_button_->setVisible(false);
157
158 // When this "onTaskFinished" slot is called, the task may NOT
159 // have really finished yet, i.e. if we configured "async save" on
160 // server, we need to query the async save progress and only close
161 // the dialog after the async save is done.
162 //
163 // TODO: Right now the async save progress query is implemented
164 // here, but ideally it should be implemented inside the
165 // ReliablePostFileTask class for better encapsulation.
166 progerss_id_ = task_->oid();
167
168 //https://dev.seafile.com/seafhttp/upload-api/b7443978-42cf-4cc6-87bf-add0fc7ad6e3
169 //https://dev.seafile.com/seafhttp/idx_progress
170 progress_url_ = ::urlJoin(QUrl(task_->url().toString(QUrl::PrettyDecoded).
171 section("upload", 0, 0)), kQueryIndexUrl);
172 if (success) {
173 // printf ("progress dialog: task success\n");
174
175 //Judge "-" as a task id or a file id
176 //Compatible with new and old server versions
177 if (progerss_id_.contains("-")) {
178 onQueryUpdate();
179 index_progress_timer_->start(3000);
180 } else {
181 accept();
182 }
183 } else {
184 // printf ("progress dialog: task failed\n");
185 reject();
186 }
187 }
188
onQueryUpdate()189 void FileBrowserProgressDialog::onQueryUpdate()
190 {
191 if (progress_request_) {
192 progress_request_->deleteLater();
193 progress_request_ = NULL;
194 }
195
196 progress_request_ = new GetIndexProgressRequest(progress_url_, progerss_id_);
197 connect(progress_request_, SIGNAL(success(const ServerIndexProgress&)),
198 this, SLOT(onQuerySuccess(const ServerIndexProgress&)));
199 connect(progress_request_, SIGNAL(failed(const ApiError& error)),
200 this, SLOT(onQueryFailed(const ApiError& error)));
201
202 progress_request_->send();
203 }
204
onQuerySuccess(const ServerIndexProgress & result)205 void FileBrowserProgressDialog::onQuerySuccess(const ServerIndexProgress &result)
206 {
207 setLabelText(tr("Saving"));
208 more_details_label_->setText(tr("%1 of %2")
209 .arg(::readableFileSizeV2(result.indexed))
210 .arg(::readableFileSizeV2(result.total)));
211 if (result.status == 0) {
212 index_progress_timer_->stop();
213 accept();
214 } else if (result.status == -1) {
215 index_progress_timer_->stop();
216 seafApplet->warningBox(tr("File save failed"), this);
217 reject();
218 }
219 }
220
onQueryFailed(const ApiError & error)221 void FileBrowserProgressDialog::onQueryFailed(const ApiError& error)
222 {
223 qWarning("get index progress request error: %s", error.toString().toUtf8().data());
224 // when http error occur stop index_progress_timer.
225 if (error.type() == ApiError::HTTP_ERROR) {
226 index_progress_timer_->stop();
227 seafApplet->warningBox(tr("Index progress request error %1").arg(error.httpErrorCode()));
228 reject();
229 }
230 }
231
cancel()232 void FileBrowserProgressDialog::cancel()
233 {
234 if (task_finished_ || task_->canceled()) {
235 return;
236 }
237 if (task_->type() == FileNetworkTask::Upload) {
238 task_->cancel();
239 } else {
240 // For download tasks we need to go through the transfer manager
241 TransferManager::instance()->cancelDownload(task_->repoId(), task_->path());
242 }
243 reject();
244 }
245
onOneFileUploadFailed(const QString & filename,bool single_file)246 void FileBrowserProgressDialog::onOneFileUploadFailed(const QString &filename,
247 bool single_file)
248 {
249 if (task_->canceled()) {
250 return;
251 }
252
253 FileUploadTask *upload_task = (FileUploadTask *)task_;
254
255 QString msg =
256 tr("Failed to upload file \"%1\", do you want to retry?").arg(filename);
257 ActionOnFailure choice = retryOrSkipOrAbort(msg, single_file);
258
259 switch (choice) {
260 case ActionRetry:
261 upload_task->continueWithFailedFile(true);
262 break;
263 case ActionSkip:
264 upload_task->continueWithFailedFile(false);
265 break;
266 case ActionAbort:
267 cancel();
268 break;
269 }
270 }
271
272 FileBrowserProgressDialog::ActionOnFailure
retryOrSkipOrAbort(const QString & msg,bool single_file)273 FileBrowserProgressDialog::retryOrSkipOrAbort(const QString& msg, bool single_file)
274 {
275 QMessageBox box(this);
276 box.setText(msg);
277 box.setWindowTitle(getBrand());
278 box.setIcon(QMessageBox::Question);
279
280 QPushButton *yes_btn = box.addButton(tr("Retry"), QMessageBox::YesRole);
281 QPushButton *no_btn = nullptr;
282 if (!single_file) {
283 // If this is single file upload/update, we only show "retry" and
284 // "abort".
285 no_btn = box.addButton(tr("Skip"), QMessageBox::NoRole);
286 }
287 box.addButton(tr("Abort"), QMessageBox::RejectRole);
288
289
290 box.setDefaultButton(yes_btn);
291 box.exec();
292 QAbstractButton *btn = box.clickedButton();
293 if (btn == yes_btn) {
294 return ActionRetry;
295 } else if (btn == no_btn) {
296 return ActionSkip;
297 }
298
299 return ActionAbort;
300 }
301