v1.4.0 - 2020-06-10

This commit is contained in:
flash 2022-08-26 01:56:27 +02:00
parent 6633f72dd7
commit 69d797523d
6 changed files with 440 additions and 17 deletions

View file

@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace TopMostFriend {
public class BlacklistWindow : Form {
public readonly List<string> Blacklist = new List<string>();
public static string[] Display(string title, string[] items) {
using (BlacklistWindow blacklist = new BlacklistWindow(title, items)) {
if (blacklist.ShowDialog() == DialogResult.OK)
return blacklist.Blacklist.ToArray();
}
return null;
}
private const int SPACING = 6;
private const int BUTTON_WIDTH = 75;
private const int BUTTON_HEIGHT = 23;
private readonly Button AddButton;
private readonly Button EditButton;
private readonly Button RemoveButton;
private readonly ListBox BlacklistView;
public BlacklistWindow(string title, string[] items) {
Blacklist.AddRange(items);
Text = title;
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
StartPosition = FormStartPosition.CenterScreen;
AutoScaleMode = AutoScaleMode.Dpi;
ClientSize = new Size(410, 203);
MinimizeBox = MaximizeBox = false;
MinimumSize = Size;
DialogResult = DialogResult.Cancel;
BlacklistView = new ListBox {
TabIndex = 101,
IntegralHeight = false,
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
Location = new Point(BUTTON_WIDTH + (SPACING * 2), SPACING),
ClientSize = new Size(ClientSize.Width - BUTTON_WIDTH - (int)(SPACING * 3.5), ClientSize.Height - (int)(SPACING * 2.5)),
};
BlacklistView.SelectedIndexChanged += BlacklistView_SelectedIndexChanged;
BlacklistView.MouseDoubleClick += BlacklistView_MouseDoubleClick;
AddButton = new Button {
Anchor = AnchorStyles.Top | AnchorStyles.Left,
Text = @"Add",
ClientSize = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
Location = new Point(SPACING, SPACING),
TabIndex = 201,
};
EditButton = new Button {
Text = @"Edit",
Location = new Point(AddButton.Location.X, AddButton.Location.Y + AddButton.Height + SPACING),
Enabled = false,
Anchor = AddButton.Anchor,
ClientSize = AddButton.ClientSize,
TabIndex = 202,
};
RemoveButton = new Button {
Text = @"Remove",
Location = new Point(AddButton.Location.X, EditButton.Location.Y + AddButton.Height + SPACING),
Enabled = false,
Anchor = AddButton.Anchor,
ClientSize = AddButton.ClientSize,
TabIndex = 203,
};
AddButton.Click += AddButton_Click;
EditButton.Click += EditButton_Click;
RemoveButton.Click += RemoveButton_Click;
CancelButton = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
Text = @"Cancel",
Location = new Point(SPACING, ClientSize.Height - AddButton.ClientSize.Height - SPACING),
ClientSize = AddButton.ClientSize,
TabIndex = 10001,
};
Button acceptButton = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
Text = @"Done",
Location = new Point(SPACING, ClientSize.Height - ((AddButton.ClientSize.Height + SPACING) * 2)),
ClientSize = AddButton.ClientSize,
TabIndex = 10002,
};
acceptButton.Click += AcceptButton_Click;
Controls.AddRange(new Control[] {
BlacklistView, AddButton, EditButton, RemoveButton, (Control)(AcceptButton = acceptButton), (Control)CancelButton,
});
RefreshList();
}
private void AcceptButton_Click(object sender, EventArgs e) {
DialogResult = DialogResult.OK;
Close();
}
private class BlacklistEditorWindow : Form {
public string Original { get; }
public string String { get => TextBox.Text; }
private TextBox TextBox;
public BlacklistEditorWindow(string original = null) {
Original = original ?? string.Empty;
Text = original == null ? @"Adding new entry..." : $@"Editing {original}...";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedToolWindow;
ClientSize = new Size(500, 39);
MaximizeBox = MinimizeBox = false;
MaximumSize = MinimumSize = Size;
Button cancelButton = new Button {
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
Location = new Point(ClientSize.Width - 75 - 8, 8),
Name = @"cancelButton",
Size = new Size(75, 23),
TabIndex = 102,
Text = @"Cancel",
};
cancelButton.Click += (s, e) => { DialogResult = DialogResult.Cancel; Close(); };
Button saveButton = new Button {
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
Location = new Point(cancelButton.Location.X - 75 - 5, cancelButton.Location.Y),
Name = @"saveButton",
Size = new Size(75, 23),
TabIndex = 101,
Text = @"Save",
};
saveButton.Click += (s, e) => { DialogResult = DialogResult.OK; Close(); };
TextBox = new TextBox {
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
Location = new Point(8, 9),
Name = @"deviceSelection",
Size = new Size(ClientSize.Width - 8 - cancelButton.Width - saveButton.Width - 19, 23),
TabIndex = 100,
Text = Original,
};
AcceptButton = saveButton;
CancelButton = cancelButton;
Controls.AddRange(new Control[] {
TextBox, saveButton, cancelButton,
});
}
}
private void AddButton_Click(object sender, EventArgs e) {
using (BlacklistEditorWindow bew = new BlacklistEditorWindow()) {
if (bew.ShowDialog() == DialogResult.OK)
SafeAdd(bew.String);
}
RefreshList();
}
private void EditButton_Click(object sender, EventArgs e) {
using (BlacklistEditorWindow bew = new BlacklistEditorWindow(BlacklistView.SelectedItem as string)) {
if (bew.ShowDialog() == DialogResult.OK) {
Blacklist.Remove(bew.Original);
SafeAdd(bew.String);
}
}
RefreshList();
}
private void SafeAdd(string str) {
if (!Blacklist.Contains(str))
Blacklist.Add(str);
}
private void RemoveButton_Click(object sender, EventArgs e) {
Blacklist.Remove(BlacklistView.SelectedItem as string);
RefreshList();
}
private void BlacklistView_MouseDoubleClick(object sender, MouseEventArgs e) {
if (BlacklistView.SelectedIndex < 0
|| BlacklistView.IndexFromPoint(e.Location) != BlacklistView.SelectedIndex)
return;
EditButton.PerformClick();
}
private void BlacklistView_SelectedIndexChanged(object sender, EventArgs e) {
EditButton.Enabled = RemoveButton.Enabled = BlacklistView.SelectedIndex >= 0;
}
public void RefreshList() {
object selected = BlacklistView.SelectedValue;
BlacklistView.Items.Clear();
BlacklistView.Items.AddRange(Blacklist.ToArray());
if (selected != null && BlacklistView.Items.Contains(selected))
BlacklistView.SelectedIndex = BlacklistView.Items.IndexOf(selected);
}
}
}

