Commit bcb3bb0f authored by Leandro Melo's avatar Leandro Melo
Browse files

Allow the user to save as files with different extensions.

There is still an open issue in this fix since the new extension might be of a different mime type (and our editors are attached to it currently).
More details documented in the code.

Task-number: QTCREATORBUG-2094
Reviewed-by: Thorbjorn Lindeijer
parent f7b555b7
......@@ -246,6 +246,8 @@ const char * const SETTINGS_CATEGORY_CORE_ICON = ":/core/images/
const char * const SETTINGS_TR_CATEGORY_CORE = QT_TRANSLATE_NOOP("Core", "Environment");
const char * const SETTINGS_ID_ENVIRONMENT = "A.General";
const char * const ALL_FILES_FILTER = QT_TRANSLATE_NOOP("Core", "All Files (*)");
const int TARGET_ICON_SIZE = 32;
} // namespace Constants
......@@ -264,8 +264,7 @@ void ShortcutSettings::exportAction()
QString fileName = ICore::instance()->fileManager()->getSaveFileNameWithExtension(
tr("Export Keyboard Mapping Scheme"),
ICore::instance()->resourcePath() + "/schemes/",
tr("Keyboard Mapping Scheme (*.kms)"),
tr("Keyboard Mapping Scheme (*.kms)"));
if (!fileName.isEmpty()) {
CommandsFile cf(fileName);
......@@ -77,6 +77,8 @@
#include <QtGui/QSplitter>
#include <QtGui/QStackedLayout>
#include <algorithm>
enum { debugEditorManager=0 };
......@@ -208,9 +210,6 @@ struct EditorManagerPrivate {
QMap<QString, QVariant> m_editorStates;
Internal::OpenEditorsViewFactory *m_openEditorsFactory;
QString fileFilters;
QString selectedFilter;
OpenEditorsModel *m_editorModel;
QString m_externalEditor;
......@@ -1152,33 +1151,28 @@ QString EditorManager::getOpenWithEditorId(const QString &fileName,
return selectedId;
static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter)
static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter = 0)
QString rc;
if (selectedFilter)
// Compile list of filter strings
// Compile list of filter strings, sort, and remove duplicates (different mime types might
// generate the same filter).
QStringList filters = core->mimeDatabase()->filterStrings();
if (filters.empty())
return rc;
return QString();
filters.erase(std::unique(filters.begin(), filters.end()), filters.end());
const QString filterSeparator = QLatin1String(";;");
foreach (const QString &filterString, filters) {
if (!rc.isEmpty())
rc += filterSeparator;
rc += filterString;
static const QString allFilesFilter =
QCoreApplication::translate("Core", Constants::ALL_FILES_FILTER);
if (selectedFilter)
*selectedFilter = allFilesFilter;
// prepend all files filter
// prepending instead of appending to work around a bug in Qt/Mac
QString allFilesFilter = EditorManager::tr("All Files (*)");
if (!rc.isEmpty())
allFilesFilter += filterSeparator;
*selectedFilter = allFilesFilter;
// Prepend all files filter (instead of appending to work around a bug in Qt/Mac).
return rc;
return filters.join(QLatin1String(";;"));
IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
......@@ -1244,10 +1238,10 @@ bool EditorManager::openExternalEditor(const QString &fileName, const QString &e
QStringList EditorManager::getOpenFileNames() const
if (m_d->fileFilters.isEmpty())
m_d->fileFilters = formatFileFilters(m_d->m_core, &m_d->selectedFilter);
return ICore::instance()->fileManager()->getOpenFileNames(m_d->fileFilters,
QString(), &m_d->selectedFilter);
QString selectedFilter;
const QString &fileFilters = formatFileFilters(m_d->m_core, &selectedFilter);
return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
QString(), &selectedFilter);
......@@ -1475,23 +1469,35 @@ bool EditorManager::saveFileAs(IEditor *editor)
if (!editor)
return false;
QString absoluteFilePath = m_d->m_core->fileManager()->getSaveAsFileName(editor->file());
IFile *file = editor->file();
const QString &filter = formatFileFilters(m_d->m_core);
QString selectedFilter =
const QString &absoluteFilePath =
m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
if (absoluteFilePath.isEmpty())
return false;
if (absoluteFilePath != editor->file()->fileName()) {
if (absoluteFilePath != file->fileName()) {
const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
if (!existList.isEmpty()) {
closeEditors(existList, false);
const bool success = editor->file()->save(absoluteFilePath);
const bool success = file->save(absoluteFilePath);
// @todo: There is an issue to be treated here. The new file might be of a different mime
// type than the original and thus require a different editor. An alternative strategy
// would be to close the current editor and open a new appropriate one, but this is not
// a good way out either (also the undo stack would be lost). Perhaps the best is to
// re-think part of the editors design.
if (success && !editor->isTemporary())
return success;
......@@ -37,6 +37,7 @@
#include "mimedatabase.h"
#include "saveitemsdialog.h"
#include "vcsmanager.h"
#include "coreconstants.h"
#include <utils/qtcassert.h>
#include <utils/pathchooser.h>
......@@ -689,22 +690,45 @@ QList<IFile *> FileManager::saveModifiedFiles(const QList<IFile *> &files,
return notSaved;
QString FileManager::getSaveFileNameWithExtension(const QString &title, const QString &pathIn,
const QString &fileFilter, const QString &extension)
QString FileManager::getSaveFileName(const QString &title, const QString &pathIn,
const QString &filter, QString *selectedFilter)
const QString &path = pathIn.isEmpty() ? fileDialogInitialDirectory() : pathIn;
QString fileName;
bool repeat;
do {
repeat = false;
const QString path = pathIn.isEmpty() ? fileDialogInitialDirectory() : pathIn;
fileName = QFileDialog::getSaveFileName(d->m_mainWindow, title, path, fileFilter);
if (!fileName.isEmpty() && !extension.isEmpty() && !fileName.endsWith(extension)) {
fileName = QFileDialog::getSaveFileName(
d->m_mainWindow, title, path, filter, selectedFilter, QFileDialog::DontConfirmOverwrite);
if (!fileName.isEmpty()) {
// If the selected filter is All Files (*) we leave the name exactly as the user
// specified. Otherwise the suffix must be one available in the selected filter. If
// the name already ends with such suffix nothing needs to be done. But if not, the
// first one from the filter is appended.
if (selectedFilter && *selectedFilter != QCoreApplication::translate(
"Core", Constants::ALL_FILES_FILTER)) {
// Mime database creates filter strings like this: Anything here (*.foo *.bar)
QRegExp regExp(".*\\s+\\((.*)\\)$");
const int index = regExp.lastIndexIn(*selectedFilter);
bool suffixOk = false;
if (index != -1) {
const QStringList &suffixes = regExp.cap(1).remove('*').split(' ');
foreach (const QString &suffix, suffixes)
if (fileName.endsWith(suffix)) {
suffixOk = true;
if (!suffixOk && !suffixes.isEmpty())
if (QFile::exists(fileName)) {
if (QMessageBox::warning(d->m_mainWindow, tr("Overwrite?"),
tr("An item named '%1' already exists at this location. Do you want to overwrite it?").arg(fileName),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
tr("An item named '%1' already exists at this location. "
"Do you want to overwrite it?").arg(fileName),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
repeat = true;
} while (repeat);
......@@ -713,12 +737,19 @@ QString FileManager::getSaveFileNameWithExtension(const QString &title, const QS
return fileName;
QString FileManager::getSaveFileNameWithExtension(const QString &title, const QString &pathIn,
const QString &filter)
QString selected = filter;
return getSaveFileName(title, pathIn, filter, &selected);
\fn QString FileManager::getSaveAsFileName(IFile *file)
Asks the user for a new file name (Save File As) for /arg file.
QString FileManager::getSaveAsFileName(IFile *file)
QString FileManager::getSaveAsFileName(IFile *file, const QString &filter, QString *selectedFilter)
if (!file)
return QLatin1String("");
......@@ -732,17 +763,20 @@ QString FileManager::getSaveAsFileName(IFile *file)
if (!defaultPath.isEmpty())
path = defaultPath;
QString filterString;
QString preferredSuffix;
if (const MimeType mt = Core::ICore::instance()->mimeDatabase()->findByFile(fi)) {
filterString = mt.filterString();
preferredSuffix = mt.preferredSuffix();
if (filter.isEmpty()) {
if (const MimeType &mt = Core::ICore::instance()->mimeDatabase()->findByFile(fi))
filterString = mt.filterString();
selectedFilter = &filterString;
} else {
filterString = filter;
absoluteFilePath = getSaveFileNameWithExtension(tr("Save File As"),
absoluteFilePath = getSaveFileName(tr("Save File As"),
path + QDir::separator() + fileName,
return absoluteFilePath;
......@@ -87,10 +87,12 @@ public:
QStringList getOpenFileNames(const QString &filters,
const QString path = QString(),
QString *selectedFilter = 0);
QString getSaveFileNameWithExtension(const QString &title, const QString &path,
const QString &fileFilter, const QString &extension);
QString getSaveAsFileName(IFile *file);
QString getSaveFileName(const QString &title, const QString &pathIn,
const QString &filter = QString(), QString *selectedFilter = 0);
QString getSaveFileNameWithExtension(const QString &title, const QString &pathIn,
const QString &filter);
QString getSaveAsFileName(IFile *file, const QString &filter = QString(),
QString *selectedFilter = 0);
QList<IFile *> saveModifiedFilesSilently(const QList<IFile *> &files);
QList<IFile *> saveModifiedFiles(const QList<IFile *> &files,
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment