<commit_before>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** 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 The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/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 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.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "gettingstartedwelcomepage.h"

#include "exampleslistmodel.h"
#include "screenshotcropper.h"

#include <utils/pathchooser.h>
#include <utils/winutils.h>

#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
#include <coreplugin/modemanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>

#include <QMutex>
#include <QThread>
#include <QMutexLocker>
#include <QPointer>
#include <QWaitCondition>
#include <QDir>
#include <QBuffer>
#include <QImage>
#include <QImageReader>
#include <QGridLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QMessageBox>
#include <QApplication>
#include <QQuickImageProvider>
#include <QQmlEngine>
#include <QQmlContext>
#include <QDesktopServices>

using namespace Utils;

namespace QtSupport {
namespace Internal {

class ExampleDialog : public QDialog
{
    Q_OBJECT
 public:
    enum ResultCode { Copy = QDialog::Accepted + 1, Keep };
    ExampleDialog(QWidget *parent = 0) : QDialog(parent) {};
 private slots:
    void handleCopyClicked() { done(Copy); };
    void handleKeepClicked() { done(Keep); };
};

const char C_FALLBACK_ROOT[] = "ProjectsFallbackRoot";

QPointer<ExamplesListModel> &examplesModelStatic()
{
    static QPointer<ExamplesListModel> s_examplesModel;
    return s_examplesModel;
}

class Fetcher : public QObject
{
    Q_OBJECT

public:
    Fetcher() : QObject(),  m_shutdown(false)
    {
        connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(shutdown()));
    }

    void wait()
    {
        if (QThread::currentThread() == QApplication::instance()->thread())
            return;
        if (m_shutdown)
            return;

        m_waitcondition.wait(&m_mutex, 4000);
    }

    QByteArray data()
    {
        QMutexLocker lock(&m_dataMutex);
        return m_fetchedData;
    }

    void clearData()
    {
        QMutexLocker lock(&m_dataMutex);
        m_fetchedData.clear();
    }

    bool asynchronousFetchData(const QUrl &url)
    {
        QMutexLocker lock(&m_mutex);

        if (!QMetaObject::invokeMethod(this,
                                       "fetchData",
                                       Qt::AutoConnection,
                                       Q_ARG(QUrl, url))) {
            return false;
        }

        wait();
        return true;
    }

public slots:
    void fetchData(const QUrl &url)
    {
        if (m_shutdown)
            return;

        QMutexLocker lock(&m_mutex);

        if (Core::HelpManager::instance()) {
            QMutexLocker dataLock(&m_dataMutex);
            m_fetchedData = Core::HelpManager::fileData(url);
        }
        m_waitcondition.wakeAll();
    }

private slots:
    void shutdown()
    {
        m_shutdown = true;
    }

public:
    QByteArray m_fetchedData;
    QWaitCondition m_waitcondition;
    QMutex m_mutex;     //This mutex synchronises the wait() and wakeAll() on the wait condition.
                        //We have to ensure that wakeAll() is called always after wait().

    QMutex m_dataMutex; //This mutex synchronises the access of m_fectedData.
                        //If the wait condition timeouts we otherwise get a race condition.
    bool m_shutdown;
};

class HelpImageProvider : public QQuickImageProvider
{
public:
    HelpImageProvider()
        : QQuickImageProvider(QQuickImageProvider::Image)
    {
    }

    // gets called by declarative in separate thread
    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
    {
        QMutexLocker lock(&m_mutex);

        QUrl url = QUrl::fromEncoded(id.toLatin1());

        if (!m_fetcher.asynchronousFetchData(url) || m_fetcher.data().isEmpty()) {
            if (size) {
                size->setWidth(0);
                size->setHeight(0);
            }
            return QImage();
        }

        QByteArray data = m_fetcher.data();
        QBuffer imgBuffer(&data);
        imgBuffer.open(QIODevice::ReadOnly);
        QImageReader reader(&imgBuffer);
        QImage img = reader.read();

        m_fetcher.clearData();
        img = ScreenshotCropper::croppedImage(img, id, requestedSize);
        if (size)
            *size = img.size();
        return img;

    }
private:
    Fetcher m_fetcher;
    QMutex m_mutex;
};

ExamplesWelcomePage::ExamplesWelcomePage()
    : m_engine(0),  m_showExamples(false)
{
}

void ExamplesWelcomePage::setShowExamples(bool showExamples)
{
    m_showExamples = showExamples;
}

QString ExamplesWelcomePage::title() const
{
    if (m_showExamples)
        return tr("Examples");
    else
        return tr("Tutorials");
}

 int ExamplesWelcomePage::priority() const
 {
     if (m_showExamples)
         return 30;
     else
         return 40;
 }

 bool ExamplesWelcomePage::hasSearchBar() const
 {
     if (m_showExamples)
         return true;
     else
         return false;
 }

QUrl ExamplesWelcomePage::pageLocation() const
{
    // normalize paths so QML doesn't freak out if it's wrongly capitalized on Windows
    const QString resourcePath = Utils::FileUtils::normalizePathName(Core::ICore::resourcePath());
    if (m_showExamples)
        return QUrl::fromLocalFile(resourcePath + QLatin1String("/welcomescreen/examples.qml"));
    else
        return QUrl::fromLocalFile(resourcePath + QLatin1String("/welcomescreen/tutorials.qml"));
}

void ExamplesWelcomePage::facilitateQml(QQmlEngine *engine)
{
    m_engine = engine;
    m_engine->addImageProvider(QLatin1String("helpimage"), new HelpImageProvider);
    ExamplesListModelFilter *proxy = new ExamplesListModelFilter(examplesModel(), this);

    proxy->setDynamicSortFilter(true);
    proxy->sort(0);
    proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);

    QQmlContext *rootContenxt = m_engine->rootContext();
    if (m_showExamples) {
        proxy->setShowTutorialsOnly(false);
        rootContenxt->setContextProperty(QLatin1String("examplesModel"), proxy);
        rootContenxt->setContextProperty(QLatin1String("exampleSetModel"), proxy->exampleSetModel());
    } else {
        rootContenxt->setContextProperty(QLatin1String("tutorialsModel"), proxy);
    }
    rootContenxt->setContextProperty(QLatin1String("gettingStarted"), this);
}

Core::Id ExamplesWelcomePage::id() const
{
    return m_showExamples ? "Examples" : "Tutorials";
}

void ExamplesWelcomePage::openHelpInExtraWindow(const QUrl &help)
{
    Core::HelpManager::handleHelpRequest(help, Core::HelpManager::ExternalHelpAlways);
}

void ExamplesWelcomePage::openHelp(const QUrl &help)
{
    Core::HelpManager::handleHelpRequest(help, Core::HelpManager::HelpModeAlways);
}

void ExamplesWelcomePage::openUrl(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileInfo, QStringList &filesToOpen, const QStringList& dependencies)
{
    const QString projectDir = proFileInfo.canonicalPath();
    ExampleDialog d(Core::ICore::mainWindow());
    QGridLayout *lay = new QGridLayout(&d);
    QLabel *descrLbl = new QLabel;
    d.setWindowTitle(tr("Copy Project to writable Location?"));
    descrLbl->setTextFormat(Qt::RichText);
    descrLbl->setWordWrap(false);
    const QString nativeProjectDir = QDir::toNativeSeparators(projectDir);
    descrLbl->setText(QString::fromLatin1("
%1
").arg(nativeProjectDir));
    descrLbl->setMinimumWidth(descrLbl->sizeHint().width());
    descrLbl->setWordWrap(true);
    descrLbl->setText(tr("
The project you are about to open is located in the "
                         "write-protected location:

%1
"
                         "
Please select a writable location below and click \"Copy Project and Open\" "
                         "to open a modifiable copy of the project or click \"Keep Project and Open\" "
                         "to open the project in location.

Note: You will not "
                         "be able to alter or compile your project in the current location.
")
                      .arg(nativeProjectDir));
    lay->addWidget(descrLbl, 0, 0, 1, 2);
    QLabel *txt = new QLabel(tr("&Location:"));
    PathChooser *chooser = new PathChooser;
    txt->setBuddy(chooser);
    chooser->setExpectedKind(PathChooser::ExistingDirectory);
    chooser->setHistoryCompleter(QLatin1String("Qt.WritableExamplesDir.History"));
    QSettings *settings = Core::ICore::settings();
    chooser->setPath(settings->value(QString::fromLatin1(C_FALLBACK_ROOT),
                                     Core::DocumentManager::projectsDirectory()).toString());
    lay->addWidget(txt, 1, 0);
    lay->addWidget(chooser, 1, 1);
    QDialogButtonBox *bb = new QDialogButtonBox;
    QPushButton *copyBtn = bb->addButton(tr("&Copy Project and Open"), QDialogButtonBox::AcceptRole);
    connect(copyBtn, SIGNAL(released()), &d, SLOT(handleCopyClicked()));
    copyBtn->setDefault(true);
    QPushButton *keepBtn = bb->addButton(tr("&Keep Project and Open"), QDialogButtonBox::RejectRole);
    connect(keepBtn, SIGNAL(released()), &d, SLOT(handleKeepClicked()));
    lay->addWidget(bb, 2, 0, 1, 2);
    connect(chooser, SIGNAL(validChanged(bool)), copyBtn, SLOT(setEnabled(bool)));
    int code = d.exec();
    if (code == ExampleDialog::Copy) {
        QString exampleDirName = proFileInfo.dir().dirName();
        QString destBaseDir = chooser->path();
        settings->setValue(QString::fromLatin1(C_FALLBACK_ROOT), destBaseDir);
        QDir toDirWithExamplesDir(destBaseDir);
        if (toDirWithExamplesDir.cd(exampleDirName)) {
            toDirWithExamplesDir.cdUp(); // step out, just to not be in the way
            QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Use Location"),
                                 tr("The specified location already exists. "
                                    "Please specify a valid location."),
                                 QMessageBox::Ok, QMessageBox::NoButton);
            return QString();
        } else {
            QString error;
            QString targetDir = destBaseDir + QLatin1Char('/') + exampleDirName;
            if (FileUtils::copyRecursively(FileName::fromString(projectDir),
                    FileName::fromString(targetDir), &error)) {
                // set vars to new location
                const QStringList::Iterator end = filesToOpen.end();
                for (QStringList::Iterator it = filesToOpen.begin(); it != end; ++it)
                    it->replace(projectDir, targetDir);

                foreach (const QString &dependency, dependencies) {
                    FileName targetFile = FileName::fromString(targetDir);
                    targetFile.appendPath(QDir(dependency).dirName());
                    if (!FileUtils::copyRecursively(FileName::fromString(dependency), targetFile,
                            &error)) {
                        QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Copy Project"), error);
                        // do not fail, just warn;
                    }
                }

                return targetDir + QLatin1Char('/') + proFileInfo.fileName();
            } else {
                QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Copy Project"), error);
            }

        }
    }
    if (code == ExampleDialog::Keep)
        return proFileInfo.absoluteFilePath();
    return QString();

}

void ExamplesWelcomePage::openProject(const QString &projectFile,
                                      const QStringList &additionalFilesToOpen,
                                      const QString &mainFile,
                                      const QUrl &help,
                                      const QStringList &dependencies,
                                      const QStringList &)
{
    QString proFile = projectFile;
    if (proFile.isEmpty())
        return;

    QStringList filesToOpen = additionalFilesToOpen;
    if (!mainFile.isEmpty()) {
        // ensure that the main file is opened on top (i.e. opened last)
        filesToOpen.removeAll(mainFile);
        filesToOpen.append(mainFile);
    }

    QFileInfo proFileInfo(proFile);
    if (!proFileInfo.exists())
        return;

    // If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail
    if (!proFileInfo.isWritable())
        proFile = copyToAlternativeLocation(proFileInfo, filesToOpen, dependencies);

    // don't try to load help and files if loading the help request is being cancelled
    QString errorMessage;
    if (proFile.isEmpty())
        return;
    if (ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile, &errorMessage)) {
        Core::ICore::openFiles(filesToOpen);
        Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
        if (help.isValid())
            openHelpInExtraWindow(help.toString());
        Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
    }
    if (!errorMessage.isEmpty())
        QMessageBox::critical(Core::ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
}

ExamplesListModel *ExamplesWelcomePage::examplesModel() const
{
    if (examplesModelStatic())
        return examplesModelStatic().data();

    examplesModelStatic() = new ExamplesListModel(const_cast<ExamplesWelcomePage*>(this));
    return examplesModelStatic().data();
}

} // namespace Internal
} // namespace QtSupport

#include "gettingstartedwelcomepage.moc"
<commit_msg>Examples: Better check for non-writable location<commit_after>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** 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 The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/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 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.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "gettingstartedwelcomepage.h"

#include "exampleslistmodel.h"
#include "screenshotcropper.h"

#include <utils/pathchooser.h>
#include <utils/winutils.h>

#include <coreplugin/coreconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
#include <coreplugin/modemanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>

#include <QMutex>
#include <QThread>
#include <QMutexLocker>
#include <QPointer>
#include <QWaitCondition>
#include <QDir>
#include <QBuffer>
#include <QImage>
#include <QImageReader>
#include <QGridLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QMessageBox>
#include <QApplication>
#include <QQuickImageProvider>
#include <QQmlEngine>
#include <QQmlContext>
#include <QDesktopServices>

using namespace Utils;

namespace QtSupport {
namespace Internal {

class ExampleDialog : public QDialog
{
    Q_OBJECT
 public:
    enum ResultCode { Copy = QDialog::Accepted + 1, Keep };
    ExampleDialog(QWidget *parent = 0) : QDialog(parent) {};
 private slots:
    void handleCopyClicked() { done(Copy); };
    void handleKeepClicked() { done(Keep); };
};

const char C_FALLBACK_ROOT[] = "ProjectsFallbackRoot";

QPointer<ExamplesListModel> &examplesModelStatic()
{
    static QPointer<ExamplesListModel> s_examplesModel;
    return s_examplesModel;
}

class Fetcher : public QObject
{
    Q_OBJECT

public:
    Fetcher() : QObject(),  m_shutdown(false)
    {
        connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(shutdown()));
    }

    void wait()
    {
        if (QThread::currentThread() == QApplication::instance()->thread())
            return;
        if (m_shutdown)
            return;

        m_waitcondition.wait(&m_mutex, 4000);
    }

    QByteArray data()
    {
        QMutexLocker lock(&m_dataMutex);
        return m_fetchedData;
    }

    void clearData()
    {
        QMutexLocker lock(&m_dataMutex);
        m_fetchedData.clear();
    }

    bool asynchronousFetchData(const QUrl &url)
    {
        QMutexLocker lock(&m_mutex);

        if (!QMetaObject::invokeMethod(this,
                                       "fetchData",
                                       Qt::AutoConnection,
                                       Q_ARG(QUrl, url))) {
            return false;
        }

        wait();
        return true;
    }

public slots:
    void fetchData(const QUrl &url)
    {
        if (m_shutdown)
            return;

        QMutexLocker lock(&m_mutex);

        if (Core::HelpManager::instance()) {
            QMutexLocker dataLock(&m_dataMutex);
            m_fetchedData = Core::HelpManager::fileData(url);
        }
        m_waitcondition.wakeAll();
    }

private slots:
    void shutdown()
    {
        m_shutdown = true;
    }

public:
    QByteArray m_fetchedData;
    QWaitCondition m_waitcondition;
    QMutex m_mutex;     //This mutex synchronises the wait() and wakeAll() on the wait condition.
                        //We have to ensure that wakeAll() is called always after wait().

    QMutex m_dataMutex; //This mutex synchronises the access of m_fectedData.
                        //If the wait condition timeouts we otherwise get a race condition.
    bool m_shutdown;
};

class HelpImageProvider : public QQuickImageProvider
{
public:
    HelpImageProvider()
        : QQuickImageProvider(QQuickImageProvider::Image)
    {
    }

    // gets called by declarative in separate thread
    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
    {
        QMutexLocker lock(&m_mutex);

        QUrl url = QUrl::fromEncoded(id.toLatin1());

        if (!m_fetcher.asynchronousFetchData(url) || m_fetcher.data().isEmpty()) {
            if (size) {
                size->setWidth(0);
                size->setHeight(0);
            }
            return QImage();
        }

        QByteArray data = m_fetcher.data();
        QBuffer imgBuffer(&data);
        imgBuffer.open(QIODevice::ReadOnly);
        QImageReader reader(&imgBuffer);
        QImage img = reader.read();

        m_fetcher.clearData();
        img = ScreenshotCropper::croppedImage(img, id, requestedSize);
        if (size)
            *size = img.size();
        return img;

    }
private:
    Fetcher m_fetcher;
    QMutex m_mutex;
};

ExamplesWelcomePage::ExamplesWelcomePage()
    : m_engine(0),  m_showExamples(false)
{
}

void ExamplesWelcomePage::setShowExamples(bool showExamples)
{
    m_showExamples = showExamples;
}

QString ExamplesWelcomePage::title() const
{
    if (m_showExamples)
        return tr("Examples");
    else
        return tr("Tutorials");
}

 int ExamplesWelcomePage::priority() const
 {
     if (m_showExamples)
         return 30;
     else
         return 40;
 }

 bool ExamplesWelcomePage::hasSearchBar() const
 {
     if (m_showExamples)
         return true;
     else
         return false;
 }

QUrl ExamplesWelcomePage::pageLocation() const
{
    // normalize paths so QML doesn't freak out if it's wrongly capitalized on Windows
    const QString resourcePath = Utils::FileUtils::normalizePathName(Core::ICore::resourcePath());
    if (m_showExamples)
        return QUrl::fromLocalFile(resourcePath + QLatin1String("/welcomescreen/examples.qml"));
    else
        return QUrl::fromLocalFile(resourcePath + QLatin1String("/welcomescreen/tutorials.qml"));
}

void ExamplesWelcomePage::facilitateQml(QQmlEngine *engine)
{
    m_engine = engine;
    m_engine->addImageProvider(QLatin1String("helpimage"), new HelpImageProvider);
    ExamplesListModelFilter *proxy = new ExamplesListModelFilter(examplesModel(), this);

    proxy->setDynamicSortFilter(true);
    proxy->sort(0);
    proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);

    QQmlContext *rootContenxt = m_engine->rootContext();
    if (m_showExamples) {
        proxy->setShowTutorialsOnly(false);
        rootContenxt->setContextProperty(QLatin1String("examplesModel"), proxy);
        rootContenxt->setContextProperty(QLatin1String("exampleSetModel"), proxy->exampleSetModel());
    } else {
        rootContenxt->setContextProperty(QLatin1String("tutorialsModel"), proxy);
    }
    rootContenxt->setContextProperty(QLatin1String("gettingStarted"), this);
}

Core::Id ExamplesWelcomePage::id() const
{
    return m_showExamples ? "Examples" : "Tutorials";
}

void ExamplesWelcomePage::openHelpInExtraWindow(const QUrl &help)
{
    Core::HelpManager::handleHelpRequest(help, Core::HelpManager::ExternalHelpAlways);
}

void ExamplesWelcomePage::openHelp(const QUrl &help)
{
    Core::HelpManager::handleHelpRequest(help, Core::HelpManager::HelpModeAlways);
}

