726c38a931b061be4ff97d53f44581ec79828366
[metze/wireshark/wip.git] / ui / qt / widgets / drag_drop_toolbar.cpp
1 /* drag_drop_toolbar.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0+*/
8
9 #include <ui/qt/widgets/drag_drop_toolbar.h>
10 #include <ui/qt/widgets/drag_label.h>
11 #include <ui/qt/utils/wireshark_mime_data.h>
12
13 #include <QAction>
14 #include <QApplication>
15 #include <QToolBar>
16 #include <QToolButton>
17 #include <QDrag>
18 #include <QLayout>
19 #include <QMimeData>
20 #include <QMouseEvent>
21 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
22 #include <QWindow>
23 #endif
24
25 #define drag_drop_toolbar_action_ "drag_drop_toolbar_action_"
26
27 DragDropToolBar::DragDropToolBar(const QString &title, QWidget *parent) :
28     QToolBar(title, parent)
29 {
30     childCounter = 0;
31     setAcceptDrops(true);
32 }
33
34 DragDropToolBar::DragDropToolBar(QWidget *parent) :
35     QToolBar(parent)
36 {
37     childCounter = 0;
38     setAcceptDrops(true);
39 }
40
41 DragDropToolBar::~DragDropToolBar()
42 {
43 }
44
45 void DragDropToolBar::childEvent(QChildEvent * event)
46 {
47     /* New action has been added */
48     if ( event->type() == QEvent::ChildAdded )
49     {
50         if ( event->child()->isWidgetType() )
51         {
52             /* Reset if it has moved underneath lower limit */
53             if ( childCounter < 0 )
54                 childCounter = 0;
55
56             ((QWidget *)event->child())->installEventFilter(this);
57             event->child()->setProperty(drag_drop_toolbar_action_, QVariant::fromValue(childCounter));
58             childCounter++;
59         }
60     }
61     else if ( event->type() == QEvent::ChildRemoved )
62     {
63         childCounter--;
64     }
65     else if ( event->type() == QEvent::ChildPolished )
66     {
67         /* Polish is called every time a child is added or removed. This is implemented by adding
68          * all childs again as hidden elements, and afterwards removing the existing ones. Therefore
69          * we have to reset child counter here, if a widget is being polished. If this is not being
70          * done, crashes will occur after an item has been removed and other items are moved afterwards */
71         if ( event->child()->isWidgetType() )
72             childCounter = 0;
73     }
74 }
75
76 void DragDropToolBar::clear()
77 {
78     QToolBar::clear();
79     childCounter = 0;
80 }
81
82 bool DragDropToolBar::eventFilter(QObject * obj, QEvent * event)
83 {
84     if ( ! obj->isWidgetType() )
85         return QToolBar::eventFilter(obj, event);
86
87     QWidget * elem = qobject_cast<QWidget *>(obj);
88
89     if ( ! elem || ( event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove ) )
90         return QToolBar::eventFilter(obj, event);
91
92     QMouseEvent * ev = (QMouseEvent *)event;
93
94     if ( event->type() == QEvent::MouseButtonPress )
95     {
96         if ( ev->buttons() & Qt::LeftButton )
97             dragStartPosition = ev->pos();
98     }
99     else if ( event->type() == QEvent::MouseMove )
100     {
101         if ( ( ev->buttons() & Qt::LeftButton ) && (ev->pos() - dragStartPosition).manhattanLength()
102                  > QApplication::startDragDistance())
103         {
104             ToolbarEntryMimeData * temd =
105                     new ToolbarEntryMimeData(((QToolButton *)elem)->text(), elem->property(drag_drop_toolbar_action_).toInt());
106             DragLabel * lbl = new DragLabel(temd->labelText(), this);
107             QDrag * drag = new QDrag(this);
108             drag->setMimeData(temd);
109
110 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
111             qreal dpr = window()->windowHandle()->devicePixelRatio();
112             QPixmap pixmap(lbl->size() * dpr);
113             pixmap.setDevicePixelRatio(dpr);
114 #else
115             QPixmap pixmap(lbl->size());
116 #endif
117             lbl->render(&pixmap);
118             drag->setPixmap(pixmap);
119
120             drag->exec(Qt::CopyAction | Qt::MoveAction);
121
122             return true;
123         }
124     }
125
126     return QToolBar::eventFilter(obj, event);
127 }
128
129 void DragDropToolBar::dragEnterEvent(QDragEnterEvent *event)
130 {
131     if ( ! event )
132         return;
133
134     if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
135     {
136         if (event->source() == this) {
137             event->setDropAction(Qt::MoveAction);
138             event->accept();
139         } else {
140             event->acceptProposedAction();
141         }
142     } else if (qobject_cast<const DisplayFilterMimeData *>(event->mimeData())) {
143         if ( event->source() != this )
144         {
145             event->setDropAction(Qt::CopyAction);
146             event->accept();
147         } else {
148             event->acceptProposedAction();
149         }
150     } else {
151         event->ignore();
152     }
153 }
154
155 void DragDropToolBar::dragMoveEvent(QDragMoveEvent *event)
156 {
157     if ( ! event )
158         return;
159
160     if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
161     {
162         QAction * actionAtPos = actionAt(event->pos() );
163         if ( actionAtPos )
164         {
165             QWidget * widget = widgetForAction(actionAtPos);
166             if ( widget )
167             {
168                 bool success = false;
169                 widget->property(drag_drop_toolbar_action_).toInt(&success);
170                 if ( ! success )
171                 {
172                     event->ignore();
173                     return;
174                 }
175             }
176         }
177
178         if (event->source() == this) {
179             event->setDropAction(Qt::MoveAction);
180             event->accept();
181         } else {
182             event->acceptProposedAction();
183         }
184     } else if (qobject_cast<const DisplayFilterMimeData *>(event->mimeData())) {
185         if ( event->source() != this )
186         {
187             event->setDropAction(Qt::CopyAction);
188             event->accept();
189         } else {
190             event->acceptProposedAction();
191         }
192     } else {
193         event->ignore();
194     }
195 }
196
197 void DragDropToolBar::dropEvent(QDropEvent *event)
198 {
199     if ( ! event )
200         return;
201
202     /* Moving items around */
203     if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
204     {
205         const ToolbarEntryMimeData * data = qobject_cast<const ToolbarEntryMimeData *>(event->mimeData());
206
207         int oldPos = data->position();
208         int newPos = -1;
209         QAction * action = actionAt(event->pos());
210         if ( action && actions().at(oldPos) )
211         {
212             widgetForAction(action)->setStyleSheet("QWidget { border: none; };");
213             newPos = widgetForAction(action)->property(drag_drop_toolbar_action_).toInt();
214             moveToolbarItems(oldPos, newPos);
215             QAction * moveAction = actions().at(oldPos);
216
217             emit actionMoved(moveAction, oldPos, newPos);
218         }
219
220         if (event->source() == this) {
221             event->setDropAction(Qt::MoveAction);
222             event->accept();
223         } else {
224             event->acceptProposedAction();
225         }
226
227     } else if (qobject_cast<const DisplayFilterMimeData *>(event->mimeData())) {
228         const DisplayFilterMimeData * data = qobject_cast<const DisplayFilterMimeData *>(event->mimeData());
229
230         if ( event->source() != this )
231         {
232             event->setDropAction(Qt::CopyAction);
233             event->accept();
234
235             emit newFilterDropped(data->description(), data->filter());
236
237         } else {
238             event->acceptProposedAction();
239         }
240
241     } else {
242         event->ignore();
243     }
244 }
245
246 void DragDropToolBar::moveToolbarItems(int fromPos, int newPos)
247 {
248     if ( fromPos == newPos )
249         return;
250
251     setUpdatesEnabled(false);
252
253     QList<QAction *> storedActions = actions();
254
255     clear();
256     childCounter = 0;
257
258     storedActions.move(fromPos, newPos);
259     foreach ( QAction * action, storedActions )
260         addAction(action);
261
262     setUpdatesEnabled(true);
263 }
264
265 /*
266  * Editor modelines
267  *
268  * Local Variables:
269  * c-basic-offset: 4
270  * tab-width: 8
271  * indent-tabs-mode: nil
272  * End:
273  *
274  * ex: set shiftwidth=4 tabstop=8 expandtab:
275  * :indentSize=4:tabSize=8:noTabs=true:
276  */