/* 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 "modinfodialog.h" #include "ui_modinfodialog.h" #include "report.h" #include "utility.h" #include "json.h" #include "messagedialog.h" #include "bbcode.h" #include "questionboxmemory.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include using QtJson::Json; using namespace MOBase; using namespace MOShared; class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); public: ModFileListWidget(const QString &text, int sortValue, QListWidget *parent = 0) : QListWidgetItem(text, parent, QListWidgetItem::UserType + 1), m_SortValue(sortValue) {} private: int m_SortValue; }; bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS) { return LHS.m_SortValue < RHS.m_SortValue; } ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, QWidget *parent) : TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo), m_ThumbnailMapper(this), m_RequestStarted(false), m_DeleteAction(NULL), m_RenameAction(NULL), m_OpenAction(NULL), m_Directory(directory), m_Origin(NULL) { ui->setupUi(this); this->setWindowTitle(modInfo->name()); this->setWindowModality(Qt::WindowModal); m_UTF8Codec = QTextCodec::codecForName("utf-8"); QListWidget *textFileList = findChild("textFileList"); QListWidget *iniFileList = findChild("iniFileList"); QListWidget *iniTweaksList = findChild("iniTweaksList"); QListWidget *activeESPList = findChild("activeESPList"); QListWidget *inactiveESPList = findChild("inactiveESPList"); QTreeWidget *categoriesTree = findChild("categoriesTree"); QHBoxLayout *thumbnailArea = findChild("thumbnailArea"); m_FileTree = findChild("fileTree"); m_RootPath = modInfo->absolutePath(); QString metaFileName = m_RootPath.mid(0).append("/meta.ini"); m_Settings = new QSettings(metaFileName, QSettings::IniFormat); QLineEdit *modIDEdit = findChild("modIDEdit"); modIDEdit->setValidator(new QIntValidator(modIDEdit)); modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); ui->notesEdit->setText(modInfo->notes()); m_FileSystemModel = new QFileSystemModel(this); m_FileSystemModel->setReadOnly(false); m_FileSystemModel->setRootPath(m_RootPath); m_FileTree->setModel(m_FileSystemModel); m_FileTree->setRootIndex(m_FileSystemModel->index(m_RootPath)); m_FileTree->setColumnWidth(0, 300); connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&))); connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); m_DeleteAction = new QAction(tr("&Delete"), m_FileTree); m_RenameAction = new QAction(tr("&Rename"), m_FileTree); m_HideAction = new QAction(tr("&Hide"), m_FileTree); m_UnhideAction = new QAction(tr("&Unhide"), m_FileTree); m_OpenAction = new QAction(tr("&Open"), m_FileTree); m_NewFolderAction = new QAction(tr("&New Folder"), m_FileTree); QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); QObject::connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); QObject::connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); // ui->descriptionView->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); // QObject::connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); connect(ui->descriptionView, SIGNAL(anchorClicked(QUrl)), this, SLOT(linkClicked(QUrl))); if (directory->originExists(ToWString(modInfo->name()))) { m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); if (m_Origin->isDisabled()) { m_Origin = NULL; } } addCategories(CategoryFactory::instance(), modInfo->getCategories(), categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); refreshLists(); int numTweaks = m_Settings->beginReadArray("INI Tweaks"); for (int i = 0; i < numTweaks; ++i) { m_Settings->setArrayIndex(i); QList items = iniTweaksList->findItems(m_Settings->value("name").toString(), Qt::MatchFixedString); if (items.size() != 0) { items.at(0)->setCheckState(Qt::Checked); } } m_Settings->endArray(); QTabWidget *tabWidget = findChild("tabWidget"); tabWidget->setTabEnabled(TAB_TEXTFILES, textFileList->count() != 0); tabWidget->setTabEnabled(TAB_INIFILES, (iniFileList->count() != 0) || (iniTweaksList->count() != 0)); tabWidget->setTabEnabled(TAB_IMAGES, thumbnailArea->count() != 0); tabWidget->setTabEnabled(TAB_ESPS, (inactiveESPList->count() != 0) || (activeESPList->count() != 0)); tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != NULL); if (tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); } ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); } ModInfoDialog::~ModInfoDialog() { m_ModInfo->setNotes(ui->notesEdit->toPlainText()); saveIniTweaks(); saveCategories(ui->categoriesTree->invisibleRootItem()); delete ui; delete m_Settings; } void ModInfoDialog::refreshLists() { int numNonConflicting = 0; int numOverwrite = 0; int numOverwritten = 0; ui->overwriteTree->clear(); ui->overwrittenTree->clear(); if (m_Origin != NULL) { std::vector files = m_Origin->getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { QString relativeName = QDir::fromNativeSeparators(ToQString((*iter)->getRelativePath())); QString fileName = relativeName.mid(0).prepend(m_RootPath); bool archive; if ((*iter)->getOrigin(archive) == m_Origin->getID()) { std::vector alternatives = (*iter)->getAlternatives(); if (!alternatives.empty()) { std::wostringstream altString; for (std::vector::iterator altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) { if (altIter != alternatives.begin()) { altString << ", "; } altString << m_Directory->getOriginByID(*altIter).getName(); } QStringList fields(relativeName.prepend("...")); fields.append(ToQString(altString.str())); QTreeWidgetItem *item = new QTreeWidgetItem(fields); item->setData(0, Qt::UserRole, fileName); item->setData(1, Qt::UserRole, ToQString(m_Directory->getOriginByID(alternatives[0]).getName())); item->setData(1, Qt::UserRole + 1, alternatives[0]); item->setData(1, Qt::UserRole + 2, archive); ui->overwriteTree->addTopLevelItem(item); ++numOverwrite; } else {// otherwise don't display the file ++numNonConflicting; } } else { FilesOrigin &realOrigin = m_Directory->getOriginByID((*iter)->getOrigin(archive)); QStringList fields(relativeName); fields.append(ToQString(realOrigin.getName())); QTreeWidgetItem *item = new QTreeWidgetItem(fields); item->setData(1, Qt::UserRole, ToQString(realOrigin.getName())); ui->overwrittenTree->addTopLevelItem(item); ++numOverwritten; } } } QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { QString fileName = dirIterator.next(); if (fileName.endsWith(".txt", Qt::CaseInsensitive)) { ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1)); } else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && !fileName.endsWith("meta.ini")) { QString namePart = fileName.mid(m_RootPath.length() + 1); if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { QListWidgetItem *newItem = new QListWidgetItem(namePart.mid(11), ui->iniTweaksList); newItem->setData(Qt::UserRole, namePart); newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); newItem->setCheckState(Qt::Unchecked); ui->iniTweaksList->addItem(newItem); } else { ui->iniFileList->addItem(namePart); } } else if (fileName.endsWith(".esp", Qt::CaseInsensitive) || fileName.endsWith(".esm", Qt::CaseInsensitive)) { QString relativePath = fileName.mid(m_RootPath.length() + 1); if (relativePath.contains('/')) { QFileInfo fileInfo(fileName); QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName()); newItem->setData(Qt::UserRole, relativePath); ui->inactiveESPList->addItem(newItem); } else { ui->activeESPList->addItem(relativePath); } } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { QImage image = QImage(fileName); if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { image = image.scaledToWidth(128); } else { image = image.scaledToHeight(96); } QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), ""); thumbnailButton->setIconSize(QSize(image.width(), image.height())); connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map())); m_ThumbnailMapper.setMapping(thumbnailButton, fileName); ui->thumbnailArea->addWidget(thumbnailButton); } } ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); ui->noConflictCount->display(numNonConflicting); } void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) { for (unsigned int i = 0; i < factory.numCategories(); ++i) { if (factory.getParentID(i) != rootLevel) { continue; } int categoryID = factory.getCategoryID(i); QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); newItem->setCheckState(0, enabledCategories.find(categoryID) != enabledCategories.end() ? Qt::Checked : Qt::Unchecked); newItem->setData(0, Qt::UserRole, categoryID); if (factory.hasChildren(i)) { addCategories(factory, enabledCategories, newItem, categoryID); } root->addChild(newItem); } } void ModInfoDialog::saveCategories(QTreeWidgetItem *currentNode) { for (int i = 0; i < currentNode->childCount(); ++i) { QTreeWidgetItem *childNode = currentNode->child(i); m_ModInfo->setCategory(childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); saveCategories(childNode); } } void ModInfoDialog::on_closeButton_clicked() { if (allowNavigateFromTXT() && allowNavigateFromINI()) { this->close(); } } QString ModInfoDialog::getModVersion() const { return m_Settings->value("version", "").toString(); } const int ModInfoDialog::getModID() const { return m_Settings->value("modid", 0).toInt(); } void ModInfoDialog::openTab(int tab) { QTabWidget *tabWidget = findChild("tabWidget"); if (tabWidget->isTabEnabled(tab)) { tabWidget->setCurrentIndex(tab); } } void ModInfoDialog::thumbnailClicked(const QString &fileName) { QLabel *imageLabel = findChild("imageLabel"); QImage image(fileName); if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { image = image.scaledToWidth(imageLabel->width()); } else { image = image.scaledToHeight(imageLabel->height()); } imageLabel->setPixmap(QPixmap::fromImage(image)); } bool ModInfoDialog::allowNavigateFromTXT() { if (ui->saveTXTButton->isEnabled()) { int res = QMessageBox::question(this, tr("Save changes?"), tr("Save changes to \"%1\"?").arg(ui->textFileView->property("currentFile").toString()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (res == QMessageBox::Cancel) { return false; } else if (res == QMessageBox::Yes) { saveCurrentTextFile(); } } return true; } bool ModInfoDialog::allowNavigateFromINI() { if (ui->saveButton->isEnabled()) { int res = QMessageBox::question(this, tr("Save changes?"), tr("Save changes to \"%1\"?").arg(ui->iniFileView->property("currentFile").toString()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (res == QMessageBox::Cancel) { return false; } else if (res == QMessageBox::Yes) { saveCurrentIniFile(); } } return true; } void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QString fullPath = m_RootPath + "/" + current->text(); QVariant currentFile = ui->textFileView->property("currentFile"); if (currentFile.isValid() && (currentFile.toString() == fullPath)) { // the new file is the same as the currently displayed file. May be the result of a cancelation return; } if (allowNavigateFromTXT()) { openTextFile(fullPath); } else { ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); } } void ModInfoDialog::openTextFile(const QString &fileName) { QFile textFile(fileName); textFile.open(QIODevice::ReadOnly); QByteArray buffer = textFile.readAll(); QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec); ui->textFileView->setText(codec->toUnicode(buffer)); ui->textFileView->setProperty("currentFile", fileName); ui->textFileView->setProperty("encoding", codec->name()); ui->saveTXTButton->setEnabled(false); } void ModInfoDialog::openIniFile(const QString &fileName) { QPushButton* saveButton = findChild("saveButton"); QFile iniFile(fileName); iniFile.open(QIODevice::ReadOnly); QByteArray buffer = iniFile.readAll(); QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec); QTextEdit *iniFileView = findChild("iniFileView"); iniFileView->setText(codec->toUnicode(buffer)); iniFileView->setProperty("currentFile", fileName); iniFileView->setProperty("encoding", codec->name()); iniFile.close(); saveButton->setEnabled(false); } void ModInfoDialog::saveIniTweaks() { QListWidget *iniTweaksList = findChild("iniTweaksList"); m_Settings->beginWriteArray("INI Tweaks"); int countEnabled = 0; for (int i = 0; i < iniTweaksList->count(); ++i) { if (iniTweaksList->item(i)->checkState() == Qt::Checked) { m_Settings->setArrayIndex(countEnabled++); m_Settings->setValue("name", iniTweaksList->item(i)->text()); } } m_Settings->endArray(); } void ModInfoDialog::on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QString fullPath = m_RootPath + "/" + current->text(); QVariant currentFile = ui->iniFileView->property("currentFile"); if (currentFile.isValid() && (currentFile.toString() == fullPath)) { // the new file is the same as the currently displayed file. May be the result of a cancelation return; } if (allowNavigateFromINI()) { openIniFile(fullPath); } else { ui->iniFileList->setCurrentItem(previous, QItemSelectionModel::Current); } } void ModInfoDialog::on_iniTweaksList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QString fullPath = m_RootPath + "/" + current->data(Qt::UserRole).toString(); QVariant currentFile = ui->iniFileView->property("currentFile"); if (currentFile.isValid() && (currentFile.toString() == fullPath)) { // the new file is the same as the currently displayed file. May be the result of a cancelation return; } if (allowNavigateFromINI()) { openIniFile(fullPath); } else { ui->iniFileList->setCurrentItem(previous, QItemSelectionModel::Current); } } void ModInfoDialog::on_saveButton_clicked() { saveCurrentIniFile(); } void ModInfoDialog::on_saveTXTButton_clicked() { saveCurrentTextFile(); } void ModInfoDialog::saveCurrentTextFile() { QVariant fileNameVar = ui->textFileView->property("currentFile"); QVariant encodingVar = ui->textFileView->property("encoding"); if (fileNameVar.isValid() && encodingVar.isValid()) { QString fileName = fileNameVar.toString(); QFile txtFile(fileName); txtFile.open(QIODevice::WriteOnly); txtFile.resize(0); QTextCodec *codec = QTextCodec::codecForName(encodingVar.toString().toUtf8()); QString data = ui->textFileView->toPlainText().replace("\n", "\r\n"); txtFile.write(codec->fromUnicode(data)); } else { reportError("no file selected"); } ui->saveTXTButton->setEnabled(false); } void ModInfoDialog::saveCurrentIniFile() { QVariant fileNameVar = ui->iniFileView->property("currentFile"); QVariant encodingVar = ui->iniFileView->property("encoding"); if (fileNameVar.isValid()) { QString fileName = fileNameVar.toString(); QFile txtFile(fileName); txtFile.open(QIODevice::WriteOnly); txtFile.resize(0); QTextCodec *codec = QTextCodec::codecForName(encodingVar.toString().toUtf8()); QString data = ui->iniFileView->toPlainText().replace("\n", "\r\n"); txtFile.write(codec->fromUnicode(data)); } else { reportError("no file selected"); } ui->saveButton->setEnabled(false); } void ModInfoDialog::on_iniFileView_textChanged() { QPushButton* saveButton = findChild("saveButton"); saveButton->setEnabled(true); } void ModInfoDialog::on_textFileView_textChanged() { ui->saveTXTButton->setEnabled(true); } void ModInfoDialog::on_activateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); QListWidget *inactiveESPList = findChild("inactiveESPList"); int selectedRow = inactiveESPList->currentRow(); if (selectedRow < 0) { return; } QListWidgetItem *selectedItem = inactiveESPList->takeItem(selectedRow); QDir root(m_RootPath); bool renamed = false; while (root.exists(selectedItem->text())) { bool okClicked = false; QString newName = QInputDialog::getText(this, tr("File Exists"), tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, selectedItem->text(), &okClicked); if (!okClicked) { inactiveESPList->insertItem(selectedRow, selectedItem); return; } else if (newName.size() > 0) { selectedItem->setText(newName); renamed = true; } } if (root.rename(selectedItem->data(Qt::UserRole).toString(), selectedItem->text())) { activeESPList->addItem(selectedItem); if (renamed) { selectedItem->setData(Qt::UserRole, QVariant()); } } else { inactiveESPList->insertItem(selectedRow, selectedItem); reportError(tr("failed to move file")); } } void ModInfoDialog::on_deactivateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); QListWidget *inactiveESPList = findChild("inactiveESPList"); int selectedRow = activeESPList->currentRow(); if (selectedRow < 0) { return; } QDir root(m_RootPath); QListWidgetItem *selectedItem = activeESPList->takeItem(selectedRow); // if we moved the file from optional to active in this session, we move the file back to // where it came from. Otherwise, it is moved to the new folder "optional" if (selectedItem->data(Qt::UserRole).isNull()) { selectedItem->setData(Qt::UserRole, QString("optional/") + selectedItem->text()); if (!root.exists("optional")) { if (!root.mkdir("optional")) { reportError(tr("failed to create directory \"optional\"")); activeESPList->insertItem(selectedRow, selectedItem); return; } } } if (root.rename(selectedItem->text(), selectedItem->data(Qt::UserRole).toString())) { inactiveESPList->addItem(selectedItem); } else { activeESPList->insertItem(selectedRow, selectedItem); } } void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) { this->close(); emit nexusLinkActivated(link); } void ModInfoDialog::linkClicked(const QUrl &url) { if (url.toString().startsWith(ToQString(GameInfo::instance().getNexusPage()))) { this->close(); emit nexusLinkActivated(url.toString()); } else { ::ShellExecuteW(NULL, L"open", ToWString(url.toString()).c_str(), NULL, NULL, SW_SHOWNORMAL); } } void ModInfoDialog::refreshNexusData(int modID) { if ((!m_RequestStarted) && (modID > 0)) { m_RequestStarted = true; m_ModInfo->updateNXMInfo(); MessageDialog::showMessage(tr("Info requested, please wait"), this); } } /*void ModInfoDialog::nxmDescriptionAvailable(int, QVariant, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { return; } else { m_RequestIDs.erase(idIter); } QVariantMap result = resultData.toMap(); if (!result["description"].isNull()) { QString descriptionAsHTML = QString("" "" "%1" "").arg(BBCode::convertToHTML(result["description"].toString())); // QString descriptionAsHTML = BBCode::convertToHTML(result["description"].toString()); ui->descriptionView->setHtml(descriptionAsHTML); } else { ui->descriptionView->setHtml(result["summary"].toString().append(QString("\r\n") + tr("(description incomplete, please visit nexus)"))); } QLineEdit *versionEdit = findChild("versionEdit"); QString version = result["version"].toString(); if (!version.isEmpty()) { m_ModInfo->setNewestVersion(version); VersionInfo currentVersion(versionEdit->text()); VersionInfo newestVersion(version); QPalette versionColor; if (currentVersion < newestVersion) { versionColor.setColor(QPalette::Text, Qt::red); versionEdit->setToolTip(tr("Current Version: %1").arg(version)); } else { versionColor.setColor(QPalette::Text, Qt::green); versionEdit->setToolTip(tr("No update available")); } versionEdit->setPalette(versionColor); } }*/ QString ModInfoDialog::getFileCategory(int categoryID) { switch (categoryID) { case 1: return tr("Main"); case 2: return tr("Update"); case 3: return tr("Optional"); case 4: return tr("Old"); case 5: return tr("Misc"); default: return tr("Unknown"); } } void ModInfoDialog::updateVersionColor() { // QPalette versionColor; if (m_ModInfo->getVersion() < m_ModInfo->getNewestVersion()) { ui->versionEdit->setStyleSheet("color: red"); // versionColor.setColor(QPalette::Text, Qt::red); ui->versionEdit->setToolTip(tr("Current Version: %1").arg(m_ModInfo->getNewestVersion().canonicalString())); } else { ui->versionEdit->setStyleSheet("color: green"); // versionColor.setColor(QPalette::Text, Qt::green); ui->versionEdit->setToolTip(tr("No update available")); } // ui->versionEdit->setPalette(versionColor); } void ModInfoDialog::modDetailsUpdated(bool success) { if (success) { QString nexusDescription = m_ModInfo->getNexusDescription(); if (!nexusDescription.isEmpty()) { /* QString input = "[size=20]sizetest[/size]\r\n" "[COLOR=yellow]colortest[/COLOR]\r\n" "[center]centertest[/center]\r\n" "[quote]quotetest 1[/quote]\r\n" "[quote=bla]quotetest 2[/quote]\r\n" "[url]www.skyrimnexus.com[/url]\r\n" "[url=www.skyrimnexus.com]urltest 2[/url]\r\n" "[ol]\r\n" "[li]item 2[/li]" "[*]item 1\r\n" "[/ol]\r\n" "[img]http://www.bbcode.org/images/bbcode_logo.png[/img]\r\n" "[table][tr][th]headertest1[/th]" "[th]headertest2[/th][/tr]" "[tr][td]rowtest11[/td][td]rowtest12[/td][/tr]" "[tr][td]rowtest21[/td][td]rowtest22[/td][/tr][/table]" "[email=\"sherb@gmx.net\"]mail me[/email]"; ui->descriptionView->setHtml(BBCode::convertToHTML(input));*/ QString descriptionAsHTML = QString("" "" "%1" "").arg(BBCode::convertToHTML(nexusDescription)); // QString descriptionAsHTML = BBCode::convertToHTML(result["description"].toString()); ui->descriptionView->setHtml(descriptionAsHTML); } else { // ui->descriptionView->setHtml(result["summary"].toString().append(QString("\r\n") + tr("(description incomplete, please visit nexus)"))); ui->descriptionView->setHtml(tr("(description incomplete, please visit nexus)")); } QString version = m_ModInfo->getNewestVersion().canonicalString(); if (!version.isEmpty()) { m_ModInfo->setNewestVersion(version); updateVersionColor(); } } } void ModInfoDialog::activateNexusTab() { QLineEdit *modIDEdit = findChild("modIDEdit"); int modID = modIDEdit->text().toInt(); if (modID != 0) { QString nexusLink = QString("%1/downloads/file.php?id=%2").arg(ToQString(GameInfo::instance().getNexusPage())).arg(modID); QLabel *visitNexusLabel = findChild("visitNexusLabel"); visitNexusLabel->setText(tr("Visit on Nexus").arg(nexusLink)); visitNexusLabel->setToolTip(nexusLink); if (m_ModInfo->getNexusDescription().isEmpty() || QDateTime::currentDateTime() > m_ModInfo->getLastNexusQuery().addDays(1)) { refreshNexusData(modID); } else { this->modDetailsUpdated(true); } } QLineEdit *versionEdit = findChild("versionEdit"); QString currentVersion = m_Settings->value("version", "0.0").toString(); versionEdit->setText(currentVersion); } void ModInfoDialog::on_tabWidget_currentChanged(int index) { if (index == TAB_NEXUS) { activateNexusTab(); } } void ModInfoDialog::on_modIDEdit_editingFinished() { int oldID = m_Settings->value("modid", 0).toInt(); int modID = ui->modIDEdit->text().toInt(); if (oldID != modID){ m_ModInfo->setNexusID(modID); ui->descriptionView->setHtml(""); if (modID != 0) { m_RequestStarted = false; refreshNexusData(modID); } } } void ModInfoDialog::on_versionEdit_editingFinished() { VersionInfo version(ui->versionEdit->text()); m_ModInfo->setVersion(version); updateVersionColor(); } bool ModInfoDialog::recursiveDelete(const QModelIndex &index) { for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); if (m_FileSystemModel->isDir(childIndex)) { if (!recursiveDelete(childIndex)) { qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); return false; } } else { if (!m_FileSystemModel->remove(childIndex)) { qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); return false; } } } if (!m_FileSystemModel->remove(index)) { qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); return false; } return true; } void ModInfoDialog::deleteFile(const QModelIndex &index) { bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) : m_FileSystemModel->remove(index); if (!res) { QString fileName = m_FileSystemModel->fileName(index); reportError(tr("Failed to delete %1").arg(fileName)); } } void ModInfoDialog::deleteTriggered() { if (m_FileSelection.count() == 0) { return; } else if (m_FileSelection.count() == 1) { QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; } } else { if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; } } foreach(QModelIndex index, m_FileSelection) { deleteFile(index); } } void ModInfoDialog::renameTriggered() { QModelIndex selection = m_FileSelection.at(0); QModelIndex index = selection.sibling(selection.row(), 0); if (!index.isValid() || m_FileSystemModel->isReadOnly()) { return; } m_FileTree->edit(index); } void ModInfoDialog::hideTriggered() { for (QModelIndexList::const_iterator iter = m_FileSelection.constBegin(); iter != m_FileSelection.constEnd(); ++iter) { QString path = m_FileSystemModel->filePath(*iter); if (!path.endsWith(ModInfo::s_HiddenExt)) { hideFile(path); } } } void ModInfoDialog::unhideTriggered() { for (QModelIndexList::const_iterator iter = m_FileSelection.constBegin(); iter != m_FileSelection.constEnd(); ++iter) { QString path = m_FileSystemModel->filePath(*iter); if (path.endsWith(ModInfo::s_HiddenExt)) { unhideFile(path); } } } void ModInfoDialog::openFile(const QModelIndex &index) { QString fileName = m_FileSystemModel->filePath(index); HINSTANCE res = ::ShellExecuteW(NULL, L"open", ToWString(fileName).c_str(), NULL, NULL, SW_SHOW); if ((int)res <= 32) { qCritical("failed to invoke %s: %d", fileName.toUtf8().constData(), res); } } void ModInfoDialog::openTriggered() { foreach(QModelIndex idx, m_FileSelection) { openFile(idx); } } void ModInfoDialog::createDirectoryTriggered() { QModelIndex selection = m_FileSelection.at(0); QModelIndex index = m_FileSystemModel->isDir(selection) ? selection : selection.parent(); index = index.sibling(index.row(), 0); QString name = tr("New Folder"); QString path = m_FileSystemModel->filePath(index).append("/"); QModelIndex existingIndex = m_FileSystemModel->index(path + name); int suffix = 1; while (existingIndex.isValid()) { name = tr("New Folder") + QString::number(suffix++); existingIndex = m_FileSystemModel->index(path + name); } QModelIndex newIndex = m_FileSystemModel->mkdir(index, name); if (!newIndex.isValid()) { reportError(tr("Failed to create \"%1\"").arg(name)); return; } m_FileTree->setCurrentIndex(newIndex); m_FileTree->edit(newIndex); } void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) { QItemSelectionModel *selectionModel = m_FileTree->selectionModel(); m_FileSelection = selectionModel->selectedRows(0); // m_FileSelection = m_FileTree->indexAt(pos); QMenu menu(m_FileTree); menu.addAction(m_NewFolderAction); bool hasFiles = false; foreach(QModelIndex idx, m_FileSelection) { if (m_FileSystemModel->fileInfo(idx).isFile()) { hasFiles = true; break; } } if (selectionModel->hasSelection()) { if (hasFiles) { menu.addAction(m_OpenAction); } menu.addAction(m_RenameAction); menu.addAction(m_DeleteAction); if (m_FileSystemModel->fileName(m_FileSelection.at(0)).endsWith(ModInfo::s_HiddenExt)) { menu.addAction(m_UnhideAction); } else { menu.addAction(m_HideAction); } } else { m_FileSelection.clear(); m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); } menu.exec(m_FileTree->mapToGlobal(pos)); } void ModInfoDialog::on_categoriesTree_itemChanged(QTreeWidgetItem *item, int) { QTreeWidgetItem *parent = item->parent(); while ((parent != NULL) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { parent->setCheckState(0, Qt::Checked); parent = parent->parent(); } refreshPrimaryCategoriesBox(); } void ModInfoDialog::addCheckedCategories(QTreeWidgetItem *tree) { for (int i = 0; i < tree->childCount(); ++i) { QTreeWidgetItem *child = tree->child(i); if (child->checkState(0) == Qt::Checked) { ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); addCheckedCategories(child); } } } void ModInfoDialog::refreshPrimaryCategoriesBox() { ui->primaryCategoryBox->clear(); int primaryCategory = m_ModInfo->getPrimaryCategory(); addCheckedCategories(ui->categoriesTree->invisibleRootItem()); for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { ui->primaryCategoryBox->setCurrentIndex(i); break; } } } void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) { if (index != -1) { m_ModInfo->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); } } void ModInfoDialog::on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int) { this->close(); emit modOpen(item->data(1, Qt::UserRole).toString(), TAB_CONFLICTS); } bool ModInfoDialog::hideFile(const QString &oldName) { QString newName = oldName + ModInfo::s_HiddenExt; if (QFileInfo(newName).exists()) { if (QMessageBox::question(this, tr("Replace file?"), tr("There already is a hidden version of this file. Replace it?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (!QFile(newName).remove()) { QMessageBox::critical(this, tr("File operation failed"), tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(newName)); return false; } } else { return false; } } if (QFile::rename(oldName, newName)) { return true; } else { reportError(tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName))); return false; } } bool ModInfoDialog::unhideFile(const QString &oldName) { QString newName = oldName.left(oldName.length() - ModInfo::s_HiddenExt.length()); if (QFileInfo(newName).exists()) { if (QMessageBox::question(this, tr("Replace file?"), tr("There already is a visible version of this file. Replace it?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (!QFile(newName).remove()) { QMessageBox::critical(this, tr("File operation failed"), tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(newName)); return false; } } else { return false; } } if (QFile::rename(oldName, newName)) { return true; } else { reportError(tr("failed to rename %1 to %2").arg(QDir::toNativeSeparators(oldName)).arg(QDir::toNativeSeparators(newName))); return false; } } void ModInfoDialog::hideConflictFile() { if (hideFile(m_ConflictsContextItem->data(0, Qt::UserRole).toString())) { emit originModified(m_Origin->getID()); refreshLists(); } } void ModInfoDialog::unhideConflictFile() { if (unhideFile(m_ConflictsContextItem->data(0, Qt::UserRole).toString())) { emit originModified(m_Origin->getID()); refreshLists(); } } void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &pos) { m_ConflictsContextItem = ui->overwriteTree->itemAt(pos.x(), pos.y()); QMenu menu; if (m_ConflictsContextItem != NULL) { // offer to hide/unhide file, but not for files from archives // if (!m_ConflictsContextItem->data(0, Qt::UserRole + 1).toBool()) { if (!m_ConflictsContextItem->data(0, Qt::UserRole + 2).toBool()) { if (m_ConflictsContextItem->text(0).endsWith(ModInfo::s_HiddenExt)) { menu.addAction(tr("Un-Hide"), this, SLOT(unhideConflictFile())); } else { menu.addAction(tr("Hide"), this, SLOT(hideConflictFile())); } } menu.exec(ui->overwriteTree->mapToGlobal(pos)); } } void ModInfoDialog::on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int) { emit modOpen(item->data(1, Qt::UserRole).toString(), TAB_CONFLICTS); this->accept(); } void ModInfoDialog::on_refreshButton_clicked() { m_ModInfo->updateNXMInfo(); MessageDialog::showMessage(tr("Info requested, please wait"), this); } void ModInfoDialog::on_endorseBtn_clicked() { emit endorseMod(m_ModInfo); } void ModInfoDialog::on_nextButton_clicked() { emit modOpenNext(); this->accept(); } void ModInfoDialog::on_prevButton_clicked() { emit modOpenPrev(); this->accept(); }