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