Author Topic: Help for a plugin newbie - reading keypresses inside the editor  (Read 7952 times)

Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Help for a plugin newbie - reading keypresses inside the editor
« on: December 06, 2009, 08:26:07 pm »
Greetings all,

I've been meaning to try and experiment a bit with extending code::blocks, adding some features to the editor window. Ultimately, if all goes well, i'd like to contribute to the code completion plugin, or create an alternative one with some features such as Netbeans code completion/refactoring features.

I'd like, firstly, to be able to detect when the Control key was pressed while the user is inside the editor, but haven't been able to figure out how.
I've been looking at the code and wiki, but it's not clear how to do this.

What i have so far:
- created a Generic Plugin using the New...Project...CodeBlocks plugin wizard
- added the table event declaration on the .h file with DECLARE_EVENT_TABLE() and associated the event with my callback function (as per wxWidget documentation, and following the byogame plugin code, as it was the only place i could find some keyboard event handling)
BEGIN_EVENT_TABLE(keys,cbPlugin)
    EVT_KEY_UP(keys::OnKeyUp)
END_EVENT_TABLE()

like so:
keys.h
Code: [Select]
#ifndef KEYS_H_INCLUDED
#define KEYS_H_INCLUDED

// For compilers that support precompilation, includes <wx/wx.h>
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <cbplugin.h> // for "class cbPlugin"

class wxKeyEvent;

class keys : public cbPlugin
{
    public:
        keys();
        virtual ~keys();

        virtual void BuildMenu(wxMenuBar* menuBar){}
        virtual void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0){}
        virtual bool BuildToolBar(wxToolBar* toolBar){ return false; }

    protected:
        virtual void OnAttach();
        virtual void OnRelease(bool appShutDown);


    private:
        void OnKeyUp(wxKeyEvent& event);

        DECLARE_EVENT_TABLE()
};

#endif // KEYS_H_INCLUDED

keys.cpp
Code: [Select]
#include <sdk.h> // Code::Blocks SDK
#include <configurationpanel.h>
#include "keys.h"

namespace
{
    PluginRegistrant<keys> reg(_T("keys"));
}


BEGIN_EVENT_TABLE(keys,cbPlugin)
    EVT_KEY_UP(keys::OnKeyUp)
END_EVENT_TABLE()


// constructor
keys::keys()
{
    if(!Manager::LoadResource(_T("keys.zip")))
    {
        NotifyMissingFile(_T("keys.zip"));
    }
}

// destructor
keys::~keys()
{
}

void keys::OnAttach()
{
}

void keys::OnRelease(bool appShutDown)
{
}

void keys::OnKeyUp(wxKeyEvent& event)
{
    if( !IsAttached() )
        return;
    Manager::Get()->GetLogManager()->Log( _("key up") );
}

I expected the releasing of any key to print to the Log Manager, but nothing so far.
Care to help?

Offline blueshake

  • Regular
  • ***
  • Posts: 459
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #1 on: December 07, 2009, 12:33:29 am »
Code: [Select]
void CodeCompletion::OnAttach()
{
    m_PageIndex = -1;
    m_InitDone = false;
    m_EditMenu = 0;
    m_SearchMenu = 0;
    m_ViewMenu = 0;
    m_Function = 0;
    m_Scope = 0;
    m_ParsedProjects.clear();
    m_FunctionsScope.clear();
    m_NameSpaces.clear();
    m_AllFunctionsScopes.clear();
    m_ToolbarChanged = true; // by default

    m_LastFile = wxEmptyString;

    LoadTokenReplacements();
    RereadOptions();

    m_LastPosForCodeCompletion = -1;
    m_StartIdxNameSpaceInScope = -1;
    m_NativeParser.SetNextHandler(this);

    m_NativeParser.CreateClassBrowser();

    // hook to editors
    EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<CodeCompletion>(this, &CodeCompletion::EditorEventHook);
    m_EditorHookId = EditorHooks::RegisterHook(myhook);

    // register event sinks
    Manager* pm = Manager::Get();

    pm->RegisterEventSink(cbEVT_EDITOR_SAVE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnReparseActiveEditor));
    pm->RegisterEventSink(cbEVT_EDITOR_OPEN, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorOpen));
    pm->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorActivated));
    pm->RegisterEventSink(cbEVT_EDITOR_TOOLTIP, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnValueTooltip));
    pm->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorClosed));

    pm->RegisterEventSink(cbEVT_APP_STARTUP_DONE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnAppDoneStartup));
    pm->RegisterEventSink(cbEVT_WORKSPACE_CHANGED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnWorkspaceChanged));
    pm->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectActivated));
    pm->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectClosed));
    pm->RegisterEventSink(cbEVT_PROJECT_SAVE, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectSaved));
    pm->RegisterEventSink(cbEVT_PROJECT_FILE_ADDED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectFileAdded));
    pm->RegisterEventSink(cbEVT_PROJECT_FILE_REMOVED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectFileRemoved));
}


