From 981f8b3acf7e76f27c02f4ced80d55b424cc7497 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 3 Feb 2013 12:49:25 +0100 Subject: initial commit to mercurial repository. Corresponds to MO version 0.12.6 --- src/fomodinstallerdialog.cpp | 939 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 939 insertions(+) create mode 100644 src/fomodinstallerdialog.cpp (limited to 'src/fomodinstallerdialog.cpp') diff --git a/src/fomodinstallerdialog.cpp b/src/fomodinstallerdialog.cpp new file mode 100644 index 00000000..16f6db60 --- /dev/null +++ b/src/fomodinstallerdialog.cpp @@ -0,0 +1,939 @@ +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "fomodinstallerdialog.h" +#include "report.h" +#include "utility.h" +#include "ui_fomodinstallerdialog.h" + +#include +#include +#include +#include +#include +#include +#include + + +bool ControlsAscending(QAbstractButton *LHS, QAbstractButton *RHS) +{ + return LHS->text() < RHS->text(); +} + + +bool ControlsDescending(QAbstractButton *LHS, QAbstractButton *RHS) +{ + return LHS->text() > RHS->text(); +} + + +bool PagesAscending(QGroupBox *LHS, QGroupBox *RHS) +{ + return LHS->title() < RHS->title(); +} + + +bool PagesDescending(QGroupBox *LHS, QGroupBox *RHS) +{ + return LHS->title() > RHS->title(); +} + + +FomodInstallerDialog::FomodInstallerDialog(const QString &modName, const QString &fomodPath, QWidget *parent) + : QDialog(parent), ui(new Ui::FomodInstallerDialog), m_FomodPath(fomodPath), m_Manual(false) +{ + ui->setupUi(this); + ui->nameEdit->setText(modName); +} + +FomodInstallerDialog::~FomodInstallerDialog() +{ + delete ui; +} + +#pragma message("implement module dependencies->file dependencies") + +void FomodInstallerDialog::initData() +{ + { // parse provided package information + QFile file(QDir::tempPath().append("/info.xml")); + if (file.open(QIODevice::ReadOnly)) { + // nmm allows files with wrong encoding and of course there are now files with broken + // so, let's do as nmm does and ignore the standard. yay + QByteArray header = file.readLine(); + if (strncmp(header.constData(), "screenshotLabel->width()); + ui->screenshotLabel->setPixmap(QPixmap::fromImage(screenshot)); + } + + { // parse xml installer file + QFile file(QDir::tempPath().append("/ModuleConfig.xml")); + if (!file.open(QIODevice::ReadOnly)) { + throw MyException(tr("ModuleConfig.xml missing")); + } + // nmm allows files with wrong encoding and of course there are now files that are broken + QByteArray header = file.readLine(); + if (strncmp(header.constData(), "nameEdit->text(); +} + + +void FomodInstallerDialog::moveTree(DirectoryTree::Node *target, DirectoryTree::Node *source) +{ + for (DirectoryTree::const_node_iterator iter = source->nodesBegin(); iter != source->nodesEnd();) { + target->addNode(*iter, true); + iter = source->detach(iter); + } + + for (DirectoryTree::const_leaf_reverse_iterator iter = source->leafsRBegin(); + iter != source->leafsREnd(); ++iter) { + target->addLeaf(*iter); + } +} + + +DirectoryTree::Node *FomodInstallerDialog::findNode(DirectoryTree::Node *node, const QString &path, bool create) +{ + if (path.length() == 0) { + return node; + } + +// static QRegExp pathSeparator("[/\\]"); + int pos = path.indexOf('\\'); + if (pos == -1) { + pos = path.indexOf('/'); + } + QString subPath = path; + if (pos > 0) { + subPath = path.mid(0, pos); + } + for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) { + if ((*iter)->getData().name.compare(subPath, Qt::CaseInsensitive) == 0) { + if (pos <= 0) { + return *iter; + } else { + return findNode(*iter, path.mid(pos + 1), create); + } + } + } + if (create) { + DirectoryTree::Node *newNode = new DirectoryTree::Node; + newNode->setData(subPath); + node->addNode(newNode, false); + if (pos <= 0) { + return newNode; + } else { + return findNode(newNode, path.mid(pos + 1), create); + } + } else { + throw MyException(QString("%1 not found in archive").arg(path)); + } +} + +void FomodInstallerDialog::copyLeaf(DirectoryTree::Node *sourceTree, const QString &sourcePath, + DirectoryTree::Node *destinationTree, const QString &destinationPath) +{ + int sourceFileIndex = sourcePath.lastIndexOf('\\'); + if (sourceFileIndex == -1) { + sourceFileIndex = sourcePath.lastIndexOf('/'); + if (sourceFileIndex == -1) { + sourceFileIndex = 0; + } + } + DirectoryTree::Node *sourceNode = sourceFileIndex == 0 ? sourceTree : findNode(sourceTree, sourcePath.mid(0, sourceFileIndex), false); + + int destinationFileIndex = destinationPath.lastIndexOf('\\'); + if (destinationFileIndex == -1) { + destinationFileIndex = destinationPath.lastIndexOf('/'); + if (destinationFileIndex == -1) { + destinationFileIndex = 0; + } + } + + DirectoryTree::Node *destinationNode = + destinationFileIndex == 0 ? destinationTree + : findNode(destinationTree, destinationPath.mid(0, destinationFileIndex), true); + + QString sourceName = sourcePath.mid((sourceFileIndex != 0) ? sourceFileIndex + 1 : 0); + QString destinationName = (destinationFileIndex != 0) ? destinationPath.mid(destinationFileIndex + 1) : destinationPath; + if (destinationName.length() == 0) { + destinationName = sourceName; + } + + bool found = false; + for (DirectoryTree::const_leaf_reverse_iterator iter = sourceNode->leafsRBegin(); + iter != sourceNode->leafsREnd(); ++iter) { + if (iter->getName().compare(sourceName, Qt::CaseInsensitive) == 0) { + destinationNode->addLeaf(*iter); + found = true; + } + } + if (!found) { + qCritical("%s not found!", sourceName.toUtf8().constData()); + } +} + + +void dumpTree(DirectoryTree::Node *node, int indent) +{ + for (DirectoryTree::const_leaf_reverse_iterator iter = node->leafsRBegin(); + iter != node->leafsREnd(); ++iter) { + qDebug("%.*s%s", indent, " ", iter->getName().toUtf8().constData()); + } + + for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) { + qDebug("%.*s-- %s", indent, " ", (*iter)->getData().name.toUtf8().constData()); + dumpTree(*iter, indent + 2); + } +} + + +bool FomodInstallerDialog::copyFileIterator(DirectoryTree *sourceTree, DirectoryTree *destinationTree, FileDescriptor *descriptor) +{ + QString source = (m_FomodPath.length() != 0) ? m_FomodPath.mid(0).append("\\").append(descriptor->m_Source) + : descriptor->m_Source; + QString destination = descriptor->m_Destination; + try { + if (descriptor->m_IsFolder) { + DirectoryTree::Node *sourceNode = findNode(sourceTree, source, false); + DirectoryTree::Node *targetNode = findNode(destinationTree, destination, true); + moveTree(targetNode, sourceNode); + } else { + copyLeaf(sourceTree, source, destinationTree, destination); + } + return true; + } catch (const MyException &e) { + qCritical("failed to extract %s to %s: %s", + source.toUtf8().constData(), destination.toUtf8().constData(), e.what()); + return false; + } +} + + +DirectoryTree *FomodInstallerDialog::updateTree(DirectoryTree *tree) +{ + DirectoryTree *newTree = new DirectoryTree; + + for (std::vector::iterator iter = m_RequiredFiles.begin(); iter != m_RequiredFiles.end(); ++iter) { + copyFileIterator(tree, newTree, *iter); + } + + for (std::vector::iterator installIter = m_ConditionalInstalls.begin(); + installIter != m_ConditionalInstalls.end(); ++installIter) { + bool match = installIter->m_Operator == ConditionalInstall::OP_AND; + for (std::vector::iterator conditionIter = installIter->m_Conditions.begin(); + conditionIter != installIter->m_Conditions.end(); ++conditionIter) { + bool conditionMatches = testCondition(ui->stepsStack->count(), conditionIter->m_Name, conditionIter->m_Value); + if (conditionMatches && (installIter->m_Operator == ConditionalInstall::OP_OR)) { + match = true; + break; + } else if (!conditionMatches && (installIter->m_Operator == ConditionalInstall::OP_AND)) { + match = false; + break; + } + } + if (match) { + for (std::vector::iterator fileIter = installIter->m_Files.begin(); + fileIter != installIter->m_Files.end(); ++fileIter) { + copyFileIterator(tree, newTree, *fileIter); + } + } + } + + QList choices = ui->stepsStack->findChildren("choice"); + foreach (QAbstractButton* choice, choices) { + if (choice->isChecked()) { + QVariantList fileList = choice->property("files").toList(); + foreach (QVariant fileVariant, fileList) { + copyFileIterator(tree, newTree, fileVariant.value()); + } + } + } + +// dumpTree(newTree, 0); + + return newTree; +} + + +void FomodInstallerDialog::highlightControl(QAbstractButton *button) +{ + QVariant screenshotName = button->property("screenshot"); + if (screenshotName.isValid()) { + QString screenshotFileName = screenshotName.toString(); + if (!screenshotFileName.isEmpty()) { + QString temp = QFileInfo(screenshotFileName).fileName(); + QImage screenshot(QDir::tempPath().append("/").append(temp)); + if (screenshot.isNull()) { + qWarning(">%s< is a null image", screenshotName.toString().toUtf8().constData()); + } + screenshot = screenshot.scaledToWidth(ui->screenshotLabel->width()); + ui->screenshotLabel->setPixmap(QPixmap::fromImage(screenshot)); + } else { + ui->screenshotLabel->setPixmap(QPixmap()); + } + } + ui->descriptionText->setText(button->property("description").toString()); +} + + +bool FomodInstallerDialog::eventFilter(QObject *object, QEvent *event) +{ + QAbstractButton *button = qobject_cast(object); + if ((button != NULL) && (event->type() == QEvent::HoverEnter)) { + highlightControl(button); + + } + return QDialog::eventFilter(object, event); +} + + +QString FomodInstallerDialog::readContent(QXmlStreamReader &reader) +{ + if (reader.readNext() == QXmlStreamReader::Characters) { + return reader.text().toString(); + } else { + return QString(); + } +} + + +void FomodInstallerDialog::parseInfo(const QByteArray &data) +{ + QXmlStreamReader reader(data); +/* while (reader.readNext() != QXmlStreamReader::StartDocument) {} + QTextDecoder *decoder = QTextCodec::codecForName(reader.documentEncoding().toLocal8Bit())->makeDecoder(QTextCodec::ConvertInvalidToNull); + QString test(decoder->toUnicode(data)); + qDebug("test: %d, %s", test.isNull(), test.toUtf8().constData()); + + qDebug(">%s<", reader.documentEncoding().toUtf8().constData());*/ + while (!reader.atEnd() && !reader.hasError()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: { + if (reader.name() == "Name") { + ui->nameEdit->setText(readContent(reader)); + } else if (reader.name() == "Author") { + ui->authorLabel->setText(readContent(reader)); + } else if (reader.name() == "Version") { + ui->versionLabel->setText(readContent(reader)); + } else if (reader.name() == "Website") { + QString url = readContent(reader); + ui->websiteLabel->setText(tr("Link").arg(url)); + ui->websiteLabel->setToolTip(url); + } + } break; + default: {} break; + } + } + if (reader.hasError()) { + throw MyException(tr("failed to parse info.xml: %1 (%2) (line %3, column %4)") + .arg(reader.errorString()) + .arg(reader.error()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber())); + + } +} + + +FomodInstallerDialog::ItemOrder FomodInstallerDialog::getItemOrder(const QString &orderString) +{ + if (orderString == "Ascending") { + return ORDER_ASCENDING; + } else if (orderString == "Descending") { + return ORDER_DESCENDING; + } else if (orderString == "Explicit") { + return ORDER_EXPLICIT; + } else { + throw MyException(tr("unsupported order type %1").arg(orderString)); + } +} + + +FomodInstallerDialog::GroupType FomodInstallerDialog::getGroupType(const QString &typeString) +{ + if (typeString == "SelectAtLeastOne") { + return TYPE_SELECTATLEASTONE; + } else if (typeString == "SelectAtMostOne") { + return TYPE_SELECTATMOSTONE; + } else if (typeString == "SelectExactlyOne") { + return TYPE_SELECTEXACTLYONE; + } else if (typeString == "SelectAny") { + return TYPE_SELECTANY; + } else if (typeString == "SelectAll") { + return TYPE_SELECTALL; + } else { + throw MyException(tr("unsupported group type %1").arg(typeString)); + } +} + + +FomodInstallerDialog::PluginType FomodInstallerDialog::getPluginType(const QString &typeString) +{ + if (typeString == "Required") { + return FomodInstallerDialog::TYPE_REQUIRED; + } else if (typeString == "Optional") { + return FomodInstallerDialog::TYPE_OPTIONAL; + } else if (typeString == "Recommended") { + return FomodInstallerDialog::TYPE_RECOMMENDED; + } else if (typeString == "NotUsable") { + return FomodInstallerDialog::TYPE_NOTUSABLE; + } else if (typeString == "CouldBeUsable") { + return FomodInstallerDialog::TYPE_COULDBEUSABLE; + } else { + qCritical("invalid plugin type %s", typeString.toUtf8().constData()); + return FomodInstallerDialog::TYPE_OPTIONAL; + } +} + + +void FomodInstallerDialog::readFileList(QXmlStreamReader &reader, std::vector &fileList) +{ + QStringRef openTag = reader.name(); + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == openTag))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if ((reader.name() == "folder") || + (reader.name() == "file")) { + QXmlStreamAttributes attributes = reader.attributes(); + FileDescriptor *file = new FileDescriptor(this); + file->m_Source = attributes.value("source").toString(); + file->m_Destination = attributes.hasAttribute("destination") ? attributes.value("destination").toString() + : file->m_Source; + file->m_Priority = attributes.hasAttribute("priority") ? attributes.value("priority").string()->toInt() + : 0; + file->m_IsFolder = reader.name() == "folder"; + file->m_InstallIfUsable = attributes.hasAttribute("installIfUsable") ? (attributes.value("installIfUsable").compare("true") == 0) + : false; + file->m_AlwaysInstall = attributes.hasAttribute("alwaysInstall") ? (attributes.value("alwaysInstall").compare("true") == 0) + : false; + + fileList.push_back(file); + } + } + } +} + + +void FomodInstallerDialog::readPluginType(QXmlStreamReader &reader, Plugin &plugin) +{ + plugin.m_Type = TYPE_OPTIONAL; + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "typeDescriptor"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "type") { + plugin.m_Type = getPluginType(reader.attributes().value("name").toString()); + } + } + } +} + + +void FomodInstallerDialog::readConditionFlags(QXmlStreamReader &reader, Plugin &plugin) +{ + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "conditionFlags"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "flag") { + QString name = reader.attributes().value("name").toString(); + plugin.m_Conditions.push_back(Condition(name, readContent(reader))); + } + } + } +} + + +bool FomodInstallerDialog::byPriority(const FileDescriptor *LHS, const FileDescriptor *RHS) +{ + return LHS->m_Priority < RHS->m_Priority; +} + + +FomodInstallerDialog::Plugin FomodInstallerDialog::readPlugin(QXmlStreamReader &reader) +{ + Plugin result; + result.m_Name = reader.attributes().value("name").toString(); + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "plugin"))) { +// QXmlStreamReader::TokenType type = reader.tokenType(); +// QString name = reader.name().toUtf8(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "description") { + result.m_Description = readContent(reader).trimmed(); + } else if (reader.name() == "image") { + result.m_ImagePath = reader.attributes().value("path").toString(); + } else if (reader.name() == "typeDescriptor") { + readPluginType(reader, result); + } else if (reader.name() == "conditionFlags") { + readConditionFlags(reader, result); + } else if (reader.name() == "files") { + readFileList(reader, result.m_Files); + } + } + } + + std::sort(result.m_Files.begin(), result.m_Files.end(), byPriority); + + return result; +} + + +void FomodInstallerDialog::readPlugins(QXmlStreamReader &reader, GroupType groupType, QLayout *layout) +{ + ItemOrder pluginOrder = reader.attributes().hasAttribute("order") ? getItemOrder(reader.attributes().value("order").toString()) + : ORDER_ASCENDING; + bool first = true; + bool maySelectMore = true; + + std::vector controls; + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "plugins"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "plugin") { + Plugin plugin = readPlugin(reader); + QAbstractButton *newControl = NULL; + switch (groupType) { + case TYPE_SELECTATLEASTONE: + case TYPE_SELECTANY: { + newControl = new QCheckBox(plugin.m_Name); + } break; + case TYPE_SELECTATMOSTONE: { + newControl = new QRadioButton(plugin.m_Name); + } break; + case TYPE_SELECTEXACTLYONE: { + newControl = new QRadioButton(plugin.m_Name); + if (first) { + newControl->setChecked(true); + } + } break; + case TYPE_SELECTALL: { + newControl = new QCheckBox(plugin.m_Name); + newControl->setChecked(true); + newControl->setEnabled(false); + } break; + } + newControl->setObjectName("choice"); + switch (plugin.m_Type) { + case TYPE_REQUIRED: { + newControl->setChecked(true); + newControl->setEnabled(false); + newControl->setToolTip(tr("This component is required")); + } break; + case TYPE_RECOMMENDED: { + if (maySelectMore) { + newControl->setChecked(true); + } + newControl->setToolTip(tr("It is recommended you enable this component")); + if ((groupType == TYPE_SELECTATMOSTONE) || (groupType == TYPE_SELECTEXACTLYONE)) { + maySelectMore = false; + } + } break; + case TYPE_OPTIONAL: { + newControl->setToolTip(tr("Optional component")); + } break; + case TYPE_NOTUSABLE: { + newControl->setChecked(false); + newControl->setEnabled(false); + newControl->setToolTip(tr("This component is not usable in combination with other installed plugins")); + } break; + case TYPE_COULDBEUSABLE: { + newControl->setCheckable(true); + newControl->setIcon(QIcon(":/new/guiresources/resources/dialog-warning_16.png")); + newControl->setToolTip(tr("You may be experiencing instability in combination with other installed plugins")); + } break; + } + + newControl->setProperty("plugintype", plugin.m_Type); + newControl->setProperty("screenshot", plugin.m_ImagePath); + newControl->setProperty("description", plugin.m_Description); + QVariantList fileList; + for (std::vector::iterator iter = plugin.m_Files.begin(); iter != plugin.m_Files.end(); ++iter) { + fileList.append(qVariantFromValue(*iter)); + } + newControl->setProperty("files", fileList); + QVariantList conditionFlags; + for (std::vector::const_iterator iter = plugin.m_Conditions.begin(); iter != plugin.m_Conditions.end(); ++iter) { + if (iter->m_Name.length() != 0) { + conditionFlags.append(qVariantFromValue(Condition(iter->m_Name, iter->m_Value))); + } + } + newControl->setProperty("conditionFlags", conditionFlags); + newControl->installEventFilter(this); + controls.push_back(newControl); + first = false; + } + } + } + + if (pluginOrder == ORDER_ASCENDING) { + std::sort(controls.begin(), controls.end(), ControlsAscending); + } else if (pluginOrder == ORDER_DESCENDING) { + std::sort(controls.begin(), controls.end(), ControlsDescending); + } + + for (std::vector::const_iterator iter = controls.begin(); iter != controls.end(); ++iter) { + layout->addWidget(*iter); + } + + if (groupType == TYPE_SELECTATMOSTONE) { + QRadioButton *newButton = new QRadioButton(tr("None")); + if (maySelectMore) { + newButton->setChecked(true); + } + layout->addWidget(newButton); + } +} + + +void FomodInstallerDialog::readGroup(QXmlStreamReader &reader, QLayout *layout) +{ + //FileGroup result; + QString name = reader.attributes().value("name").toString(); + GroupType type = getGroupType(reader.attributes().value("type").toString()); + + if (type == TYPE_SELECTATLEASTONE) { + QLabel *label = new QLabel(tr("Select one or more of these options:")); + layout->addWidget(label); + } + + QGroupBox *groupBox = new QGroupBox(name); + + QVBoxLayout *groupLayout = new QVBoxLayout; + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "group"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "plugins") { + readPlugins(reader, type, groupLayout); + } + } + } + + groupBox->setLayout(groupLayout); + layout->addWidget(groupBox); +} + + +void FomodInstallerDialog::readGroups(QXmlStreamReader &reader, QLayout *layout) +{ + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "optionalFileGroups"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "group") { + readGroup(reader, layout); + } + } + } +} + + +void FomodInstallerDialog::readVisible(QXmlStreamReader &reader, QVariantList &conditions) +{ + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "visible"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "flagDependency") { + Condition condition(reader.attributes().value("flag").toString(), + reader.attributes().value("value").toString()); + conditions.append(qVariantFromValue(condition)); + } + } + } +} + +QGroupBox *FomodInstallerDialog::readInstallerStep(QXmlStreamReader &reader) +{ + QString name = reader.attributes().value("name").toString(); + QGroupBox *page = new QGroupBox(name); + QVBoxLayout *pageLayout = new QVBoxLayout; + QScrollArea *scrollArea = new QScrollArea; + QFrame *scrolledArea = new QFrame; + QVBoxLayout *scrollLayout = new QVBoxLayout; + + QVariantList conditions; + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "installStep"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "optionalFileGroups") { + readGroups(reader, scrollLayout); + } else if (reader.name() == "visible") { + readVisible(reader, conditions); + } + } + } + if (conditions.length() != 0) { + page->setProperty("conditions", conditions); + } + + scrolledArea->setLayout(scrollLayout); + scrollArea->setWidget(scrolledArea); + scrollArea->setWidgetResizable(true); + pageLayout->addWidget(scrollArea); + page->setLayout(pageLayout); + return page; +} + + +void FomodInstallerDialog::readInstallerSteps(QXmlStreamReader &reader) +{ + ItemOrder stepOrder = reader.attributes().hasAttribute("order") ? getItemOrder(reader.attributes().value("order").toString()) + : ORDER_ASCENDING; + + std::vector pages; + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "installSteps"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "installStep") { + pages.push_back(readInstallerStep(reader)); + } + } + } + + if (stepOrder == ORDER_ASCENDING) { + std::sort(pages.begin(), pages.end(), PagesAscending); + } else if (stepOrder == ORDER_DESCENDING) { + std::sort(pages.begin(), pages.end(), PagesDescending); + } + + for (std::vector::const_iterator iter = pages.begin(); iter != pages.end(); ++iter) { + ui->stepsStack->addWidget(*iter); + } +} + + +FomodInstallerDialog::ConditionalInstall FomodInstallerDialog::readConditionalPattern(QXmlStreamReader &reader) +{ + ConditionalInstall result; + result.m_Operator = ConditionalInstall::OP_AND; + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "pattern"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "dependencies") { + QStringRef dependencyOperator = reader.attributes().value("operator"); + if (dependencyOperator == "And") { + result.m_Operator = ConditionalInstall::OP_AND; + } else if (dependencyOperator == "Or") { + result.m_Operator = ConditionalInstall::OP_OR; + } // otherwise operator is not set (which we can ignore) or invalid (which we should report actually) + + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "dependencies"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "flagDependency") { + result.m_Conditions.push_back(Condition(reader.attributes().value("flag").toString(), + reader.attributes().value("value").toString())); + } + } + } + } else if (reader.name() == "files") { + readFileList(reader, result.m_Files); + } + } + } + return result; +} + + +void FomodInstallerDialog::readConditionalFileInstalls(QXmlStreamReader &reader) +{ + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "conditionalFileInstalls"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "patterns") { + while (!((reader.readNext() == QXmlStreamReader::EndElement) && + (reader.name() == "patterns"))) { + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == "pattern") { + m_ConditionalInstalls.push_back(readConditionalPattern(reader)); + } + } + } + } + } + } +} + + +void FomodInstallerDialog::parseModuleConfig(const QByteArray &data) +{ + QXmlStreamReader reader(data); + while (!reader.atEnd() && !reader.hasError()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: { + if (reader.name() == "installSteps") { + readInstallerSteps(reader); + } else if (reader.name() == "requiredInstallFiles") { + readFileList(reader, m_RequiredFiles); + } else if (reader.name() == "conditionalFileInstalls") { + readConditionalFileInstalls(reader); + } + } break; + default: {} break; + } + } + if (reader.hasError()) { + reportError(tr("failed to parse ModuleConfig.xml: %1 - %2").arg(reader.errorString()).arg(reader.lineNumber())); + } + activateCurrentPage(); +} + + +void FomodInstallerDialog::on_manualBtn_clicked() +{ + m_Manual = true; + this->reject(); +} + +void FomodInstallerDialog::on_cancelBtn_clicked() +{ + this->reject(); +} + + +void FomodInstallerDialog::on_websiteLabel_linkActivated(const QString &link) +{ + ::ShellExecuteW(NULL, L"open", ToWString(link).c_str(), NULL, NULL, SW_SHOWNORMAL); +} + + +void FomodInstallerDialog::activateCurrentPage() +{ + QList choices = ui->stepsStack->currentWidget()->findChildren("choice"); + if (choices.count() > 0) { + highlightControl(choices.at(0)); + } +} + + +bool FomodInstallerDialog::testCondition(int maxIndex, const QString &flag, const QString &value) +{ + // iterate through all set condition flags on all activated controls on all visible pages if one of them matches the condition + for (int i = 0; i < maxIndex; ++i) { + if (testVisible(i)) { + QWidget *page = ui->stepsStack->widget(i); + QList choices = page->findChildren("choice"); + foreach (QAbstractButton* choice, choices) { + if (choice->isChecked()) { + QVariant temp = choice->property("conditionFlags"); + if (temp.isValid()) { + QVariantList conditionFlags = temp.toList(); + for (QVariantList::const_iterator iter = conditionFlags.begin(); iter != conditionFlags.end(); ++iter) { + Condition condition = iter->value(); + if ((condition.m_Name == flag) && (condition.m_Value == value)) { + return true; + } + } + } + } + } + } + } + return false; +} + + +bool FomodInstallerDialog::testVisible(int pageIndex) +{ + QWidget *page = ui->stepsStack->widget(pageIndex); + QVariant temp = page->property("conditions"); + if (temp.isValid()) { + QVariantList conditions = temp.toList(); + for (QVariantList::const_iterator iter = conditions.begin(); iter != conditions.end(); ++iter) { + Condition condition = iter->value(); + if (!testCondition(pageIndex, condition.m_Name, condition.m_Value)) { + return false; + } + } + return true; + } else { + return true; + } +} + + +bool FomodInstallerDialog::nextPage() +{ + int index = ui->stepsStack->currentIndex() + 1; + while (index < ui->stepsStack->count()) { + if (testVisible(index)) { + ui->stepsStack->setCurrentIndex(index); + return true; + } + ++index; + } + // no more visible pages -> install + return false; +} + + +void FomodInstallerDialog::on_nextBtn_clicked() +{ + if (ui->stepsStack->currentIndex() == ui->stepsStack->count() - 1) { + this->accept(); + } else { + if (nextPage()) { + if (ui->stepsStack->currentIndex() == ui->stepsStack->count() - 1) { + ui->nextBtn->setText(tr("Install")); + } + ui->prevBtn->setEnabled(true); + activateCurrentPage(); + } else { + this->accept(); + } + } +} + +void FomodInstallerDialog::on_prevBtn_clicked() +{ + if (ui->stepsStack->currentIndex() != 0) { + ui->stepsStack->setCurrentIndex(ui->stepsStack->currentIndex() - 1); + ui->nextBtn->setText(tr("Next")); + } + if (ui->stepsStack->currentIndex() == 0) { + ui->prevBtn->setEnabled(false); + } + activateCurrentPage(); +} -- cgit v1.3.1