Commit eaea867e authored by con's avatar con

Implement renaming of tools and categories.

Not possible to reset them yet.
Saves the change in the settings instead of writing new tools xml files.
This probably needs to be adapted for tools that are "local" from the
beginning.
parent d768008d
......@@ -268,10 +268,32 @@ void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group
QAction *beforeAction = insertLocation(groupIt);
m_groups[groupIt-m_groups.constBegin()].items.append(menu);
connect(menu, SIGNAL(destroyed()), this, SLOT(itemDestroyed()));
insertMenu(beforeAction, container->menu());
scheduleUpdate();
}
void ActionContainerPrivate::clear()
{
QMutableListIterator<Group> it(m_groups);
while (it.hasNext()) {
Group &group = it.next();
foreach (QObject *item, group.items) {
if (Command *command = qobject_cast<Command *>(item)) {
removeAction(command->action());
disconnect(command, SIGNAL(activeStateChanged()), this, SLOT(scheduleUpdate()));
disconnect(command, SIGNAL(destroyed()), this, SLOT(itemDestroyed()));
} else if (ActionContainer *container = qobject_cast<ActionContainer *>(item)) {
container->clear();
disconnect(container, SIGNAL(destroyed()), this, SLOT(itemDestroyed()));
removeMenu(container->menu());
}
}
group.items.clear();
}
scheduleUpdate();
}
void ActionContainerPrivate::itemDestroyed()
{
QObject *obj = sender();
......@@ -350,6 +372,16 @@ void MenuActionContainer::insertMenu(QAction *before, QMenu *menu)
m_menu->insertMenu(before, menu);
}
void MenuActionContainer::removeAction(QAction *action)
{
m_menu->removeAction(action);
}
void MenuActionContainer::removeMenu(QMenu *menu)
{
m_menu->removeAction(menu->menuAction());
}
bool MenuActionContainer::updateInternal()
{
if (onAllDisabledBehavior() == Show)
......@@ -442,6 +474,16 @@ void MenuBarActionContainer::insertMenu(QAction *before, QMenu *menu)
m_menuBar->insertMenu(before, menu);
}
void MenuBarActionContainer::removeAction(QAction *action)
{
m_menuBar->removeAction(action);
}
void MenuBarActionContainer::removeMenu(QMenu *menu)
{
m_menuBar->removeAction(menu->menuAction());
}
bool MenuBarActionContainer::updateInternal()
{
if (onAllDisabledBehavior() == Show)
......
......@@ -70,6 +70,9 @@ public:
virtual void addAction(Core::Command *action, const QString &group = QString()) = 0;
virtual void addMenu(Core::ActionContainer *menu, const QString &group = QString()) = 0;
// clears this menu and submenus from all actions and submenus
// doesn't destroy the submenus and commands, just removes them from their parents
virtual void clear() = 0;
virtual ~ActionContainer() {}
};
......
......@@ -63,6 +63,7 @@ public:
void appendGroup(const QString &id);
void addAction(Command *action, const QString &group = QString());
void addMenu(ActionContainer *menu, const QString &group = QString());
virtual void clear();
int id() const;
......@@ -72,6 +73,9 @@ public:
virtual void insertAction(QAction *before, QAction *action) = 0;
virtual void insertMenu(QAction *before, QMenu *menu) = 0;
virtual void removeAction(QAction *action) = 0;
virtual void removeMenu(QMenu *menu) = 0;
virtual bool updateInternal() = 0;
protected:
......@@ -107,6 +111,9 @@ public:
void insertAction(QAction *before, QAction *action);
void insertMenu(QAction *before, QMenu *menu);
void removeAction(QAction *action);
void removeMenu(QMenu *menu);
protected:
bool canBeAddedToMenu() const;
bool updateInternal();
......@@ -126,6 +133,9 @@ public:
void insertAction(QAction *before, QAction *action);
void insertMenu(QAction *before, QMenu *menu);
void removeAction(QAction *action);
void removeMenu(QMenu *menu);
protected:
bool canBeAddedToMenu() const;
bool updateInternal();
......
......@@ -248,6 +248,8 @@ ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
ActionManagerPrivate::~ActionManagerPrivate()
{
// first delete containers to avoid them reacting to command deletion
foreach (ActionContainerPrivate *container, m_idContainerMap)
disconnect(container, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
qDeleteAll(m_idContainerMap.values());
qDeleteAll(m_idCmdMap.values());
}
......@@ -314,6 +316,7 @@ ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
mc->setMenu(m);
m_idContainerMap.insert(uid, mc);
connect(mc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
return mc;
}
......@@ -332,10 +335,17 @@ ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
mbc->setMenuBar(mb);
m_idContainerMap.insert(uid, mbc);
connect(mbc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
return mbc;
}
void ActionManagerPrivate::containerDestroyed()
{
ActionContainerPrivate *container = static_cast<ActionContainerPrivate *>(sender());
m_idContainerMap.remove(m_idContainerMap.key(container));
}
Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
{
Action *a = overridableAction(id);
......
......@@ -93,6 +93,8 @@ public:
void unregisterAction(QAction *action, const Id &id);
void unregisterShortcut(const Id &id);
private slots:
void containerDestroyed();
private:
bool hasContext(const Context &context) const;
Action *overridableAction(const Id &id);
......
......@@ -34,6 +34,8 @@
#include "externaltoolconfig.h"
#include "ui_externaltoolconfig.h"
#include <utils/qtcassert.h>
#include <QtCore/QTextStream>
using namespace Core::Internal;
......@@ -43,13 +45,21 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
ui(new Ui::ExternalToolConfig)
{
ui->setupUi(this);
ui->toolTree->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
connect(ui->toolTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
this, SLOT(showInfoForItem(QTreeWidgetItem*)));
connect(ui->toolTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
this, SLOT(updateItem(QTreeWidgetItem *)));
showInfoForItem(0);
}
ExternalToolConfig::~ExternalToolConfig()
{
QMapIterator<QString, QList<ExternalTool *> > it(m_tools);
while (it.hasNext()) {
it.next();
qDeleteAll(it.value());
}
delete ui;
}
......@@ -69,19 +79,32 @@ QString ExternalToolConfig::searchKeywords() const
void ExternalToolConfig::setTools(const QMap<QString, QList<ExternalTool *> > &tools)
{
// TODO make copy of tools
QMapIterator<QString, QList<ExternalTool *> > it(tools);
while (it.hasNext()) {
it.next();
QList<ExternalTool *> itemCopy;
foreach (ExternalTool *tool, it.value())
itemCopy.append(new ExternalTool(tool));
m_tools.insert(it.key(), itemCopy);
}
QMapIterator<QString, QList<ExternalTool *> > categories(tools);
bool blocked = ui->toolTree->blockSignals(true); // block itemChanged
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
QMapIterator<QString, QList<ExternalTool *> > categories(m_tools);
while (categories.hasNext()) {
categories.next();
QString name = (categories.key().isEmpty() ? tr("External Tools Menu") : categories.key());
QTreeWidgetItem *category = new QTreeWidgetItem(ui->toolTree, QStringList() << name);
category->setFlags(flags);
category->setData(0, Qt::UserRole, name); // save the name in case of category being renamed by user
foreach (ExternalTool *tool, categories.value()) {
QTreeWidgetItem *item = new QTreeWidgetItem(category, QStringList() << tool->displayName());
item->setFlags(flags);
item->setData(0, Qt::UserRole, qVariantFromValue(tool));
}
}
ui->toolTree->expandAll();
ui->toolTree->blockSignals(blocked); // unblock itemChanged
}
void ExternalToolConfig::showInfoForItem(QTreeWidgetItem *item)
......@@ -110,3 +133,51 @@ void ExternalToolConfig::showInfoForItem(QTreeWidgetItem *item)
ui->description->setCursorPosition(0);
ui->arguments->setCursorPosition(0);
}
void ExternalToolConfig::updateItem(QTreeWidgetItem *item)
{
ExternalTool *tool = 0;
if (item)
tool = item->data(0, Qt::UserRole).value<ExternalTool *>();
if (tool) {
// tool was renamed
const QString &newName = item->data(0, Qt::DisplayRole).toString();
if (newName == tool->displayName())
return;
if (newName.isEmpty()) {
// prevent empty names
bool blocked = ui->toolTree->blockSignals(true); // block itemChanged
item->setData(0, Qt::DisplayRole, tool->displayName());
ui->toolTree->blockSignals(blocked); // unblock itemChanged
return;
}
tool->setDisplayName(item->data(0, Qt::DisplayRole).toString());
} else {
// category was renamed
const QString &oldName = item->data(0, Qt::UserRole).toString();
const QString &newName = item->data(0, Qt::DisplayRole).toString();
if (oldName == newName)
return;
if (newName.isEmpty() && m_tools.contains(newName)) {
// prevent empty or duplicate names
bool blocked = ui->toolTree->blockSignals(true); // block itemChanged
item->setData(0, Qt::DisplayRole, oldName);
ui->toolTree->blockSignals(blocked); // unblock itemChanged
return;
}
QTC_ASSERT(m_tools.contains(oldName), return);
m_tools.insert(newName, m_tools.value(oldName));
m_tools.remove(oldName);
bool blocked = ui->toolTree->blockSignals(true); // block itemChanged
item->setData(0, Qt::UserRole, newName);
int currentIndex = ui->toolTree->indexOfTopLevelItem(item);
bool wasExpanded = ui->toolTree->isExpanded(ui->toolTree->model()->index(currentIndex, 0));
ui->toolTree->takeTopLevelItem(currentIndex);
int newIndex = m_tools.keys().indexOf(newName);
ui->toolTree->insertTopLevelItem(newIndex, item);
if (wasExpanded)
ui->toolTree->expand(ui->toolTree->model()->index(newIndex, 0));
ui->toolTree->blockSignals(blocked); // unblock itemChanged
}
}
......@@ -55,14 +55,17 @@ public:
~ExternalToolConfig();
void setTools(const QMap<QString, QList<ExternalTool *> > &tools);
QMap<QString, QList<ExternalTool *> > tools() const { return m_tools; }
QString searchKeywords() const;
private slots:
void showInfoForItem(QTreeWidgetItem *item);
void updateItem(QTreeWidgetItem *item);
private:
Ui::ExternalToolConfig *ui;
QMap<QString, QList<ExternalTool *> > m_tools;
};
} // Internal
......
......@@ -29,7 +29,6 @@
#include "externaltool.h"
#include "actionmanager/actionmanager.h"
#include "actionmanager/actioncontainer.h"
#include "coreconstants.h"
#include "variablemanager.h"
......@@ -80,7 +79,8 @@ namespace {
ExternalTool::ExternalTool() :
m_order(-1),
m_outputHandling(ShowInPane),
m_errorHandling(ShowInPane)
m_errorHandling(ShowInPane),
m_isDisplayNameChanged(false)
{
}
......@@ -95,7 +95,8 @@ ExternalTool::ExternalTool(const ExternalTool *other)
m_input(other->m_input),
m_workingDirectory(other->m_workingDirectory),
m_outputHandling(other->m_outputHandling),
m_errorHandling(other->m_errorHandling)
m_errorHandling(other->m_errorHandling),
m_isDisplayNameChanged(other->m_isDisplayNameChanged)
{
}
......@@ -118,6 +119,14 @@ QString ExternalTool::displayName() const
return m_displayName;
}
void ExternalTool::setDisplayName(const QString &name)
{
if (name == m_displayName)
return;
m_isDisplayNameChanged = true;
m_displayName = name;
}
QString ExternalTool::displayCategory() const
{
return m_displayCategory;
......@@ -298,16 +307,37 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error
return tool;
}
bool ExternalTool::operator==(const ExternalTool &other)
{
return m_id == other.m_id
&& m_description == other.m_description
&& m_displayName == other.m_displayName
&& m_displayCategory == other.m_displayCategory
&& m_order == other.m_order
&& m_executables == other.m_executables
&& m_arguments == other.m_arguments
&& m_input == other.m_input
&& m_workingDirectory == other.m_workingDirectory
&& m_outputHandling == other.m_outputHandling
&& m_errorHandling == other.m_errorHandling;
}
// #pragma mark -- ExternalToolRunner
ExternalToolRunner::ExternalToolRunner(const ExternalTool *tool)
: m_tool(tool),
: m_tool(new ExternalTool(tool)),
m_process(0),
m_outputCodec(QTextCodec::codecForLocale())
{
run();
}
ExternalToolRunner::~ExternalToolRunner()
{
if (m_tool)
delete m_tool;
}
bool ExternalToolRunner::resolve()
{
if (!m_tool)
......@@ -450,60 +480,50 @@ ExternalToolManager::ExternalToolManager(Core::ICore *core)
ExternalToolManager::~ExternalToolManager()
{
writeSettings();
// TODO kill running tools
qDeleteAll(m_tools);
}
void ExternalToolManager::initialize()
{
// add the external tools menu
ActionManager *am = m_core->actionManager();
ActionContainer *mexternaltools = am->createMenu(Id(Constants::M_TOOLS_EXTERNAL));
mexternaltools->menu()->setTitle(tr("External"));
ActionContainer *mtools = am->actionContainer(Constants::M_TOOLS);
mtools->addMenu(mexternaltools, Constants::G_DEFAULT_THREE);
QMap<QString, QMultiMap<int, ExternalTool*> > categoryMap;
QMap<QString, QMultiMap<int, ExternalTool*> > categoryPriorityMap;
QMap<QString, ExternalTool *> tools;
parseDirectory(m_core->userResourcePath() + QLatin1String("/externaltools"),
&categoryMap);
&categoryPriorityMap,
&tools);
parseDirectory(m_core->resourcePath() + QLatin1String("/externaltools"),
&categoryMap, true);
&categoryPriorityMap,
&tools,
true);
QMapIterator<QString, QMultiMap<int, ExternalTool*> > it(categoryMap);
while (it.hasNext()) {
it.next();
m_categoryMap.insert(it.key(), it.value().values());
}
// adapt overridden names and categories etc
readSettings(tools, &categoryPriorityMap);
// add all the category menus, QMap is nicely sorted
it.toFront();
QMap<QString, QList<Internal::ExternalTool *> > categoryMap;
QMapIterator<QString, QMultiMap<int, ExternalTool*> > it(categoryPriorityMap);
while (it.hasNext()) {
it.next();
ActionContainer *container = 0;
if (it.key() == QString()) { // no displayCategory, so put into external tools menu directly
container = mexternaltools;
} else {
container = am->createMenu(Id("Tools.External.Category." + it.key()));
mexternaltools->addMenu(container, Constants::G_DEFAULT_ONE);
container->menu()->setTitle(it.key());
}
foreach (ExternalTool *tool, it.value().values()) {
// tool action and command
QAction *action = new QAction(tool->displayName(), this);
action->setToolTip(tool->description());
action->setWhatsThis(tool->description());
action->setData(tool->id());
Command *cmd = am->registerAction(action, Id("Tools.External." + tool->id()), Context(Constants::C_GLOBAL));
connect(action, SIGNAL(triggered()), this, SLOT(menuActivated()));
container->addAction(cmd, Constants::G_DEFAULT_TWO);
}
categoryMap.insert(it.key(), it.value().values());
}
setToolsByCategory(categoryMap);
}
void ExternalToolManager::parseDirectory(const QString &directory, QMap<QString, QMultiMap<int, ExternalTool*> > *categoryMenus,
void ExternalToolManager::parseDirectory(const QString &directory,
QMap<QString, QMultiMap<int, Internal::ExternalTool*> > *categoryMenus,
QMap<QString, ExternalTool *> *tools,
bool ignoreDuplicates)
{
QTC_ASSERT(categoryMenus, return);
QTC_ASSERT(tools, return);
QDir dir(directory, QLatin1String("*.xml"), QDir::Unsorted, QDir::Files | QDir::Readable);
foreach (const QFileInfo &info, dir.entryInfoList()) {
QFile file(info.absoluteFilePath());
......@@ -517,14 +537,14 @@ void ExternalToolManager::parseDirectory(const QString &directory, QMap<QString,
qDebug() << tr("Error while parsing external tool %1: %2").arg(file.fileName(), error);
continue;
}
if (m_tools.contains(tool->id())) {
if (tools->contains(tool->id())) {
// TODO error handling
if (!ignoreDuplicates)
qDebug() << tr("Error: External tool in %1 has duplicate id").arg(file.fileName());
delete tool;
continue;
}
m_tools.insert(tool->id(), tool);
tools->insert(tool->id(), tool);
(*categoryMenus)[tool->displayCategory()].insert(tool->order(), tool);
}
}
......@@ -539,8 +559,162 @@ void ExternalToolManager::menuActivated()
new ExternalToolRunner(tool);
}
QMap<QString, QList<Internal::ExternalTool *> > ExternalToolManager::tools() const
QMap<QString, QList<Internal::ExternalTool *> > ExternalToolManager::toolsByCategory() const
{
return m_categoryMap;
}
QMap<QString, ExternalTool *> ExternalToolManager::toolsById() const
{
return m_tools;
}
void ExternalToolManager::setToolsByCategory(const QMap<QString, QList<Internal::ExternalTool *> > &tools)
{
// clear menu
ActionManager *am = m_core->actionManager();
ActionContainer *mexternaltools = am->actionContainer(Id(Constants::M_TOOLS_EXTERNAL));
mexternaltools->clear();
// delete old tools and create list of new ones
QMap<QString, ExternalTool *> newTools;
QMap<QString, QAction *> newActions;
QMapIterator<QString, QList<ExternalTool *> > it(tools);
while (it.hasNext()) {
it.next();
foreach (ExternalTool *tool, it.value()) {
const QString &id = tool->id();
if (m_tools.value(id) == tool) {
newActions.insert(id, m_actions.value(id));
// remove from list to prevent deletion
m_tools.remove(id);
m_actions.remove(id);
}
newTools.insert(id, tool);
}
}
qDeleteAll(m_tools);
QMapIterator<QString, QAction *> remainingActions(m_actions);
while (remainingActions.hasNext()) {
remainingActions.next();
am->unregisterAction(remainingActions.value(), Id("Tools.External." + remainingActions.key()));
delete remainingActions.value();
}
m_actions.clear();
// assign the new stuff
m_tools = newTools;
m_actions = newActions;
m_categoryMap = tools;
// create menu structure and remove no-longer used containers
// add all the category menus, QMap is nicely sorted
QMap<QString, ActionContainer *> newContainers;
it.toFront();
while (it.hasNext()) {
it.next();
ActionContainer *container = 0;
const QString &containerName = it.key();
if (containerName == QString()) { // no displayCategory, so put into external tools menu directly
container = mexternaltools;
} else {
if (m_containers.contains(containerName)) {
container = m_containers.take(containerName); // remove to avoid deletion below
} else {
container = am->createMenu(Id("Tools.External.Category." + containerName));
}
newContainers.insert(containerName, container);
mexternaltools->addMenu(container, Constants::G_DEFAULT_ONE);
container->menu()->setTitle(containerName);
}
foreach (ExternalTool *tool, it.value()) {
const QString &toolId = tool->id();
// tool action and command
QAction *action = 0;
Command *command = 0;
if (m_actions.contains(toolId)) {
action = m_actions.value(toolId);
command = am->command(Id("Tools.External." + toolId));
} else {
action = new QAction(tool->displayName(), this);
action->setData(toolId);
m_actions.insert(toolId, action);
connect(action, SIGNAL(triggered()), this, SLOT(menuActivated()));
command = am->registerAction(action, Id("Tools.External." + toolId), Context(Constants::C_GLOBAL));
command->setAttribute(Command::CA_UpdateText);
}
action->setText(tool->displayName());
action->setToolTip(tool->description());
action->setWhatsThis(tool->description());
container->addAction(command, Constants::G_DEFAULT_TWO);
}
}
// delete the unused containers
qDeleteAll(m_containers);
// remember the new containers
m_containers = newContainers;
}
void ExternalToolManager::readSettings(const QMap<QString, ExternalTool *> &tools,
QMap<QString, QMultiMap<int, Internal::ExternalTool*> > *categoryPriorityMap)
{
QSettings *settings = m_core->settings();
settings->beginGroup(QLatin1String("ExternalTools"));
settings->beginGroup(QLatin1String("OverrideDisplayNames"));
foreach (const QString &id, settings->allKeys()) {
if (tools.contains(id)) {
const QString &newName = settings->value(id).toString();
if (tools.value(id)->displayName() != newName)
tools.value(id)->setDisplayName(newName);
}
}
settings->endGroup();