It seems use this way to register event.Not sure about it.
Keep low and hear the sadness of little dog.
I fall in love with a girl,but I don't dare to tell her.What should I do?

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #2 on: December 07, 2009, 03:42:12 am »
I expected the releasing of any key to print to the Log Manager, but nothing so far.
Care to help?

what you are doing with your event table is telling wxWidgets you want to capture key events from your plugin and any of its children. one problem: your plugin is not the parent of the editors.

read this blog post: http://wxwidgets.blogspot.com/2007/01/in-praise-of-connect.html

Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #3 on: December 07, 2009, 07:10:06 am »
blueshake, after your reply, i went and dove into the code and tried to make some sense of it. Before noticing dmoore's reply, i came up with the following:

My OnAttach is now:
Code: [Select]
void keys::OnAttach()
{
    // hook events to editors
    EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<keys>(this, &keys::EditorEventHook);
    this->m_EditorHookId = EditorHooks::RegisterHook(myhook);
}

and "keys::EditorEventHook":
Code: [Select]
void keys::EditorEventHook(cbEditor* editor, wxScintillaEvent& event)
{
    wxString str(_T("keys::EditorEventHook:"));

    str += _(" event type:");
    std::stringstream ss;
    ss << event.GetEventType();
    str += wxString(ss.str().c_str(), wxConvUTF8);

    str += _(" event key:");
    ss.str("");
    ss << event.GetKey();
    str += wxString(ss.str().c_str(), wxConvUTF8);

    Manager::Get()->GetLogManager()->Log(str);

//    if(event.GetEventType() == wxEVT_SCI_KEY)
//        Manager::Get()->GetLogManager()->Log(_("key event type"));

    if(event.GetControl())
        Manager::Get()->GetLogManager()->Log(_("ctrl"));

    if(event.GetKey() == wxSCI_KEY_UP)
        Manager::Get()->GetLogManager()->Log(_("key up event"));

    if(event.GetKey() == wxSCI_KEY_DOWN)
        Manager::Get()->GetLogManager()->Log(_("key down event"));

    //if(event.GetEventType() == wxEVT_CHAR)
    //    Manager::Get()->GetLogManager()->Log(_("key character event"));

    // allow others to handle this event
    event.Skip();
}

I've managed to receive and print a few wxScintillaEvents, but i haven't been able to make much sense of them (no correspondance with the defines in wx/wxScintilla.h). Like a keypress will activate 5 or 6 events in the 10300 to 10*** range.

event.GetKey() is always 0, so none of those messages are printed.

If I leave the "if(event.GetControl())" line, i get an "undefined reference to `_imp___ZNK16wxScintillaEvent10GetControlEv' in keys.cpp :|
In the same way, if i leave the "wxEVT_SCI_KEY" check, i get another unexpected linker error...

The code looks nicely organized, but it's a bit overwhelming to the newbie, lots of different libraries mixed together and not many comments in the code make it a bit hard to follow.

