#include "nexusmoddialog.h" #include "core/log.h" #include "tablepushbutton.h" #include "ui_nexusmoddialog.h" #include #include #include #include #include #include #include #include namespace str = std::ranges; NexusModDialog::NexusModDialog(QWidget* parent) : QDialog(parent), ui(new Ui::NexusModDialog) { ui->setupUi(this); ui->files_widget->setLayout(new QVBoxLayout()); } NexusModDialog::~NexusModDialog() { delete ui; } void NexusModDialog::setupDialog(int app_id, int mod_id, const nexus::Page& page) { app_id_ = app_id; mod_id_ = mod_id; page_ = page; QString description = page.mod.description.c_str(); ui->description_box->setHtml(bbcodeToHtml(description)); QString changelog; for(const auto& [version, changes] : page.changelog) { changelog.append( (R"()" + version + R"(
    )").c_str()); for(const auto& change : changes) changelog.append(("
  • " + change + "
  • ").c_str()); changelog.append("

"); } ui->changelog_box->setHtml(changelog); for(auto child : ui->files_widget->children()) delete child; if(page.files.empty()) { auto layout = ui->files_widget->layout(); ui->files_widget->setLayout(new QVBoxLayout()); delete layout; return; } const QString mod_link = std::format( "" "Link To " "NexusMods Page", page.mod.domain_name, page.mod.mod_id) .c_str(); ui->link_label_desc->setText(mod_link); ui->link_label_changelog->setText(mod_link); ui->link_label_files->setText( std::format( "" "Link To NexusMods " "Page", page.mod.domain_name, page.mod.mod_id) .c_str()); std::vector files = page.files; std::sort(files.begin(), files.end(), [](nexus::File f1, nexus::File f2) { if(f1.category_id != f2.category_id) return f1.category_id < f2.category_id; return f1.name < f2.name; }); auto base_layout = new QVBoxLayout(); auto old_layout = ui->files_widget->layout(); delete old_layout; ui->files_widget->setLayout(base_layout); long cur_cat_id = -1; QString cur_cat_name = ""; for(const auto& file : files) { if(file.category_id != cur_cat_id) { cur_cat_id = file.category_id; cur_cat_name = file.category_name.empty() ? "Other" : file.category_name.c_str(); auto label = new QLabel(); label->setTextFormat(Qt::RichText); label->setText(R"()" + cur_cat_name + " Files"); base_layout->addWidget(label); } auto frame = new QFrame(); frame->setFrameShadow(QFrame::Plain); frame->setFrameShape(QFrame::Panel); auto frame_layout = new QVBoxLayout(); frame->setLayout(frame_layout); QString mod_text; mod_text.append((R"()" + file.name + "").c_str()); if(!file.version.empty()) mod_text.append(("
Version: " + file.version + "").c_str()); std::stringstream ss; ss << std::put_time(std::localtime(&file.uploaded_time), "%F %T"); mod_text.append(("
Upload Time: " + ss.str() + "").c_str()); QString size_string; long size = file.size_in_bytes; if(size < 1024) size_string = QString::number(size); else { long last_size = 0; int exp = 0; const std::vector units{ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; while(size > 1024 && exp < units.size()) { last_size = size; size /= 1024; exp++; } last_size /= 1.024; size_string = QString::number(size); const int first_digit = (last_size / 100) % 10; const int second_digit = (last_size / 10) % 10; if(first_digit != 0 || second_digit != 0) size_string += "." + QString::number(first_digit); if(second_digit != 0) size_string += QString::number(second_digit); size_string += " " + units[exp]; } mod_text.append("
Size: " + size_string + "
"); if(file.external_virus_scan_url.empty()) mod_text.append("No external virus scan
"); else mod_text.append( ("Virus scan link
") .c_str()); mod_text.append("

" + bbcodeToHtml(file.description.c_str()) + "
"); if(!file.changelog_html.empty()) mod_text.append("
Changes
" + bbcodeToHtml(file.changelog_html.c_str()) + "
"); auto text_label = new QLabel(); text_label->setTextFormat(Qt::RichText); text_label->setText(mod_text); text_label->setWordWrap(true); text_label->setTextInteractionFlags(text_label->textInteractionFlags() | Qt::TextSelectableByMouse); frame_layout->addWidget(text_label); auto manual_link_label = new QLabel(); manual_link_label->setTextFormat(Qt::RichText); manual_link_label->setText( std::format( "" "Manual Download " "Link", page.mod.domain_name, page.mod.mod_id, file.file_id) .c_str()); manual_link_label->setOpenExternalLinks(true); frame_layout->addWidget(manual_link_label); auto manager_link_label = new QLabel(); manager_link_label->setTextFormat(Qt::RichText); manager_link_label->setText(std::format( "" "Mod Manager Download " "Link", page.mod.domain_name, page.mod.mod_id, file.file_id) .c_str()); manager_link_label->setOpenExternalLinks(true); frame_layout->addWidget(manager_link_label); QSettings settings(QCoreApplication::applicationName()); settings.beginGroup("nexus"); const bool is_premium = settings.value("info_is_premium", false).toBool(); settings.endGroup(); if(is_premium) { auto download_button = new TablePushButton(file.file_id, file.file_id); download_button->setText("Download"); download_button->setIcon(QIcon::fromTheme("edit-download")); connect( download_button, &TablePushButton::clickedAt, this, &NexusModDialog::onDownloadClicked); frame_layout->addWidget(download_button); } frame_layout->addStretch(); base_layout->addWidget(frame); } base_layout->addStretch(); } QString NexusModDialog::bbcodeToHtml(const QString& bbcode) { QString html = bbcode; html.remove(QChar(0xFEFF)); html.remove("\ufeff"); html.replace("\xa0", " "); html.remove("\n"); std::vector> tokens = { { R"(\[center\])", R"(\[/center\])", R"(
\1
)" }, { R"(\[b\])", R"(\[/b\])", R"(\1)" }, { R"(\[i\])", R"(\[/i\])", R"(\1)" }, { R"(\[u\])", R"(\[/u\])", R"(\1)" }, { R"(\[s\])", R"(\[/s\])", R"(\1)" }, { R"(\[url\])", R"(\[/url\])", R"(\1)" }, { R"(\[url=(.*?)\])", R"(\[/url\])", R"(\2)" }, { R"(\[youtube\])", R"(\[/youtube\])", R"(https://www.youtube.com/watch?v=\1)" }, // { R"(\[img\])", R"(\[/img\])", R"()" }, { R"(\[img\])", R"(\[/img\])", R"( [\1] )" }, { R"(\[quote\])", R"(\[/quote\])", R"(
\1
)" }, { R"(\[quote=(.*?)\])", R"(\[/quote\])", R"(
\1\2
)" }, { R"(\[code\])", R"(\[/code\])", R"(
\1
)" }, { R"(\[list\])", R"(\[/list\])", R"(
    \1
)" }, { R"(\[list=1\])", R"(\[/list\])", R"(
    \1
)" }, { R"(\[li\])", R"(\[/li\])", R"(
  • \1
  • )" }, { R"(\[color=(.*?)\])", R"(\[/color\])", R"(\2)" }, { R"(\[size=(.*?)\])", R"(\[/size\])", R"(\2)" }, { R"(\[left\])", R"(\[/left\])", R"(\1)" } }; for(const auto& [begin, end, replace] : tokens) { while(html.contains(QRegularExpression(begin + R"((.*?))" + end))) html.replace(QRegularExpression(begin + "((?!" + begin + R"().*?))" + end), replace); } return html; } void NexusModDialog::onDownloadClicked(int file_id, int file_id_copy) { auto iter = str::find_if(page_.files, [file_id](auto f){return f.file_id == file_id;}); if(iter == page_.files.end()) Log::error(std::format("Failed to parse Nexus response for file: {}", file_id)); else emit modDownloadRequested(app_id_, mod_id_, file_id, page_.url.c_str(), iter->version.c_str()); }