Skip to content

Commit 1adfd05

Browse files
author
Andrin Meier
committed
Merge branch 'next'
2 parents ab3d28a + b0cea34 commit 1adfd05

File tree

132 files changed

+6582
-4375
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+6582
-4375
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ This library makes localizing WPF applications at runtime using resx files a bre
9292

9393
> Licensed under [The Code Project Open License](http://www.codeproject.com/info/cpol10.aspx).
9494
95+
###[EventHook](https://github.com/justcoding121/Windows-User-Action-Hook)
96+
97+
> A one stop library for global windows user actions such mouse, keyboard, clipboard, website visit & print events.
98+
99+
This library allows Rubberduck to detect righ-click actions in the active code pane, to dynamically enable/disable menu commands depending on the current context/selection. We're also using it to capture keypresses, to trigger a reparse of the current module as it's being modified.
100+
95101
##Icons
96102

97103
We didn't come up with these icons ourselves! Here's who did what:

RetailCoder.VBE/API/ParserState.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,18 @@ public void Initialize(VBE vbe)
6565
_parser = new RubberduckParser(vbe, _state, _attributeParser);
6666
}
6767

68+
/// <summary>
69+
/// Blocking call, for easier unit-test code
70+
/// </summary>
6871
public void Parse()
6972
{
7073
// blocking call
7174
_parser.Parse();
7275
}
7376

