1
0
Fork 0
x64dbg/src/gui/Src/BasicView/SearchListView.cpp

325 lines
10 KiB
C++

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QLabel>
#include "SearchListView.h"
#include "FlickerThread.h"
SearchListView::SearchListView(bool EnableRegex, QWidget* parent, bool EnableLock) : QWidget(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
// Create the main button/bar view with QSplitter
//
// |- Splitter --------------------------------------------|
// | LISTVIEW |
// | SEARCH: | SEARCH BOX | LOCK CHECKBOX | REGEX CHECKBOX |
// |-------------------------------------------------------|
QSplitter* barSplitter = new QSplitter(Qt::Vertical);
{
// Create list layout (contains both ListViews)
{
// Create reference & search list
mList = new SearchListViewTable();
mSearchList = new SearchListViewTable();
mSearchList->hide();
// Vertical layout
QVBoxLayout* listLayout = new QVBoxLayout();
listLayout->setContentsMargins(0, 0, 0, 0);
listLayout->setSpacing(0);
listLayout->addWidget(mList);
listLayout->addWidget(mSearchList);
// Add list placeholder
QWidget* listPlaceholder = new QWidget();
listPlaceholder->setLayout(listLayout);
barSplitter->addWidget(listPlaceholder);
}
// Filtering elements
{
// Input box
mSearchBox = new QLineEdit();
mSearchBox->setPlaceholderText(tr("Type here to filter results..."));
// Regex parsing checkbox
mRegexCheckbox = new QCheckBox(tr("Regex"));
mRegexCheckbox->setTristate(true);
// Lock checkbox
mLockCheckbox = new QCheckBox(tr("Lock"));
if(!EnableRegex)
mRegexCheckbox->hide();
if(!EnableLock)
mLockCheckbox->hide();
// Horizontal layout
QHBoxLayout* horzLayout = new QHBoxLayout();
horzLayout->setContentsMargins(4, 0, (EnableRegex || EnableLock) ? 0 : 4, 0);
horzLayout->setSpacing(2);
horzLayout->addWidget(new QLabel(tr("Search: ")));
horzLayout->addWidget(mSearchBox);
horzLayout->addWidget(mLockCheckbox);
horzLayout->addWidget(mRegexCheckbox);
// Add searchbar placeholder
QWidget* horzPlaceholder = new QWidget();
horzPlaceholder->setLayout(horzLayout);
barSplitter->addWidget(horzPlaceholder);
}
// Minimum size for the search box
barSplitter->setStretchFactor(0, 1000);
barSplitter->setStretchFactor(0, 1);
// Disable main splitter
for(int i = 0; i < barSplitter->count(); i++)
barSplitter->handle(i)->setEnabled(false);
}
// Set the main layout which holds the splitter
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(barSplitter);
setLayout(mainLayout);
// Set global variables
mCurList = mList;
mSearchStartCol = 0;
// Install input event filter
mSearchBox->installEventFilter(this);
if(parent)
mSearchBox->setWindowTitle(parent->metaObject()->className());
// Setup search menu action
mSearchAction = new QAction(DIcon("find.png"), tr("Search..."), this);
connect(mSearchAction, SIGNAL(triggered()), this, SLOT(searchSlot()));
// Slots
connect(mList, SIGNAL(contextMenuSignal(QPoint)), this, SLOT(listContextMenu(QPoint)));
connect(mList, SIGNAL(doubleClickedSignal()), this, SLOT(doubleClickedSlot()));
connect(mSearchList, SIGNAL(contextMenuSignal(QPoint)), this, SLOT(listContextMenu(QPoint)));
connect(mSearchList, SIGNAL(doubleClickedSignal()), this, SLOT(doubleClickedSlot()));
connect(mSearchBox, SIGNAL(textChanged(QString)), this, SLOT(searchTextChanged(QString)));
connect(mRegexCheckbox, SIGNAL(stateChanged(int)), this, SLOT(on_checkBoxRegex_stateChanged(int)));
connect(mLockCheckbox, SIGNAL(toggled(bool)), this, SLOT(on_checkBoxLock_toggled(bool)));
// List input should always be forwarded to the filter edit
mSearchList->setFocusProxy(mSearchBox);
mList->setFocusProxy(mSearchBox);
}
SearchListView::~SearchListView()
{
}
bool SearchListView::findTextInList(SearchListViewTable* list, QString text, int row, int startcol, bool startswith)
{
int count = list->getColumnCount();
if(startcol + 1 > count)
return false;
if(startswith)
{
for(int i = startcol; i < count; i++)
if(list->getCellContent(row, i).startsWith(text, Qt::CaseInsensitive))
return true;
}
else
{
for(int i = startcol; i < count; i++)
{
auto state = mRegexCheckbox->checkState();
if(state != Qt::Unchecked)
{
if(list->getCellContent(row, i).contains(QRegExp(text, state == Qt::PartiallyChecked ? Qt::CaseInsensitive : Qt::CaseSensitive)))
return true;
}
else
{
if(list->getCellContent(row, i).contains(text, Qt::CaseInsensitive))
return true;
}
}
}
return false;
}
void SearchListView::searchTextChanged(const QString & arg1)
{
if(mSearchList->isHidden())
{
auto selList = mList->getSelection();
if(!selList.empty() && mList->isValidIndex(selList[0], 0))
mLastFirstColValue = mList->getCellContent(selList[0], 0);
}
else
{
auto selList = mSearchList->getSelection();
if(!selList.empty() && mSearchList->isValidIndex(selList[0], 0))
mLastFirstColValue = mSearchList->getCellContent(selList[0], 0);
}
if(arg1.length())
{
mList->hide();
mSearchList->show();
mCurList = mSearchList;
}
else
{
mSearchList->hide();
mList->show();
mCurList = mList;
}
mCurList->setSingleSelection(0);
mSearchList->setRowCount(0);
int rows = mList->getRowCount();
int columns = mList->getColumnCount();
for(int i = 0, j = 0; i < rows; i++)
{
if(findTextInList(mList, arg1, i, mSearchStartCol, false))
{
mSearchList->setRowCount(j + 1);
for(int k = 0; k < columns; k++)
mSearchList->setCellContent(j, k, mList->getCellContent(i, k));
j++;
}
}
mSearchList->reloadData();
if(!mLastFirstColValue.isEmpty())
{
rows = mCurList->getRowCount();
mCurList->setTableOffset(0);
for(int i = 0; i < rows; i++)
{
if(mCurList->getCellContent(i, 0) == mLastFirstColValue)
{
if(rows > mCurList->getViewableRowsCount())
{
int cur = i - mCurList->getViewableRowsCount() / 2;
if(!mCurList->isValidIndex(cur, 0))
cur = i;
mCurList->setTableOffset(cur);
}
mCurList->setSingleSelection(i);
break;
}
}
}
if(rows == 0)
emit emptySearchResult();
// Do not highlight with regex
if(mRegexCheckbox->checkState() == Qt::Unchecked)
mSearchList->highlightText = arg1;
else
mSearchList->highlightText = "";
}
void SearchListView::refreshSearchList()
{
searchTextChanged(mSearchBox->text());
}
void SearchListView::listContextMenu(const QPoint & pos)
{
QMenu wMenu(this);
emit listContextMenuSignal(&wMenu);
wMenu.addSeparator();
wMenu.addAction(mSearchAction);
QMenu wCopyMenu(tr("&Copy"), this);
wCopyMenu.setIcon(DIcon("copy.png"));
mCurList->setupCopyMenu(&wCopyMenu);
if(wCopyMenu.actions().length())
wMenu.addMenu(&wCopyMenu);
wMenu.exec(mCurList->mapToGlobal(pos));
}
void SearchListView::doubleClickedSlot()
{
emit enterPressedSignal();
}
void SearchListView::on_checkBoxRegex_stateChanged(int state)
{
Q_UNUSED(state);
refreshSearchList();
}
void SearchListView::on_checkBoxLock_toggled(bool checked)
{
mSearchBox->setDisabled(checked);
}
bool SearchListView::isSearchBoxLocked()
{
return mLockCheckbox->isChecked();
}
bool SearchListView::eventFilter(QObject* obj, QEvent* event)
{
// Keyboard button press being sent to the QLineEdit
if(obj == mSearchBox && event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
switch(keyEvent->key())
{
// The user pressed enter/return
case Qt::Key_Return:
case Qt::Key_Enter:
if(mCurList->getCellContent(mCurList->getInitialSelection(), 0).length())
emit enterPressedSignal();
return true;
// Search box misc controls
case Qt::Key_Escape:
mSearchBox->clear();
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Backspace:
case Qt::Key_Delete:
case Qt::Key_Home:
case Qt::Key_End:
case Qt::Key_Insert:
return QWidget::eventFilter(obj, event);
// Search box shortcuts
case Qt::Key_V: //Ctrl+V
case Qt::Key_X: //Ctrl+X
case Qt::Key_Z: //Ctrl+Z
case Qt::Key_A: //Ctrl+A
case Qt::Key_Y: //Ctrl+Y
if(keyEvent->modifiers() == Qt::CTRL)
return QWidget::eventFilter(obj, event);
}
// Printable characters go to the search box
char key = keyEvent->text().toUtf8().constData()[0];
if(isprint(key))
return QWidget::eventFilter(obj, event);
// By default, all other keys are forwarded to the search view
return QApplication::sendEvent(mCurList, event);
}
return QWidget::eventFilter(obj, event);
}
void SearchListView::searchSlot()
{
FlickerThread* thread = new FlickerThread(mSearchBox, this);
connect(thread, SIGNAL(setStyleSheet(QString)), mSearchBox, SLOT(setStyleSheet(QString)));
thread->start();
}