void ExamplesWelcomePage::openUrl(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileInfo, QStringList &filesToOpen, const QStringList& dependencies)
{
    const QString projectDir = proFileInfo.canonicalPath();
    ExampleDialog d(Core::ICore::mainWindow());
    QGridLayout *lay = new QGridLayout(&d);
    QLabel *descrLbl = new QLabel;
    d.setWindowTitle(tr("Copy Project to writable Location?"));
    descrLbl->setTextFormat(Qt::RichText);
    descrLbl->setWordWrap(false);
    const QString nativeProjectDir = QDir::toNativeSeparators(projectDir);
    descrLbl->setText(QString::fromLatin1("
%1
").arg(nativeProjectDir));
    descrLbl->setMinimumWidth(descrLbl->sizeHint().width());
    descrLbl->setWordWrap(true);
    descrLbl->setText(tr("
The project you are about to open is located in the "
                         "write-protected location:

%1
"
                         "
Please select a writable location below and click \"Copy Project and Open\" "
                         "to open a modifiable copy of the project or click \"Keep Project and Open\" "
                         "to open the project in location.

Note: You will not "
                         "be able to alter or compile your project in the current location.
")
                      .arg(nativeProjectDir));
    lay->addWidget(descrLbl, 0, 0, 1, 2);
    QLabel *txt = new QLabel(tr("&Location:"));
    PathChooser *chooser = new PathChooser;
    txt->setBuddy(chooser);
    chooser->setExpectedKind(PathChooser::ExistingDirectory);
    chooser->setHistoryCompleter(QLatin1String("Qt.WritableExamplesDir.History"));
    QSettings *settings = Core::ICore::settings();
    chooser->setPath(settings->value(QString::fromLatin1(C_FALLBACK_ROOT),
                                     Core::DocumentManager::projectsDirectory()).toString());
    lay->addWidget(txt, 1, 0);
    lay->addWidget(chooser, 1, 1);
    QDialogButtonBox *bb = new QDialogButtonBox;
    QPushButton *copyBtn = bb->addButton(tr("&Copy Project and Open"), QDialogButtonBox::AcceptRole);
    connect(copyBtn, SIGNAL(released()), &d, SLOT(handleCopyClicked()));
    copyBtn->setDefault(true);
    QPushButton *keepBtn = bb->addButton(tr("&Keep Project and Open"), QDialogButtonBox::RejectRole);
    connect(keepBtn, SIGNAL(released()), &d, SLOT(handleKeepClicked()));
    lay->addWidget(bb, 2, 0, 1, 2);
    connect(chooser, SIGNAL(validChanged(bool)), copyBtn, SLOT(setEnabled(bool)));
    int code = d.exec();
    if (code == ExampleDialog::Copy) {
        QString exampleDirName = proFileInfo.dir().dirName();
        QString destBaseDir = chooser->path();
        settings->setValue(QString::fromLatin1(C_FALLBACK_ROOT), destBaseDir);
        QDir toDirWithExamplesDir(destBaseDir);
        if (toDirWithExamplesDir.cd(exampleDirName)) {
            toDirWithExamplesDir.cdUp(); // step out, just to not be in the way
            QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Use Location"),
                                 tr("The specified location already exists. "
                                    "Please specify a valid location."),
                                 QMessageBox::Ok, QMessageBox::NoButton);
            return QString();
        } else {
            QString error;
            QString targetDir = destBaseDir + QLatin1Char('/') + exampleDirName;
            if (FileUtils::copyRecursively(FileName::fromString(projectDir),
                    FileName::fromString(targetDir), &error)) {
                // set vars to new location
                const QStringList::Iterator end = filesToOpen.end();
                for (QStringList::Iterator it = filesToOpen.begin(); it != end; ++it)
                    it->replace(projectDir, targetDir);

                foreach (const QString &dependency, dependencies) {
                    FileName targetFile = FileName::fromString(targetDir);
                    targetFile.appendPath(QDir(dependency).dirName());
                    if (!FileUtils::copyRecursively(FileName::fromString(dependency), targetFile,
                            &error)) {
                        QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Copy Project"), error);
                        // do not fail, just warn;
                    }
                }

                return targetDir + QLatin1Char('/') + proFileInfo.fileName();
            } else {
                QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Copy Project"), error);
            }

        }
    }
    if (code == ExampleDialog::Keep)
        return proFileInfo.absoluteFilePath();
    return QString();

}

void ExamplesWelcomePage::openProject(const QString &projectFile,
                                      const QStringList &additionalFilesToOpen,
                                      const QString &mainFile,
                                      const QUrl &help,
                                      const QStringList &dependencies,
                                      const QStringList &)
{
    QString proFile = projectFile;
    if (proFile.isEmpty())
        return;

    QStringList filesToOpen = additionalFilesToOpen;
    if (!mainFile.isEmpty()) {
        // ensure that the main file is opened on top (i.e. opened last)
        filesToOpen.removeAll(mainFile);
        filesToOpen.append(mainFile);
    }

    QFileInfo proFileInfo(proFile);
    if (!proFileInfo.exists())
        return;

    QFileInfo pathInfo(proFileInfo.path());
    // If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail
    if (!proFileInfo.isWritable()
            || !pathInfo.isWritable() /* path of .pro file */
            || !QFileInfo(pathInfo.path()).isWritable() /* shadow build directory */) {
        proFile = copyToAlternativeLocation(proFileInfo, filesToOpen, dependencies);
    }

    // don't try to load help and files if loading the help request is being cancelled
    QString errorMessage;
    if (proFile.isEmpty())
        return;
    if (ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFile, &errorMessage)) {
        Core::ICore::openFiles(filesToOpen);
        Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
        if (help.isValid())
            openHelpInExtraWindow(help.toString());
        Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION);
    }
    if (!errorMessage.isEmpty())
        QMessageBox::critical(Core::ICore::mainWindow(), tr("Failed to Open Project"), errorMessage);
}

ExamplesListModel *ExamplesWelcomePage::examplesModel() const
{
    if (examplesModelStatic())
        return examplesModelStatic().data();

    examplesModelStatic() = new ExamplesListModel(const_cast<ExamplesWelcomePage*>(this));
    return examplesModelStatic().data();
}

} // namespace Internal
} // namespace QtSupport

#include "gettingstartedwelcomepage.moc"
<|endoftext|>


<commit_before>/*
   Copyright 2014 John Bailey

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <sstream>
#include <iostream>

#include "MetricMatcher.hpp"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/AST/ASTContext.h"

using namespace clang;
using namespace std;

//#define DEBUG_FN_TRACE_OUTOUT 1

MetricVisitor::MetricVisitor(clang::ASTContext* p_Context, MetricUnit* p_topUnit, Options* p_options) : astContext(p_Context), m_topUnit( p_topUnit ), m_currentUnit( NULL ), m_options( p_options )
{
}

MetricVisitor::~MetricVisitor(void)
{
}

bool MetricVisitor::ShouldIncludeFile( const std::string& p_fn ) const
{
	bool ret_val = true;
	if (( m_options != NULL ) &&
		( m_options->ExcludeFiles != NULL ))
	{
		for( std::vector<std::string>::const_iterator it = m_options->ExcludeFiles->begin();
			 it != m_options->ExcludeFiles->end();
			 it++ )
		{
			if( p_fn.find( *it ) !=  string::npos )
			{
				ret_val = false;
				break;
			}
		}
	}
	return ret_val;
}

bool MetricVisitor::ShouldIncludeFunction( const std::string& p_fn ) const
{
	return (( m_options == NULL ) ||
			( m_options->ExcludeFunctions == NULL ) ||
			( std::find( m_options->ExcludeFunctions->begin(), m_options->ExcludeFunctions->end(), p_fn ) == m_options->ExcludeFunctions->end() ));
}

bool MetricVisitor::VisitFunctionDecl(clang::FunctionDecl *func) {

#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitFunctionDecl" << std::endl;
#endif
	if( func->doesThisDeclarationHaveABody() )
	{
		m_currentFileName = astContext->getSourceManager().getFilename( func->getLocation() ).str();
		m_currentFunctionName = func->getQualifiedNameAsString();

#if defined( DEBUG_FN_TRACE_OUTOUT )
		std::cout << "VisitFunctionDecl - " << m_currentFileName << "::"<< m_currentFunctionName << std::endl;
#endif

		if( ShouldIncludeFile( m_currentFileName ) && ShouldIncludeFunction( m_currentFunctionName ))
		{
#if defined( DEBUG_FN_TRACE_OUTOUT )
			std::cout << "VisitFunctionDecl - Should be included" << std::endl;
#endif
			MetricUnit* fileUnit = m_topUnit->getSubUnit(m_currentFileName, METRIC_UNIT_FILE);

			MetricUnitType_e type = METRIC_UNIT_FUNCTION;

			if( func->isCXXClassMember() )
			{
				type = METRIC_UNIT_METHOD;
			}

			m_currentUnit = fileUnit->getSubUnit(m_currentFunctionName, type);

			if( func->getLinkageAndVisibility().getLinkage() == InternalLinkage )
			{
				fileUnit->increment( METRIC_TYPE_LOCAL_FUNCTIONS );
			}
			fileUnit->increment( METRIC_TYPE_FUNCTIONS );
		}
		else
		{
			m_currentUnit = NULL;
		}
	}
	return true;
}

bool MetricVisitor::VisitVarDecl(clang::VarDecl *p_varDec) {

#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitVarDecl : CONTEXT " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif

	/* Check it's not something like a function parameter */
	if( p_varDec->getKind() == clang::Decl::Var )
	{
		/* Check to see if this variable does not have a parent function/method */
		if( !p_varDec->getParentFunctionOrMethod() )
		{
			/* Don't count external references */
			if ( !p_varDec->hasExternalStorage() )
			{
#if defined( DEBUG_FN_TRACE_OUTOUT )
				std::cout << "VisitVarDecl : Processing " << p_varDec->getNameAsString() << std::endl;
#endif

				SourceLocation loc = p_varDec->getLocation();
				if( loc.isMacroID() )
				{
					loc = astContext->getSourceManager().getFileLoc(loc);
				}

				/* File-scope variable */
				m_currentFileName = astContext->getSourceManager().getFilename( loc ).str();
				m_currentFunctionName = "";
				if( ShouldIncludeFile( m_currentFileName ))
				{
					m_currentUnit = m_topUnit->getSubUnit(m_currentFileName, METRIC_UNIT_FILE);
				}
				else
				{
					m_currentUnit = NULL;
				}
			}
		}

		if( m_currentUnit )
		{
			m_currentUnit->increment( METRIC_TYPE_VARIABLE );
		}
	}
	return true;
}

