basefilewizard.cpp 25.9 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "basefilewizard.h"
34 35 36 37

#include "coreconstants.h"
#include "icore.h"
#include "ifilewizardextension.h"
con's avatar
con committed
38
#include "mimedatabase.h"
39
#include "editormanager/editormanager.h"
40
#include "dialogs/promptoverwritedialog.h"
41
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
42
#include <utils/filewizarddialog.h>
43
#include <utils/qtcassert.h>
44
#include <utils/stringutils.h>
45
#include <utils/fileutils.h>
con's avatar
con committed
46 47 48 49 50 51 52 53 54

#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QVector>
#include <QtCore/QDebug>
#include <QtCore/QSharedData>
#include <QtCore/QEventLoop>
#include <QtCore/QSharedPointer>
55
#include <QtCore/QScopedPointer>
con's avatar
con committed
56 57 58 59

#include <QtGui/QMessageBox>
#include <QtGui/QWizard>
#include <QtGui/QMainWindow>
60
#include <QtGui/QIcon>
con's avatar
con committed
61 62 63

enum { debugWizard = 0 };

64 65 66 67 68 69 70 71 72 73 74
/*!
    \class Core::GeneratedFile
    \brief Represents a file generated by a wizard.

    The Wizard class will check for each file whether it already exists and will
    report any errors that may occur during creation of the files.

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

con's avatar
con committed
75 76
namespace Core {

hjk's avatar
hjk committed
77 78
class GeneratedFilePrivate : public QSharedData
{
con's avatar
con committed
79
public:
80
    GeneratedFilePrivate() : binary(false) {}
con's avatar
con committed
81 82
    explicit GeneratedFilePrivate(const QString &p);
    QString path;
83
    QByteArray contents;
84
    QString editorId;
85
    bool binary;
86
    GeneratedFile::Attributes attributes;
con's avatar
con committed
87 88 89
};

GeneratedFilePrivate::GeneratedFilePrivate(const QString &p) :
90
    path(QDir::cleanPath(p)),
91
    binary(false),
92
    attributes(0)
con's avatar
con committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
{
}

GeneratedFile::GeneratedFile() :
    m_d(new GeneratedFilePrivate)
{
}

GeneratedFile::GeneratedFile(const QString &p) :
    m_d(new GeneratedFilePrivate(p))
{
}

GeneratedFile::GeneratedFile(const GeneratedFile &rhs) :
    m_d(rhs.m_d)
{
}

GeneratedFile &GeneratedFile::operator=(const GeneratedFile &rhs)
{
    if (this != &rhs)
        m_d.operator=(rhs.m_d);
    return *this;
}

GeneratedFile::~GeneratedFile()
{
}

QString GeneratedFile::path() const
{
    return m_d->path;
}

void GeneratedFile::setPath(const QString &p)
{
129
    m_d->path = QDir::cleanPath(p);
con's avatar
con committed
130 131 132 133
}

QString GeneratedFile::contents() const
{
134
    return QString::fromUtf8(m_d->contents);
con's avatar
con committed
135 136 137
}

void GeneratedFile::setContents(const QString &c)
138 139 140 141 142 143 144 145 146 147
{
    m_d->contents = c.toUtf8();
}

QByteArray GeneratedFile::binaryContents() const
{
    return m_d->contents;
}

void GeneratedFile::setBinaryContents(const QByteArray &c)
con's avatar
con committed
148 149 150 151
{
    m_d->contents = c;
}

152 153 154 155 156 157 158 159 160 161
bool GeneratedFile::isBinary() const
{
    return m_d->binary;
}

void GeneratedFile::setBinary(bool b)
{
    m_d->binary = b;
}

162
QString GeneratedFile::editorId() const
con's avatar
con committed
163
{
164
    return m_d->editorId;
con's avatar
con committed
165 166
}

167
void GeneratedFile::setEditorId(const QString &k)
con's avatar
con committed
168
{
169
    m_d->editorId = k;
con's avatar
con committed
170 171 172 173 174 175 176 177 178 179 180 181 182
}

bool GeneratedFile::write(QString *errorMessage) const
{
    // Ensure the directory
    const QFileInfo info(m_d->path);
    const QDir dir = info.absoluteDir();
    if (!dir.exists()) {
        if (!dir.mkpath(dir.absolutePath())) {
            *errorMessage = BaseFileWizard::tr("Unable to create the directory %1.").arg(dir.absolutePath());
            return false;
        }
    }
183

184
    // Write out
185 186 187 188
    QIODevice::OpenMode flags = QIODevice::WriteOnly|QIODevice::Truncate;
    if (!isBinary())
        flags |= QIODevice::Text;

189 190 191
    Utils::FileSaver saver(m_d->path, flags);
    saver.write(m_d->contents);
    return saver.finalize(errorMessage);
con's avatar
con committed
192
}
hjk's avatar
hjk committed
193

194 195 196 197 198 199 200 201 202
GeneratedFile::Attributes GeneratedFile::attributes() const
{
    return m_d->attributes;
}

void GeneratedFile::setAttributes(Attributes a)
{
    m_d->attributes = a;
}
hjk's avatar
hjk committed
203

204 205 206 207 208 209 210 211 212
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;
}

con's avatar
con committed
213
// ------------ BaseFileWizardParameterData
hjk's avatar
hjk committed
214 215
class BaseFileWizardParameterData : public QSharedData
{
con's avatar
con committed
216
public:
217
    explicit BaseFileWizardParameterData(IWizard::WizardKind kind = IWizard::FileWizard);
218
    void clear();
con's avatar
con committed
219

220
    IWizard::WizardKind kind;
con's avatar
con committed
221 222
    QIcon icon;
    QString description;
223
    QString displayName;
Friedemann Kleint's avatar
Friedemann Kleint committed
224
    QString id;
con's avatar
con committed
225
    QString category;
226
    QString displayCategory;
con's avatar
con committed
227 228
};

229
BaseFileWizardParameterData::BaseFileWizardParameterData(IWizard::WizardKind k) :
con's avatar
con committed
230 231 232 233
    kind(k)
{
}

234 235 236 237 238 239 240 241 242 243 244
void BaseFileWizardParameterData::clear()
{
    kind = IWizard::FileWizard;
    icon = QIcon();
    description.clear();
    displayName.clear();
    id.clear();
    category.clear();
    displayCategory.clear();
}

245 246 247 248 249 250 251 252 253
/*!
    \class Core::BaseFileWizardParameters
    \brief Parameter class for passing parameters to instances of class Wizard
     containing name, icon and such.

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

254
BaseFileWizardParameters::BaseFileWizardParameters(IWizard::WizardKind kind) :
con's avatar
con committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
   m_d(new BaseFileWizardParameterData(kind))
{
}

BaseFileWizardParameters::BaseFileWizardParameters(const BaseFileWizardParameters &rhs) :
    m_d(rhs.m_d)
{
}

BaseFileWizardParameters &BaseFileWizardParameters::operator=(const BaseFileWizardParameters &rhs)
{
    if (this != &rhs)
        m_d.operator=(rhs.m_d);
    return *this;
}

BaseFileWizardParameters::~BaseFileWizardParameters()
{
}

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
void BaseFileWizardParameters::clear()
{
    m_d->clear();
}

CORE_EXPORT QDebug operator<<(QDebug d, const BaseFileWizardParameters &p)
{
    d.nospace() << "Kind: " << p.kind() << " Id: " << p.id()
                << " Category: " << p.category()
                << " DisplayName: " << p.displayName()
                << " Description: " << p.description()
                << " DisplayCategory: " << p.displayCategory();
    return d;
}

290
IWizard::WizardKind BaseFileWizardParameters::kind() const
con's avatar
con committed
291 292 293 294
{
    return m_d->kind;
}

295
void BaseFileWizardParameters::setKind(IWizard::WizardKind k)
con's avatar
con committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
{
    m_d->kind = k;
}

QIcon BaseFileWizardParameters::icon() const
{
    return m_d->icon;
}

void BaseFileWizardParameters::setIcon(const QIcon &icon)
{
    m_d->icon = icon;
}

QString BaseFileWizardParameters::description() const
{
    return m_d->description;
}

void BaseFileWizardParameters::setDescription(const QString &v)
{
    m_d->description = v;
}

320
QString BaseFileWizardParameters::displayName() const
con's avatar
con committed
321
{
322
    return m_d->displayName;
con's avatar
con committed
323 324
}

325
void BaseFileWizardParameters::setDisplayName(const QString &v)
con's avatar
con committed
326
{
327
    m_d->displayName = v;
con's avatar
con committed
328 329
}

Friedemann Kleint's avatar
Friedemann Kleint committed
330 331 332 333 334 335 336 337 338
QString BaseFileWizardParameters::id() const
{
    return m_d->id;
}

void BaseFileWizardParameters::setId(const QString &v)
{
    m_d->id = v;
}
con's avatar
con committed
339 340 341 342 343 344 345 346 347 348 349

QString BaseFileWizardParameters::category() const
{
    return m_d->category;
}

void BaseFileWizardParameters::setCategory(const QString &v)
{
    m_d->category = v;
}

350
QString BaseFileWizardParameters::displayCategory() const
con's avatar
con committed
351
{
352
    return m_d->displayCategory;
con's avatar
con committed
353 354
}

355
void BaseFileWizardParameters::setDisplayCategory(const QString &v)
con's avatar
con committed
356
{
357
    m_d->displayCategory = v;
con's avatar
con committed
358 359
}

360 361 362 363 364 365 366 367 368
/*!
    \class Core::Internal::WizardEventLoop
    \brief Special event loop that runs a QWizard and terminates if the page changes.

    Use by Core::BaseFileWizard to intercept the change from the standard wizard pages
    to the extension pages (as the latter require the list of Core::GeneratedFile generated).

    Synopsis:
    \code
con's avatar
con committed
369 370 371 372 373
    Wizard wizard(parent);
    WizardEventLoop::WizardResult wr;
    do {
        wr = WizardEventLoop::execWizardPage(wizard);
    } while (wr == WizardEventLoop::PageChanged);
374 375 376 377
    \endcode

    \sa Core::GeneratedFile, Core::BaseFileWizardParameters, Core::BaseFileWizard, Core::StandardFileWizard
*/
con's avatar
con committed
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

