summaryrefslogtreecommitdiffstats
path: root/ui/qt/widgets/drag_drop_toolbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/widgets/drag_drop_toolbar.cpp')
-rw-r--r--ui/qt/widgets/drag_drop_toolbar.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/ui/qt/widgets/drag_drop_toolbar.cpp b/ui/qt/widgets/drag_drop_toolbar.cpp
new file mode 100644
index 0000000..88726aa
--- /dev/null
+++ b/ui/qt/widgets/drag_drop_toolbar.cpp
@@ -0,0 +1,295 @@
+/* drag_drop_toolbar.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <wsutil/utf8_entities.h>
+
+#include <ui/qt/widgets/drag_drop_toolbar.h>
+#include <ui/qt/widgets/drag_label.h>
+#include <ui/qt/utils/wireshark_mime_data.h>
+
+#include <QAction>
+#include <QApplication>
+#include <QToolBar>
+#include <QToolButton>
+#include <QDrag>
+#include <QLayout>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QWindow>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+#define drag_drop_toolbar_action_ "drag_drop_toolbar_action_"
+
+DragDropToolBar::DragDropToolBar(const QString &title, QWidget *parent) :
+ QToolBar(title, parent)
+{
+ setupToolbar();
+}
+
+DragDropToolBar::DragDropToolBar(QWidget *parent) :
+ QToolBar(parent)
+{
+ setupToolbar();
+}
+
+void DragDropToolBar::setupToolbar()
+{
+ childCounter = 0;
+ setAcceptDrops(true);
+
+ // Each QToolBar has a QToolBarExtension button. Its icon looks
+ // terrible. We might want to create our own icon, but the double
+ // angle quote is a similar, nice-looking shape.
+ QToolButton *ext_button = findChild<QToolButton*>();
+ if (ext_button) {
+ ext_button->setIcon(QIcon());
+ ext_button->setText(UTF8_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK);
+ }
+}
+
+DragDropToolBar::~DragDropToolBar()
+{
+}
+
+void DragDropToolBar::childEvent(QChildEvent * event)
+{
+ /* New action has been added */
+ if (event->type() == QEvent::ChildAdded)
+ {
+ if (event->child()->isWidgetType())
+ {
+ /* Reset if it has moved underneath lower limit */
+ if (childCounter < 0)
+ childCounter = 0;
+
+ ((QWidget *)event->child())->installEventFilter(this);
+ event->child()->setProperty(drag_drop_toolbar_action_, QVariant::fromValue(childCounter));
+ childCounter++;
+ }
+ }
+ else if (event->type() == QEvent::ChildRemoved)
+ {
+ childCounter--;
+ }
+ else if (event->type() == QEvent::ChildPolished)
+ {
+ /* Polish is called every time a child is added or removed. This is implemented by adding
+ * all childs again as hidden elements, and afterwards removing the existing ones. Therefore
+ * we have to reset child counter here, if a widget is being polished. If this is not being
+ * done, crashes will occur after an item has been removed and other items are moved afterwards */
+ if (event->child()->isWidgetType())
+ childCounter = 0;
+ }
+}
+
+void DragDropToolBar::clear()
+{
+ QToolBar::clear();
+ childCounter = 0;
+}
+
+WiresharkMimeData * DragDropToolBar::createMimeData(QString name, int position)
+{
+ return new ToolbarEntryMimeData(name, position);
+}
+
+bool DragDropToolBar::eventFilter(QObject * obj, QEvent * event)
+{
+ if (! obj->isWidgetType())
+ return QToolBar::eventFilter(obj, event);
+
+ QWidget * elem = qobject_cast<QWidget *>(obj);
+
+ if (! elem || (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove) )
+ return QToolBar::eventFilter(obj, event);
+
+ QMouseEvent * ev = (QMouseEvent *)event;
+
+ if (event->type() == QEvent::MouseButtonPress)
+ {
+ if (ev->buttons() & Qt::LeftButton)
+ dragStartPosition = ev->pos();
+ }
+ else if (event->type() == QEvent::MouseMove)
+ {
+ if ((ev->buttons() & Qt::LeftButton) && (ev->pos() - dragStartPosition).manhattanLength()
+ > QApplication::startDragDistance())
+ {
+ if (! qobject_cast<QToolButton *>(elem) || ! elem->property(drag_drop_toolbar_action_).isValid())
+ return QToolBar::eventFilter(obj, event);
+
+ WiresharkMimeData * temd = createMimeData(((QToolButton *)elem)->text(), elem->property(drag_drop_toolbar_action_).toInt());
+ DragLabel * lbl = new DragLabel(temd->labelText(), this);
+ QDrag * drag = new QDrag(this);
+ drag->setMimeData(temd);
+
+ qreal dpr = window()->windowHandle()->devicePixelRatio();
+ QPixmap pixmap(lbl->size() * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+
+ lbl->render(&pixmap);
+ drag->setPixmap(pixmap);
+
+ drag->exec(Qt::CopyAction | Qt::MoveAction);
+
+ return true;
+ }
+ }
+
+ return QToolBar::eventFilter(obj, event);
+}
+
+void DragDropToolBar::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
+ {
+ if (event->source() == this) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) {
+ if (event->source() != this)
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void DragDropToolBar::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0)
+ QAction * actionAtPos = actionAt(event->position().toPoint());
+#else
+ QAction * actionAtPos = actionAt(event->pos());
+#endif
+ if (actionAtPos)
+ {
+ QWidget * widget = widgetForAction(actionAtPos);
+ if (widget)
+ {
+ bool success = false;
+ widget->property(drag_drop_toolbar_action_).toInt(&success);
+ if (! success)
+ {
+ event->ignore();
+ return;
+ }
+ }
+ }
+
+ if (event->source() == this) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) {
+ if (event->source() != this)
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void DragDropToolBar::dropEvent(QDropEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ /* Moving items around */
+ if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
+ {
+ const ToolbarEntryMimeData * data = qobject_cast<const ToolbarEntryMimeData *>(event->mimeData());
+
+ int oldPos = data->position();
+ int newPos = -1;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0)
+ QAction * action = actionAt(event->position().toPoint());
+#else
+ QAction * action = actionAt(event->pos());
+#endif
+ if (action && actions().at(oldPos))
+ {
+ widgetForAction(action)->setStyleSheet("QWidget { border: none; };");
+ newPos = widgetForAction(action)->property(drag_drop_toolbar_action_).toInt();
+ moveToolbarItems(oldPos, newPos);
+ QAction * moveAction = actions().at(oldPos);
+
+ emit actionMoved(moveAction, oldPos, newPos);
+ }
+
+ if (event->source() == this) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+
+ } else if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) {
+ QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType);
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
+ if (jsonDoc.isObject())
+ {
+ QJsonObject data = jsonDoc.object();
+
+ if (event->source() != this && data.contains("description") && data.contains("filter"))
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+
+ emit newFilterDropped(data["description"].toString(), data["filter"].toString());
+
+ } else {
+ event->acceptProposedAction();
+ }
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void DragDropToolBar::moveToolbarItems(int fromPos, int newPos)
+{
+ if (fromPos == newPos)
+ return;
+
+ setUpdatesEnabled(false);
+
+ QList<QAction *> storedActions = actions();
+
+ clear();
+ childCounter = 0;
+
+ storedActions.move(fromPos, newPos);
+ foreach (QAction * action, storedActions)
+ addAction(action);
+
+ setUpdatesEnabled(true);
+}