mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-08 12:18:50 +02:00
Ensure consistente naming
This commit is contained in:
@@ -20,27 +20,27 @@ internal sealed class OpenWindows
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to enforce single execution of EnumWindows
|
/// Used to enforce single execution of EnumWindows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Lock _enumWindowsLock = new();
|
private static readonly Lock EnumWindowsLock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PowerLauncher main executable
|
/// PowerLauncher main executable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly string? _powerLauncherExe = Path.GetFileName(Environment.ProcessPath);
|
private static readonly string? PowerLauncherExe = Path.GetFileName(Environment.ProcessPath);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of all the open windows
|
/// List of all the open windows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<Window> windows = [];
|
private readonly List<Window> _windows = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An instance of the class OpenWindows
|
/// An instance of the class OpenWindows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static OpenWindows? instance;
|
private static OpenWindows? _instance;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the list of all open windows
|
/// Gets the list of all open windows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal List<Window> Windows => [..windows];
|
internal List<Window> Windows => [.._windows];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an instance property of this class that makes sure that
|
/// Gets an instance property of this class that makes sure that
|
||||||
@@ -51,9 +51,9 @@ internal sealed class OpenWindows
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
instance ??= new OpenWindows();
|
_instance ??= new OpenWindows();
|
||||||
|
|
||||||
return instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +75,9 @@ internal sealed class OpenWindows
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tokenHandleParam = GCHandle.ToIntPtr(tokenHandle);
|
var tokenHandleParam = GCHandle.ToIntPtr(tokenHandle);
|
||||||
lock (_enumWindowsLock)
|
lock (EnumWindowsLock)
|
||||||
{
|
{
|
||||||
windows.Clear();
|
_windows.Clear();
|
||||||
var callbackptr = new EnumWindowsProc(WindowEnumerationCallBack);
|
var callbackptr = new EnumWindowsProc(WindowEnumerationCallBack);
|
||||||
_ = NativeMethods.EnumWindows(callbackptr, tokenHandleParam);
|
_ = NativeMethods.EnumWindows(callbackptr, tokenHandleParam);
|
||||||
}
|
}
|
||||||
@@ -114,13 +114,13 @@ internal sealed class OpenWindows
|
|||||||
if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
|
if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
|
||||||
(!newWindow.IsToolWindow || newWindow.IsAppWindow) && !newWindow.TaskListDeleted &&
|
(!newWindow.IsToolWindow || newWindow.IsAppWindow) && !newWindow.TaskListDeleted &&
|
||||||
(newWindow.Desktop.IsVisible || !SettingsManager.Instance.ResultsFromVisibleDesktopOnly || WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetDesktopCount() < 2) &&
|
(newWindow.Desktop.IsVisible || !SettingsManager.Instance.ResultsFromVisibleDesktopOnly || WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetDesktopCount() < 2) &&
|
||||||
newWindow.ClassName != "Windows.UI.Core.CoreWindow" && newWindow.Process.Name != _powerLauncherExe)
|
newWindow.ClassName != "Windows.UI.Core.CoreWindow" && newWindow.Process.Name != PowerLauncherExe)
|
||||||
{
|
{
|
||||||
// To hide (not add) preloaded uwp app windows that are invisible to the user and other cloaked windows, we check the cloak state. (Issue #13637.)
|
// To hide (not add) preloaded uwp app windows that are invisible to the user and other cloaked windows, we check the cloak state. (Issue #13637.)
|
||||||
// (If user asking to see cloaked uwp app windows again we can add an optional plugin setting in the future.)
|
// (If user asking to see cloaked uwp app windows again we can add an optional plugin setting in the future.)
|
||||||
if (!newWindow.IsCloaked || newWindow.GetWindowCloakState() == Window.WindowCloakState.OtherDesktop)
|
if (!newWindow.IsCloaked || newWindow.GetWindowCloakState() == Window.WindowCloakState.OtherDesktop)
|
||||||
{
|
{
|
||||||
windows.Add(newWindow);
|
_windows.Add(newWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,26 +20,11 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Components;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class Window
|
internal sealed class Window
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The handle to the window
|
|
||||||
/// </summary>
|
|
||||||
private readonly IntPtr hwnd;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A static cache for the process data of all known windows
|
/// A static cache for the process data of all known windows
|
||||||
/// that we don't have to query the data every time
|
/// that we don't have to query the data every time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Dictionary<IntPtr, WindowProcess> _handlesToProcessCache = new();
|
private static readonly Dictionary<IntPtr, WindowProcess> HandlesToProcessCache = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An instance of <see cref="WindowProcess"/> that contains the process information for the window
|
|
||||||
/// </summary>
|
|
||||||
private readonly WindowProcess processInfo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An instance of <see cref="VDesktop"/> that contains the desktop information for the window
|
|
||||||
/// </summary>
|
|
||||||
private readonly VDesktop desktopInfo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the title of the window (the string displayed at the top of the window)
|
/// Gets the title of the window (the string displayed at the top of the window)
|
||||||
@@ -48,11 +33,11 @@ internal sealed class Window
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var sizeOfTitle = NativeMethods.GetWindowTextLength(hwnd);
|
var sizeOfTitle = NativeMethods.GetWindowTextLength(Hwnd);
|
||||||
if (sizeOfTitle++ > 0)
|
if (sizeOfTitle++ > 0)
|
||||||
{
|
{
|
||||||
var titleBuffer = new StringBuilder(sizeOfTitle);
|
var titleBuffer = new StringBuilder(sizeOfTitle);
|
||||||
var numCharactersWritten = NativeMethods.GetWindowText(hwnd, titleBuffer, sizeOfTitle);
|
var numCharactersWritten = NativeMethods.GetWindowText(Hwnd, titleBuffer, sizeOfTitle);
|
||||||
if (numCharactersWritten == 0)
|
if (numCharactersWritten == 0)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
@@ -70,17 +55,17 @@ internal sealed class Window
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handle to the window
|
/// Gets the handle to the window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IntPtr Hwnd => hwnd;
|
internal IntPtr Hwnd { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the object of with the process information of the window
|
/// Gets the object of with the process information of the window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal WindowProcess Process => processInfo;
|
internal WindowProcess Process { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the object of with the desktop information of the window
|
/// Gets the object of with the desktop information of the window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal VDesktop Desktop => desktopInfo;
|
internal VDesktop Desktop { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the class for the window represented
|
/// Gets the name of the class for the window represented
|
||||||
@@ -140,9 +125,9 @@ internal sealed class Window
|
|||||||
internal Window(IntPtr hwnd)
|
internal Window(IntPtr hwnd)
|
||||||
{
|
{
|
||||||
// TODO: Add verification as to whether the window handle is valid
|
// TODO: Add verification as to whether the window handle is valid
|
||||||
this.hwnd = hwnd;
|
Hwnd = hwnd;
|
||||||
processInfo = CreateWindowProcessInstance(hwnd);
|
Process = CreateWindowProcessInstance(hwnd);
|
||||||
desktopInfo = WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetWindowDesktop(hwnd);
|
Desktop = WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.GetWindowDesktop(hwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -155,7 +140,7 @@ internal sealed class Window
|
|||||||
// to use ShowWindow for switching tabs in IE
|
// to use ShowWindow for switching tabs in IE
|
||||||
// 2) SetForegroundWindow fails on minimized windows
|
// 2) SetForegroundWindow fails on minimized windows
|
||||||
// Using Ordinal since this is internal
|
// Using Ordinal since this is internal
|
||||||
if (processInfo.Name?.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) == true || !Minimized)
|
if (Process.Name?.ToUpperInvariant().Equals("IEXPLORE.EXE", StringComparison.Ordinal) == true || !Minimized)
|
||||||
{
|
{
|
||||||
NativeMethods.SetForegroundWindow(Hwnd);
|
NativeMethods.SetForegroundWindow(Hwnd);
|
||||||
}
|
}
|
||||||
@@ -197,27 +182,27 @@ internal sealed class Window
|
|||||||
{
|
{
|
||||||
icon = null;
|
icon = null;
|
||||||
|
|
||||||
if (hwnd == IntPtr.Zero)
|
if (Hwnd == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try WM_GETICON with SendMessageTimeout
|
// Try WM_GETICON with SendMessageTimeout
|
||||||
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_BIG, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out var result) != 0 && result != 0)
|
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_BIG, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out var result) != 0 && result != 0)
|
||||||
{
|
{
|
||||||
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
||||||
NativeMethods.DestroyIcon((IntPtr)result);
|
NativeMethods.DestroyIcon((IntPtr)result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
|
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
|
||||||
{
|
{
|
||||||
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
||||||
NativeMethods.DestroyIcon((IntPtr)result);
|
NativeMethods.DestroyIcon((IntPtr)result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NativeMethods.SendMessageTimeout(hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL2, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
|
if (NativeMethods.SendMessageTimeout(Hwnd, Win32Constants.WM_GETICON, (UIntPtr)Win32Constants.ICON_SMALL2, IntPtr.Zero, Win32Constants.SMTO_ABORTIFHUNG, 100, out result) != 0 && result != 0)
|
||||||
{
|
{
|
||||||
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
icon = System.Drawing.Icon.FromHandle((IntPtr)result);
|
||||||
NativeMethods.DestroyIcon((IntPtr)result);
|
NativeMethods.DestroyIcon((IntPtr)result);
|
||||||
@@ -225,7 +210,7 @@ internal sealed class Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to GetClassLongPtr
|
// Fallback to GetClassLongPtr
|
||||||
var iconHandle = NativeMethods.GetClassLongPtr(hwnd, Win32Constants.GCLP_HICON);
|
var iconHandle = NativeMethods.GetClassLongPtr(Hwnd, Win32Constants.GCLP_HICON);
|
||||||
if (iconHandle != IntPtr.Zero)
|
if (iconHandle != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
icon = System.Drawing.Icon.FromHandle(iconHandle);
|
icon = System.Drawing.Icon.FromHandle(iconHandle);
|
||||||
@@ -233,7 +218,7 @@ internal sealed class Window
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
iconHandle = NativeMethods.GetClassLongPtr(hwnd, Win32Constants.GCLP_HICONSM);
|
iconHandle = NativeMethods.GetClassLongPtr(Hwnd, Win32Constants.GCLP_HICONSM);
|
||||||
if (iconHandle != IntPtr.Zero)
|
if (iconHandle != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
icon = System.Drawing.Icon.FromHandle(iconHandle);
|
icon = System.Drawing.Icon.FromHandle(iconHandle);
|
||||||
@@ -251,7 +236,7 @@ internal sealed class Window
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// Using CurrentCulture since this is user facing
|
// Using CurrentCulture since this is user facing
|
||||||
return Title + " (" + processInfo.Name?.ToUpper(CultureInfo.CurrentCulture) + ")";
|
return Title + " (" + Process.Name?.ToUpper(CultureInfo.CurrentCulture) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -304,7 +289,7 @@ internal sealed class Window
|
|||||||
case (int)DwmWindowCloakStates.CloakedApp:
|
case (int)DwmWindowCloakStates.CloakedApp:
|
||||||
return WindowCloakState.App;
|
return WindowCloakState.App;
|
||||||
case (int)DwmWindowCloakStates.CloakedShell:
|
case (int)DwmWindowCloakStates.CloakedShell:
|
||||||
return WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.IsWindowCloakedByVirtualDesktopManager(hwnd, Desktop.Id) ? WindowCloakState.OtherDesktop : WindowCloakState.Shell;
|
return WindowWalkerCommandsProvider.VirtualDesktopHelperInstance.IsWindowCloakedByVirtualDesktopManager(Hwnd, Desktop.Id) ? WindowCloakState.OtherDesktop : WindowCloakState.Shell;
|
||||||
case (int)DwmWindowCloakStates.CloakedInherited:
|
case (int)DwmWindowCloakStates.CloakedInherited:
|
||||||
return WindowCloakState.Inherited;
|
return WindowCloakState.Inherited;
|
||||||
default:
|
default:
|
||||||
@@ -350,16 +335,16 @@ internal sealed class Window
|
|||||||
/// <returns>A new Instance of type <see cref="WindowProcess"/></returns>
|
/// <returns>A new Instance of type <see cref="WindowProcess"/></returns>
|
||||||
private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
|
private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow)
|
||||||
{
|
{
|
||||||
lock (_handlesToProcessCache)
|
lock (HandlesToProcessCache)
|
||||||
{
|
{
|
||||||
if (_handlesToProcessCache.Count > 7000)
|
if (HandlesToProcessCache.Count > 7000)
|
||||||
{
|
{
|
||||||
Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count);
|
Debug.Print("Clearing Process Cache because it's size is " + HandlesToProcessCache.Count);
|
||||||
_handlesToProcessCache.Clear();
|
HandlesToProcessCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add window's process to cache if missing
|
// Add window's process to cache if missing
|
||||||
if (!_handlesToProcessCache.ContainsKey(hWindow))
|
if (!HandlesToProcessCache.ContainsKey(hWindow))
|
||||||
{
|
{
|
||||||
// Get process ID and name
|
// Get process ID and name
|
||||||
var processId = WindowProcess.GetProcessIDFromWindowHandle(hWindow);
|
var processId = WindowProcess.GetProcessIDFromWindowHandle(hWindow);
|
||||||
@@ -368,19 +353,19 @@ internal sealed class Window
|
|||||||
|
|
||||||
if (processName.Length != 0)
|
if (processName.Length != 0)
|
||||||
{
|
{
|
||||||
_handlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
|
HandlesToProcessCache.Add(hWindow, new WindowProcess(processId, threadId, processName));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// For the dwm process we cannot receive the name. This is no problem because the window isn't part of result list.
|
// For the dwm process we cannot receive the name. This is no problem because the window isn't part of result list.
|
||||||
ExtensionHost.LogMessage(new LogMessage() { Message = $"Invalid process {processId} ({processName}) for window handle {hWindow}." });
|
ExtensionHost.LogMessage(new LogMessage() { Message = $"Invalid process {processId} ({processName}) for window handle {hWindow}." });
|
||||||
_handlesToProcessCache.Add(hWindow, new WindowProcess(0, 0, string.Empty));
|
HandlesToProcessCache.Add(hWindow, new WindowProcess(0, 0, string.Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe'
|
// Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe'
|
||||||
// (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.)
|
// (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.)
|
||||||
if (_handlesToProcessCache[hWindow].IsUwpAppFrameHost)
|
if (HandlesToProcessCache[hWindow].IsUwpAppFrameHost)
|
||||||
{
|
{
|
||||||
new Task(() =>
|
new Task(() =>
|
||||||
{
|
{
|
||||||
@@ -395,7 +380,7 @@ internal sealed class Window
|
|||||||
var childProcessName = WindowProcess.GetProcessNameFromProcessID(childProcessId);
|
var childProcessName = WindowProcess.GetProcessNameFromProcessID(childProcessId);
|
||||||
|
|
||||||
// Update process info in cache
|
// Update process info in cache
|
||||||
_handlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
|
HandlesToProcessCache[hWindow].UpdateProcessInfo(childProcessId, childThreadId, childProcessName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -407,7 +392,7 @@ internal sealed class Window
|
|||||||
}).Start();
|
}).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _handlesToProcessCache[hWindow];
|
return HandlesToProcessCache[hWindow];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
|
|||||||
|
|
||||||
public class SettingsManager : JsonSettingsManager, ISettingsInterface
|
public class SettingsManager : JsonSettingsManager, ISettingsInterface
|
||||||
{
|
{
|
||||||
private static readonly string _namespace = "windowWalker";
|
private const string Namespace = "windowWalker";
|
||||||
|
|
||||||
private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}";
|
private static string Namespaced(string propertyName) => $"{Namespace}.{propertyName}";
|
||||||
|
|
||||||
private static SettingsManager? instance;
|
private static SettingsManager? _instance;
|
||||||
|
|
||||||
private readonly ToggleSetting _resultsFromVisibleDesktopOnly = new(
|
private readonly ToggleSetting _resultsFromVisibleDesktopOnly = new(
|
||||||
Namespaced(nameof(ResultsFromVisibleDesktopOnly)),
|
Namespaced(nameof(ResultsFromVisibleDesktopOnly)),
|
||||||
@@ -130,8 +130,8 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
instance ??= new SettingsManager();
|
_instance ??= new SettingsManager();
|
||||||
return instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,11 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Pages;
|
|||||||
|
|
||||||
internal sealed partial class WindowWalkerListItem : ListItem
|
internal sealed partial class WindowWalkerListItem : ListItem
|
||||||
{
|
{
|
||||||
private readonly Window? _window;
|
public Window? Window { get; }
|
||||||
|
|
||||||
public Window? Window => _window;
|
|
||||||
|
|
||||||
public WindowWalkerListItem(Window? window)
|
public WindowWalkerListItem(Window? window)
|
||||||
: base(new SwitchToWindowCommand(window))
|
: base(new SwitchToWindowCommand(window))
|
||||||
{
|
{
|
||||||
_window = window;
|
Window = window;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user