diff --git a/PowerToys.sln b/PowerToys.sln index 70785dc18669..a1fb9fc4cf66 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -6,7 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}" ProjectSection(ProjectDependencies) = postProject {48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227} + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} {74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3} + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} = {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} {07C389E3-6BC8-41CF-923E-307B1265FA2D} = {07C389E3-6BC8-41CF-923E-307B1265FA2D} EndProjectSection @@ -86,6 +88,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUWPUI", "src\mod {0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windowwalker", "windowwalker", "{8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Window Walker", "src\modules\windowwalker\app\Window Walker\Window Walker.csproj", "{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowWalker", "src\modules\windowwalker\dll\WindowWalker.vcxproj", "{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -160,6 +168,14 @@ Global {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.Build.0 = Debug|x64 {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.ActiveCfg = Release|x64 {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.Build.0 = Release|x64 + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.ActiveCfg = Debug|x64 + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.Build.0 = Debug|x64 + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.ActiveCfg = Release|x64 + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.Build.0 = Release|x64 + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.ActiveCfg = Debug|x64 + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.Build.0 = Debug|x64 + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.ActiveCfg = Release|x64 + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,6 +199,9 @@ Global {2151F984-E006-4A9F-92EF-C6DDE3DC8413} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {64A80062-4D8B-4229-8A38-DFA1D7497749} = {BEEAB7F2-FFF6-45AB-9CDB-B04CC0734B88} {0485F45C-EA7A-4BB5-804B-3E8D14699387} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} + {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} + {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/modules/windowwalker/app/Window Walker.sln b/src/modules/windowwalker/app/Window Walker.sln new file mode 100644 index 000000000000..8dd54bcba847 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Window Walker", "Window Walker\Window Walker.csproj", "{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}" +EndProject +Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/modules/windowwalker/app/Window Walker/App.config b/src/modules/windowwalker/app/Window Walker/App.config new file mode 100644 index 000000000000..de277144ee45 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/modules/windowwalker/app/Window Walker/App.xaml b/src/modules/windowwalker/app/Window Walker/App.xaml new file mode 100644 index 000000000000..f238d2ecf0bc --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/App.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/modules/windowwalker/app/Window Walker/App.xaml.cs b/src/modules/windowwalker/app/Window Walker/App.xaml.cs new file mode 100644 index 000000000000..787aa56e74dd --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/App.xaml.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Windows; + +namespace WindowWalker +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/ApplicationUpdates.cs b/src/modules/windowwalker/app/Window Walker/Components/ApplicationUpdates.cs new file mode 100644 index 000000000000..cbe0549e29df --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/ApplicationUpdates.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Deployment.Application; + +namespace WindowWalker.Components +{ + public class ApplicationUpdates + { + private static DateTime _lastUpdateCheck = DateTime.Now; + private static bool alreadyCheckingForUpdate = false; + + private static bool updateAvailable = false; + + public static void InstallUpdateSyncWithInfo() + { + if (alreadyCheckingForUpdate) + { + return; + } + else + { + alreadyCheckingForUpdate = true; + } + + var daysSinceLastUpdate = (DateTime.Now - _lastUpdateCheck).Days; + + if (ApplicationDeployment.IsNetworkDeployed) + { + if (updateAvailable) + { + UpdateCheckInfo info = null; + ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment; + + try + { + info = ad.CheckForDetailedUpdate(); + } + catch + { + return; + } + finally + { + _lastUpdateCheck = DateTime.Now; + } + + if (info.UpdateAvailable || true) + { + try + { + ad.Update(); + System.Windows.Application.Current.Shutdown(); + System.Windows.Forms.Application.Restart(); + } + catch + { + return; + } + } + } + else + { + ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment; + + ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(CheckForUpdateCompleted); + ad.CheckForUpdateAsync(); + + _lastUpdateCheck = DateTime.Now; + } + } + } + + private static void CheckForUpdateCompleted(object sender, CheckForUpdateCompletedEventArgs e) + { + if (e.Error != null || !e.UpdateAvailable) + { + alreadyCheckingForUpdate = false; + return; + } + else + { + updateAvailable = true; + alreadyCheckingForUpdate = false; + } + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/Command.cs b/src/modules/windowwalker/app/Window Walker/Components/Command.cs new file mode 100644 index 000000000000..6d368b95c33c --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/Command.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +namespace WindowWalker.Components +{ + /// + /// Command class representing a single command + /// + public class Command + { + /// + /// Gets or sets the set of substrings to search for in the search text to figure out if the user wants this command + /// + public string[] SearchTexts { get; set; } + + /// + /// Gets or sets the help tip to get displayed in the cycling display + /// + public string Tip { get; set; } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/Commands.cs b/src/modules/windowwalker/app/Window Walker/Components/Commands.cs new file mode 100644 index 000000000000..3c44dee77158 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/Commands.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace WindowWalker.Components +{ + /// + /// A class to handle the commands entered by the user, different + /// form the user being able to search through their windows + /// + internal class Commands + { + /// + /// Initializes static members of the class. + /// Constructor primarily used to enforce the creation of tips + /// and populate the enabled commands list + /// + static Commands() + { + _enabledCommands = new Dictionary + { + { + "quit", + new Command() + { + SearchTexts = new string[] + { + ":quit", + ":q", + }, + Tip = "type \":quit\" to exit", + } + }, + { + "launchTerminal", + new Command() + { + SearchTexts = new string[] + { + ":lterminal", + ":lcmd", + ":lterm", + ":lt", + }, + Tip = "type \":lt\" or \":lcmd\"to launch a new terminal window", + } + }, + { + "launchVSCode", + new Command() + { + SearchTexts = new string[] + { + ":lvscode", + ":lcode", + }, + Tip = "type \":lvscode\" or \":lcode\"to launch a new instance of VSCode", + } + }, + }; + } + + /// + /// Dictionary containing all the enabled commands + /// + private static readonly Dictionary _enabledCommands; + + /// + /// Primary method which executes on the commands that are passed to it + /// + /// The search text the user has entered + public static void ProcessCommand(string commandText) + { + LivePreview.DeactivateLivePreview(); + + if (_enabledCommands["quit"].SearchTexts.Contains(commandText)) + { + System.Windows.Application.Current.Shutdown(); + } + else if (_enabledCommands["launchTerminal"].SearchTexts.Contains(commandText)) + { + Process.Start(new ProcessStartInfo("cmd.exe") + { WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) }); + } + else if (_enabledCommands["launchVSCode"].SearchTexts.Contains(commandText)) + { + Process.Start("code"); + } + } + + /// + /// Gets the tips for all the enabled commands + /// + public static IEnumerable GetTips() + { + return _enabledCommands.Select(x => x.Value.Tip); + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatching.cs b/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatching.cs new file mode 100644 index 000000000000..78cf1048757a --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatching.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Collections.Generic; +using System.Linq; + +namespace WindowWalker.Components +{ + /// + /// Class housing fuzzy matching methods + /// + public class FuzzyMatching + { + /// + /// Finds the best match (the one with the most + /// number of letters adjecent to each other) and + /// returns the index location of each of the letters + /// of the matches + /// + /// The text to search inside of + /// the text to search for + /// returns the index location of each of the letters of the matches + public static List FindBestFuzzyMatch(string text, string searchText) + { + searchText = searchText.ToLower(); + text = text.ToLower(); + + // Create a grid to march matches like + // eg. + // a b c a d e c f g + // a x x + // c x x + bool[,] matches = new bool[text.Length, searchText.Length]; + for (int firstIndex = 0; firstIndex < text.Length; firstIndex++) + { + for (int secondIndex = 0; secondIndex < searchText.Length; secondIndex++) + { + matches[firstIndex, secondIndex] = + searchText[secondIndex] == text[firstIndex] ? + true : + false; + } + } + + // use this table to get all the possible matches + List> allMatches = GetAllMatchIndexes(matches); + + // return the score that is the max + int maxScore = allMatches.Count > 0 ? CalculateScoreForMatches(allMatches[0]) : 0; + List bestMatch = allMatches.Count > 0 ? allMatches[0] : new List(); + + foreach (var match in allMatches) + { + int score = CalculateScoreForMatches(match); + if (score > maxScore) + { + bestMatch = match; + maxScore = score; + } + } + + return bestMatch; + } + + /// + /// Gets all the possible matches to the search string with in the text + /// + /// a table showing the matches as generated by + /// a two dimentional array with the first dimention the text and the second + /// one the search string and each cell marked as an intersection between the two + /// a list of the possible combinations that match the search text + public static List> GetAllMatchIndexes(bool[,] matches) + { + List> results = new List>(); + + for (int secondIndex = 0; secondIndex < matches.GetLength(1); secondIndex++) + { + for (int firstIndex = 0; firstIndex < matches.GetLength(0); firstIndex++) + { + if (secondIndex == 0 && matches[firstIndex, secondIndex]) + { + results.Add(new List { firstIndex }); + } + else if (matches[firstIndex, secondIndex]) + { + var tempList = results.Where(x => x.Count == secondIndex && x[x.Count - 1] < firstIndex).Select(x => x.ToList()).ToList(); + + foreach (var pathSofar in tempList) + { + pathSofar.Add(firstIndex); + } + + results.AddRange(tempList); + } + } + + results = results.Where(x => x.Count == secondIndex + 1).ToList(); + } + + return results.Where(x => x.Count == matches.GetLength(1)).ToList(); + } + + /// + /// Calculates the score for a string + /// + /// the index of the matches + /// an integer representing the score + public static int CalculateScoreForMatches(List matches) + { + var score = 0; + + for (int currentIndex = 1; currentIndex < matches.Count; currentIndex++) + { + var previousIndex = currentIndex - 1; + + score -= matches[currentIndex] - matches[previousIndex]; + } + + return score == 0 ? -10000 : score; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatchingUnitTest.cs b/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatchingUnitTest.cs new file mode 100644 index 000000000000..d0664cace97d --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/FuzzyMatchingUnitTest.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace WindowWalker.Components +{ + [TestClass] + public class FuzzyMatchingUnitTest + { + [TestMethod] + public void SimpleMatching() + { + List result = FuzzyMatching.FindBestFuzzyMatch("watsapp hellow", "hello"); + List expected = new List() { 8, 9, 10, 11, 12 }; + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void NoResult() + { + List result = FuzzyMatching.FindBestFuzzyMatch("what is going on?", "whatsx goin on?"); + List expected = new List(); + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void ZeroLengthSearchString() + { + List result = FuzzyMatching.FindBestFuzzyMatch("whatsapp hellow", string.Empty); + List expected = new List(); + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void ZeroLengthText() + { + List result = FuzzyMatching.FindBestFuzzyMatch(string.Empty, "hello"); + List expected = new List(); + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void ZeroLengthInputs() + { + List result = FuzzyMatching.FindBestFuzzyMatch(string.Empty, string.Empty); + List expected = new List(); + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void BestMatch() + { + List result = FuzzyMatching.FindBestFuzzyMatch("aaacaab", "ab"); + List expected = new List() { 5, 6 }; + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void RealWorldProgramManager() + { + List result = FuzzyMatching.FindBestFuzzyMatch("Program Manager", "pr"); + List expected = new List() { 0, 1 }; + + Assert.IsTrue(IsEqual(expected, result)); + } + + [TestMethod] + public void BestScoreTest() + { + int score = FuzzyMatching.CalculateScoreForMatches(new List() { 1, 2, 3, 4 }); + Assert.IsTrue(score == -3); + } + + private static bool IsEqual(List list1, List list2) + { + if (list1.Count != list2.Count) + { + return false; + } + + for (int i = 0; i < list1.Count; i++) + { + if (list1[i] != list2[i]) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/InteropAndHelpers.cs b/src/modules/windowwalker/app/Window Walker/Components/InteropAndHelpers.cs new file mode 100644 index 000000000000..3ebe543fb1d9 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/InteropAndHelpers.cs @@ -0,0 +1,652 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace WindowWalker.Components +{ + /// + /// Interop calls with helper layers + /// + internal class InteropAndHelpers + { + public delegate bool CallBackPtr(IntPtr hwnd, IntPtr lParam); + + /// + /// Some flags for interop calls to SetWindowPosition + /// + [Flags] + public enum SetWindowPosFlags : uint + { + /// + /// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request. + /// + SWP_ASYNCWINDOWPOS = 0x4000, + + /// + /// Prevents generation of the WM_SYNCPAINT message. + /// + SWP_DEFERERASE = 0x2000, + + /// + /// Draws a frame (defined in the window's class description) around the window. + /// + SWP_DRAWFRAME = 0x0020, + + /// + /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed. + /// + SWP_FRAMECHANGED = 0x0020, + + /// + /// Hides the window. + /// + SWP_HIDEWINDOW = 0x0080, + + /// + /// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter). + /// + SWP_NOACTIVATE = 0x0010, + + /// + /// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned. + /// + SWP_NOCOPYBITS = 0x0100, + + /// + /// Retains the current position (ignores X and Y parameters). + /// + SWP_NOMOVE = 0x0002, + + /// + /// Does not change the owner window's position in the Z order. + /// + SWP_NOOWNERZORDER = 0x0200, + + /// + /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing. + /// + SWP_NOREDRAW = 0x0008, + + /// + /// Same as the SWP_NOOWNERZORDER flag. + /// + SWP_NOREPOSITION = 0x0200, + + /// + /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message. + /// + SWP_NOSENDCHANGING = 0x0400, + + /// + /// Retains the current size (ignores the cx and cy parameters). + /// + SWP_NOSIZE = 0x0001, + + /// + /// Retains the current Z order (ignores the hWndInsertAfter parameter). + /// + SWP_NOZORDER = 0x0004, + + /// + /// Displays the window. + /// + SWP_SHOWWINDOW = 0x0040, + } + + /// + /// Flags for setting hotkeys + /// + [Flags] + public enum Modifiers + { + NoMod = 0x0000, + Alt = 0x0001, + Ctrl = 0x0002, + Shift = 0x0004, + Win = 0x0008, + } + + /// + /// Options for DwmpActivateLivePreview + /// + public enum LivePreviewTrigger + { + /// + /// Show Desktop button + /// + ShowDesktop = 1, + + /// + /// WIN+SPACE hotkey + /// + WinSpace, + + /// + /// Hover-over Superbar thumbnails + /// + Superbar, + + /// + /// Alt-Tab + /// + AltTab, + + /// + /// Press and hold on Superbar thumbnails + /// + SuperbarTouch, + + /// + /// Press and hold on Show desktop + /// + ShowDesktopTouch, + } + + /// + /// Show Window Enums + /// + public enum ShowWindowCommands + { + /// + /// Hides the window and activates another window. + /// + Hide = 0, + + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + + /// + /// Maximizes the specified window. + /// + Maximize = 3, // is this the right value? + + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not activated. + /// + ShowNoActivate = 4, + + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11, + } + + /// + /// The rendering policy to use for set window attribute + /// + [Flags] + public enum DwmNCRenderingPolicy + { + UseWindowStyle, + Disabled, + Enabled, + Last, + } + + /// + /// Window attribute + /// + [Flags] + public enum DwmWindowAttribute + { + NCRenderingEnabled = 1, + NCRenderingPolicy, + TransitionsForceDisabled, + AllowNCPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExcludedFromPeek, + Last, + } + + /// + /// Flags for accessing the process in trying to get icon for the process + /// + [Flags] + public enum ProcessAccessFlags + { + /// + /// Required to create a thread. + /// + CreateThread = 0x0002, + + /// + /// + /// + SetSessionId = 0x0004, + + /// + /// Required to perform an operation on the address space of a process + /// + VmOperation = 0x0008, + + /// + /// Required to read memory in a process using ReadProcessMemory. + /// + VmRead = 0x0010, + + /// + /// Required to write to memory in a process using WriteProcessMemory. + /// + VmWrite = 0x0020, + + /// + /// Required to duplicate a handle using DuplicateHandle. + /// + DupHandle = 0x0040, + + /// + /// Required to create a process. + /// + CreateProcess = 0x0080, + + /// + /// Required to set memory limits using SetProcessWorkingSetSize. + /// + SetQuota = 0x0100, + + /// + /// Required to set certain information about a process, such as its priority class (see SetPriorityClass). + /// + SetInformation = 0x0200, + + /// + /// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken). + /// + QueryInformation = 0x0400, + + /// + /// Required to suspend or resume a process. + /// + SuspendResume = 0x0800, + + /// + /// Required to retrieve certain information about a process (see GetExitCodeProcess, GetPriorityClass, IsProcessInJob, QueryFullProcessImageName). + /// A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. + /// + QueryLimitedInformation = 0x1000, + + /// + /// Required to wait for the process to terminate using the wait functions. + /// + Synchronize = 0x100000, + + /// + /// Required to delete the object. + /// + Delete = 0x00010000, + + /// + /// Required to read information in the security descriptor for the object, not including the information in the SACL. + /// To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right. + /// + ReadControl = 0x00020000, + + /// + /// Required to modify the DACL in the security descriptor for the object. + /// + WriteDac = 0x00040000, + + /// + /// Required to change the owner in the security descriptor for the object. + /// + WriteOwner = 0x00080000, + + StandardRightsRequired = 0x000F0000, + + /// + /// All possible access rights for a process object. + /// + AllAccess = StandardRightsRequired | Synchronize | 0xFFFF, + } + + /// + /// Contains information about the placement of a window on the screen. + /// + [Serializable] + [StructLayout(LayoutKind.Sequential)] + internal struct WINDOWPLACEMENT + { + /// + /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT). + /// + /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly. + /// + /// + public int Length; + + /// + /// Specifies flags that control the position of the minimized window and the method by which the window is restored. + /// + public int Flags; + + /// + /// The current show state of the window. + /// + public ShowWindowCommands ShowCmd; + + /// + /// The coordinates of the window's upper-left corner when the window is minimized. + /// + public POINT MinPosition; + + /// + /// The coordinates of the window's upper-left corner when the window is maximized. + /// + public POINT MaxPosition; + + /// + /// The window's coordinates when the window is in the restored position. + /// + public RECT NormalPosition; + + /// + /// Gets the default (empty) value. + /// + public static WINDOWPLACEMENT Default + { + get + { + WINDOWPLACEMENT result = default; + result.Length = Marshal.SizeOf(result); + return result; + } + } + } + + /// + /// Required pointless variables that we don't use in making a windows show + /// + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) + : this(r.Left, r.Top, r.Right, r.Bottom) + { + } + + public int X + { + get + { + return Left; + } + + set + { + Right -= Left - value; + Left = value; + } + } + + public int Y + { + get + { + return Top; + } + + set + { + Bottom -= Top - value; + Top = value; + } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get + { + return new System.Drawing.Point(Left, Top); + } + + set + { + X = value.X; + Y = value.Y; + } + } + + public System.Drawing.Size Size + { + get + { + return new System.Drawing.Size(Width, Height); + } + + set + { + Width = value.Width; + Height = value.Height; + } + } + + public static implicit operator System.Drawing.Rectangle(RECT r) + { + return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + } + + public static implicit operator RECT(System.Drawing.Rectangle r) + { + return new RECT(r); + } + + public static bool operator ==(RECT r1, RECT r2) + { + return r1.Equals(r2); + } + + public static bool operator !=(RECT r1, RECT r2) + { + return !r1.Equals(r2); + } + + public bool Equals(RECT r) + { + return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + } + + public override bool Equals(object obj) + { + if (obj is RECT) + { + return Equals((RECT)obj); + } + else if (obj is System.Drawing.Rectangle) + { + return Equals(new RECT((System.Drawing.Rectangle)obj)); + } + + return false; + } + + public override int GetHashCode() + { + return ((System.Drawing.Rectangle)this).GetHashCode(); + } + + public override string ToString() + { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom); + } + } + + /// + /// Same as the RECT struct above + /// + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int X; + public int Y; + + public POINT(int x, int y) + { + X = x; + Y = y; + } + + public POINT(System.Drawing.Point pt) + : this(pt.X, pt.Y) + { + } + + public static implicit operator System.Drawing.Point(POINT p) + { + return new System.Drawing.Point(p.X, p.Y); + } + + public static implicit operator POINT(System.Drawing.Point p) + { + return new POINT(p.X, p.Y); + } + } + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern int EnumWindows(CallBackPtr callPtr, int lPar); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern int GetWindowTextLength(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk); + + [DllImport("user32.dll")] + public static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow); + + [DllImport("user32.dll")] + public static extern bool FlashWindow(IntPtr hwnd, bool bInvert); + + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("psapi.dll")] + public static extern uint GetProcessImageFileName(IntPtr hProcess, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] int nSize); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); + + [DllImport("dwmapi.dll", EntryPoint = "#113", CallingConvention = CallingConvention.StdCall)] + public static extern int DwmpActivateLivePreview([MarshalAs(UnmanagedType.Bool)]bool fActivate, IntPtr hWndExclude, IntPtr hWndInsertBefore, LivePreviewTrigger lpt, IntPtr prcFinalRect); + + [DllImport("dwmapi.dll", PreserveSig = false)] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + [DllImport("user32.dll")] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/LivePreview.cs b/src/modules/windowwalker/app/Window Walker/Components/LivePreview.cs new file mode 100644 index 000000000000..08f9feefc6f2 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/LivePreview.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; + +namespace WindowWalker.Components +{ + /// + /// Class containg methods to control the live preview + /// + internal class LivePreview + { + /// + /// Makes sure that a window is excluded from the live preview + /// + /// handle to the window to exclude + public static void SetWindowExlusionFromLivePreview(IntPtr hwnd) + { + int renderPolicy = (int)InteropAndHelpers.DwmNCRenderingPolicy.Enabled; + + InteropAndHelpers.DwmSetWindowAttribute( + hwnd, + 12, + ref renderPolicy, + sizeof(int)); + } + + /// + /// Activates the live preview + /// + /// the window to show by making all other windows transparent + /// the window which should not be transparent but is not the target window + public static void ActivateLivePreview(IntPtr targetWindow, IntPtr windowToSpare) + { + InteropAndHelpers.DwmpActivateLivePreview( + true, + targetWindow, + windowToSpare, + InteropAndHelpers.LivePreviewTrigger.Superbar, + IntPtr.Zero); + } + + /// + /// Deactivates the live preview + /// + public static void DeactivateLivePreview() + { + InteropAndHelpers.DwmpActivateLivePreview( + false, + IntPtr.Zero, + IntPtr.Zero, + InteropAndHelpers.LivePreviewTrigger.AltTab, + IntPtr.Zero); + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/OpenWindows.cs b/src/modules/windowwalker/app/Window Walker/Components/OpenWindows.cs new file mode 100644 index 000000000000..968089401b83 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/OpenWindows.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WindowWalker.Components +{ + /// + /// Class that represents the state of the desktops windows + /// + internal class OpenWindows + { + /// + /// Delegate handler for open windows updates + /// + public delegate void OpenWindowsUpdateHandler(object sender, SearchController.SearchResultUpdateEventArgs e); + + /// + /// Event raised when there is an update to the list of open windows + /// + public event OpenWindowsUpdateHandler OnOpenWindowsUpdate; + + /// + /// List of all the open windows + /// + private readonly List windows = new List(); + + /// + /// An instance of the class OpenWindows + /// + private static OpenWindows instance; + + /// + /// Gets the list of all open windows + /// + public List Windows + { + get { return new List(windows); } + } + + /// + /// Gets an instance property of this class that makes sure that + /// the first instance gets created and that all the requests + /// end up at that one instance + /// + public static OpenWindows Instance + { + get + { + if (instance == null) + { + instance = new OpenWindows(); + } + + return instance; + } + } + + /// + /// Initializes a new instance of the class. + /// Private constructor to make sure there is never + /// more than one instance of this class + /// + private OpenWindows() + { + } + + /// + /// Updates the list of open windows + /// + public void UpdateOpenWindowsList() + { + windows.Clear(); + + new Task(() => + { + InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr(WindowEnumerationCallBack); + InteropAndHelpers.EnumWindows(callbackptr, 0); + }).Start(); + } + + /// + /// Call back method for window enumeration + /// + /// The handle to the current window being enumerated + /// Value being passed from the caller (we don't use this but might come in handy + /// in the future + /// true to make sure to contiue enumeration + public bool WindowEnumerationCallBack(IntPtr hwnd, IntPtr lParam) + { + Window newWindow = new Window(hwnd); + + if (windows.Select(x => x.Title).Contains(newWindow.Title)) + { + if (newWindow.ProcessName.ToLower().Equals("applicationframehost.exe")) + { + windows.Remove(windows.Where(x => x.Title == newWindow.Title).First()); + } + + return true; + } + + if ((newWindow.Visible && !newWindow.ProcessName.ToLower().Equals("iexplore.exe")) || + (newWindow.ProcessName.ToLower().Equals("iexplore.exe") && newWindow.ClassName == "TabThumbnailWindow")) + { + windows.Add(newWindow); + + OnOpenWindowsUpdate?.Invoke(this, new SearchController.SearchResultUpdateEventArgs()); + } + + return true; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/Point.cs b/src/modules/windowwalker/app/Window Walker/Components/Point.cs new file mode 100644 index 000000000000..c91de3f5537e --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/Point.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +namespace WindowWalker.Components +{ + /// + /// Custom point class to ease storing a point + /// + public class Point + { + public double X { get; set; } + + public double Y { get; set; } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/SearchController.cs b/src/modules/windowwalker/app/Window Walker/Components/SearchController.cs new file mode 100644 index 000000000000..1764071cea2f --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/SearchController.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WindowWalker.Components +{ + /// + /// Responsible for searching and finding matches for the strings provided. + /// Essentially the UI independent model of the application + /// + internal class SearchController + { + /// + /// the current search text + /// + private string searchText; + + /// + /// Open window search results + /// searchMatches; + + /// + /// Singleton pattern + /// + private static SearchController instance; + + /// + /// Delegate handler for open windows updates + /// + public delegate void SearchResultUpdateHandler(object sender, SearchResultUpdateEventArgs e); + + /// + /// Event raised when there is an update to the list of open windows + /// + public event SearchResultUpdateHandler OnSearchResultUpdate; + + /// + /// Gets or sets the current search text + /// + public string SearchText + { + get + { + return searchText; + } + + set + { + searchText = value.ToLower().Trim(); + SearchTextUpdated(); + } + } + + /// + /// Gets the open window search results + /// + public List SearchMatches + { + get { return new List(searchMatches).OrderByDescending(x => x.Score).ToList(); } + } + + /// + /// Gets singleton Pattern + /// + public static SearchController Instance + { + get + { + if (instance == null) + { + instance = new SearchController(); + } + + return instance; + } + } + + /// + /// Initializes a new instance of the class. + /// Initializes the search controller object + /// + private SearchController() + { + searchText = string.Empty; + OpenWindows.Instance.OnOpenWindowsUpdate += OpenWindowsUpdateHandler; + } + + /// + /// Event handler for when the search text has been updated + /// + public void SearchTextUpdated() + { + SyncOpenWindowsWithModelAsync(); + } + + /// + /// Event handler called when the OpenWindows list changes + /// + /// + /// + public void OpenWindowsUpdateHandler(object sender, SearchResultUpdateEventArgs e) + { + SyncOpenWindowsWithModelAsync(); + } + + /// + /// Syncs the open windows with the OpenWindows Model + /// + private async void SyncOpenWindowsWithModelAsync() + { + System.Diagnostics.Debug.Print("Syncing WindowSearch result with OpenWindows Model"); + + List snapshotOfOpenWindows = OpenWindows.Instance.Windows; + + if (SearchText == string.Empty) + { + searchMatches = new List(); + } + else + { + searchMatches = await FuzzySearchOpenWindowsAsync(snapshotOfOpenWindows); + } + + OnSearchResultUpdate?.Invoke(this, new SearchResultUpdateEventArgs()); + } + + /// + /// Redirecting method for Fuzzy searching + /// + /// + /// Returns search results + private Task> FuzzySearchOpenWindowsAsync(List openWindows) + { + return Task.Run( + () => + FuzzySearchOpenWindows(openWindows)); + } + + /// + /// Search method that matches the title of windows with the user search text + /// + /// + /// Returns search results + private List FuzzySearchOpenWindows(List openWindows) + { + List result = new List(); + List searchStrings = new List(); + + List shortcuts = SettingsManager.Instance.GetShortcut(SearchText); + + foreach (var shortcut in shortcuts) + { + searchStrings.Add(new SearchString(shortcut, SearchResult.SearchType.Shortcut)); + } + + searchStrings.Add(new SearchString(searchText, SearchResult.SearchType.Fuzzy)); + + foreach (var searchString in searchStrings) + { + foreach (var window in openWindows) + { + var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchString.SearchText); + var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.ProcessName, searchString.SearchText); + + if ((titleMatch.Count != 0 || processMatch.Count != 0) && + window.Title.Length != 0) + { + var temp = new SearchResult(window, titleMatch, processMatch, searchString.SearchType); + result.Add(temp); + } + } + } + + System.Diagnostics.Debug.Print("Found " + result.Count + " windows that match the search text"); + + return result; + } + + /// + /// Event args for a window list update event + /// + public class SearchResultUpdateEventArgs : EventArgs + { + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/SearchResult.cs b/src/modules/windowwalker/app/Window Walker/Components/SearchResult.cs new file mode 100644 index 000000000000..2dbeb4db58e6 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/SearchResult.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Collections.Generic; + +namespace WindowWalker.Components +{ + /// + /// Contains search result windows with each window including the reason why the result was included + /// + public class SearchResult + { + /// + /// Gets the actual window reference for the search result + /// + public Window Result + { + get; + private set; + } + + /// + /// Gets the list of indexes of the matching characters for the search in the title window + /// + public List SearchMatchesInTitle + { + get; + private set; + } + + /// + /// Gets the list of indexes of the matching characters for the search in the + /// name of the process + /// + public List SearchMatchesInProcessName + { + get; + private set; + } + + /// + /// Gets the type of match (shortcut, fuzzy or nothing) + /// + public SearchType SearchResultMatchType + { + get; + private set; + } + + /// + /// Gets a score indicating how well this matches what we are looking for + /// + public int Score + { + get; + private set; + } + + /// + /// Gets the source of where the best score was found + /// + public TextType BestScoreSource + { + get; + private set; + } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + public SearchResult(Window window, List matchesInTitle, List matchesInProcessName, SearchType matchType) + { + Result = window; + SearchMatchesInTitle = matchesInTitle; + SearchMatchesInProcessName = matchesInProcessName; + SearchResultMatchType = matchType; + CalculateScore(); + } + + /// + /// Calculates the score for how closely this window matches the search string + /// + /// + /// Higher Score is better + /// + private void CalculateScore() + { + if (FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName) > + FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle)) + { + Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInProcessName); + BestScoreSource = TextType.ProcessName; + } + else + { + Score = FuzzyMatching.CalculateScoreForMatches(SearchMatchesInTitle); + BestScoreSource = TextType.WindowTitle; + } + } + + /// + /// The type of text that a string represents + /// + public enum TextType + { + ProcessName, + WindowTitle, + } + + /// + /// The type of search + /// + public enum SearchType + { + /// + /// the search string is empty, which means all open windows are + /// going to be returned + /// + Empty, + + /// + /// Regular fuzzy match search + /// + Fuzzy, + + /// + /// The user has entered text that has been matched to a shortcut + /// and the shortcut is now being searched + /// + Shortcut, + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/SearchString.cs b/src/modules/windowwalker/app/Window Walker/Components/SearchString.cs new file mode 100644 index 000000000000..125bdc97da1a --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/SearchString.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +namespace WindowWalker.Components +{ + /// + /// A class to represent a search string + /// + /// Class was added inorder to be able to attach various context data to + /// a search string + internal class SearchString + { + /// + /// Gets where is the search string coming from (is it a shortcut + /// or direct string, etc...) + /// + public SearchResult.SearchType SearchType + { + get; + private set; + } + + /// + /// Gets the actual text we are searching for + /// + public string SearchText + { + get; + private set; + } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// + /// + public SearchString(string searchText, SearchResult.SearchType searchType) + { + SearchText = searchText; + SearchType = searchType; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/Settings.cs b/src/modules/windowwalker/app/Window Walker/Components/Settings.cs new file mode 100644 index 000000000000..ff4f24272c19 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/Settings.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Collections.Generic; +using System.Web.Script.Serialization; + +namespace WindowWalker.Components +{ + /// + /// Class that represents all the settings and + /// can be serialized into JSON for easy saving + /// + internal class Settings + { + /// + /// Gets or sets the version of the settings file + /// + public string Version { get; set; } + + /// + /// Gets or sets a list of all the shortcuts + /// + public Dictionary> Shortcuts { get; set; } + + /// + /// Gets or sets a list of saved window locations catagorized by number of screens + /// + public Dictionary WindowLocations { get; set; } + + /// + /// Gets or sets the location of the search windows (the top left point) + /// + [ScriptIgnore] + public Point WindowLocation + { + get + { + if (WindowLocations.ContainsKey(System.Windows.Forms.Screen.AllScreens.Length.ToString())) + { + return WindowLocations[System.Windows.Forms.Screen.AllScreens.Length.ToString()]; + } + else + { + return new Point() { X = 0, Y = 0 }; + } + } + + set + { + if (WindowLocations == null) + { + WindowLocations = new Dictionary(); + } + + WindowLocations[System.Windows.Forms.Screen.AllScreens.Length.ToString()] = value; + } + } + + /// + /// Initializes a new instance of the class. + /// Constructer to initialize some default values + /// + public Settings() + { + Version = string.Empty; + Shortcuts = new Dictionary>(); + WindowLocation = new Point() { X = 0, Y = 0 }; + WindowLocations = new Dictionary(); + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/SettingsManager.cs b/src/modules/windowwalker/app/Window Walker/Components/SettingsManager.cs new file mode 100644 index 000000000000..3f88d5e31b68 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/SettingsManager.cs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Collections.Generic; +using System.IO; +using System.Web.Script.Serialization; + +namespace WindowWalker.Components +{ + /// + /// Class for managing shortcuts + /// Example: When you type "i" we actually search for "internet" + /// + internal class SettingsManager + { + /// + /// The path to the shortcut file + /// + private static readonly string _shortcutsFile = Path.GetTempPath() + "WindowWalkerShortcuts.ini"; + + /// + /// Reference to a serializer for saving the settings + /// + private static readonly JavaScriptSerializer _serializer = new JavaScriptSerializer(); + + /// + /// An instance of the settings class representing the current settings + /// + private static readonly Settings _settingsInstance = new Settings(); + + /// + /// Instance of the manager itself + /// + private static SettingsManager _instance; + + /// + /// Gets implements Singlton pattern + /// + public static SettingsManager Instance + { + get + { + if (_instance == null) + { + _instance = new SettingsManager(); + } + + return _instance; + } + } + + /// + /// Initializes static members of the class. + /// Static constructor + /// + /// Not sure why we have this AND a singlton pattern + static SettingsManager() + { + try + { + if (File.Exists(_shortcutsFile)) + { + using (StreamReader reader = new StreamReader(_shortcutsFile)) + { + string jsonString = reader.ReadToEnd(); + _settingsInstance = (Settings)_serializer.Deserialize(jsonString, typeof(Settings)); + } + } + } + catch + { + } + } + + /// + /// Initializes a new instance of the class. + /// Contructor that does nothing? + /// + private SettingsManager() + { + return; + } + + /// + /// Adds a shortcut to the settings + /// + /// what the user types + /// what the resulting search string is going to be + /// Returns true if it succeeds, false otherwise + /// Proably not usefull to actually do the true/false return since + /// we can now have multiple shortcuts + public bool AddShortcut(string before, string after) + { + if (!_settingsInstance.Shortcuts.ContainsKey(before)) + { + _settingsInstance.Shortcuts.Add(before, new List()); + } + + _settingsInstance.Shortcuts[before].Add(after); + + // Write the updated shortcuts list to a file + SaveSettings(); + + return true; + } + + /// + /// Removes a shortcut + /// + /// the input shortcut string + /// true if it succeeds, false otherwise + /// Probably has a bug since you can now a single input + /// mapping to multiple outputs + public bool RemoveShortcut(string input) + { + if (!_settingsInstance.Shortcuts.ContainsKey(input)) + { + return false; + } + + _settingsInstance.Shortcuts.Remove(input); + + // Write the updated shortcuts list to a file + SaveSettings(); + + return true; + } + + /// + /// Retrieves a shortcut and returns all possible mappings + /// + /// the input string for the shortcuts + /// A list of all the shortcut strings that result from the user input + public List GetShortcut(string input) + { + return _settingsInstance.Shortcuts.ContainsKey(input) ? _settingsInstance.Shortcuts[input] : new List(); + } + + /// + /// Writes the current shortcuts to the shortcuts file. + /// Note: We are writing the file even if there are no shortcuts. This handles + /// the case where the user deletes their last shortcut. + /// + public void SaveSettings() + { + using (StreamWriter writer = new StreamWriter(_shortcutsFile, false)) + { + writer.Write(_serializer.Serialize(_settingsInstance)); + } + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/Window.cs b/src/modules/windowwalker/app/Window Walker/Components/Window.cs new file mode 100644 index 000000000000..ea7f73c53ed8 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/Window.cs @@ -0,0 +1,265 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace WindowWalker.Components +{ + /// + /// Represents a specific open window + /// + public class Window + { + /// + /// Maximum size of a file name + /// + private const int MaximumFileNameLength = 1000; + + /// + /// The list of owners of a window so that we don't have to + /// constantly query for the process owning a specific window + /// + private static readonly Dictionary _handlesToProcessCache = new Dictionary(); + + /// + /// The list of icons from process so that we don't have to keep + /// loading them from disk + /// + private static readonly Dictionary _processIdsToIconsCache = new Dictionary(); + + /// + /// The handle to the window + /// + private readonly IntPtr hwnd; + + /// + /// Gets the title of the window (the string displayed at the top of the window) + /// + public string Title + { + get + { + int sizeOfTitle = InteropAndHelpers.GetWindowTextLength(hwnd); + if (sizeOfTitle++ > 0) + { + StringBuilder titleBuffer = new StringBuilder(sizeOfTitle); + InteropAndHelpers.GetWindowText(hwnd, titleBuffer, sizeOfTitle); + return titleBuffer.ToString(); + } + else + { + return string.Empty; + } + } + } + + /// + /// Gets the handle to the window + /// + public IntPtr Hwnd + { + get { return hwnd; } + } + + /// + /// Gets returns the name of the process + /// + public string ProcessName + { + get + { + lock (_handlesToProcessCache) + { + if (_handlesToProcessCache.Count > 7000) + { + Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count); + _handlesToProcessCache.Clear(); + } + + if (!_handlesToProcessCache.ContainsKey(Hwnd)) + { + InteropAndHelpers.GetWindowThreadProcessId(Hwnd, out uint processId); + IntPtr processHandle = InteropAndHelpers.OpenProcess(InteropAndHelpers.ProcessAccessFlags.AllAccess, true, (int)processId); + StringBuilder processName = new StringBuilder(MaximumFileNameLength); + + if (InteropAndHelpers.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0) + { + _handlesToProcessCache.Add( + Hwnd, + processName.ToString().Split('\\').Reverse().ToArray()[0]); + } + else + { + _handlesToProcessCache.Add(Hwnd, string.Empty); + } + } + + return _handlesToProcessCache[hwnd]; + } + } + } + + /// + /// Gets returns the name of the class for the window represented + /// + public string ClassName + { + get + { + StringBuilder windowClassName = new StringBuilder(300); + InteropAndHelpers.GetClassName(Hwnd, windowClassName, windowClassName.MaxCapacity); + + return windowClassName.ToString(); + } + } + + /// + /// Gets represents the Window Icon for the specified window + /// + public ImageSource WindowIcon + { + get + { + lock (_processIdsToIconsCache) + { + InteropAndHelpers.GetWindowThreadProcessId(Hwnd, out uint processId); + + if (!_processIdsToIconsCache.ContainsKey(processId)) + { + try + { + Process process = Process.GetProcessById((int)processId); + Icon tempIcon = Icon.ExtractAssociatedIcon(process.Modules[0].FileName); + _processIdsToIconsCache.Add(processId, Imaging.CreateBitmapSourceFromHIcon( + tempIcon.Handle, + Int32Rect.Empty, + BitmapSizeOptions.FromEmptyOptions())); + } + catch + { + BitmapImage failedImage = new BitmapImage(new Uri(@"Images\failedIcon.jpg", UriKind.Relative)); + _processIdsToIconsCache.Add(processId, failedImage); + } + } + + return _processIdsToIconsCache[processId]; + } + } + } + + /// + /// Gets a value indicating whether is the window visible (might return false if it is a hidden IE tab) + /// + public bool Visible + { + get + { + return InteropAndHelpers.IsWindowVisible(Hwnd); + } + } + + /// + /// Gets a value indicating whether returns true if the window is minimized + /// + public bool Minimized + { + get + { + return GetWindowSizeState() == WindowSizeState.Minimized; + } + } + + /// + /// Initializes a new instance of the class. + /// Initializes a new Window representation + /// + /// the handle to the window we are representing + public Window(IntPtr hwnd) + { + // TODO: Add verification as to whether the window handle is valid + this.hwnd = hwnd; + } + + /// + /// Highlights a window to help the user identify the window that has been selected + /// + public void HighlightWindow() + { + throw new NotImplementedException(); + } + + /// + /// Switches dekstop focus to the window + /// + public void SwitchToWindow() + { + // The following block is necessary because + // 1) There is a weird flashing behaviour when trying + // to use ShowWindow for switching tabs in IE + // 2) SetForegroundWindow fails on minimized windows + if (ProcessName.ToLower().Equals("iexplore.exe") || !Minimized) + { + InteropAndHelpers.SetForegroundWindow(Hwnd); + } + else + { + InteropAndHelpers.ShowWindow(Hwnd, InteropAndHelpers.ShowWindowCommands.Restore); + } + + InteropAndHelpers.FlashWindow(Hwnd, true); + } + + /// + /// Converts the window name to string along with the process name + /// + /// The title of the window + public override string ToString() + { + return Title + " (" + ProcessName.ToUpper() + ")"; + } + + /// + /// Returns what the window size is + /// + /// The state (minimized, maximized, etc..) of the window + public WindowSizeState GetWindowSizeState() + { + InteropAndHelpers.GetWindowPlacement(Hwnd, out InteropAndHelpers.WINDOWPLACEMENT placement); + + switch (placement.ShowCmd) + { + case InteropAndHelpers.ShowWindowCommands.Normal: + return WindowSizeState.Normal; + case InteropAndHelpers.ShowWindowCommands.Minimize: + case InteropAndHelpers.ShowWindowCommands.ShowMinimized: + return WindowSizeState.Minimized; + case InteropAndHelpers.ShowWindowCommands.Maximize: // No need for ShowMaximized here since its also of value 3 + return WindowSizeState.Maximized; + default: + // throw new Exception("Don't know how to handle window state = " + placement.ShowCmd); + return WindowSizeState.Unknown; + } + } + + /// + /// Enum to simplify the state of the window + /// + public enum WindowSizeState + { + Normal, + Minimized, + Maximized, + Unknown, + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Components/WindowResult.cs b/src/modules/windowwalker/app/Window Walker/Components/WindowResult.cs new file mode 100644 index 000000000000..e0a4dd2615ed --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Components/WindowResult.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WindowWalker.Components +{ + class WindowResult:Window + { + /// + /// Number of letters in between constant for when + /// the result hasn't been set yet + /// + public static const int NoResult = -1; + + /// + /// Properties that signify how many characters (including spaces) + /// were found when matching the results + /// + public int LettersInBetweenScore + { + get; + set; + } + + /// + /// Constructor for WindowResult + /// + public WindowResult(Window window):base(window.Hwnd) + { + LettersInBetweenScore = WindowResult.NoResult; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/GlobalSuppressions.cs b/src/modules/windowwalker/app/Window Walker/GlobalSuppressions.cs new file mode 100644 index 000000000000..37063f3c9adf --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/GlobalSuppressions.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1009:ClosingParenthesisMustBeSpacedCorrectly", Justification = "All current violations are due to Tuple shorthand and so valid.")] + +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:PrefixLocalCallsWithThis", Justification = "We follow the C# Core Coding Style which avoids using `this` unless absolutely necessary.")] + +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:UsingDirectivesMustBePlacedWithinNamespace", Justification = "We follow the C# Core Coding Style which puts using statements outside the namespace.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "It is not a priority and have hight impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "It is not a priority and have hight impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1203:ConstantsMustAppearBeforeFields", Justification = "It is not a priority and have hight impact in code changes.")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "It is not a priority and have hight impact in code changes.")] + +[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:FieldNamesMustNotBeginWithUnderscore", Justification = "We follow the C# Core Coding Style which uses underscores as prefixes rather than using `this.`.")] + +[assembly: SuppressMessage("StyleCop.CSharp.SpecialRules", "SA0001:XmlCommentAnalysisDisabled", Justification = "Not enabled as we don't want or need XML documentation.")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:DocumentationTextMustEndWithAPeriod", Justification = "Not enabled as we don't want or need XML documentation.")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1625:ElementDocumentationMustNotBeCopiedAndPasted", Justification = "Reviewed.")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1614:ElementParameterDocumentationMustHaveText", Justification = "Reviewed.")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1616:ElementReturnValueDocumentationMustHaveText", Justification = "Reviewed.")] +[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly", Scope = "member", Target = "Microsoft.Templates.Core.Locations.TemplatesSynchronization.#SyncStatusChanged", Justification = "Using an Action does not allow the required notation")] + +// Non general supressions +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "The WebBrowser is loading source code to be shown to the user. No localization required.", MessageId = "System.Windows.Controls.WebBrowser.NavigateToString(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.CodeViewer.#UpdateCodeView(System.Func`2,System.String,System.String,System.Boolean)")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This is part of the markdown processing", MessageId = "System.Windows.Documents.Run.#ctor(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.Markdown.#ImageInlineEvaluator(System.Text.RegularExpressions.Match)")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer especification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.ITemplateInfoExtensions.#GetQueryableProperties(Microsoft.TemplateEngine.Abstractions.ITemplateInfo)")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer especification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.Composition.CompositionQuery.#Match(System.Collections.Generic.IEnumerable`1,Microsoft.Templates.Core.Composition.QueryablePropertyDictionary)")] +[assembly: SuppressMessage("Usage", "VSTHRD103:Call async methods when in an async method", Justification = "Resource DictionaryWriter does not implement flush async", Scope = "member", Target = "~M:Microsoft.Templates.Core.PostActions.Catalog.Merge.MergeResourceDictionaryPostAction.ExecuteInternalAsync~System.Threading.Tasks.Task")] + +// Threading supressions +[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.Controls.Notification.OnClose")] +[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel.OnDelete")] +[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.WizardNavigation.GoBack")] +[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.WizardNavigation.GoForward")] +[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel.OnDelete(Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel)")] + +// Localization suppressions +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#CreateJunction(System.String,System.String,System.Boolean)", Justification = "Only used for local generation")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#DeleteJunction(System.String)", Justification = "Only used for local generation")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#InternalGetTarget(Microsoft.Win32.SafeHandles.SafeFileHandle)", Justification = "Only used for local generation")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#OpenReparsePoint(System.String,Microsoft.Templates.Core.Locations.JunctionNativeMethods+EFileAccess)", Justification = "Only used for local generation")] +[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Windows.Documents.InlineCollection.Add(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Extensions.TextBlockExtensions.#OnSequentialFlowStepChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)", Justification = "No text here")] diff --git a/src/modules/windowwalker/app/Window Walker/HotKeyHandler.cs b/src/modules/windowwalker/app/Window Walker/HotKeyHandler.cs new file mode 100644 index 000000000000..0237e964c3e9 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/HotKeyHandler.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Interop; +using System.Windows.Media; + +namespace WindowWalker +{ + /// + /// This class handles all hotkey related activities + /// + /// Large pieces of this class were retrived from + /// http://www.dreamincode.net/forums/topic/323708-global-hotkeys-for-wpf-applications-c%23/ + /// + internal class HotKeyHandler + { + private readonly IntPtr hwnd; + + /// + /// Delegate handler for Hotkey being called + /// + public delegate void HotKeyPressedHandler(object sender, EventArgs e); + + /// + /// Event raised when there is an update to the list of open windows + /// + public event HotKeyPressedHandler OnHotKeyPressed; + + /// + /// Initializes a new instance of the class. + /// Constructor for the class + /// + /// The handle to the window we are registering the key for + public HotKeyHandler(Visual window) + { + hwnd = new WindowInteropHelper((Window)window).Handle; + + if (!(PresentationSource.FromVisual(window) is HwndSource source)) + { + throw new Exception("Could not create hWnd source from window."); + } + + source.AddHook(WndProc); + + bool result = Components.InteropAndHelpers.RegisterHotKey( + hwnd, + 1, + (int)Components.InteropAndHelpers.Modifiers.Ctrl | (int)Components.InteropAndHelpers.Modifiers.Win, + (int)Keys.None); + } + + /// + /// Call back function to detect when the hot key has been called + /// + /// + /// + /// + /// + /// if a key was called + /// if a key was called + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (msg == 0x0312 && OnHotKeyPressed != null) + { + OnHotKeyPressed(this, new EventArgs()); + } + + return IntPtr.Zero; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Images/failedIcon.jpg b/src/modules/windowwalker/app/Window Walker/Images/failedIcon.jpg new file mode 100644 index 000000000000..5d7dae5aa420 Binary files /dev/null and b/src/modules/windowwalker/app/Window Walker/Images/failedIcon.jpg differ diff --git a/src/modules/windowwalker/app/Window Walker/MVVMHelpers/PropertyChangedBase.cs b/src/modules/windowwalker/app/Window Walker/MVVMHelpers/PropertyChangedBase.cs new file mode 100644 index 000000000000..8e4cc80b7c46 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/MVVMHelpers/PropertyChangedBase.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.ComponentModel; + +namespace WindowWalker.MVVMHelpers +{ + internal class PropertyChangedBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/MVVMHelpers/RelayCommand.cs b/src/modules/windowwalker/app/Window Walker/MVVMHelpers/RelayCommand.cs new file mode 100644 index 000000000000..5d558f9ebc36 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/MVVMHelpers/RelayCommand.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Windows.Input; + +namespace WindowWalker.MVVMHelpers +{ + /// + /// Copy pasted from https://docs.microsoft.com/en-us/dotnet/standard/cross-platform/using-portable-class-library-with-model-view-view-model + /// + public class RelayCommand : ICommand + { + private readonly Action _handler; + private bool _isEnabled; + + public RelayCommand(Action handler) + { + _handler = handler; + } + + public bool IsEnabled + { + get + { + return _isEnabled; + } + + set + { + if (value != _isEnabled) + { + _isEnabled = value; + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } + } + + public bool CanExecute(object parameter) + { + return IsEnabled; + } + + public event EventHandler CanExecuteChanged; + + public void Execute(object parameter) + { + _handler(); + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/MainWindow.xaml b/src/modules/windowwalker/app/Window Walker/MainWindow.xaml new file mode 100644 index 000000000000..f42a182d9ed3 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/MainWindow.xaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/windowwalker/app/Window Walker/MainWindow.xaml.cs b/src/modules/windowwalker/app/Window Walker/MainWindow.xaml.cs new file mode 100644 index 000000000000..9c1a32f5092e --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/MainWindow.xaml.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Deployment.Application; +using System.Windows; +using System.Windows.Input; + +namespace WindowWalker +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + } + + private void UpdateDisplayedVersionNumber() + { + // Since displaying version number is not critical to functionality, we don't need to do anything if it fails. + try + { + Version applicationVersion = new Version("0.0.0.0"); + if (ApplicationDeployment.IsNetworkDeployed) + { + applicationVersion = ApplicationDeployment.CurrentDeployment.CurrentVersion; + } + + if (ApplicationDeployment.CurrentDeployment.UpdateLocation.Host.Contains("develop")) + { + versionDisplay.Text = "(develop) " + applicationVersion.ToString(); + } + else + { + versionDisplay.Text = applicationVersion.ToString(); + } + } + catch + { + } + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + DataContext = new ViewModels.WindowWalkerViewModel(this); + searchBox.Focus(); + + UpdateDisplayedVersionNumber(); + + HideWindow(); + } + + private void SearchBoxKeyUp(object sender, KeyEventArgs e) + { + var viewModel = (ViewModels.WindowWalkerViewModel)DataContext; + + if (e.Key == Key.Escape) + { + if (viewModel.WindowHideCommand.CanExecute(null)) + { + viewModel.WindowHideCommand.Execute(null); + } + } + else if (e.Key == Key.Down) + { + if (viewModel.WindowNavigateToNextResultCommand.CanExecute(null)) + { + viewModel.WindowNavigateToNextResultCommand.Execute(null); + } + } + else if (e.Key == Key.Up) + { + if (viewModel.WindowNavigateToPreviousResultCommand.CanExecute(null)) + { + viewModel.WindowNavigateToPreviousResultCommand.Execute(null); + } + } + else if (e.Key == Key.Enter) + { + if (viewModel.SwitchToSelectedWindowCommand.CanExecute(null)) + { + viewModel.SwitchToSelectedWindowCommand.Execute(null); + } + } + } + + private void Results_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + var viewModel = (ViewModels.WindowWalkerViewModel)DataContext; + + if (viewModel.SwitchToSelectedWindowCommand.CanExecute(null)) + { + viewModel.SwitchToSelectedWindowCommand.Execute(null); + } + } + + private void Window_Deactivated(object sender, EventArgs e) + { + HideWindow(); + } + + private void HideWindow() + { + var viewModel = (ViewModels.WindowWalkerViewModel)DataContext; + if (viewModel.WindowHideCommand.CanExecute(null)) + { + viewModel.WindowHideCommand.Execute(null); + } + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Properties/AssemblyInfo.cs b/src/modules/windowwalker/app/Window Walker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..207a81b9ba88 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Properties/AssemblyInfo.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Window Walker")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Window Walker")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// In order to begin building localizable applications, set +// CultureYouAreCodingWith in your .csproj file +// inside a . For example, if you are using US english +// in your source files, set the to en-US. Then uncomment +// the NeutralResourceLanguage attribute below. Update the "en-US" in +// the line below to match the UICulture setting in the project file. + +// [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] +// ThemeInfo.None = where theme specific resource dictionaries are located +// (used if a resource is not found in the page, +// or application resource dictionaries) +// ThemeInfo.SourceAssembly = where the generic resource dictionary is located +// (used if a resource is not found in the page, +// app, or any theme specific resource dictionaries) +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, + ResourceDictionaryLocation.SourceAssembly) +] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/windowwalker/app/Window Walker/Properties/Resources.Designer.cs b/src/modules/windowwalker/app/Window Walker/Properties/Resources.Designer.cs new file mode 100644 index 000000000000..4ae2128cccc0 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowWalker.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowWalker.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Properties/Resources.resx b/src/modules/windowwalker/app/Window Walker/Properties/Resources.resx new file mode 100644 index 000000000000..ffecec851ab4 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/Properties/Settings.Designer.cs b/src/modules/windowwalker/app/Window Walker/Properties/Settings.Designer.cs new file mode 100644 index 000000000000..2343f0340d5e --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowWalker.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Properties/Settings.settings b/src/modules/windowwalker/app/Window Walker/Properties/Settings.settings new file mode 100644 index 000000000000..8f2fd95d626c --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/Resources/Entypo-license.txt b/src/modules/windowwalker/app/Window Walker/Resources/Entypo-license.txt new file mode 100644 index 000000000000..3c7d5f59d8e7 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Resources/Entypo-license.txt @@ -0,0 +1,3 @@ +Entypo (http://www.entypo.com/) is created by Daniel Bruce and released under the Creative Commons, Share Alike/Attribution license. + +http://creativecommons.org/licenses/by-sa/3.0/ \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/Resources/Entypo.ttf b/src/modules/windowwalker/app/Window Walker/Resources/Entypo.ttf new file mode 100644 index 000000000000..d6d7687eb432 Binary files /dev/null and b/src/modules/windowwalker/app/Window Walker/Resources/Entypo.ttf differ diff --git a/src/modules/windowwalker/app/Window Walker/Resources/Icons.xaml b/src/modules/windowwalker/app/Window Walker/Resources/Icons.xaml new file mode 100644 index 000000000000..534cbf88256e --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Resources/Icons.xaml @@ -0,0 +1,5138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/windowwalker/app/Window Walker/Resources/IconsNonShared.xaml b/src/modules/windowwalker/app/Window Walker/Resources/IconsNonShared.xaml new file mode 100644 index 000000000000..893a131d756b --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Resources/IconsNonShared.xaml @@ -0,0 +1,5138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/windowwalker/app/Window Walker/Resources/WindowsIcons-license.txt b/src/modules/windowwalker/app/Window Walker/Resources/WindowsIcons-license.txt new file mode 100644 index 000000000000..cd3dc13aa60e --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Resources/WindowsIcons-license.txt @@ -0,0 +1,62 @@ +# License + +Please carefully understand the license and download the latest icons at ModernUIIcons.com. + +## Understand Your Rights +No Attribution and No Derived Works +http://creativecommons.org/licenses/by-nd/3.0/ * + +- If your project is open source include this license file in the source. +- Nothing is needed in the front facing project (UNLESS you + are using any of the icons listed below in the attribution section). +- Commercial use is not only allowed but encouraged. If it is an icon + in the attribution list below, you still need to attribute those! +- Do not distribute the entire package (I've allowed this dozens of + times for open source projects, but email me first). + +## Creator +- Austin Andrews (@templarian) + +## Contributor** +- Jay Zawrotny (@JayZawrotny) + - A Bunch +- Oren Nachman + - appbar.chevron.down + - appbar.chevron.up + - appbar.chevron.left + - appbar.chevron.right + +## Derived Works +- Alex Peattie + - Social: http://www.alexpeattie.com/projects/justvector_icons/ + +## Attribution*** +- Kris Vandermotten (@kvandermotten) + - appbar.medical.pulse +- Constantin Kichinsky (@kichinsky) + - appbar.currency.rubles + - appbar.currency.grivna +- Massimo Savazzi (@msavazzi) + - List of missing exported icons +- Proletkult Graphik, from The Noun Project + - appbar.draw.pen (inspired) +- Olivier Guin, from The Noun Project + - appbar.draw.marker +- Gibran Bisio, from The Noun Project + - appbar.draw.bucket +Andrew Forrester, from The Noun Project + - appbar.fingerprint + +* The license is for attribution, but this is not required. +** Developers and designers that emailed Templarian the source .design icons to be added into the package. PNGs also accepted, but may take longer to be added. +*** Icons I've copied so closely you want to attribute them and are also under the CC license. + +Contact +- http://templarian.com/ +- admin[@]templarian[.]com + +* Does not apply to copyrighted logos +- Skype +- Facebook +- Twitter +- etc... diff --git a/src/modules/windowwalker/app/Window Walker/Rules.ruleset b/src/modules/windowwalker/app/Window Walker/Rules.ruleset new file mode 100644 index 000000000000..b2b655ee7400 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Rules.ruleset @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/StyleCop.json b/src/modules/windowwalker/app/Window Walker/StyleCop.json new file mode 100644 index 000000000000..96df894ce499 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/StyleCop.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Microsoft Corporation", + "copyrightText": "Copyright (c) {companyName}\r\nThe {companyName} licenses this file to you under the MIT license.\r\nSee the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/", + "xmlHeader": false, + "headerDecoration": "", + "fileNamingConvention": "metadata", + "documentInterfaces": false, + "documentExposedElements": false, + "documentInternalElements": false + }, + "layoutRules": { + "newlineAtEndOfFile": "require" + }, + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace" + } + } +} \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/ViewModels/WindowWalkerViewModel.cs b/src/modules/windowwalker/app/Window Walker/ViewModels/WindowWalkerViewModel.cs new file mode 100644 index 000000000000..57b3f1ba5a31 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/ViewModels/WindowWalkerViewModel.cs @@ -0,0 +1,283 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Interop; +using Microsoft.Win32; +using WindowWalker.Components; +using WindowWalker.MVVMHelpers; + +namespace WindowWalker.ViewModels +{ + internal class WindowWalkerViewModel : PropertyChangedBase + { + private readonly HotKeyHandler _hotKeyHandler; + private readonly List _hints = new List() + { + "search...", + "you can reinvoke this app using CTRL + WIN", + }; + + private string _searchText = string.Empty; + private List _results = new List(); + private SearchResult _selectedWindow; + private bool _windowVisibility = true; + private string _hint = string.Empty; + private int _hintCounter = 0; + + private void WireCommands() + { + SwitchToSelectedWindowCommand = new RelayCommand(SwitchToSelectedWindow) + { + IsEnabled = true, + }; + WindowNavigateToNextResultCommand = new RelayCommand(WindowNavigateToNextResult) + { + IsEnabled = true, + }; + WindowNavigateToPreviousResultCommand = new RelayCommand(WindowNavigateToPreviousResult) + { + IsEnabled = true, + }; + WindowHideCommand = new RelayCommand(WindowHide) + { + IsEnabled = true, + }; + WindowShowCommand = new RelayCommand(WindowShow) + { + IsEnabled = true, + }; + } + + public string SearchText + { + get => _searchText; + + set + { + if (_searchText != value) + { + _searchText = value; + SearchController.Instance.SearchText = value; + NotifyPropertyChanged("SearchText"); + } + } + } + + public string Hint + { + get => _hint; + + set + { + if (_hint != value) + { + _hint = value; + NotifyPropertyChanged("Hint"); + } + } + } + + public List Results + { + get => _results; + + set + { + if (_results != value) + { + _results = value; + NotifyPropertyChanged("Results"); + } + } + } + + public SearchResult SelectedWindowResult + { + get => _selectedWindow; + set + { + if (_selectedWindow != value) + { + _selectedWindow = value; + WindowResultSelected(); + NotifyPropertyChanged("SelectedWindowResult"); + } + } + } + + public IntPtr Hwnd { get; private set; } + + public bool WindowVisibility + { + get + { + return _windowVisibility; + } + + set + { + if (_windowVisibility != value) + { + _windowVisibility = value; + NotifyPropertyChanged("WindowVisibility"); + } + } + } + + public RelayCommand SwitchToSelectedWindowCommand + { + get; + private set; + } + + public RelayCommand WindowNavigateToNextResultCommand + { + get; + private set; + } + + public RelayCommand WindowNavigateToPreviousResultCommand + { + get; + private set; + } + + public RelayCommand WindowHideCommand + { + get; + private set; + } + + public RelayCommand WindowShowCommand + { + get; + private set; + } + + public WindowWalkerViewModel(System.Windows.Window mainWindow) + { + // The path to the key where Windows looks for startup applications + RegistryKey rkApp = Registry.CurrentUser.OpenSubKey( + @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true); + + // Path to launch shortcut + string startPath = Environment.GetFolderPath(Environment.SpecialFolder.Programs) + + @"\WindowWalker\WindowWalker.appref-ms"; + + rkApp.SetValue("WindowWalker", startPath); + + rkApp.Close(); + + SearchController.Instance.OnSearchResultUpdate += SearchResultUpdated; + OpenWindows.Instance.UpdateOpenWindowsList(); + Hwnd = new WindowInteropHelper(mainWindow).Handle; + LivePreview.SetWindowExlusionFromLivePreview(Hwnd); + + _hotKeyHandler = new HotKeyHandler(mainWindow); + _hotKeyHandler.OnHotKeyPressed += HotKeyPressedHandler; + + _hints.AddRange(Commands.GetTips()); + Hint = _hints[_hintCounter]; + + WireCommands(); + } + + private void HotKeyPressedHandler(object sender, EventArgs e) + { + if (SearchText == string.Empty && WindowVisibility) + { + WindowHide(); + } + else + { + WindowShow(); + } + } + + private void WindowResultSelected() + { + if (SelectedWindowResult != null) + { + LivePreview.ActivateLivePreview(SelectedWindowResult.Result.Hwnd, Hwnd); + } + } + + private void WindowNavigateToPreviousResult() + { + if (SelectedWindowResult == null && Results.Count > 0) + { + SelectedWindowResult = Results.Last(); + return; + } + + if (Results.Count > 0) + { + SelectedWindowResult = Results[(Results.IndexOf(SelectedWindowResult) + Results.Count - 1) % Results.Count]; + } + } + + private void WindowNavigateToNextResult() + { + if (SelectedWindowResult == null && Results.Count > 0) + { + SelectedWindowResult = Results.First(); + return; + } + + if (Results.Count > 0) + { + SelectedWindowResult = Results[(Results.IndexOf(SelectedWindowResult) + 1) % Results.Count]; + } + } + + private void WindowHide() + { + LivePreview.DeactivateLivePreview(); + WindowVisibility = false; + ApplicationUpdates.InstallUpdateSyncWithInfo(); + } + + private void WindowShow() + { + _hintCounter = (_hintCounter + 1) % _hints.Count; + Hint = _hints[_hintCounter]; + + SearchText = string.Empty; + OpenWindows.Instance.UpdateOpenWindowsList(); + LivePreview.DeactivateLivePreview(); + WindowVisibility = true; + InteropAndHelpers.SetForegroundWindow(Hwnd); + } + + public void SwitchToSelectedWindow() + { + if (SearchText.StartsWith(":")) + { + LivePreview.DeactivateLivePreview(); + WindowHide(); + Commands.ProcessCommand(SearchText); + } + else if (SelectedWindowResult != null) + { + LivePreview.DeactivateLivePreview(); + SelectedWindowResult.Result.SwitchToWindow(); + WindowHide(); + } + else if (Results != null && Results.Count > 0) + { + LivePreview.DeactivateLivePreview(); + Results.First().Result.SwitchToWindow(); + WindowHide(); + } + } + + private void SearchResultUpdated(object sender, SearchController.SearchResultUpdateEventArgs e) + { + Results = SearchController.Instance.SearchMatches; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/Window Walker.csproj b/src/modules/windowwalker/app/Window Walker/Window Walker.csproj new file mode 100644 index 000000000000..8888c71bd5ed --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/Window Walker.csproj @@ -0,0 +1,219 @@ + + + + + Debug + AnyCPU + {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} + WinExe + Properties + WindowWalker + WindowWalker + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + + + x64 + true + full + false + ..\..\..\..\..\x64\Debug\modules\ + DEBUG;TRACE + prompt + 4 + false + ..\..\..\..\codeAnalysis\Rules.ruleset + false + + + x64 + pdbonly + true + $(SolutionDir)$(Platform)\$(Configuration)\modules\ + TRACE + prompt + 4 + false + ..\..\..\..\codeAnalysis\Rules.ruleset + + + + + + windowwalker.ico + + + + ..\..\..\..\..\packages\MaterialDesignColors.1.2.1\lib\net45\MaterialDesignColors.dll + + + ..\..\..\..\..\packages\MaterialDesignThemes.3.0.0\lib\net45\MaterialDesignThemes.Wpf.dll + + + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + + + + + + MainWindow.xaml + Code + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + Designer + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + + + + False + + + + + Include + True + File + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/WindowSearchResultToXamlConverter.cs b/src/modules/windowwalker/app/Window Walker/WindowSearchResultToXamlConverter.cs new file mode 100644 index 000000000000..79981e3399c1 --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/WindowSearchResultToXamlConverter.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Security; +using System.Windows.Data; +using System.Windows.Markup; +using System.Xml; +using WindowWalker.Components; + +namespace WindowWalker +{ + /// + /// Converts a string containing valid XAML into WPF objects. + /// + [ValueConversion(typeof(SearchResult), typeof(object))] + public sealed class WindowSearchResultToXamlConverter : IValueConverter + { + /// + /// Converts a string containing valid XAML into WPF objects. + /// + /// The string to convert. + /// This parameter is not used. + /// This parameter is not used. + /// This parameter is not used. + /// A WPF object. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is SearchResult input) + { + string withTags; + + if (input.BestScoreSource == SearchResult.TextType.ProcessName) + { + withTags = input.Result.Title; + withTags += $" ({InsertHighlightTags(input.Result.ProcessName, input.SearchMatchesInProcessName)})"; + } + else + { + withTags = InsertHighlightTags(input.Result.Title, input.SearchMatchesInTitle); + withTags += $" ({input.Result.ProcessName})"; + } + + withTags = SecurityElement.Escape(withTags); + + withTags = withTags.Replace("[[", ""). + Replace("]]", ""); + + string wrappedInput = string.Format("{0}", withTags); + + using (StringReader stringReader = new StringReader(wrappedInput)) + { + using (XmlReader xmlReader = XmlReader.Create(stringReader)) + { + return XamlReader.Load(xmlReader); + } + } + } + + return null; + } + + /// + /// Converts WPF framework objects into a XAML string. + /// + /// The WPF Famework object to convert. + /// This parameter is not used. + /// This parameter is not used. + /// This parameter is not used. + /// A string containg XAML. + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException("This converter cannot be used in two-way binding."); + } + + private string InsertHighlightTags(string content, List indexes) + { + int offset = 0; + var result = content.Replace("[[", "**").Replace("]]", "**"); + + string startTag = "[["; + string stopTag = "]]"; + + foreach (var index in indexes) + { + result = result.Insert(index + offset, startTag); + result = result.Insert(index + offset + startTag.Length + 1, stopTag); + + offset += startTag.Length + stopTag.Length; + } + + return result; + } + } +} diff --git a/src/modules/windowwalker/app/Window Walker/packages.config b/src/modules/windowwalker/app/Window Walker/packages.config new file mode 100644 index 000000000000..4ea3e0dbfc7b --- /dev/null +++ b/src/modules/windowwalker/app/Window Walker/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/modules/windowwalker/app/Window Walker/windowwalker.ico b/src/modules/windowwalker/app/Window Walker/windowwalker.ico new file mode 100644 index 000000000000..3517d980d181 Binary files /dev/null and b/src/modules/windowwalker/app/Window Walker/windowwalker.ico differ diff --git a/src/modules/windowwalker/dll/WindowWalker.rc b/src/modules/windowwalker/dll/WindowWalker.rc new file mode 100644 index 000000000000..3cef7a390331 --- /dev/null +++ b/src/modules/windowwalker/dll/WindowWalker.rc @@ -0,0 +1,32 @@ +1 VERSIONINFO + FILEVERSION 0,1,0,0 + PRODUCTVERSION 0,1,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Company Name" + VALUE "FileDescription", "$projectname$ Module" + VALUE "FileVersion", "0.1.0.0" + VALUE "InternalName", "$projectname$" + VALUE "LegalCopyright", "Copyright (C) 2019 Company Name" + VALUE "OriginalFilename", "$projectname$.dll" + VALUE "ProductName", "$projectname$" + VALUE "ProductVersion", "0.1.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END \ No newline at end of file diff --git a/src/modules/windowwalker/dll/WindowWalker.vcxproj b/src/modules/windowwalker/dll/WindowWalker.vcxproj new file mode 100644 index 000000000000..c913901e28a8 --- /dev/null +++ b/src/modules/windowwalker/dll/WindowWalker.vcxproj @@ -0,0 +1,126 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {51d3bd1f-07a8-48eb-b2a0-0a249cd4e1a6} + Win32Proj + WindowWalker + 10.0 + WindowWalker + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\modules\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\modules\ + + + + Use + Level3 + Disabled + true + _DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + MultiThreadedDebug + stdcpplatest + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + MultiThreaded + stdcpplatest + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) + + + + + + + + + + + Create + Create + pch.h + pch.h + + + + + + {74485049-c722-400f-abe5-86ac52d929b3} + + + + + + + + + \ No newline at end of file diff --git a/src/modules/windowwalker/dll/dllmain.cpp b/src/modules/windowwalker/dll/dllmain.cpp new file mode 100644 index 000000000000..6391ce6229dd --- /dev/null +++ b/src/modules/windowwalker/dll/dllmain.cpp @@ -0,0 +1,204 @@ +#include "pch.h" +#include +#include +#include +#include +#include "trace.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +// The PowerToy name that will be shown in the settings. +const static wchar_t* MODULE_NAME = L"Window Walker"; +// Add a description that will we shown in the module settings page. +const static wchar_t* MODULE_DESC = L"Alt+Tab but with search"; + +// These are the properties shown in the Settings page. +struct ModuleSettings +{ + +} g_settings; + +// Implement the PowerToy Module Interface and all the required methods. +class WindowWalker : public PowertoyModuleIface +{ +private: + // The PowerToy state. + bool m_enabled = true; + + // Load initial settings from the persisted values. + void init_settings(); + + // Handle to the Window Walker app we launch + HANDLE m_hProcess; + +public: + // Constructor + WindowWalker() + { + init_settings(); + }; + + // Destroy the powertoy and free memory + virtual void destroy() override + { + delete this; + } + + // Return the display name of the powertoy, this will be cached by the runner + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + // Return array of the names of all events that this powertoy listens for, with + // nullptr as the last element of the array. Nullptr can also be retured for empty + // list. + virtual const wchar_t** get_events() override + { + static const wchar_t* events[] = { nullptr }; + + return events; + } + + // Return JSON with the configuration options. + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(MODULE_DESC); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + // Signal from the Settings editor to call a custom action. + // This can be used to spawn more complex editors. + virtual void call_custom_action(const wchar_t* action) override + { + static UINT custom_action_num_calls = 0; + try + { + // Parse the action values, including name. + PowerToysSettings::CustomActionObject action_object = + PowerToysSettings::CustomActionObject::from_json_string(action); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Called by the runner to pass the updated settings values as a serialized JSON. + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config); + + values.save_to_settings_file(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Enable the powertoy + virtual void enable() + { + SHELLEXECUTEINFO sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\WindowWalker.exe"; + sei.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&sei); + + m_hProcess = sei.hProcess; + + m_enabled = true; + } + + // Disable the powertoy + virtual void disable() + { + if (m_enabled) + { + TerminateProcess(m_hProcess, 1); + } + + m_enabled = false; + } + + // Returns if the powertoys is enabled + virtual bool is_enabled() override + { + return m_enabled; + } + + // Handle incoming event, data is event-specific + virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override + { + if (wcscmp(name, ll_keyboard) == 0) + { + auto& event = *(reinterpret_cast(data)); + // Return 1 if the keypress is to be suppressed (not forwarded to Windows), + // otherwise return 0. + return 0; + } + else if (wcscmp(name, win_hook_event) == 0) + { + auto& event = *(reinterpret_cast(data)); + // Return value is ignored + return 0; + } + return 0; + } + + // This methods are part of an experimental features not fully supported yet + virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override + { + } + + virtual void signal_system_menu_action(const wchar_t* name) override + { + } +}; + +// Load the settings file. +void WindowWalker::init_settings() +{ + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(WindowWalker::get_name()); + } + catch (std::exception&) + { + // Error while loading from the settings file. Let default values stay as they are. + } +} + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new WindowWalker(); +} \ No newline at end of file diff --git a/src/modules/windowwalker/dll/pch.cpp b/src/modules/windowwalker/dll/pch.cpp new file mode 100644 index 000000000000..6db1b5fa2924 --- /dev/null +++ b/src/modules/windowwalker/dll/pch.cpp @@ -0,0 +1,2 @@ +#include "pch.h" +#pragma comment(lib, "windowsapp") \ No newline at end of file diff --git a/src/modules/windowwalker/dll/pch.h b/src/modules/windowwalker/dll/pch.h new file mode 100644 index 000000000000..8ef8e5eed698 --- /dev/null +++ b/src/modules/windowwalker/dll/pch.h @@ -0,0 +1,6 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include \ No newline at end of file diff --git a/src/modules/windowwalker/dll/resource.h b/src/modules/windowwalker/dll/resource.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/modules/windowwalker/dll/trace.cpp b/src/modules/windowwalker/dll/trace.cpp new file mode 100644 index 000000000000..1ba411198c9e --- /dev/null +++ b/src/modules/windowwalker/dll/trace.cpp @@ -0,0 +1,29 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +void Trace::MyEvent() +{ + TraceLoggingWrite( + g_hProvider, + "PowerToyName_MyEvent", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} diff --git a/src/modules/windowwalker/dll/trace.h b/src/modules/windowwalker/dll/trace.h new file mode 100644 index 000000000000..acd37a7a2051 --- /dev/null +++ b/src/modules/windowwalker/dll/trace.h @@ -0,0 +1,9 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + static void MyEvent(); +};