1 // Copyright (C) 2017-2021 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // Author : Anthony GEAY (EDF R&D)
21 #include "QRemoteCopyWidget.h"
22 #include "QRemoteFileBrowser.h"
24 #include "QVBoxLayout"
25 #include "QPushButton"
26 #include "QHeaderView"
27 #include "QResizeEvent"
30 #include "QPaintDevice"
32 #include "QApplication"
33 #include "QMessageBox"
37 const char QFilesDirsCopierModel::ATOMIC_STOP_MSG[]="Clean interruption";
39 void FilterEntries(const TopDirDataStructure *tpds, QList<const DataStructure *>& listToFilter)
41 for(QList<const DataStructure *>::iterator it(listToFilter.begin());it!=listToFilter.end();it++)
43 const DataStructure *elt(*it);
44 std::vector<const DataStructure *> toKill(elt->getItermediateElts(tpds));
45 for(QList<const DataStructure *>::iterator it2(listToFilter.begin());it2!=listToFilter.end();)
52 if(std::find(toKill.begin(),toKill.end(),*it2)!=toKill.end())
53 it2=listToFilter.erase(it2);
60 void PerformCopy(QWidget *parent, QRemoteFileSystemModel *srcModel, const QModelIndexList& srcSelectedFiles, DataStructure *ds)
63 if(srcSelectedFiles.isEmpty())
66 const TopDirDataStructure *tpdsSrc(nullptr);
67 QList<const DataStructure *> listOfEntries;
68 foreach(const QModelIndex& srcItem, srcSelectedFiles)
70 quintptr pt(srcItem.internalId());
71 const DataStructure *srcDS(reinterpret_cast<const DataStructure *>(pt));
75 tpdsSrc=srcDS->getRoot();
76 listOfEntries.push_back(srcDS);
78 FilterEntries(tpdsSrc,listOfEntries);
80 FilesDirsCopier fdc(parent,listOfEntries,ds);
82 if(res!=QDialog::Accepted)
84 QMessageBox mb(QMessageBox::Warning,"Copy status",fdc.getErrorStr());
90 void CopierThread::run()
93 FilesDirsCopier *par(qobject_cast<FilesDirsCopier *>(parent()));
96 emit par->myAcceptSignal(_model->getErrorStr().isEmpty());//emit signal to notify main thread. Not direct invocation because executed in different thread
99 void CopierThread::stopRequested()
101 _model->stopCurrentCopy();
102 requestInterruption();
105 QFilesDirsCopierModel::QFilesDirsCopierModel(QObject *parent2, const QList<const DataStructure *>& srcFiles, DataStructure *destLoc):QAbstractListModel(parent2),_srcFiles(srcFiles),_currentElt(0),_curProc(nullptr),_destLoc(destLoc),_progress(_srcFiles.size(),0)
107 QTableView *par(qobject_cast<QTableView *>(parent()));
108 par->resizeColumnToContents(0);
109 qRegisterMetaType<QVector<int>>();//to be able to send 3th arg on dataChanged signal
112 int QFilesDirsCopierModel::nbOfRows() const
114 return _srcFiles.size();
117 int QFilesDirsCopierModel::rowCount(const QModelIndex&) const
122 int QFilesDirsCopierModel::columnCount(const QModelIndex&) const
127 QVariant QFilesDirsCopierModel::data(const QModelIndex& index, int role) const
131 if(role==Qt::DisplayRole || role==Qt::EditRole)
133 return _srcFiles[index.row()]->fullName();
138 QVariant QFilesDirsCopierModel::headerData(int section, Qt::Orientation orientation, int role) const
140 if(role==Qt::DisplayRole || role==Qt::EditRole)
142 if(orientation==Qt::Horizontal)
144 return QString("Files to be copied");
147 return QAbstractListModel::headerData(section,orientation,role);
150 int QFilesDirsCopierModel::getProgressOf(int srcFileId) const
152 QMutexLocker locker(&_mutOnProc);
153 return _progress[srcFileId];
156 QString QFilesDirsCopierModel::getFullNameOf(int srcFileId) const
158 return _srcFiles[srcFileId]->fullName();
161 QString QFilesDirsCopierModel::getNameOf(int srcFileId) const
163 return _srcFiles[srcFileId]->name();
166 QString QFilesDirsCopierModel::getPrettyText(int srcFileId) const
168 int progress(getProgressOf(srcFileId));
169 QString name(getNameOf(srcFileId));
172 case PROGRESS_STATUS_START:
173 return QString("Copy of %1 has started").arg(name);
174 case PROGRESS_STATUS_OVER:
175 return QString("Copy of %1 has finished").arg(name);
177 return QString("%1 (%2%)").arg(name).arg(progress);
181 QSize QFilesDirsCopierModel::sizeHint() const
184 for(int zePos=0;zePos<_srcFiles.size();zePos++)
186 int progress(getProgressOf(zePos));
187 QString txt(getPrettyText(zePos));
190 QSize sz(fm.boundingRect(txt).size());
191 w=std::max(sz.width(),w);
194 return QSize(2*w,2*h);
197 void QFilesDirsCopierModel::launchCopy()
199 foreach(const DataStructure *srcFile,_srcFiles)
202 QMutexLocker locker(&_mutOnProc);
203 _progress[_currentElt]=PROGRESS_STATUS_START;
204 QModelIndex ind(this->index(_currentElt,0));
205 emit this->dataChanged(ind,ind,QVector<int>());
207 if(QThread::currentThread()->isInterruptionRequested())
209 _error=QString("%1 just before %2 (%3/%4)").arg(ATOMIC_STOP_MSG).arg(srcFile->fullName()).arg(_currentElt).arg(_srcFiles.size());
213 QMutexLocker locker(&_mutOnProc);
214 _curProc=new QProcess;
216 args << "--progress" << "-r";
217 fillArgsForRSync(srcFile,args);
218 _curProc->start("rsync",args);
219 connect(_curProc,SIGNAL(readyReadStandardOutput()),this,SLOT(newOutputAvailable()));
221 bool s1(_curProc->waitForFinished(-1));
222 bool s2(_curProc->exitStatus()==QProcess::NormalExit && _curProc->exitCode()==0);
225 QMutexLocker locker(&_mutOnProc);
226 _progress[_currentElt]=PROGRESS_STATUS_OVER;
227 QModelIndex ind(this->index(_currentElt,0));
228 emit this->dataChanged(ind,ind,QVector<int>());
232 QMutexLocker locker(&_mutOnProc);
233 _error=QString("The copy of %1 has not finished correctly (%2/%3)").arg(srcFile->fullName()).arg(_currentElt).arg(_srcFiles.size());
236 if(QThread::currentThread()->isInterruptionRequested())
238 QMutexLocker locker(&_mutOnProc);
240 _error=QString("%1 right after %2 (%3/%4)").arg(ATOMIC_STOP_MSG).arg(srcFile->fullName()).arg(_currentElt).arg(_srcFiles.size());
242 _error=QString("Interrupted during copy of %1 (%2/%3)").arg(srcFile->fullName()).arg(_currentElt).arg(_srcFiles.size());
246 QMutexLocker locker(&_mutOnProc);
253 void QFilesDirsCopierModel::stopCurrentCopy()
255 QMutexLocker locker(&_mutOnProc);
256 if(!_curProc.isNull())
260 void QFilesDirsCopierModel::newOutputAvailable()
262 QMutexLocker locker(&_mutOnProc);
263 QByteArray ba(_curProc->readAllStandardOutput());
264 QString str(QString::fromLocal8Bit(ba));
265 QRegularExpression re("[\\s]*([\\d\\,]+)[\\s]+([\\d]+)\\%[\\s]+([\\d]+\\.[\\d]{2}[Mk]B/s)[\\s]+([\\d]{1,2}:[\\d]{2}:[\\d]{2})");
266 QRegularExpressionMatch rem(re.match(str,0,QRegularExpression::PartialPreferFirstMatch));
269 QString s(rem.captured(2));
271 int prog(s.toInt(&isOK));
274 _progress[_currentElt]=prog;
275 QModelIndex ind(this->index(_currentElt,0));
276 emit this->dataChanged(ind,ind,QVector<int>());
281 void QFilesDirsCopierModel::fillArgsForRSync(const DataStructure *srcFile, QStringList& args) const
283 QString src(srcFile->entryForRSyncSrc()),dest(_destLoc->entryForRSyncDest());
287 void ProgressDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
289 int zePos(index.row());
290 QWidget *wid( dynamic_cast<QWidget *>(painter->device()) );
291 int progress(_model->getProgressOf(zePos));
292 QString txt(_model->getPrettyText(zePos));
293 QRect refRect(option.rect);
294 QFont ft(wid->font());
296 QSize refRect2(fm.boundingRect(txt).size());
297 if(progress==QFilesDirsCopierModel::PROGRESS_STATUS_OVER)
301 painter->setFont(ft);
302 QPen p(painter->pen());
303 painter->setPen(QPen(Qt::green));
304 painter->drawText(QPoint(refRect.x()+(refRect.width()-refRect2.width())/2,
305 refRect.y()+refRect.height()/2+refRect2.height()/2-fm.descent()),txt);
307 painter->setFont(ft2);
310 if(progress==QFilesDirsCopierModel::PROGRESS_STATUS_START)
314 painter->setFont(ft);
315 QPen p(painter->pen());
316 painter->setPen(QPen(Qt::red));
317 QString txt2(QString("Copy in progress of %1").arg(txt));
318 painter->drawText(QPoint(refRect.x()+(refRect.width()-refRect2.width())/2,
319 refRect.y()+refRect.height()/2+refRect2.height()/2-fm.descent()),txt);
321 painter->setFont(ft2);
325 QBrush brush(Qt::green),b2(painter->brush());
326 painter->setBrush(brush);
327 painter->drawRect(refRect.x(),refRect.y(),int((float)refRect.width()*float(progress/100.f)),refRect.height());
328 painter->drawText(QPoint(refRect.x()+(refRect.width()-refRect2.width())/2,
329 refRect.y()+refRect.height()/2+refRect2.height()/2-fm.descent()),txt);
330 painter->setBrush(b2);
334 CopierTableView::CopierTableView(QWidget *parent):QTableView(parent)
336 setSelectionMode(QAbstractItemView::NoSelection);
337 setFocusPolicy(Qt::NoFocus);
340 int CopierTableView::sizeHintForColumn(int column) const
345 int CopierTableView::sizeHintForRow(int row) const
347 QFilesDirsCopierModel *mod(qobject_cast<QFilesDirsCopierModel *>(model()));
348 int nbElts(mod->nbOfRows());
349 int sz((height()-horizontalHeader()->height())/(nbElts>0?nbElts:1));
353 void CopierTableView::resizeEvent(QResizeEvent *event)
355 resizeColumnToContents(0);
356 resizeRowsToContents();
357 QTableView::resizeEvent(event);
360 QSize CopierTableView::sizeHint() const
362 QFilesDirsCopierModel *model2(qobject_cast<QFilesDirsCopierModel *>(model()));
364 return model2->sizeHint();
365 return CopierTableView::sizeHint();
368 FilesDirsCopier::FilesDirsCopier(QWidget *parent, const QList<const DataStructure *>& srcFiles, DataStructure *destLoc):QDialog(parent),_table(new CopierTableView(this)),_cancel(new QPushButton(this)),_model(nullptr)
370 QVBoxLayout *vb(new QVBoxLayout(this));
371 _cancel->setText("Cancel");
372 vb->addWidget(_table);
373 vb->addWidget(_cancel);
374 _model=new QFilesDirsCopierModel(_table,srcFiles,destLoc);
375 _th=new CopierThread(this,_model);
376 _table->setModel(_model);
377 _table->setShowGrid(false);
378 _table->setItemDelegate(new ProgressDelegate(_table,_model));
379 connect(_cancel,SIGNAL(clicked()),this,SLOT(cancelRequested()));
380 connect(this,SIGNAL(myAcceptSignal(bool)),this,SLOT(myAccept(bool)));
383 void FilesDirsCopier::cancelRequested()
385 _th->stopRequested();
390 void FilesDirsCopier::myAccept(bool isOK)
399 int FilesDirsCopier::exec()
402 return QDialog::exec();