// TODO: Lots of repetition here - template it?
bool MetricVisitor::VisitForStmt(clang::ForStmt *p_forSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_FORLOOP );
	}
    return true;
}

bool MetricVisitor::VisitGotoStmt(clang::GotoStmt *p_gotoSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_GOTO );
	}

    return true;
}

bool MetricVisitor::VisitLabelStmt(clang::LabelStmt *p_LabelSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_LABEL );
	}
    return true;
}

bool MetricVisitor::VisitWhileStmt(clang::WhileStmt *p_whileSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_WHILELOOP );
	}
    return true;
}

bool MetricVisitor::VisitReturnStmt(clang::ReturnStmt *p_returnSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_RETURN );

		if( !isLastExecutableStmtInFn( p_returnSt, astContext ) )
		{
			m_currentUnit->increment( METRIC_TYPE_RETURNPOINTS );
		}
	}
    return true;
}

bool MetricVisitor::VisitCallExpr(clang::CallExpr *p_callExpr)
{
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_FUNCTION_CALLS );
	}
    return true;
}

bool MetricVisitor::VisitSwitchStmt(clang::SwitchStmt *p_switchSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_SWITCH );
	}
    return true;
}

bool MetricVisitor::VisitConditionalOperator(clang::ConditionalOperator *p_condOp) {
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitConditionalOperator : " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_TERNARY );
	}
    return true;
}

bool MetricVisitor::VisitDefaultStmt(clang::DefaultStmt *p_defaultSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_DEFAULT );
	}
    return true;
}

bool MetricVisitor::VisitCaseStmt(clang::CaseStmt *p_caseSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_CASE );
	}
    return true;
}

bool MetricVisitor::VisitBinaryOperator(clang::BinaryOperator *p_binOp) {
	if( m_currentUnit )
	{
		switch( p_binOp->getOpcode() )
		{
			case clang::BO_LAnd:
				m_currentUnit->increment( METRIC_TYPE_LOGICAL_AND );
				break;
			case clang::BO_LOr:
				m_currentUnit->increment( METRIC_TYPE_LOGICAL_OR );
				break;
			default:
				break;
		}
	}
    return true;
}

bool MetricVisitor::VisitStmt(clang::Stmt *p_statement) {

	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_STATEMENTS );
	}
	return true;
}

bool MetricVisitor::VisitIfStmt(clang::IfStmt *p_ifSt) {
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitIfStmt : CONTEXT " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif

	if( m_currentUnit )
	{
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitIfStmt - Recorded" << std::endl;
#endif
		m_currentUnit->increment( METRIC_TYPE_IF );

		if( p_ifSt->getElse() )
		{
			// TODO: This means that "else if" statements get counted as both an IF and an ELSE, which may not be what everyone wants
			m_currentUnit->increment( METRIC_TYPE_ELSE );
		}
	} else {
		/* TODO */
	}

    return true;
}

void MetricVisitor::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt )
{
	m_topUnit->dump( out, p_output, p_fmt );
}

MetricASTConsumer::MetricASTConsumer(clang::ASTContext *CI, MetricUnit* p_topUnit, MetricVisitor::Options* p_options ) : visitor(new MetricVisitor(CI, p_topUnit, p_options))
{
}

MetricASTConsumer::~MetricASTConsumer(void)
{
};

void MetricASTConsumer::HandleTranslationUnit(clang::ASTContext &Context) {
	/* we can use ASTContext to get the TranslationUnitDecl, which is
		a single Decl that collectively represents the entire source file */
	visitor->TraverseDecl(Context.getTranslationUnitDecl());
}

void MetricASTConsumer::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt )
{
	visitor->dump( out, p_output, p_fmt );
}<commit_msg>Updates #31 by being more selective when attributing variables to a MetricUnit<commit_after>/*
   Copyright 2014 John Bailey

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <sstream>
#include <iostream>

#include "MetricMatcher.hpp"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/AST/ASTContext.h"

using namespace clang;
using namespace std;

//#define DEBUG_FN_TRACE_OUTOUT 1

MetricVisitor::MetricVisitor(clang::ASTContext* p_Context, MetricUnit* p_topUnit, Options* p_options) : astContext(p_Context), m_topUnit( p_topUnit ), m_currentUnit( NULL ), m_options( p_options )
{
}

MetricVisitor::~MetricVisitor(void)
{
}

bool MetricVisitor::ShouldIncludeFile( const std::string& p_fn ) const
{
	bool ret_val = true;
	if (( m_options != NULL ) &&
		( m_options->ExcludeFiles != NULL ))
	{
		for( std::vector<std::string>::const_iterator it = m_options->ExcludeFiles->begin();
			 it != m_options->ExcludeFiles->end();
			 it++ )
		{
			if( p_fn.find( *it ) !=  string::npos )
			{
				ret_val = false;
				break;
			}
		}
	}
	return ret_val;
}

bool MetricVisitor::ShouldIncludeFunction( const std::string& p_fn ) const
{
	return (( m_options == NULL ) ||
			( m_options->ExcludeFunctions == NULL ) ||
			( std::find( m_options->ExcludeFunctions->begin(), m_options->ExcludeFunctions->end(), p_fn ) == m_options->ExcludeFunctions->end() ));
}

bool MetricVisitor::VisitFunctionDecl(clang::FunctionDecl *func) {

#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitFunctionDecl" << std::endl;
#endif
	if( func->doesThisDeclarationHaveABody() )
	{
		m_currentFileName = astContext->getSourceManager().getFilename( func->getLocation() ).str();
		m_currentFunctionName = func->getQualifiedNameAsString();

#if defined( DEBUG_FN_TRACE_OUTOUT )
		std::cout << "VisitFunctionDecl - " << m_currentFileName << "::"<< m_currentFunctionName << std::endl;
#endif

		if( ShouldIncludeFile( m_currentFileName ) && ShouldIncludeFunction( m_currentFunctionName ))
		{
#if defined( DEBUG_FN_TRACE_OUTOUT )
			std::cout << "VisitFunctionDecl - Should be included" << std::endl;
#endif
			MetricUnit* fileUnit = m_topUnit->getSubUnit(m_currentFileName, METRIC_UNIT_FILE);

			MetricUnitType_e type = METRIC_UNIT_FUNCTION;

			if( func->isCXXClassMember() )
			{
				type = METRIC_UNIT_METHOD;
			}

			m_currentUnit = fileUnit->getSubUnit(m_currentFunctionName, type);

			if( func->getLinkageAndVisibility().getLinkage() == InternalLinkage )
			{
				fileUnit->increment( METRIC_TYPE_LOCAL_FUNCTIONS );
			}
			fileUnit->increment( METRIC_TYPE_FUNCTIONS );
		}
		else
		{
			m_currentUnit = NULL;
		}
	}
	return true;
}

bool MetricVisitor::VisitVarDecl(clang::VarDecl *p_varDec) {

#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitVarDecl : CONTEXT " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif

	/* Check it's not something like a function parameter */
	if( p_varDec->getKind() == clang::Decl::Var )
	{
		/* Check to see if this variable does not have a parent function/method */
		if( !p_varDec->getParentFunctionOrMethod() )
		{
			/* Don't count external references */
			if ( !p_varDec->hasExternalStorage() )
			{

				SourceLocation loc = p_varDec->getLocation();
				if( loc.isMacroID() )
				{
					loc = astContext->getSourceManager().getFileLoc(loc);
				}

				/* File-scope variable */
				m_currentFileName = astContext->getSourceManager().getFilename( loc ).str();
				m_currentFunctionName = "";
				if( ShouldIncludeFile( m_currentFileName ))
				{
#if defined( DEBUG_FN_TRACE_OUTOUT )
				std::cout << "VisitVarDecl : Processing " << p_varDec->getNameAsString() << std::endl;
#endif
					m_currentUnit = m_topUnit->getSubUnit(m_currentFileName, METRIC_UNIT_FILE);
					m_currentUnit->increment( METRIC_TYPE_VARIABLE );
				}
				else
				{
					m_currentUnit = NULL;
				}
			}
		}

	}
	return true;
}

// TODO: Lots of repetition here - template it?
bool MetricVisitor::VisitForStmt(clang::ForStmt *p_forSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_FORLOOP );
	}
    return true;
}

bool MetricVisitor::VisitGotoStmt(clang::GotoStmt *p_gotoSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_GOTO );
	}

    return true;
}

bool MetricVisitor::VisitLabelStmt(clang::LabelStmt *p_LabelSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_LABEL );
	}
    return true;
}

bool MetricVisitor::VisitWhileStmt(clang::WhileStmt *p_whileSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_WHILELOOP );
	}
    return true;
}

bool MetricVisitor::VisitReturnStmt(clang::ReturnStmt *p_returnSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_RETURN );

		if( !isLastExecutableStmtInFn( p_returnSt, astContext ) )
		{
			m_currentUnit->increment( METRIC_TYPE_RETURNPOINTS );
		}
	}
    return true;
}

bool MetricVisitor::VisitCallExpr(clang::CallExpr *p_callExpr)
{
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_FUNCTION_CALLS );
	}
    return true;
}

bool MetricVisitor::VisitSwitchStmt(clang::SwitchStmt *p_switchSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_SWITCH );
	}
    return true;
}

bool MetricVisitor::VisitConditionalOperator(clang::ConditionalOperator *p_condOp) {
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitConditionalOperator : " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_TERNARY );
	}
    return true;
}

bool MetricVisitor::VisitDefaultStmt(clang::DefaultStmt *p_defaultSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_DEFAULT );
	}
    return true;
}

bool MetricVisitor::VisitCaseStmt(clang::CaseStmt *p_caseSt) {
	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_CASE );
	}
    return true;
}

bool MetricVisitor::VisitBinaryOperator(clang::BinaryOperator *p_binOp) {
	if( m_currentUnit )
	{
		switch( p_binOp->getOpcode() )
		{
			case clang::BO_LAnd:
				m_currentUnit->increment( METRIC_TYPE_LOGICAL_AND );
				break;
			case clang::BO_LOr:
				m_currentUnit->increment( METRIC_TYPE_LOGICAL_OR );
				break;
			default:
				break;
		}
	}
    return true;
}

bool MetricVisitor::VisitStmt(clang::Stmt *p_statement) {

	if( m_currentUnit )
	{
		m_currentUnit->increment( METRIC_TYPE_STATEMENTS );
	}
	return true;
}

bool MetricVisitor::VisitIfStmt(clang::IfStmt *p_ifSt) {
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitIfStmt : CONTEXT " << m_currentFileName << "::" << m_currentFunctionName << std::endl;
#endif

	if( m_currentUnit )
	{
#if defined( DEBUG_FN_TRACE_OUTOUT )
	std::cout << "VisitIfStmt - Recorded" << std::endl;
#endif
		m_currentUnit->increment( METRIC_TYPE_IF );

		if( p_ifSt->getElse() )
		{
			// TODO: This means that "else if" statements get counted as both an IF and an ELSE, which may not be what everyone wants
			m_currentUnit->increment( METRIC_TYPE_ELSE );
		}
	} else {
		/* TODO */
	}

    return true;
}

void MetricVisitor::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt )
{
	m_topUnit->dump( out, p_output, p_fmt );
}

MetricASTConsumer::MetricASTConsumer(clang::ASTContext *CI, MetricUnit* p_topUnit, MetricVisitor::Options* p_options ) : visitor(new MetricVisitor(CI, p_topUnit, p_options))
{
}

MetricASTConsumer::~MetricASTConsumer(void)
{
};

void MetricASTConsumer::HandleTranslationUnit(clang::ASTContext &Context) {
	/* we can use ASTContext to get the TranslationUnitDecl, which is
		a single Decl that collectively represents the entire source file */
	visitor->TraverseDecl(Context.getTranslationUnitDecl());
}

void MetricASTConsumer::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt )
{
	visitor->dump( out, p_output, p_fmt );
}<|endoftext|>


<commit_before>// Copyright (c) 2016 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Windows.h>
#include <DbgHelp.h>
#include <process.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "../TPlatform.h"

namespace tyr { namespace basic {

static const uint64_t kEpoch = 116444736000000000ULL;
static const DWORD kMSVCException = 0x406D1388;
static const int kMaxBackTrace = 256;
static const HANDLE kMainProc = GetCurrentProcess();
static const BOOL kBTTempInit = SymInitialize(kMainProc, NULL, TRUE);

#pragma pack(push, 8)
typedef struct KernThreadName {
  DWORD dwType; // must be 0x1000
  LPCSTR szName;
  DWORD dwThreadID; // thread id
  DWORD dwFlags;
} KernThreadName;
#pragma pack(pop)

typedef struct KernThreadParams {
  KernThread* thread;
  void* (*start)(void*);
  void* arg;
} KernThreadParams;

int gettimeofday(struct timeval* tv, struct timezone* /*tz*/) {
  if (tv) {
    FILETIME ft;
    SYSTEMTIME st;
    ULARGE_INTEGER uli;

    GetSystemTime(&st);
    SystemTimeToFileTime(&st, &ft);
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;

    tv->tv_sec = static_cast<long>((uli.QuadPart - kEpoch) / 10000000L);
    tv->tv_usec = static_cast<long>(st.wMilliseconds * 1000);
  }
  return 0;
}

pid_t kern_getpid(void) {
  return static_cast<pid_t>(GetCurrentProcessId());
}

pid_t kern_gettid(void) {
  return static_cast<pid_t>(GetCurrentThreadId());
}

int kern_backtrace(std::string& bt) {
  void* stack[kMaxBackTrace];
  int frames = CaptureStackBackTrace(0, kMaxBackTrace, stack, nullptr);

  char symbol_buff[sizeof(SYMBOL_INFO) + kMaxBackTrace * sizeof(char)];
  PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbol_buff;
  symbol->MaxNameLen = kMaxBackTrace;
  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

  char message[1024];
  for (int i = 0; i < frames; ++i) {
    SymFromAddr(kMainProc, (DWORD)stack[i], 0, symbol);
    snprintf(message, sizeof(message), "%i: %s - 0x%p\n", frames - i - 1, symbol->Name, (void*)symbol->Address);
    bt.append(message);
  }

  return 0;
}

int kern_mutex_init(KernMutex* mtx) {
  return InitializeCriticalSection(mtx), 0;
}

int kern_mutex_destroy(KernMutex* mtx) {
  return DeleteCriticalSection(mtx), 0;
}

int kern_mutex_lock(KernMutex* mtx) {
  if ((DWORD)mtx->OwningThread != GetCurrentThreadId())
    EnterCriticalSection(mtx);
  return 0;
}

int kern_mutex_unlock(KernMutex* mtx) {
  return LeaveCriticalSection(mtx), 0;
}

int kern_this_thread_setname(const char* name) {
  KernThreadName ni;
  ni.dwType = 0x1000;
  ni.szName = name;
  ni.dwThreadID = GetCurrentThreadId();
  ni.dwFlags = 0;
  __try {
    RaiseException(kMSVCException, 0, sizeof(ni) / sizeof(ULONG_PTR), (ULONG_PTR*)&ni);
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
  }
  return 0;
}

int kern_cond_init(KernCond* cond) {
  return InitializeConditionVariable(cond), 0;
}

int kern_cond_destroy(KernCond* /*cond*/) {
  return 0;
}

int kern_cond_signal(KernCond* cond) {
  return WakeConditionVariable(cond), 0;
}

int kern_cond_broadcast(KernCond* cond) {
  return WakeAllConditionVariable(cond), 0;
}

int kern_cond_wait(KernCond* cond, KernMutex* mtx) {
  return SleepConditionVariableCS(cond, mtx, INFINITE) ? 0 : -1;
}

int kern_cond_timedwait(KernCond* cond, KernMutex* mtx, uint64_t nanosec) {
  return SleepConditionVariableCS(cond, mtx, static_cast<DWORD>(nanosec / 1e6)) ? 0 : -1;
}

UINT WINAPI kern_thread_start_routine(void* arg) {
  std::unique_ptr<KernThreadParams> params(static_cast<KernThreadParams*>(arg));
  if (!params)
    return 0;

  SetEvent(params->thread->start_event);
  if (nullptr != params->start)
    params->start(params->arg);

  return 0;
}

int kern_thread_create(KernThread* thread, void* (*start_routine)(void*), void* arg) {
  thread->start_event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (nullptr == thread->start_event)
    return -1;

  int result = -1;
  std::unique_ptr<KernThreadParams> params(new KernThreadParams);
  if (!params)
    goto _Exit;
  params->thread = thread;
  params->start = start_routine;
  params->arg = arg;

  thread->thrd_handle = reinterpret_cast<HANDLE>(_beginthreadex(
        nullptr, 0, kern_thread_start_routine, params.get(), 0, nullptr));
  if (nullptr == thread->thrd_handle)
    goto _Exit;

  WaitForSingleObject(thread->start_event, INFINITE);
  params.release();
  result = 0;

_Exit:
  CloseHandle(thread->start_event);
  thread->start_event = nullptr;
  return result;
}

int kern_thread_join(KernThread thread) {
  if (nullptr != thread.thrd_handle) {
    WaitForSingleObject(thread.thrd_handle, INFINITE);
    CloseHandle(thread.thrd_handle);
  }
  return 0;
}

int kern_thread_detach(KernThread thread) {
  if (nullptr != thread.thrd_handle)
    CloseHandle(thread.thrd_handle);
  return 0;
}

int kern_thread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
  return 0;
}

int kern_threadkey_create(KernThreadKey* key, void (*destructor)(void*)) {
  return *key = FlsAlloc((PFLS_CALLBACK_FUNCTION)destructor), 0;
}

int kern_threadkey_delete(KernThreadKey key) {
  return TRUE == FlsFree(key) ? 0 : -1;
}

int kern_setspecific(KernThreadKey key, const void* value) {
  return TRUE == FlsSetValue(key, (PVOID)value) ? 0 : -1;
}

void* kern_getspecific(KernThreadKey key) {
  return FlsGetValue(key);
}

}}
<commit_msg>chore(tyr.basic): updated the mutex lock for windows<commit_after>// Copyright (c) 2016 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Windows.h>
#include <DbgHelp.h>
#include <process.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "../TPlatform.h"