class WizardEventLoop : public QEventLoop
{
    Q_OBJECT
    Q_DISABLE_COPY(WizardEventLoop)
    WizardEventLoop(QObject *parent);

public:
    enum WizardResult { Accepted, Rejected , PageChanged };

    static WizardResult execWizardPage(QWizard &w);

private slots:
    void pageChanged(int);
    void accepted();
    void rejected();

private:
    WizardResult execWizardPageI();

    WizardResult m_result;
};

WizardEventLoop::WizardEventLoop(QObject *parent) :
    QEventLoop(parent),
    m_result(Rejected)
{
}

WizardEventLoop::WizardResult WizardEventLoop::execWizardPage(QWizard &wizard)
{
    /* Install ourselves on the wizard. Main trick is here to connect
     * to the page changed signal and quit() on it. */
    WizardEventLoop *eventLoop = wizard.findChild<WizardEventLoop *>();
    if (!eventLoop) {
        eventLoop = new WizardEventLoop(&wizard);
        connect(&wizard, SIGNAL(currentIdChanged(int)), eventLoop, SLOT(pageChanged(int)));
        connect(&wizard, SIGNAL(accepted()), eventLoop, SLOT(accepted()));
        connect(&wizard, SIGNAL(rejected()), eventLoop, SLOT(rejected()));
        wizard.setAttribute(Qt::WA_ShowModal, true);
        wizard.show();
    }
    const WizardResult result = eventLoop->execWizardPageI();
    // Quitting?
    if (result != PageChanged)
        delete eventLoop;
    if (debugWizard)
        qDebug() << "WizardEventLoop::runWizard" << wizard.pageIds() << " returns " << result;

    return result;
}

