VcsCommand: Merge with VcsCommandDecorator

Change-Id: I84df1779377648624984c9e57806f1307ae62b5b
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Jarek Kobus
2022-08-01 18:35:07 +02:00
parent 7c2ab8cf8f
commit 83739bde38
8 changed files with 94 additions and 159 deletions

View File

@@ -25,26 +25,22 @@
#include "vcsbaseclient.h" #include "vcsbaseclient.h"
#include "vcsbaseclientsettings.h" #include "vcsbaseclientsettings.h"
#include "vcsbaseeditor.h"
#include "vcsbaseeditorconfig.h" #include "vcsbaseeditorconfig.h"
#include "vcsbaseplugin.h"
#include "vcscommand.h" #include "vcscommand.h"
#include "vcsplugin.h" #include "vcsoutputwindow.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/vcsmanager.h> #include <coreplugin/vcsmanager.h>
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/commandline.h> #include <utils/commandline.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/globalfilechangeblocker.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/vcsoutputwindow.h>
#include <vcsbase/vcsbaseplugin.h>
#include <QDebug> #include <QDebug>
#include <QFileInfo> #include <QFileInfo>
#include <QFutureInterface> #include <QFutureInterface>
@@ -77,85 +73,6 @@ static IEditor *locateEditor(const char *property, const QString &entry)
namespace VcsBase { namespace VcsBase {
class VcsCommandDecorator : public QObject
{
public:
VcsCommandDecorator(VcsCommand *command);
~VcsCommandDecorator();
private:
void addTask(const QFuture<void> &future);
void postRunCommand(const FilePath &workingDirectory);
VcsCommand *m_command;
QFutureInterface<void> m_futureInterface;
};
VcsCommandDecorator::VcsCommandDecorator(VcsCommand *command)
: QObject(command)
, m_command(command)
{
Environment env = m_command->environment();
VcsBase::setProcessEnvironment(&env);
m_command->setEnvironment(env);
VcsOutputWindow::setRepository(m_command->defaultWorkingDirectory().toString());
m_command->setDisableUnixTerminal();
connect(m_command, &VcsCommand::started, this, [this] {
if (m_command->flags() & VcsCommand::ExpectRepoChanges)
GlobalFileChangeBlocker::instance()->forceBlocked(true);
});
connect(m_command, &VcsCommand::finished, this, [this] {
if (m_command->flags() & VcsCommand::ExpectRepoChanges)
GlobalFileChangeBlocker::instance()->forceBlocked(false);
});
VcsOutputWindow *outputWindow = VcsOutputWindow::instance();
connect(m_command, &VcsCommand::append, outputWindow, [outputWindow](const QString &t) {
outputWindow->append(t);
});
connect(m_command, &VcsCommand::appendSilently, outputWindow, &VcsOutputWindow::appendSilently);
connect(m_command, &VcsCommand::appendError, outputWindow, &VcsOutputWindow::appendError);
connect(m_command, &VcsCommand::appendCommand, outputWindow, &VcsOutputWindow::appendCommand);
connect(m_command, &VcsCommand::appendMessage, outputWindow, &VcsOutputWindow::appendMessage);
connect(m_command, &VcsCommand::executedAsync, this, &VcsCommandDecorator::addTask);
const auto connection = connect(m_command, &VcsCommand::runCommandFinished,
this, &VcsCommandDecorator::postRunCommand);
connect(ICore::instance(), &ICore::coreAboutToClose, this, [this, connection] {
disconnect(connection);
m_command->abort();
});
}
VcsCommandDecorator::~VcsCommandDecorator()
{
m_futureInterface.reportFinished();
}
void VcsCommandDecorator::addTask(const QFuture<void> &future)
{
if ((m_command->flags() & VcsCommand::SuppressCommandLogging))
return;
const QString name = m_command->displayName();
const auto id = Id::fromString(name + QLatin1String(".action"));
if (m_command->hasProgressParser()) {
ProgressManager::addTask(future, name, id);
} else {
ProgressManager::addTimedTask(m_futureInterface, name, id, qMax(2, m_command->timeoutS() / 5));
}
Internal::VcsPlugin::addFuture(future);
}
void VcsCommandDecorator::postRunCommand(const FilePath &workingDirectory)
{
if (!(m_command->flags() & VcsCommand::ExpectRepoChanges))
return;
// TODO tell the document manager that the directory now received all expected changes
// Core::DocumentManager::unexpectDirectoryChange(d->m_workingDirectory);
VcsManager::emitRepositoryChanged(workingDirectory);
}
VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings) VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings)
: m_baseSettings(baseSettings) : m_baseSettings(baseSettings)
{ {
@@ -250,7 +167,6 @@ CommandResult VcsBaseClientImpl::vcsFullySynchronousExec(const FilePath &working
const CommandLine &cmdLine, unsigned flags, int timeoutS, QTextCodec *codec) const const CommandLine &cmdLine, unsigned flags, int timeoutS, QTextCodec *codec) const
{ {
VcsCommand command(workingDir, processEnvironment()); VcsCommand command(workingDir, processEnvironment());
new VcsCommandDecorator(&command);
command.addFlags(flags); command.addFlags(flags);
if (codec) if (codec)
command.setCodec(codec); command.setCodec(codec);
@@ -297,7 +213,6 @@ CommandResult VcsBaseClientImpl::vcsSynchronousExec(const FilePath &workingDir,
{ {
Environment env = processEnvironment(); Environment env = processEnvironment();
VcsCommand command(workingDir, env.isValid() ? env : Environment::systemEnvironment()); VcsCommand command(workingDir, env.isValid() ? env : Environment::systemEnvironment());
new VcsCommandDecorator(&command);
command.addFlags(flags); command.addFlags(flags);
command.setCodec(outputCodec); command.setCodec(outputCodec);
return command.runCommand({vcsBinary(), args}, workingDir, vcsTimeoutS()); return command.runCommand({vcsBinary(), args}, workingDir, vcsTimeoutS());
@@ -311,9 +226,7 @@ int VcsBaseClientImpl::vcsTimeoutS() const
VcsCommand *VcsBaseClientImpl::createVcsCommand(const FilePath &defaultWorkingDir, VcsCommand *VcsBaseClientImpl::createVcsCommand(const FilePath &defaultWorkingDir,
const Environment &environment) const Environment &environment)
{ {
VcsCommand *command = new VcsCommand(defaultWorkingDir, environment); return new VcsCommand(defaultWorkingDir, environment);
new VcsCommandDecorator(command);
return command;
} }
VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Id kind, QString title, VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Id kind, QString title,

View File

@@ -28,12 +28,11 @@
#include <utils/aspects.h> #include <utils/aspects.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <QComboBox>
#include <QAction> #include <QAction>
#include <QHBoxLayout> #include <QComboBox>
#include <QStringList>
#include <QDebug> #include <QDebug>
#include <QHBoxLayout>
#include <QStringList>
using namespace Utils; using namespace Utils;

View File

@@ -24,9 +24,10 @@
****************************************************************************/ ****************************************************************************/
#include "vcsbaseplugin.h" #include "vcsbaseplugin.h"
#include "commonvcssettings.h"
#include "vcsbasesubmiteditor.h" #include "vcsbasesubmiteditor.h"
#include "vcsplugin.h" #include "vcsplugin.h"
#include "commonvcssettings.h"
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -43,8 +44,8 @@
#include <QDir> #include <QDir>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMessageBox> #include <QMessageBox>
#include <QSharedData>
#include <QScopedPointer> #include <QScopedPointer>
#include <QSharedData>
#include <QSharedPointer> #include <QSharedPointer>
#include <QProcessEnvironment> #include <QProcessEnvironment>
#include <QTextCodec> #include <QTextCodec>

View File

@@ -25,7 +25,15 @@
#include "vcscommand.h" #include "vcscommand.h"
#include "vcsbaseplugin.h"
#include "vcsoutputwindow.h"
#include "vcsplugin.h"
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/globalfilechangeblocker.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
@@ -56,6 +64,7 @@
when a progress string is detected. when a progress string is detected.
*/ */
using namespace Core;
using namespace Utils; using namespace Utils;
namespace VcsBase { namespace VcsBase {
@@ -77,7 +86,9 @@ public:
VcsCommandPrivate(const FilePath &defaultWorkingDirectory, const Environment &environment) VcsCommandPrivate(const FilePath &defaultWorkingDirectory, const Environment &environment)
: m_defaultWorkingDirectory(defaultWorkingDirectory), : m_defaultWorkingDirectory(defaultWorkingDirectory),
m_environment(environment) m_environment(environment)
{} {
VcsBase::setProcessEnvironment(&m_environment);
}
~VcsCommandPrivate() { delete m_progressParser; } ~VcsCommandPrivate() { delete m_progressParser; }
@@ -98,6 +109,7 @@ public:
QTextCodec *m_codec = nullptr; QTextCodec *m_codec = nullptr;
ProgressParser *m_progressParser = nullptr; ProgressParser *m_progressParser = nullptr;
QFutureWatcher<void> m_watcher; QFutureWatcher<void> m_watcher;
QFutureInterface<void> m_futureInterface;
QList<Job> m_jobs; QList<Job> m_jobs;
unsigned m_flags = 0; unsigned m_flags = 0;
@@ -106,7 +118,6 @@ public:
bool m_progressiveOutput = false; bool m_progressiveOutput = false;
bool m_hadOutput = false; bool m_hadOutput = false;
bool m_aborted = false; bool m_aborted = false;
bool m_disableUnixTerminal = false;
}; };
VcsCommandPrivate::Job::Job(const FilePath &wd, const CommandLine &command, VcsCommandPrivate::Job::Job(const FilePath &wd, const CommandLine &command,
@@ -127,10 +138,52 @@ VcsCommand::VcsCommand(const FilePath &workingDirectory, const Environment &envi
d(new Internal::VcsCommandPrivate(workingDirectory, environment)) d(new Internal::VcsCommandPrivate(workingDirectory, environment))
{ {
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, &VcsCommand::cancel); connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, &VcsCommand::cancel);
VcsOutputWindow::setRepository(defaultWorkingDirectory().toString());
VcsOutputWindow *outputWindow = VcsOutputWindow::instance(); // Keep me here, just to be sure it's not instantiated in other thread
connect(this, &VcsCommand::append, outputWindow, [outputWindow](const QString &t) {
outputWindow->append(t);
});
connect(this, &VcsCommand::appendSilently, outputWindow, &VcsOutputWindow::appendSilently);
connect(this, &VcsCommand::appendError, outputWindow, &VcsOutputWindow::appendError);
connect(this, &VcsCommand::appendCommand, outputWindow, &VcsOutputWindow::appendCommand);
connect(this, &VcsCommand::appendMessage, outputWindow, &VcsOutputWindow::appendMessage);
const auto connection = connect(this, &VcsCommand::runCommandFinished,
this, &VcsCommand::postRunCommand);
connect(ICore::instance(), &ICore::coreAboutToClose, this, [this, connection] {
disconnect(connection);
abort();
});
}
void VcsCommand::addTask(const QFuture<void> &future)
{
if ((d->m_flags & VcsCommand::SuppressCommandLogging))
return;
const QString name = displayName();
const auto id = Id::fromString(name + QLatin1String(".action"));
if (d->m_progressParser) {
ProgressManager::addTask(future, name, id);
} else {
ProgressManager::addTimedTask(d->m_futureInterface, name, id, qMax(2, timeoutS() / 5));
}
Internal::VcsPlugin::addFuture(future);
}
void VcsCommand::postRunCommand(const FilePath &workingDirectory)
{
if (!(d->m_flags & VcsCommand::ExpectRepoChanges))
return;
// TODO tell the document manager that the directory now received all expected changes
// Core::DocumentManager::unexpectDirectoryChange(d->m_workingDirectory);
VcsManager::emitRepositoryChanged(workingDirectory);
} }
VcsCommand::~VcsCommand() VcsCommand::~VcsCommand()
{ {
d->m_futureInterface.reportFinished();
delete d; delete d;
} }
@@ -164,16 +217,6 @@ const FilePath &VcsCommand::defaultWorkingDirectory() const
return d->m_defaultWorkingDirectory; return d->m_defaultWorkingDirectory;
} }
Environment VcsCommand::environment() const
{
return d->m_environment;
}
void VcsCommand::setEnvironment(const Environment &env)
{
d->m_environment = env;
}
int VcsCommand::defaultTimeoutS() const int VcsCommand::defaultTimeoutS() const
{ {
return d->m_defaultTimeoutS; return d->m_defaultTimeoutS;
@@ -184,11 +227,6 @@ void VcsCommand::setDefaultTimeoutS(int timeout)
d->m_defaultTimeoutS = timeout; d->m_defaultTimeoutS = timeout;
} }
unsigned VcsCommand::flags() const
{
return d->m_flags;
}
void VcsCommand::addFlags(unsigned f) void VcsCommand::addFlags(unsigned f)
{ {
d->m_flags |= f; d->m_flags |= f;
@@ -216,7 +254,7 @@ void VcsCommand::execute()
QFuture<void> task = runAsync(&VcsCommand::run, this); QFuture<void> task = runAsync(&VcsCommand::run, this);
d->m_watcher.setFuture(task); d->m_watcher.setFuture(task);
emit executedAsync(task); addTask(task);
} }
void VcsCommand::abort() void VcsCommand::abort()
@@ -253,7 +291,11 @@ void VcsCommand::run(QFutureInterface<void> &future)
QString stdOut; QString stdOut;
QString stdErr; QString stdErr;
emit started(); if (d->m_flags & VcsCommand::ExpectRepoChanges) {
QMetaObject::invokeMethod(this, [] {
GlobalFileChangeBlocker::instance()->forceBlocked(true);
});
}
if (d->m_progressParser) if (d->m_progressParser)
d->m_progressParser->setFuture(&future); d->m_progressParser->setFuture(&future);
else else
@@ -278,6 +320,11 @@ void VcsCommand::run(QFutureInterface<void> &future)
emit stdErrText(stdErr); emit stdErrText(stdErr);
} }
if (d->m_flags & VcsCommand::ExpectRepoChanges) {
QMetaObject::invokeMethod(this, [] {
GlobalFileChangeBlocker::instance()->forceBlocked(false);
});
}
emit finished(lastExecSuccess, cookie()); emit finished(lastExecSuccess, cookie());
if (lastExecSuccess) if (lastExecSuccess)
future.setProgressValue(future.progressMaximum()); future.setProgressValue(future.progressMaximum());
@@ -292,7 +339,7 @@ void VcsCommand::run(QFutureInterface<void> &future)
} }
CommandResult VcsCommand::runCommand(const CommandLine &command, const FilePath &workingDirectory, CommandResult VcsCommand::runCommand(const CommandLine &command, const FilePath &workingDirectory,
int timeoutS, const ExitCodeInterpreter &interpreter) int timeoutS, const ExitCodeInterpreter &interpreter)
{ {
QtcProcess proc; QtcProcess proc;
if (command.executable().isEmpty()) if (command.executable().isEmpty())
@@ -309,8 +356,7 @@ CommandResult VcsCommand::runCommand(const CommandLine &command, const FilePath
emit appendCommand(dir, command); emit appendCommand(dir, command);
proc.setCommand(command); proc.setCommand(command);
if (d->m_disableUnixTerminal) proc.setDisableUnixTerminal();
proc.setDisableUnixTerminal();
proc.setEnvironment(d->environment()); proc.setEnvironment(d->environment());
if (d->m_flags & MergeOutputChannels) if (d->m_flags & MergeOutputChannels)
proc.setProcessChannelMode(QProcess::MergedChannels); proc.setProcessChannelMode(QProcess::MergedChannels);
@@ -404,11 +450,6 @@ void VcsCommand::setCookie(const QVariant &cookie)
d->m_cookie = cookie; d->m_cookie = cookie;
} }
QTextCodec *VcsCommand::codec() const
{
return d->m_codec;
}
void VcsCommand::setCodec(QTextCodec *codec) void VcsCommand::setCodec(QTextCodec *codec)
{ {
d->m_codec = codec; d->m_codec = codec;
@@ -421,21 +462,11 @@ void VcsCommand::setProgressParser(ProgressParser *parser)
d->m_progressParser = parser; d->m_progressParser = parser;
} }
bool VcsCommand::hasProgressParser() const
{
return d->m_progressParser;
}
void VcsCommand::setProgressiveOutput(bool progressive) void VcsCommand::setProgressiveOutput(bool progressive)
{ {
d->m_progressiveOutput = progressive; d->m_progressiveOutput = progressive;
} }
void VcsCommand::setDisableUnixTerminal()
{
d->m_disableUnixTerminal = true;
}
ProgressParser::ProgressParser() : ProgressParser::ProgressParser() :
m_futureMutex(new QMutex) m_futureMutex(new QMutex)
{ } { }

View File

@@ -36,9 +36,9 @@ QT_BEGIN_NAMESPACE
class QMutex; class QMutex;
class QVariant; class QVariant;
template <typename T> template <typename T>
class QFutureInterface;
template <typename T>
class QFuture; class QFuture;
template <typename T>
class QFutureInterface;
class QTextCodec; class QTextCodec;
QT_END_NAMESPACE QT_END_NAMESPACE
@@ -119,18 +119,13 @@ public:
NoOutput = SuppressStdErr | SuppressFailMessage | SuppressCommandLogging NoOutput = SuppressStdErr | SuppressFailMessage | SuppressCommandLogging
}; };
VcsCommand(const Utils::FilePath &workingDirectory, const Utils::Environment &environment); VcsCommand(const Utils::FilePath &workingDirectory, const Utils::Environment &environment);
~VcsCommand() override; ~VcsCommand() override;
QString displayName() const;
void setDisplayName(const QString &name); void setDisplayName(const QString &name);
const Utils::FilePath &defaultWorkingDirectory() const; const Utils::FilePath &defaultWorkingDirectory() const;
Utils::Environment environment() const;
void setEnvironment(const Utils::Environment &env);
void addJob(const Utils::CommandLine &command, void addJob(const Utils::CommandLine &command,
const Utils::FilePath &workingDirectory = {}, const Utils::FilePath &workingDirectory = {},
const Utils::ExitCodeInterpreter &interpreter = {}); const Utils::ExitCodeInterpreter &interpreter = {});
@@ -143,17 +138,14 @@ public:
int defaultTimeoutS() const; int defaultTimeoutS() const;
void setDefaultTimeoutS(int timeout); void setDefaultTimeoutS(int timeout);
unsigned flags() const;
void addFlags(unsigned f); void addFlags(unsigned f);
const QVariant &cookie() const; const QVariant &cookie() const;
void setCookie(const QVariant &cookie); void setCookie(const QVariant &cookie);
QTextCodec *codec() const;
void setCodec(QTextCodec *codec); void setCodec(QTextCodec *codec);
void setProgressParser(ProgressParser *parser); void setProgressParser(ProgressParser *parser);
bool hasProgressParser() const;
void setProgressiveOutput(bool progressive); void setProgressiveOutput(bool progressive);
// This is called once per job in a thread. // This is called once per job in a thread.
@@ -162,16 +154,11 @@ public:
CommandResult runCommand(const Utils::CommandLine &command, CommandResult runCommand(const Utils::CommandLine &command,
const Utils::FilePath &workingDirectory = {}, const Utils::FilePath &workingDirectory = {},
int timeoutS = 10, const Utils::ExitCodeInterpreter &interpreter = {}); int timeoutS = 10, const Utils::ExitCodeInterpreter &interpreter = {});
void cancel(); void cancel();
void setDisableUnixTerminal();
int timeoutS() const;
signals: signals:
void stdOutText(const QString &); void stdOutText(const QString &);
void stdErrText(const QString &); void stdErrText(const QString &);
void started();
void finished(bool success, const QVariant &cookie); void finished(bool success, const QVariant &cookie);
void terminate(); // Internal void terminate(); // Internal
@@ -179,16 +166,19 @@ signals:
void append(const QString &text); void append(const QString &text);
void appendSilently(const QString &text); void appendSilently(const QString &text);
void appendError(const QString &text); void appendError(const QString &text);
// TODO: remove Utils:: scope when support for Qt5 is dropped (Creator 9.0)
void appendCommand(const Utils::FilePath &workingDirectory, const Utils::CommandLine &command); void appendCommand(const Utils::FilePath &workingDirectory, const Utils::CommandLine &command);
void appendMessage(const QString &text); void appendMessage(const QString &text);
void executedAsync(const QFuture<void> &future);
void runCommandFinished(const Utils::FilePath &workingDirectory); void runCommandFinished(const Utils::FilePath &workingDirectory);
private: private:
Utils::FilePath workDirectory(const Utils::FilePath &wd) const; Utils::FilePath workDirectory(const Utils::FilePath &wd) const;
void run(QFutureInterface<void> &future); void run(QFutureInterface<void> &future);
void addTask(const QFuture<void> &future);
void postRunCommand(const Utils::FilePath &workingDirectory);
QString displayName() const;
int timeoutS() const;
// Run without a event loop in fully blocking mode. No signals will be delivered. // Run without a event loop in fully blocking mode. No signals will be delivered.
void runFullySynchronous(Utils::QtcProcess &proc); void runFullySynchronous(Utils::QtcProcess &proc);