I will try your suggestion later tomorrow, dmoore. Thank you for your help. If you have any idea on why i have the above problems, please share.
« Last Edit: December 07, 2009, 07:14:03 am by Turbo »

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #4 on: December 07, 2009, 02:26:17 pm »
did you try looking at void CodeCompletion::EditorEventHook(cbEditor* editor, wxScintillaEvent& event) in src/plugins/codecompletion/codecompletion.cpp (approx line 1850)?

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #5 on: December 07, 2009, 02:47:25 pm »
If I leave the "if(event.GetControl())" line, i get an "undefined reference to `_imp___ZNK16wxScintillaEvent10GetControlEv' in keys.cpp :|
In the same way, if i leave the "wxEVT_SCI_KEY" check, i get another unexpected linker error...

you need to link against the wxscintilla .dll or .so (again see how its done in codecompletion)

Quote
The code looks nicely organized, but it's a bit overwhelming to the newbie, lots of different libraries mixed together and not many comments in the code make it a bit hard to follow.

agree, but not easy to fix. suggetions?

Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #6 on: December 07, 2009, 06:11:07 pm »
did you try looking at void CodeCompletion::EditorEventHook(cbEditor* editor, wxScintillaEvent& event) in src/plugins/codecompletion/codecompletion.cpp (approx line 1850)?
Yes, it's been my main source of reference. But it doesn't handle extended keys. From the wxScintilla events I'm printing, I don't think the Ctrl/Alt/Shift keys generate wxScintillaEvents, unless it's in combination with. My guess is Scintilla is too high level for a Ctrl key event, and i have to go lower level. I'll now try your first suggestion, using Connect() and associating it with the currently selected Editor.
Your help has helped me learn a lot from the inner workings of CodeBlocks, thank you.

you need to link against the wxscintilla .dll or .so (again see how its done in codecompletion)
Thanks, i was a bit tired and did not realize wxscintilla.dll was not being linked.

Quote
Quote
The code looks nicely organized, but it's a bit overwhelming to the newbie, lots of different libraries mixed together and not many comments in the code make it a bit hard to follow.

agree, but not easy to fix. suggetions?
I'm not too confident with the code to suggest a proper solution yet (or to realize that this is in fact a real problem), but i just tend to leave lots of comments everywhere on my code. Some people have called me on that, saying that code looks dirty :)
I guess most of my doubts can be resolved with source code digging and checking out what each 3rd party library inner workings are, but it takes a lot longer than simply looking at the wiki's tutorials :)

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #7 on: December 07, 2009, 07:25:54 pm »
Quote
I guess most of my doubts can be resolved with source code digging and checking out what each 3rd party library inner workings are,

that's the way I work. html docs for 3rd party libs + right click -> find declaration/implementation work a charm :) Currently, the source of the various plugins are the best tutorials we have.

Quote
but it takes a lot longer than simply looking at the wiki's tutorials

Improving the wiki tutorials would probably be one of the the most valuable things we can do, but it does take quite a bit of work to put tutorials together and keep them up to date.

Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #8 on: December 08, 2009, 12:29:32 am »
Quote
what you are doing with your event table is telling wxWidgets you want to capture key events from your plugin and any of its children. one problem: your plugin is not the parent of the editors.

Which is the editor's plugin's parent? The wxWindow from which the editor inherits, or the EditorManager, or some other class?

I've tried using the Connect as mentioned in the post, but keys::OnKeyUp is never called. It now looks like:
Code: [Select]
void keys::OnAttach()
{
    this->Connect(wxID_ANY, wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp), NULL, this); // does nothing
    //Connect(wxID_ANY, wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp), NULL, this); // this does nothing as well
    //Connect(wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp)); // neither does this
}

Quote
Improving the wiki tutorials would probably be one of the the most valuable things we can do, but it does take quite a bit of work to put tutorials together and keep them up to date.
I'd contribute, if i can ever get this thing off the ground. Maybe i should go play with event catching in wxWidgets first to make sure i can make it work and then try in CodeBlocks.
I also need a new computer, 2 minutes to compile this barebones plugin is too much :)
« Last Edit: December 08, 2009, 01:20:49 am by Turbo »

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #9 on: December 08, 2009, 02:31:56 am »
Quote
what you are doing with your event table is telling wxWidgets you want to capture key events from your plugin and any of its children. one problem: your plugin is not the parent of the editors.

Which is the editor's plugin's parent? The wxWindow from which the editor inherits, or the EditorManager, or some other class?