WizardEventLoop::WizardResult WizardEventLoop::execWizardPageI()
{
    m_result = Rejected;
    exec(QEventLoop::DialogExec);
    return m_result;
}

void WizardEventLoop::pageChanged(int /*page*/)
{
    m_result = PageChanged;
    quit(); // !
}

void WizardEventLoop::accepted()
{
    m_result = Accepted;
    quit();
}

void WizardEventLoop::rejected()
{
    m_result = Rejected;
    quit();
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
/*!
    \class Core::BaseFileWizard
    \brief A generic wizard for creating files.

    The abstract methods:
    \list
    \o createWizardDialog(): Called to create the QWizard dialog to be shown
    \o generateFiles(): Generate file content
    \endlist

    must be implemented.
    The behaviour can be further customized by overwriting the virtual method \c postGenerateFiles(),
    which is called after generating the files.

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

hjk's avatar
hjk committed
473 474
struct BaseFileWizardPrivate
{
475 476 477
    explicit BaseFileWizardPrivate(const Core::BaseFileWizardParameters &parameters)
      : m_parameters(parameters), m_wizardDialog(0)
    {}
con's avatar
con committed
478 479 480 481 482 483 484 485 486

    const Core::BaseFileWizardParameters m_parameters;
    QWizard *m_wizardDialog;
};

// ---------------- Wizard
BaseFileWizard::BaseFileWizard(const BaseFileWizardParameters &parameters,
                       QObject *parent) :
    IWizard(parent),
487
    m_d(new BaseFileWizardPrivate(parameters))
con's avatar
con committed
488 489 490 491 492 493 494 495
{
}

BaseFileWizard::~BaseFileWizard()
{
    delete m_d;
}

496
IWizard::WizardKind  BaseFileWizard::kind() const
con's avatar
con committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510
{
    return m_d->m_parameters.kind();
}

QIcon BaseFileWizard::icon() const
{
    return m_d->m_parameters.icon();
}

QString BaseFileWizard::description() const
{
    return m_d->m_parameters.description();
}

511
QString BaseFileWizard::displayName() const
con's avatar
con committed
512
{
513
    return m_d->m_parameters.displayName();
con's avatar
con committed
514 515
}

Friedemann Kleint's avatar
Friedemann Kleint committed
516 517 518 519 520
QString BaseFileWizard::id() const
{
    return m_d->m_parameters.id();
}

con's avatar
con committed
521 522 523 524 525
QString BaseFileWizard::category() const
{
    return m_d->m_parameters.category();
}

526
QString BaseFileWizard::displayCategory() const
con's avatar
con committed
527
{
528
    return m_d->m_parameters.displayCategory();
con's avatar
con committed
529 530
}

531
void BaseFileWizard::runWizard(const QString &path, QWidget *parent)
con's avatar
con committed
532
{
533
    QTC_ASSERT(!path.isEmpty(), return);
534

con's avatar
con committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
    typedef  QList<IFileWizardExtension*> ExtensionList;

    QString errorMessage;
    // Compile extension pages, purge out unused ones
    ExtensionList extensions = ExtensionSystem::PluginManager::instance()->getObjects<IFileWizardExtension>();
    WizardPageList  allExtensionPages;
    for (ExtensionList::iterator it = extensions.begin(); it !=  extensions.end(); ) {
        const WizardPageList extensionPages = (*it)->extensionPages(this);
        if (extensionPages.empty()) {
            it = extensions.erase(it);
        } else {
            allExtensionPages += extensionPages;
            ++it;
        }
    }

    if (debugWizard)
        qDebug() << Q_FUNC_INFO <<  path << parent << "exs" <<  extensions.size() << allExtensionPages.size();

    QWizardPage *firstExtensionPage = 0;
    if (!allExtensionPages.empty())
        firstExtensionPage = allExtensionPages.front();

    // Create dialog and run it. Ensure that the dialog is deleted when
    // leaving the func, but not before the IFileWizardExtension::process
    // has been called
561
    const QScopedPointer<QWizard> wizard(createWizardDialog(parent, path, allExtensionPages));
562
    QTC_ASSERT(!wizard.isNull(), return);
563

con's avatar
con committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
    GeneratedFiles files;
    // Run the wizard: Call generate files on switching to the first extension
    // page is OR after 'Accepted' if there are no extension pages
    while (true) {
        const WizardEventLoop::WizardResult wr = WizardEventLoop::execWizardPage(*wizard);
        if (wr == WizardEventLoop::Rejected) {
            files.clear();
            break;
        }
        const bool accepted = wr == WizardEventLoop::Accepted;
        const bool firstExtensionPageHit = wr == WizardEventLoop::PageChanged
                                           && wizard->page(wizard->currentId()) == firstExtensionPage;
        const bool needGenerateFiles = firstExtensionPageHit || (accepted && allExtensionPages.empty());
        if (needGenerateFiles) {
            QString errorMessage;
            files = generateFiles(wizard.data(), &errorMessage);
            if (files.empty()) {
                QMessageBox::critical(0, tr("File Generation Failure"), errorMessage);
                break;
            }
        }
        if (firstExtensionPageHit)
hjk's avatar
hjk committed
586
            foreach (IFileWizardExtension *ex, extensions)
587
                ex->firstExtensionPageShown(files);
con's avatar
con committed
588 589 590 591
        if (accepted)
            break;
    }
    if (files.empty())
592
        return;
con's avatar
con committed
593
    // Compile result list and prompt for overwrite
594
    switch (promptOverwrite(&files, &errorMessage)) {
con's avatar
con committed
595
    case OverwriteCanceled:
596
        return;
con's avatar
con committed
597 598
    case OverwriteError:
        QMessageBox::critical(0, tr("Existing files"), errorMessage);
599
        return;
con's avatar
con committed
600 601 602 603
    case OverwriteOk:
        break;
    }
    // Write
604 605
    if (!writeFiles(files, &errorMessage)) {
        QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage);
606
        return;
con's avatar
con committed
607
    }
608

609
    bool removeOpenProjectAttribute = false;
con's avatar
con committed
610
    // Run the extensions
611 612
    foreach (IFileWizardExtension *ex, extensions) {
        bool remove;
613
        if (!ex->process(files, &remove, &errorMessage)) {
con's avatar
con committed
614
            QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage);
615
            return;
con's avatar
con committed
616
        }
617 618 619 620 621 622 623 624 625
        removeOpenProjectAttribute |= remove;
    }

    if (removeOpenProjectAttribute) {
        for (int i = 0; i < files.count(); i++) {
            if (files[i].attributes() & Core::GeneratedFile::OpenProjectAttribute)
                files[i].setAttributes(Core::GeneratedFile::OpenEditorAttribute);
        }
    }
con's avatar
con committed
626 627

    // Post generation handler
628
    if (!postGenerateFiles(wizard.data(), files, &errorMessage))
con's avatar
con committed
629 630 631
        QMessageBox::critical(0, tr("File Generation Failure"), errorMessage);
}

632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
/*!
    \fn virtual QWizard *Core::BaseFileWizard::createWizardDialog(QWidget *parent,
                                                                  const QString &defaultPath,
                                                                  const WizardPageList &extensionPages) const = 0
    \brief Implement to create the wizard dialog on the parent, adding the extension pages.
*/

/*!
    \fn virtual Core::GeneratedFiles Core::BaseFileWizard::generateFiles(const QWizard *w,
                                                                         QString *errorMessage) const = 0
     \brief Overwrite to query the parameters from the dialog and generate the files.

     Note: This does not generate physical files, but merely the list of Core::GeneratedFile.
*/

/*!
    \brief Physically write files.

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

653 654
bool BaseFileWizard::writeFiles(const GeneratedFiles &files, QString *errorMessage)
{
655 656
    const GeneratedFile::Attributes noWriteAttributes
        = GeneratedFile::CustomGeneratorAttribute|GeneratedFile::KeepExistingFileAttribute;
657
    foreach (const GeneratedFile &generatedFile, files)
658
        if (!(generatedFile.attributes() & noWriteAttributes ))
659 660 661 662 663
            if (!generatedFile.write(errorMessage))
                return false;
    return true;
}

664 665 666
/*!
    \brief Sets some standard options on a QWizard
*/
667

con's avatar
con committed
668 669
void BaseFileWizard::setupWizard(QWizard *w)
{
670 671
    w->setOption(QWizard::NoCancelButton, false);
    w->setOption(QWizard::NoDefaultButton, false);
672
    w->setOption(QWizard::NoBackButtonOnStartPage, true);
673
    w->setWindowFlags(w->windowFlags() & ~Qt::WindowContextHelpButtonHint);
con's avatar
con committed
674 675
}

676 677 678 679 680
/*!
    \brief Read "shortTitle" dynamic property of the pageId and apply it as the title
    of corresponding progress item
*/

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
void BaseFileWizard::applyExtensionPageShortTitle(Utils::Wizard *wizard, int pageId)
{
    if (pageId < 0)
        return;
    QWizardPage *p = wizard->page(pageId);
    if (!p)
        return;
    Utils::WizardProgressItem *item = wizard->wizardProgress()->item(pageId);
    if (!item)
        return;
    const QString shortTitle = p->property("shortTitle").toString();
    if (!shortTitle.isEmpty())
      item->setTitle(shortTitle);
}

696 697 698 699 700 701
/*!
    \brief Overwrite to perform steps to be done after files are actually created.

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

702 703 704 705 706
bool BaseFileWizard::postGenerateFiles(const QWizard *, const GeneratedFiles &l, QString *errorMessage)
{
    return BaseFileWizard::postGenerateOpenEditors(l, errorMessage);
}

707 708 709 710
/*!
    \brief Utility to open the editors for the files whose attribute is set accordingly.
*/

711
bool BaseFileWizard::postGenerateOpenEditors(const GeneratedFiles &l, QString *errorMessage)
con's avatar
con committed
712
{
713
    Core::EditorManager *em = Core::EditorManager::instance();
714 715
    foreach(const Core::GeneratedFile &file, l) {
        if (file.attributes() & Core::GeneratedFile::OpenEditorAttribute) {
716
            if (!em->openEditor(file.path(), file.editorId(), Core::EditorManager::ModeSwitch )) {
717
                if (errorMessage)
718
                    *errorMessage = tr("Failed to open an editor for '%1'.").arg(QDir::toNativeSeparators(file.path()));
719 720
                return false;
            }
con's avatar
con committed
721 722 723 724 725
        }
    }
    return true;
}

726 727 728 729 730
/*!
    \brief Utility that performs an overwrite check on a set of files. It checks if
    the file exists, can be overwritten at all and prompts the user with a summary.
*/

731
BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(GeneratedFiles *files,
con's avatar
con committed
732 733 734
                                                                QString *errorMessage) const
{
    if (debugWizard)
735
        qDebug() << Q_FUNC_INFO << files;
con's avatar
con committed
736

737
    QStringList existingFiles;
con's avatar
con committed
738 739 740
    bool oddStuffFound = false;

    static const QString readOnlyMsg = tr(" [read only]");
741
    static const QString directoryMsg = tr(" [folder]");
con's avatar
con committed
742 743
    static const QString symLinkMsg = tr(" [symbolic link]");

744 745
    foreach (const GeneratedFile &file, *files) {
        const QFileInfo fi(file.path());
746
        if (fi.exists())
747
            existingFiles.append(file.path());
748
    }
749 750 751 752 753
    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]").
754
    const QString commonExistingPath = Utils::commonPath(existingFiles);
con's avatar
con committed
755
    QString fileNamesMsgPart;
756
    foreach (const QString &fileName, existingFiles) {
con's avatar
con committed
757 758 759 760
        const QFileInfo fi(fileName);
        if (fi.exists()) {
            if (!fileNamesMsgPart.isEmpty())
                fileNamesMsgPart += QLatin1String(", ");
761
            fileNamesMsgPart += QDir::toNativeSeparators(fileName.mid(commonExistingPath.size() + 1));
con's avatar
con committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
            do {
                if (fi.isDir()) {
                    oddStuffFound = true;
                    fileNamesMsgPart += directoryMsg;
                    break;
                }
                if (fi.isSymLink()) {
                    oddStuffFound = true;
                    fileNamesMsgPart += symLinkMsg;
                    break;
            }
                if (!fi.isWritable()) {
                    oddStuffFound = true;
                    fileNamesMsgPart += readOnlyMsg;
                }
            } while (false);
        }
    }

    if (oddStuffFound) {
782 783
        *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
784 785
        return OverwriteError;
    }
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
    // Prompt to overwrite existing files.
    Internal::PromptOverwriteDialog overwriteDialog;
    // 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);
        QTC_ASSERT(i != -1, return OverwriteCanceled; )
        GeneratedFile &file = (*files)[i];
        file.setAttributes(file.attributes() | GeneratedFile::KeepExistingFileAttribute);
    }
    return OverwriteOk;
