1
0
Fork 0

Hide some menus in a submenu (#1144)

* Hide some menus in a submenu

* use class name as id
This commit is contained in:
Torusrxxx 2016-10-04 18:16:18 +00:00 committed by Duncan Ogilvie
parent 7eecb558a0
commit 54d176f0a4
17 changed files with 380 additions and 56 deletions

View File

@ -629,6 +629,8 @@ void CPUDisassembly::setupRightClickContextMenu()
return false;
return text != mHighlightToken.text;
});
mMenuBuilder->loadFromConfig();
}
void CPUDisassembly::gotoOriginSlot()

View File

@ -255,6 +255,7 @@ void CPUDump::setupContextMenu()
return true;
}));
mMenuBuilder->loadFromConfig();
updateShortcuts();
}

View File

@ -39,6 +39,7 @@ void CallStackView::setupContextMenu()
// Column count cannot be zero
mMenuBuilder->addSeparator();
mMenuBuilder->addMenu(makeMenu(DIcon("copy.png"), tr("&Copy")), mCopyMenu);
mMenuBuilder->loadFromConfig();
}
void CallStackView::updateCallStack()

View File

@ -0,0 +1,65 @@
#include "CustomizeMenuDialog.h"
#include "ui_CustomizeMenuDialog.h"
#include "MenuBuilder.h"
#include "Configuration.h"
CustomizeMenuDialog::CustomizeMenuDialog(QWidget* parent) :
QDialog(parent),
ui(new Ui::CustomizeMenuDialog)
{
ui->setupUi(this);
for(const auto & i : Config()->NamedMenuBuilders)
{
QString viewName;
const char* id = i.first->getId();
if(strcmp(id, "CPUDisassembly") == 0)
viewName = tr("Disassembler");
else if(strcmp(id, "CPUDump") == 0)
viewName = tr("Dump");
else if(strcmp(id, "WatchView") == 0)
viewName = tr("Watch");
else if(strcmp(id, "CallStackView") == 0)
viewName = tr("Call Stack");
else if(strcmp(id, "ThreadView") == 0)
viewName = tr("Threads");
else
continue;
QTreeWidgetItem* parentItem = new QTreeWidgetItem(ui->treeWidget);
parentItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
parentItem->setText(0, viewName);
for(size_t j = 0; j < i.second; j++)
{
QString text = i.first->getText(j);
if(!text.isEmpty())
{
QTreeWidgetItem* menuItem = new QTreeWidgetItem(parentItem, 0);
menuItem->setText(0, text.replace(QChar('&'), ""));
QString configString = QString("Menu%1Hidden%2").arg(i.first->getId()).arg(j);
menuItem->setCheckState(0, Config()->getBool("Gui", configString) ? Qt::Checked : Qt::Unchecked);
menuItem->setData(0, Qt::UserRole, QVariant(configString));
menuItem->setFlags(Qt::ItemIsSelectable | Qt::ItemNeverHasChildren | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
}
}
ui->treeWidget->addTopLevelItem(parentItem);
}
connect(ui->btnOk, SIGNAL(clicked()), this, SLOT(onOk()));
}
void CustomizeMenuDialog::onOk()
{
for(int i = ui->treeWidget->topLevelItemCount(); i != 0; i--)
{
const QTreeWidgetItem* parentItem = ui->treeWidget->topLevelItem(i - 1);
for(int j = parentItem->childCount(); j != 0; j--)
{
const QTreeWidgetItem* childItem = parentItem->child(j - 1);
Config()->setBool("Gui", childItem->data(0, Qt::UserRole).toString(), childItem->checkState(0) == Qt::Checked);
}
}
emit accept();
}
CustomizeMenuDialog::~CustomizeMenuDialog()
{
delete ui;
}

View File

@ -0,0 +1,26 @@
#ifndef CUSTOMIZEMENUDIALOG_H
#define CUSTOMIZEMENUDIALOG_H
#include <QDialog>
namespace Ui
{
class CustomizeMenuDialog;
}
class CustomizeMenuDialog : public QDialog
{
Q_OBJECT
public:
explicit CustomizeMenuDialog(QWidget* parent = 0);
~CustomizeMenuDialog();
public slots:
void onOk();
private:
Ui::CustomizeMenuDialog* ui;
};
#endif // CUSTOMIZEMENUDIALOG_H

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomizeMenuDialog</class>
<widget class="QDialog" name="CustomizeMenuDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>446</width>
<height>431</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Customize which menu item will be shown in the &quot;More commands&quot; submenu</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="columnCount">
<number>1</number>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnOk">
<property name="text">
<string>&amp;OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>&amp;Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnCancel</sender>
<signal>clicked()</signal>
<receiver>CustomizeMenuDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>526</x>
<y>519</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>270</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -46,6 +46,7 @@
#include "CPUStack.h"
#include "GotoDialog.h"
#include "BrowseDialog.h"
#include "CustomizeMenuDialog.h"
#include "main.h"
QString MainWindow::windowTitle = "";
@ -324,6 +325,7 @@ MainWindow::MainWindow(QWidget* parent)
connect(ui->actionAnimateOver, SIGNAL(triggered()), this, SLOT(animateOverSlot()));
connect(ui->actionAnimateCommand, SIGNAL(triggered()), this, SLOT(animateCommandSlot()));
connect(ui->actionSetInitializationScript, SIGNAL(triggered()), this, SLOT(setInitialzationScript()));
connect(ui->actionCustomizeMenus, SIGNAL(triggered()), this, SLOT(customizeMenu()));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString)));
connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget()));
@ -1713,6 +1715,14 @@ void MainWindow::setInitialzationScript()
}
}
void MainWindow::customizeMenu()
{
CustomizeMenuDialog customMenuDialog(this);
customMenuDialog.setWindowTitle(tr("Customize Menus"));
customMenuDialog.setWindowIcon(DIcon("analysis.png"));
customMenuDialog.exec();
}
#include "../src/bridge/Utf8Ini.h"
void MainWindow::on_actionImportSettings_triggered()

