santiago 36 Posted May 6, 2019 Hi there, I am using the Open Tools Api to develop a plugin for the Delphi IDE. What I would like to be able to do, is detect that a user has pressed a key in the code editor window. If the active file (module) is read-only I would then like to take a specific action. Unfortunately the Open Tools Api does not seem to offer the functionality I require (be notified of a key press in the editor window). The only other option I can think of, would be to use a Windows Hook. SetWindowsHookEx(WH_KEYBOARD, ... Maybe someone knows if there is a better way to achieve this instead of having to resort to a Windows Hook. Thanks!! Share this post Link to post
Attila Kovacs 629 Posted May 6, 2019 Or you could hook the editors onkeypress event. In both cases be careful, you have to clean up before your module gets unloaded/dumped. 1 Share this post Link to post
Dave Nottage 557 Posted May 6, 2019 If you don't have it already, I recommend downloading the GExperts source: https://sourceforge.net/p/gexperts/code/HEAD/tree/trunk/ Then have a look at the GX_EditorChangeServices unit Share this post Link to post
Edwin Yip 154 Posted May 7, 2019 If you don't have it already, I know Dave is another expert in the Delphi IDE extension dev area, read this blog: https://www.davidghoyle.co.uk/WordPress/?page_id=667 Share this post Link to post
David Hoyle 68 Posted May 7, 2019 Try the following: https://www.davidghoyle.co.uk/WordPress/?p=349 Share this post Link to post
Rudy Velthuis 91 Posted May 7, 2019 (edited) 31 minutes ago, David Hoyle said: Try the following: https://www.davidghoyle.co.uk/WordPress/?p=349 David, You wrote there, that WindowCommand is pretty unreliable. And that is the only one I see that might have been of interest. Edited May 7, 2019 by Rudy Velthuis Share this post Link to post
Uwe Raabe 2057 Posted May 7, 2019 20 hours ago, santiago said: If the active file (module) is read-only I would then like to take a specific action. If you are not actually interested in the key itself, but only in the fact that the editor content has changed, you might get away with EditorViewModified. Share this post Link to post
Rudy Velthuis 91 Posted May 7, 2019 29 minutes ago, Uwe Raabe said: If you are not actually interested in the key itself, but only in the fact that the editor content has changed, you might get away with EditorViewModified. Yes, I noticed, but that is not really detecting keyboard actions, is it, it is just very indirectly detecting some of them. Hooking up the editor, if possible, would be the best way, IMO. I just don't know how. <g> Share this post Link to post
santiago 36 Posted May 7, 2019 @Uwe Raabe The file is read-only. So unfortunately, the EditorViewModified event will never be triggered. I got it to work. Here is a high level overview of how it works, in case it might be of help to someone else: I react to the INTAEditServicesNotifier.EditorViewModified event. This is triggered whenever a new editor window is activated. Here I can easily detect whether the source file is read only or not, using EditView.Buffer.IsReadOnly. The trickiest part was then getting the handle of the editor control window (TEditControl). The Open Tools Api provides no functionality for that. It does provide you with the IDE's TCustomForm though (INTAEditWindow.Form). With the form you can find the editor control by searching for a child component with ClassName = 'TEditControl' and ComponentName = 'Editor'. Instead of working with a Windows Hook, I just added a handler for Application.OnMessage. procedure TEditorNotifier.ApplicationEventsMessage(var Msg: tagMSG; var Handled: Boolean); begin if (Msg.message = WM_KEYDOWN) and (FEditControl.Handle = Msg.hwnd) then begin // magic happens... end; if not Handled and Assigned(FPreviousMessageEvent) then FPreviousMessageEvent(Msg, Handled); end; 3 Share this post Link to post
Attila Kovacs 629 Posted May 7, 2019 (edited) TApplication.OnMessage seems to be assigned by default, so you are just overwriting it. I'd hook it or use a TApplicationevents component on a custom form. (Never tried) Edit: Ah I see, you are already hooking it. Edited May 7, 2019 by Attila Kovacs Share this post Link to post
santiago 36 Posted May 7, 2019 (edited) @Attila Kovacs yes I am keeping a reference to the previous handler in case it was set. I would prefer to use TApplicationEvents though. Thx for the tip. Unfortunately I could not get it to work. FApplicationEvents := TApplicationEvents.Create(EditWindow.Form);// nil); FApplicationEvents.OnMessage := ApplicationEventsMessage; The callback method (ApplicationEventsMessage) was never called. I also tried without setting an owner. Edited May 7, 2019 by santiago typo Share this post Link to post
dummzeuch 1505 Posted May 8, 2019 Hooking events in the Delphi IDE is dangerous, as you never know whether there already is another plugin that uses the same event. I tried to describe a way to make that process a little bit safer and blogged about it: https://blog.dummzeuch.de/2016/03/28/safe-event-hooking-for-delphi-ide-plugins-revisited/ But it's not just other plugins you must be wary about. New versions of the IDE might also start using previously unused events. E.g. Delphi 10.3 is now using the previously unused Screen.OnFormChanged event, but only within the Options dialog. This broke some of the GExperts enhancements to this dialog and those dialogs opened from there, in particular those for the path edit dialog. 2 Share this post Link to post
santiago 36 Posted May 8, 2019 @dummzeuch this plugin is only for internal use. I am chaining the event. For my use case the TApplicationEvents class which was recommended by @Attila Kovacs seems to be a good choice. Unfortunately, I could not get it to work. Have you used this class inside an ide plugin? If I understand correctly this would allow multiple event subcribers to coexist. Share this post Link to post
Attila Kovacs 629 Posted May 8, 2019 Works for me: procedure TTestWizard.OnAppMessage(var Msg: TMsg; var Handled: Boolean); begin if (Msg.message = WM_KEYDOWN) then begin Beep; end; end; constructor TTestWizard.Create; begin FAppEv := TApplicationEvents.Create(Application.FindComponent('EditWindow_0')); FAppEv.OnMessage := OnAppMessage; 1 1 Share this post Link to post
santiago 36 Posted May 8, 2019 Thx, good to know!! I will give it another shot tomorrow. 🙂 Share this post Link to post
santiago 36 Posted May 8, 2019 @Attila Kovacs I gave it another go, and it does indeed work. Don't know what I did wrong the first time I tried... I also tested if multiple TApplicationEvents can be used simultaneously. This also works well. Thank you! 1 Share this post Link to post
Remy Lebeau 1396 Posted May 9, 2019 (edited) 4 hours ago, santiago said: For my use case the TApplicationEvents class which was recommended by @Attila Kovacs seems to be a good choice. Unfortunately, I could not get it to work. TApplicationEvents works by assigning its own handlers to the TApplication events. So if the IDE, or another plugin, assigns its own handlers to the TApplication events directly then TApplicationEvents will not work correctly. I also tested if multiple TApplicationEvents can be used simultaneously. This also works well. As it should, since TApplicationEvents is specifically designed to be used that way. When everyone uses TApplicationEvents handlers instead of TApplication handlers, then everyone gets notified of events. Edited May 9, 2019 by Remy Lebeau 1 Share this post Link to post
dummzeuch 1505 Posted May 21, 2020 (edited) On 5/7/2019 at 12:00 AM, Dave Nottage said: If you don't have it already, I recommend downloading the GExperts source: https://sourceforge.net/p/gexperts/code/HEAD/tree/trunk/ Just to make that clear: The url Dave posted is for browsing the source code in a web browser, not for downloading it with subversion. The url for subversion is this: https://svn.code.sf.net/p/gexperts/code/trunk Edited May 21, 2020 by dummzeuch 1 Share this post Link to post