mirror of
https://github.com/flashwave/topmostfriend.git
synced 2024-11-24 10:46:05 +00:00
v1.6.0 - 2021-07-09
This commit is contained in:
parent
103d3d4bff
commit
19f16b4f64
23 changed files with 1342 additions and 290 deletions
|
@ -3,3 +3,5 @@
|
|||
utility that lets you quickly force any program to be always on top
|
||||
|
||||
should work on any version of windows that supports .net framework 4.0
|
||||
|
||||
[Click here to download!](https://github.com/flashwave/topmostfriend/releases/latest)
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace TopMostFriend {
|
|||
}
|
||||
|
||||
public AboutWindow() {
|
||||
Text = @"About Top Most Friend";
|
||||
Text = Locale.String(@"AboutTitle");
|
||||
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
BackgroundImage = Properties.Resources.about;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
|
@ -24,11 +24,12 @@ namespace TopMostFriend {
|
|||
ClientSize = Properties.Resources.about.Size;
|
||||
MaximizeBox = MinimizeBox = false;
|
||||
MaximumSize = MinimumSize = Size;
|
||||
TopMost = true;
|
||||
|
||||
int tabIndex = 0;
|
||||
|
||||
Button closeButton = new Button {
|
||||
Text = @"Close",
|
||||
Text = Locale.String(@"AboutClose"),
|
||||
Size = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||
TabIndex = ++tabIndex,
|
||||
};
|
||||
|
@ -37,7 +38,7 @@ namespace TopMostFriend {
|
|||
Controls.Add(closeButton);
|
||||
|
||||
Button websiteButton = new Button {
|
||||
Text = @"Website",
|
||||
Text = Locale.String(@"AboutWebsite"),
|
||||
Size = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||
TabIndex = ++tabIndex,
|
||||
};
|
||||
|
@ -46,7 +47,7 @@ namespace TopMostFriend {
|
|||
Controls.Add(websiteButton);
|
||||
|
||||
Button donateButton = new Button {
|
||||
Text = @"Donate",
|
||||
Text = Locale.String(@"AboutDonate"),
|
||||
Size = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||
TabIndex = ++tabIndex,
|
||||
};
|
||||
|
|
35
TopMostFriend/ActionTimeout.cs
Normal file
35
TopMostFriend/ActionTimeout.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,26 +35,27 @@ namespace TopMostFriend {
|
|||
MinimizeBox = MaximizeBox = false;
|
||||
MinimumSize = Size;
|
||||
DialogResult = DialogResult.Cancel;
|
||||
TopMost = true;
|
||||
|
||||
BlacklistView = new ListBox {
|
||||
TabIndex = 101,
|
||||
IntegralHeight = false,
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right,
|
||||
Location = new Point(BUTTON_WIDTH + (SPACING * 2), SPACING),
|
||||
Location = new Point(SPACING, 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",
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Right,
|
||||
Text = Locale.String(@"BlacklistAdd"),
|
||||
ClientSize = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||
Location = new Point(SPACING, SPACING),
|
||||
Location = new Point(BlacklistView.Width + (SPACING * 2), SPACING),
|
||||
TabIndex = 201,
|
||||
};
|
||||
EditButton = new Button {
|
||||
Text = @"Edit",
|
||||
Text = Locale.String(@"BlacklistEdit"),
|
||||
Location = new Point(AddButton.Location.X, AddButton.Location.Y + AddButton.Height + SPACING),
|
||||
Enabled = false,
|
||||
Anchor = AddButton.Anchor,
|
||||
|
@ -62,7 +63,7 @@ namespace TopMostFriend {
|
|||
TabIndex = 202,
|
||||
};
|
||||
RemoveButton = new Button {
|
||||
Text = @"Remove",
|
||||
Text = Locale.String(@"BlacklistRemove"),
|
||||
Location = new Point(AddButton.Location.X, EditButton.Location.Y + AddButton.Height + SPACING),
|
||||
Enabled = false,
|
||||
Anchor = AddButton.Anchor,
|
||||
|
@ -75,16 +76,16 @@ namespace TopMostFriend {
|
|||
RemoveButton.Click += RemoveButton_Click;
|
||||
|
||||
CancelButton = new Button {
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||
Text = @"Cancel",
|
||||
Location = new Point(SPACING, ClientSize.Height - AddButton.ClientSize.Height - SPACING),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
Text = Locale.String(@"BlacklistCancel"),
|
||||
Location = new Point(AddButton.Location.X, 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)),
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
Text = Locale.String(@"BlacklistDone"),
|
||||
Location = new Point(AddButton.Location.X, ClientSize.Height - ((AddButton.ClientSize.Height + SPACING) * 2)),
|
||||
ClientSize = AddButton.ClientSize,
|
||||
TabIndex = 10002,
|
||||
};
|
||||
|
@ -106,16 +107,18 @@ namespace TopMostFriend {
|
|||
public string Original { get; }
|
||||
public string String { get => TextBox.Text; }
|
||||
|
||||
private TextBox TextBox;
|
||||
private readonly TextBox TextBox;
|
||||
|
||||
public BlacklistEditorWindow(string original = null) {
|
||||
Original = original ?? string.Empty;
|
||||
Text = original == null ? @"Adding new entry..." : $@"Editing {original}...";
|
||||
Text = Locale.String(original == null ? @"BlacklistEditorAdding" : @"BlacklistEditorEditing", original);
|
||||
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
StartPosition = FormStartPosition.CenterParent;
|
||||
FormBorderStyle = FormBorderStyle.FixedToolWindow;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
ClientSize = new Size(500, 39);
|
||||
MaximizeBox = MinimizeBox = false;
|
||||
MaximumSize = MinimumSize = Size;
|
||||
TopMost = true;
|
||||
|
||||
Button cancelButton = new Button {
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
|
@ -123,7 +126,7 @@ namespace TopMostFriend {
|
|||
Name = @"cancelButton",
|
||||
Size = new Size(75, 23),
|
||||
TabIndex = 102,
|
||||
Text = @"Cancel",
|
||||
Text = Locale.String(@"BlacklistEditorCancel"),
|
||||
};
|
||||
cancelButton.Click += (s, e) => { DialogResult = DialogResult.Cancel; Close(); };
|
||||
|
||||
|
@ -133,14 +136,14 @@ namespace TopMostFriend {
|
|||
Name = @"saveButton",
|
||||
Size = new Size(75, 23),
|
||||
TabIndex = 101,
|
||||
Text = @"Save",
|
||||
Text = Locale.String(@"BlacklistEditorSave"),
|
||||
};
|
||||
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",
|
||||
Name = @"deviceSelection", // ??????
|
||||
Size = new Size(ClientSize.Width - 8 - cancelButton.Width - saveButton.Width - 19, 23),
|
||||
TabIndex = 100,
|
||||
Text = Original,
|
||||
|
|
357
TopMostFriend/FirstRunWindow.cs
Normal file
357
TopMostFriend/FirstRunWindow.cs
Normal file
|
@ -0,0 +1,357 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public class FirstRunWindow : Form {
|
||||
public static void Display() {
|
||||
using(FirstRunWindow firstRun = new FirstRunWindow())
|
||||
firstRun.ShowDialog();
|
||||
}
|
||||
|
||||
private bool CanClose = false;
|
||||
private bool IsClosing = false;
|
||||
private bool IsSizing = false;
|
||||
|
||||
private Button NextBtn { get; }
|
||||
private Button PrevBtn { get; }
|
||||
|
||||
private Action NextAct = null;
|
||||
private Action PrevAct = null;
|
||||
|
||||
private bool NextVisible { get => NextBtn.Visible; set => NextBtn.Visible = value; }
|
||||
private bool PrevVisible { get => PrevBtn.Visible; set => PrevBtn.Visible = value; }
|
||||
|
||||
private Panel WorkArea { get; }
|
||||
|
||||
public FirstRunWindow() {
|
||||
Text = Program.TITLE + @" v" + Application.ProductVersion.Substring(0, Application.ProductVersion.Length - 2);
|
||||
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
AutoScaleMode = AutoScaleMode.Dpi;
|
||||
MaximizeBox = MinimizeBox = false;
|
||||
TopMost = true;
|
||||
ClientSize = new Size(410, 80);
|
||||
|
||||
Controls.Add(new PictureBox {
|
||||
Image = Properties.Resources.firstrun,
|
||||
Size = Properties.Resources.firstrun.Size,
|
||||
Location = new Point(0, 0),
|
||||
});
|
||||
|
||||
NextBtn = new Button {
|
||||
Text = Locale.String(@"FirstRunNext"),
|
||||
Anchor = AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
Visible = false,
|
||||
Location = new Point(ClientSize.Width - 81, ClientSize.Height - 29),
|
||||
};
|
||||
NextBtn.Click += NextBtn_Click;
|
||||
Controls.Add(NextBtn);
|
||||
|
||||
PrevBtn = new Button {
|
||||
Text = Locale.String(@"FirstRunPrev"),
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Bottom,
|
||||
Visible = false,
|
||||
Location = new Point(6, ClientSize.Height - 29),
|
||||
};
|
||||
PrevBtn.Click += PrevBtn_Click;
|
||||
Controls.Add(PrevBtn);
|
||||
|
||||
WorkArea = new Panel {
|
||||
Dock = DockStyle.Fill,
|
||||
};
|
||||
Controls.Add(WorkArea);
|
||||
}
|
||||
|
||||
private void PrevBtn_Click(object sender, EventArgs e) {
|
||||
if(!PrevVisible)
|
||||
return;
|
||||
WorkArea.Controls.Clear();
|
||||
if(PrevAct == null)
|
||||
Close();
|
||||
else
|
||||
Invoke(PrevAct);
|
||||
}
|
||||
|
||||
private void NextBtn_Click(object sender, EventArgs e) {
|
||||
if(!NextVisible)
|
||||
return;
|
||||
WorkArea.Controls.Clear();
|
||||
if(NextAct == null)
|
||||
Close();
|
||||
else
|
||||
Invoke(NextAct);
|
||||
}
|
||||
|
||||
protected override void OnShown(EventArgs e) {
|
||||
base.OnShown(e);
|
||||
Update();
|
||||
Thread.Sleep(500);
|
||||
ShowPageIntro();
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e) {
|
||||
if(e.CloseReason == CloseReason.UserClosing && !CanClose) {
|
||||
e.Cancel = true;
|
||||
|
||||
if(!IsClosing) {
|
||||
IsClosing = true;
|
||||
|
||||
SetHeight(80, new Action(() => {
|
||||
CanClose = true;
|
||||
Thread.Sleep(100);
|
||||
Close();
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
|
||||
public void SetHeight(int height, Action onFinish = null) {
|
||||
if(height < 80)
|
||||
throw new ArgumentException(@"target height must be more than or equal to 80.", nameof(height));
|
||||
|
||||
if(IsSizing)
|
||||
return;
|
||||
IsSizing = true;
|
||||
|
||||
const int timeout = 1000 / 60;
|
||||
double time = 0;
|
||||
double period = timeout / 400d;
|
||||
|
||||
int currentHeight = ClientSize.Height;
|
||||
int currentY = Location.Y;
|
||||
int diffHeight = height - currentHeight;
|
||||
int diffY = diffHeight / 2;
|
||||
|
||||
Action setHeight = new Action(() => {
|
||||
int newHeight = currentHeight + (int)Math.Ceiling(time * diffHeight);
|
||||
int newY = currentY - (int)Math.Ceiling(time * diffY);
|
||||
ClientSize = new Size(ClientSize.Width, newHeight);
|
||||
Location = new Point(Location.X, newY);
|
||||
});
|
||||
|
||||
new Thread(() => {
|
||||
Stopwatch sw = new Stopwatch();
|
||||
|
||||
try {
|
||||
do {
|
||||
sw.Restart();
|
||||
|
||||
Invoke(setHeight);
|
||||
time += period;
|
||||
|
||||
int delay = timeout - (int)sw.ElapsedMilliseconds;
|
||||
if(delay > 1)
|
||||
Thread.Sleep(delay);
|
||||
} while(time < 1d + period);
|
||||
} finally {
|
||||
sw.Stop();
|
||||
if(onFinish != null)
|
||||
Invoke(onFinish);
|
||||
IsSizing = false;
|
||||
}
|
||||
}) {
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.AboveNormal,
|
||||
}.Start();
|
||||
}
|
||||
|
||||
public void ShowPageIntro() {
|
||||
Text = Locale.String(@"FirstRunWelcomeTitle");
|
||||
PrevVisible = false;
|
||||
NextVisible = true;
|
||||
NextAct = ShowPageHotKey;
|
||||
|
||||
SetHeight(190, new Action(() => {
|
||||
WorkArea.Controls.Add(new Label {
|
||||
Text = Locale.String(@"FirstRunWelcomeIntro"),
|
||||
Location = new Point(10, 90),
|
||||
Size = new Size(ClientSize.Width - 20, 200),
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
public void ShowPageHotKey() {
|
||||
Text = Locale.String(@"FirstRunHotKeyTitle");
|
||||
PrevVisible = NextVisible = true;
|
||||
PrevAct = ShowPageIntro;
|
||||
NextAct = () => {
|
||||
Program.SetForegroundHotKey(Settings.Get(Program.FOREGROUND_HOTKEY_SETTING, 0));
|
||||
ShowPageElevation();
|
||||
};
|
||||
|
||||
SetHeight(230, new Action(() => {
|
||||
WorkArea.Controls.Add(new Label {
|
||||
Text = Locale.String(@"FirstRunHotKeyExplain"),
|
||||
Location = new Point(10, 90),
|
||||
Size = new Size(ClientSize.Width - 20, 40),
|
||||
});
|
||||
|
||||
SettingsWindow.CreateHotKeyInput(
|
||||
WorkArea,
|
||||
() => Settings.Get(Program.FOREGROUND_HOTKEY_SETTING, 0),
|
||||
keyCode => Settings.Set(Program.FOREGROUND_HOTKEY_SETTING, keyCode),
|
||||
0,
|
||||
110
|
||||
);
|
||||
|
||||
CheckBox flShowNotification = new CheckBox {
|
||||
Text = Locale.String(@"FirstRunHotKeyNotify"),
|
||||
Location = new Point(12, 170),
|
||||
Checked = Settings.Get(Program.TOGGLE_BALLOON_SETTING, Program.ToggleBalloonDefault),
|
||||
AutoSize = true,
|
||||
TabIndex = 201,
|
||||
};
|
||||
flShowNotification.CheckedChanged += (s, e) => {
|
||||
Settings.Set(Program.TOGGLE_BALLOON_SETTING, flShowNotification.Checked);
|
||||
};
|
||||
WorkArea.Controls.Add(flShowNotification);
|
||||
}));
|
||||
}
|
||||
|
||||
public void ShowPageElevation() {
|
||||
Text = Locale.String(@"FirstRunAdminTitle");
|
||||
PrevVisible = NextVisible = true;
|
||||
PrevAct = ShowPageHotKey;
|
||||
NextAct = ShowPageThanks;
|
||||
|
||||
SetHeight(280, () => {
|
||||
WorkArea.Controls.Add(new Label {
|
||||
Text = Locale.String(@"FirstRunAdminExplain"),
|
||||
Location = new Point(10, 90),
|
||||
Size = new Size(ClientSize.Width - 20, 40),
|
||||
});
|
||||
|
||||
bool alwaysAdmin = Settings.Get(Program.ALWAYS_ADMIN_SETTING, false);
|
||||
bool implicitAdmin = Settings.Get(Program.ALWAYS_RETRY_ELEVATED, false);
|
||||
|
||||
RadioButton rdAsk = new RadioButton {
|
||||
Text = Locale.String(@"FirstRunAdminOptionAsk"),
|
||||
Location = new Point(10, 140),
|
||||
Size = new Size(ClientSize.Width - 20, 30),
|
||||
Appearance = Appearance.Button,
|
||||
Checked = !alwaysAdmin && !implicitAdmin,
|
||||
};
|
||||
rdAsk.CheckedChanged += (s, e) => {
|
||||
if(rdAsk.Checked) {
|
||||
Settings.Set(Program.ALWAYS_ADMIN_SETTING, false);
|
||||
Settings.Set(Program.ALWAYS_RETRY_ELEVATED, false);
|
||||
}
|
||||
};
|
||||
WorkArea.Controls.Add(rdAsk);
|
||||
|
||||
RadioButton rdImplicit = new RadioButton {
|
||||
Text = Locale.String(@"FirstRunAdminOptionImplicit"),
|
||||
Location = new Point(rdAsk.Location.X, rdAsk.Location.Y + 36),
|
||||
Size = rdAsk.Size,
|
||||
Appearance = rdAsk.Appearance,
|
||||
Checked = implicitAdmin && !alwaysAdmin,
|
||||
};
|
||||
rdImplicit.CheckedChanged += (s, e) => {
|
||||
if(rdImplicit.Checked) {
|
||||
Settings.Set(Program.ALWAYS_ADMIN_SETTING, false);
|
||||
Settings.Set(Program.ALWAYS_RETRY_ELEVATED, true);
|
||||
}
|
||||
};
|
||||
WorkArea.Controls.Add(rdImplicit);
|
||||
|
||||
RadioButton rdAlways = new RadioButton {
|
||||
Text = Locale.String(@"FirstRunAdminOptionAlways"),
|
||||
Location = new Point(rdAsk.Location.X, rdImplicit.Location.Y + 36),
|
||||
Size = rdAsk.Size,
|
||||
Appearance = rdAsk.Appearance,
|
||||
Checked = alwaysAdmin,
|
||||
};
|
||||
rdAlways.CheckedChanged += (s, e) => {
|
||||
if(rdAlways.Checked) {
|
||||
Settings.Set(Program.ALWAYS_ADMIN_SETTING, true);
|
||||
Settings.Set(Program.ALWAYS_RETRY_ELEVATED, false);
|
||||
}
|
||||
};
|
||||
WorkArea.Controls.Add(rdAlways);
|
||||
});
|
||||
}
|
||||
|
||||
public void ShowPageThanks() {
|
||||
Text = Locale.String(@"FirstRunThanksTitle");
|
||||
PrevVisible = NextVisible = true;
|
||||
PrevAct = ShowPageElevation;
|
||||
NextAct = CheckRestartNeeded;
|
||||
|
||||
SetHeight(270, () => {
|
||||
Label thankYou = new Label {
|
||||
Text = Locale.String(@"FirstRunThanksThank"),
|
||||
Location = new Point(10, 90),
|
||||
Size = new Size(ClientSize.Width - 20, 20),
|
||||
};
|
||||
WorkArea.Controls.Add(thankYou);
|
||||
|
||||
string updateLinkString = Locale.String(@"FirstRunThanksUpdate");
|
||||
|
||||
int websiteStart = updateLinkString.IndexOf(@"[WEB]");
|
||||
updateLinkString = updateLinkString.Substring(0, websiteStart) + updateLinkString.Substring(websiteStart + 5);
|
||||
int websiteEnd = updateLinkString.IndexOf(@"[/WEB]");
|
||||
updateLinkString = updateLinkString.Substring(0, websiteEnd) + updateLinkString.Substring(websiteEnd + 6);
|
||||
|
||||
int changelogStart = updateLinkString.IndexOf(@"[CHANGELOG]");
|
||||
updateLinkString = updateLinkString.Substring(0, changelogStart) + updateLinkString.Substring(changelogStart + 11);
|
||||
int changelogEnd = updateLinkString.IndexOf(@"[/CHANGELOG]");
|
||||
updateLinkString = updateLinkString.Substring(0, changelogEnd) + updateLinkString.Substring(changelogEnd + 12);
|
||||
|
||||
LinkLabel updateLink;
|
||||
WorkArea.Controls.Add(updateLink = new LinkLabel {
|
||||
Text = updateLinkString,
|
||||
Location = new Point(10, 120),
|
||||
Size = new Size(ClientSize.Width - 20, 34),
|
||||
Font = thankYou.Font,
|
||||
Links = {
|
||||
new LinkLabel.Link(websiteStart, websiteEnd - websiteStart, @"https://flash.moe/topmostfriend"),
|
||||
new LinkLabel.Link(changelogStart, changelogEnd - changelogStart, @"https://flash.moe/topmostfriend/changelog.php"),
|
||||
},
|
||||
});
|
||||
updateLink.LinkClicked += (s, e) => {
|
||||
Process.Start((string)e.Link.LinkData);
|
||||
};
|
||||
|
||||
string settingsLinkString = Locale.String(@"FirstRunThanksSettings");
|
||||
|
||||
int settingsStart = settingsLinkString.IndexOf(@"[SETTINGS]");
|
||||
settingsLinkString = settingsLinkString.Substring(0, settingsStart) + settingsLinkString.Substring(settingsStart + 10);
|
||||
int settingsEnd = settingsLinkString.IndexOf(@"[/SETTINGS]");
|
||||
settingsLinkString = settingsLinkString.Substring(0, settingsEnd) + settingsLinkString.Substring(settingsEnd + 11);
|
||||
|
||||
LinkLabel settingsLink;
|
||||
WorkArea.Controls.Add(settingsLink = new LinkLabel {
|
||||
Text = settingsLinkString,
|
||||
Location = new Point(10, 160),
|
||||
Size = new Size(ClientSize.Width - 20, 30),
|
||||
Font = thankYou.Font,
|
||||
Links = {
|
||||
new LinkLabel.Link(settingsStart, settingsEnd - settingsStart),
|
||||
},
|
||||
});
|
||||
settingsLink.LinkClicked += (s, e) => {
|
||||
SettingsWindow.Display();
|
||||
};
|
||||
|
||||
WorkArea.Controls.Add(new Label {
|
||||
Text = Locale.String(@"FirstRunThanksAdmin"),
|
||||
Location = new Point(10, 200),
|
||||
Size = new Size(ClientSize.Width - 20, 80),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void CheckRestartNeeded() {
|
||||
if(Settings.Get(Program.ALWAYS_ADMIN_SETTING, false))
|
||||
UAC.RestartElevated();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
22
TopMostFriend/Languages/Language.cs
Normal file
22
TopMostFriend/Languages/Language.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TopMostFriend.Languages {
|
||||
[XmlRoot(@"Language")]
|
||||
public class Language {
|
||||
[XmlElement(@"Info")]
|
||||
public LanguageInfo Info { get; set; }
|
||||
|
||||
[XmlArray(@"Strings")]
|
||||
[XmlArrayItem(@"String", Type = typeof(LanguageString))]
|
||||
public LanguageString[] Strings { get; set; }
|
||||
|
||||
public LanguageString GetString(string name) {
|
||||
if(name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
return Strings.FirstOrDefault(s => name.Equals(s.Name));
|
||||
}
|
||||
}
|
||||
}
|
21
TopMostFriend/Languages/LanguageInfo.cs
Normal file
21
TopMostFriend/Languages/LanguageInfo.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace TopMostFriend.Languages {
|
||||
public class LanguageInfo {
|
||||
[XmlElement(@"Id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlElement(@"NameNative")]
|
||||
public string NameNative { get; set; }
|
||||
|
||||
[XmlElement(@"NameEnglish")]
|
||||
public string NameEnglish { get; set; }
|
||||
|
||||
[XmlElement(@"TargetVersion")]
|
||||
public string TargetVersion { get; set; }
|
||||
|
||||
public override string ToString() {
|
||||
return $@"{NameNative} / {NameEnglish} ({Id})";
|
||||
}
|
||||
}
|
||||
}
|
19
TopMostFriend/Languages/LanguageString.cs
Normal file
19
TopMostFriend/Languages/LanguageString.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace TopMostFriend.Languages {
|
||||
public class LanguageString {
|
||||
[XmlAttribute(@"name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public string Value { get; set; }
|
||||
|
||||
public string Format(params object[] args) {
|
||||
return string.Format(Value, args);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $@"{Name}: {Value}";
|
||||
}
|
||||
}
|
||||
}
|
109
TopMostFriend/Languages/en-GB.xml
Normal file
109
TopMostFriend/Languages/en-GB.xml
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Language>
|
||||
<Info>
|
||||
<Id>en-GB</Id>
|
||||
<NameNative>British English</NameNative>
|
||||
<NameEnglish>British English</NameEnglish>
|
||||
<TargetVersion>1.6.0</TargetVersion>
|
||||
</Info>
|
||||
<Strings>
|
||||
<String name="AlreadyRunning">An instance of {0} is already running.</String>
|
||||
|
||||
<String name="TrayRefresh">&Refresh</String>
|
||||
<String name="TraySettings">&Settings</String>
|
||||
<String name="TrayAbout">&About</String>
|
||||
<String name="TrayQuit">&Quit</String>
|
||||
|
||||
<String name="NotifyOnTop">Now always on top</String>
|
||||
<String name="NotifyNoLonger">No longer always on top</String>
|
||||
<String name="NotifyNoTitle">Window has no title.</String>
|
||||
|
||||
<String name="ErrUnableAlterStatus1">Wasn't able to change always on top status on this window.</String>
|
||||
<String name="ErrUnableAlterStatus2">Do you want to try again as an Administrator? A User Account Control dialog will pop up after selecting Yes.</String>
|
||||
<String name="ErrUnableAlterStatusProtected">{0} was still unable to change always on top status for the window. It might be protected by Windows.</String>
|
||||
|
||||
<String name="AboutTitle">About {0}</String>
|
||||
<String name="AboutClose">Close</String>
|
||||
<String name="AboutWebsite">Website</String>
|
||||
<String name="AboutDonate">Donate</String>
|
||||
|
||||
<String name="SettingsTitle">{0} Settings</String>
|
||||
<String name="SettingsApply">Apply</String>
|
||||
<String name="SettingsCancel">Cancel</String>
|
||||
<String name="SettingsOk">OK</String>
|
||||
|
||||
<String name="SettingsHotKeysTitle">Hot Keys</String>
|
||||
<String name="SettingsHotKeysReset">Reset</String>
|
||||
<String name="SettingsHotKeysToggle">Toggle always on top status on active window</String>
|
||||
|
||||
<String name="SettingsOptionsTitle">Options</String>
|
||||
<String name="SettingsOptionsToggleNotify">Show notification when using toggle hot key</String>
|
||||
<String name="SettingsOptionsToggleNotifyIcon">Show icon of window affected by hot key</String>
|
||||
<String name="SettingsOptionsElevatedRetry">Always retry changing always on top status as Administrator on first failure</String>
|
||||
<String name="SettingsOptionsShiftBlacklist">SHIFT+CLICK items in the tray area list to add to the title blacklist</String>
|
||||
<String name="SettingsOptionsRevertOnExit">Revert status to before {0} altered them on exit</String>
|
||||
<String name="SettingsOptionsShowTrayList">Show list of open windows in the tray area menu</String>
|
||||
<String name="SettingsOptionsAlwaysAdmin">Always run as Administrator</String>
|
||||
|
||||
<String name="SettingsLanguageTitle">Language</String>
|
||||
<String name="SettingsLanguageChangedInfo">You should restart {0} for the language change to fully take effect. A lot of text will still appear in the previously selected language.</String>
|
||||
|
||||
<String name="SettingsOtherTitle">Other</String>
|
||||
<String name="SettingsOtherBlacklistButton">Manage Blacklist...</String>
|
||||
<String name="SettingsOtherBlacklistWindowTitle">Title Blacklist</String>
|
||||
<String name="SettingsOtherStartupButton">Start with Windows...</String>
|
||||
<String name="SettingsOtherStartupConfirm">
|
||||
Should {0} start with Windows?
|
||||
Clicking Yes will create a shortcut to the {0} in your Start-up folder, clicking No will delete the shortcut.
|
||||
Make sure you've placed your {0} executable in a permanent location before clicking Yes.
|
||||
</String>
|
||||
<String name="SettingsOtherResetButton">Reset All Settings...</String>
|
||||
<String name="SettingsOtherResetConfirm">
|
||||
This will reset all {0} settings and restart the application.
|
||||
Are you absolutely sure?
|
||||
</String>
|
||||
|
||||
<String name="BlacklistAdd">Add</String>
|
||||
<String name="BlacklistEdit">Edit</String>
|
||||
<String name="BlacklistRemove">Remove</String>
|
||||
<String name="BlacklistCancel">Cancel</String>
|
||||
<String name="BlacklistDone">Done</String>
|
||||
|
||||
<String name="BlacklistEditorAdding">Adding new entry...</String>
|
||||
<String name="BlacklistEditorEditing">Editing {0}...</String>
|
||||
<String name="BlacklistEditorCancel">Cancel</String>
|
||||
<String name="BlacklistEditorSave">Save</String>
|
||||
|
||||
<String name="FirstRunNext">Next</String>
|
||||
<String name="FirstRunPrev">Previous</String>
|
||||
|
||||
<String name="FirstRunWelcomeTitle">Welcome to {0}!</String>
|
||||
<String name="FirstRunWelcomeIntro">
|
||||
{0} is a utility that lets you manage the always-on-top state of windows from programs you have open.
|
||||
|
||||
You will now be taken through a few steps to configure {0} to your liking!
|
||||
</String>
|
||||
|
||||
<String name="FirstRunHotKeyTitle">Selecting a hot key</String>
|
||||
<String name="FirstRunHotKeyExplain">
|
||||
Select a global hot key for toggling the always on top status of whatever window is currently in the foreground.
|
||||
If you don't wish to specify one just hit Reset followed by Next.
|
||||
</String>
|
||||
<String name="FirstRunHotKeyNotify">Show a notification when the hot key has been used</String>
|
||||
|
||||
<String name="FirstRunAdminTitle">Administrator actions</String>
|
||||
<String name="FirstRunAdminExplain">
|
||||
Sometimes {0} is not able to change the always on top state for certain windows without having administrative privileges.
|
||||
{0} provides multiple options for this situation:
|
||||
</String>
|
||||
<String name="FirstRunAdminOptionAsk">Ask me what to do when it's required.</String>
|
||||
<String name="FirstRunAdminOptionImplicit">Ask for administrative privileges automatically when required.</String>
|
||||
<String name="FirstRunAdminOptionAlways">Always run {0} as Administrator (not recommended).</String>
|
||||
|
||||
<String name="FirstRunThanksTitle">Thank you!</String>
|
||||
<String name="FirstRunThanksThank">Thank you for using {0}!</String>
|
||||
<String name="FirstRunThanksUpdate">Be sure to check the {0} [WEB]website[/WEB] and [CHANGELOG]changelog[/CHANGELOG] for updates from time to time.</String>
|
||||
<String name="FirstRunThanksSettings">There are more options you may be interested in taking a look at in the [SETTINGS]Settings[/SETTINGS] window.</String>
|
||||
<String name="FirstRunThanksAdmin">If you have chosen to always run {0} as Administrator, you will receive a User Account Control prompt after hitting next.</String>
|
||||
</Strings>
|
||||
</Language>
|
109
TopMostFriend/Languages/nl-NL.xml
Normal file
109
TopMostFriend/Languages/nl-NL.xml
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Language>
|
||||
<Info>
|
||||
<Id>nl-NL</Id>
|
||||
<NameNative>Nederlands</NameNative>
|
||||
<NameEnglish>Dutch</NameEnglish>
|
||||
<TargetVersion>1.6.0</TargetVersion>
|
||||
</Info>
|
||||
<Strings>
|
||||
<String name="AlreadyRunning">{0} is al gestart.</String>
|
||||
|
||||
<String name="TrayRefresh">&Verversen</String>
|
||||
<String name="TraySettings">&Instellingen</String>
|
||||
<String name="TrayAbout">&Over</String>
|
||||
<String name="TrayQuit">&Sluiten</String>
|
||||
|
||||
<String name="NotifyOnTop">Nu altijd op voorgrond</String>
|
||||
<String name="NotifyNoLonger">Niet langer altijd op voorgrond</String>
|
||||
<String name="NotifyNoTitle">Venster heeft geen titel.</String>
|
||||
|
||||
<String name="ErrUnableAlterStatus1">Het was niet mogelijk om de altijd op voorgrond status van dit venster te veranderen.</String>
|
||||
<String name="ErrUnableAlterStatus2">Wil je het opnieuw proberen als Administrator? Een Gebruikersaccountbeheer dialoog zal verschijnen als je op Ja klikt.</String>
|
||||
<String name="ErrUnableAlterStatusProtected">{0} kon nog steeds niet de altijd op voorgrond status van dit venster veranderen. Het wordt mogelijk beschermd door Windows.</String>
|
||||
|
||||
<String name="AboutTitle">Over {0}</String>
|
||||
<String name="AboutClose">Sluiten</String>
|
||||
<String name="AboutWebsite">Website</String>
|
||||
<String name="AboutDonate">Doneer</String>
|
||||
|
||||
<String name="SettingsTitle">{0} Instellingen</String>
|
||||
<String name="SettingsApply">Toepassen</String>
|
||||
<String name="SettingsCancel">Annuleren</String>
|
||||
<String name="SettingsOk">OK</String>
|
||||
|
||||
<String name="SettingsHotKeysTitle">Sneltoetsen</String>
|
||||
<String name="SettingsHotKeysReset">Herstel</String>
|
||||
<String name="SettingsHotKeysToggle">Schakel altijd op voorgrond status op actief venster</String>
|
||||
|
||||
<String name="SettingsOptionsTitle">Opties</String>
|
||||
<String name="SettingsOptionsToggleNotify">Toon notificatie als de sneltoets gebruikt is</String>
|
||||
<String name="SettingsOptionsToggleNotifyIcon">Toon icoon van venster dat beïnvloed was door de sneltoets</String>
|
||||
<String name="SettingsOptionsElevatedRetry">Probeer altijd opnieuw als Administrator bij de eerste mislukking van het veranderen van de status</String>
|
||||
<String name="SettingsOptionsShiftBlacklist">SHIFT+CLICK items in de systeemvak lijst om ze toe te voegen aan de titel blacklist</String>
|
||||
<String name="SettingsOptionsRevertOnExit">Herstel status naar voordat {0} ze heeft aangepast tijdens het sluiten</String>
|
||||
<String name="SettingsOptionsShowTrayList">Geef lijst met open venster weer in het systeemvak menu</String>
|
||||
<String name="SettingsOptionsAlwaysAdmin">Start altijd als Administrator</String>
|
||||
|
||||
<String name="SettingsLanguageTitle">Taal (Language)</String>
|
||||
<String name="SettingsLanguageChangedInfo">Je moet {0} opnieuw starten om de verandering van de taal volledig effect te laten nemen. Veel tekst zal nog steeds verschijnen in de voorheen geselecteerde taal.</String>
|
||||
|
||||
<String name="SettingsOtherTitle">Overig</String>
|
||||
<String name="SettingsOtherBlacklistButton">Beheer Blacklist...</String>
|
||||
<String name="SettingsOtherBlacklistWindowTitle">Titel Blacklist</String>
|
||||
<String name="SettingsOtherStartupButton">Start met Windows...</String>
|
||||
<String name="SettingsOtherStartupConfirm">
|
||||
Moet {0} tegelijkertijd met Windows starten?
|
||||
Als je op Ja klikt zal een een snelkoppeling aangemaakt worden in de Opstarten map van het start menu, als je op Nee klikt wordt de snelkoppeling verwijderd.
|
||||
Zorg ervoor dat je het EXE bestand voor {0} in een permanente locatie hebt voordat je op Ja klikt.
|
||||
</String>
|
||||
<String name="SettingsOtherResetButton">Herstel alle instellingen...</String>
|
||||
<String name="SettingsOtherResetConfirm">
|
||||
Dit zal alle instellingen verwijderen en het programma opnieuw opstarten.
|
||||
Weet je het absoluut zeker?
|
||||
</String>
|
||||
|
||||
<String name="BlacklistAdd">Toevoegen</String>
|
||||
<String name="BlacklistEdit">Bewerken</String>
|
||||
<String name="BlacklistRemove">Verwijderen</String>
|
||||
<String name="BlacklistCancel">Annuleren</String>
|
||||
<String name="BlacklistDone">Klaar</String>
|
||||
|
||||
<String name="BlacklistEditorAdding">Nieuw item toevoegen...</String>
|
||||
<String name="BlacklistEditorEditing">{0} bewerken...</String>
|
||||
<String name="BlacklistEditorCancel">Annuleren</String>
|
||||
<String name="BlacklistEditorSave">Opslaan</String>
|
||||
|
||||
<String name="FirstRunNext">Volgende</String>
|
||||
<String name="FirstRunPrev">Vorige</String>
|
||||
|
||||
<String name="FirstRunWelcomeTitle">Welkom bij {0}!</String>
|
||||
<String name="FirstRunWelcomeIntro">
|
||||
{0} is een hulpprogramma dat jou controle geeft over de altijd-op-voorgrond status van programma's die je open hebt.
|
||||
|
||||
Je zal nu door een aantal stappen genomen worden om {0} naar jouw wens te configureren!
|
||||
</String>
|
||||
|
||||
<String name="FirstRunHotKeyTitle">Sneltoets selecteren</String>
|
||||
<String name="FirstRunHotKeyExplain">
|
||||
Selecteer een globale sneltoets voor het wijzigen van de altijd op voorgrond status van het venster dat op dat moment in de voorgrond staat.
|
||||
Als je geen sneltoets toe wil wijzen, druk op Herstel en vervolgens op Volgende.
|
||||
</String>
|
||||
<String name="FirstRunHotKeyNotify">Toon een melding wanneer de sneltoets is gebruikt</String>
|
||||
|
||||
<String name="FirstRunAdminTitle">Administrator acties</String>
|
||||
<String name="FirstRunAdminExplain">
|
||||
Soms kan {0} niet de altijd op voorgrond status van bepaalde vensters aanpassen omdat daar administratieve rechten voor nodig zijn.
|
||||
{0} geeft je de volgende opties voor deze situatie:
|
||||
</String>
|
||||
<String name="FirstRunAdminOptionAsk">Vraag mij wat te doen wanneer het nodig is.</String>
|
||||
<String name="FirstRunAdminOptionImplicit">Vraag mij automatisch om administratieve rechten wanneer het nodig is.</String>
|
||||
<String name="FirstRunAdminOptionAlways">Start {0} altijd als Administrator (niet aanbevolen).</String>
|
||||
|
||||
<String name="FirstRunThanksTitle">Bedankt!</String>
|
||||
<String name="FirstRunThanksThank">Bedankt voor het gebruiken van {0}!</String>
|
||||
<String name="FirstRunThanksUpdate">Controlleer de {0} [WEB]website[/WEB] en [CHANGELOG]changelog[/CHANGELOG] eens in de zoveel tijd om te kijken of er updates zijn.</String>
|
||||
<String name="FirstRunThanksSettings">Naast de opties die je al gekregen hebt, zijn er nog meer beschikbaar in het [SETTINGS]Instellingen[/SETTINGS] venster als je daarin geïnteresseerd bent.</String>
|
||||
<String name="FirstRunThanksAdmin">Als je gekozen hebt om {0} altijd als Administrator te starten, zal je een Gebruikersaccountbeheer dialoog zien nadat je op volgende klikt.</String>
|
||||
</Strings>
|
||||
</Language>
|
89
TopMostFriend/Locale.cs
Normal file
89
TopMostFriend/Locale.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using TopMostFriend.Languages;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public static class Locale {
|
||||
public const string DEFAULT = @"en-GB";
|
||||
private static XmlSerializer Serializer { get; }
|
||||
private static Dictionary<string, Language> Languages { get; }
|
||||
private static Language ActiveLanguage { get; set; }
|
||||
|
||||
static Locale() {
|
||||
Serializer = new XmlSerializer(typeof(Language));
|
||||
Languages = new Dictionary<string, Language>();
|
||||
Assembly currentAsm = Assembly.GetExecutingAssembly();
|
||||
string[] resources = currentAsm.GetManifestResourceNames();
|
||||
|
||||
foreach(string resource in resources)
|
||||
if(resource.StartsWith(@"TopMostFriend.Languages.") && resource.EndsWith(@".xml"))
|
||||
using(Stream resourceStream = currentAsm.GetManifestResourceStream(resource))
|
||||
LoadLanguage(resourceStream);
|
||||
}
|
||||
|
||||
public static string LoadLanguage(Stream stream) {
|
||||
Language lang = (Language)Serializer.Deserialize(stream);
|
||||
foreach(LanguageString ls in lang.Strings)
|
||||
ls.Value = ls.Value.Trim();
|
||||
|
||||
Languages.Add(lang.Info.Id, lang);
|
||||
if(ActiveLanguage == null && DEFAULT.Equals(lang.Info.Id))
|
||||
ActiveLanguage = lang;
|
||||
|
||||
#if DEBUG
|
||||
Debug.WriteLine(@" ==========");
|
||||
Debug.WriteLine(lang.Info);
|
||||
foreach(LanguageString str in lang.Strings)
|
||||
Debug.WriteLine(str);
|
||||
Debug.WriteLine(string.Empty);
|
||||
#endif
|
||||
|
||||
return lang.Info.Id;
|
||||
}
|
||||
|
||||
public static LanguageInfo GetCurrentLanguage() {
|
||||
return ActiveLanguage.Info;
|
||||
}
|
||||
|
||||
public static LanguageInfo[] GetAvailableLanguages() {
|
||||
return Languages.Values.Select(l => l.Info).ToArray();
|
||||
}
|
||||
|
||||
public static string GetPreferredLanguage() {
|
||||
return Settings.Has(Program.LANGUAGE)
|
||||
? Settings.Get(Program.LANGUAGE, DEFAULT)
|
||||
: CultureInfo.InstalledUICulture.Name;
|
||||
}
|
||||
|
||||
public static void SetLanguage(string langId) {
|
||||
if(!Languages.ContainsKey(langId))
|
||||
langId = DEFAULT;
|
||||
ActiveLanguage = Languages[langId];
|
||||
}
|
||||
|
||||
public static void SetLanguage(LanguageInfo langInfo) {
|
||||
SetLanguage(langInfo.Id);
|
||||
}
|
||||
|
||||
public static string String(string name, params object[] args) {
|
||||
LanguageString str = ActiveLanguage.GetString(name);
|
||||
if(str == null)
|
||||
return name;
|
||||
|
||||
List<object> rargs = new List<object> { Program.TITLE };
|
||||
rargs.AddRange(args);
|
||||
|
||||
return str.Format(rargs.ToArray());
|
||||
}
|
||||
|
||||
public static void Meow() {
|
||||
Debug.WriteLine(@"meow");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ using System.Diagnostics;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
@ -17,6 +16,8 @@ namespace TopMostFriend {
|
|||
private static HotKeyWindow HotKeys;
|
||||
private static Icon OriginalIcon;
|
||||
|
||||
public const string TITLE = @"Top Most Friend";
|
||||
|
||||
private const string GUID =
|
||||
#if DEBUG
|
||||
@"{1A22D9CA-2AA9-48F2-B007-3A48CF205CDD}";
|
||||
|
@ -25,6 +26,8 @@ namespace TopMostFriend {
|
|||
#endif
|
||||
private static readonly Mutex GlobalMutex = new Mutex(true, GUID);
|
||||
|
||||
private const string CUSTOM_LANGUAGE = @"TopMostFriendLanguage.xml";
|
||||
|
||||
public const string FOREGROUND_HOTKEY_ATOM = @"{86795D64-770D-4BD6-AA26-FA638FBAABCF}";
|
||||
#if DEBUG
|
||||
public const string FOREGROUND_HOTKEY_SETTING = @"ForegroundHotKey_DEBUG";
|
||||
|
@ -44,6 +47,10 @@ namespace TopMostFriend {
|
|||
public const string TITLE_BLACKLIST = @"TitleBlacklist";
|
||||
public const string SHOW_HOTKEY_ICON = @"ShowHotkeyIcon";
|
||||
public const string SHOW_WINDOW_LIST = @"ShowWindowList";
|
||||
public const string LAST_VERSION = @"LastVersion";
|
||||
public const string ALWAYS_RETRY_ELEVATED = @"AlwaysRetryElevated";
|
||||
public const string REVERT_ON_EXIT = @"RevertOnExit";
|
||||
public const string LANGUAGE = @"Language";
|
||||
|
||||
private static ToolStripItem RefreshButton;
|
||||
private static ToolStripItem LastSelectedItem = null;
|
||||
|
@ -53,8 +60,39 @@ namespace TopMostFriend {
|
|||
private static ToolStripItem[] ListActionItems;
|
||||
private static ToolStripItem[] AppActionItems;
|
||||
|
||||
private static readonly Dictionary<IntPtr, bool> OriginalStates = new Dictionary<IntPtr, bool>();
|
||||
|
||||
[STAThread]
|
||||
public static void Main(string[] args) {
|
||||
public static int Main(string[] args) {
|
||||
Settings.Set(LAST_VERSION, Application.ProductVersion);
|
||||
|
||||
IEnumerable<string> cliToggleNew = args.Where(a => a.StartsWith(@"--toggle=")).Select(a => a.Substring(9));
|
||||
if(cliToggleNew.Any()) {
|
||||
bool is32bit = IntPtr.Size == 4;
|
||||
|
||||
foreach(string hwndStr in cliToggleNew) {
|
||||
IntPtr hwnd;
|
||||
if(is32bit) {
|
||||
if(int.TryParse(hwndStr, out int hwnd32))
|
||||
hwnd = new IntPtr(hwnd32);
|
||||
else
|
||||
return 1;
|
||||
} else {
|
||||
if(long.TryParse(hwndStr, out long hwnd64))
|
||||
hwnd = new IntPtr(hwnd64);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
// pass 0 to skip implicit FindOwnerId call
|
||||
WindowInfo wi = new WindowInfo(hwnd, 0);
|
||||
if(!wi.ToggleTopMost(!args.Contains($@"--background={hwndStr}")))
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Environment.OSVersion.Version.Major >= 6)
|
||||
Win32.SetProcessDPIAware();
|
||||
|
||||
|
@ -64,26 +102,53 @@ namespace TopMostFriend {
|
|||
if(args.Contains(@"--reset-admin"))
|
||||
Settings.Remove(ALWAYS_ADMIN_SETTING);
|
||||
|
||||
string cliToggle = args.FirstOrDefault(x => x.StartsWith(@"--hwnd="));
|
||||
if(!string.IsNullOrEmpty(cliToggle) && int.TryParse(cliToggle.Substring(7), out int cliToggleHWnd)) {
|
||||
WindowInfo cliWindow = new WindowInfo(cliToggleHWnd);
|
||||
if(!cliWindow.ToggleTopMost())
|
||||
TopMostFailed(cliWindow);
|
||||
string cliToggleOld = args.FirstOrDefault(a => a.StartsWith(@"--hwnd="))?.Substring(7);
|
||||
if(!string.IsNullOrEmpty(cliToggleOld)) {
|
||||
IntPtr cliToggleHWnd = IntPtr.Zero;
|
||||
if(IntPtr.Size == 4) {
|
||||
if(int.TryParse(cliToggleOld, out int hwnd32))
|
||||
cliToggleHWnd = new IntPtr(hwnd32);
|
||||
} else {
|
||||
if(long.TryParse(cliToggleOld, out long hwnd64))
|
||||
cliToggleHWnd = new IntPtr(hwnd64);
|
||||
}
|
||||
|
||||
if(cliToggleHWnd != IntPtr.Zero) {
|
||||
WindowInfo cliWindow = new WindowInfo(cliToggleHWnd);
|
||||
if(!cliWindow.ToggleTopMost())
|
||||
TopMostFailed(cliWindow);
|
||||
}
|
||||
}
|
||||
|
||||
if(args.Contains(@"--stop"))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if(File.Exists(CUSTOM_LANGUAGE)) {
|
||||
string customLanguage;
|
||||
using(Stream s = File.OpenRead(CUSTOM_LANGUAGE))
|
||||
try {
|
||||
customLanguage = Locale.LoadLanguage(s);
|
||||
} catch {
|
||||
customLanguage = Locale.DEFAULT;
|
||||
}
|
||||
Locale.SetLanguage(customLanguage);
|
||||
} else
|
||||
Locale.SetLanguage(Locale.GetPreferredLanguage());
|
||||
|
||||
if(!GlobalMutex.WaitOne(0, true)) {
|
||||
MessageBox.Show(@"An instance of Top Most Friend is already running.", @"Top Most Friend");
|
||||
return;
|
||||
MessageBox.Show(Locale.String(@"AlreadyRunning"), TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isFirstRun = !Settings.Has(FOREGROUND_HOTKEY_SETTING);
|
||||
|
||||
Settings.SetDefault(FOREGROUND_HOTKEY_SETTING, 0);
|
||||
Settings.SetDefault(ALWAYS_ADMIN_SETTING, false);
|
||||
Settings.SetDefault(SHIFT_CLICK_BLACKLIST, true);
|
||||
Settings.SetDefault(SHOW_HOTKEY_ICON, true);
|
||||
Settings.SetDefault(SHOW_WINDOW_LIST, true);
|
||||
Settings.SetDefault(ALWAYS_RETRY_ELEVATED, false);
|
||||
Settings.SetDefault(REVERT_ON_EXIT, false);
|
||||
// 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);
|
||||
|
@ -102,9 +167,9 @@ namespace TopMostFriend {
|
|||
Settings.Set(TITLE_BLACKLIST, titles.ToArray());
|
||||
}
|
||||
|
||||
if(Settings.Get<bool>(ALWAYS_ADMIN_SETTING) && !IsElevated()) {
|
||||
Elevate();
|
||||
return;
|
||||
if(Settings.Get<bool>(ALWAYS_ADMIN_SETTING) && !UAC.IsElevated) {
|
||||
UAC.RestartElevated();
|
||||
return -2;
|
||||
}
|
||||
|
||||
TitleBlacklist.Clear();
|
||||
|
@ -134,19 +199,19 @@ namespace TopMostFriend {
|
|||
CtxMenu.ItemClicked += CtxMenu_ItemClicked;
|
||||
ListActionItems = new ToolStripItem[] {
|
||||
new ToolStripSeparator(),
|
||||
RefreshButton = new ToolStripMenuItem(@"&Refresh", Properties.Resources.arrow_refresh, new EventHandler((s, e) => RefreshWindowList())),
|
||||
RefreshButton = new ToolStripMenuItem(Locale.String(@"TrayRefresh"), Properties.Resources.arrow_refresh, new EventHandler((s, e) => RefreshWindowList())),
|
||||
};
|
||||
AppActionItems = new ToolStripItem[] {
|
||||
new ToolStripMenuItem(@"&Settings", Properties.Resources.cog, new EventHandler((s, e) => SettingsWindow.Display())),
|
||||
new ToolStripMenuItem(@"&About", Properties.Resources.help, new EventHandler((s, e) => AboutWindow.Display())),
|
||||
new ToolStripMenuItem(@"&Quit", Properties.Resources.door_in, new EventHandler((s, e) => Application.Exit())),
|
||||
new ToolStripMenuItem(Locale.String(@"TraySettings"), Properties.Resources.cog, new EventHandler((s, e) => SettingsWindow.Display())),
|
||||
new ToolStripMenuItem(Locale.String(@"TrayAbout"), Properties.Resources.help, new EventHandler((s, e) => AboutWindow.Display())),
|
||||
new ToolStripMenuItem(Locale.String(@"TrayQuit"), Properties.Resources.door_in, new EventHandler((s, e) => Application.Exit())),
|
||||
};
|
||||
CtxMenu.Items.AddRange(AppActionItems);
|
||||
|
||||
SysIcon = new NotifyIcon {
|
||||
Visible = true,
|
||||
Icon = OriginalIcon,
|
||||
Text = @"Top Most Application Manager",
|
||||
Text = TITLE,
|
||||
};
|
||||
SysIcon.ContextMenuStrip = CtxMenu;
|
||||
SysIcon.MouseDown += SysIcon_MouseDown;
|
||||
|
@ -154,9 +219,26 @@ namespace TopMostFriend {
|
|||
HotKeys = new HotKeyWindow();
|
||||
SetForegroundHotKey(Settings.Get<int>(FOREGROUND_HOTKEY_SETTING));
|
||||
|
||||
if(isFirstRun)
|
||||
FirstRunWindow.Display();
|
||||
|
||||
Application.Run();
|
||||
|
||||
if(Settings.Get(REVERT_ON_EXIT, false))
|
||||
RevertTopMostStatus();
|
||||
|
||||
Shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void RevertTopMostStatus() {
|
||||
foreach(KeyValuePair<IntPtr, bool> originalState in OriginalStates) {
|
||||
WindowInfo wi = new WindowInfo(originalState.Key);
|
||||
if(wi.IsTopMost != originalState.Value)
|
||||
if(!wi.ToggleTopMost(false))
|
||||
TopMostFailed(wi, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddBlacklistedTitle(string title) {
|
||||
|
@ -192,33 +274,6 @@ namespace TopMostFriend {
|
|||
GlobalMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
private static bool? IsElevatedValue;
|
||||
|
||||
public static bool IsElevated() {
|
||||
if(!IsElevatedValue.HasValue) {
|
||||
using(WindowsIdentity identity = WindowsIdentity.GetCurrent())
|
||||
IsElevatedValue = identity != null && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
return IsElevatedValue.Value;
|
||||
}
|
||||
|
||||
public static void Elevate(string args = null) {
|
||||
if(IsElevated())
|
||||
return;
|
||||
|
||||
Shutdown();
|
||||
|
||||
Process.Start(new ProcessStartInfo {
|
||||
UseShellExecute = true,
|
||||
FileName = Application.ExecutablePath,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
Arguments = args ?? string.Empty,
|
||||
Verb = @"runas",
|
||||
});
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
public static void SetForegroundHotKey(int keyCode) {
|
||||
SetForegroundHotKey((Win32ModKeys)(keyCode & 0xFFFF), (Keys)((keyCode & 0xFFFF0000) >> 16));
|
||||
}
|
||||
|
@ -270,8 +325,13 @@ namespace TopMostFriend {
|
|||
if(Settings.Get(SHIFT_CLICK_BLACKLIST, true) && Control.ModifierKeys.HasFlag(Keys.Shift)) {
|
||||
AddBlacklistedTitle(title);
|
||||
SaveBlacklistedTitles();
|
||||
} else if(!window.ToggleTopMost())
|
||||
TopMostFailed(window);
|
||||
} else {
|
||||
if(window.ToggleTopMost()) {
|
||||
if(!OriginalStates.ContainsKey(window.Handle))
|
||||
OriginalStates[window.Handle] = !window.IsTopMost;
|
||||
} else
|
||||
TopMostFailed(window);
|
||||
}
|
||||
})) {
|
||||
CheckOnClick = true,
|
||||
Checked = window.IsTopMost,
|
||||
|
@ -287,50 +347,29 @@ namespace TopMostFriend {
|
|||
CtxMenu.Items.AddRange(items.ToArray());
|
||||
}
|
||||
|
||||
private static void TopMostFailed(WindowInfo window) {
|
||||
MessageBoxButtons buttons = MessageBoxButtons.OK;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(@"Wasn't able to change topmost status on this window.");
|
||||
private static void TopMostFailed(WindowInfo window, bool switchWindow = true) {
|
||||
bool retryElevated = Settings.Get(ALWAYS_RETRY_ELEVATED, false),
|
||||
isElevated = UAC.IsElevated;
|
||||
|
||||
if(!IsElevated()) {
|
||||
sb.AppendLine(@"Do you want to restart Top Most Friend as administrator and try again?");
|
||||
buttons = MessageBoxButtons.YesNo;
|
||||
if(!retryElevated) {
|
||||
MessageBoxButtons buttons = MessageBoxButtons.OK;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(Locale.String(@"ErrUnableAlterStatus1"));
|
||||
|
||||
if(!isElevated) {
|
||||
sb.AppendLine(Locale.String(@"ErrUnableAlterStatus2"));
|
||||
buttons = MessageBoxButtons.YesNo;
|
||||
}
|
||||
|
||||
retryElevated = MessageBox.Show(sb.ToString(), TITLE, buttons, MessageBoxIcon.Error) == DialogResult.Yes;
|
||||
}
|
||||
|
||||
DialogResult result = MessageBox.Show(sb.ToString(), @"Top Most Friend", buttons, MessageBoxIcon.Error);
|
||||
|
||||
if(result == DialogResult.Yes)
|
||||
Elevate($@"--hwnd={window.Handle}");
|
||||
}
|
||||
|
||||
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;
|
||||
if(retryElevated) {
|
||||
if(window.ToggleTopMostElevated(switchWindow)) {
|
||||
if(!OriginalStates.ContainsKey(window.Handle))
|
||||
OriginalStates[window.Handle] = !window.IsTopMost;
|
||||
} else
|
||||
MessageBox.Show(Locale.String(@"ErrUnableAlterStatusProtected"), TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,8 +382,8 @@ namespace TopMostFriend {
|
|||
if(Settings.Get(TOGGLE_BALLOON_SETTING, false)) {
|
||||
string title = window.Title;
|
||||
SysIcon?.ShowBalloonTip(
|
||||
2000, window.IsTopMost ? @"Always on top" : @"No longer always on top",
|
||||
string.IsNullOrEmpty(title) ? @"Window has no title." : title,
|
||||
2000, Locale.String(window.IsTopMost ? @"NotifyOnTop" : @"NotifyNoLonger"),
|
||||
string.IsNullOrEmpty(title) ? Locale.String(@"NotifyNoTitle") : title,
|
||||
ToolTipIcon.Info
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
|
|||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.5.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.5.1.0")]
|
||||
[assembly: AssemblyVersion("1.6.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.6.0.0")]
|
||||
|
|
10
TopMostFriend/Properties/Resources.Designer.cs
generated
10
TopMostFriend/Properties/Resources.Designer.cs
generated
|
@ -100,6 +100,16 @@ namespace TopMostFriend.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap firstrun {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("firstrun", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
|
|
@ -130,6 +130,9 @@
|
|||
<data name="door_in" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\door_in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="firstrun" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\firstrun.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="help" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\help.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
|
BIN
TopMostFriend/Resources/firstrun.png
Normal file
BIN
TopMostFriend/Resources/firstrun.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -26,9 +26,7 @@ namespace TopMostFriend {
|
|||
}
|
||||
|
||||
public static string[] Get(string name, string[] fallback = null) {
|
||||
byte[] buffer = GetRoot().GetValue(name, null) as byte[];
|
||||
|
||||
if (buffer == null)
|
||||
if(!(GetRoot().GetValue(name, null) is byte[] buffer))
|
||||
return fallback;
|
||||
|
||||
List<string> strings = new List<string>();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using TopMostFriend.Languages;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public class SettingsWindow : Form {
|
||||
|
@ -29,184 +32,172 @@ namespace TopMostFriend {
|
|||
public readonly CheckBox FlShiftClickBlacklist;
|
||||
public readonly CheckBox FlShowHotkeyIcon;
|
||||
public readonly CheckBox FlShowWindowList;
|
||||
public readonly CheckBox FlAlwaysRetryAsAdmin;
|
||||
public readonly CheckBox FlRevertOnExit;
|
||||
|
||||
public readonly ComboBox LangSelect;
|
||||
|
||||
public SettingsWindow() {
|
||||
Text = @"Top Most Friend Settings";
|
||||
Text = Locale.String(@"SettingsTitle");
|
||||
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
AutoScaleMode = AutoScaleMode.Dpi;
|
||||
ClientSize = new Size(430, 298);
|
||||
ClientSize = new Size(430, 407);
|
||||
MinimizeBox = MaximizeBox = false;
|
||||
MinimumSize = MaximumSize = Size;
|
||||
TopMost = true;
|
||||
|
||||
KeyCode = Settings.Get(Program.FOREGROUND_HOTKEY_SETTING, 0);
|
||||
|
||||
Button applyButton = new Button {
|
||||
Text = @"Apply",
|
||||
Text = Locale.String(@"SettingsApply"),
|
||||
Size = new Size(75, 23),
|
||||
Location = new Point(ClientSize.Width - 81, ClientSize.Height - 30),
|
||||
TabIndex = 10003,
|
||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||
};
|
||||
applyButton.Click += ApplyButton_Click;
|
||||
Button cancelButton = new Button {
|
||||
Text = @"Cancel",
|
||||
Text = Locale.String(@"SettingsCancel"),
|
||||
Size = applyButton.Size,
|
||||
Location = new Point(ClientSize.Width - 162, applyButton.Location.Y),
|
||||
TabIndex = 10002,
|
||||
Anchor = applyButton.Anchor,
|
||||
};
|
||||
cancelButton.Click += CancelButton_Click;
|
||||
Button okButton = new Button {
|
||||
Text = @"OK",
|
||||
Text = Locale.String(@"SettingsOk"),
|
||||
Size = applyButton.Size,
|
||||
Location = new Point(ClientSize.Width - 243, applyButton.Location.Y),
|
||||
TabIndex = 10001,
|
||||
Anchor = applyButton.Anchor,
|
||||
};
|
||||
okButton.Click += OkButton_Click;
|
||||
|
||||
GroupBox hotKeyGroup = new GroupBox {
|
||||
Text = @"Hotkeys",
|
||||
Text = Locale.String(@"SettingsHotKeysTitle"),
|
||||
Location = new Point(6, 6),
|
||||
Size = new Size(Width - 18, 70),
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Right,
|
||||
};
|
||||
|
||||
GroupBox flagsGroup = new GroupBox {
|
||||
Text = @"Flags",
|
||||
Location = new Point(6, 76),
|
||||
Size = new Size(Width - 18, 130),
|
||||
Text = Locale.String(@"SettingsOptionsTitle"),
|
||||
Location = new Point(6, 80),
|
||||
Size = new Size(Width - 18, 170),
|
||||
Anchor = hotKeyGroup.Anchor,
|
||||
};
|
||||
|
||||
GroupBox blackListGroup = new GroupBox {
|
||||
Text = @"Blacklist",
|
||||
Location = new Point(6, 206),
|
||||
GroupBox langGroup = new GroupBox {
|
||||
Text = Locale.String(@"SettingsLanguageTitle"),
|
||||
Location = new Point(6, 254),
|
||||
Size = new Size(Width - 18, 55),
|
||||
Anchor = hotKeyGroup.Anchor,
|
||||
};
|
||||
GroupBox otherGroup = new GroupBox {
|
||||
Text = Locale.String(@"SettingsOtherTitle"),
|
||||
Location = new Point(6, 313),
|
||||
Size = new Size(Width - 18, 55),
|
||||
Anchor = hotKeyGroup.Anchor,
|
||||
};
|
||||
|
||||
Controls.AddRange(new Control[] {
|
||||
applyButton, cancelButton, okButton,
|
||||
hotKeyGroup, flagsGroup, blackListGroup,
|
||||
hotKeyGroup, flagsGroup, langGroup, otherGroup,
|
||||
});
|
||||
|
||||
Label toggleForegroundLabel = new Label {
|
||||
hotKeyGroup.Controls.Add(new Label {
|
||||
AutoSize = true,
|
||||
Text = @"Toggle always on top status on active window",
|
||||
Text = Locale.String(@"SettingsHotKeysToggle"),
|
||||
Location = new Point(8, 17),
|
||||
};
|
||||
|
||||
const int mod_x = 120;
|
||||
const int mod_y = 34;
|
||||
|
||||
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;
|
||||
|
||||
FgModCtrl = new CheckBox {
|
||||
Text = @"CTRL",
|
||||
Location = new Point(mod_x, mod_y),
|
||||
Checked = (KeyCode & (int)Win32ModKeys.MOD_CONTROL) > 0,
|
||||
Appearance = Appearance.Button,
|
||||
Size = new Size(50, 23),
|
||||
TextAlign = ContentAlignment.MiddleCenter,
|
||||
TabIndex = 102,
|
||||
};
|
||||
FgModCtrl.Click += FgModCtrl_Click;
|
||||
|
||||
FgModAlt = new CheckBox {
|
||||
Text = @"ALT",
|
||||
Location = new Point(mod_x + 50, mod_y),
|
||||
Checked = (KeyCode & (int)Win32ModKeys.MOD_ALT) > 0,
|
||||
Appearance = FgModCtrl.Appearance,
|
||||
Size = FgModCtrl.Size,
|
||||
TextAlign = FgModCtrl.TextAlign,
|
||||
TabIndex = 103,
|
||||
};
|
||||
FgModAlt.Click += FgModAlt_Click;
|
||||
|
||||
FgModShift = new CheckBox {
|
||||
Text = @"SHIFT",
|
||||
Location = new Point(mod_x + 100, mod_y),
|
||||
Checked = (KeyCode & (int)Win32ModKeys.MOD_SHIFT) > 0,
|
||||
Appearance = FgModCtrl.Appearance,
|
||||
Size = FgModCtrl.Size,
|
||||
TextAlign = FgModCtrl.TextAlign,
|
||||
TabIndex = 104,
|
||||
};
|
||||
FgModShift.Click += FgModShift_Click;
|
||||
|
||||
FgModWindows = new CheckBox {
|
||||
Text = @"WIN",
|
||||
Location = new Point(mod_x + 150, mod_y),
|
||||
Checked = (KeyCode & (int)Win32ModKeys.MOD_WIN) > 0,
|
||||
Appearance = FgModCtrl.Appearance,
|
||||
Size = FgModCtrl.Size,
|
||||
TextAlign = FgModCtrl.TextAlign,
|
||||
TabIndex = 105,
|
||||
};
|
||||
FgModWindows.Click += FgModWindows_Click;
|
||||
|
||||
hotKeyGroup.Controls.AddRange(new Control[] {
|
||||
toggleForegroundLabel, FgModCtrl, FgModAlt, FgModShift, FgModWindows, fgReset, FgKey,
|
||||
});
|
||||
|
||||
FlAlwaysAdmin = new CheckBox {
|
||||
Text = @"Always run as administrator",
|
||||
CreateHotKeyInput(
|
||||
hotKeyGroup,
|
||||
() => KeyCode,
|
||||
keyCode => KeyCode = keyCode
|
||||
);
|
||||
|
||||
FlToggleNotification = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsToggleNotify"),
|
||||
Location = new Point(10, 20),
|
||||
Checked = Settings.Get(Program.ALWAYS_ADMIN_SETTING, false),
|
||||
Checked = Settings.Get(Program.TOGGLE_BALLOON_SETTING, Program.ToggleBalloonDefault),
|
||||
AutoSize = true,
|
||||
TabIndex = 201,
|
||||
};
|
||||
FlToggleNotification = new CheckBox {
|
||||
Text = @"Show notification when using toggle hotkey",
|
||||
FlShowHotkeyIcon = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsToggleNotifyIcon"),
|
||||
Location = new Point(10, 40),
|
||||
Checked = Settings.Get(Program.TOGGLE_BALLOON_SETTING, Program.ToggleBalloonDefault),
|
||||
Checked = Settings.Get(Program.SHOW_HOTKEY_ICON, true),
|
||||
AutoSize = true,
|
||||
TabIndex = 202,
|
||||
};
|
||||
FlShiftClickBlacklist = new CheckBox {
|
||||
Text = @"SHIFT+CLICK items in the list to add to the title blacklist",
|
||||
FlAlwaysRetryAsAdmin = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsElevatedRetry"),
|
||||
Location = new Point(10, 60),
|
||||
Checked = Settings.Get(Program.SHIFT_CLICK_BLACKLIST, true),
|
||||
Checked = Settings.Get(Program.ALWAYS_RETRY_ELEVATED, false),
|
||||
AutoSize = true,
|
||||
TabIndex = 203,
|
||||
};
|
||||
FlShowHotkeyIcon = new CheckBox {
|
||||
Text = @"Show icon of window affected by hotkey",
|
||||
FlShiftClickBlacklist = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsShiftBlacklist"),
|
||||
Location = new Point(10, 80),
|
||||
Checked = Settings.Get(Program.SHOW_HOTKEY_ICON, true),
|
||||
Checked = Settings.Get(Program.SHIFT_CLICK_BLACKLIST, true),
|
||||
AutoSize = true,
|
||||
TabIndex = 204,
|
||||
};
|
||||
FlShowWindowList = new CheckBox {
|
||||
Text = @"Show list of open windows in the task bar context menu",
|
||||
FlRevertOnExit = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsRevertOnExit"),
|
||||
Location = new Point(10, 100),
|
||||
Checked = Settings.Get(Program.SHOW_WINDOW_LIST, true),
|
||||
Checked = Settings.Get(Program.REVERT_ON_EXIT, false),
|
||||
AutoSize = true,
|
||||
TabIndex = 205,
|
||||
};
|
||||
FlShowWindowList = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsShowTrayList"),
|
||||
Location = new Point(10, 120),
|
||||
Checked = Settings.Get(Program.SHOW_WINDOW_LIST, true),
|
||||
AutoSize = true,
|
||||
TabIndex = 206,
|
||||
};
|
||||
FlAlwaysAdmin = new CheckBox {
|
||||
Text = Locale.String(@"SettingsOptionsAlwaysAdmin"),
|
||||
Location = new Point(10, 140),
|
||||
Checked = Settings.Get(Program.ALWAYS_ADMIN_SETTING, false),
|
||||
AutoSize = true,
|
||||
TabIndex = 207,
|
||||
};
|
||||
|
||||
flagsGroup.Controls.AddRange(new[] {
|
||||
CheckBox[] options = new[] {
|
||||
FlAlwaysAdmin, FlToggleNotification, FlShiftClickBlacklist,
|
||||
FlShowHotkeyIcon, FlShowWindowList,
|
||||
});
|
||||
FlShowHotkeyIcon, FlShowWindowList, FlAlwaysRetryAsAdmin,
|
||||
FlRevertOnExit,
|
||||
};
|
||||
|
||||
flagsGroup.Controls.AddRange(options);
|
||||
|
||||
foreach(CheckBox option in options)
|
||||
if((option.Width + (option.Left * 2)) > flagsGroup.ClientSize.Width)
|
||||
ClientSize = new Size(option.Width + 30, ClientSize.Height);
|
||||
|
||||
LangSelect = new ComboBox {
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Right,
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
Size = new Size(langGroup.Width - 24, 21),
|
||||
Location = new Point(12, 22),
|
||||
};
|
||||
LangSelect.Items.AddRange(Locale.GetAvailableLanguages());
|
||||
LangSelect.SelectedItem = Locale.GetCurrentLanguage();
|
||||
langGroup.Controls.Add(LangSelect);
|
||||
|
||||
Button titleBlacklist = new Button {
|
||||
Size = new Size(120, 23),
|
||||
Location = new Point(10, 20),
|
||||
Text = @"Manage...",
|
||||
Text = Locale.String(@"SettingsOtherBlacklistButton"),
|
||||
TabIndex = 301,
|
||||
AutoSize = true,
|
||||
AutoSizeMode = AutoSizeMode.GrowOnly,
|
||||
};
|
||||
titleBlacklist.Click += (s, e) => {
|
||||
string[] newList = BlacklistWindow.Display(@"Title Blacklist", Program.GetBlacklistedTitles());
|
||||
string[] newList = BlacklistWindow.Display(Locale.String(@"SettingsOtherBlacklistWindowTitle"), Program.GetBlacklistedTitles());
|
||||
|
||||
if(newList != null) {
|
||||
Program.ApplyBlacklistedTitles(newList);
|
||||
|
@ -214,22 +205,207 @@ namespace TopMostFriend {
|
|||
}
|
||||
};
|
||||
|
||||
blackListGroup.Controls.AddRange(new[] { titleBlacklist });
|
||||
Button startWithWindows = new Button {
|
||||
Size = new Size(120, 23),
|
||||
Location = new Point(134, 20),
|
||||
Text = Locale.String(@"SettingsOtherStartupButton"),
|
||||
TabIndex = 302,
|
||||
AutoSize = true,
|
||||
AutoSizeMode = AutoSizeMode.GrowOnly,
|
||||
};
|
||||
startWithWindows.Click += (s, e) => {
|
||||
DialogResult dr = MessageBox.Show(Locale.String(@"SettingsOtherStartupConfirm"), Program.TITLE, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
|
||||
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), @"TopMostFriend.lnk");
|
||||
|
||||
if(File.Exists(path))
|
||||
File.Delete(path);
|
||||
|
||||
if(dr == DialogResult.Yes) {
|
||||
IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell();
|
||||
IWshRuntimeLibrary.IWshShortcut shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(path);
|
||||
shortcut.TargetPath = Application.ExecutablePath;
|
||||
shortcut.Save();
|
||||
}
|
||||
};
|
||||
|
||||
Button resetSettings = new Button {
|
||||
Size = new Size(120, 23),
|
||||
Location = new Point(258, 20),
|
||||
Text = Locale.String(@"SettingsOtherResetButton"),
|
||||
TabIndex = 303,
|
||||
AutoSize = true,
|
||||
AutoSizeMode = AutoSizeMode.GrowOnly,
|
||||
};
|
||||
resetSettings.Click += (s, e) => {
|
||||
DialogResult dr = MessageBox.Show(Locale.String(@"SettingsOtherResetConfirm"), Program.TITLE, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
|
||||
|
||||
if(dr == DialogResult.Yes) {
|
||||
Settings.Remove(Program.FOREGROUND_HOTKEY_SETTING);
|
||||
Settings.Remove(Program.PROCESS_SEPARATOR_SETTING);
|
||||
Settings.Remove(Program.LIST_SELF_SETTING);
|
||||
Settings.Remove(Program.LIST_BACKGROUND_PATH_SETTING);
|
||||
Settings.Remove(Program.LIST_BACKGROUND_LAYOUT_SETTING);
|
||||
Settings.Remove(Program.ALWAYS_ADMIN_SETTING);
|
||||
Settings.Remove(Program.TOGGLE_BALLOON_SETTING);
|
||||
Settings.Remove(Program.SHIFT_CLICK_BLACKLIST);
|
||||
Settings.Remove(Program.TITLE_BLACKLIST);
|
||||
Settings.Remove(Program.SHOW_HOTKEY_ICON);
|
||||
Settings.Remove(Program.SHOW_WINDOW_LIST);
|
||||
Settings.Remove(Program.LAST_VERSION);
|
||||
Settings.Remove(Program.ALWAYS_RETRY_ELEVATED);
|
||||
Settings.Remove(Program.REVERT_ON_EXIT);
|
||||
Program.Shutdown();
|
||||
Process.Start(Application.ExecutablePath);
|
||||
Application.Exit();
|
||||
}
|
||||
};
|
||||
|
||||
Button[] otherButtons = new[] { titleBlacklist, startWithWindows, resetSettings };
|
||||
|
||||
otherGroup.Controls.AddRange(otherButtons);
|
||||
}
|
||||
|
||||
private void FgReset_Click(object sender, EventArgs e) {
|
||||
FgModCtrl.Checked = FgModAlt.Checked = FgModShift.Checked = false;
|
||||
FgKey.Text = string.Empty;
|
||||
KeyCode = 0;
|
||||
public static void CreateHotKeyInput(
|
||||
Control target,
|
||||
Func<int> getKeyCode,
|
||||
Action<int> setKeyCode,
|
||||
int offsetX = 0,
|
||||
int offsetY = 0
|
||||
) {
|
||||
int modX = 120 + offsetX;
|
||||
int modY = 34 + offsetY;
|
||||
int keyCode = getKeyCode();
|
||||
|
||||
Button fgReset = new Button {
|
||||
Text = Locale.String(@"SettingsHotKeysReset"),
|
||||
Location = new Point(target.Width - 85 + offsetX, modY),
|
||||
TabIndex = 105,
|
||||
};
|
||||
|
||||
TextBox fgKey = new TextBox {
|
||||
Text = ((Keys)(keyCode >> 16)).ToString(),
|
||||
Location = new Point(12 + offsetX, modY + 2),
|
||||
TabIndex = 101,
|
||||
};
|
||||
|
||||
CheckBox fgModCtrl = new CheckBox {
|
||||
Text = @"CTRL",
|
||||
Location = new Point(modX, modY),
|
||||
Checked = (keyCode & (int)Win32ModKeys.MOD_CONTROL) > 0,
|
||||
Appearance = Appearance.Button,
|
||||
Size = new Size(50, 23),
|
||||
TextAlign = ContentAlignment.MiddleCenter,
|
||||
TabIndex = 102,
|
||||
};
|
||||
|
||||
CheckBox fgModWindows = new CheckBox {
|
||||
Text = @"WIN",
|
||||
Location = new Point(modX + 50, modY),
|
||||
Checked = (keyCode & (int)Win32ModKeys.MOD_WIN) > 0,
|
||||
Appearance = fgModCtrl.Appearance,
|
||||
Size = fgModCtrl.Size,
|
||||
TextAlign = fgModCtrl.TextAlign,
|
||||
TabIndex = 103,
|
||||
};
|
||||
|
||||
CheckBox fgModAlt = new CheckBox {
|
||||
Text = @"ALT",
|
||||
Location = new Point(modX + 100, modY),
|
||||
Checked = (keyCode & (int)Win32ModKeys.MOD_ALT) > 0,
|
||||
Appearance = fgModCtrl.Appearance,
|
||||
Size = fgModCtrl.Size,
|
||||
TextAlign = fgModCtrl.TextAlign,
|
||||
TabIndex = 104,
|
||||
};
|
||||
|
||||
CheckBox fgModShift = new CheckBox {
|
||||
Text = @"SHIFT",
|
||||
Location = new Point(modX + 150, modY),
|
||||
Checked = (keyCode & (int)Win32ModKeys.MOD_SHIFT) > 0,
|
||||
Appearance = fgModCtrl.Appearance,
|
||||
Size = fgModCtrl.Size,
|
||||
TextAlign = fgModCtrl.TextAlign,
|
||||
TabIndex = 105,
|
||||
};
|
||||
|
||||
fgReset.Click += (s, e) => {
|
||||
fgModCtrl.Checked = fgModAlt.Checked = fgModShift.Checked = false;
|
||||
fgKey.Text = ((Keys)0).ToString();
|
||||
setKeyCode(0);
|
||||
};
|
||||
fgModCtrl.Click += (s, e) => {
|
||||
if(s is CheckBox cb) {
|
||||
if(cb.Checked)
|
||||
keyCode |= (int)Win32ModKeys.MOD_CONTROL;
|
||||
else
|
||||
keyCode &= ~(int)Win32ModKeys.MOD_CONTROL;
|
||||
setKeyCode(keyCode);
|
||||
}
|
||||
};
|
||||
fgModAlt.Click += (s, e) => {
|
||||
if(s is CheckBox cb) {
|
||||
if(cb.Checked)
|
||||
keyCode |= (int)Win32ModKeys.MOD_ALT;
|
||||
else
|
||||
keyCode &= ~(int)Win32ModKeys.MOD_ALT;
|
||||
setKeyCode(keyCode);
|
||||
}
|
||||
};
|
||||
fgModShift.Click += (s, e) => {
|
||||
if(s is CheckBox cb) {
|
||||
if(cb.Checked)
|
||||
keyCode |= (int)Win32ModKeys.MOD_SHIFT;
|
||||
else
|
||||
keyCode &= ~(int)Win32ModKeys.MOD_SHIFT;
|
||||
setKeyCode(keyCode);
|
||||
}
|
||||
};
|
||||
fgModWindows.Click += (s, e) => {
|
||||
if(s is CheckBox cb) {
|
||||
if(cb.Checked)
|
||||
keyCode |= (int)Win32ModKeys.MOD_WIN;
|
||||
else
|
||||
keyCode &= ~(int)Win32ModKeys.MOD_WIN;
|
||||
setKeyCode(keyCode);
|
||||
}
|
||||
};
|
||||
fgKey.KeyDown += (s, e) => {
|
||||
if(!(s is TextBox textBox))
|
||||
return;
|
||||
e.Handled = e.SuppressKeyPress = true;
|
||||
textBox.Text = e.KeyCode.ToString();
|
||||
keyCode &= 0xFFFF;
|
||||
keyCode |= (int)e.KeyCode << 16;
|
||||
setKeyCode(keyCode);
|
||||
};
|
||||
|
||||
target.Controls.AddRange(new Control[] {
|
||||
fgModCtrl, fgModAlt, fgModShift, fgModWindows, fgReset, fgKey,
|
||||
});
|
||||
}
|
||||
|
||||
public void Apply() {
|
||||
if(LangSelect.SelectedItem is LanguageInfo li) {
|
||||
if(li != Locale.GetCurrentLanguage()) {
|
||||
if(Locale.GetPreferredLanguage() == li.Id)
|
||||
Settings.Remove(Program.LANGUAGE);
|
||||
else
|
||||
Settings.Set(Program.LANGUAGE, li.Id);
|
||||
|
||||
Locale.SetLanguage(li);
|
||||
MessageBox.Show(Locale.String(@"SettingsLanguageChangedInfo"), Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Settings.Set(Program.SHOW_WINDOW_LIST, FlShowWindowList.Checked);
|
||||
Settings.Set(Program.ALWAYS_RETRY_ELEVATED, FlAlwaysRetryAsAdmin.Checked);
|
||||
Settings.Set(Program.REVERT_ON_EXIT, FlRevertOnExit.Checked);
|
||||
Program.SetForegroundHotKey(KeyCode);
|
||||
}
|
||||
|
||||
|
@ -244,49 +420,6 @@ namespace TopMostFriend {
|
|||
Apply();
|
||||
}
|
||||
|
||||
private void FgModCtrl_Click(object sender, EventArgs e) {
|
||||
if(sender is CheckBox cb) {
|
||||
if (cb.Checked)
|
||||
KeyCode |= (int)Win32ModKeys.MOD_CONTROL;
|
||||
else
|
||||
KeyCode &= ~(int)Win32ModKeys.MOD_CONTROL;
|
||||
}
|
||||
}
|
||||
private void FgModAlt_Click(object sender, EventArgs e) {
|
||||
if (sender is CheckBox cb) {
|
||||
if (cb.Checked)
|
||||
KeyCode |= (int)Win32ModKeys.MOD_ALT;
|
||||
else
|
||||
KeyCode &= ~(int)Win32ModKeys.MOD_ALT;
|
||||
}
|
||||
}
|
||||
private void FgModShift_Click(object sender, EventArgs e) {
|
||||
if (sender is CheckBox cb) {
|
||||
if (cb.Checked)
|
||||
KeyCode |= (int)Win32ModKeys.MOD_SHIFT;
|
||||
else
|
||||
KeyCode &= ~(int)Win32ModKeys.MOD_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
private void FgModWindows_Click(object sender, EventArgs e) {
|
||||
if(sender is CheckBox cb) {
|
||||
if(cb.Checked)
|
||||
KeyCode |= (int)Win32ModKeys.MOD_WIN;
|
||||
else
|
||||
KeyCode &= ~(int)Win32ModKeys.MOD_WIN;
|
||||
}
|
||||
}
|
||||
|
||||
private void FgKey_KeyDown(object sender, KeyEventArgs e) {
|
||||
if (!(sender is TextBox textBox))
|
||||
return;
|
||||
e.Handled = e.SuppressKeyPress = true;
|
||||
textBox.Text = e.KeyCode.ToString();
|
||||
KeyCode &= 0xFFFF;
|
||||
KeyCode |= (int)e.KeyCode << 16;
|
||||
}
|
||||
|
||||
protected override void OnFormClosed(FormClosedEventArgs e) {
|
||||
base.OnFormClosed(e);
|
||||
Instance.Dispose();
|
||||
|
|
|
@ -44,12 +44,20 @@
|
|||
<Compile Include="AboutWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ActionTimeout.cs" />
|
||||
<Compile Include="BlacklistWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="FirstRunWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="HotKeyWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Languages\Language.cs" />
|
||||
<Compile Include="Languages\LanguageInfo.cs" />
|
||||
<Compile Include="Languages\LanguageString.cs" />
|
||||
<Compile Include="Locale.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
|
@ -61,10 +69,12 @@
|
|||
<Compile Include="SettingsWindow.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UAC.cs" />
|
||||
<Compile Include="Win32.cs" />
|
||||
<Compile Include="WindowInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
|
@ -80,6 +90,9 @@
|
|||
<None Include="Resources\about.png" />
|
||||
<None Include="Resources\cog.png" />
|
||||
<None Include="Resources\arrow_refresh.png" />
|
||||
<None Include="Resources\firstrun.png" />
|
||||
<EmbeddedResource Include="Languages\en-GB.xml" />
|
||||
<EmbeddedResource Include="Languages\nl-NL.xml" />
|
||||
<Content Include="TopMostFriend.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -88,5 +101,16 @@
|
|||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
81
TopMostFriend/UAC.cs
Normal file
81
TopMostFriend/UAC.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Principal;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public static class UAC {
|
||||
private static bool? IsElevatedValue;
|
||||
private static string ExecutablePathValue;
|
||||
|
||||
static UAC() {
|
||||
ExecutablePath = null;
|
||||
}
|
||||
|
||||
public static bool IsElevated {
|
||||
get {
|
||||
if(!IsElevatedValue.HasValue)
|
||||
using(WindowsIdentity identity = WindowsIdentity.GetCurrent())
|
||||
IsElevatedValue = identity != null && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
return IsElevatedValue.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExecutablePath {
|
||||
get => ExecutablePathValue;
|
||||
set {
|
||||
ExecutablePathValue = string.IsNullOrWhiteSpace(value) || !File.Exists(value)
|
||||
? Assembly.GetEntryAssembly().Location
|
||||
: value;
|
||||
}
|
||||
}
|
||||
|
||||
public static int RunElevatedTask(string args) {
|
||||
if(string.IsNullOrWhiteSpace(args))
|
||||
throw new ArgumentException(@"No arguments provided.", nameof(args));
|
||||
|
||||
try {
|
||||
Process process = Process.Start(new ProcessStartInfo {
|
||||
UseShellExecute = true,
|
||||
FileName = ExecutablePath,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
Arguments = args,
|
||||
Verb = @"runas",
|
||||
});
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
return process.ExitCode;
|
||||
} catch(Win32Exception ex) {
|
||||
return ex.ErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static int ToggleWindowTopMost(WindowInfo window, bool switchWindow)
|
||||
=> ToggleWindowTopMost(window.Handle, switchWindow);
|
||||
|
||||
public static int ToggleWindowTopMost(IntPtr handle, bool switchWindow)
|
||||
=> RunElevatedTask(switchWindow ? $@"--toggle={handle}" : $@"--toggle={handle} --background={handle}");
|
||||
|
||||
public static void RestartElevated() {
|
||||
if(IsElevated)
|
||||
return;
|
||||
|
||||
Program.Shutdown();
|
||||
|
||||
Process.Start(new ProcessStartInfo {
|
||||
UseShellExecute = true,
|
||||
FileName = ExecutablePath,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
Arguments = string.Join(@" ", Environment.GetCommandLineArgs()),
|
||||
Verb = @"runas",
|
||||
});
|
||||
|
||||
Application.Exit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,12 +52,10 @@ namespace TopMostFriend {
|
|||
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
||||
|
||||
[DllImport(@"user32", SetLastError = true)]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
|
||||
|
||||
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex) {
|
||||
if (IntPtr.Size == 8)
|
||||
return GetWindowLongPtr64(hWnd, nIndex);
|
||||
return new IntPtr(GetWindowLong32(hWnd, nIndex));
|
||||
return IntPtr.Size == 8 ? GetWindowLongPtr64(hWnd, nIndex) : new IntPtr(GetWindowLong32(hWnd, nIndex));
|
||||
}
|
||||
|
||||
[DllImport(@"user32", EntryPoint = "GetWindowLong")]
|
||||
|
@ -84,9 +82,7 @@ namespace TopMostFriend {
|
|||
}
|
||||
|
||||
public static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex) {
|
||||
if (IntPtr.Size > 4)
|
||||
return GetClassLongPtr64(hWnd, nIndex);
|
||||
return new IntPtr(GetClassLongPtr32(hWnd, nIndex));
|
||||
return IntPtr.Size > 4 ? GetClassLongPtr64(hWnd, nIndex) : new IntPtr(GetClassLongPtr32(hWnd, nIndex));
|
||||
}
|
||||
|
||||
[DllImport(@"user32", EntryPoint = "GetClassLong")]
|
||||
|
|
|
@ -3,18 +3,18 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TopMostFriend {
|
||||
public class WindowInfo {
|
||||
public IntPtr Handle { get; }
|
||||
public Process Owner { get; }
|
||||
public int ProcessId { get; }
|
||||
|
||||
public Process Owner => Process.GetProcessById(ProcessId);
|
||||
|
||||
public string Title => Win32.GetWindowTextString(Handle);
|
||||
|
||||
public long Flags => Win32.GetWindowLongPtr(Handle, Win32.GWL_EXSTYLE).ToInt32();
|
||||
public bool IsTopMost {
|
||||
get => (Flags & Win32.WS_EX_TOPMOST) > 0;
|
||||
get => (Win32.GetWindowLongPtr(Handle, Win32.GWL_EXSTYLE).ToInt32() & Win32.WS_EX_TOPMOST) > 0;
|
||||
set {
|
||||
Win32.SetWindowPos(
|
||||
Handle, new IntPtr(value ? Win32.HWND_TOPMOST : Win32.HWND_NOTOPMOST),
|
||||
|
@ -52,33 +52,34 @@ namespace TopMostFriend {
|
|||
public Image IconBitmap
|
||||
=> Icon?.ToBitmap();
|
||||
|
||||
public WindowInfo(int handle)
|
||||
: this(new IntPtr(handle)) {}
|
||||
|
||||
public WindowInfo(IntPtr handle)
|
||||
: this(handle, FindOwner(handle)) {}
|
||||
: this(handle, FindOwnerId(handle)) { }
|
||||
|
||||
public WindowInfo(IntPtr handle, Process owner) {
|
||||
public WindowInfo(IntPtr handle, int processId) {
|
||||
Handle = handle;
|
||||
Owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
||||
ProcessId = processId;
|
||||
}
|
||||
|
||||
public void SwitchTo() {
|
||||
Win32.SwitchToThisWindow(Handle, false);
|
||||
}
|
||||
|
||||
public bool ToggleTopMost() {
|
||||
public bool ToggleTopMost(bool switchWindow = true) {
|
||||
bool expected = !IsTopMost;
|
||||
IsTopMost = expected;
|
||||
bool success = IsTopMost == expected;
|
||||
if(expected && success)
|
||||
if(switchWindow && expected && success)
|
||||
SwitchTo();
|
||||
return success;
|
||||
}
|
||||
|
||||
public static Process FindOwner(IntPtr hWnd) {
|
||||
Win32.GetWindowThreadProcessId(hWnd, out uint procId);
|
||||
return Process.GetProcessById((int)procId);
|
||||
public bool ToggleTopMostElevated(bool switchWindow = true) {
|
||||
return UAC.ToggleWindowTopMost(this, switchWindow) == 0;
|
||||
}
|
||||
|
||||
public static int FindOwnerId(IntPtr hWnd) {
|
||||
Win32.GetWindowThreadProcessId(hWnd, out int procId);
|
||||
return procId;
|
||||
}
|
||||
|
||||
public static WindowInfo GetForegroundWindow() {
|
||||
|
|
BIN
firstrun.psd
Normal file
BIN
firstrun.psd
Normal file
Binary file not shown.
Loading…
Reference in a new issue