basefilewizardfactory.cpp 11 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

31
#include "basefilewizardfactory.h"
Tobias Hunger's avatar
Tobias Hunger committed
32
33

#include "basefilewizard.h"
34
#include "icontext.h"
35
36
37
#include "icore.h"
#include "ifilewizardextension.h"
#include "editormanager/editormanager.h"
38
#include "dialogs/promptoverwritedialog.h"
39
#include <extensionsystem/pluginmanager.h>
40
#include <utils/filewizardpage.h>
Eike Ziller's avatar
Eike Ziller committed
41
#include <utils/mimetypes/mimedatabase.h>
42
#include <utils/qtcassert.h>
43
#include <utils/stringutils.h>
Eike Ziller's avatar
Eike Ziller committed
44
#include <utils/wizard.h>
con's avatar
con committed
45

46
47
48
49
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QIcon>
con's avatar
con committed
50
51
52
53
54

enum { debugWizard = 0 };

namespace Core {

55
56
57
58
59
60
61
62
63
static int indexOfFile(const GeneratedFiles &f, const QString &path)
{
    const int size = f.size();
    for (int i = 0; i < size; ++i)
        if (f.at(i).path() == path)
            return i;
    return -1;
}

64
65
/*!
    \class Core::BaseFileWizard
66
67
    \brief The BaseFileWizard class implements a generic wizard for
    creating files.
68

69
    The following abstract functions must be implemented:
70
    \list
71
    \li create(): Called to create the QWizard dialog to be shown.
Leena Miettinen's avatar
Leena Miettinen committed
72
    \li generateFiles(): Generates file content.
73
74
    \endlist

75
    The behaviour can be further customized by overwriting the virtual function \c postGenerateFiles(),
76
77
78
79
80
81
    which is called after generating the files.

    \sa Core::GeneratedFile, Core::BaseFileWizardParameters, Core::StandardFileWizard
    \sa Core::Internal::WizardEventLoop
*/

82
Utils::Wizard *BaseFileWizardFactory::runWizardImpl(const QString &path, QWidget *parent,
83
                                                    Id platform,
84
                                                    const QVariantMap &extraValues)
con's avatar
con committed
85
{
86
    QTC_ASSERT(!path.isEmpty(), return 0);
87

con's avatar
con committed
88
89
90
    // Create dialog and run it. Ensure that the dialog is deleted when
    // leaving the func, but not before the IFileWizardExtension::process
    // has been called
91
92
93
94
95
96

    WizardDialogParameters::DialogParameterFlags dialogParameterFlags;

    if (flags().testFlag(ForceCapitalLetterForFileName))
        dialogParameterFlags |= WizardDialogParameters::ForceCapitalLetterForFileName;

Tobias Hunger's avatar
Tobias Hunger committed
97
98
99
100
101
102
    Utils::Wizard *wizard = create(parent, WizardDialogParameters(path, platform,
                                                                  requiredFeatures(),
                                                                  dialogParameterFlags,
                                                                  extraValues));
    QTC_CHECK(wizard);
    return wizard;
con's avatar
con committed
103
104
}

105
/*!
106
107
    \fn virtual QWizard *Core::BaseFileWizard::create(QWidget *parent,
                                                      const WizardDialogParameters &parameters) const
Leena Miettinen's avatar
Leena Miettinen committed
108

109
    Creates the wizard on the \a parent with the \a parameters.
110
111
112
113
114
*/

/*!
    \fn virtual Core::GeneratedFiles Core::BaseFileWizard::generateFiles(const QWizard *w,
                                                                         QString *errorMessage) const = 0
Leena Miettinen's avatar
Leena Miettinen committed
115
    Overwrite to query the parameters from the dialog and generate the files.
116

Leena Miettinen's avatar
Leena Miettinen committed
117
118
    \note This does not generate physical files, but merely the list of
    Core::GeneratedFile.
119
120
121
*/

/*!
Leena Miettinen's avatar
Leena Miettinen committed
122
    Physically writes files.
123
124
125
126

    Re-implement (calling the base implementation) to create files with CustomGeneratorAttribute set.
*/

Tobias Hunger's avatar
Tobias Hunger committed
127
bool BaseFileWizardFactory::writeFiles(const GeneratedFiles &files, QString *errorMessage) const
128
{
129
130
    const GeneratedFile::Attributes noWriteAttributes
        = GeneratedFile::CustomGeneratorAttribute|GeneratedFile::KeepExistingFileAttribute;
131
    foreach (const GeneratedFile &generatedFile, files)
132
        if (!(generatedFile.attributes() & noWriteAttributes ))
133
134
135
136
137
            if (!generatedFile.write(errorMessage))
                return false;
    return true;
}

138
/*!
Leena Miettinen's avatar
Leena Miettinen committed
139
    Overwrite to perform steps to be done after files are actually created.
140
141
142
143

    The default implementation opens editors with the newly generated files.
*/

Tobias Hunger's avatar
Tobias Hunger committed
144
145
bool BaseFileWizardFactory::postGenerateFiles(const QWizard *, const GeneratedFiles &l,
                                              QString *errorMessage) const
146
{
147
    return BaseFileWizardFactory::postGenerateOpenEditors(l, errorMessage);
148
149
}

150
/*!
Leena Miettinen's avatar
Leena Miettinen committed
151
    Opens the editors for the files whose attribute is set accordingly.
152
153
*/

154
bool BaseFileWizardFactory::postGenerateOpenEditors(const GeneratedFiles &l, QString *errorMessage)
con's avatar
con committed
155
{
hjk's avatar
hjk committed
156
157
158
    foreach (const GeneratedFile &file, l) {
        if (file.attributes() & GeneratedFile::OpenEditorAttribute) {
            if (!EditorManager::openEditor(file.path(), file.editorId())) {
159
                if (errorMessage)
160
                    *errorMessage = tr("Failed to open an editor for \"%1\".").arg(QDir::toNativeSeparators(file.path()));
161
162
                return false;
            }
con's avatar
con committed
163
164
165
166
167
        }
    }
    return true;
}

168
/*!
Leena Miettinen's avatar
Leena Miettinen committed
169
170
    Performs an overwrite check on a set of \a files. Checks if the file exists and
    can be overwritten at all, and then prompts the user with a summary.
171
172
*/

173
BaseFileWizardFactory::OverwriteResult BaseFileWizardFactory::promptOverwrite(GeneratedFiles *files,
con's avatar
con committed
174
175
176
                                                                QString *errorMessage) const
{
    if (debugWizard)
177
        qDebug() << Q_FUNC_INFO << files;
con's avatar
con committed
178

179
    QStringList existingFiles;
con's avatar
con committed
180
181
    bool oddStuffFound = false;

182
183
184
    static const QString readOnlyMsg = tr("[read only]");
    static const QString directoryMsg = tr("[folder]");
    static const QString symLinkMsg = tr("[symbolic link]");
con's avatar
con committed
185

186
    foreach (const GeneratedFile &file, *files) {
187
188
189
        const QString path = file.path();
        if (QFileInfo::exists(path))
            existingFiles.append(path);
190
    }
191
192
193
194
195
    if (existingFiles.isEmpty())
        return OverwriteOk;
    // Before prompting to overwrite existing files, loop over files and check
    // if there is anything blocking overwriting them (like them being links or folders).
    // Format a file list message as ( "<file1> [readonly], <file2> [folder]").
196
    const QString commonExistingPath = Utils::commonPath(existingFiles);
con's avatar
con committed
197
    QString fileNamesMsgPart;
198
    foreach (const QString &fileName, existingFiles) {
con's avatar
con committed
199
200
201
202
        const QFileInfo fi(fileName);
        if (fi.exists()) {
            if (!fileNamesMsgPart.isEmpty())
                fileNamesMsgPart += QLatin1String(", ");
203
            fileNamesMsgPart += QDir::toNativeSeparators(fileName.mid(commonExistingPath.size() + 1));
con's avatar
con committed
204
205
206
            do {
                if (fi.isDir()) {
                    oddStuffFound = true;
207
                    fileNamesMsgPart += QLatin1Char(' ') + directoryMsg;
con's avatar
con committed
208
209
210
211
                    break;
                }
                if (fi.isSymLink()) {
                    oddStuffFound = true;
212
                    fileNamesMsgPart += QLatin1Char(' ') + symLinkMsg;
con's avatar
con committed
213
214
215
216
                    break;
            }
                if (!fi.isWritable()) {
                    oddStuffFound = true;
217
                    fileNamesMsgPart += QLatin1Char(' ') + readOnlyMsg;
con's avatar
con committed
218
219
220
221
222
223
                }
            } while (false);
        }
    }

    if (oddStuffFound) {
224
225
        *errorMessage = tr("The project directory %1 contains files which cannot be overwritten:\n%2.")
                .arg(QDir::toNativeSeparators(commonExistingPath)).arg(fileNamesMsgPart);
con's avatar
con committed
226
227
        return OverwriteError;
    }
228
    // Prompt to overwrite existing files.
229
    PromptOverwriteDialog overwriteDialog;
230
231
232
233
234
235
236
237
238
239
240
241
242
    // Scripts cannot handle overwrite
    overwriteDialog.setFiles(existingFiles);
    foreach (const GeneratedFile &file, *files)
        if (file.attributes() & GeneratedFile::CustomGeneratorAttribute)
            overwriteDialog.setFileEnabled(file.path(), false);
    if (overwriteDialog.exec() != QDialog::Accepted)
        return OverwriteCanceled;
    const QStringList existingFilesToKeep = overwriteDialog.uncheckedFiles();
    if (existingFilesToKeep.size() == files->size()) // All exist & all unchecked->Cancel.
        return OverwriteCanceled;
    // Set 'keep' attribute in files
    foreach (const QString &keepFile, existingFilesToKeep) {
        const int i = indexOfFile(*files, keepFile);
243
        QTC_ASSERT(i != -1, return OverwriteCanceled);
244
245
246
247
        GeneratedFile &file = (*files)[i];
        file.setAttributes(file.attributes() | GeneratedFile::KeepExistingFileAttribute);
    }
    return OverwriteOk;
con's avatar
con committed
248
249
}

250
/*!
Leena Miettinen's avatar
Leena Miettinen committed
251
252
    Constructs a file name, adding the \a extension unless \a baseName already has
    one.
253
254
*/

255
QString BaseFileWizardFactory::buildFileName(const QString &path,
con's avatar
con committed
256
257
258
259
                                      const QString &baseName,
                                      const QString &extension)
{
    QString rc = path;
260
261
262
    const QChar slash = QLatin1Char('/');
    if (!rc.isEmpty() && !rc.endsWith(slash))
        rc += slash;
con's avatar
con committed
263
264
265
266
267
268
269
270
271
272
273
274
275
    rc += baseName;
    // Add extension unless user specified something else
    const QChar dot = QLatin1Char('.');
    if (!extension.isEmpty() && !baseName.contains(dot)) {
        if (!extension.startsWith(dot))
            rc += dot;
        rc += extension;
    }
    if (debugWizard)
        qDebug() << Q_FUNC_INFO << rc;
    return rc;
}

276
/*!
Leena Miettinen's avatar
Leena Miettinen committed
277
    Returns the preferred suffix for \a mimeType.
278
279
*/

280
QString BaseFileWizardFactory::preferredSuffix(const QString &mimeType)
con's avatar
con committed
281
{
Eike Ziller's avatar
Eike Ziller committed
282
283
284
285
286
    QString rc;
    Utils::MimeDatabase mdb;
    Utils::MimeType mt = mdb.mimeTypeForName(mimeType);
    if (mt.isValid())
        rc = mt.preferredSuffix();
con's avatar
con committed
287
288
289
290
291
292
    if (rc.isEmpty())
        qWarning("%s: WARNING: Unable to find a preferred suffix for %s.",
                 Q_FUNC_INFO, mimeType.toUtf8().constData());
    return rc;
}

293
294
/*!
    \class Core::StandardFileWizard
295
296
    \brief The StandardFileWizard class is a convenience class for
    creating one file.
297
298
299
300
301
302
303

    It uses Utils::FileWizardDialog and introduces a new virtual to generate the
    files from path and name.

    \sa Core::GeneratedFile, Core::BaseFileWizardParameters, Core::BaseFileWizard
*/

con's avatar
con committed
304
} // namespace Core