diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp
index bbea103821b7520d6c61d98e750aa6508cce5dc3..dac798e2210305890b84d730d1138584b804a8c0 100644
--- a/src/plugins/coreplugin/externaltool.cpp
+++ b/src/plugins/coreplugin/externaltool.cpp
@@ -42,8 +42,10 @@
 #include <utils/environment.h>
 
 #include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
 #include <QtCore/QDir>
 #include <QtCore/QFile>
+#include <QtCore/QDateTime>
 #include <QtGui/QMenu>
 #include <QtGui/QMenuItem>
 #include <QtGui/QAction>
@@ -55,6 +57,7 @@ using namespace Core::Internal;
 
 namespace {
     const char * const kExternalTool = "externaltool";
+    const char * const kId = "id";
     const char * const kDescription = "description";
     const char * const kDisplayName = "displayname";
     const char * const kCategory = "category";
@@ -84,8 +87,7 @@ ExternalTool::ExternalTool() :
     m_order(-1),
     m_outputHandling(ShowInPane),
     m_errorHandling(ShowInPane),
-    m_modifiesCurrentDocument(false),
-    m_isDisplayNameChanged(false)
+    m_modifiesCurrentDocument(false)
 {
 }
 
@@ -102,7 +104,8 @@ ExternalTool::ExternalTool(const ExternalTool *other)
       m_outputHandling(other->m_outputHandling),
       m_errorHandling(other->m_errorHandling),
       m_modifiesCurrentDocument(other->m_modifiesCurrentDocument),
-      m_isDisplayNameChanged(other->m_isDisplayNameChanged)
+      m_fileName(other->m_fileName),
+      m_presetFileName(other->m_presetFileName)
 {
 }
 
@@ -170,82 +173,75 @@ bool ExternalTool::modifiesCurrentDocument() const
     return m_modifiesCurrentDocument;
 }
 
+void ExternalTool::setFileName(const QString &fileName)
+{
+    m_fileName = fileName;
+}
+
+void ExternalTool::setPresetFileName(const QString &fileName)
+{
+    m_presetFileName = fileName;
+}
+
+QString ExternalTool::fileName() const
+{
+    return m_fileName;
+}
+
+QString ExternalTool::presetFileName() const
+{
+    return m_presetFileName;
+}
+
 void ExternalTool::setDisplayName(const QString &name)
 {
-    if (name == m_displayName)
-        return;
-    m_isDisplayNameChanged = true;
     m_displayName = name;
 }
 
 void ExternalTool::setDescription(const QString &description)
 {
-    if (description == m_description)
-        return;
-    m_isChanged = true;
     m_description = description;
 }
 
 
 void ExternalTool::setOutputHandling(OutputHandling handling)
 {
-    if (handling == m_outputHandling)
-        return;
-    m_isChanged = true;
     m_outputHandling = handling;
 }
 
 
 void ExternalTool::setErrorHandling(OutputHandling handling)
 {
-    if (handling == m_errorHandling)
-        return;
-    m_isChanged = true;
     m_errorHandling = handling;
 }
 
 
 void ExternalTool::setModifiesCurrentDocument(bool modifies)
 {
-    if (modifies == m_modifiesCurrentDocument)
-        return;
-    m_isChanged = true;
     m_modifiesCurrentDocument = modifies;
 }
 
 
 void ExternalTool::setExecutables(const QStringList &executables)
 {
-    if (executables == m_executables)
-        return;
-    m_isChanged = true;
     m_executables = executables;
 }
 
 
 void ExternalTool::setArguments(const QString &arguments)
 {
-    if (arguments == m_arguments)
-        return;
-    m_isChanged = true;
     m_arguments = arguments;
 }
 
 
 void ExternalTool::setInput(const QString &input)
 {
-    if (input == m_input)
-        return;
-    m_isChanged = true;
     m_input = input;
 }
 
 
 void ExternalTool::setWorkingDirectory(const QString &workingDirectory)
 {
-    if (workingDirectory == m_workingDirectory)
-        return;
-    m_isChanged = true;
     m_workingDirectory = workingDirectory;
 }
 
@@ -319,7 +315,7 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error
 
     if (!reader.readNextStartElement() || reader.name() != QLatin1String(kExternalTool))
         reader.raiseError(QLatin1String("Missing start element <externaltool>"));
-    tool->m_id = reader.attributes().value(QLatin1String("id")).toString();
+    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()) {
@@ -398,6 +394,82 @@ ExternalTool * ExternalTool::createFromXml(const QByteArray &xml, QString *error
     return tool;
 }
 
+ExternalTool * ExternalTool::createFromFile(const QString &fileName, QString *errorMessage, const QString &locale, bool isPreset)
+{
+    QFileInfo info(fileName);
+    QFile file(info.absoluteFilePath());
+    if (!file.open(QIODevice::ReadOnly)) {
+        if (errorMessage)
+            *errorMessage = tr("Could not open tool specification %1 for reading").arg(fileName);
+        return 0;
+    }
+    const QByteArray &bytes = file.readAll();
+    file.close();
+    ExternalTool *tool = ExternalTool::createFromXml(bytes, errorMessage, locale);
+    if (!tool) {
+        return 0;
+    }
+    tool->m_fileName = file.fileName();
+    if (isPreset) {
+        tool->setPresetFileName(file.fileName());
+    }
+    return tool;
+}
+
+static QLatin1String stringForOutputHandling(ExternalTool::OutputHandling handling)
+{
+    switch (handling) {
+    case Core::Internal::ExternalTool::Ignore:
+        return QLatin1String(kOutputIgnore);
+    case Core::Internal::ExternalTool::ShowInPane:
+        return QLatin1String(kOutputShowInPane);
+    case Core::Internal::ExternalTool::ReplaceSelection:
+        return QLatin1String(kOutputReplaceSelection);
+    }
+    return QLatin1String("");
+}
+
+bool ExternalTool::save(QString *errorMessage) const
+{
+    if (m_fileName.isEmpty())
+        return false;
+    QFile file(m_fileName);
+    if (!file.open(QIODevice::WriteOnly)) {
+        if (errorMessage)
+            *errorMessage = tr("Could not write tool specification %1").arg(m_fileName);
+        return false;
+    }
+    QXmlStreamWriter out(&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();
+    file.close();
+}
+
 bool ExternalTool::operator==(const ExternalTool &other)
 {
     return m_id == other.m_id
@@ -610,33 +682,31 @@ void ExternalToolManager::initialize()
 void ExternalToolManager::parseDirectory(const QString &directory,
                                          QMap<QString, QMultiMap<int, Internal::ExternalTool*> > *categoryMenus,
                                          QMap<QString, ExternalTool *> *tools,
-                                         bool ignoreDuplicates)
+                                         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()) {
-        QFile file(info.absoluteFilePath());
-        if (file.open(QIODevice::ReadOnly)) {
-            const QByteArray &bytes = file.readAll();
-            file.close();
-            QString error;
-            ExternalTool *tool = ExternalTool::createFromXml(bytes, &error, m_core->userInterfaceLanguage());
-            if (!tool) {
-                // TODO error handling
-                qDebug() << tr("Error while parsing external tool %1: %2").arg(file.fileName(), error);
-                continue;
-            }
-            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;
+        const QString &fileName = info.absoluteFilePath();
+        QString error;
+        ExternalTool *tool = ExternalTool::createFromFile(fileName, &error, m_core->userInterfaceLanguage(), isPreset);
+        if (!tool) {
+            qWarning() << tr("Error while parsing external tool %1: %2").arg(fileName, error);
+            continue;
+        }
+        if (tools->contains(tool->id())) {
+            if (isPreset) {
+                ExternalTool *other = tools->value(tool->id());
+                other->setPresetFileName(fileName);
+            } else {
+                qWarning() << tr("Error: External tool in %1 has duplicate id").arg(fileName);
             }
-            tools->insert(tool->id(), tool);
-            (*categoryMenus)[tool->displayCategory()].insert(tool->order(), tool);
+            delete tool;
+            continue;
         }
+        tools->insert(tool->id(), tool);
+        (*categoryMenus)[tool->displayCategory()].insert(tool->order(), tool);
     }
 }
 
@@ -750,16 +820,6 @@ void ExternalToolManager::readSettings(const QMap<QString, ExternalTool *> &tool
     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();
-
     if (categoryPriorityMap) {
         settings->beginGroup(QLatin1String("OverrideCategories"));
         foreach (const QString &id, settings->allKeys()) {
@@ -786,14 +846,6 @@ void ExternalToolManager::writeSettings()
     settings->beginGroup(QLatin1String("ExternalTools"));
     settings->remove(QLatin1String(""));
 
-    settings->beginGroup(QLatin1String("OverrideDisplayNames"));
-    foreach (ExternalTool *tool, m_tools) {
-        if (tool->isDisplayNameChanged()) {
-            settings->setValue(tool->id(), tool->displayName());
-        }
-    }
-    settings->endGroup();
-
     settings->beginGroup(QLatin1String("OverrideCategories"));
     QMapIterator<QString, QList<ExternalTool *> > it(m_categoryMap);
     while (it.hasNext()) {
diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h
index 445426ebf55058c853017963ebb75022ec3a5d60..a51fe2ba319c35b027dfd665904735a5c2dbe04f 100644
--- a/src/plugins/coreplugin/externaltool.h
+++ b/src/plugins/coreplugin/externaltool.h
@@ -75,11 +75,21 @@ public:
     QString input() const;
     QString workingDirectory() const;
 
+    void setFileName(const QString &fileName);
+    void setPresetFileName(const QString &fileName);
+    QString fileName() const;
+    QString presetFileName() const;
+
     static ExternalTool *createFromXml(const QByteArray &xml, QString *errorMessage = 0, const QString &locale = QString());
+    static ExternalTool *createFromFile(const QString &fileName, QString *errorMessage = 0,
+                                        const QString &locale = QString(), bool isPreset = false);
+
+    bool save(QString *errorMessage = 0) const;
 
     // ignores changed state
     bool operator==(const ExternalTool &other);
 
+    // display category and order are handled specially
     void setDisplayName(const QString &name);
     void setDescription(const QString &description);
     void setOutputHandling(OutputHandling handling);
@@ -89,9 +99,7 @@ public:
     void setArguments(const QString &arguments);
     void setInput(const QString &input);
     void setWorkingDirectory(const QString &workingDirectory);
-    // if the display name is different from the one in the original xml
-    bool isDisplayNameChanged() const { return m_isDisplayNameChanged; }
-    bool isChanged() const { return m_isChanged; }
+
 private:
     QString m_id;
     QString m_description;
@@ -106,8 +114,10 @@ private:
     OutputHandling m_errorHandling;
     bool m_modifiesCurrentDocument;
 
-    bool m_isDisplayNameChanged;
-    bool m_isChanged;
+    QString m_fileName;
+    QString m_presetFileName;
+
+    mutable bool m_isChanged;
 };
 
 class ExternalToolRunner : public QObject
@@ -169,7 +179,7 @@ private:
     void parseDirectory(const QString &directory,
                         QMap<QString, QMultiMap<int, Internal::ExternalTool*> > *categoryMenus,
                         QMap<QString, Internal::ExternalTool *> *tools,
-                        bool ignoreDuplicates = false);
+                        bool isPreset = false);
     void readSettings(const QMap<QString, Internal::ExternalTool *> &tools,
                       QMap<QString, QMultiMap<int, Internal::ExternalTool*> > *categoryPriorityMap);
     void writeSettings();
diff --git a/src/plugins/coreplugin/toolsettings.cpp b/src/plugins/coreplugin/toolsettings.cpp
index 3e6a0c0007681e5ebca6986ff4aadf3b8e949a61..d38a3352898d320f581942f26619f8dcf5e8ffc8 100644
--- a/src/plugins/coreplugin/toolsettings.cpp
+++ b/src/plugins/coreplugin/toolsettings.cpp
@@ -33,6 +33,8 @@
 #include "coreconstants.h"
 
 #include <QtCore/QCoreApplication>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
 
 using namespace Core;
 using namespace Core::Internal;
@@ -107,6 +109,20 @@ void ToolSettings::apply()
                 if ((*originalTool) == (*tool)) {
                     toolToAdd = originalTool;
                 } else {
+                    // save the new tool description and make it be added
+                    if (!tool->presetFileName().isEmpty() && tool->fileName() == tool->presetFileName()) {
+                        // we don't overwrite the preset, so give it a new file name in user resources
+                        // TODO avoid overwriting a tool xml file of another existing tool?
+                        const QString &fileName = QFileInfo(tool->presetFileName()).fileName();
+                        QDir resourceDir(ICore::instance()->userResourcePath());
+                        if (!resourceDir.exists(QLatin1String("externaltools")))
+                            resourceDir.mkpath(QLatin1String("externaltools"));
+                        const QString &newFilePath = ICore::instance()->userResourcePath()
+                                + QLatin1String("/externaltools/") + fileName;
+                        tool->setFileName(newFilePath);
+                    }
+                    // TODO error handling
+                    tool->save();
                     toolToAdd = new ExternalTool(tool);
                 }
             }
diff --git a/src/share/qtcreator/externaltools/vi_mac.xml b/src/share/qtcreator/externaltools/vi_mac.xml
index eca14163c4e356bc7636191b2f9033e02748516a..e2f5fd6d172d89fd8cb983a73821ccb06aa1ef86 100644
--- a/src/share/qtcreator/externaltools/vi_mac.xml
+++ b/src/share/qtcreator/externaltools/vi_mac.xml
@@ -41,13 +41,13 @@
         <input>
         --Terminal opens a window by default when it is not running, so check
         on applicationIsRunning(applicationName)
-                tell application "System Events" to count (every process whose name is applicationName)
+                tell application &quot;System Events&quot; to count (every process whose name is applicationName)
                 return result is greater than 0
         end applicationIsRunning
-        set terminalWasRunning to applicationIsRunning("Terminal")
+        set terminalWasRunning to applicationIsRunning(&quot;Terminal&quot;)
 
-        set editorScript to "vi \"%{CurrentDocument:FilePath}\" +%{CurrentDocument:Row} +\"normal %{CurrentDocument:Column}|\"; exit"
-        tell application "Terminal"
+        set editorScript to &quot;vi \&quot;%{CurrentDocument:FilePath}\&quot; +%{CurrentDocument:Row} +\&quot;normal %{CurrentDocument:Column}|\&quot;; exit&quot;
+        tell application &quot;Terminal&quot;
             --do script will open a new window if none given, but terminal already opens one if not running
             if terminalWasRunning then
                 do script editorScript