@@ -200,6 +200,7 @@ MainWindow::MainWindow(QSettings &initSettings
200200 , m_OrganizerCore(organizerCore)
201201 , m_PluginContainer(pluginContainer)
202202 , m_DidUpdateMasterList(false )
203+ , m_ArchiveListWriter(std::bind(&MainWindow::saveArchiveList, this ))
203204{
204205 QWebEngineProfile::defaultProfile ()->setPersistentCookiesPolicy (QWebEngineProfile::NoPersistentCookies);
205206 QWebEngineProfile::defaultProfile ()->setHttpCacheMaximumSize (52428800 );
@@ -278,6 +279,8 @@ MainWindow::MainWindow(QSettings &initSettings
278279 ui->espList ->setItemDelegateForColumn (PluginList::COL_FLAGS, new GenericIconDelegate (ui->espList ));
279280 ui->espList ->installEventFilter (m_OrganizerCore.pluginList ());
280281
282+ ui->bsaList ->setLocalMoveOnly (true );
283+
281284 bool pluginListAdjusted = registerWidgetState (ui->espList ->objectName (), ui->espList ->header (), " plugin_list_state" );
282285 registerWidgetState (ui->dataTree ->objectName (), ui->dataTree ->header ());
283286 registerWidgetState (ui->downloadView ->objectName (),
@@ -358,6 +361,9 @@ MainWindow::MainWindow(QSettings &initSettings
358361
359362 connect (this , SIGNAL (styleChanged (QString)), this , SLOT (updateStyle (QString)));
360363
364+ m_CheckBSATimer.setSingleShot (true );
365+ connect (&m_CheckBSATimer, SIGNAL (timeout ()), this , SLOT (checkBSAList ()));
366+
361367 connect (ui->espList ->selectionModel (), SIGNAL (selectionChanged (QItemSelection, QItemSelection)), this , SLOT (esplistSelectionsChanged (QItemSelection)));
362368 connect (ui->modList ->selectionModel (), SIGNAL (selectionChanged (QItemSelection, QItemSelection)), this , SLOT (modlistSelectionsChanged (QItemSelection)));
363369
@@ -1142,17 +1148,17 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director
11421148 fileChild->setData (1 , Qt::UserRole, source);
11431149 fileChild->setData (1 , Qt::UserRole + 1 , originID);
11441150
1145- std::vector<int > alternatives = current->getAlternatives ();
1151+ std::vector<std::pair< int , std::wstring> > alternatives = current->getAlternatives ();
11461152
11471153 if (!alternatives.empty ()) {
11481154 std::wostringstream altString;
11491155 altString << ToWString (tr (" Also in: <br>" ));
1150- for (std::vector<int >::iterator altIter = alternatives.begin ();
1156+ for (std::vector<std::pair< int , std::wstring> >::iterator altIter = alternatives.begin ();
11511157 altIter != alternatives.end (); ++altIter) {
11521158 if (altIter != alternatives.begin ()) {
11531159 altString << " , " ;
11541160 }
1155- altString << " <span style=\" white-space: nowrap;\" ><i>" << m_OrganizerCore.directoryStructure ()->getOriginByID (* altIter).getName () << " </font></span>" ;
1161+ altString << " <span style=\" white-space: nowrap;\" ><i>" << m_OrganizerCore.directoryStructure ()->getOriginByID (altIter-> first ).getName () << " </font></span>" ;
11561162 }
11571163 fileChild->setToolTip (1 , QString (" %1" ).arg (ToQString (altString.str ())));
11581164 fileChild->setForeground (1 , QBrush (Qt::red));
@@ -1378,6 +1384,151 @@ static QStringList toStringList(InputIterator current, InputIterator end)
13781384 return result;
13791385}
13801386
1387+ void MainWindow::updateBSAList (const QStringList &defaultArchives, const QStringList &activeArchives)
1388+ {
1389+ m_DefaultArchives = defaultArchives;
1390+ ui->bsaList ->clear ();
1391+ ui->bsaList ->header ()->setSectionResizeMode (QHeaderView::ResizeToContents);
1392+ std::vector<std::pair<UINT32, QTreeWidgetItem*>> items;
1393+
1394+ BSAInvalidation * invalidation = m_OrganizerCore.managedGame ()->feature <BSAInvalidation>();
1395+ std::vector<FileEntry::Ptr> files = m_OrganizerCore.directoryStructure ()->getFiles ();
1396+
1397+ QStringList plugins = m_OrganizerCore.findFiles (" " , [](const QString &fileName) -> bool {
1398+ return fileName.endsWith (" .esp" , Qt::CaseInsensitive)
1399+ || fileName.endsWith (" .esm" , Qt::CaseInsensitive)
1400+ || fileName.endsWith (" .esl" , Qt::CaseInsensitive);
1401+ });
1402+
1403+ auto hasAssociatedPlugin = [&](const QString &bsaName) -> bool {
1404+ for (const QString &pluginName : plugins) {
1405+ QFileInfo pluginInfo (pluginName);
1406+ if (bsaName.startsWith (QFileInfo (pluginName).baseName (), Qt::CaseInsensitive)
1407+ && (m_OrganizerCore.pluginList ()->state (pluginInfo.fileName ()) == IPluginList::STATE_ACTIVE)) {
1408+ return true ;
1409+ }
1410+ }
1411+ return false ;
1412+ };
1413+
1414+ for (FileEntry::Ptr current : files) {
1415+ QFileInfo fileInfo (ToQString (current->getName ().c_str ()));
1416+
1417+ if (fileInfo.suffix ().toLower () == " bsa" || fileInfo.suffix ().toLower () == " ba2" ) {
1418+ int index = activeArchives.indexOf (fileInfo.fileName ());
1419+ if (index == -1 ) {
1420+ index = 0xFFFF ;
1421+ }
1422+ else {
1423+ index += 2 ;
1424+ }
1425+
1426+ if ((invalidation != nullptr ) && invalidation->isInvalidationBSA (fileInfo.fileName ())) {
1427+ index = 1 ;
1428+ }
1429+
1430+ int originId = current->getOrigin ();
1431+ FilesOrigin & origin = m_OrganizerCore.directoryStructure ()->getOriginByID (originId);
1432+
1433+ QTreeWidgetItem * newItem = new QTreeWidgetItem (QStringList ()
1434+ << fileInfo.fileName ()
1435+ << ToQString (origin.getName ()));
1436+ newItem->setData (0 , Qt::UserRole, index);
1437+ newItem->setData (1 , Qt::UserRole, originId);
1438+ newItem->setFlags (newItem->flags () & ~(Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable));
1439+ newItem->setCheckState (0 , (index != -1 ) ? Qt::Checked : Qt::Unchecked);
1440+ newItem->setData (0 , Qt::UserRole, false );
1441+ if (m_OrganizerCore.settings ().forceEnableCoreFiles ()
1442+ && defaultArchives.contains (fileInfo.fileName ())) {
1443+ newItem->setCheckState (0 , Qt::Checked);
1444+ newItem->setDisabled (true );
1445+ newItem->setData (0 , Qt::UserRole, true );
1446+ } else if (fileInfo.fileName ().compare (" update.bsa" , Qt::CaseInsensitive) == 0 ) {
1447+ newItem->setCheckState (0 , Qt::Checked);
1448+ newItem->setDisabled (true );
1449+ } else if (hasAssociatedPlugin (fileInfo.fileName ())) {
1450+ newItem->setCheckState (0 , Qt::Checked);
1451+ newItem->setDisabled (true );
1452+ } else {
1453+ newItem->setCheckState (0 , Qt::Unchecked);
1454+ newItem->setDisabled (true );
1455+ }
1456+ if (index < 0 ) index = 0 ;
1457+
1458+ UINT32 sortValue = ((origin.getPriority () & 0xFFFF ) << 16 ) | (index & 0xFFFF );
1459+ items.push_back (std::make_pair (sortValue, newItem));
1460+ }
1461+ }
1462+ std::sort (items.begin (), items.end (), BySortValue);
1463+
1464+ for (auto iter = items.begin (); iter != items.end (); ++iter) {
1465+ int originID = iter->second ->data (1 , Qt::UserRole).toInt ();
1466+
1467+ FilesOrigin origin = m_OrganizerCore.directoryStructure ()->getOriginByID (originID);
1468+ QString modName (" data" );
1469+ unsigned int modIndex = ModInfo::getIndex (ToQString (origin.getName ()));
1470+ if (modIndex != UINT_MAX) {
1471+ ModInfo::Ptr modInfo = ModInfo::getByIndex (modIndex);
1472+ modName = modInfo->name ();
1473+ }
1474+ QList<QTreeWidgetItem*> items = ui->bsaList ->findItems (modName, Qt::MatchFixedString);
1475+ QTreeWidgetItem * subItem = nullptr ;
1476+ if (items.length () > 0 ) {
1477+ subItem = items.at (0 );
1478+ }
1479+ else {
1480+ subItem = new QTreeWidgetItem (QStringList (modName));
1481+ subItem->setFlags (subItem->flags () & ~Qt::ItemIsDragEnabled);
1482+ ui->bsaList ->addTopLevelItem (subItem);
1483+ }
1484+ subItem->addChild (iter->second );
1485+ subItem->setExpanded (true );
1486+ }
1487+ checkBSAList ();
1488+ }
1489+
1490+ void MainWindow::checkBSAList ()
1491+ {
1492+ DataArchives * archives = m_OrganizerCore.managedGame ()->feature <DataArchives>();
1493+
1494+ if (archives != nullptr ) {
1495+ ui->bsaList ->blockSignals (true );
1496+ ON_BLOCK_EXIT ([&]() { ui->bsaList ->blockSignals (false ); });
1497+
1498+ QStringList defaultArchives = archives->archives (m_OrganizerCore.currentProfile ());
1499+
1500+ bool warning = false ;
1501+
1502+ for (int i = 0 ; i < ui->bsaList ->topLevelItemCount (); ++i) {
1503+ bool modWarning = false ;
1504+ QTreeWidgetItem * tlItem = ui->bsaList ->topLevelItem (i);
1505+ for (int j = 0 ; j < tlItem->childCount (); ++j) {
1506+ QTreeWidgetItem * item = tlItem->child (j);
1507+ QString filename = item->text (0 );
1508+ item->setIcon (0 , QIcon ());
1509+ item->setToolTip (0 , QString ());
1510+
1511+ if (item->checkState (0 ) == Qt::Unchecked) {
1512+ if (defaultArchives.contains (filename)) {
1513+ item->setIcon (0 , QIcon (" :/MO/gui/warning" ));
1514+ item->setToolTip (0 , tr (" This bsa is enabled in the ini file so it may be required!" ));
1515+ modWarning = true ;
1516+ }
1517+ }
1518+ }
1519+ if (modWarning) {
1520+ ui->bsaList ->expandItem (ui->bsaList ->topLevelItem (i));
1521+ warning = true ;
1522+ }
1523+ }
1524+ if (warning) {
1525+ ui->tabWidget ->setTabIcon (1 , QIcon (" :/MO/gui/warning" ));
1526+ } else {
1527+ ui->tabWidget ->setTabIcon (1 , QIcon ());
1528+ }
1529+ }
1530+ }
1531+
13811532void MainWindow::saveModMetas ()
13821533{
13831534 for (unsigned int i = 0 ; i < ModInfo::getNumMods (); ++i) {
@@ -1524,10 +1675,12 @@ void MainWindow::on_tabWidget_currentChanged(int index)
15241675 if (index == 0 ) {
15251676 m_OrganizerCore.refreshESPList ();
15261677 } else if (index == 1 ) {
1527- refreshDataTree ();
1678+ m_OrganizerCore. refreshBSAList ();
15281679 } else if (index == 2 ) {
1529- refreshSaveList ();
1680+ refreshDataTree ();
15301681 } else if (index == 3 ) {
1682+ refreshSaveList ();
1683+ } else if (index == 4 ) {
15311684 ui->downloadView ->scrollToBottom ();
15321685 }
15331686}
@@ -1806,6 +1959,7 @@ void MainWindow::modorder_changed()
18061959 }
18071960 m_OrganizerCore.refreshBSAList ();
18081961 m_OrganizerCore.currentProfile ()->modlistWriter ().write ();
1962+ m_ArchiveListWriter.write ();
18091963 m_OrganizerCore.directoryStructure ()->getFileRegister ()->sortOrigins ();
18101964
18111965 { // refresh selection
@@ -2741,6 +2895,27 @@ void MainWindow::replaceCategories_MenuHandler() {
27412895 refreshFilters ();
27422896}
27432897
2898+ void MainWindow::saveArchiveList ()
2899+ {
2900+ if (m_OrganizerCore.isArchivesInit ()) {
2901+ SafeWriteFile archiveFile (m_OrganizerCore.currentProfile ()->getArchivesFileName ());
2902+ for (int i = 0 ; i < ui->bsaList ->topLevelItemCount (); ++i) {
2903+ QTreeWidgetItem * tlItem = ui->bsaList ->topLevelItem (i);
2904+ for (int j = 0 ; j < tlItem->childCount (); ++j) {
2905+ QTreeWidgetItem * item = tlItem->child (j);
2906+ if (item->checkState (0 ) == Qt::Checked) {
2907+ archiveFile->write (item->text (0 ).toUtf8 ().append (" \r\n " ));
2908+ }
2909+ }
2910+ }
2911+ if (archiveFile.commitIfDifferent (m_ArchiveListHash)) {
2912+ qDebug (" %s saved" , qPrintable (QDir::toNativeSeparators (m_OrganizerCore.currentProfile ()->getArchivesFileName ())));
2913+ }
2914+ } else {
2915+ qWarning (" archive list not initialised" );
2916+ }
2917+ }
2918+
27442919void MainWindow::checkModsForUpdates ()
27452920{
27462921 statusBar ()->show ();
@@ -3588,8 +3763,8 @@ void MainWindow::previewDataFile()
35883763 };
35893764
35903765 addFunc (file->getOrigin ());
3591- for (int i : file->getAlternatives ()) {
3592- addFunc (i );
3766+ for (auto alt : file->getAlternatives ()) {
3767+ addFunc (alt. first );
35933768 }
35943769 if (preview.numVariants () > 0 ) {
35953770 preview.exec ();
@@ -3957,6 +4132,12 @@ void MainWindow::displayColumnSelection(const QPoint &pos)
39574132 }
39584133}
39594134
4135+ void MainWindow::on_bsaList_itemChanged (QTreeWidgetItem*, int )
4136+ {
4137+ m_ArchiveListWriter.write ();
4138+ m_CheckBSATimer.start (500 );
4139+ }
4140+
39604141void MainWindow::on_actionProblems_triggered ()
39614142{
39624143 ProblemsDialog problems (m_PluginContainer.plugins <IPluginDiagnose>(), this );
@@ -4543,6 +4724,12 @@ void MainWindow::on_categoriesOrBtn_toggled(bool checked)
45434724 }
45444725}
45454726
4727+ void MainWindow::on_managedArchiveLabel_linkHovered (const QString&)
4728+ {
4729+ QToolTip::showText (QCursor::pos (),
4730+ ui->managedArchiveLabel ->toolTip ());
4731+ }
4732+
45464733void MainWindow::dragEnterEvent (QDragEnterEvent *event)
45474734{
45484735 // Accept copy or move drags to the download window. Link drags are not
0 commit comments