namespace tyr { namespace basic {

static const uint64_t kEpoch = 116444736000000000ULL;
static const DWORD kMSVCException = 0x406D1388;
static const int kMaxBackTrace = 256;
static const HANDLE kMainProc = GetCurrentProcess();
static const BOOL kBTTempInit = SymInitialize(kMainProc, NULL, TRUE);

#pragma pack(push, 8)
typedef struct KernThreadName {
  DWORD dwType; // must be 0x1000
  LPCSTR szName;
  DWORD dwThreadID; // thread id
  DWORD dwFlags;
} KernThreadName;
#pragma pack(pop)

typedef struct KernThreadParams {
  KernThread* thread;
  void* (*start)(void*);
  void* arg;
} KernThreadParams;

int gettimeofday(struct timeval* tv, struct timezone* /*tz*/) {
  if (tv) {
    FILETIME ft;
    SYSTEMTIME st;
    ULARGE_INTEGER uli;

    GetSystemTime(&st);
    SystemTimeToFileTime(&st, &ft);
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;

    tv->tv_sec = static_cast<long>((uli.QuadPart - kEpoch) / 10000000L);
    tv->tv_usec = static_cast<long>(st.wMilliseconds * 1000);
  }
  return 0;
}

pid_t kern_getpid(void) {
  return static_cast<pid_t>(GetCurrentProcessId());
}

pid_t kern_gettid(void) {
  return static_cast<pid_t>(GetCurrentThreadId());
}

int kern_backtrace(std::string& bt) {
  void* stack[kMaxBackTrace];
  int frames = CaptureStackBackTrace(0, kMaxBackTrace, stack, nullptr);

  char symbol_buff[sizeof(SYMBOL_INFO) + kMaxBackTrace * sizeof(char)];
  PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbol_buff;
  symbol->MaxNameLen = kMaxBackTrace;
  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

  char message[1024];
  for (int i = 0; i < frames; ++i) {
    SymFromAddr(kMainProc, (DWORD)stack[i], 0, symbol);
    snprintf(message, sizeof(message), "%i: %s - 0x%p\n", frames - i - 1, symbol->Name, (void*)symbol->Address);
    bt.append(message);
  }

  return 0;
}

int kern_mutex_init(KernMutex* mtx) {
  return InitializeCriticalSection(mtx), 0;
}

int kern_mutex_destroy(KernMutex* mtx) {
  return DeleteCriticalSection(mtx), 0;
}

int kern_mutex_lock(KernMutex* mtx) {
  // FIXME: need non-recursive mutex on Windows
  EnterCriticalSection(mtx);
  return 0;
}

int kern_mutex_unlock(KernMutex* mtx) {
  return LeaveCriticalSection(mtx), 0;
}

int kern_this_thread_setname(const char* name) {
  KernThreadName ni;
  ni.dwType = 0x1000;
  ni.szName = name;
  ni.dwThreadID = GetCurrentThreadId();
  ni.dwFlags = 0;
  __try {
    RaiseException(kMSVCException, 0, sizeof(ni) / sizeof(ULONG_PTR), (ULONG_PTR*)&ni);
  }
  __except (EXCEPTION_EXECUTE_HANDLER) {
  }
  return 0;
}

int kern_cond_init(KernCond* cond) {
  return InitializeConditionVariable(cond), 0;
}

int kern_cond_destroy(KernCond* /*cond*/) {
  return 0;
}

int kern_cond_signal(KernCond* cond) {
  return WakeConditionVariable(cond), 0;
}

int kern_cond_broadcast(KernCond* cond) {
  return WakeAllConditionVariable(cond), 0;
}

int kern_cond_wait(KernCond* cond, KernMutex* mtx) {
  return SleepConditionVariableCS(cond, mtx, INFINITE) ? 0 : -1;
}

int kern_cond_timedwait(KernCond* cond, KernMutex* mtx, uint64_t nanosec) {
  return SleepConditionVariableCS(cond, mtx, static_cast<DWORD>(nanosec / 1e6)) ? 0 : -1;
}

UINT WINAPI kern_thread_start_routine(void* arg) {
  std::unique_ptr<KernThreadParams> params(static_cast<KernThreadParams*>(arg));
  if (!params)
    return 0;

  SetEvent(params->thread->start_event);
  if (nullptr != params->start)
    params->start(params->arg);

  return 0;
}

int kern_thread_create(KernThread* thread, void* (*start_routine)(void*), void* arg) {
  thread->start_event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
  if (nullptr == thread->start_event)
    return -1;

  int result = -1;
  std::unique_ptr<KernThreadParams> params(new KernThreadParams);
  if (!params)
    goto _Exit;
  params->thread = thread;
  params->start = start_routine;
  params->arg = arg;

  thread->thrd_handle = reinterpret_cast<HANDLE>(_beginthreadex(
        nullptr, 0, kern_thread_start_routine, params.get(), 0, nullptr));
  if (nullptr == thread->thrd_handle)
    goto _Exit;

  WaitForSingleObject(thread->start_event, INFINITE);
  params.release();
  result = 0;

_Exit:
  CloseHandle(thread->start_event);
  thread->start_event = nullptr;
  return result;
}

int kern_thread_join(KernThread thread) {
  if (nullptr != thread.thrd_handle) {
    WaitForSingleObject(thread.thrd_handle, INFINITE);
    CloseHandle(thread.thrd_handle);
  }
  return 0;
}

int kern_thread_detach(KernThread thread) {
  if (nullptr != thread.thrd_handle)
    CloseHandle(thread.thrd_handle);
  return 0;
}

int kern_thread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
  return 0;
}

int kern_threadkey_create(KernThreadKey* key, void (*destructor)(void*)) {
  return *key = FlsAlloc((PFLS_CALLBACK_FUNCTION)destructor), 0;
}

int kern_threadkey_delete(KernThreadKey key) {
  return TRUE == FlsFree(key) ? 0 : -1;
}

int kern_setspecific(KernThreadKey key, const void* value) {
  return TRUE == FlsSetValue(key, (PVOID)value) ? 0 : -1;
}

void* kern_getspecific(KernThreadKey key) {
  return FlsGetValue(key);
}

}}
<|endoftext|>