View file

@ -14,6 +14,7 @@ namespace TopMostFriend {
public static class Program {
private static NotifyIcon SysIcon;
private static HotKeyWindow HotKeys;
private static Icon OriginalIcon;
private static int InitialItems = 0;
private const string GUID =
@ -25,17 +26,25 @@ namespace TopMostFriend {
private static readonly Mutex GlobalMutex = new Mutex(true, GUID);
public const string FOREGROUND_HOTKEY_ATOM = @"{86795D64-770D-4BD6-AA26-FA638FBAABCF}";
#if DEBUG
public const string FOREGROUND_HOTKEY_SETTING = @"ForegroundHotKey_DEBUG";
#else
public const string FOREGROUND_HOTKEY_SETTING = @"ForegroundHotKey";
#endif
public const string PROCESS_SEPARATOR_SETTING = @"InsertProcessSeparator";
public const string LIST_SELF_SETTING = @"ListSelf";
public const string SHOW_EMPTY_WINDOW_SETTING = @"ShowEmptyWindowTitles";
public const string SHOW_EXPLORER_SETTING = @"ShowExplorerMisc";
public const string LIST_BACKGROUND_PATH_SETTING = @"ListBackgroundPath";
public const string LIST_BACKGROUND_LAYOUT_SETTING = @"ListBackgroundLayout";
public const string ALWAYS_ADMIN_SETTING = @"RunAsAdministrator";
public const string TOGGLE_BALLOON_SETTING = @"ShowNotificationOnHotKey";
public static readonly bool ToggleBalloonDefault = Environment.OSVersion.Version.Major < 10;
public const string SHIFT_CLICK_BLACKLIST = @"ShiftClickToBlacklist";
public const string TITLE_BLACKLIST = @"TitleBlacklist";
public const string SHOW_HOTKEY_ICON = @"ShowHotkeyIcon";
private static readonly List<string> TitleBlacklist = new List<string>();
[STAThread]
public static void Main(string[] args) {
@ -62,15 +71,37 @@ namespace TopMostFriend {
Settings.SetDefault(FOREGROUND_HOTKEY_SETTING, 0);
Settings.SetDefault(ALWAYS_ADMIN_SETTING, false);
Settings.SetDefault(SHIFT_CLICK_BLACKLIST, true);
Settings.SetDefault(SHOW_HOTKEY_ICON, true);
// Defaulting to false on Windows 10 because it uses the stupid, annoying and intrusive new Android style notification system
// This would fucking piledrive the notification history and also just be annoying in general because intrusive
Settings.SetDefault(TOGGLE_BALLOON_SETTING, ToggleBalloonDefault);
if(!Settings.Has(TITLE_BLACKLIST)) {
List<string> titles = new List<string> { @"Program Manager" };
if(Environment.OSVersion.Version.Major >= 10)
titles.Add(@"Windows Shell Experience Host");
if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)
titles.Add(@"Start menu");
else if(Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major < 6 && Environment.OSVersion.Version.Minor < 2))
titles.Add(@"Start");
Settings.Set(TITLE_BLACKLIST, titles.ToArray());
}
if (Settings.Get<bool>(ALWAYS_ADMIN_SETTING) && !IsElevated()) {
Elevate();
return;
}
TitleBlacklist.Clear();
string[] titleBlacklist = Settings.Get(TITLE_BLACKLIST);
if (titleBlacklist != null)
ApplyBlacklistedTitles(titleBlacklist);
string backgroundPath = Settings.Get(LIST_BACKGROUND_PATH_SETTING, string.Empty);
Image backgroundImage = null;
ImageLayout backgroundLayout = 0;
@ -82,9 +113,11 @@ namespace TopMostFriend {
} catch {}
}
OriginalIcon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
SysIcon = new NotifyIcon {
Visible = true,
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath),
Icon = OriginalIcon,
Text = @"Top Most Application Manager",
};
SysIcon.MouseDown += SysIcon_MouseDown;
@ -114,6 +147,33 @@ namespace TopMostFriend {
Shutdown();
}
public static void AddBlacklistedTitle(string title) {
lock (TitleBlacklist)
TitleBlacklist.Add(title);
}
public static void RemoveBlacklistedTitle(string title) {
lock (TitleBlacklist)
TitleBlacklist.RemoveAll(x => x == title);
}
public static void ApplyBlacklistedTitles(string[] arr) {
lock (TitleBlacklist) {
TitleBlacklist.Clear();
TitleBlacklist.AddRange(arr);
}
}
public static bool CheckBlacklistedTitles(string title) {
lock (TitleBlacklist)
return TitleBlacklist.Contains(title);
}
public static string[] GetBlacklistedTitles() {
lock (TitleBlacklist)
return TitleBlacklist.ToArray();
}
public static void SaveBlacklistedTitles() {
lock (TitleBlacklist)
Settings.Set(TITLE_BLACKLIST, TitleBlacklist.ToArray());
}
public static void Shutdown() {
HotKeys?.Dispose();
SysIcon?.Dispose();
@ -171,8 +231,8 @@ namespace TopMostFriend {
IEnumerable<WindowEntry> windows = GetWindowList();
Process lastProc = null;
bool procSeparator = Settings.Get(PROCESS_SEPARATOR_SETTING, false);
bool showEmptyTitles = Settings.Get(SHOW_EMPTY_WINDOW_SETTING, Debugger.IsAttached);
bool showExplorerMisc = Settings.Get(SHOW_EXPLORER_SETTING, Debugger.IsAttached);
bool showEmptyTitles = Settings.Get(SHOW_EMPTY_WINDOW_SETTING, false);
bool shiftClickBlacklist = Settings.Get(SHIFT_CLICK_BLACKLIST, true);
foreach(WindowEntry window in windows) {
if(procSeparator && lastProc != window.Process) {
@ -187,9 +247,8 @@ namespace TopMostFriend {
if (!showEmptyTitles && string.IsNullOrEmpty(title))
continue;
// skip explorer things with specific titles, there's probably a much better way of doing this check
// and this will also probably only work properly on english windows but Fuck It what do you want from me
if (!showExplorerMisc && window.Process.ProcessName == @"explorer" && (title == @"Program Manager" || title == @"Start"))
// Skip items in the blacklist
if (CheckBlacklistedTitles(title))
continue;
Image icon = GetWindowIcon(window.Window)?.ToBitmap() ?? null;
@ -197,7 +256,13 @@ namespace TopMostFriend {
SysIcon.ContextMenuStrip.Items.Insert(0, new ToolStripMenuItem(
title, icon,
new EventHandler((s, e) => SetTopMost(window.Window, !isTopMost))
new EventHandler((s, e) => {
if (shiftClickBlacklist && Control.ModifierKeys.HasFlag(Keys.Shift)) {
AddBlacklistedTitle(title);
SaveBlacklistedTitles();
} else
SetTopMost(window.Window, !isTopMost);
})
) {
CheckOnClick = true,
Checked = isTopMost,
@ -210,6 +275,37 @@ namespace TopMostFriend {
return (flags.ToInt32() & Win32.WS_EX_TOPMOST) > 0;
}
private class ActionTimeout {
private readonly Action Action;
private bool Continue = true;
private int Remaining = 0;
private const int STEP = 500;
public ActionTimeout(Action action, int timeout) {
Action = action ?? throw new ArgumentNullException(nameof(action));
if (timeout < 1)
throw new ArgumentException(@"Timeout must be a positive integer.", nameof(timeout));
Remaining = timeout;
new Thread(ThreadBody) { IsBackground = true }.Start();
}
private void ThreadBody() {
do {
Thread.Sleep(STEP);
Remaining -= STEP;
if (!Continue)
return;
} while (Remaining > 0);
Action.Invoke();
}
public void Cancel() {
Continue = false;
}
}
public static bool SetTopMost(IntPtr hWnd, bool state) {
Win32.SetWindowPos(
hWnd, new IntPtr(state ? Win32.HWND_TOPMOST : Win32.HWND_NOTOPMOST),
@ -239,6 +335,8 @@ namespace TopMostFriend {
return true;
}
private static ActionTimeout IconTimeout;
public static void ToggleForegroundWindow() {
IntPtr hWnd = Win32.GetForegroundWindow();
@ -246,13 +344,22 @@ namespace TopMostFriend {
if (Settings.Get(TOGGLE_BALLOON_SETTING, false)) {
string title = Win32.GetWindowTextString(hWnd);
SysIcon.ShowBalloonTip(
500,
IsTopMost(hWnd) ? @"Always on top" : @"No longer always on top",
SysIcon?.ShowBalloonTip(
2000, IsTopMost(hWnd) ? @"Always on top" : @"No longer always on top",
string.IsNullOrEmpty(title) ? @"Window has no title." : title,
ToolTipIcon.Info
);
}
if (SysIcon != null && Settings.Get(SHOW_HOTKEY_ICON, true)) {
Icon icon = GetWindowIcon(hWnd);
if (icon != null) {
IconTimeout?.Cancel();
SysIcon.Icon = icon;
IconTimeout = new ActionTimeout(() => SysIcon.Icon = OriginalIcon, 2000);
}
}
}
}

View file

@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyVersion("1.4.0.0")]
[assembly: AssemblyFileVersion("1.4.0.0")]

View file

@ -1,5 +1,8 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace TopMostFriend {
public static class Settings {
@ -22,6 +25,35 @@ namespace TopMostFriend {
}
}
public static string[] Get(string name, string[] fallback = null) {
byte[] buffer = GetRoot().GetValue(name, null) as byte[];
if (buffer == null)
return fallback;
List<string> strings = new List<string>();
using (MemoryStream src = new MemoryStream(buffer))
using (MemoryStream ms = new MemoryStream()) {
int b;
for(; ; ) {
b = src.ReadByte();
if (b == -1)
break;
else if (b != 0)
ms.WriteByte((byte)b);
else {
strings.Add(Encoding.UTF8.GetString(ms.ToArray()));
ms.SetLength(0);
}
}
}
return strings.ToArray();
}
public static bool Has(string name) {
try {
GetRoot().GetValueKind(name);
@ -46,11 +78,33 @@ namespace TopMostFriend {
GetRoot().SetValue(name, value);
}
public static void Set(string name, string[] values) {
if(values == null || values.Length < 1) {
Remove(name);
return;
}
using (MemoryStream ms = new MemoryStream()) {
foreach (string value in values) {
byte[] buffer = Encoding.UTF8.GetBytes(value);
ms.Write(buffer, 0, buffer.Length);
ms.WriteByte(0);
}
GetRoot().SetValue(name, ms.ToArray(), RegistryValueKind.Binary);
}
}
public static void SetDefault(string name, object value) {
if (!Has(name))
Set(name, value);
}
public static void SetDefault(string name, string[] value) {
if (!Has(name))
Set(name, value);
}
public static void Remove(string name) {
GetRoot().DeleteValue(name, false);
}

View file

@ -25,6 +25,8 @@ namespace TopMostFriend {
public readonly CheckBox FlAlwaysAdmin;
public readonly CheckBox FlToggleNotification;
public readonly CheckBox FlShiftClickBlacklist;
public readonly CheckBox FlShowHotkeyIcon;
public SettingsWindow() {
Text = @"Top Most Friend Settings";
@ -32,7 +34,7 @@ namespace TopMostFriend {
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.FixedSingle;
AutoScaleMode = AutoScaleMode.Dpi;
ClientSize = new Size(410, 183);
ClientSize = new Size(410, 278);
MinimizeBox = MaximizeBox = false;
MinimumSize = MaximumSize = Size;
@ -69,11 +71,17 @@ namespace TopMostFriend {
GroupBox flagsGroup = new GroupBox {
Text = @"Flags",
Location = new Point(6, 76),
Size = new Size(Width - 18, 70),
Size = new Size(Width - 18, 110),
};
GroupBox blackListGroup = new GroupBox {
Text = @"Blacklist",
Location = new Point(6, 186),
Size = new Size(Width - 18, 55),
};
Controls.AddRange(new Control[] {
applyButton, cancelButton, okButton, hotKeyGroup, flagsGroup,
applyButton, cancelButton, okButton, hotKeyGroup, flagsGroup, blackListGroup,
});
Label toggleForegroundLabel = new Label {
@ -88,12 +96,14 @@ namespace TopMostFriend {
Button fgReset = new Button {
Text = @"Reset",
Location = new Point(hotKeyGroup.Width - 85, mod_y),
TabIndex = 105,
};
fgReset.Click += FgReset_Click;
FgKey = new TextBox {
Text = ((Keys)(KeyCode >> 16)).ToString(),
Location = new Point(12, mod_y + 2),
TabIndex = 101,
};
FgKey.KeyDown += FgKey_KeyDown;
@ -104,6 +114,7 @@ namespace TopMostFriend {
Appearance = Appearance.Button,
Size = new Size(50, 23),
TextAlign = ContentAlignment.MiddleCenter,
TabIndex = 102,
};
FgModCtrl.Click += FgModCtrl_Click;
@ -114,6 +125,7 @@ namespace TopMostFriend {
Appearance = FgModCtrl.Appearance,
Size = FgModCtrl.Size,
TextAlign = FgModCtrl.TextAlign,
TabIndex = 103,
};
FgModAlt.Click += FgModAlt_Click;
@ -124,6 +136,7 @@ namespace TopMostFriend {
Appearance = FgModCtrl.Appearance,
Size = FgModCtrl.Size,
TextAlign = FgModCtrl.TextAlign,
TabIndex = 104,
};
FgModShift.Click += FgModShift_Click;
@ -136,15 +149,48 @@ namespace TopMostFriend {
Location = new Point(10, 20),
Checked = Settings.Get(Program.ALWAYS_ADMIN_SETTING, false),
AutoSize = true,
TabIndex = 201,
};
FlToggleNotification = new CheckBox {
Text = @"Show notification when using toggle hotkey",
Location = new Point(10, 40),
Checked = Settings.Get(Program.TOGGLE_BALLOON_SETTING, Program.ToggleBalloonDefault),
AutoSize = true,
TabIndex = 202,
};
FlShiftClickBlacklist = new CheckBox {
Text = @"SHIFT+CLICK items in the list to add to the title blacklist",
Location = new Point(10, 60),
Checked = Settings.Get(Program.SHIFT_CLICK_BLACKLIST, true),
AutoSize = true,
TabIndex = 203,
};
FlShowHotkeyIcon = new CheckBox {
Text = @"Show icon of window affected by hotkey",
Location = new Point(10, 80),
Checked = Settings.Get(Program.SHOW_HOTKEY_ICON, true),
AutoSize = true,
TabIndex = 204,
};
flagsGroup.Controls.AddRange(new[] { FlAlwaysAdmin, FlToggleNotification });
flagsGroup.Controls.AddRange(new[] { FlAlwaysAdmin, FlToggleNotification, FlShiftClickBlacklist, FlShowHotkeyIcon, });
Button titleBlacklist = new Button {
Size = new Size(120, 23),
Location = new Point(10, 20),
Text = @"Manage...",
TabIndex = 301,
};
titleBlacklist.Click += (s, e) => {
string[] newList = BlacklistWindow.Display(@"Title Blacklist", Program.GetBlacklistedTitles());
if(newList != null) {
Program.ApplyBlacklistedTitles(newList);
Program.SaveBlacklistedTitles();
}
};
blackListGroup.Controls.AddRange(new[] { titleBlacklist });
}
private void FgReset_Click(object sender, EventArgs e) {
@ -156,6 +202,9 @@ namespace TopMostFriend {
public void Apply() {
Settings.Set(Program.FOREGROUND_HOTKEY_SETTING, KeyCode);
Settings.Set(Program.ALWAYS_ADMIN_SETTING, FlAlwaysAdmin.Checked);
Settings.Set(Program.TOGGLE_BALLOON_SETTING, FlToggleNotification.Checked);
Settings.Set(Program.SHIFT_CLICK_BLACKLIST, FlShiftClickBlacklist.Checked);
Settings.Set(Program.SHOW_HOTKEY_ICON, FlShowHotkeyIcon.Checked);
Program.SetForegroundHotKey(KeyCode);
}

View file

@ -44,6 +44,9 @@
<Compile Include="AboutWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="BlacklistWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="HotKeyWindow.cs">
<SubType>Form</SubType>
</Compile>