I think the editor manager owns the editors, but C::B also uses a general event sink that traps many of the events so that they can be redirected to plugins.

Quote
I've tried using the Connect as mentioned in the post, but keys::OnKeyUp is never called. It now looks like:
Code: [Select]
void keys::OnAttach()
{
    this->Connect(wxID_ANY, wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp), NULL, this); // does nothing
    //Connect(wxID_ANY, wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp), NULL, this); // this does nothing as well
    //Connect(wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp)); // neither does this
}

replace

Code: [Select]
this->Connect(...);
with

Code: [Select]
target_window->Connect(wxEVT_KEY_UP, wxKeyEventHandler(keys::OnKeyUp), NULL, this);
where target window is some window that will receive keystrokes (editor/editormanager?). Also don't forget that scintilla also generates wxEVT_SCI_KEY events (I don't know what impact that has, if any, on regular key events)

btw, the Keybinder plugin uses a slightly different strategy to trap key events. It adds an entire event handler to relevant windows:

Code: [Select]
target_window->PushEventHandler(this);
You could then use a regular event table in your plugin (or whatever handler object you choose)

Quote
I'd contribute, if i can ever get this thing off the ground. Maybe i should go play with event catching in wxWidgets first to make sure i can make it work and then try in CodeBlocks.

If you plan to hack on C::B, the more comfortable you are with wxWidgets the better.

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #10 on: December 08, 2009, 10:37:22 pm »
Turbo: You might find this useful. I made a little plugin for tweaking editor settings that illustrates handling keypresses (in this case, disabling the insert key)

Code: [Select]
svn checkout http://svn.berlios.de/svnroot/repos/cbilplugin/branches/EditorTweaks
« Last Edit: December 08, 2009, 11:01:38 pm by dmoore »

Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #11 on: December 12, 2009, 07:56:30 am »
Wow, thanks a lot, you got it! I don't think i'd have figured the connection specifics like you have, anytime soon.
Building plugins is a process too heavy for my computer (one compilation took 7 minutes, averaging to 3), i'm waiting for the new pc so i can dive back into this.

Anyway, for completion's sake, if anyone comes looking for the code, here's what i ended up with:
keys.h:
Code: [Select]
/***************************************************************
 * Name:      keys
 * Purpose:   Code::Blocks plugin
 * Author:     (Turbo)
 * Created:   2009-12-06
 * Copyright:
 * License:   GPL
 **************************************************************/

#ifndef KEYS_H_INCLUDED
#define KEYS_H_INCLUDED

// For compilers that support precompilation, includes <wx/wx.h>
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <cbplugin.h>

#include <wx/wxscintilla.h> // for wxScintillaEvent

class CodeBlocksEvent;
class cbEditor;

class keys : public cbPlugin
{
    public:
        /** Constructor. */
        keys();
        /** Destructor. */
        virtual ~keys();

        virtual void BuildMenu(wxMenuBar* menuBar){}
        virtual void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0){}
        virtual bool BuildToolBar(wxToolBar* toolBar){ return false; }
    protected:
        virtual void OnAttach();
        virtual void OnRelease(bool appShutDown);


    private:
        void OnKeyUp(wxKeyEvent& event);

    private:
        int                                m_EditorHookId;
};
#endif // KEYS_H_INCLUDED


keys.cpp:
Code: [Select]
#include <sdk.h> // Code::Blocks SDK
#include <configurationpanel.h>
#include "keys.h"

#include <manager.h>
#include <sdk_events.h>
#include <editorbase.h>
#include <editor_hooks.h>
#include <wx/wxscintilla.h>
#include "cbstyledtextctrl.h"

#include <sstream>


// Register the plugin with Code::Blocks.
// We are using an anonymous namespace so we don't litter the global one.
namespace
{
    PluginRegistrant<keys> reg(_T("keys"));
}


// constructor
keys::keys()
{
    // Make sure our resources are available.
    // In the generated boilerplate code we have no resources but when
    // we add some, it will be nice that this code is in place already ;)
    if(!Manager::LoadResource(_T("keys.zip")))
    {
        NotifyMissingFile(_T("keys.zip"));
    }
}

// destructor
keys::~keys()
{
}

