Salome HOME
Copyright update 2022
[modules/gui.git] / tools / RemoteFileBrowser / QRemoteFileBrowser.cxx
1 // Copyright (C) 2017-2022  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Anthony GEAY (EDF R&D)
20
21 #include "QRemoteFileBrowser.h"
22 #include "QMachineBrowser.h"
23 #include "QRemoteCopyWidget.h"
24
25 #include "QDirModel"
26 #include "QFileSystemModel"
27 #include "QVBoxLayout"
28 #include "QTreeView"
29 #include "QProcess"
30 #include "QMouseEvent"
31 #include "QApplication"
32 #include "QDrag"
33 #include "QScrollBar"
34 #include "QThread"
35 #include "QPainter"
36 #include "QMessageBox"
37
38 #include <iostream> 
39
40 class DirDataStructure;
41
42 void ListOfDir(DirDataStructure *parent);
43
44 QRemoteFileBrowser::QRemoteFileBrowser(QWidget *parent):QWidget(parent),_treeView(nullptr),_mb(nullptr)
45 {
46   _treeView=new AnotherTreeView(this);
47   QVBoxLayout *lay(new QVBoxLayout(this));
48   _mb=new QMachineBrowser(this);
49   lay->addWidget(_mb);
50   lay->addWidget(_treeView);
51   connect(_mb,SIGNAL(locationChanged()),this,SLOT(onLocationChanged()));
52   connect(_treeView,SIGNAL(modelHasBeenGeneratedSignal(bool)),this,SLOT(locationHasBeenChanged()));
53   connect(_treeView,SIGNAL(somethingChangedDueToFileModif()),this,SLOT(onLocationChanged()));
54   _mb->initLocation();
55 }
56
57 void LoadingThread::run()
58 {
59   //std::cout << "start" << std::endl;
60   FileLoader *fl(_mb->generateFileLoader());
61   TopDirDataStructure *fds(new TopDirDataStructure(nullptr,fl));//costly
62   fds->moveToThread(_fatherThread);
63   emit this->letsGenerateModel(fds);
64   //std::cout << "end" << std::endl;
65 }
66
67 void QRemoteFileBrowser::onLocationChanged()
68 {
69   _mb->setEnabled(false);
70   _treeView->generateModel(_mb);
71 }
72
73 void QRemoteFileBrowser::locationHasBeenChanged()
74 {
75   _mb->setEnabled(true);
76 }
77
78 QRemoteFileTransfer::QRemoteFileTransfer(QWidget *parent):QWidget(parent),_left(nullptr),_right(nullptr)
79 {
80   QHBoxLayout *lay(new QHBoxLayout(this));
81   _left=new QRemoteFileBrowser(this);
82   _right=new QRemoteFileBrowser(this);
83   lay->addWidget(_left);
84   lay->addWidget(_right);
85 }
86
87 QString DataStructure::name() const
88 {
89   QDir qd(_name);
90   return qd.dirName();
91 }
92
93 void DataStructure::removeFileArgs(QString& prg, QStringList& args) const
94 {
95   const TopDirDataStructure *root(getRoot());
96   root->removeFileArgsImpl(fullName(),prg,args);
97 }
98
99 QString DataStructure::entryForRSyncSrc() const
100 {
101   const TopDirDataStructure *root(getRoot());
102   return root->entryForRSync(fullName());
103 }
104
105 QString FileDataStructure::entryForRSyncDest() const
106 {
107   const TopDirDataStructure *root(getRoot());
108   return root->entryForRSync(getDirParent()->fullName());
109 }
110
111 QString DirDataStructure::entryForRSyncDest() const
112 {
113   const TopDirDataStructure *root(getRoot());
114   return root->entryForRSync(fullName());
115 }
116
117 class FileLoader;
118
119 const TopDirDataStructure *DataStructure::getRoot() const
120 {
121   if(isRoot())
122     return dynamic_cast<const TopDirDataStructure *>(this);
123   const DataStructure *work(this);
124   const DataStructure *ret(work->getDirParent());
125   while(true)
126     {
127       if(ret->isRoot())
128         return dynamic_cast<const TopDirDataStructure *>(ret);
129       work=ret;
130       ret=ret->getDirParent();
131     }
132 }
133
134 std::vector<const DataStructure *> DataStructure::getItermediateElts(const TopDirDataStructure *tpds) const
135 {
136   std::vector<const DataStructure *> ret;
137   if(isRoot())
138     return ret;
139   const DataStructure *work(this);
140   const DataStructure *cand(work->getDirParent());
141   while(true)
142     {
143       if(cand==tpds)
144         return ret;
145       work=cand;
146       ret.push_back(cand);
147       cand=cand->getDirParent();
148     }
149 }
150
151 void LocalFileLoader::fillArgs(const QString& dn, QString& prg, QStringList& args) const
152 {
153   prg="ls"; args << "-l" << dn;
154 }
155
156 QString LocalFileLoader::prettyPrint() const
157 {
158   return QString("Browsing %1 local directory").arg(getDirName());
159 }
160
161 QString LocalFileLoader::entryForRSync(const QString& fn) const
162 {
163   return fn;
164 }
165
166 QString LocalFileLoader::getMachine() const
167 {
168   return QString("localhost");
169 }
170
171 void LocalFileLoader::removeFileArgs(const QString& filePath, QString& prg, QStringList& args) const
172 {
173   prg="rm";
174   args << "-rf" << filePath;
175 }
176
177 void RemoteFileLoader::fillArgs(const QString& dn, QString& prg, QStringList& args) const
178 {
179   // find non hidden file recursive. If dn does not exist propagate the error status
180   prg="ssh"; args << _machine << QString("find %1 -maxdepth 1 -regextype egrep -regex '%1/[^\\.].*' | xargs ls -l ; test ${PIPESTATUS[0]} -eq 0").arg(dn);
181 }
182
183 QString RemoteFileLoader::prettyPrint() const
184 {
185   return QString("Browsing files in %1 on %2").arg(getDirName()).arg(_machine);
186 }
187
188 QString RemoteFileLoader::entryForRSync(const QString& fn) const
189 {
190   return QString("%1:%2").arg(_machine).arg(fn);
191 }
192
193 QString RemoteFileLoader::getMachine() const
194 {
195   return _machine;
196 }
197
198 void RemoteFileLoader::removeFileArgs(const QString& filePath, QString& prg, QStringList& args) const
199 {
200   prg="ssh";
201   args << _machine << "rm" << "-rf" << filePath;
202 }
203
204 bool LocalFileLoader::load(DirDataStructure *parent) const
205 {
206   QProcess *proc(new QProcess);
207   QString prg;
208   QStringList args;
209   
210   fillArgs(parent->fullName(),prg,args);
211   
212   proc->start(prg,args);
213   static const int timeEllapse(3000);
214   if(!proc->waitForFinished(timeEllapse))
215     return false;
216   if(proc->exitCode()!=0)
217     return false;
218   QByteArray arr(proc->readAll());
219   delete proc;
220   QList<QByteArray> sarr(arr.split('\n'));
221   std::size_t ii(0);
222   QRegularExpression re1("\\s+");
223   QRegularExpression re2("^(d|-)(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)$");
224   foreach(QByteArray arr0,sarr)
225     {
226       bool specialCase(ii==0 || ii==sarr.size()-1);
227       ii++;
228       if(specialCase)
229         continue;
230       QStringList arr1(QString(arr0).split(re1));
231       if(arr1.size()<9)
232         continue;
233       QRegularExpressionMatch match(re2.match(arr1[0]));
234       if(!match.hasMatch())
235         continue;
236       QDir qd(parent->fullName());
237       if(match.captured(1)=="d")
238         new DirDataStructure(parent,qd.absoluteFilePath(arr1[8]));
239       if(match.captured(1)=="-")
240         new FileDataStructure(parent,qd.absoluteFilePath(arr1[8]));
241     }
242   return true;
243 }
244
245 void readStdPart(DirDataStructure *parent, const QList<QByteArray>& arrs, int startLine, int endLine)
246 {
247   QRegularExpression re1("\\s+");
248   QRegularExpression re2("^(d|-)(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)$");
249   for(int i=startLine;i<endLine;i++)
250     {
251       const QByteArray& arr0(arrs[i]);
252       QStringList arr1(QString(arr0).split(re1));
253       if(arr1.size()<9)
254         continue;
255       QRegularExpressionMatch match(re2.match(arr1[0]));
256       if(!match.hasMatch())
257         continue;
258       QDir qd(parent->fullName());
259       if(match.captured(1)=="d")
260         {
261           //std::cout << "Dir " << qd.absoluteFilePath(arr1[8]) << std::endl;
262           new DirDataStructure(parent,qd.absoluteFilePath(arr1[8]));
263         }
264       if(match.captured(1)=="-")
265         {
266           //std::cout << "File " << qd.absoluteFilePath(arr1[8]) << std::endl;
267           new FileDataStructure(parent,qd.absoluteFilePath(arr1[8]));
268         }
269     }
270   parent->markAsLoaded();
271 }
272
273 bool RemoteFileLoader::load(DirDataStructure *parent) const
274 {
275   QProcess *proc(new QProcess);
276   QString prg;
277   QStringList args;
278   fillArgs(parent->fullName(),prg,args);
279   proc->start(prg,args);
280   static const int timeEllapse(3000);
281   if(!proc->waitForFinished(timeEllapse))
282     return false;
283   if(proc->exitCode()!=0)
284     return false;
285   QByteArray arr(proc->readAll());
286   delete proc;
287   QList<QByteArray> sarr(arr.split('\n'));
288   if(sarr.empty())
289     return false;
290   int sz(sarr.size()),ii(0);
291   std::vector<int> sections(1,0);
292   foreach(QByteArray arr0,sarr)
293     {
294       QString tmp(arr0);
295       if(tmp.size()==0)
296         sections.push_back(ii);
297       ii++;
298     }
299   QRegularExpression re3("^total ([0-9]+)$");
300   std::size_t nbSections(sections.size()-1);
301   for(std::size_t isec=0;isec<nbSections;isec++)
302     {
303       int startLine(sections[isec]),endLine(sections[isec+1]);
304       if(startLine==endLine)
305         continue;
306       QString firstLine(sarr[startLine]);
307       if(firstLine.size()!=0)
308         {
309           readStdPart(parent,sarr,startLine,endLine);
310           continue ;
311         }
312       if(endLine-startLine<3)
313         continue ;
314       QString almostDn(sarr[startLine+1]);
315       QRegularExpression re4(QString("%1%2(.+\\:)$").arg(parent->fullName()).arg(QDir::separator()));
316       QRegularExpressionMatch m4(re4.match(almostDn));
317       if(!m4.hasMatch())
318         continue ;
319       QString dn(almostDn.mid(0,almostDn.size()-1));
320       DirDataStructure *subParent(new DirDataStructure(parent,dn));
321       QString total(sarr[startLine+2]);
322       QRegularExpressionMatch m3(re3.match(total));
323       if(!m3.hasMatch())
324         continue;
325       readStdPart(subParent,sarr,startLine+3,endLine);
326     }
327   // sort it !
328   const QObjectList &cs(parent->children());
329   std::map< QString, QObject *> sorter;
330   foreach(QObject *child,cs)
331     {
332       DataStructure *child2(qobject_cast<DataStructure *>(child));
333       if(!child2)
334         continue ;
335       sorter[child2->name()]=child2;
336       child2->setParent(nullptr);
337     }
338   for(std::map< QString, QObject *>::const_iterator it=sorter.begin();it!=sorter.end();it++)
339     (*it).second->setParent(parent);
340   return true;
341 }
342   
343 bool DirDataStructure::load() const
344 {
345   bool ret(true);
346   if(!_is_loaded)
347     {
348       ret=getRoot()->getLoader()->load(const_cast<DirDataStructure *>(this));
349       _is_loaded=true;
350     }
351   return ret;
352 }
353
354 TopDirDataStructure::TopDirDataStructure(QObject *dds, FileLoader *fl):DirDataStructure(dds,fl->getDirName()),_fl(fl),_isOK(true)
355 {
356   _isOK=load();
357   //QThread::sleep(3);
358 }
359
360 TopDirDataStructure::~TopDirDataStructure()
361 {
362   delete _fl;
363 }
364
365 QString TopDirDataStructure::entryForRSync(const QString& fn) const
366 {
367   return _fl->entryForRSync(fn);
368 }
369
370 QString TopDirDataStructure::getMachine() const
371 {
372   return _fl->getMachine();
373 }
374
375 void TopDirDataStructure::removeFileArgsImpl(const QString& filePath, QString& prg, QStringList& args) const
376 {
377   _fl->removeFileArgs(filePath,prg,args);
378 }
379
380 const DirDataStructure *DataStructure::getDirParent() const
381 {
382   const QObject *p(parent());
383   if(!p)
384     return NULL;
385   const DirDataStructure *ret(dynamic_cast<const DirDataStructure *>(p));
386   return ret;
387 }
388
389 int DirDataStructure::posOf(const DataStructure *ds) const
390 {
391   load();
392   return children().indexOf(const_cast<DataStructure *>(ds));
393 }
394
395 const DataStructure *DirDataStructure::operator[](int pos) const
396 {
397   load();
398   const QObject *obj(children()[pos]);
399   const DataStructure *obj2(dynamic_cast<const DataStructure *>(obj));
400   return obj2;
401 }
402
403 FileDataStructure::FileDataStructure(DirDataStructure *dds, const QString& name):DataStructure(dds,name)
404 {
405 }
406
407 QString FileDataStructure::nameOnDrop() const
408 {
409   const DirDataStructure *ds3(getDirParent());
410   return QString("Dir %1 (%2)").arg(ds3->name()).arg(name());
411 }
412
413 void QRemoteFileSystemModel::emitResetModel()
414 {
415   emit this->beginResetModel();
416   emit this->endResetModel();
417 }
418
419 QRemoteFileSystemModel::QRemoteFileSystemModel(QObject *parent, FileLoader *fl):QAbstractItemModel(parent),_fds(nullptr)
420 {
421   _fds=new TopDirDataStructure(this,fl);
422 }
423
424 QRemoteFileSystemModel::QRemoteFileSystemModel(QObject *parent, TopDirDataStructure *fds):QAbstractItemModel(parent),_fds(fds)
425 {
426   if(_fds)
427     _fds->setParent(this);
428 }
429
430 QVariant QRemoteFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
431 {
432   if(role == Qt::DisplayRole || role == Qt::EditRole)
433     {
434       if(section==0)
435         return _fds->getLoader()->prettyPrint();
436     }
437   return QAbstractItemModel::headerData(section,orientation,role);
438 }
439
440 QModelIndex QRemoteFileSystemModel::parent(const QModelIndex& child) const
441 {
442   if(!child.isValid())
443     return QModelIndex();
444   quintptr pt(child.internalId());
445   DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
446   if(!ds)
447     return QModelIndex();
448   if(ds->isRoot())
449     return QModelIndex();
450   const DirDataStructure *father(ds->getDirParent());
451   if(father->isRoot())
452     return QModelIndex();
453   const DirDataStructure *grandFather(father->getDirParent());
454   return createIndex(grandFather->posOf(father),0,const_cast<DataStructure *>(static_cast<const DataStructure *>(father)));
455 }
456
457 QModelIndex QRemoteFileSystemModel::index(int row, int column, const QModelIndex& parent) const
458 {
459   quintptr pt(parent.internalId());
460   if(!pt)
461     {
462       if(_fds->size()<=row)
463         return QModelIndex();
464       const DataStructure *ds((*_fds)[row]);
465       return createIndex(row,column,const_cast<DataStructure *>(ds));
466     }
467   else
468     {
469       DataStructure *ds0(reinterpret_cast<DataStructure *>(pt));
470       DirDataStructure *ds1(dynamic_cast<DirDataStructure *>(ds0));
471       const DataStructure *ds((*ds1)[row]);
472       return createIndex(row,column,const_cast<DataStructure *>(ds));
473     }
474 }
475   
476 int QRemoteFileSystemModel::rowCount(const QModelIndex& mi) const
477 {
478   if(!mi.isValid())
479     return _fds->size();
480   quintptr pt(mi.internalId());
481   DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
482   if(!ds)
483     {
484       return _fds->size();
485     }
486   else
487     {
488       return ds->size();
489     }
490 }
491
492 int QRemoteFileSystemModel::columnCount(const QModelIndex&) const
493 {
494   return 1;
495 }
496
497 QVariant QRemoteFileSystemModel::data(const QModelIndex& index, int role) const
498 {  
499   if(!index.isValid())
500     return QVariant();
501   if(role==Qt::DisplayRole || role==Qt::EditRole)
502     {
503       quintptr pt(index.internalId());
504       DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
505       if(!ds->isSelected())
506         return ds->name();
507       return ds->nameOnDrop();
508     }
509   if(role==Qt::ForegroundRole)
510     {
511       quintptr pt(index.internalId());
512       DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
513       if(!ds->isSelected())
514         return QVariant();
515       QColor red0(154,18,20);
516       return QBrush(red0);
517     }
518   return QVariant();
519 }
520
521 MyTreeView::MyTreeView(QWidget *parent):QTreeView(parent),_sel(NULL),_slider_pos(-1)
522 {
523   setAcceptDrops(true);
524   setSelectionMode(QAbstractItemView::ContiguousSelection);
525   connect(this,SIGNAL(expanded(const QModelIndex&)),this,SLOT(itemExpanded(const QModelIndex&)));
526   connect(this,SIGNAL(collapsed(const QModelIndex&)),this,SLOT(itemCollapsed(const QModelIndex&)));
527 }
528
529 void MyTreeView::mousePressEvent(QMouseEvent *event)
530 {
531   if(event->button() == Qt::LeftButton)
532     {
533       _start_pos=event->pos();
534     }
535   QModelIndexList elts(selectedIndexes());
536   if(elts.size()<=1)
537     QTreeView::mousePressEvent(event);
538 }
539
540 void MyTreeView::mouseReleaseEvent(QMouseEvent *event)
541 {
542   QModelIndexList elts(selectedIndexes());
543   if(elts.size()>1)
544     QTreeView::mousePressEvent(event);
545   QTreeView::mouseReleaseEvent(event);
546 }
547
548 void MyTreeView::mouseMoveEvent(QMouseEvent *event)
549 {
550   if(event->buttons() & Qt::LeftButton)
551     {
552       int dist(std::abs(event->pos().x()-_start_pos.x()));//.manhattanLength());
553       if(dist>=QApplication::startDragDistance())
554         {
555           QMimeData *mimeData(new SelectionMimeData(selectedIndexes()));
556           QDrag *drag(new QDrag(this));
557           drag->setMimeData(mimeData);
558           if(drag->exec(Qt::CopyAction)==Qt::CopyAction)
559             {
560             }
561           else
562             {
563             }
564         }
565     }
566   QTreeView::mouseMoveEvent(event);
567 }
568
569 void MyTreeView::keyPressEvent(QKeyEvent *event)
570 {
571   if(event->key()==Qt::Key_Delete)
572     {
573       if(!selectedIndexes().isEmpty())
574         {
575           QString mach;
576           QRemoteFileSystemModel *mod(qobject_cast<QRemoteFileSystemModel *>(model()));
577           if(mod)
578             mach=mod->getMachine();
579           QString txt(QString("On %1 you are about to delete %2 files/dirs. Confirm it ?").arg(mach).arg(selectedIndexes().size()));
580           QMessageBox mb(QMessageBox::Warning,"Confirm deletion of files",txt,QMessageBox::Ok | QMessageBox::No,this);
581           mb.setEscapeButton(QMessageBox::No);
582           mb.setDefaultButton(QMessageBox::No);
583           if(mb.exec())
584             {
585               if(mb.buttonRole(mb.clickedButton())==QMessageBox::AcceptRole)
586                 {
587                   foreach(const QModelIndex& ind,selectedIndexes())
588                     {
589                       quintptr pt(ind.internalId());
590                       DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
591                       QString prg;
592                       QStringList args;
593                       ds->removeFileArgs(prg,args);
594                       QProcess proc;
595                       proc.start(prg,args);
596                       bool isOK(proc.waitForFinished());
597                       emit this->somethingChangedDueToFileModif();
598                       if(isOK && proc.exitCode()==0)
599                         {
600                           return ;
601                         }
602                     }
603                 }
604             }
605         }
606     }
607   QTreeView::keyPressEvent(event);
608 }
609
610 void MyTreeView::dragEnterEvent(QDragEnterEvent *event)
611 {
612   MyTreeView *source(qobject_cast<MyTreeView *>(event->source()));
613   if(source && source!=this)
614     {
615       _slider_pos=verticalScrollBar()->sliderPosition();
616       event->setDropAction(Qt::CopyAction);
617       event->accept();
618     }
619 }
620
621 void MyTreeView::dragMoveEvent(QDragMoveEvent *event)
622 {
623   MyTreeView *source(qobject_cast<MyTreeView *>(event->source()));
624   if(source && source!=this)
625     {
626       QModelIndex ind(indexAt(event->pos()));
627       if(ind.isValid())
628         {
629           quintptr pt(ind.internalId());
630           DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
631           if(ds)
632             {
633               if(_sel!=ds)
634                 {
635                   ds->select();
636                   if(_sel)
637                     _sel->unselect();
638                   _sel=ds;
639                   this->emitResetModel();
640                 }
641               event->setDropAction(Qt::CopyAction);
642               event->accept();
643               qApp->processEvents();
644             }
645         }
646     }
647 }
648
649 void MyTreeView::dragLeaveEvent(QDragLeaveEvent *event)
650 {
651   if(_sel)
652     {
653       _sel->unselect();
654       _sel=NULL;
655     }
656   _slider_pos=-1;
657   this->emitResetModel();
658   QTreeView::dragLeaveEvent(event);
659 }
660
661 void MyTreeView::dropEvent(QDropEvent *event)
662 {
663   MyTreeView *source(qobject_cast<MyTreeView *>(event->source()));
664   if(source && source!=this)
665     {
666       {
667         const QMimeData *data(event->mimeData());
668         const SelectionMimeData *data1(qobject_cast<const SelectionMimeData *>(data));
669         if(!data1)
670           {
671             _sel->unselect();
672             event->ignore();
673             this->emitResetModel();
674           }
675         QModelIndex ind(indexAt(event->pos()));
676         if(ind.isValid())
677           {
678             const QModelIndexList& listOfSelectedSrcFiles(data1->getSelection());
679             quintptr pt(ind.internalId());
680             DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
681             QRemoteFileSystemModel *srcModel(qobject_cast<QRemoteFileSystemModel *>(source->model()));
682             PerformCopy(this,srcModel,listOfSelectedSrcFiles,ds);
683             emit this->somethingChangedDueToFileModif();
684           }
685         else
686           {
687             event->ignore();
688             this->emitResetModel();
689           }
690       }
691       //
692       _slider_pos=-1;
693       event->setDropAction(Qt::MoveAction);
694       event->accept();
695       if(_sel)
696         _sel->unselect();
697       this->emitResetModel();
698     }
699 }
700
701 QRemoteFileSystemModel *MyTreeView::zeModel()
702 {
703   QAbstractItemModel *mod(this->model());
704   QRemoteFileSystemModel *mod2(qobject_cast<QRemoteFileSystemModel *>(mod));
705   return mod2;
706 }
707
708 void MyTreeView::itemExpanded(const QModelIndex &index)
709 {
710   itemExpandedStatus(index,true);
711 }
712
713 void MyTreeView::itemCollapsed(const QModelIndex &index)
714 {
715   itemExpandedStatus(index,false);
716 }
717   
718 void MyTreeView::itemExpandedStatus(const QModelIndex &index, bool status)
719 {
720   quintptr pt(index.internalId());
721   DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
722   DirDataStructure *ds1(dynamic_cast<DirDataStructure *>(ds));
723   if(!ds1)
724     return;
725   ds1->setExpanded(status);
726 }
727
728 void MyTreeView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
729 {
730   quintptr pt(index.internalId());
731   DataStructure *ds(reinterpret_cast<DataStructure *>(pt));
732   DirDataStructure *ds1(dynamic_cast<DirDataStructure *>(ds));
733   if(ds1)
734     (const_cast<MyTreeView *>(this))->setExpanded(index,ds1->isExpanded());
735   QTreeView::drawBranches(painter,rect,index);
736 }
737
738 void MyTreeView::paintEvent(QPaintEvent *event)
739 {
740   QTreeView::paintEvent(event);
741   if(_slider_pos!=-1)
742     {
743       verticalScrollBar()->setSliderPosition(_slider_pos);
744     }
745 }
746
747 void MyTreeView::emitResetModel()
748 {
749   this->zeModel()->emitResetModel();
750 }
751
752 AnotherTreeView::AnotherTreeView(QWidget *parent):QWidget(parent),_timerId(-1),_angle(0),_painter(nullptr),_tw(nullptr),_th(nullptr)
753 {
754   QVBoxLayout *lay(new QVBoxLayout(this));
755   _tw=new MyTreeView(this);
756   lay->addWidget(_tw);
757   setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
758   connect(_tw,SIGNAL(somethingChangedDueToFileModif()),this,SIGNAL(somethingChangedDueToFileModif()));
759   _tw->hide();
760 }
761
762 void AnotherTreeView::goGenerate(TopDirDataStructure *fds)
763 {
764   LoadingThread *s2(qobject_cast<LoadingThread *>(sender()));
765   if(!s2)
766     return ;
767   QRemoteFileSystemModel *model(new QRemoteFileSystemModel(_tw,fds));
768   s2->setGeneratedModel(model);
769 }
770
771 void AnotherTreeView::generateModel(QMachineBrowser *mb)
772 {
773   _tw->hide();
774   _th=new LoadingThread(QThread::currentThread(),mb);
775   connect(_th,SIGNAL(letsGenerateModel(TopDirDataStructure *)),this,SLOT(goGenerate(TopDirDataStructure *)));
776   connect(_th,SIGNAL(finished()),this,SLOT(modelHasBeenGenerated()));
777   _timerId=this->startTimer(50);
778   delete _painter;
779   _painter=new AnotherTreeViewWaitPainter;
780   _th->start();
781 }
782
783 AnotherTreeView::~AnotherTreeView()
784 {
785   delete _painter;
786 }
787
788 void AnotherTreeView::modelHasBeenGenerated()
789 {
790   _th->wait();
791   QRemoteFileSystemModel *model(_th->generatedModel());
792   {
793     QAbstractItemModel *oldModel(_tw->model());
794     if(oldModel)
795       delete oldModel;
796     _tw->setModel(model);
797   }
798   delete _th;
799   _th=nullptr;
800   this->killTimer(_timerId);
801   delete _painter;
802   _painter=nullptr;
803   if(!model->isOK())
804     _painter=new AnotherTreeViewNothingPainter;
805   else
806     _tw->show();
807   emit this->modelHasBeenGeneratedSignal(model->isOK());
808   updateGeometry();
809   update();
810 }
811
812 void AnotherTreeViewWaitPainter::paint(AnotherTreeView *atv, QPaintEvent *event) const
813 {
814   QSize sz3(atv->width(),atv->height());
815   int width0(sz3.width()),height0(sz3.height());
816   int radius(std::min(width0,height0)/2);
817   QRect refRect((width0-radius)/2,(height0-radius)/2,radius,radius);
818   QPainter painter(atv);
819   QColor red(154,18,20);
820   QRadialGradient grad(refRect.center(),radius);
821   grad.setColorAt(0.f,red);
822   grad.setColorAt(0.5f,Qt::white);
823   painter.setBrush(grad);
824   painter.drawPie(refRect,atv->getAngle(),90*16);
825 }
826
827 void AnotherTreeViewNothingPainter::paint(AnotherTreeView *atv, QPaintEvent *event) const
828 {
829   QPainter painter(atv);
830   const int SZP(12);
831   static const int WARN_Y=176,WARN_X=200;
832   const float RATIO(float(WARN_X)/float(WARN_Y));
833   //
834   int width0(atv->width()),height0(atv->height());
835   //QPen(QColor(255,203,189)
836   if(float(width0)>RATIO*float(height0))
837     painter.setViewport(int(width0-RATIO*float(height0))/2,0,height0*RATIO,height0);
838   else
839     painter.setViewport(0,(float(height0)-width0/RATIO)/2,width0,width0/RATIO);//width-height/RATIO
840   painter.setRenderHint(QPainter::Antialiasing,true);
841   painter.setWindow(0,0,WARN_X,WARN_Y);
842   //
843   painter.setPen(QPen(QColor(255,203,189),SZP,Qt::SolidLine,Qt::RoundCap));
844   painter.drawLine(QPoint(100,13),QPoint(11,164));
845   painter.drawLine(QPoint(11,164),QPoint(185,164));
846   painter.drawLine(QPoint(185,164),QPoint(100,13));
847   QColor lightBlack(200,200,200);
848   painter.setBrush(QBrush(lightBlack));
849   painter.setPen(QPen(lightBlack,2,Qt::SolidLine,Qt::RoundCap));
850   painter.drawEllipse(87,47,24,24);
851   painter.drawEllipse(93,105,12,12);
852   painter.drawEllipse(90,129,18,18);
853   const QPoint points[4]={QPoint(87,59),QPoint(93,111),QPoint(105,111),QPoint(111,59)};
854   painter.drawPolygon(points,4);
855
856   /*int width0(atv->width()),height0(atv->height());
857   int radius(std::min(width0,height0)/2);
858   QRect refRect((width0-radius)/2,(height0-radius)/2,radius,radius);
859   QPainter painter(atv);
860   QColor red(154,18,20);
861   painter.setBrush(QBrush(red));
862   painter.drawPie(refRect,0,45*16);*/
863 }
864
865 QSize AnotherTreeView::sizeHint() const
866 {
867   return _tw->sizeHint();
868 }
869
870 QSize AnotherTreeView::minimumSizeHint() const
871 {
872   return sizeHint();
873 }
874
875 void AnotherTreeView::paintEvent(QPaintEvent *event)
876 {
877   if(!_painter)
878     {
879       QWidget::paintEvent(event);
880       return ;
881     }
882   else
883     _painter->paint(this,event);
884 }
885
886 void AnotherTreeView::timerEvent(QTimerEvent *e)
887 {
888   if(e->timerId()!=_timerId)
889     return ;
890   _angle+=16*10;
891   update();
892 }