1 //===========================================
2 //  Lumina-DE source code
3 //  Copyright (c) 2017, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 // This is a drag and drop capable version of the QTabBar
8 //===========================================
9 #ifndef _LUMINA_TEXT_EDITOR_DND_TABWIDGET_H
10 #define _LUMINA_TEXT_EDITOR_DND_TABWIDGET_H
11 
12 #include <QTabBar>
13 #include <QTabWidget>
14 #include <QMouseEvent>
15 #include <QDebug>
16 #include <QDrag>
17 #include <QMimeData>
18 #include <QMenu>
19 #include <QAction>
20 
21 class DnDTabBar : public QTabBar{
22 	Q_OBJECT
23 signals:
24 	void DetachTab(int); //
25 	void DroppedIn(QStringList); //The full path of some file(s)
26 	void DraggedOut(int, Qt::DropAction); //The tab number dragged/accepted by another app, and which action was accepted
27 
28 private:
29 	QMenu *tabMenu;
30 	QString selTab;
31 
32 private slots:
slotDetachTab()33 	void slotDetachTab(){
34 	  //qDebug() << "Detach Tab:" << selTab;
35 	  int tab = tabMenu->whatsThis().toInt();
36 	  if(tab>=0){ emit DetachTab(tab); }
37 	}
38 public:
DnDTabBar(QWidget * parent)39 	DnDTabBar(QWidget *parent) : QTabBar(parent){
40 	  this->setAcceptDrops(true);
41 	  this->setMouseTracking(true);
42 	  tabMenu = new QMenu(this);
43 	  tabMenu->addAction(tr("Detach Tab"), this, SLOT(slotDetachTab()) );
44 	}
~DnDTabBar()45 	~DnDTabBar(){
46 
47 	}
48 
49 
50 
51 protected:
mousePressEvent(QMouseEvent * ev)52 	virtual void mousePressEvent(QMouseEvent *ev){
53 	  int tab = this->tabAt(ev->pos());
54 	  if(ev->button() == Qt::LeftButton && tab>=0){
55 	    selTab = this->tabWhatsThis(tab);
56 	  }
57 	  QTabBar::mousePressEvent(ev);
58 	}
59 
mouseMoveEvent(QMouseEvent * ev)60 	virtual void mouseMoveEvent(QMouseEvent *ev){
61           //qDebug() << "Got Move Event:" << this->geometry() << ev->pos();
62 	  QWidget *parent = this->parentWidget(); //top-level parent widget
63           while(parent->parentWidget()!=0 && !parent->isWindow()){ parent = parent->parentWidget(); }
64 	  if(selTab>=0 && !QRect(QPoint(0,0), parent->size()).contains( this->mapTo(parent, ev->pos())) ){
65             //qDebug() << "Got Mouse outside of parent:" << parent->geometry() << this->geometry() << this->mapTo(parent, ev->pos());
66             //if(ev->button()!=Qt::LeftButton){ QTabBar::mouseMoveEvent(ev); return; }
67 	    qDebug() << "Starting Drag:" << this->geometry() << ev->pos();
68 	    QString tab = selTab;
69             this->mouseReleaseEvent(new QMouseEvent(QEvent::MouseButtonRelease, ev->pos(), ev->button(), ev->buttons(), ev->modifiers()) ); //will reset selTab
70 	    //this->update();
71 	     QDrag *drag = new QDrag(this);
72               QMimeData *mimeData = new QMimeData;
73               mimeData->setUrls(QList<QUrl>() << QUrl::fromLocalFile(tab));
74 	      //qDebug() << "Start Drag:" << this->tabWhatsThis(tab);
75               drag->setMimeData(mimeData);
76 	      Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
77 	      //Convert the tab->number and emit
78               for(int i=0; i<this->count(); i++){
79 	        if(this->tabWhatsThis(i) == tab){ this->emit DraggedOut(i, dropAction); this->setCurrentIndex(i); break; }
80  	      }
81 	    return;
82 	  }
83 	  QTabBar::mouseMoveEvent(ev);
84 	}
85 
mouseReleaseEvent(QMouseEvent * ev)86 	virtual void mouseReleaseEvent(QMouseEvent *ev){
87  	  int tab = this->tabAt(ev->pos());
88 	  if(ev->button() == Qt::RightButton && tab>=0){
89 	    tabMenu->setWhatsThis(QString::number(tab));
90 	    tabMenu->popup(ev->globalPos());
91 	  }
92 	  selTab.clear(); //reset this flag - not in a drag right now
93 	  QTabBar::mouseReleaseEvent(ev);
94 	}
95 
dragEnterEvent(QDragEnterEvent * ev)96 	virtual void dragEnterEvent(QDragEnterEvent *ev){
97 	  //qDebug() << "Got Drag Enter Event:" << ev->mimeData()->hasUrls();
98 	  if(ev->mimeData()->hasUrls() && ev->source()!=this){
99 	    ev->acceptProposedAction();
100 	  }
101 	}
102 
dragMoveEvent(QDragMoveEvent * ev)103 	virtual void dragMoveEvent(QDragMoveEvent *ev){
104 	  //qDebug() << "Got Drag Move Event:" << ev->mimeData()->hasUrls();
105 	  if(ev->mimeData()->hasUrls() && ev->source()!=this){
106 	    ev->accept();
107 	  }
108 	}
109 
dropEvent(QDropEvent * ev)110 	virtual void dropEvent(QDropEvent *ev){
111 	  //qDebug() << "Got Drop Event:" << ev->mimeData()->hasUrls();
112 	  if(ev->mimeData()->hasUrls() && ev->source()!=this){
113 	    QStringList files;
114 	    for(int i=0; i<ev->mimeData()->urls().length(); i++){
115 	      QString path = ev->mimeData()->urls().at(i).toLocalFile();
116 	      if(QFile::exists(path)){ files << path; }
117 	    }
118 	    //qDebug() << "[DROP] URLS:" << ev->mimeData()->urls() << "Files:" << files;
119 	    if(!files.isEmpty() && (ev->proposedAction()==Qt::CopyAction || ev->proposedAction()==Qt::MoveAction)){
120 	      ev->setDropAction(Qt::MoveAction);
121 	      ev->accept();
122 	      this->emit DroppedIn(files);
123 	    }
124 	  }
125 	}
126 };
127 
128 class DnDTabWidget : public QTabWidget{
129 	Q_OBJECT
130 private:
131 	DnDTabBar *TB;
132 public:
DnDTabWidget(QWidget * parent)133 	DnDTabWidget(QWidget *parent) : QTabWidget(parent){
134 	  TB = new DnDTabBar(this);
135 	  this->setTabBar(TB);
136 	  this->setTabsClosable(true);
137 	  this->setMovable(true);
138 	}
~DnDTabWidget()139 	~DnDTabWidget(){}
140 
dndTabBar()141 	DnDTabBar* dndTabBar(){ return TB; }
142 };
143 
144 #endif
145