aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Knall <rknall@gmail.com>2022-06-01 16:28:13 +0200
committerRoland Knall <rknall@gmail.com>2022-06-04 21:28:05 +0200
commitb06c1c451cec88e9cc1931d1cd8f2ce8e09ce129 (patch)
tree7328b89c14ea6d7eab056fec9652a90c5af889d9
parent9edf06383acdea1fa8e22ebabda134a0c0fd5df8 (diff)
Qt: Make TrafficTable detachable
Allow the endpoint and conversation dialogs to have detachable tabs. At the same time move the tree functionality to a subclass to better be able to handle the context menu when detached. Right now, still a lot of tree stuff is in the tabwidget, but could be moved to the tree for the future
-rw-r--r--docbook/release-notes.adoc2
-rw-r--r--ui/logwolf/CMakeLists.txt2
-rw-r--r--ui/qt/CMakeLists.txt2
-rw-r--r--ui/qt/widgets/detachable_tabwidget.cpp212
-rw-r--r--ui/qt/widgets/detachable_tabwidget.h87
-rw-r--r--ui/qt/widgets/traffic_tab.cpp94
-rw-r--r--ui/qt/widgets/traffic_tab.h14
-rw-r--r--ui/qt/widgets/traffic_tree.cpp7
-rw-r--r--ui/qt/widgets/traffic_tree.h1
9 files changed, 401 insertions, 20 deletions
diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc
index 8783fc8f2e..997bee1ed6 100644
--- a/docbook/release-notes.adoc
+++ b/docbook/release-notes.adoc
@@ -33,6 +33,8 @@ wsbuglink:17779[]
* The Conversation and Endpoint dialogs have been redesigned with the following improvements:
- The context menu now includes the option to resize all columns, as well as copying elements
- Data may be exported as Json
+ - Tabs may be detached and reattached from the dialog
+ - Adding/Removing tabs will keep them in the same order all the time
Many improvements have been made.
See the “New and Updated Features” section below for more details.
diff --git a/ui/logwolf/CMakeLists.txt b/ui/logwolf/CMakeLists.txt
index 1b72e95431..0a0b002a0e 100644
--- a/ui/logwolf/CMakeLists.txt
+++ b/ui/logwolf/CMakeLists.txt
@@ -23,6 +23,7 @@ set(WIRESHARK_WIDGET_HEADERS
../qt/widgets/capture_filter_edit.h
../qt/widgets/clickable_label.h
../qt/widgets/copy_from_profile_button.h
+ ../qt/widgets/detachable_tabwidget.h
../qt/widgets/display_filter_combo.h
../qt/widgets/display_filter_edit.h
../qt/widgets/dissector_tables_view.h
@@ -249,6 +250,7 @@ set(WIRESHARK_WIDGET_SRCS
../qt/widgets/capture_filter_edit.cpp
../qt/widgets/clickable_label.cpp
../qt/widgets/copy_from_profile_button.cpp
+ ../qt/widgets/detachable_tabwidget.cpp
../qt/widgets/display_filter_combo.cpp
../qt/widgets/display_filter_edit.cpp
../qt/widgets/dissector_tables_view.cpp
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index 168a9a3baf..77d4452a62 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -23,6 +23,7 @@ set(WIRESHARK_WIDGET_HEADERS
widgets/capture_filter_edit.h
widgets/clickable_label.h
widgets/copy_from_profile_button.h
+ widgets/detachable_tabwidget.h
widgets/display_filter_combo.h
widgets/display_filter_edit.h
widgets/dissector_tables_view.h
@@ -274,6 +275,7 @@ set(WIRESHARK_WIDGET_SRCS
widgets/capture_filter_edit.cpp
widgets/clickable_label.cpp
widgets/copy_from_profile_button.cpp
+ widgets/detachable_tabwidget.cpp
widgets/display_filter_combo.cpp
widgets/display_filter_edit.cpp
widgets/dissector_tables_view.cpp
diff --git a/ui/qt/widgets/detachable_tabwidget.cpp b/ui/qt/widgets/detachable_tabwidget.cpp
new file mode 100644
index 0000000000..0dd62a2ffb
--- /dev/null
+++ b/ui/qt/widgets/detachable_tabwidget.cpp
@@ -0,0 +1,212 @@
+/* @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/widgets/detachable_tabwidget.h>
+
+#include <QStackedWidget>
+#include <QBoxLayout>
+#include <QEvent>
+#include <QCloseEvent>
+#include <QMouseEvent>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QMimeData>
+#include <QStringList>
+#include <QApplication>
+#include <QDrag>
+#include <QPixmap>
+#include <QPainter>
+
+DetachableTabWidget::DetachableTabWidget(QWidget *parent) :
+ QTabWidget(parent)
+{
+ DragDropTabBar * tabBar = new DragDropTabBar(this);
+ connect(tabBar, &DragDropTabBar::onDetachTab, this, &DetachableTabWidget::detachTab);
+ connect(tabBar, &DragDropTabBar::onMoveTab, this, &DetachableTabWidget::moveTab);
+
+ setMovable(false);
+
+ setTabBar(tabBar);
+}
+
+void DetachableTabWidget::setTabBasename(QString newName) {
+ _tabBasename = newName;
+}
+
+QString DetachableTabWidget::tabBasename() const {
+ return _tabBasename;
+}
+
+void DetachableTabWidget::moveTab(int from, int to)
+{
+ QWidget * contentWidget = widget(from);
+ QString text = tabText(from);
+
+ removeTab(from);
+ insertTab(to, contentWidget, text);
+ setCurrentIndex(to);
+}
+
+void DetachableTabWidget::detachTab(int tabIdx, QPoint pos)
+{
+ QString name = tabText(tabIdx);
+
+ QWidget * contentWidget = widget(tabIdx);
+
+ /* For the widget to properly show in the dialog, it has to be
+ * removed properly and unhidden. QTabWidget uses a QStackedWidget for
+ * all parents of widgets. So we remove it from it's own parent and then
+ * unhide it to show the widget in the dialog */
+ QStackedWidget * par = qobject_cast<QStackedWidget *>(contentWidget->parent());
+ if (!par)
+ return;
+ QRect contentWidgetRect = par->frameGeometry();
+ par->removeWidget(contentWidget);
+ contentWidget->setHidden(false);
+
+ ToolDialog * detachedTab = new ToolDialog(contentWidget, parentWidget());
+ detachedTab->setWindowModality(Qt::NonModal);
+ detachedTab->setWindowTitle(_tabBasename + ": " + name);
+ detachedTab->setObjectName(name);
+ detachedTab->setGeometry(contentWidgetRect);
+ connect(detachedTab, &ToolDialog::onCloseSignal, this, &DetachableTabWidget::attachTab);
+ detachedTab->move(pos);
+ detachedTab->show();
+}
+
+void DetachableTabWidget::attachTab(QWidget * content, QString name)
+{
+ content->setParent(this);
+
+ int index = addTab(content, name);
+ if (index > -1)
+ setCurrentIndex(index);
+}
+
+ToolDialog::ToolDialog(QWidget *contentWidget, QWidget *parent, Qt::WindowFlags f) :
+ QDialog(parent, f)
+{
+ _contentWidget = contentWidget;
+
+ _contentWidget->setParent(this);
+ QVBoxLayout * layout = new QVBoxLayout(this);
+ layout->addWidget(_contentWidget);
+ this->setLayout(layout);
+}
+
+bool ToolDialog::event(QEvent *event)
+{
+ /**
+ * Capture a double click event on the dialog's window frame
+ */
+ if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) {
+ event->accept();
+ close();
+ }
+
+ return QDialog::event(event);
+}
+
+void ToolDialog::closeEvent(QCloseEvent * /*event*/)
+{
+ emit onCloseSignal(_contentWidget, objectName());
+}
+
+DragDropTabBar::DragDropTabBar(QWidget *parent) :
+ QTabBar(parent)
+{
+ setAcceptDrops(true);
+ setElideMode(Qt::ElideRight);
+ setSelectionBehaviorOnRemove(QTabBar::SelectLeftTab);
+
+ _dragStartPos = QPoint();
+ _dragDropPos = QPoint();
+ _mouseCursor = QCursor();
+ _dragInitiated = false;
+}
+
+void DragDropTabBar::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ event->accept();
+ emit onDetachTab(tabAt(event->pos()), _mouseCursor.pos());
+}
+
+void DragDropTabBar::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ _dragStartPos = event->pos();
+
+ _dragDropPos = QPoint(0, 0);
+ _dragInitiated = false;
+
+ QTabBar::mousePressEvent(event);
+}
+
+void DragDropTabBar::mouseMoveEvent(QMouseEvent *event)
+{
+ if (!_dragStartPos.isNull() &&
+ ((event->pos() - _dragStartPos).manhattanLength() > QApplication::startDragDistance()))
+ _dragInitiated = true;
+
+ if ((event->buttons() & Qt::LeftButton) && _dragInitiated) {
+ QMouseEvent * finishMouseMove = new QMouseEvent(QEvent::MouseMove, event->pos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
+ QTabBar::mouseMoveEvent(finishMouseMove);
+
+ QDrag * drag = new QDrag(this);
+ QMimeData * mimeData = new QMimeData();
+ mimeData->setData("action", "application/tab-detach");
+ drag->setMimeData(mimeData);
+
+ QWidget * original = parentWidget();
+ if (qobject_cast<DetachableTabWidget *>(original)) {
+ DetachableTabWidget * tabWidget = qobject_cast<DetachableTabWidget *>(original);
+ original = tabWidget->widget(tabWidget->currentIndex());
+ }
+ QPixmap pixmap = original->grab();
+ QPixmap targetPixmap = QPixmap(pixmap.size());
+ targetPixmap.fill(Qt::transparent);
+
+ QPainter painter(&targetPixmap);
+ painter.setOpacity(0.85);
+ painter.drawPixmap(0, 0, pixmap);
+ painter.end();
+ drag->setPixmap(targetPixmap);
+
+ Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::CopyAction);
+ if (dropAction == Qt::IgnoreAction) {
+ event->accept();
+ emit onDetachTab(tabAt(_dragStartPos), _mouseCursor.pos());
+ } if (dropAction == Qt::MoveAction) {
+ if (! _dragDropPos.isNull()) {
+ event->accept();
+ emit onMoveTab(tabAt(_dragStartPos), tabAt(_dragDropPos));
+ }
+ }
+ } else
+ QTabBar::mouseMoveEvent(event);
+}
+
+void DragDropTabBar::dragEnterEvent(QDragEnterEvent *event)
+{
+ const QMimeData * mimeData = event->mimeData();
+ QStringList formats = mimeData->formats();
+
+ if (formats.contains("action") && mimeData->data("action") == "application/tab-detach")
+ event->acceptProposedAction();
+}
+
+void DragDropTabBar::dropEvent(QDropEvent *event)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ _dragDropPos = event->position().toPoint();
+#else
+ _dragDropPos = event->pos();
+#endif
+ QTabBar::dropEvent(event);
+}
diff --git a/ui/qt/widgets/detachable_tabwidget.h b/ui/qt/widgets/detachable_tabwidget.h
new file mode 100644
index 0000000000..4b5220439f
--- /dev/null
+++ b/ui/qt/widgets/detachable_tabwidget.h
@@ -0,0 +1,87 @@
+/* @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DETACHABLE_TABWIDGET_H
+#define DETACHABLE_TABWIDGET_H
+
+#include <QTabWidget>
+#include <QDialog>
+#include <QEvent>
+#include <QCloseEvent>
+#include <QTabBar>
+#include <QPoint>
+#include <QCursor>
+
+class DetachableTabWidget : public QTabWidget
+{
+ Q_OBJECT
+public:
+ DetachableTabWidget(QWidget * parent = nullptr);
+
+ QString tabBasename() const;
+
+protected:
+
+ void setTabBasename(QString newName);
+
+protected slots:
+
+ virtual void moveTab(int from, int to);
+ virtual void detachTab(int tabIdx, QPoint pos);
+ virtual void attachTab(QWidget * content, QString name);
+
+private:
+ QString _tabBasename;
+
+};
+
+class ToolDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit ToolDialog(QWidget * _contentWidget, QWidget * parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
+
+protected:
+
+ virtual bool event(QEvent *event);
+ virtual void closeEvent(QCloseEvent *event);
+
+signals:
+ void onCloseSignal(QWidget * contentWidget, QString name);
+
+private:
+ QWidget * _contentWidget;
+};
+
+class DragDropTabBar : public QTabBar
+{
+ Q_OBJECT
+public:
+ explicit DragDropTabBar(QWidget * parent);
+
+signals:
+ void onDetachTab(int tabIdx, QPoint pos);
+ void onMoveTab(int oldIdx, int newIdx);
+
+protected:
+ virtual void mouseDoubleClickEvent(QMouseEvent *event);
+ virtual void mousePressEvent(QMouseEvent *event);
+ virtual void mouseMoveEvent(QMouseEvent *event);
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent *event);
+
+private:
+ QPoint _dragStartPos;
+ QPoint _dragDropPos;
+ QCursor _mouseCursor;
+ bool _dragInitiated;
+
+};
+
+#endif // DETACHABLE_TABWIDGET_H
diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp
index 9230c4c54e..0d13b0f063 100644
--- a/ui/qt/widgets/traffic_tab.cpp
+++ b/ui/qt/widgets/traffic_tab.cpp
@@ -24,6 +24,7 @@
#include <ui/qt/utils/variant_pointer.h>
#include <ui/qt/widgets/traffic_tab.h>
#include <ui/qt/widgets/traffic_tree.h>
+#include <ui/qt/widgets/detachable_tabwidget.h>
#include <QVector>
#include <QStringList>
@@ -77,23 +78,24 @@ static gboolean iterateProtocols(const void *key, void *value, void *userdata)
}
TrafficTab::TrafficTab(QWidget * parent) :
- QTabWidget(parent)
+ DetachableTabWidget(parent)
{
_createModel = nullptr;
_disableTaps = false;
_nameResolution = false;
- _tableName = QString();
_cliId = 0;
_recentList = nullptr;
+ setTabBasename(QString());
+
}
TrafficTab::~TrafficTab()
{
prefs_clear_string_list(*_recentList);
*_recentList = NULL;
+ _protocolButtons.clear();
- QList<int> protocols = _tabs.keys();
- foreach (int protoId, protocols)
+ foreach (int protoId, _tabs.keys())
{
char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(protoId)));
*_recentList = g_list_append(*_recentList, title);
@@ -102,7 +104,7 @@ TrafficTab::~TrafficTab()
void TrafficTab::setProtocolInfo(QString tableName, int cliId, GList ** recentList, ATapModelCallback createModel)
{
- _tableName = tableName;
+ setTabBasename(tableName);
_cliId = cliId;
_recentList = recentList;
if (createModel)
@@ -145,6 +147,7 @@ void TrafficTab::setProtocolInfo(QString tableName, int cliId, GList ** recentLi
endPoint->setCheckable(true);
endPoint->setChecked(_protocols.contains(protoId));
connect(endPoint, &QAction::triggered, this, &TrafficTab::toggleTab);
+ _protocolButtons.insert(protoId, endPoint);
cornerMenu->addAction(endPoint);
}
cornerButton->setMenu(cornerMenu);
@@ -199,8 +202,8 @@ void TrafficTab::setDelegate(int column, ATapCreateDelegate createDelegate)
QTreeView * TrafficTab::createTree(int protoId)
{
- TrafficTree * tree = new TrafficTree(_tableName, this);
-
+ TrafficTree * tree = new TrafficTree(tabBasename(), this);
+
if (_createModel) {
ATapDataModel * model = _createModel(protoId, "");
connect(model, &ATapDataModel::tapListenerChanged, tree, &TrafficTree::tapListenerEnabled);
@@ -269,15 +272,31 @@ void TrafficTab::disableTap()
_disableTaps = true;
cornerWidget()->setEnabled(false);
+ emit disablingTaps();
}
void TrafficTab::updateTabs()
{
QList<int> keys = _tabs.keys();
+ QList<int> allProtocols = _allTaps.keys();
- /* Adding new Tabs */
+ /* Adding new Tabs, and keeping the same order they are in the drop-down menu */
foreach (int proto, _protocols) {
if (!keys.contains(proto)) {
+
+ int insertIndex = -1;
+ auto bIdx = allProtocols.indexOf(proto);
+ int idx = 0;
+ while (insertIndex < 0 && idx < keys.count())
+ {
+ auto aIdx = allProtocols.indexOf(keys[idx]);
+ if (aIdx < 0) /* Key not in all protocols. This would be a fluke */
+ break;
+ if (aIdx > bIdx) /* Should never be equal, as proto is not yet in keys */
+ insertIndex = _tabs[keys[idx]];
+ idx++;
+ }
+
QTreeView * tree = createTree(proto);
QString tableName = proto_get_protocol_short_name(find_protocol_by_id(proto));
TabData tabData(tableName, proto);
@@ -286,23 +305,29 @@ void TrafficTab::updateTabs()
if (tree->model()->rowCount() > 0)
tableName += QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(tree->model()->rowCount());
- int tabId = addTab(tree, tableName);
+ int tabId = insertTab(insertIndex, tree, tableName);
+ _protocolButtons[proto]->setChecked(true);
tabBar()->setTabData(tabId, storage);
}
}
- /* Removing tabs no longer required */
+ /* Removing tabs no longer required. First filter the key array, for all tabs which
+ * are still being displayed */
foreach(int key, keys)
{
- if ( _protocols.contains(key))
+ if ( _protocols.contains(key)) {
+ _protocolButtons[key]->setChecked(true);
keys.removeAll(key);
+ }
}
-
- /* Counting down, otherwise removing a tab will shift the indeces */
+ /* Removal step 2, now actually remove all elements. Counting down, otherwise removing
+ * a tab will shift the indeces */
for(int idx = count(); idx > 0; idx--) {
TabData tabData = qvariant_cast<TabData>(tabBar()->tabData(idx - 1));
- if (keys.contains(tabData.protoId()))
+ if (keys.contains(tabData.protoId())) {
removeTab(idx - 1);
+ _protocolButtons[tabData.protoId()]->setChecked(false);
+ }
}
/* We reset the correct tab idxs. That operations is costly, but it is only
@@ -384,8 +409,13 @@ ATapDataModel * TrafficTab::modelForTabIndex(int tabIdx)
if (tabIdx == -1)
tabIdx = currentIndex();
- if (qobject_cast<QTreeView *>(widget(tabIdx))) {
- QTreeView * tree = qobject_cast<QTreeView *>(widget(tabIdx));
+ return modelForWidget(widget(tabIdx));
+}
+
+ATapDataModel * TrafficTab::modelForWidget(QWidget * searchWidget)
+{
+ if (qobject_cast<QTreeView *>(searchWidget)) {
+ QTreeView * tree = qobject_cast<QTreeView *>(searchWidget);
if (qobject_cast<QSortFilterProxyModel *>(tree->model())) {
QSortFilterProxyModel * qsfpm = qobject_cast<QSortFilterProxyModel *>(tree->model());
if (qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) {
@@ -599,3 +629,35 @@ QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx)
return QUrl::fromLocalFile(tf.fileName());
}
#endif
+
+void TrafficTab::detachTab(int tabIdx, QPoint pos) {
+ ATapDataModel * model = modelForTabIndex(tabIdx);
+ if (!model)
+ return;
+
+ int protocol = model->protoId();
+ _protocols.removeAll(protocol);
+
+ TrafficTree * tree = qobject_cast<TrafficTree *>(widget(tabIdx));
+ if (!tree)
+ return;
+
+ connect(this, &TrafficTab::disablingTaps ,tree , &TrafficTree::disableTap);
+ DetachableTabWidget::detachTab(tabIdx, pos);
+
+ updateTabs();
+}
+
+void TrafficTab::attachTab(QWidget * content, QString name)
+{
+ ATapDataModel * model = modelForWidget(content);
+ if (!model) {
+ attachTab(content, name);
+ return;
+ }
+
+ int protocol = model->protoId();
+ _protocols.append(protocol);
+
+ updateTabs();
+}
diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h
index 778ae98c81..b864e0f2a6 100644
--- a/ui/qt/widgets/traffic_tab.h
+++ b/ui/qt/widgets/traffic_tab.h
@@ -16,6 +16,7 @@
#include <ui/qt/models/atap_data_model.h>
#include <ui/qt/filter_action.h>
+#include <ui/qt/widgets/detachable_tabwidget.h>
#include <QTabWidget>
#include <QTreeView>
@@ -69,12 +70,11 @@ Q_DECLARE_METATYPE(TabData)
* removing the need of the dialog to know how data is being stored or
* generated.
*/
-class TrafficTab : public QTabWidget
+class TrafficTab : public DetachableTabWidget
{
Q_OBJECT
public:
-
TrafficTab(QWidget *parent = nullptr);
virtual ~TrafficTab();
@@ -204,12 +204,18 @@ signals:
void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type);
void tabDataChanged(int idx);
void retapRequired();
+ void disablingTaps();
+
+protected slots:
+
+ virtual void detachTab(int idx, QPoint pos) override;
+ virtual void attachTab(QWidget * content, QString name) override;
private:
- QString _tableName;
int _cliId;
QVector<int> _protocols;
QMap<int, QString> _allTaps;
+ QMap<int, QAction *> _protocolButtons;
QMap<int, int> _tabs;
GList ** _recentList;
ATapModelCallback _createModel;
@@ -221,7 +227,7 @@ private:
void updateTabs();
QTreeView * createTree(int protoId);
ATapDataModel * modelForTabIndex(int tabIdx = -1);
-
+ ATapDataModel * modelForWidget(QWidget * widget);
#ifdef HAVE_MAXMINDDB
bool writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel);
diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp
index a04e0b7ff2..25d08ad9d8 100644
--- a/ui/qt/widgets/traffic_tree.cpp
+++ b/ui/qt/widgets/traffic_tree.cpp
@@ -269,3 +269,10 @@ void TrafficTree::copyToClipboard(eTrafficTreeClipboard type)
mainApp->clipboard()->setText(stream.readAll());
}
+void TrafficTree::disableTap()
+{
+ ATapDataModel * model = dataModel();
+ if (!model)
+ return;
+ model->disableTap();
+}
diff --git a/ui/qt/widgets/traffic_tree.h b/ui/qt/widgets/traffic_tree.h
index 48946db1c8..34f87b6f37 100644
--- a/ui/qt/widgets/traffic_tree.h
+++ b/ui/qt/widgets/traffic_tree.h
@@ -53,6 +53,7 @@ signals:
public slots:
void tapListenerEnabled(bool enable);
+ void disableTap();
private:
bool _tapEnabled;