View File

@@ -25,16 +25,18 @@
#include "vcsoutputwindow.h" #include "vcsoutputwindow.h"
#include <coreplugin/editormanager/editormanager.h> #include "vcsoutputformatter.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/outputwindow.h> #include <coreplugin/outputwindow.h>
#include <utils/fileutils.h>
#include <utils/qtcprocess.h>
#include <texteditor/behaviorsettings.h> #include <texteditor/behaviorsettings.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <utils/fileutils.h>
#include <utils/qtcprocess.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#include <vcsbase/vcsoutputformatter.h>
#include <QAction> #include <QAction>
#include <QContextMenuEvent> #include <QContextMenuEvent>

View File

@@ -27,7 +27,7 @@
#include "vcsbase_global.h" #include "vcsbase_global.h"
#include <coreplugin/ioutputpane.h> #include <coreplugin/ioutputpane.h>
namespace Utils { namespace Utils {
class CommandLine; class CommandLine;

View File

@@ -25,11 +25,10 @@
#include "vcsplugin.h" #include "vcsplugin.h"
#include "vcsbaseconstants.h"
#include "vcsbasesubmiteditor.h"
#include "commonvcssettings.h" #include "commonvcssettings.h"
#include "nicknamedialog.h" #include "nicknamedialog.h"
#include "vcsbaseconstants.h"
#include "vcsbasesubmiteditor.h"
#include "vcsoutputwindow.h" #include "vcsoutputwindow.h"
#include "wizard/vcscommandpage.h" #include "wizard/vcscommandpage.h"
#include "wizard/vcsconfigurationpage.h" #include "wizard/vcsconfigurationpage.h"