View File

@ -138,6 +138,7 @@ public slots:
void clickFavouriteTool();
void chooseLanguage();
void setInitialzationScript();
void customizeMenu();
void addFavouriteItem(int type, const QString & name, const QString & description);
void setFavouriteItemShortcut(int type, const QString & name, const QString & shortcut);

View File

@ -23,7 +23,7 @@
<x>0</x>
<y>0</y>
<width>868</width>
<height>21</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -159,6 +159,7 @@
<addaction name="actionSettings"/>
<addaction name="actionAppearance"/>
<addaction name="actionShortcuts"/>
<addaction name="actionCustomizeMenus"/>
<addaction name="actionTopmost"/>
<addaction name="actionReloadStylesheet"/>
<addaction name="actionSetInitializationScript"/>
@ -1038,6 +1039,15 @@
<string>Import settings...</string>
</property>
</action>
<action name="actionCustomizeMenus">
<property name="icon">
<iconset resource="../../resource.qrc">
<normaloff>:/icons/images/analysis.png</normaloff>:/icons/images/analysis.png</iconset>
</property>
<property name="text">
<string>Customize menus</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -113,6 +113,7 @@ void ThreadView::setupContextMenu()
// Column count cannot be zero
mMenuBuilder->addSeparator();
mMenuBuilder->addMenu(makeMenu(DIcon("copy.png"), tr("&Copy")), mCopyMenu);
mMenuBuilder->loadFromConfig();
}
/**

View File

@ -168,6 +168,7 @@ void WatchView::setupContextMenu()
MenuBuilder* copyMenu = new MenuBuilder(this);
setupCopyMenu(copyMenu);
mMenu->addMenu(makeMenu(DIcon("copy.png"), tr("&Copy")), copyMenu);
mMenu->loadFromConfig();
}
QString WatchView::getSelectedId()

View File

@ -7,6 +7,12 @@
Configuration* Configuration::mPtr = nullptr;
inline void insertMenuBuilderBools(QMap<QString, bool>* config, const char* id, size_t count)
{
for(size_t i = 0; i < count; i++)
config->insert(QString("Menu%1Hidden%2").arg(id).arg(i), false);
}
Configuration::Configuration() : QObject(), noMoreMsgbox(false)
{
mPtr = this;
@ -207,6 +213,12 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
guiBool.insert("NoCloseDialog", false);
guiBool.insert("PidInHex", true);
guiBool.insert("SidebarWatchLabels", true);
//Named menu settings
insertMenuBuilderBools(&guiBool, "CPUDisassembly", 35); //CPUDisassembly
insertMenuBuilderBools(&guiBool, "CPUDump", 30); //CPUDump
insertMenuBuilderBools(&guiBool, "WatchView", 7); //Watch
insertMenuBuilderBools(&guiBool, "CallStackView", 5); //CallStackView
insertMenuBuilderBools(&guiBool, "ThreadView", 12); //Thread
defaultBools.insert("Gui", guiBool);
QMap<QString, duint> guiUint;
@ -976,3 +988,14 @@ bool Configuration::shortcutToConfig(const QString id, const QKeySequence shortc
_key = "NOT_SET";
return BridgeSettingSet("Shortcuts", _id.toUtf8().constData(), _key.toUtf8().constData());
}
void Configuration::registerMenuBuilder(MenuBuilder* menu, size_t count)
{
bool exists = false;
const char* id = menu->getId();
for(const auto & i : NamedMenuBuilders)
if(strcmp(i.first->getId() , id) == 0)
exists = true;
if(!exists)
NamedMenuBuilders.push_back(std::make_pair(menu, count));
}

View File

@ -17,6 +17,8 @@
#define ConfigHScrollBarStyle() "QScrollBar:horizontal{border:1px solid grey;background:#f1f1f1;height:10px}QScrollBar::handle:horizontal{background:#aaa;min-width:20px;margin:1px}QScrollBar::add-line:horizontal,QScrollBar::sub-line:horizontal{width:0;height:0}"
#define ConfigVScrollBarStyle() "QScrollBar:vertical{border:1px solid grey;background:#f1f1f1;width:10px}QScrollBar::handle:vertical{background:#aaa;min-height:20px;margin:1px}QScrollBar::add-line:vertical,QScrollBar::sub-line:vertical{width:0;height:0}"
class MenuBuilder;
class Configuration : public QObject
{
Q_OBJECT
@ -52,6 +54,7 @@ public:
void readShortcuts();
void writeShortcuts();
void emitShortcutsUpdated();
void registerMenuBuilder(MenuBuilder* menu, size_t count);
const QColor getColor(const QString id) const;
const bool getBool(const QString category, const QString id) const;
@ -76,6 +79,9 @@ public:
QMap<QString, QFont> Fonts;
QMap<QString, Shortcut> Shortcuts;
//custom menu maps
QList<std::pair<MenuBuilder*, size_t>> NamedMenuBuilders;
static Configuration* mPtr;
signals:

View File

@ -0,0 +1,112 @@
#include "MenuBuilder.h"
#include "Bridge.h"
#include "Configuration.h"
/**
* @brief MenuBuilder::loadFromConfig Set the menu builder to be customizable
* @param id The id of menu builder. It should be the same on every same menu.
* See CustomizeMenuDialog for a list of defined identifiers.
*/
void MenuBuilder::loadFromConfig()
{
this->id = parent()->metaObject()->className(); // Set the ID first because the following subroutine will use it
Config()->registerMenuBuilder(this, _containers.size()); // Register it to the config so the customization dialog can get the text of actions here.
}
QMenu* MenuBuilder::addMenu(QMenu* submenu, BuildCallback callback)
{
addBuilder(new MenuBuilder(submenu->parent(), [submenu, callback](QMenu*)
{
submenu->clear();
return callback(submenu);
}))->addMenu(submenu);
return submenu;
}
QMenu* MenuBuilder::addMenu(QMenu* submenu, MenuBuilder* builder)
{
addBuilder(new MenuBuilder(submenu->parent(), [submenu, builder](QMenu*)
{
submenu->clear();
return builder->build(submenu);
}))->addMenu(submenu);
return submenu;
}
/**
* @brief MenuBuilder::getText Get the title of id-th element. This function is called by CustomizeMenuDialog to initialize the dialog.
* @param id The index of the element in "_containers"
* @return The title, or empty. If it is empty, that element will not appear in the CustomizeMenuDialog.
*/
QString MenuBuilder::getText(size_t id) const
{
const Container & container = _containers.at(id);
switch(container.type)
{
case Container::Action:
return container.action->text();
case Container::Menu:
return container.menu->title();
case Container::Builder:
{
if(container.builder->_containers.size() == 1)
return container.builder->getText(0); // recursively get the text inside the menu builder
else
return "";
}
default: // separator
return "";
}
}
/**
* @brief MenuBuilder::build Build the menu with contents stored in the menu builder
* @param menu The menu to build.
* @return true if the callback succeeds, false if the callback returns false.
*/
bool MenuBuilder::build(QMenu* menu) const
{
if(_callback && !_callback(menu))
return false;
QMenu* submenu;
if(id != 0)
submenu = new QMenu(tr("More commands"), menu);
else
submenu = nullptr;
for(size_t i = 0; i < _containers.size(); i++)
{
const Container & container = _containers.at(i);
QMenu* _menu;
if(id != 0 && container.type != Container::Separator && Config()->getBool("Gui", QString("Menu%1Hidden%2").arg(id).arg(i)))
_menu = submenu;
else
_menu = menu;
switch(container.type)
{
case Container::Separator:
_menu->addSeparator();
break;
case Container::Action:
_menu->addAction(container.action);
break;
case Container::Menu:
_menu->addMenu(container.menu);
break;
case Container::Builder:
container.builder->build(_menu);
break;
default:
break;
}
}
if(id != 0 && !submenu->actions().isEmpty())
{
menu->addSeparator();
menu->addMenu(submenu);
}
else if(submenu)
{
delete submenu;
}
return true;
}