void keys::OnAttach()
{
    // do whatever initialization you need for your plugin
    // NOTE: after this function, the inherited member variable
    // m_IsAttached will be TRUE...
    // You should check for it in other functions, because if it
    // is FALSE, it means that the application did *not* "load"
    // (see: does not need) this plugin...


    // hook "key up" event with member callback function
    EditorManager* em = Manager::Get()->GetEditorManager();
    for(int i=0;i<em->GetEditorsCount();i++)
    {
        cbEditor* ed=em->GetBuiltinEditor(i);
        if(ed && ed->GetControl())
        {
            ed->GetControl()->SetOvertype(false);
            ed->GetControl()->Connect(wxEVT_KEY_UP,(wxObjectEventFunction) (wxEventFunction) (wxCharEventFunction)&keys::OnKeyUp,NULL,this);
        }
    }
}

void keys::OnRelease(bool appShutDown)
{
    // do de-initialization for your plugin
    // if appShutDown is true, the plugin is unloaded because Code::Blocks is being shut down,
    // which means you must not use any of the SDK Managers
    // NOTE: after this function, the inherited member variable
    // m_IsAttached will be FALSE...

    // unregister hook
    EditorManager* em = Manager::Get()->GetEditorManager();
    for(int i=0;i<em->GetEditorsCount();i++)
    {
        cbEditor* ed=em->GetBuiltinEditor(i);
        if(ed && ed->GetControl())
            ed->GetControl()->Disconnect(wxEVT_NULL,(wxObjectEventFunction) (wxEventFunction) (wxCharEventFunction)&keys::OnKeyUp);
    }
}

void keys::OnKeyUp(wxKeyEvent& event)
{
    if( !IsAttached() )
        return;

    if(event.GetKeyCode() == WXK_CONTROL)
        Manager::Get()->GetLogManager()->Log(wxString::Format(_("CTRL DOWN")));
    else
    {
        Manager::Get()->GetLogManager()->Log(wxString::Format(wxT("%i"),event.GetKeyCode()));
    }

    event.Skip();  // allow event to propagate
}
and remember to link with "wxscintilla".

Cookie goes to dmoore!
« Last Edit: December 12, 2009, 08:00:55 am by Turbo »

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2226
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #12 on: December 12, 2009, 02:40:58 pm »
Quote
void keys::OnAttach()
{
    // do whatever initialization you need for your plugin
    // NOTE: after this function, the inherited member variable
    // m_IsAttached will be TRUE...
    // You should check for it in other functions, because if it
    // is FALSE, it means that the application did *not* "load"
    // (see: does not need) this plugin...


    // hook "key up" event with member callback function
    EditorManager* em = Manager::Get()->GetEditorManager();
    for(int i=0;i<em->GetEditorsCount();i++)
    {
        cbEditor* ed=em->GetBuiltinEditor(i);
        if(ed && ed->GetControl())
        {
            ed->GetControl()->SetOvertype(false);
            ed->GetControl()->Connect(wxEVT_KEY_UP,(wxObjectEventFunction) (wxEventFunction) (wxCharEventFunction)&keys::OnKeyUp,NULL,this);
        }
    }
}

This will only work if you load the plugin *after* some editors are already open.

If CB loads this plugin at startup, there are no editors open.

To catch editors opened after startup, you will have to monitor a cbEvent such as cbEVT_EDITOR_ACTIVATED

Code: [Select]
   Manager::Get()->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnEditorActivated));

Then move your "hook "key up" event" loop to a function called OnEditorActivated.


Offline Turbo

  • Single posting newcomer
  • *
  • Posts: 8
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #13 on: December 12, 2009, 04:20:26 pm »
Oh, thanks! That would've definitely come up to byte me back later on.

Offline dmoore

  • Developer
  • Lives here!
  • *****
  • Posts: 1576
Re: Help for a plugin newbie - reading keypresses inside the editor
« Reply #14 on: December 12, 2009, 04:42:38 pm »
you don't need this:

Code: [Select]
ed->GetControl()->SetOvertype(false);
I only put it in my plugin code to make sure every editor I connected to was set to insert mode before blocking the insert key.