/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mod Organizer. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace MOBase
{
TutorialControl::TutorialControl(const TutorialControl& reference)
: QObject(reference.parent()), m_TargetControl(reference.m_TargetControl),
m_Name(reference.m_Name), m_TutorialView(nullptr),
m_Manager(TutorialManager::instance()), m_ExpectedTab(0),
m_CurrentClickControl(nullptr)
{}
TutorialControl::TutorialControl(QWidget* targetControl, const QString& name)
: QObject(nullptr), m_TargetControl(targetControl), m_Name(name),
m_TutorialView(nullptr), m_Manager(TutorialManager::instance()), m_ExpectedTab(0),
m_CurrentClickControl(nullptr)
{}
TutorialControl::~TutorialControl()
{
m_Manager.unregisterControl(m_Name);
finish();
}
void TutorialControl::registerControl()
{
m_Manager.registerControl(m_Name, this);
}
void TutorialControl::resize(const QSize& size)
{
if (m_TutorialView != nullptr) {
m_TutorialView->resize(size.width(), size.height());
}
}
void TutorialControl::expose(const QString& widgetName, QObject* widget)
{
m_ExposedObjects.push_back(std::make_pair(widgetName, widget));
}
static QString canonicalPath(const QString& path)
{
#ifdef _WIN32
std::unique_ptr buffer(new wchar_t[32768]);
DWORD res = ::GetShortPathNameW((wchar_t*)path.utf16(), buffer.get(), 32768);
if (res == 0) {
return path;
}
res = ::GetLongPathNameW(buffer.get(), buffer.get(), 32768);
if (res == 0) {
return path;
}
return QString::fromWCharArray(buffer.get());
#else
QFileInfo fi(path);
QString canonical = fi.canonicalFilePath();
return canonical.isEmpty() ? path : canonical;
#endif
}
void TutorialControl::startTutorial(const QString& tutorial)
{
if (m_TutorialView == nullptr) {
m_TutorialView = new QQuickWidget(m_TargetControl);
m_TutorialView->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_TutorialView->setAttribute(Qt::WA_TranslucentBackground);
m_TutorialView->setAttribute(Qt::WA_AlwaysStackOnTop);
m_TutorialView->setClearColor(Qt::transparent);
m_TutorialView->setStyleSheet("background: transparent");
m_TutorialView->setObjectName("tutorialView");
m_TutorialView->rootContext()->setContextProperty("manager", &m_Manager);
QString qmlName =
canonicalPath(QCoreApplication::applicationDirPath() + "/tutorials") +
"/tutorials_" + m_Name.toLower() + ".qml";
QUrl qmlSource = QUrl::fromLocalFile(qmlName);
m_TutorialView->setSource(qmlSource);
m_TutorialView->resize(m_TargetControl->width(), m_TargetControl->height());
m_TutorialView->rootContext()->setContextProperty("scriptName", tutorial);
m_TutorialView->rootContext()->setContextProperty("tutorialControl", this);
m_TutorialView->rootContext()->setContextProperty("applicationWindow",
m_TargetControl);
m_TutorialView->rootContext()->setContextProperty("organizer",
m_Manager.organizerCore());
for (std::vector>::const_iterator iter =
m_ExposedObjects.begin();
iter != m_ExposedObjects.end(); ++iter) {
m_TutorialView->rootContext()->setContextProperty(iter->first, iter->second);
}
m_TutorialView->show();
m_TutorialView->raise();
if (!QMetaObject::invokeMethod(m_TutorialView->rootObject(), "init")) {
reportError(tr(
"Tutorial failed to start, please check \"mo_interface.log\" for details."));
m_TutorialView->close();
}
}
}
void TutorialControl::lockUI(bool locked)
{
m_TutorialView->setAttribute(Qt::WA_TransparentForMouseEvents, !locked);
QMetaObject::invokeMethod(m_TutorialView->rootObject(), "enableBackground",
Q_ARG(QVariant, QVariant(locked)));
}
void TutorialControl::simulateClick(int x, int y)
{
bool wasTransparent = m_TutorialView->testAttribute(Qt::WA_TransparentForMouseEvents);
if (!wasTransparent) {
m_TutorialView->setAttribute(Qt::WA_TransparentForMouseEvents, true);
}
QWidget* hitControl = m_TargetControl->childAt(x, y);
QPoint globalPos = m_TargetControl->mapToGlobal(QPoint(x, y));
QPoint hitPos = hitControl->mapFromGlobal(globalPos);
QMouseEvent* downEvent = new QMouseEvent(
QEvent::MouseButtonPress, QPointF(hitPos), QPointF(globalPos),
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QMouseEvent* upEvent =
new QMouseEvent(QEvent::MouseButtonRelease, QPointF(hitPos), QPointF(globalPos),
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
qApp->postEvent(hitControl, (QEvent*)downEvent);
qApp->postEvent(hitControl, (QEvent*)upEvent);
if (!wasTransparent) {
m_TutorialView->setAttribute(Qt::WA_TransparentForMouseEvents, false);
}
}
QWidget* TutorialControl::getChild(const QString& name)
{
if (m_TargetControl != nullptr) {
return m_TargetControl->findChild(name);
} else {
return nullptr;
}
}
void TutorialControl::finish()
{
if (m_TutorialView != nullptr) {
m_TutorialView->deleteLater();
}
m_TutorialView = nullptr;
m_TutorialView = nullptr;
}
QRect TutorialControl::getRect(const QString& widgetName)
{
if (m_TargetControl != nullptr) {
QWidget* widget = m_TargetControl->findChild(widgetName);
if (widget != nullptr) {
QRect res = widget->rect();
QPoint pos = widget->mapTo(m_TargetControl, res.topLeft());
res.moveTopLeft(pos);
return res;
} else {
log::error("{} not found", widgetName);
return QRect();
}
} else {
return QRect();
}
}
QRect TutorialControl::getActionRect(const QString& widgetName)
{
if (m_TargetControl != nullptr) {
QToolBar* toolBar = m_TargetControl->findChild("toolBar");
foreach (QAction* action, toolBar->actions()) {
if (action->objectName() == widgetName) {
return toolBar->actionGeometry(action);
}
}
}
return QRect();
}
QRect TutorialControl::getMenuRect(const QString&)
{
if (m_TargetControl != nullptr) {
QMenuBar* menuBar = m_TargetControl->findChild("menuBar");
return menuBar->geometry();
}
return QRect();
}
void TutorialControl::nextTutorialStepProxy()
{
if (m_TutorialView != nullptr) {
QObject* background = m_TutorialView->rootObject();
QTimer::singleShot(1, background, SLOT(nextStep()));
lockUI(true);
bool success = true;
for (QMetaObject::Connection connection : m_Connections) {
if (!disconnect(connection)) {
success = false;
}
}
m_Connections.clear();
if (!success) {
log::error("failed to disconnect tutorial proxy");
}
} else {
log::error("failed to proceed to next tutorial step");
finish();
}
}
void TutorialControl::tabChangedProxy(int selected)
{
if ((m_TutorialView != nullptr) && (selected == m_ExpectedTab)) {
QObject* background = m_TutorialView->rootObject();
QTimer::singleShot(1, background, SLOT(nextStep()));
lockUI(true);
bool success = true;
for (QMetaObject::Connection connection : m_Connections) {
if (!disconnect(connection)) {
success = false;
}
}
m_Connections.clear();
if (!success) {
log::error("failed to disconnect tab-changed proxy");
}
}
}
bool TutorialControl::waitForAction(const QString& actionName)
{
if (m_TargetControl != nullptr) {
QAction* action = m_TargetControl->findChild(actionName);
if (action == nullptr) {
log::error("no action \"{}\" in control \"{}\"", actionName, m_Name);
return false;
}
if (action->isEnabled()) {
if (action->menu() != nullptr) {
m_Connections.append(connect(action->menu(), SIGNAL(aboutToShow()), this,
SLOT(nextTutorialStepProxy())));
} else {
m_Connections.append(
connect(action, SIGNAL(triggered()), this, SLOT(nextTutorialStepProxy())));
}
lockUI(false);
return true;
} else {
return false;
}
} else {
return false;
}
}
bool TutorialControl::waitForButton(const QString& buttonName)
{
if (m_TargetControl != nullptr) {
QAbstractButton* button = m_TargetControl->findChild(buttonName);
if (button == nullptr) {
log::error("no button \"{}\" in control \"{}\"", buttonName, m_Name);
return false;
}
if (button->isEnabled()) {
m_Connections.append(
connect(button, SIGNAL(pressed()), this, SLOT(nextTutorialStepProxy())));
lockUI(false);
return true;
} else {
return false;
}
} else {
return false;
}
}
bool TutorialControl::waitForTabOpen(const QString& tabControlName, const QString& tab)
{
if (m_TargetControl != nullptr) {
QTabWidget* tabWidget = m_TargetControl->findChild(tabControlName);
if (tabWidget == nullptr) {
log::error("no tab widget \"{}\" in control \"{}\"", tabControlName, m_Name);
return false;
}
if (tabWidget->findChild(tab) == nullptr) {
log::error("no widget \"{}\" found in tab widget \"{}\"", tab, tabControlName);
return false;
}
int tabIndex = tabWidget->indexOf(tabWidget->findChild(tab));
if (tabIndex == -1) {
log::error("widget \"{}\" does not appear to be a tab in tab widget \"{}\"", tab,
tabControlName);
return false;
}
if (tabWidget->isEnabled()) {
if (tabWidget->currentIndex() != tabIndex) {
m_ExpectedTab = tabIndex;
m_Connections.append(connect(tabWidget, SIGNAL(currentChanged(int)), this,
SLOT(tabChangedProxy(int))));
lockUI(false);
} else {
QObject* background = m_TutorialView->rootObject();
QTimer::singleShot(1, background, SLOT(nextStep()));
lockUI(true);
}
return true;
} else {
return false;
}
} else {
return false;
}
}
const QString TutorialControl::getTabName(const QString& tabControlName)
{
if (m_TargetControl != nullptr) {
QTabWidget* tabWidget = m_TargetControl->findChild(tabControlName);
if (tabWidget == nullptr) {
log::error("no tab widget \"{}\" in control \"{}\"", tabControlName, m_Name);
return QString();
}
if (tabWidget->currentIndex() == -1) {
return QString();
} else {
return tabWidget->currentWidget()->objectName();
}
} else {
return QString();
}
}
} // namespace MOBase