Add desktop stuff

Loose plugins belonging to known mods will no longer be magically added
to list.
Rewrite how Plugins/LoadOrder interacts with model to prevent clobbering
the listview on save/update.
Fix 2 bugs in enabling/disabling a mod
If src="file.esp" && dest="", try to do the right thing.
This commit is contained in:
Daniel O'Neill 2023-01-24 17:10:50 -08:00
parent 1b3f9e5aca
commit 734e7bf498
6 changed files with 113 additions and 9 deletions

View file

@ -191,6 +191,28 @@ Item {
return results;
}
function getFilesEndingWith(suffixes)
{
if( !checkConnection ) return;
let qstr = "SELECT fileId, modId, relative, source, dest, priority FROM files";
let token = ' WHERE ';
let args = [];
// I has feels this is gonna get beat tf up...
suffixes.forEach( function(d) {
qstr += token + "dest LIKE '%' || ?";
token = ' OR ';
args.push(d);
} );
let q = conn.query(qstr, args);
const results = q.toArray();
q.destroy();
return results;
}
function insertFile(modId, fileInfo)
{
if( !checkConnection ) return;

View file

@ -131,8 +131,9 @@ Item {
for( let a=0; a < pluginsTable.model.length; a++ )
{
const ent = visualModel.items.get(a);
const modent = ent.model.modelData;
//console.log(`Visual item ${a}: ${JSON.stringify(modent)}`);
console.log(`Visual item ${a}: ${JSON.stringify(ent.model.index)}`);
//const modent = ent.model.modelData;
const modent = mmodel.get(ent.model.index);
if( modent['filepath'].toLowerCase().endsWith('.esp')
|| modent['filepath'].toLowerCase().endsWith('.esl'))
@ -155,7 +156,8 @@ Item {
for( let a=0; a < pluginsTable.model.length; a++ )
{
const ent = visualModel.items.get(a);
const modent = ent.model.modelData;
//const modent = ent.model.modelData;
const modent = mmodel.get(ent.model.index);
const nent = { 'enabled':modent['enabled'], 'filename':modent['filepath'] };
if( modent['filepath'].toLowerCase().endsWith('.esm') )
@ -281,10 +283,60 @@ Item {
} // MouseArea
} // Component:pluginRowDelegate
// Try to smoothly update the model to avoid our scroll position getting
// clobbered whenever we save a change.
onModelChanged: function() {
// we key on the filename:
let mnames = model.map( m => m['filepath'] );
let toremove = [];
let lmmap = {};
for( let a=0; a < mmodel.count; a++ )
{
const dentry = mmodel.get(a);
let ent = { 'index':a, 'entry':dentry };
if( !mnames.includes(dentry['filepath']) )
toremove.unshift(a);
else
lmmap[ dentry['filepath'] ] = ent;
}
toremove.forEach( i => mmodel.remove(i) );
for( let b=0; b < mnames.length; b++ )
{
const n = mnames[b];
const nent = model[b];
if( !lmmap[ n ] )
{
mmodel.append( nent );
continue;
}
mmodel.set( lmmap[n]['index'], nent );
}
for( let c=0; c < mmodel.count; c++ )
{
const ent = visualModel.items.get(c);
if( c === ent.model.index )
continue;
console.log(`Visual item ${c}: ${JSON.stringify(ent.model.index)}`);
visualModel.items.move(c, ent.model.index);
}
}
ListModel {
id: mmodel
dynamicRoles: true
}
DelegateModel {
id: visualModel
model: pluginsTable.model
model: mmodel
delegate: pluginRowDelegate
}

View file

@ -188,7 +188,6 @@ function enableMod(mod)
let loadorder = Plugins.readLoadOrder();
files.forEach( function(f) {
let baseName = f['dest'];
let parts = f['dest'].split(/\//g);
if( parts.length > 1 )
baseName = parts.pop();
@ -233,7 +232,7 @@ function removePlugin(mod)
let loadorder = Plugins.readLoadOrder();
files.forEach( function(f) {
const parts = f['dest'].split(/\//g);
if( parts.length === 1 )
if( parts.length > 0 )
{
const baseName = parts.pop().toLowerCase();
@ -246,7 +245,7 @@ function removePlugin(mod)
}
let nlo = loadorder[sec].filter( m => m.toLowerCase() !== baseName );
if( nsec.length !== loadorder[sec].length )
if( nlo.length !== loadorder[sec].length )
{
loadorder[sec] = nlo;
updatePlugins = true;
@ -722,6 +721,11 @@ function installBasicMod(mod)
fdpath = parts.join('/');
}
if( fdpath.length === 0 )
fdpath = parts.pop();
if( !fdpath )
fdpath = f['pathname'];
const dpath = (sobj.gamePath + '/' + currentGameEntry['datadir'] + '/' + fdpath).replace(/\/\//g, '/');
console.log(`Extract "${f['pathname']}" -> "${dpath}"`);
fileMap[fpath] = dpath;

View file

@ -43,10 +43,14 @@ function readLoadOrder()
function writeLoadOrder(loadorder)
{
let written = [];
let output = ["# Generated by Quickmod"];
['masters', 'normal'].forEach( function(sec) {
loadorder[sec].forEach( function(ent) {
output.push(`${ent}`);
if( !written.includes(ent) )
output.push(`${ent}`);
written.push(ent);
} );
});
const res = output.join("\r\n");
@ -169,10 +173,14 @@ function readPlugins()
function writePlugins(plugins)
{
let written = [];
let output = ["# Generated by Quickmod"];
['masters', 'normal'].forEach( function(sec) {
plugins[sec].forEach( function(ent) {
output.push(`${ ent['enabled'] ? '*' : '' }${ent['filename']}`);
if( !written.includes(ent['filename']) )
output.push(`${ ent['enabled'] ? '*' : '' }${ent['filename']}`);
written.push(ent['filename']);
} );
});
const res = output.join("\r\n");
@ -197,6 +205,9 @@ function scanForLoose(plugins)
if( !contents )
return plugins; // ... weird?
// Don't add files that belong to a mod:
const justFiles = db.getFilesEndingWith(['.esm', '.esl', '.esp']).map( e => e['dest'].toLowerCase() );
const normalsLC = plugins['normal'].map( e => e['filename'].toLowerCase() );
const mastersLC = plugins['masters'].map( e => e['filename'].toLowerCase() );
@ -204,6 +215,9 @@ function scanForLoose(plugins)
{
const ent = contents[x];
const lcfn = ent['fileName'].toLowerCase();
if( justFiles.includes(lcfn) )
continue;
if( lcfn.endsWith('.esl') || lcfn.endsWith('.esp') )
{
if( !normalsLC.includes(lcfn) )

12
quickmod.desktop Normal file
View file

@ -0,0 +1,12 @@
[Desktop Entry]
Name=Quickmod
GenericName=Mod Manager
Comment=Mod manager for Skyrim SE, Skyrim VR, Fallout 4, and Fallout 4 VR
Keywords=mod;game;games;
Icon=quickmod
Type=Application
Categories=Game;
Exec=quickmod %u
StartupNotify=false
Terminal=false
MimeType=x-scheme-handler/nxm;

BIN
quickmod.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB