Files
qt-creator/src/plugins/coreplugin/externaltool.cpp
Tobias Hunger 88a4421a84 DocumentManager: Refactor saveModified methods
Introduce methods to save a document/list of documents/all documents,
both silently and with a dialog to the DocumentManager.

All of these return a bool that signifies whether the save was
successful or not.

Detailed information on which files failed to load or whether the
save was canceled by the user are still available as optional
in/out parameters.

Change-Id: Id17798302f2a8ba6b85a07c1f0b91f03b20da03f
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
2014-01-23 13:50:29 +01:00

963 lines
34 KiB
C++

/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "externaltool.h"
#include "externaltoolmanager.h"
#include "actionmanager/actionmanager.h"
#include "actionmanager/actioncontainer.h"
#include "coreconstants.h"
#include "variablemanager.h"
#include <app/app_version.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <utils/qtcassert.h>
#include <utils/fileutils.h>
#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QDir>
#include <QDateTime>
#include <QAction>
#include <QDebug>
using namespace Core;
using namespace Core::Internal;
namespace {
const char kExternalTool[] = "externaltool";
const char kId[] = "id";
const char kDescription[] = "description";
const char kDisplayName[] = "displayname";
const char kCategory[] = "category";
const char kOrder[] = "order";
const char kExecutable[] = "executable";
const char kPath[] = "path";
const char kArguments[] = "arguments";
const char kInput[] = "input";
const char kWorkingDirectory[] = "workingdirectory";
const char kXmlLang[] = "xml:lang";
const char kOutput[] = "output";
const char kError[] = "error";
const char kOutputShowInPane[] = "showinpane";
const char kOutputReplaceSelection[] = "replaceselection";
const char kOutputIgnore[] = "ignore";
const char kModifiesDocument[] = "modifiesdocument";
const char kYes[] = "yes";
const char kNo[] = "no";
const char kTrue[] = "true";
const char kFalse[] = "false";
const char kSpecialUncategorizedSetting[] = "SpecialEmptyCategoryForUncategorizedTools";
}
// #pragma mark -- ExternalTool
ExternalTool::ExternalTool() :
m_displayCategory(QLatin1String("")), // difference between isNull and isEmpty
m_order(-1),
m_outputHandling(ShowInPane),
m_errorHandling(ShowInPane),
m_modifiesCurrentDocument(false)
{
}
ExternalTool::ExternalTool(const ExternalTool *other)
: 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),
m_modifiesCurrentDocument(other->m_modifiesCurrentDocument),
m_fileName(other->m_fileName),
m_presetTool(other->m_presetTool)
{
}
ExternalTool &ExternalTool::operator=(const ExternalTool &other)
{
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;
m_modifiesCurrentDocument = other.m_modifiesCurrentDocument;
m_fileName = other.m_fileName;
m_presetTool = other.m_presetTool;
return *this;
}
ExternalTool::~ExternalTool()
{
}
QString ExternalTool::id() const
{
return m_id;
}
QString ExternalTool::description() const
{
return m_description;
}
QString ExternalTool::displayName() const
{
return m_displayName;
}
QString ExternalTool::displayCategory() const
{
return m_displayCategory;
}
int ExternalTool::order() const
{
return m_order;
}
QStringList ExternalTool::executables() const
{
return m_executables;
}
QString ExternalTool::arguments() const
{
return m_arguments;
}
QString ExternalTool::input() const
{
return m_input;
}
QString ExternalTool::workingDirectory() const
{
return m_workingDirectory;
}
ExternalTool::OutputHandling ExternalTool::outputHandling() const
{
return m_outputHandling;
}
ExternalTool::OutputHandling ExternalTool::errorHandling() const
{
return m_errorHandling;
}
bool ExternalTool::modifiesCurrentDocument() const
{
return m_modifiesCurrentDocument;
}
void ExternalTool::setFileName(const QString &fileName)
{
m_fileName = fileName;
}
void ExternalTool::setPreset(QSharedPointer<ExternalTool> preset)
{
m_presetTool = preset;
}
QString ExternalTool::fileName() const
{
return m_fileName;
}
QSharedPointer<ExternalTool> ExternalTool::preset() const
{
return m_presetTool;
}
void ExternalTool::setId(const QString &id)
{
m_id = id;
}
void ExternalTool::setDisplayCategory(const QString &category)
{
m_displayCategory = category;
}
void ExternalTool::setDisplayName(const QString &name)
{
m_displayName = name;
}
void ExternalTool::setDescription(const QString &description)
{
m_description = description;
}
void ExternalTool::setOutputHandling(OutputHandling handling)
{
m_outputHandling = handling;
}
void ExternalTool::setErrorHandling(OutputHandling handling)
{
m_errorHandling = handling;
}
void ExternalTool::setModifiesCurrentDocument(bool modifies)
{
m_modifiesCurrentDocument = modifies;
}
void ExternalTool::setExecutables(const QStringList &executables)
{
m_executables = executables;
}
void ExternalTool::setArguments(const QString &arguments)
{
m_arguments = arguments;
}
void ExternalTool::setInput(const QString &input)
{
m_input = input;
}
void ExternalTool::setWorkingDirectory(const QString &workingDirectory)
{
m_workingDirectory = workingDirectory;
}
static QStringList splitLocale(const QString &locale)
{
QString value = locale;
QStringList values;
if (!value.isEmpty())
values << value;
int index = value.indexOf(QLatin1Char('.'));
if (index >= 0) {
value = value.left(index);
if (!value.isEmpty())
values << value;
}
index = value.indexOf(QLatin1Char('_'));
if (index >= 0) {
value = value.left(index);
if (!value.isEmpty())
values << value;
}
return values;
}
static void localizedText(const QStringList &locales, QXmlStreamReader *reader, int *currentLocale, QString *currentText)
{
Q_ASSERT(reader);
Q_ASSERT(currentLocale);
Q_ASSERT(currentText);
if (reader->attributes().hasAttribute(QLatin1String(kXmlLang))) {
int index = locales.indexOf(reader->attributes().value(QLatin1String(kXmlLang)).toString());
if (index >= 0 && (index < *currentLocale || *currentLocale < 0)) {
*currentText = reader->readElementText();
*currentLocale = index;
} else {
reader->skipCurrentElement();
}
} else {
if (*currentLocale < 0 && currentText->isEmpty()) {
*currentText = QCoreApplication::translate("Core::Internal::ExternalTool",
reader->readElementText().toUtf8().constData(),
"", QCoreApplication::UnicodeUTF8);
} else {
reader->skipCurrentElement();
}
}
if (currentText->isNull()) // prefer isEmpty over isNull
*currentText = QLatin1String("");
}
static bool parseOutputAttribute(const QString &attribute, QXmlStreamReader *reader, ExternalTool::OutputHandling *value)
{
const QStringRef output = reader->attributes().value(attribute);
if (output == QLatin1String(kOutputShowInPane)) {
*value = ExternalTool::ShowInPane;
} else if (output == QLatin1String(kOutputReplaceSelection)) {
*value = ExternalTool::ReplaceSelection;
} else if (output == QLatin1String(kOutputIgnore)) {
*value = ExternalTool::Ignore;
} else {
reader->raiseError(QLatin1String("Allowed values for output attribute are 'showinpane','replaceselection','ignore'"));
return false;
}
return true;
}
ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *errorMessage, const QString &locale)
{
int descriptionLocale = -1;
int nameLocale = -1;
int categoryLocale = -1;
const QStringList &locales = splitLocale(locale);
ExternalTool *tool = new ExternalTool;
QXmlStreamReader reader(xml);
if (!reader.readNextStartElement() || reader.name() != QLatin1String(kExternalTool))
reader.raiseError(QLatin1String("Missing start element <externaltool>"));
tool->m_id = reader.attributes().value(QLatin1String(kId)).toString();
if (tool->m_id.isEmpty())
reader.raiseError(QLatin1String("Missing or empty id attribute for <externaltool>"));
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String(kDescription)) {
localizedText(locales, &reader, &descriptionLocale, &tool->m_description);
} else if (reader.name() == QLatin1String(kDisplayName)) {
localizedText(locales, &reader, &nameLocale, &tool->m_displayName);
} else if (reader.name() == QLatin1String(kCategory)) {
localizedText(locales, &reader, &categoryLocale, &tool->m_displayCategory);
} else if (reader.name() == QLatin1String(kOrder)) {
if (tool->m_order >= 0) {
reader.raiseError(QLatin1String("only one <order> element allowed"));
break;
}
bool ok;
tool->m_order = reader.readElementText().toInt(&ok);
if (!ok || tool->m_order < 0)
reader.raiseError(QLatin1String("<order> element requires non-negative integer value"));
} else if (reader.name() == QLatin1String(kExecutable)) {
if (reader.attributes().hasAttribute(QLatin1String(kOutput))) {
if (!parseOutputAttribute(QLatin1String(kOutput), &reader, &tool->m_outputHandling))
break;
}
if (reader.attributes().hasAttribute(QLatin1String(kError))) {
if (!parseOutputAttribute(QLatin1String(kError), &reader, &tool->m_errorHandling))
break;
}
if (reader.attributes().hasAttribute(QLatin1String(kModifiesDocument))) {
const QStringRef value = reader.attributes().value(QLatin1String(kModifiesDocument));
if (value == QLatin1String(kYes) || value == QLatin1String(kTrue)) {
tool->m_modifiesCurrentDocument = true;
} else if (value == QLatin1String(kNo) || value == QLatin1String(kFalse)) {
tool->m_modifiesCurrentDocument = false;
} else {
reader.raiseError(QLatin1String("Allowed values for modifiesdocument attribute are 'yes','true','no','false'"));
break;
}
}
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String(kPath)) {
tool->m_executables.append(reader.readElementText());
} else if (reader.name() == QLatin1String(kArguments)) {
if (!tool->m_arguments.isEmpty()) {
reader.raiseError(QLatin1String("only one <arguments> element allowed"));
break;
}
tool->m_arguments = reader.readElementText();
} else if (reader.name() == QLatin1String(kInput)) {
if (!tool->m_input.isEmpty()) {
reader.raiseError(QLatin1String("only one <input> element allowed"));
break;
}
tool->m_input = reader.readElementText();
} else if (reader.name() == QLatin1String(kWorkingDirectory)) {
if (!tool->m_workingDirectory.isEmpty()) {
reader.raiseError(QLatin1String("only one <workingdirectory> element allowed"));
break;
}
tool->m_workingDirectory = reader.readElementText();
} else {
reader.raiseError(QString::fromLatin1("Unknown element <%1> as subelement of <%2>").arg(
reader.qualifiedName().toString(), QLatin1String(kExecutable)));
break;
}
}
} else {
reader.raiseError(QString::fromLatin1("Unknown element <%1>").arg(reader.qualifiedName().toString()));
}
}
if (reader.hasError()) {
if (errorMessage)
*errorMessage = reader.errorString();
delete tool;
return 0;
}
return tool;
}
ExternalTool * ExternalTool::createFromFile(const QString &fileName, QString *errorMessage, const QString &locale)
{
QString absFileName = QFileInfo(fileName).absoluteFilePath();
Utils::FileReader reader;
if (!reader.fetch(absFileName, errorMessage))
return 0;
ExternalTool *tool = ExternalTool::createFromXml(reader.data(), errorMessage, locale);
if (!tool)
return 0;
tool->m_fileName = absFileName;
return tool;
}
static QLatin1String stringForOutputHandling(ExternalTool::OutputHandling handling)
{
switch (handling) {
case ExternalTool::Ignore:
return QLatin1String(kOutputIgnore);
case ExternalTool::ShowInPane:
return QLatin1String(kOutputShowInPane);
case ExternalTool::ReplaceSelection:
return QLatin1String(kOutputReplaceSelection);
}
return QLatin1String("");
}
bool ExternalTool::save(QString *errorMessage) const
{
if (m_fileName.isEmpty())
return false;
Utils::FileSaver saver(m_fileName);
if (!saver.hasError()) {
QXmlStreamWriter out(saver.file());
out.setAutoFormatting(true);
out.writeStartDocument(QLatin1String("1.0"));
out.writeComment(QString::fromLatin1("Written on %1 by Qt Creator %2")
.arg(QDateTime::currentDateTime().toString(), QLatin1String(Constants::IDE_VERSION_LONG)));
out.writeStartElement(QLatin1String(kExternalTool));
out.writeAttribute(QLatin1String(kId), m_id);
out.writeTextElement(QLatin1String(kDescription), m_description);
out.writeTextElement(QLatin1String(kDisplayName), m_displayName);
out.writeTextElement(QLatin1String(kCategory), m_displayCategory);
if (m_order != -1)
out.writeTextElement(QLatin1String(kOrder), QString::number(m_order));
out.writeStartElement(QLatin1String(kExecutable));
out.writeAttribute(QLatin1String(kOutput), stringForOutputHandling(m_outputHandling));
out.writeAttribute(QLatin1String(kError), stringForOutputHandling(m_errorHandling));
out.writeAttribute(QLatin1String(kModifiesDocument), m_modifiesCurrentDocument ? QLatin1String(kYes) : QLatin1String(kNo));
foreach (const QString &executable, m_executables)
out.writeTextElement(QLatin1String(kPath), executable);
if (!m_arguments.isEmpty())
out.writeTextElement(QLatin1String(kArguments), m_arguments);
if (!m_input.isEmpty())
out.writeTextElement(QLatin1String(kInput), m_input);
if (!m_workingDirectory.isEmpty())
out.writeTextElement(QLatin1String(kWorkingDirectory), m_workingDirectory);
out.writeEndElement();
out.writeEndDocument();
saver.setResult(&out);
}
return saver.finalize(errorMessage);
}
bool ExternalTool::operator==(const ExternalTool &other) const
{
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_modifiesCurrentDocument == other.m_modifiesCurrentDocument
&& m_errorHandling == other.m_errorHandling
&& m_fileName == other.m_fileName;
}
// #pragma mark -- ExternalToolRunner
ExternalToolRunner::ExternalToolRunner(const ExternalTool *tool)
: m_tool(new ExternalTool(tool)),
m_process(0),
m_outputCodec(QTextCodec::codecForLocale()),
m_hasError(false)
{
run();
}
ExternalToolRunner::~ExternalToolRunner()
{
if (m_tool)
delete m_tool;
}
bool ExternalToolRunner::hasError() const
{
return m_hasError;
}
QString ExternalToolRunner::errorString() const
{
return m_errorString;
}
bool ExternalToolRunner::resolve()
{
if (!m_tool)
return false;
m_resolvedExecutable.clear();
m_resolvedArguments.clear();
m_resolvedWorkingDirectory.clear();
{ // executable
QStringList expandedExecutables; /* for error message */
foreach (const QString &executable, m_tool->executables()) {
QString expanded = Core::VariableManager::expandedString(executable);
expandedExecutables << expanded;
m_resolvedExecutable =
Utils::Environment::systemEnvironment().searchInPath(expanded);
if (!m_resolvedExecutable.isEmpty())
break;
}
if (m_resolvedExecutable.isEmpty()) {
m_hasError = true;
for (int i = 0; i < expandedExecutables.size(); ++i) {
m_errorString += tr("Could not find executable for '%1' (expanded '%2')")
.arg(m_tool->executables().at(i))
.arg(expandedExecutables.at(i));
m_errorString += QLatin1Char('\n');
}
if (!m_errorString.isEmpty())
m_errorString.chop(1);
return false;
}
}
{ // arguments
m_resolvedArguments = Utils::QtcProcess::expandMacros(m_tool->arguments(),
Core::VariableManager::macroExpander());
}
{ // input
m_resolvedInput = Core::VariableManager::expandedString(m_tool->input());
}
{ // working directory
m_resolvedWorkingDirectory = Core::VariableManager::expandedString(m_tool->workingDirectory());
}
return true;
}
void ExternalToolRunner::run()
{
if (!resolve()) {
deleteLater();
return;
}
if (m_tool->modifiesCurrentDocument()) {
if (IDocument *document = EditorManager::currentDocument()) {
m_expectedFileName = document->filePath();
if (!DocumentManager::saveModifiedDocument(document)) {
deleteLater();
return;
}
DocumentManager::expectFileChange(m_expectedFileName);
}
}
m_process = new Utils::QtcProcess(this);
connect(m_process, SIGNAL(started()), this, SLOT(started()));
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus)));
connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError()));
if (!m_resolvedWorkingDirectory.isEmpty())
m_process->setWorkingDirectory(m_resolvedWorkingDirectory);
m_process->setCommand(m_resolvedExecutable, m_resolvedArguments);
MessageManager::write(
tr("Starting external tool '%1' %2").arg(m_resolvedExecutable, m_resolvedArguments), MessageManager::Silent);
m_process->start();
}
void ExternalToolRunner::started()
{
if (!m_resolvedInput.isEmpty())
m_process->write(m_resolvedInput.toLocal8Bit());
m_process->closeWriteChannel();
}
void ExternalToolRunner::finished(int exitCode, QProcess::ExitStatus status)
{
if (status == QProcess::NormalExit && exitCode == 0
&& (m_tool->outputHandling() == ExternalTool::ReplaceSelection
|| m_tool->errorHandling() == ExternalTool::ReplaceSelection)) {
ExternalToolManager::emitReplaceSelectionRequested(m_processOutput);
}
if (m_tool->modifiesCurrentDocument())
DocumentManager::unexpectFileChange(m_expectedFileName);
MessageManager::write(
tr("'%1' finished").arg(m_resolvedExecutable), MessageManager::Silent);
deleteLater();
}
void ExternalToolRunner::error(QProcess::ProcessError error)
{
if (m_tool->modifiesCurrentDocument())
DocumentManager::unexpectFileChange(m_expectedFileName);
// TODO inform about errors
Q_UNUSED(error);
deleteLater();
}
void ExternalToolRunner::readStandardOutput()
{
if (m_tool->outputHandling() == ExternalTool::Ignore)
return;
QByteArray data = m_process->readAllStandardOutput();
QString output = m_outputCodec->toUnicode(data.constData(), data.length(), &m_outputCodecState);
if (m_tool->outputHandling() == ExternalTool::ShowInPane)
MessageManager::write(output);
else if (m_tool->outputHandling() == ExternalTool::ReplaceSelection)
m_processOutput.append(output);
}
void ExternalToolRunner::readStandardError()
{
if (m_tool->errorHandling() == ExternalTool::Ignore)
return;
QByteArray data = m_process->readAllStandardError();
QString output = m_outputCodec->toUnicode(data.constData(), data.length(), &m_errorCodecState);
if (m_tool->errorHandling() == ExternalTool::ShowInPane)
MessageManager::write(output);
else if (m_tool->errorHandling() == ExternalTool::ReplaceSelection)
m_processOutput.append(output);
}
// ExternalToolManager
struct ExternalToolManagerPrivate
{
QMap<QString, ExternalTool *> m_tools;
QMap<QString, QList<ExternalTool *> > m_categoryMap;
QMap<QString, QAction *> m_actions;
QMap<QString, ActionContainer *> m_containers;
QAction *m_configureSeparator;
QAction *m_configureAction;
};
static ExternalToolManager *m_instance = 0;
static ExternalToolManagerPrivate *d = 0;
static void writeSettings();
static void readSettings(const QMap<QString, ExternalTool *> &tools,
QMap<QString, QList<ExternalTool*> > *categoryPriorityMap);
static void parseDirectory(const QString &directory,
QMap<QString, QMultiMap<int, ExternalTool*> > *categoryMenus,
QMap<QString, ExternalTool *> *tools,
bool isPreset = false);
ExternalToolManager::ExternalToolManager()
: QObject(ICore::instance())
{
m_instance = this;
d = new ExternalToolManagerPrivate;
d->m_configureSeparator = new QAction(this);
d->m_configureSeparator->setSeparator(true);
d->m_configureAction = new QAction(tr("Configure..."), this);
connect(d->m_configureAction, SIGNAL(triggered()), this, SLOT(openPreferences()));
// add the external tools menu
ActionContainer *mexternaltools = ActionManager::createMenu(Id(Constants::M_TOOLS_EXTERNAL));
mexternaltools->menu()->setTitle(ExternalToolManager::tr("&External"));
ActionContainer *mtools = ActionManager::actionContainer(Constants::M_TOOLS);
mtools->addMenu(mexternaltools, Constants::G_DEFAULT_THREE);
QMap<QString, QMultiMap<int, ExternalTool*> > categoryPriorityMap;
QMap<QString, ExternalTool *> tools;
parseDirectory(ICore::userResourcePath() + QLatin1String("/externaltools"),
&categoryPriorityMap,
&tools);
parseDirectory(ICore::resourcePath() + QLatin1String("/externaltools"),
&categoryPriorityMap,
&tools,
true);
QMap<QString, QList<ExternalTool *> > categoryMap;
QMapIterator<QString, QMultiMap<int, ExternalTool*> > it(categoryPriorityMap);
while (it.hasNext()) {
it.next();
categoryMap.insert(it.key(), it.value().values());
}
// read renamed categories and custom order
readSettings(tools, &categoryMap);
setToolsByCategory(categoryMap);
}
ExternalToolManager::~ExternalToolManager()
{
writeSettings();
// TODO kill running tools
qDeleteAll(d->m_tools);
delete d;
}
QObject *ExternalToolManager::instance()
{
return m_instance;
}
static void parseDirectory(const QString &directory,
QMap<QString, QMultiMap<int, ExternalTool*> > *categoryMenus,
QMap<QString, ExternalTool *> *tools,
bool isPreset)
{
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()) {
const QString &fileName = info.absoluteFilePath();
QString error;
ExternalTool *tool = ExternalTool::createFromFile(fileName, &error, ICore::userInterfaceLanguage());
if (!tool) {
qWarning() << ExternalTool::tr("Error while parsing external tool %1: %2").arg(fileName, error);
continue;
}
if (tools->contains(tool->id())) {
if (isPreset) {
// preset that was changed
ExternalTool *other = tools->value(tool->id());
other->setPreset(QSharedPointer<ExternalTool>(tool));
} else {
qWarning() << ExternalToolManager::tr("Error: External tool in %1 has duplicate id").arg(fileName);
delete tool;
}
continue;
}
if (isPreset) {
// preset that wasn't changed --> save original values
tool->setPreset(QSharedPointer<ExternalTool>(new ExternalTool(tool)));
}
tools->insert(tool->id(), tool);
(*categoryMenus)[tool->displayCategory()].insert(tool->order(), tool);
}
}
void ExternalToolManager::menuActivated()
{
QAction *action = qobject_cast<QAction *>(sender());
QTC_ASSERT(action, return);
ExternalTool *tool = d->m_tools.value(action->data().toString());
QTC_ASSERT(tool, return);
ExternalToolRunner *runner = new ExternalToolRunner(tool);
if (runner->hasError())
MessageManager::write(runner->errorString());
}
QMap<QString, QList<ExternalTool *> > ExternalToolManager::toolsByCategory()
{
return d->m_categoryMap;
}
QMap<QString, ExternalTool *> ExternalToolManager::toolsById()
{
return d->m_tools;
}
void ExternalToolManager::setToolsByCategory(const QMap<QString, QList<ExternalTool *> > &tools)
{
// clear menu
ActionContainer *mexternaltools = ActionManager::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 (d->m_tools.value(id) == tool) {
newActions.insert(id, d->m_actions.value(id));
// remove from list to prevent deletion
d->m_tools.remove(id);
d->m_actions.remove(id);
}
newTools.insert(id, tool);
}
}
qDeleteAll(d->m_tools);
QMapIterator<QString, QAction *> remainingActions(d->m_actions);
const Id externalToolsPrefix = "Tools.External.";
while (remainingActions.hasNext()) {
remainingActions.next();
ActionManager::unregisterAction(remainingActions.value(),
externalToolsPrefix.withSuffix(remainingActions.key()));
delete remainingActions.value();
}
d->m_actions.clear();
// assign the new stuff
d->m_tools = newTools;
d->m_actions = newActions;
d->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.isEmpty()) { // no displayCategory, so put into external tools menu directly
container = mexternaltools;
} else {
if (d->m_containers.contains(containerName))
container = d->m_containers.take(containerName); // remove to avoid deletion below
else
container = ActionManager::createMenu(Id("Tools.External.Category.").withSuffix(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 (d->m_actions.contains(toolId)) {
action = d->m_actions.value(toolId);
command = ActionManager::command(externalToolsPrefix.withSuffix(toolId));
} else {
action = new QAction(tool->displayName(), m_instance);
action->setData(toolId);
d->m_actions.insert(toolId, action);
connect(action, SIGNAL(triggered()), m_instance, SLOT(menuActivated()));
command = ActionManager::registerAction(action, externalToolsPrefix.withSuffix(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(d->m_containers);
// remember the new containers
d->m_containers = newContainers;
// (re)add the configure menu item
mexternaltools->menu()->addAction(d->m_configureSeparator);
mexternaltools->menu()->addAction(d->m_configureAction);
}
static void readSettings(const QMap<QString, ExternalTool *> &tools,
QMap<QString, QList<ExternalTool *> > *categoryMap)
{
QSettings *settings = ICore::settings();
settings->beginGroup(QLatin1String("ExternalTools"));
if (categoryMap) {
settings->beginGroup(QLatin1String("OverrideCategories"));
foreach (const QString &settingsCategory, settings->childGroups()) {
QString displayCategory = settingsCategory;
if (displayCategory == QLatin1String(kSpecialUncategorizedSetting))
displayCategory = QLatin1String("");
int count = settings->beginReadArray(settingsCategory);
for (int i = 0; i < count; ++i) {
settings->setArrayIndex(i);
const QString &toolId = settings->value(QLatin1String("Tool")).toString();
if (tools.contains(toolId)) {
ExternalTool *tool = tools.value(toolId);
// remove from old category
(*categoryMap)[tool->displayCategory()].removeAll(tool);
if (categoryMap->value(tool->displayCategory()).isEmpty())
categoryMap->remove(tool->displayCategory());
// add to new category
(*categoryMap)[displayCategory].append(tool);
}
}
settings->endArray();
}
settings->endGroup();
}
settings->endGroup();
}
static void writeSettings()
{
QSettings *settings = ICore::settings();
settings->beginGroup(QLatin1String("ExternalTools"));
settings->remove(QLatin1String(""));
settings->beginGroup(QLatin1String("OverrideCategories"));
QMapIterator<QString, QList<ExternalTool *> > it(d->m_categoryMap);
while (it.hasNext()) {
it.next();
QString category = it.key();
if (category.isEmpty())
category = QLatin1String(kSpecialUncategorizedSetting);
settings->beginWriteArray(category, it.value().count());
int i = 0;
foreach (ExternalTool *tool, it.value()) {
settings->setArrayIndex(i);
settings->setValue(QLatin1String("Tool"), tool->id());
++i;
}
settings->endArray();
}
settings->endGroup();
settings->endGroup();
}
void ExternalToolManager::openPreferences()
{
ICore::showOptionsDialog(Constants::SETTINGS_CATEGORY_CORE, Constants::SETTINGS_ID_TOOLS);
}
void ExternalToolManager::emitReplaceSelectionRequested(const QString &output)
{
emit m_instance->replaceSelectionRequested(output);
}