cemu-DS4Windows/DS4Windows/DS4Forms/ViewModels/AutoProfilesViewModel.cs

546 lines
19 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DS4WinWPF.DS4Forms.ViewModels
{
public class AutoProfilesViewModel
{
private object _colLockobj = new object();
private ObservableCollection<ProgramItem> programColl;
private AutoProfileHolder autoProfileHolder;
private ProfileList profileList;
private int selectedIndex = -1;
private ProgramItem selectedItem;
private HashSet<string> existingapps;
public ObservableCollection<ProgramItem> ProgramColl { get => programColl; }
public AutoProfileHolder AutoProfileHolder { get => autoProfileHolder; }
public int SelectedIndex { get => selectedIndex; set => selectedIndex = value; }
public ProgramItem SelectedItem
{
get => selectedItem;
set
{
selectedItem = value;
CurrentItemChange?.Invoke(this, value);
}
}
public ProfileList ProfileList { get => profileList; }
public delegate void CurrentItemChangeHandler(AutoProfilesViewModel sender, ProgramItem item);
public event CurrentItemChangeHandler CurrentItemChange;
public event EventHandler SearchFinished;
public delegate void AutoProfileStateHandler(AutoProfilesViewModel sender, bool state);
public event AutoProfileStateHandler AutoProfileSystemChange;
public bool RevertDefaultProfileOnUnknown
{
get => DS4Windows.Global.AutoProfileRevertDefaultProfile;
set => DS4Windows.Global.AutoProfileRevertDefaultProfile = value;
}
public AutoProfilesViewModel(AutoProfileHolder autoProfileHolder, ProfileList profileList)
{
programColl = new ObservableCollection<ProgramItem>();
existingapps = new HashSet<string>();
this.autoProfileHolder = autoProfileHolder;
this.profileList = profileList;
PopulateCurrentEntries();
BindingOperations.EnableCollectionSynchronization(programColl, _colLockobj);
}
private void PopulateCurrentEntries()
{
foreach(AutoProfileEntity entry in autoProfileHolder.AutoProfileColl)
{
ProgramItem item = new ProgramItem(entry.Path, entry);
programColl.Add(item);
existingapps.Add(entry.Path);
}
}
public void RemoveUnchecked()
{
AutoProfileSystemChange?.Invoke(this, false);
programColl.Clear();
existingapps.Clear();
PopulateCurrentEntries();
AutoProfileSystemChange?.Invoke(this, true);
}
public async void AddProgramsFromStartMenu()
{
AutoProfileSystemChange?.Invoke(this, false);
await Task.Run(() =>
{
AddFromStartMenu(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu) + "\\Programs");
});
SearchFinished?.Invoke(this, EventArgs.Empty);
AutoProfileSystemChange?.Invoke(this, true);
}
public async void AddProgramsFromSteam(string location)
{
AutoProfileSystemChange?.Invoke(this, false);
await Task.Run(() =>
{
AddAppsFromLocation(location);
});
SearchFinished?.Invoke(this, EventArgs.Empty);
AutoProfileSystemChange?.Invoke(this, true);
}
public async void AddProgramsFromDir(string location)
{
AutoProfileSystemChange?.Invoke(this, false);
await Task.Run(() =>
{
AddAppsFromLocation(location);
});
SearchFinished?.Invoke(this, EventArgs.Empty);
AutoProfileSystemChange?.Invoke(this, true);
}
public async void AddProgramExeLocation(string location)
{
AutoProfileSystemChange?.Invoke(this, false);
await Task.Run(() =>
{
AddAppExeLocation(location);
});
SearchFinished?.Invoke(this, EventArgs.Empty);
AutoProfileSystemChange?.Invoke(this, true);
}
private void AddFromStartMenu(string path)
{
List<string> lnkpaths = new List<string>();
lnkpaths.AddRange(Directory.GetFiles(path, "*.lnk", SearchOption.AllDirectories));
lnkpaths.AddRange(Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu) + "\\Programs", "*.lnk", SearchOption.AllDirectories));
List<string> exepaths = new List<string>();
foreach(string link in lnkpaths)
{
string target = GetTargetPath(link);
exepaths.Add(target);
}
ScanApps(exepaths);
}
private void AddAppsFromLocation(string path)
{
List<string> exepaths = new List<string>();
exepaths.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.AllDirectories));
ScanApps(exepaths);
}
private void AddAppExeLocation(string path)
{
List<string> exepaths = new List<string>();
exepaths.Add(path);
ScanApps(exepaths, checkexisting: false, skipsetupapps: false);
}
private void ScanApps(List<string> exepaths, bool checkexisting = true,
bool skipsetupapps = true)
{
foreach (string target in exepaths)
{
bool skip = !File.Exists(target) || Path.GetExtension(target) != ".exe";
skip = skip || (skipsetupapps && (target.Contains("etup") || target.Contains("dotnet") || target.Contains("SETUP")
|| target.Contains("edist") || target.Contains("nstall") || string.IsNullOrEmpty(target)));
skip = skip || (checkexisting && existingapps.Contains(target));
if (!skip)
{
ProgramItem item = new ProgramItem(target);
/*if (autoProfileHolder.AutoProfileDict.TryGetValue(target, out AutoProfileEntity autoEntity))
{
item.MatchedAutoProfile = autoEntity;
}
*/
programColl.Add(item);
existingapps.Add(target);
}
}
}
public void CreateAutoProfileEntry(ProgramItem item)
{
if (item.MatchedAutoProfile == null)
{
AutoProfileEntity tempEntry = new AutoProfileEntity(item.Path, item.Title);
tempEntry.Turnoff = item.Turnoff;
int tempindex = item.SelectedIndexCon1;
tempEntry.ProfileNames[0] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon2;
tempEntry.ProfileNames[1] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon3;
tempEntry.ProfileNames[2] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon4;
tempEntry.ProfileNames[3] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
item.MatchedAutoProfile = tempEntry;
autoProfileHolder.AutoProfileColl.Add(item.MatchedAutoProfile);
}
}
public void PersistAutoProfileEntry(ProgramItem item)
{
if (item.MatchedAutoProfile != null)
{
AutoProfileEntity tempEntry = item.MatchedAutoProfile;
int tempindex = item.SelectedIndexCon1;
tempEntry.ProfileNames[0] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon2;
tempEntry.ProfileNames[1] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon3;
tempEntry.ProfileNames[2] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
tempindex = item.SelectedIndexCon4;
tempEntry.ProfileNames[3] = tempindex > 0 ? profileList.ProfileListCol[tempindex - 1].Name :
AutoProfileEntity.NONE_STRING;
}
}
public void RemoveAutoProfileEntry(ProgramItem item)
{
autoProfileHolder.AutoProfileColl.Remove(item.MatchedAutoProfile);
item.MatchedAutoProfile = null;
}
private string GetTargetPath(string filePath)
{
string targetPath = ResolveMsiShortcut(filePath);
if (targetPath == null)
{
targetPath = ResolveShortcut(filePath);
}
return targetPath;
}
public string ResolveShortcutAndArgument(string filePath)
{
Type t = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")); // Windows Script Host Shell Object
dynamic shell = Activator.CreateInstance(t);
string result;
try
{
var shortcut = shell.CreateShortcut(filePath);
result = shortcut.TargetPath + " " + shortcut.Arguments;
Marshal.FinalReleaseComObject(shortcut);
}
catch (COMException)
{
// A COMException is thrown if the file is not a valid shortcut (.lnk) file
result = null;
}
finally
{
Marshal.FinalReleaseComObject(shell);
}
return result;
}
public string ResolveMsiShortcut(string file)
{
StringBuilder product = new StringBuilder(NativeMethods2.MaxGuidLength + 1);
StringBuilder feature = new StringBuilder(NativeMethods2.MaxFeatureLength + 1);
StringBuilder component = new StringBuilder(NativeMethods2.MaxGuidLength + 1);
NativeMethods2.MsiGetShortcutTarget(file, product, feature, component);
int pathLength = NativeMethods2.MaxPathLength;
StringBuilder path = new StringBuilder(pathLength);
NativeMethods2.InstallState installState = NativeMethods2.MsiGetComponentPath(product.ToString(), component.ToString(), path, ref pathLength);
if (installState == NativeMethods2.InstallState.Local)
{
return path.ToString();
}
else
{
return null;
}
}
public string ResolveShortcut(string filePath)
{
Type t = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")); // Windows Script Host Shell Object
dynamic shell = Activator.CreateInstance(t);
string result;
try
{
var shortcut = shell.CreateShortcut(filePath);
result = shortcut.TargetPath;
Marshal.FinalReleaseComObject(shortcut);
}
catch (COMException)
{
// A COMException is thrown if the file is not a valid shortcut (.lnk) file
result = null;
}
finally
{
Marshal.FinalReleaseComObject(shell);
}
return result;
}
public bool MoveItemUpDown(ProgramItem item, int moveDirection)
{
// Move autoprofile item up (-1) or down (1) both in listView (programColl) and in autoProfileHolder data structure (will be written into AutoProfiles.xml file)
bool itemMoved = true;
int oldIdx = programColl.IndexOf(item);
if (moveDirection == -1 && oldIdx > 0 && oldIdx < autoProfileHolder.AutoProfileColl.Count)
{
programColl.Move(oldIdx, oldIdx - 1);
autoProfileHolder.AutoProfileColl.Move(oldIdx, oldIdx - 1);
}
else if (moveDirection == 1 && oldIdx >= 0 && oldIdx < programColl.Count - 1 && oldIdx < autoProfileHolder.AutoProfileColl.Count - 1)
{
programColl.Move(oldIdx, oldIdx + 1);
autoProfileHolder.AutoProfileColl.Move(oldIdx, oldIdx + 1);
}
else
itemMoved = false;
return itemMoved;
}
}
public class ProgramItem
{
private string path;
private string path_lowercase;
private string filename;
private string title;
private string title_lowercase;
private AutoProfileEntity matchedAutoProfile;
private ImageSource exeicon;
private bool turnoff;
public string Path { get => path; set => path = value; }
public string Title { get => title;
set
{
if (title == value) return;
title = value;
if (matchedAutoProfile != null)
{
matchedAutoProfile.Title = value;
}
TitleChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler TitleChanged;
public AutoProfileEntity MatchedAutoProfile
{
get => matchedAutoProfile;
set
{
matchedAutoProfile = value;
if (matchedAutoProfile != null)
{
title = matchedAutoProfile.Title ?? string.Empty;
title_lowercase = title.ToLower();
}
MatchedAutoProfileChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler MatchedAutoProfileChanged;
public delegate void AutoProfileHandler(ProgramItem sender, bool added);
public event AutoProfileHandler AutoProfileAction;
public string Filename { get => filename; }
public ImageSource Exeicon { get => exeicon; }
public bool Turnoff
{
get
{
bool result = turnoff;
if (matchedAutoProfile != null)
{
result = matchedAutoProfile.Turnoff;
}
return result;
}
set
{
turnoff = value;
if (matchedAutoProfile != null)
{
matchedAutoProfile.Turnoff = value;
}
TurnoffChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler TurnoffChanged;
public bool Exists
{
get => matchedAutoProfile != null;
}
public event EventHandler ExistsChanged;
private int selectedIndexCon1 = 0;
private int selectedIndexCon2 = 0;
private int selectedIndexCon3 = 0;
private int selectedIndexCon4 = 0;
public int SelectedIndexCon1
{
get => selectedIndexCon1;
set
{
if (selectedIndexCon1 == value) return;
selectedIndexCon1 = value;
}
}
public int SelectedIndexCon2
{
get => selectedIndexCon2;
set
{
if (selectedIndexCon2 == value) return;
selectedIndexCon2 = value;
}
}
public int SelectedIndexCon3
{
get => selectedIndexCon3;
set
{
if (selectedIndexCon3 == value) return;
selectedIndexCon3 = value;
}
}
public int SelectedIndexCon4
{
get => selectedIndexCon4;
set
{
if (selectedIndexCon4 == value) return;
selectedIndexCon4 = value;
}
}
public ProgramItem(string path, AutoProfileEntity autoProfileEntity = null)
{
this.path = path;
this.path_lowercase = path.ToLower();
filename = System.IO.Path.GetFileNameWithoutExtension(path);
this.matchedAutoProfile = autoProfileEntity;
if (autoProfileEntity != null)
{
title = autoProfileEntity.Title;
title_lowercase = title.ToLower();
turnoff = autoProfileEntity.Turnoff;
}
if (File.Exists(path))
{
using (Icon ico = Icon.ExtractAssociatedIcon(path))
{
exeicon = Imaging.CreateBitmapSourceFromHIcon(ico.Handle, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
exeicon.Freeze();
}
}
MatchedAutoProfileChanged += ProgramItem_MatchedAutoProfileChanged;
}
private void ProgramItem_MatchedAutoProfileChanged(object sender, EventArgs e)
{
if (matchedAutoProfile == null)
{
selectedIndexCon1 = 0;
selectedIndexCon2 = 0;
selectedIndexCon3 = 0;
selectedIndexCon4 = 0;
}
ExistsChanged?.Invoke(this, EventArgs.Empty);
}
}
[SuppressUnmanagedCodeSecurity]
class NativeMethods2
{
[DllImport("msi.dll", CharSet = CharSet.Auto)]
public static extern uint MsiGetShortcutTarget(string targetFile, StringBuilder productCode, StringBuilder featureID, StringBuilder componentCode);
[DllImport("msi.dll", CharSet = CharSet.Auto)]
public static extern InstallState MsiGetComponentPath(string productCode, string componentCode, StringBuilder componentPath, ref int componentPathBufferSize);
public const int MaxFeatureLength = 38;
public const int MaxGuidLength = 38;
public const int MaxPathLength = 1024;
public enum InstallState
{
NotUsed = -7,
BadConfig = -6,
Incomplete = -5,
SourceAbsent = -4,
MoreData = -3,
InvalidArg = -2,
Unknown = -1,
Broken = 0,
Advertised = 1,
Removed = 1,
Absent = 2,
Local = 3,
Source = 4,
Default = 5
}
}
}