Mercurial > shortcutkeyfinder
view ShortcutKeyFinder/MainForm.cs @ 0:209d9210c18f default tip
It works.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 25 Jun 2016 13:42:54 +1000 |
parents | |
children |
line wrap: on
line source
using Microsoft.Win32; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Security.Principal; using System.Windows.Forms; namespace ShortcutKeyFinder { /// <summary>Shortcut key manager main form</summary> public partial class MainForm : Form { private const int _iconSize = 16; private const int _iconMargin = 4; private const string _registryKey = "Software\\ShortcutKeyManager", _showAllPreference = "ShowAllShortcuts"; private bool _suspendUpdates, _suspendUpdateButtons; private int _firstDisplayedRowIndex; private ShortcutKeyFinder _shortcutKeyFinder; private BindingSource _dataSource; private SolidBrush _textBrush; private StringFormat _cellFormat; private List<Shortcut> _oldSelection; private Hotkey _hotkey; /// <summary>List of shortcuts currently selected by the user</summary> private List<Shortcut> SelectedShortcuts { get { List<Shortcut> shortcuts = new List<Shortcut>(); foreach (DataGridViewRow row in ShortcutGrid.SelectedRows) if ((ShortcutGrid.DataSource as BindingSource).Count > row.Index) shortcuts.Add((Shortcut)row.DataBoundItem); return shortcuts; } } /// <summary>Data source for the shortcut grid</summary> private BindingList<Shortcut> Shortcuts { get { return _shortcutKeyFinder != null ? _shortcutKeyFinder.Shortcuts : null; } } /// <summary>Constructor and form initialization</summary> public MainForm() { InitializeComponent(); // Set up grid data source _shortcutKeyFinder = new ShortcutKeyFinder(); _dataSource = new BindingSource() { DataSource = _shortcutKeyFinder.Shortcuts }; // Cell format for custom painting of shortcut icons and names in the same cell _cellFormat = new StringFormat(StringFormatFlags.NoWrap) { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter }; // Set up grid ShortcutGrid.AutoGenerateColumns = false; HotkeyColumn.ReadOnly = false; ShortcutGrid.DataSource = _dataSource; ShortcutGrid.CellParsing += ShortcutGrid_CellParsing; _shortcutKeyFinder.Shortcuts.ListChanged += Shortcuts_ListChanged; // Set up UI SetupElevateButton(); LoadPreferences(); // Reduce grid flicker typeof(DataGridView).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty, null, ShortcutGrid, new object[] { true }); } /// <summary>Refreshes data when form receives focus</summary> protected override void OnActivated(EventArgs e) { base.OnActivated(e); if (!_suspendUpdates) FindShortcutWorker.RunWorkerAsync(); } /// <summary>Cancels the inline editor if the user leaves the program</summary> protected override void OnDeactivate(EventArgs e) { ShortcutGrid.CancelEdit(); ShortcutGrid.EndEdit(); } /// <summary>Scans for shortcuts in the background</summary> private void FindShortcutWorker_DoWork(object sender, DoWorkEventArgs e) { // Disable list change events while scan is in progress _dataSource.RaiseListChangedEvents = false; _shortcutKeyFinder.LoadShortcuts(IsAdministrator()); } /// <summary>Updates the grid after the scan is complete</summary> private void FindShortcutWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { _suspendUpdateButtons = true; // Prevent button flicker // Save UI state _firstDisplayedRowIndex = ShortcutGrid.FirstDisplayedScrollingRowIndex; _oldSelection = SelectedShortcuts.ToList(); // Refresh the grid _dataSource.RaiseListChangedEvents = true; _dataSource.ResetBindings(false); ShortcutGrid.ClearSelection(); // Restore UI state if (ShortcutGrid.Rows.Count > 0) ShortcutGrid.FirstDisplayedScrollingRowIndex = Math.Max(0, Math.Min(_firstDisplayedRowIndex, ShortcutGrid.Rows.Count - 1)); foreach (DataGridViewRow row in ShortcutGrid.Rows) row.Selected = _oldSelection.Contains((Shortcut)row.DataBoundItem); _suspendUpdateButtons = false; UpdateButtons(); // Hide busy indicators that appear when the form is first launched InitialProgressBar.Visible = false; ShortcutGrid.Visible = true; Cursor = Cursors.Default; } /// <summary>Updates the UI when a shortcut's properties have changed</summary> void Shortcuts_ListChanged(object sender, ListChangedEventArgs e) { // Update the "Clear shortcut key" button since the selection's shortcut may have been set or unset if (!_suspendUpdateButtons) { if (this.InvokeRequired) this.Invoke(new Action(() => UpdateButtons())); else UpdateButtons(); } // Force repainting of changed rows so the background color gets updated if (e.ListChangedType == ListChangedType.ItemChanged && e.NewIndex < ShortcutGrid.Rows.Count) ShortcutGrid.InvalidateRow(e.NewIndex); } /// <summary>Enables or disables the buttons depending on the current selection</summary> private void UpdateButtons() { ClearButton.Enabled = ShortcutGrid.SelectedRows.Count > 0 && SelectedShortcuts.Any(s => s.Hotkey != null); ClearAllButton.Enabled = Shortcuts.Any(s => s.Hotkey != null); } /// <summary>Sets and saves a shortcut's hotkey</summary> private void SetShortcutHotkey(Shortcut shortcut, Hotkey hotkey) { try { shortcut.Hotkey = hotkey; shortcut.Save(); } catch { MessageBox.Show("Error setting shortcut key", "Set Shortcut Key", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// <summary>Sets cell background color based on its shortcut's properties, and inserts the shortcut's icon next to its name</summary> private void ShortcutGrid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { // Header row if (e.RowIndex < 0) return; DataGridViewRow row = ShortcutGrid.Rows[e.RowIndex]; Shortcut shortcut = (Shortcut)row.DataBoundItem; // Paint duplicate shortcut keys red if (shortcut.HasDuplicateHotkey) { e.CellStyle.BackColor = Color.DarkRed; e.CellStyle.ForeColor = Color.White; e.CellStyle.SelectionBackColor = Color.OrangeRed; e.CellStyle.SelectionForeColor = Color.White; } // Paint readonly shortcuts gray else if (shortcut.ReadOnly) { e.CellStyle.BackColor = Color.LightGray; e.CellStyle.ForeColor = Color.Black; e.CellStyle.SelectionBackColor = Color.SteelBlue; e.CellStyle.SelectionForeColor = Color.White; } // Paint the shortcut's icon next to its name if (e.ColumnIndex == ShortcutColumn.Index) { Color textColor = row.Selected ? e.CellStyle.SelectionForeColor : e.CellStyle.ForeColor; if (_textBrush == null || _textBrush.Color != textColor) _textBrush = new SolidBrush(textColor); e.PaintBackground(e.ClipBounds, row.Selected); Rectangle textRect = new Rectangle(e.CellBounds.X + _iconSize + _iconMargin * 2, e.CellBounds.Y, e.CellBounds.Width - _iconSize - _iconMargin * 2, e.CellBounds.Height); e.Graphics.DrawImage(shortcut.Icon, _iconMargin, textRect.Y + ((textRect.Height - _iconSize) / 2)); e.Graphics.DrawString(e.FormattedValue.ToString(), e.CellStyle.Font, _textBrush, textRect, _cellFormat); e.Handled = true; } } /// <summary>Triggers direct hotkey editing if the hotkey column is double clicked, or opens the shortcut's Properties window for any other column</summary> private void ShortcutGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { if (e.ColumnIndex == HotkeyColumn.Index) { ShortcutGrid.CurrentCell = ShortcutGrid.Rows[e.RowIndex].Cells[e.ColumnIndex]; ShortcutGrid.BeginEdit(true); } else { ((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem).ShowExplorerPropertiesWindow(Handle); } } /// <summary>Opens the Properties window for the selected row on Enter, or activates the inline shortcut editor on F2</summary> private void ShortcutGrid_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { e.Handled = true; if (SelectedShortcuts.Count == 1) SelectedShortcuts[0].ShowExplorerPropertiesWindow(Handle); } else if (e.KeyCode == Keys.F2) { if (ShortcutGrid.SelectedRows.Count == 1) { ShortcutGrid.CurrentCell = ShortcutGrid.SelectedRows[0].Cells[HotkeyColumn.Index]; ShortcutGrid.BeginEdit(true); } } } /// <summary>Prepares the inline editor by clearing the text and registering event handlers</summary> private void ShortcutGrid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { (e.Control as TextBox).Text = ""; (e.Control as TextBox).KeyDown += CellEditor_KeyDown; (e.Control as TextBox).PreviewKeyDown += CellEditor_PreviewKeyDown; (e.Control as TextBox).Leave += CellEditor_Leave; } /// <summary>Removes event handlers from inline editor when it loses focus</summary> void CellEditor_Leave(object sender, EventArgs e) { (sender as TextBox).KeyDown -= CellEditor_KeyDown; (sender as TextBox).PreviewKeyDown -= CellEditor_PreviewKeyDown; (sender as TextBox).Leave -= CellEditor_Leave; } /// <summary>Finishes inline hotkey editing on Enter</summary> private void CellEditor_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { if (e.KeyCode == Keys.Enter) ShortcutGrid.EndEdit(); } /// <summary>Listens for key events when the inline editor is active and converts them to a hotkey</summary> void CellEditor_KeyDown(object sender, KeyEventArgs e) { System.Diagnostics.Debug.WriteLine("keydown"); if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) { _hotkey = null; (sender as TextBox).Clear(); } else if (e.KeyCode != Keys.Enter) { _hotkey = new Hotkey() { KeyCode = (byte)e.KeyCode, Control = e.Control, Alt = e.Alt, Shift = e.Shift }; (sender as TextBox).Text = _hotkey.ToString(); } e.Handled = true; e.SuppressKeyPress = true; } /// <summary>Saves the new hotkey to the data source and the shortcut when inline editing is complete</summary> private void ShortcutGrid_CellParsing(object sender, DataGridViewCellParsingEventArgs e) { e.ParsingApplied = true; e.Value = _hotkey; SetShortcutHotkey((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem, _hotkey); } /// <summary>Updates the list view to show all shorcuts or only shortcuts with hotkeys</summary> private void ShowAllCheckBox_CheckedChanged(object sender, EventArgs e) { _shortcutKeyFinder.ShowAll = ShowAllCheckBox.Checked; ShortcutGrid.ClearSelection(); SavePreferences(); } /// <summary>Removes the hotkeys from the selected shortcuts when the Clear button is clicked</summary> private void ClearButton_Click(object sender, EventArgs e) { foreach (Shortcut shortcut in SelectedShortcuts.Where(s => !s.ReadOnly && s.Hotkey != null)) SetShortcutHotkey(shortcut, null); } /// <summary>Clears shortcuts from all hotkeys when the Clear All button is clicked</summary> private void ClearAllButton_Click(object sender, EventArgs e) { _suspendUpdates = true; if (MessageBox.Show("Are you sure you want to clear all assigned shortcut keys?", "Clear Shortcut Keys", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { foreach (Shortcut shortcut in Shortcuts.Where(s => !s.ReadOnly && s.Hotkey != null)) SetShortcutHotkey(shortcut, null); } _suspendUpdates = false; } /// <summary>Prepares for inline shortcut editing, or prevents it if the shortcut is read only</summary> private void ShortcutGrid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { _hotkey = null; e.Cancel = ((Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem).ReadOnly; } /// <summary>Updates the button state when the grid selection changes</summary> private void ShortcutGrid_SelectionChanged(object sender, EventArgs e) { if (!_suspendUpdateButtons) UpdateButtons(); } /// <summary>Updates the button state since the hotkey of the current item may have changed</summary> private void ShortcutGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e) { if (!_suspendUpdateButtons) UpdateButtons(); } /// <summary>Checks whether the program is running with elevated permissions</summary> /// <remarks>http://stackoverflow.com/a/11660205</remarks> public static bool IsAdministrator() { return (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator); } /// <summary>Shows or hides the button to elevate to administrator based on whether the program is already running as an administrator</summary> private void SetupElevateButton() { if (IsAdministrator()) ElevateButton.Visible = false; else Win32Helpers.AddUacShield(ElevateButton); } /// <summary>Restarts the program, requesting elevation</summary> private void ElevateButton_Click(object sender, EventArgs e) { ProcessStartInfo info = new ProcessStartInfo() { UseShellExecute = true, WorkingDirectory = Environment.CurrentDirectory, FileName = Application.ExecutablePath, Verb = "runas" }; try { Process.Start(info); Application.Exit(); } catch { } // Do nothing if the user canceled the consent dialog } /// <summary>Saves checkbox state so it can be restored when the program is relaunched later</summary> private void SavePreferences() { try { RegistryKey key = Registry.CurrentUser.CreateSubKey(_registryKey); key.SetValue(_showAllPreference, ShowAllCheckBox.Checked, RegistryValueKind.DWord); } catch { } // Silently fail if we can't save preferences } /// <summary>Restores the checkbox state to the same as when the program last exited</summary> private void LoadPreferences() { try { RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryKey); ShowAllCheckBox.Checked = Convert.ToBoolean((int)key.GetValue(_showAllPreference, 0)); } catch { } // Silently fail if we can't load preferences } private void ShortcutGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { string tooltip = null; Shortcut shortcut = (Shortcut)ShortcutGrid.Rows[e.RowIndex].DataBoundItem; if (shortcut.HasDuplicateHotkey) tooltip = "More than one shortcut has the same hotkey assigned"; else if (shortcut.ReadOnly) tooltip = "Administrative privileges are required to edit this shortcut." + Environment.NewLine + "To edit, click the \"Allow editing shortcuts shared by all users\" button."; ShortcutGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].ToolTipText = tooltip; } } }