View File

@ -5,6 +5,9 @@
#include <QMenu>
#include <functional>
/**
* @brief The MenuBuilder class implements the dynamical context menu system for many views.
*/
class MenuBuilder : public QObject
{
Q_OBJECT
@ -13,10 +16,13 @@ public:
inline MenuBuilder(QObject* parent, BuildCallback callback = nullptr)
: QObject(parent),
id(0),
_callback(callback)
{
}
void loadFromConfig();
inline void addSeparator()
{
_containers.push_back(Container());
@ -40,29 +46,9 @@ public:
return menu;
}
inline QMenu* addMenu(QMenu* submenu, BuildCallback callback)
{
addBuilder(new MenuBuilder(submenu->parent(), [submenu, callback](QMenu * menu)
{
submenu->clear();
if(callback(submenu))
menu->addMenu(submenu);
return true;
}));
return submenu;
}
QMenu* addMenu(QMenu* submenu, BuildCallback callback);
inline QMenu* addMenu(QMenu* submenu, MenuBuilder* builder)
{
addBuilder(new MenuBuilder(submenu->parent(), [submenu, builder](QMenu * menu)
{
submenu->clear();
if(builder->build(submenu))
menu->addMenu(submenu);
return true;
}));
return submenu;
}
QMenu* addMenu(QMenu* submenu, MenuBuilder* builder);
inline MenuBuilder* addBuilder(MenuBuilder* builder)
{
@ -70,33 +56,15 @@ public:
return builder;
}
inline bool build(QMenu* menu) const
QString getText(size_t id) const;
const char* getId() const
{
if(_callback && !_callback(menu))
return false;
for(const Container & container : _containers)
{
switch(container.type)
{
case Container::Separator:
menu->addSeparator();
break;
case Container::Action:
menu->addAction(container.action);
break;
case Container::Menu:
menu->addMenu(container.menu);
break;
case Container::Builder:
container.builder->build(menu);
break;
default:
break;
}
}
return true;
return id;
}
bool build(QMenu* menu) const;
inline bool empty() const
{
return _containers.empty();
@ -147,6 +115,7 @@ private:
};
BuildCallback _callback;
const char* id;
std::vector<Container> _containers;
};