<commit_before>/*
 * Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"

enum Says
{
    SAY_AGGRO           = 0,
    SAY_GREET           = 1,
    SAY_SLAY            = 2,
};

enum Spells
{
    SPELL_IMPALE_10                 = 28783,
    SPELL_IMPALE_25                 = 56090,
    SPELL_LOCUST_SWARM_10           = 28785,
    SPELL_LOCUST_SWARM_25           = 54021,
    SPELL_SUMMON_CORPSE_SCRABS_5    = 29105,
    SPELL_SUMMON_CORPSE_SCRABS_10   = 28864,
    SPELL_BERSERK                   = 26662,
};

enum Events
{
    EVENT_SPELL_IMPALE              = 1,
    EVENT_SPELL_LOCUST_SWARM        = 2,
    EVENT_SPELL_BERSERK             = 3,
};

enum Misc
{
    NPC_CORPSE_SCARAB               = 16698,
    NPC_CRYPT_GUARD                 = 16573,

    ACHIEV_TIMED_START_EVENT        = 9891,
};

class boss_anubrekhan : public CreatureScript
{
public:
    boss_anubrekhan() : CreatureScript("boss_anubrekhan") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_anubrekhanAI (pCreature);
    }

    struct boss_anubrekhanAI : public ScriptedAI
    {
        boss_anubrekhanAI(Creature *c) : ScriptedAI(c), summons(me)
        {
            pInstance = c->GetInstanceScript();
            sayGreet = false;
        }

        InstanceScript* pInstance;
        EventMap events;
        SummonList summons;
        bool sayGreet;

        void SummonCryptGuards()
        {
            if (Is25ManRaid())
            {
                me->SummonCreature(NPC_CRYPT_GUARD, 3308.590f, -3486.29f, 287.16f, M_PI, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
                me->SummonCreature(NPC_CRYPT_GUARD, 3308.590f, -3466.29f, 287.16f, M_PI, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
            }
        }

        void Reset()
        {
            events.Reset();
            summons.DespawnAll();
            SummonCryptGuards();

            if (pInstance)
            {
                pInstance->SetData(EVENT_ANUB, NOT_STARTED);
                if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE)))
                    go->SetGoState(GO_STATE_ACTIVE);
            }
        }

        void JustSummoned(Creature* cr)
        {
            if (me->IsInCombat())
                cr->SetInCombatWithZone();
            if (cr->GetEntry() == NPC_CORPSE_SCARAB)
            {
                cr->SetReactState(REACT_PASSIVE);
                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                    cr->AI()->AttackStart(target);
            }

            summons.Summon(cr);
        }

        void SummonedCreatureDies(Creature* cr, Unit*)
        {
            if (cr->GetEntry() == NPC_CRYPT_GUARD)
                cr->CastSpell(cr, SPELL_SUMMON_CORPSE_SCRABS_10, true, NULL, NULL, me->GetGUID());
        }

        void SummonedCreatureDespawn(Creature* cr) { summons.Despawn(cr); }

        void JustDied(Unit* Killer)
        {
            summons.DespawnAll();
            if (pInstance)
            {
                pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
                pInstance->SetData(EVENT_ANUB, DONE);
            }
        }

        void KilledUnit(Unit* victim)
        {
            if (victim->GetTypeId() != TYPEID_PLAYER)
                return;

            if (!urand(0,3))
                Talk(SAY_SLAY);

            //Force the player to spawn corpse scarabs via spell
            victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, NULL, NULL, me->GetGUID());

            if (pInstance)
                pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
        }

        void EnterCombat(Unit *who)
        {
            me->CallForHelp(30.0f); // catch helpers
            Talk(SAY_AGGRO);
            if (pInstance)
            {
                pInstance->SetData(EVENT_ANUB, IN_PROGRESS);
                if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE)))
                    go->SetGoState(GO_STATE_READY);
            }

            events.ScheduleEvent(EVENT_SPELL_IMPALE, 15000);
            events.ScheduleEvent(EVENT_SPELL_LOCUST_SWARM, 70000+urand(0,50000));
            events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);

            if (!summons.HasEntry(NPC_CRYPT_GUARD))
                SummonCryptGuards();
        }

        void MoveInLineOfSight(Unit *who)
        {
            if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
            {
                Talk(SAY_GREET);
                sayGreet = true;
            }

            ScriptedAI::MoveInLineOfSight(who);
        }

        void UpdateAI(uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);
            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (events.GetEvent())
            {
                case EVENT_SPELL_IMPALE:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, RAID_MODE(SPELL_IMPALE_10, SPELL_IMPALE_25), false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_LOCUST_SWARM:
                    me->CastSpell(me, RAID_MODE(SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25), false);
                    Position pos;
                    me->GetNearPosition(pos, 10.0f, rand_norm()*2*M_PI);
                    me->SummonCreature(NPC_CRYPT_GUARD, pos);
                    events.RepeatEvent(90000);
                    break;
                case EVENT_SPELL_BERSERK:
                    me->CastSpell(me, SPELL_BERSERK, true);
                    events.PopEvent();
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

void AddSC_boss_anubrekhan()
{
    new boss_anubrekhan();
}

<commit_msg>Changed the spawn location of Anubrekhan adds to be more blizzlike<commit_after>/*
 * Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"

enum Says
{
    SAY_AGGRO           = 0,
    SAY_GREET           = 1,
    SAY_SLAY            = 2,
};

enum Spells
{
    SPELL_IMPALE_10                 = 28783,
    SPELL_IMPALE_25                 = 56090,
    SPELL_LOCUST_SWARM_10           = 28785,
    SPELL_LOCUST_SWARM_25           = 54021,
    SPELL_SUMMON_CORPSE_SCRABS_5    = 29105,
    SPELL_SUMMON_CORPSE_SCRABS_10   = 28864,
    SPELL_BERSERK                   = 26662,
};

enum Events
{
    EVENT_SPELL_IMPALE              = 1,
    EVENT_SPELL_LOCUST_SWARM        = 2,
    EVENT_SPELL_BERSERK             = 3,
};

enum Misc
{
    NPC_CORPSE_SCARAB               = 16698,
    NPC_CRYPT_GUARD                 = 16573,

    ACHIEV_TIMED_START_EVENT        = 9891,
};

class boss_anubrekhan : public CreatureScript
{
public:
    boss_anubrekhan() : CreatureScript("boss_anubrekhan") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_anubrekhanAI (pCreature);
    }

    struct boss_anubrekhanAI : public ScriptedAI
    {
        boss_anubrekhanAI(Creature *c) : ScriptedAI(c), summons(me)
        {
            pInstance = c->GetInstanceScript();
            sayGreet = false;
        }

        InstanceScript* pInstance;
        EventMap events;
        SummonList summons;
        bool sayGreet;

        void SummonCryptGuards()
        {
            if (Is25ManRaid())
            {
                me->SummonCreature(NPC_CRYPT_GUARD, 3299.825f, -3502.27f, 287.16f, M_PI, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
                me->SummonCreature(NPC_CRYPT_GUARD, 3299.087f, -3450.93f, 287.16f, M_PI, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
            }
        }

        void Reset()
        {
            events.Reset();
            summons.DespawnAll();
            SummonCryptGuards();

            if (pInstance)
            {
                pInstance->SetData(EVENT_ANUB, NOT_STARTED);
                if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE)))
                    go->SetGoState(GO_STATE_ACTIVE);
            }
        }

        void JustSummoned(Creature* cr)
        {
            if (me->IsInCombat())
                cr->SetInCombatWithZone();
            if (cr->GetEntry() == NPC_CORPSE_SCARAB)
            {
                cr->SetReactState(REACT_PASSIVE);
                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                    cr->AI()->AttackStart(target);
            }

            summons.Summon(cr);
        }

        void SummonedCreatureDies(Creature* cr, Unit*)
        {
            if (cr->GetEntry() == NPC_CRYPT_GUARD)
                cr->CastSpell(cr, SPELL_SUMMON_CORPSE_SCRABS_10, true, NULL, NULL, me->GetGUID());
        }

        void SummonedCreatureDespawn(Creature* cr) { summons.Despawn(cr); }

        void JustDied(Unit* Killer)
        {
            summons.DespawnAll();
            if (pInstance)
            {
                pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
                pInstance->SetData(EVENT_ANUB, DONE);
            }
        }

        void KilledUnit(Unit* victim)
        {
            if (victim->GetTypeId() != TYPEID_PLAYER)
                return;

            if (!urand(0,3))
                Talk(SAY_SLAY);

            //Force the player to spawn corpse scarabs via spell
            victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, NULL, NULL, me->GetGUID());

            if (pInstance)
                pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
        }

        void EnterCombat(Unit *who)
        {
            me->CallForHelp(30.0f); // catch helpers
            Talk(SAY_AGGRO);
            if (pInstance)
            {
                pInstance->SetData(EVENT_ANUB, IN_PROGRESS);
                if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE)))
                    go->SetGoState(GO_STATE_READY);
            }

            events.ScheduleEvent(EVENT_SPELL_IMPALE, 15000);
            events.ScheduleEvent(EVENT_SPELL_LOCUST_SWARM, 70000+urand(0,50000));
            events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);

            if (!summons.HasEntry(NPC_CRYPT_GUARD))
                SummonCryptGuards();
        }

        void MoveInLineOfSight(Unit *who)
        {
            if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
            {
                Talk(SAY_GREET);
                sayGreet = true;
            }

            ScriptedAI::MoveInLineOfSight(who);
        }

        void UpdateAI(uint32 diff)
        {
            if (!UpdateVictim())
                return;

            events.Update(diff);
            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (events.GetEvent())
            {
                case EVENT_SPELL_IMPALE:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, RAID_MODE(SPELL_IMPALE_10, SPELL_IMPALE_25), false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_LOCUST_SWARM:
                    me->CastSpell(me, RAID_MODE(SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25), false);
                    Position pos;
                    me->GetNearPosition(pos, 10.0f, rand_norm()*2*M_PI);
                    me->SummonCreature(NPC_CRYPT_GUARD, pos);
                    events.RepeatEvent(90000);
                    break;
                case EVENT_SPELL_BERSERK:
                    me->CastSpell(me, SPELL_BERSERK, true);
                    events.PopEvent();
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

void AddSC_boss_anubrekhan()
{
    new boss_anubrekhan();
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Threads.h>

#ifdef HAVE_PTHREAD

#include <znc/ZNCDebug.h>
#include <algorithm>

/* Just an arbitrary limit for the number of idle threads */
static const size_t MAX_IDLE_THREADS = 3;

/* Just an arbitrary limit for the number of running threads */
static const size_t MAX_TOTAL_THREADS = 20;

CThreadPool& CThreadPool::Get() {
	// Beware! The following is not thread-safe! This function must
	// be called once any thread is started.
	static CThreadPool pool;
	return pool;
}

CThreadPool::CThreadPool() : m_done(false), m_num_threads(0), m_num_idle(0) {
	if (pipe(m_iJobPipe)) {
		DEBUG("Ouch, can't open pipe for thread pool: " << strerror(errno));
		exit(1);
	}
}

void CThreadPool::jobDone(CJob* job) {
	// This must be called with the mutex locked!

	enum CJob::EJobState oldState = job->m_eState;
	job->m_eState = CJob::DONE;

	if (oldState == CJob::CANCELLED) {
		// Signal the main thread that cancellation is done
		m_cancellationCond.signal();
		return;
	}

	// This write() must succeed because POSIX guarantees that writes of
	// less than PIPE_BUF are atomic (and PIPE_BUF is at least 512).
	// (Yes, this really wants to write a pointer(!) to the pipe.
	size_t w = write(m_iJobPipe[1], &job, sizeof(job));
	if (w != sizeof(job)) {
		DEBUG("Something bad happened during write() to a pipe for thread pool, wrote " << w << " bytes: " << strerror(errno));
		exit(1);
	}
}

void CThreadPool::handlePipeReadable() const {
	finishJob(getJobFromPipe());
}

CJob *CThreadPool::getJobFromPipe() const {
	CJob* a = NULL;
	ssize_t need = sizeof(a);
	ssize_t r = read(m_iJobPipe[0], &a, need);
	if (r != need) {
		DEBUG("Something bad happened during read() from a pipe for thread pool: " << strerror(errno));
		exit(1);
	}
	return a;
}

void CThreadPool::finishJob(CJob *job) const {
	job->runMain();
	delete job;
}

CThreadPool::~CThreadPool() {
	CMutexLocker guard(m_mutex);
	m_done = true;

	if (m_num_threads > 0) {
		m_cond.broadcast();
		m_exit_cond.wait(m_mutex);
	}
}

bool CThreadPool::threadNeeded() const {
	if (m_num_idle > MAX_IDLE_THREADS)
		return false;
	return !m_done;
}

void CThreadPool::threadFunc() {
	CMutexLocker guard(m_mutex);
	// m_num_threads was already increased
	m_num_idle++;

	while (true) {
		while (m_jobs.empty()) {
			if (!threadNeeded())
				break;
			m_cond.wait(m_mutex);
		}
		if (!threadNeeded())
			break;

		// Figure out a job to do
		CJob *job = m_jobs.front();
		m_jobs.pop_front();

		// Now do the actual job
		m_num_idle--;
		job->m_eState = CJob::RUNNING;
		guard.unlock();

		job->runThread();

		guard.lock();
		jobDone(job);
		m_num_idle++;
	}
	assert(m_num_threads > 0 && m_num_idle > 0);
	m_num_threads--;
	m_num_idle--;

	if (m_num_threads == 0 && m_done)
		m_exit_cond.signal();
}

void CThreadPool::addJob(CJob *job) {
	CMutexLocker guard(m_mutex);
	m_jobs.push_back(job);

	// Do we already have a thread which can handle this job?
	if (m_num_idle > 0) {
		m_cond.signal();
		return;
	}

	if (m_num_threads >= MAX_TOTAL_THREADS)
		// We can't start a new thread. The job will be handled once
		// some thread finishes its current job.
		return;

	// Start a new thread for our pool
	m_num_threads++;
	CThread::startThread(threadPoolFunc, this);
}

void CThreadPool::cancelJob(CJob *job) {
	std::set<CJob *> jobs;
	jobs.insert(job);
	cancelJobs(jobs);
}