con's avatar
con committed
806 807
}

808 809 810 811
/*!
    \brief Build a file name, adding the extension unless baseName already has one.
*/

con's avatar
con committed
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
QString BaseFileWizard::buildFileName(const QString &path,
                                      const QString &baseName,
                                      const QString &extension)
{
    QString rc = path;
    if (!rc.isEmpty() && !rc.endsWith(QDir::separator()))
        rc += QDir::separator();
    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;
}

832 833 834 835
/*!
    \brief Utility that returns the preferred suffix for a mime type
*/

836
QString BaseFileWizard::preferredSuffix(const QString &mimeType)
con's avatar
con committed
837
{
838
    const QString rc = Core::ICore::instance()->mimeDatabase()->preferredSuffixByType(mimeType);
con's avatar
con committed
839 840 841 842 843 844
    if (rc.isEmpty())
        qWarning("%s: WARNING: Unable to find a preferred suffix for %s.",
                 Q_FUNC_INFO, mimeType.toUtf8().constData());
    return rc;
}

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
/*!
    \class Core::StandardFileWizard
    \brief Convenience class for creating one file.

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

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

/*!
    \fn Core::GeneratedFiles Core::StandardFileWizard::generateFilesFromPath(const QString &path,
                                                                             const QString &name,
                                                                             QString *errorMessage) const = 0
    \brief Newly introduced virtual that creates the files under the path.
*/
con's avatar
con committed
862 863 864

