/* 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 using namespace MOBase; 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, bool nameWasGuessed, const QString &fomodPath, QWidget *parent) : QDialog(parent), ui(new Ui::FomodInstallerDialog), m_NameWasGuessed(nameWasGuessed), m_FomodPath(fomodPath), m_Manual(false) { ui->setupUi(this); ui->nameEdit->setText(modName); } FomodInstallerDialog::~FomodInstallerDialog() { delete ui; } int FomodInstallerDialog::bomOffset(const QByteArray &buffer) { static const unsigned char BOM_UTF8[] = { 0xEF, 0xBB, 0xBF }; static const unsigned char BOM_UTF16BE[] = { 0xFE, 0xFF }; static const unsigned char BOM_UTF16LE[] = { 0xFF, 0xFE }; if (buffer.startsWith(reinterpret_cast(BOM_UTF8))) return 3; if (buffer.startsWith(reinterpret_cast(BOM_UTF16BE)) || buffer.startsWith(reinterpret_cast(BOM_UTF16LE))) return 2; return 0; } #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() + bomOffset(header), "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() + bomOffset(header), "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") { if (ui->nameEdit->text().isEmpty() || m_NameWasGuessed) { 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(); }