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