77+
/// <summary>
78+
/// Begins asynchronous parsing
79+
/// </summary>
7480
public void BeginParse()
7581
{
7682
// non-blocking call

RetailCoder.VBE/App.cs

Lines changed: 37 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
using System.Globalization;
55
using System.Linq;
66
using System.Runtime.InteropServices.ComTypes;
7-
using System.Threading.Tasks;
87
using System.Windows.Forms;
9-
using System.Windows.Input;
108
using Microsoft.Vbe.Interop;
119
using NLog;
1210
using Rubberduck.Common;
@@ -18,9 +16,6 @@
1816
using Rubberduck.UI.Command.MenuItems;
1917
using Infralution.Localization.Wpf;
2018
using Rubberduck.Common.Dispatch;
21-
using Rubberduck.Common.Hotkeys;
22-
using Rubberduck.UI.Command;
23-
using Hotkey = Rubberduck.Common.Hotkeys.Hotkey;
2419

2520
namespace Rubberduck
2621
{
@@ -35,7 +30,6 @@ public class App : IDisposable
3530
private readonly RubberduckCommandBar _stateBar;
3631
private readonly IIndenter _indenter;
3732
private readonly IRubberduckHooks _hooks;
38-
private readonly IEnumerable<ICommand> _appCommands;
3933

4034
private readonly Logger _logger;
4135

@@ -47,19 +41,13 @@ public class App : IDisposable
4741
private readonly IDictionary<VBComponents, Tuple<IConnectionPoint, int>> _componentsEventsConnectionPoints =
4842
new Dictionary<VBComponents, Tuple<IConnectionPoint, int>>();
4943

50-
private IReadOnlyDictionary<string, RubberduckHotkey> _hotkeyNameMap;
51-
52-
private IReadOnlyDictionary<RubberduckHotkey, ICommand> _hotkeyActions;
53-
private IReadOnlyDictionary<string, ICommand> _secondKeyActions;
54-
5544
public App(VBE vbe, IMessageBox messageBox,
5645
IRubberduckParser parser,
5746
IGeneralConfigService configService,
5847
IAppMenu appMenus,
5948
RubberduckCommandBar stateBar,
6049
IIndenter indenter,
61-
IRubberduckHooks hooks,
62-
IEnumerable<ICommand> appCommands)
50+
IRubberduckHooks hooks)
6351
{
6452
_vbe = vbe;
6553
_messageBox = messageBox;
@@ -70,10 +58,10 @@ public App(VBE vbe, IMessageBox messageBox,
7058
_stateBar = stateBar;
7159
_indenter = indenter;
7260
_hooks = hooks;
73-
_appCommands = appCommands;
7461
_logger = LogManager.GetCurrentClassLogger();
7562

76-
_hooks.MessageReceived += hooks_MessageReceived;
63+
_hooks.MessageReceived += _hooks_MessageReceived;
64+
_configService.SettingsChanged += _configService_SettingsChanged;
7765
_configService.LanguageChanged += ConfigServiceLanguageChanged;
7866
_parser.State.StateChanged += Parser_StateChanged;
7967
_stateBar.Refresh += _stateBar_Refresh;
@@ -93,20 +81,43 @@ public App(VBE vbe, IMessageBox messageBox,
9381
UiDispatcher.Initialize();
9482
}
9583

84+
private void _hooks_MessageReceived(object sender, HookEventArgs e)
85+
{
86+
if (sender is MouseHookWrapper)
87+
{
88+
// right-click detected
89+
_appMenus.EvaluateCanExecute(_parser.State);
90+
}
91+
}
92+
93+
private void _configService_SettingsChanged(object sender, EventArgs e)
94+
{
95+
// also updates the ShortcutKey text
96+
_appMenus.Localize();
97+
_hooks.HookHotkeys();
98+
}
99+
96100
public void Startup()
97101
{
98102
CleanReloadConfig();
99103

104+
foreach (var project in _vbe.VBProjects.Cast<VBProject>())
105+
{
106+
_parser.State.AddProject(project);
107+
}
108+
100109
_appMenus.Initialize();
101110
_appMenus.Localize();
102111

103-
//_hooks.AddHook(new LowLevelKeyboardHook(_vbe));
104-
HookHotkeys();
112+
_hooks.HookHotkeys();
105113
_hooks.Attach();
106114
}
107115

116+
#region sink handlers. todo: move to another class
108117
async void sink_ProjectRemoved(object sender, DispatcherEventArgs<VBProject> e)
109118
{
119+
_parser.State.RemoveProject(e.Item);
120+
110121
Debug.WriteLine(string.Format("Project '{0}' was removed.", e.Item.Name));
111122
Tuple<IConnectionPoint, int> value;
112123
if (_componentsEventsConnectionPoints.TryGetValue(e.Item.VBComponents, out value))
@@ -120,6 +131,8 @@ async void sink_ProjectRemoved(object sender, DispatcherEventArgs<VBProject> e)
120131

121132
async void sink_ProjectAdded(object sender, DispatcherEventArgs<VBProject> e)
122133
{
134+
_parser.State.AddProject(e.Item);
135+
123136
if (!_parser.State.AllDeclarations.Any())
124137
{
125138
// forces menus to evaluate their CanExecute state:
@@ -239,138 +252,31 @@ async void sink_ProjectActivated(object sender, DispatcherEventArgs<VBProject> e
239252
Debug.WriteLine(string.Format("Project '{0}' was activated.", e.Item.Name));
240253
// do something?
241254
}
255+
#endregion
242256

243-
private Keys _firstStepHotKey;
244-
private bool _isAwaitingTwoStepKey;
245-
private bool _skipKeyUp;
246-
247-
private void hooks_MessageReceived(object sender, HookEventArgs e)
248-
{
249-
if (sender is LowLevelKeyboardHook)
250-
{
251-
HandleLowLevelKeyhook(e);
252-
return;
253-
}
254-
255-
var hotKey = sender as IHotkey;
256-
if (hotKey != null)
257-
{
258-
HandleHotkey(hotKey);
259-
}
260-
else
261-
{
262-
AwaitNextKey();
263-
}
264-
}
265-
266-
private void HandleHotkey(IHotkey hotkey)
267-
{
268-
if (hotkey.IsTwoStepHotkey)
269-
{
270-
_firstStepHotKey = hotkey.HotkeyInfo.Keys;
271-
AwaitNextKey(true, hotkey.HotkeyInfo);
272-
}
273-
else
274-
{
275-
_firstStepHotKey = Keys.None;
276-
_hotkeyActions[_hotkeyNameMap[hotkey.Key]].Execute(null);
277-
AwaitNextKey();
278-
}
279-
}
280-
281-
private void HandleLowLevelKeyhook(HookEventArgs e)
257+
private void _stateBar_Refresh(object sender, EventArgs e)
282258
{
283-
if (_skipKeyUp)
284-
{
285-
_skipKeyUp = false;
286-
return;
287-
}
288-
289-
if (_isAwaitingTwoStepKey)
290-
{
291-
// todo: use _firstStepHotKey and e.Key to run 2-step hotkey action
292-
if (_firstStepHotKey == Keys.I && e.Key == Keys.M)
293-
{
294-
_indenter.IndentCurrentModule();
295-
}
296-
297-
AwaitNextKey();
298-
return;
299-
}
300-
301-
var component = _vbe.ActiveCodePane.CodeModule.Parent;
302-
_parser.ParseComponent(component);
303-
304-
AwaitNextKey();
305-
return;
259+
_parser.State.OnParseRequested(sender);
306260
}
307261

308-
private void AwaitNextKey(bool eatNextKey = false, HotkeyInfo info = default(HotkeyInfo))
262+
private void Parser_StateChanged(object sender, EventArgs e)
309263
{
310-
_isAwaitingTwoStepKey = eatNextKey;
311-
foreach (var hook in _hooks.Hooks.OfType<ILowLevelKeyboardHook>())
264+
if (_parser.State.Status != ParserState.Ready)
312265
{
313-
hook.EatNextKey = eatNextKey;
314-
}
315-
316-
_skipKeyUp = eatNextKey;
317-
if (eatNextKey)
318-
{
319-
_stateBar.SetStatusText("(" + info + ") was pressed. Waiting for second key...");
266+
_hooks.Detach();
320267
}
321268
else
322269
{
323-
_stateBar.SetStatusText(_parser.State.Status.ToString());
270+
_hooks.Attach();
324271
}
325-
}
326-
327-
private void _stateBar_Refresh(object sender, EventArgs e)
328-
{
329-
_parser.State.OnParseRequested(sender);
330-
}
331272

332-
private void Parser_StateChanged(object sender, EventArgs e)
333-
{
334273
Debug.WriteLine("App handles StateChanged ({0}), evaluating menu states...", _parser.State.Status);
335274
_appMenus.EvaluateCanExecute(_parser.State);
336275
}
337276

338-
private void HookHotkeys()
339-
{
340-
var settings = _config.UserSettings.GeneralSettings.HotkeySettings;
341-
foreach (var hotkey in settings.Where(hotkey => hotkey.IsEnabled))
342-
{
343-
_hooks.AddHook(new Hotkey((IntPtr)_vbe.MainWindow.HWnd, hotkey.ToString()));
344-
}
345-
}
346-
347277
private void CleanReloadConfig()
348278
{
349279
LoadConfig();
350-
var hotkeys = _config.UserSettings.GeneralSettings.HotkeySettings
351-
.Where(hotkey => hotkey.IsEnabled).ToList();
352-
353-
_hotkeyNameMap = hotkeys
354-
.ToDictionary(
355-
hotkey => hotkey.ToString(),
356-
hotkey => (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey), hotkey.Name));
357-
358-
_hotkeyActions = (from hotkey in hotkeys
359-
let value = (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey),hotkey.Name)
360-
where string.IsNullOrEmpty(hotkey.Key2)
361-
select new
362-
{
363-
Hotkey = value,
364-
Command = _appCommands.OfType<IHotkeyCommand>()
365-
.SingleOrDefault(command => command.Hotkey == (RubberduckHotkey)Enum.Parse(typeof(RubberduckHotkey),hotkey.Name))
366-
})
367-
.ToDictionary(kvp => kvp.Hotkey, kvp => (ICommand)kvp.Command);
368-
369-
_secondKeyActions = hotkeys
370-
.Where(hotkey => !string.IsNullOrEmpty(hotkey.Key2))
371-
.ToDictionary(
372-
hotkey => hotkey.Key2,
373-
hotkey => hotkey.Command);
374280
}
375281

376282
private void ConfigServiceLanguageChanged(object sender, EventArgs e)
@@ -410,7 +316,6 @@ public void Dispose()
410316
item.Value.Item1.Unadvise(item.Value.Item2);
411317
}
412318

413-
_hooks.MessageReceived -= hooks_MessageReceived;
414319
_hooks.Dispose();
415320
}
416321
}

RetailCoder.VBE/AutoSave/AutoSave.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,21 @@ public AutoSave(VBE vbe, IGeneralConfigService configService)
2020
{
2121
_vbe = vbe;
2222
_configService = configService;
23-
_config = _configService.LoadConfiguration();
2423

2524
_configService.SettingsChanged += ConfigServiceSettingsChanged;
2625

27-
_timer.Enabled = _config.UserSettings.GeneralSettings.AutoSaveEnabled
28-
&& _config.UserSettings.GeneralSettings.AutoSavePeriod != 0;
26+
// todo: move this out of ctor
27+
//_timer.Enabled = _config.UserSettings.GeneralSettings.AutoSaveEnabled
28+
// && _config.UserSettings.GeneralSettings.AutoSavePeriod != 0;
2929

30-
if (_config.UserSettings.GeneralSettings.AutoSavePeriod != 0)
31-
{
32-
_timer.Interval = _config.UserSettings.GeneralSettings.AutoSavePeriod * 1000;
33-
_timer.Elapsed += _timer_Elapsed;
34-
}
30+
//if (_config.UserSettings.GeneralSettings.AutoSavePeriod != 0)
31+
//{
32+
// _timer.Interval = _config.UserSettings.GeneralSettings.AutoSavePeriod * 1000;
33+
// _timer.Elapsed += _timer_Elapsed;
34+
//}
3535
}
3636

37-
void ConfigServiceSettingsChanged(object sender, EventArgs e)
37+
private void ConfigServiceSettingsChanged(object sender, EventArgs e)
3838
{
3939
_config = _configService.LoadConfiguration();
4040

@@ -48,11 +48,11 @@ private void _timer_Elapsed(object sender, ElapsedEventArgs e)
4848
{
4949
try
5050
{
51-
// note: VBProject.FileName getter throws IOException if unsaved
52-
_vbe.VBProjects.OfType<VBProject>().Select(p => p.FileName).ToList();
51+
var projects = _vbe.VBProjects.OfType<VBProject>().Select(p => p.FileName).ToList();
5352
}
54-
catch (DirectoryNotFoundException)
53+
catch (IOException)
5554
{
55+
// note: VBProject.FileName getter throws IOException if unsaved
5656
return;
5757
}
5858

0 commit comments

Comments
 (0)