StandardFileWizard::StandardFileWizard(const BaseFileWizardParameters &parameters,
                                       QObject *parent) :
865
    BaseFileWizard(parameters, parent)
con's avatar
con committed
866 867 868
{
}

869 870 871 872
/*!
    \brief Implemented to create a Utils::FileWizardDialog.
*/

con's avatar
con committed
873 874 875 876
QWizard *StandardFileWizard::createWizardDialog(QWidget *parent,
                                                const QString &defaultPath,
                                                const WizardPageList &extensionPages) const
{
877
    Utils::FileWizardDialog *standardWizardDialog = new Utils::FileWizardDialog(parent);
878
    standardWizardDialog->setWindowTitle(tr("New %1").arg(displayName()));
con's avatar
con committed
879 880 881
    setupWizard(standardWizardDialog);
    standardWizardDialog->setPath(defaultPath);
    foreach (QWizardPage *p, extensionPages)
882
        BaseFileWizard::applyExtensionPageShortTitle(standardWizardDialog, standardWizardDialog->addPage(p));
con's avatar
con committed
883 884 885
    return standardWizardDialog;
}

886 887 888 889
/*!
    \brief Implemented to retrieve path and name and call generateFilesFromPath()
*/

con's avatar
con committed
890 891 892
GeneratedFiles StandardFileWizard::generateFiles(const QWizard *w,
                                                 QString *errorMessage) const
{
893
    const Utils::FileWizardDialog *standardWizardDialog = qobject_cast<const Utils::FileWizardDialog *>(w);
con's avatar
con committed
894
    return generateFilesFromPath(standardWizardDialog->path(),
895
                                 standardWizardDialog->fileName(),
con's avatar
con committed
896 897 898 899
                                 errorMessage);
}

} // namespace Core
hjk's avatar
hjk committed
900

con's avatar
con committed
901
#include "basefilewizard.moc"