void CThreadPool::cancelJobs(const std::set<CJob *> &jobs) {
	// Thanks to the mutex, jobs cannot change state anymore. There are
	// three different states which can occur:
	//
	// READY: The job is still in our list of pending jobs and no threads
	// got it yet. Just clean up.
	//
	// DONE: The job finished running and was already written to the pipe
	// that is used for waking up finished jobs. We can just read from the
	// pipe until we see this job.
	//
	// RUNNING: This is the complicated case. The job is currently being
	// executed. We change its state to CANCELLED so that wasCancelled()
	// returns true. Afterwards we wait on a CV for the job to have finished
	// running. This CV is signaled by jobDone() which checks the job's
	// status and sees that the job was cancelled. It signals to us that
	// cancellation is done by changing the job's status to DONE.

	CMutexLocker guard(m_mutex);
	std::set<CJob *> wait, finished, deleteLater;
	std::set<CJob *>::const_iterator it;

	// Start cancelling all jobs
	for (it = jobs.begin(); it != jobs.end(); ++it) {
		switch ((*it)->m_eState) {
		case CJob::READY: {
			(*it)->m_eState = CJob::CANCELLED;

			// Job wasn't started yet, must be in the queue
			std::list<CJob *>::iterator it2 = std::find(m_jobs.begin(), m_jobs.end(), *it);
			assert(it2 != m_jobs.end());
			m_jobs.erase(it2);
			deleteLater.insert(*it);
			continue;
		}

		case CJob::RUNNING:
			(*it)->m_eState = CJob::CANCELLED;
			wait.insert(*it);
			continue;

		case CJob::DONE:
			(*it)->m_eState = CJob::CANCELLED;
			finished.insert(*it);
			continue;

		case CJob::CANCELLED:
		default:
			assert(0);
		}
	}

	// Now wait for cancellation to be done

	// Collect jobs that really were cancelled. Finished cancellation is
	// signaled by changing their state to DONE.
	while (!wait.empty()) {
		it = wait.begin();
		while (it != wait.end()) {
			if ((*it)->m_eState != CJob::CANCELLED) {
				assert((*it)->m_eState == CJob::DONE);
				// Re-set state for the destructor
				(*it)->m_eState = CJob::CANCELLED;;
				deleteLater.insert(*it);
				wait.erase(it++);
			} else
				it++;
		}

		if (wait.empty())
			break;

		// Then wait for more to be done
		m_cancellationCond.wait(m_mutex);
	}

	// We must call destructors with m_mutex unlocked so that they can call wasCancelled()
	guard.unlock();

	// Handle finished jobs. They must already be in the pipe.
	while (!finished.empty()) {
		CJob *job = getJobFromPipe();
		if (finished.erase(job) > 0) {
			assert(job->m_eState == CJob::CANCELLED);
			delete job;
		} else
			finishJob(job);
	}

	// Delete things that still need to be deleted
	while (!deleteLater.empty()) {
		delete *deleteLater.begin();
		deleteLater.erase(deleteLater.begin());
	}
}

bool CJob::wasCancelled() const {
	CMutexLocker guard(CThreadPool::Get().m_mutex);
	return m_eState == CANCELLED;
}

#endif // HAVE_PTHREAD
<commit_msg>~CThreadPool(): Handle spurious wakeups<commit_after>/*
 * Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Threads.h>

#ifdef HAVE_PTHREAD

#include <znc/ZNCDebug.h>
#include <algorithm>

/* Just an arbitrary limit for the number of idle threads */
static const size_t MAX_IDLE_THREADS = 3;

/* Just an arbitrary limit for the number of running threads */
static const size_t MAX_TOTAL_THREADS = 20;

CThreadPool& CThreadPool::Get() {
	// Beware! The following is not thread-safe! This function must
	// be called once any thread is started.
	static CThreadPool pool;
	return pool;
}

CThreadPool::CThreadPool() : m_done(false), m_num_threads(0), m_num_idle(0) {
	if (pipe(m_iJobPipe)) {
		DEBUG("Ouch, can't open pipe for thread pool: " << strerror(errno));
		exit(1);
	}
}

void CThreadPool::jobDone(CJob* job) {
	// This must be called with the mutex locked!

	enum CJob::EJobState oldState = job->m_eState;
	job->m_eState = CJob::DONE;

	if (oldState == CJob::CANCELLED) {
		// Signal the main thread that cancellation is done
		m_cancellationCond.signal();
		return;
	}

	// This write() must succeed because POSIX guarantees that writes of
	// less than PIPE_BUF are atomic (and PIPE_BUF is at least 512).
	// (Yes, this really wants to write a pointer(!) to the pipe.
	size_t w = write(m_iJobPipe[1], &job, sizeof(job));
	if (w != sizeof(job)) {
		DEBUG("Something bad happened during write() to a pipe for thread pool, wrote " << w << " bytes: " << strerror(errno));
		exit(1);
	}
}

void CThreadPool::handlePipeReadable() const {
	finishJob(getJobFromPipe());
}

CJob *CThreadPool::getJobFromPipe() const {
	CJob* a = NULL;
	ssize_t need = sizeof(a);
	ssize_t r = read(m_iJobPipe[0], &a, need);
	if (r != need) {
		DEBUG("Something bad happened during read() from a pipe for thread pool: " << strerror(errno));
		exit(1);
	}
	return a;
}

void CThreadPool::finishJob(CJob *job) const {
	job->runMain();
	delete job;
}

CThreadPool::~CThreadPool() {
	CMutexLocker guard(m_mutex);
	m_done = true;

	while (m_num_threads > 0) {
		m_cond.broadcast();
		m_exit_cond.wait(m_mutex);
	}
}

bool CThreadPool::threadNeeded() const {
	if (m_num_idle > MAX_IDLE_THREADS)
		return false;
	return !m_done;
}

void CThreadPool::threadFunc() {
	CMutexLocker guard(m_mutex);
	// m_num_threads was already increased
	m_num_idle++;

	while (true) {
		while (m_jobs.empty()) {
			if (!threadNeeded())
				break;
			m_cond.wait(m_mutex);
		}
		if (!threadNeeded())
			break;

		// Figure out a job to do
		CJob *job = m_jobs.front();
		m_jobs.pop_front();

		// Now do the actual job
		m_num_idle--;
		job->m_eState = CJob::RUNNING;
		guard.unlock();

		job->runThread();

		guard.lock();
		jobDone(job);
		m_num_idle++;
	}
	assert(m_num_threads > 0 && m_num_idle > 0);
	m_num_threads--;
	m_num_idle--;

	if (m_num_threads == 0 && m_done)
		m_exit_cond.signal();
}

void CThreadPool::addJob(CJob *job) {
	CMutexLocker guard(m_mutex);
	m_jobs.push_back(job);

	// Do we already have a thread which can handle this job?
	if (m_num_idle > 0) {
		m_cond.signal();
		return;
	}

	if (m_num_threads >= MAX_TOTAL_THREADS)
		// We can't start a new thread. The job will be handled once
		// some thread finishes its current job.
		return;

	// Start a new thread for our pool
	m_num_threads++;
	CThread::startThread(threadPoolFunc, this);
}

void CThreadPool::cancelJob(CJob *job) {
	std::set<CJob *> jobs;
	jobs.insert(job);
	cancelJobs(jobs);
}

void CThreadPool::cancelJobs(const std::set<CJob *> &jobs) {
	// Thanks to the mutex, jobs cannot change state anymore. There are
	// three different states which can occur:
	//
	// READY: The job is still in our list of pending jobs and no threads
	// got it yet. Just clean up.
	//
	// DONE: The job finished running and was already written to the pipe
	// that is used for waking up finished jobs. We can just read from the
	// pipe until we see this job.
	//
	// RUNNING: This is the complicated case. The job is currently being
	// executed. We change its state to CANCELLED so that wasCancelled()
	// returns true. Afterwards we wait on a CV for the job to have finished
	// running. This CV is signaled by jobDone() which checks the job's
	// status and sees that the job was cancelled. It signals to us that
	// cancellation is done by changing the job's status to DONE.

	CMutexLocker guard(m_mutex);
	std::set<CJob *> wait, finished, deleteLater;
	std::set<CJob *>::const_iterator it;

	// Start cancelling all jobs
	for (it = jobs.begin(); it != jobs.end(); ++it) {
		switch ((*it)->m_eState) {
		case CJob::READY: {
			(*it)->m_eState = CJob::CANCELLED;

			// Job wasn't started yet, must be in the queue
			std::list<CJob *>::iterator it2 = std::find(m_jobs.begin(), m_jobs.end(), *it);
			assert(it2 != m_jobs.end());
			m_jobs.erase(it2);
			deleteLater.insert(*it);
			continue;
		}

		case CJob::RUNNING:
			(*it)->m_eState = CJob::CANCELLED;
			wait.insert(*it);
			continue;

		case CJob::DONE:
			(*it)->m_eState = CJob::CANCELLED;
			finished.insert(*it);
			continue;

		case CJob::CANCELLED:
		default:
			assert(0);
		}
	}

	// Now wait for cancellation to be done

	// Collect jobs that really were cancelled. Finished cancellation is
	// signaled by changing their state to DONE.
	while (!wait.empty()) {
		it = wait.begin();
		while (it != wait.end()) {
			if ((*it)->m_eState != CJob::CANCELLED) {
				assert((*it)->m_eState == CJob::DONE);
				// Re-set state for the destructor
				(*it)->m_eState = CJob::CANCELLED;;
				deleteLater.insert(*it);
				wait.erase(it++);
			} else
				it++;
		}

		if (wait.empty())
			break;

		// Then wait for more to be done
		m_cancellationCond.wait(m_mutex);
	}

	// We must call destructors with m_mutex unlocked so that they can call wasCancelled()
	guard.unlock();

	// Handle finished jobs. They must already be in the pipe.
	while (!finished.empty()) {
		CJob *job = getJobFromPipe();
		if (finished.erase(job) > 0) {
			assert(job->m_eState == CJob::CANCELLED);
			delete job;
		} else
			finishJob(job);
	}

	// Delete things that still need to be deleted
	while (!deleteLater.empty()) {
		delete *deleteLater.begin();
		deleteLater.erase(deleteLater.begin());
	}
}

bool CJob::wasCancelled() const {
	CMutexLocker guard(CThreadPool::Get().m_mutex);
	return m_eState == CANCELLED;
}

#endif // HAVE_PTHREAD
<|endoftext|>