View File

@ -171,7 +171,9 @@ SOURCES += \
Src/BasicView/LabeledSplitter.cpp \
Src/BasicView/LabeledSplitterDetachedWindow.cpp \
Src/Gui/LogStatusLabel.cpp \
Src/Gui/DebugStatusLabel.cpp
Src/Gui/DebugStatusLabel.cpp \
Src/Utils/MenuBuilder.cpp \
Src/Gui/CustomizeMenuDialog.cpp
HEADERS += \
@ -279,7 +281,8 @@ HEADERS += \
Src/BasicView/LabeledSplitter.h \
Src/BasicView/LabeledSplitterDetachedWindow.h \
Src/Gui/LogStatusLabel.h \
Src/Gui/DebugStatusLabel.h
Src/Gui/DebugStatusLabel.h \
Src/Gui/CustomizeMenuDialog.h
FORMS += \
@ -315,7 +318,8 @@ FORMS += \
Src/Gui/ColumnReorderDialog.ui \
Src/Gui/FavouriteTools.ui \
Src/Gui/BrowseDialog.ui \
Src/Gui/VirtualModDialog.ui
Src/Gui/VirtualModDialog.ui \
Src/Gui/CustomizeMenuDialog.ui
##
## Libraries

View File

@ -208,10 +208,9 @@ SOURCES += \
dbg/commands/cmd-undocumented.cpp \
dbg/commands/cmd-user-database.cpp \
dbg/commands/cmd-variables.cpp \
dbg/commands/cmd-watch-control.cpp
TRANSLATIONS = \
gui/Translations/x64dbg.ts
dbg/commands/cmd-watch-control.cpp \
gui/Src/Gui/CustomizeMenuDialog.cpp \
gui/Src/Utils/MenuBuilder.cpp
HEADERS += \
gui/Src/Exports.h \
@ -432,7 +431,8 @@ HEADERS += \
dbg/commands/cmd-undocumented.h \
dbg/commands/cmd-user-database.h \
dbg/commands/cmd-variables.h \
dbg/commands/cmd-watch-control.h
dbg/commands/cmd-watch-control.h \
gui/Src/Gui/CustomizeMenuDialog.h
FORMS += \
gui/Src/Gui/AppearanceDialog.ui \
@ -467,7 +467,8 @@ FORMS += \
gui/Src/Gui/VirtualModDialog.ui \
gui/Src/Gui/WordEditDialog.ui \
gui/Src/Gui/XrefBrowseDialog.ui \
gui/Src/Gui/YaraRuleSelectionDialog.ui
gui/Src/Gui/YaraRuleSelectionDialog.ui \
gui/Src/Gui/CustomizeMenuDialog.ui
TRANSLATIONS += \
gui/Translations/x64dbg.ts