Open wxPopupMenu by RightClick on wxEdit

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
eriX
Experienced Solver
Experienced Solver
Posts: 84
Joined: Wed Feb 04, 2009 2:08 pm
Location: Germany
Contact:

Open wxPopupMenu by RightClick on wxEdit

Post by eriX » Fri Apr 06, 2012 2:15 pm

Hey guys,

I want to open a wxPopupMenu, if the user makes a right-click on a wxEdit field.

After this the Popup appears and the user can select a item (special character in this case) that becomes insert into the wxEdit element.


What are the steps to create a RightClickEvent?
I'm working with wxDevC++ and wxWidgets 2.9.2

Thank you very much!

- Eric.

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: Open wxPopupMenu by RightClick on wxEdit

Post by Radek » Fri Apr 06, 2012 3:08 pm

(1) Connect() your wxEdit to wxEVT_CONTEXT_MENU. Do not forget NULL and this because you will Connect() something (the editfield) to something else (your frame, most likely). Now, you get the context menu event whenever you right click to the editfield.
(2) The context menu handler needs to PopupMenu(your menu).
(3) Connect() all menu items to wxEVT_COMMAND_MENU_SELECTED. This event has ID (the menu item ID) so that you need not Connect() the menu. You can Connect() your frame and make your life easier. Your handler gets control whenever you select a menu item.
(4) The handler gets the menu item ID from event.GetId() and does what is needed.

Note that you can Connect() a range of ID's so that you need not to Connect() each menu item separately. Make sure that the ID's of your menu items form a range and Connect() only once.

eriX
Experienced Solver
Experienced Solver
Posts: 84
Joined: Wed Feb 04, 2009 2:08 pm
Location: Germany
Contact:

Re: Open wxPopupMenu by RightClick on wxEdit

Post by eriX » Fri Apr 06, 2012 4:04 pm

Ok.
Shall I do it that way?

Code: Select all

WxEdit1->Connect(wxEVT_CONTEXT_MENU,(wxObjectEventFunction)&MyFrm::WHATSHERE?!,0,this);
How can I open the WxPopupMenu ?
Sorry, but I can't follow your instructions beginning from point 2 (I don't understand them - I'm not very experienced)

Code: Select all

WxPopupMenu1 = new wxMenu(wxT("TEST"));
WxPopupMenu1->Append(ID_MNU_A_1416, wxT("A"), wxT(""), wxITEM_NORMAL);
WxPopupMenu1->Append(ID_MNU_B_1417, wxT("B"), wxT(""), wxITEM_NORMAL);

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Re: Open wxPopupMenu by RightClick on wxEdit

Post by Auria » Fri Apr 06, 2012 4:33 pm

Take a look at wxWindow::PopupWindow in the docs. Also note that most wx classes derive from wxWindow, which means PopupWindow can be called on your frame or on a panel for instance
"Keyboard not detected. Press F1 to continue"
-- Windows

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2463
Joined: Sun Jan 03, 2010 5:45 pm

Re: Open wxPopupMenu by RightClick on wxEdit

Post by PB » Fri Apr 06, 2012 4:47 pm

Hi, you can take a look at the following example, should be simple enough to follow.

Code: Select all

#include <wx/wxprec.h>

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


class MyFrame : public wxFrame
{
public:
    MyFrame()
        : wxFrame(NULL, wxID_ANY, _("Test"))
    {
        
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        wxTextCtrl* textCtrl = new wxTextCtrl(this, wxID_ANY);
        bSizer->Add(textCtrl, 1, wxEXPAND);   
        
        SetSizer(bSizer);
        Layout();
        Centre();        

        textCtrl->Bind(wxEVT_CONTEXT_MENU, &MyFrame::OnShowContextMenu, this);        
        Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnContextMenuSelected, this, MENU_ID_CONTEXT_1, MENU_ID_CONTEXT_3);
    }	
private:
    enum MenuIDs { MENU_ID_CONTEXT_1 = wxID_HIGHEST + 1, MENU_ID_CONTEXT_2, MENU_ID_CONTEXT_3 };

    void OnShowContextMenu(wxContextMenuEvent& event)
    {
        wxMenu menu;

        menu.Append(MENU_ID_CONTEXT_1, "Context Menu command 1");
        menu.Append(MENU_ID_CONTEXT_2, "Context Menu command 2");
        menu.Append(MENU_ID_CONTEXT_3, "Context Menu command 3");
        
        PopupMenu(&menu);        
    }

    void OnContextMenuSelected(wxCommandEvent& event)
    {
        wxString str;

        switch (event.GetId()) {
            case MENU_ID_CONTEXT_1:
                str = "Context Menu command 1";
                break;
            case MENU_ID_CONTEXT_2:
                str = "Context Menu command 2";
                break;
            case MENU_ID_CONTEXT_3:
                str = "Context Menu command 3";
                break;
            default:
                str = "Uknown command?!";
        }
        wxMessageBox(str);
    }
};

/**** MyApp ****/
class MyApp : public wxApp
{
public:	
	virtual bool OnInit()
	{
		if (!wxApp::OnInit())
			return false;       	

        MyFrame* frame = new MyFrame();
        frame ->Show();

        return true;
	}
};

IMPLEMENT_APP(MyApp)
Be aware that if you override the context menu for an edit control, you will lose its native context menu. On MSW, it has Cut/Copy/Paste/Select All, insert special unicode character, change RTL direction, launch IME... Depending on the context of your application, you should try to duplicate at least those the clipboard related commands, many people don't use (or even know) keyboard short cuts for them.
Last edited by PB on Fri Apr 06, 2012 4:53 pm, edited 1 time in total.

Radek
Super wx Problem Solver
Super wx Problem Solver
Posts: 286
Joined: Sun Sep 11, 2011 7:17 am

Re: Open wxPopupMenu by RightClick on wxEdit

Post by Radek » Fri Apr 06, 2012 4:50 pm

(1) Yes. Instead of casting to ObjectEventFunction use wxContextMenuEventHandler(MyFrm::WHATSHERE)
(2) The WHATSHERE handler needs ContextMenuEvent& event as a parameter and it needs to call PopupMenu(wxPopupMenu1). In fact, it need not do more.
(3) The menu items A and B needs to be connected to a handler so that clicking them can trigger something. Supposing the ID_MNU_* ID's form a sequence, you can

Code: Select all

Connect(
         ID_MNU_A_1416,
         ID_MNU_B_1417,
         wxEVT_COMMAND_MENU_SELECTED,
         wxCommandEventHandler(MyFrm::OnPopupMenu)
       );
The OnPopupMenu handler needs a wxCommandEvent& event as a parameter and it will check event.GetId() to find out which menu item has been clicked.

eriX
Experienced Solver
Experienced Solver
Posts: 84
Joined: Wed Feb 04, 2009 2:08 pm
Location: Germany
Contact:

Re: Open wxPopupMenu by RightClick on wxEdit

Post by eriX » Fri Apr 06, 2012 5:24 pm

@PB:
I used your example in my and it is working. Thank you!

Can I bind this function on some more TextBoxes - so that the Popup is shown at wxEdit 1 - 10 and changes the text in the Textbox from where the Popup is called?

Could I call the popup-function with a variable (e.g.: OnContextMenuSelected(3) and the number 3 stands for the wxEdit element?)
or is there any easier way to bind and display the popup at many controls and change the label from them then?

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2463
Joined: Sun Jan 03, 2010 5:45 pm

Re: Open wxPopupMenu by RightClick on wxEdit

Post by PB » Fri Apr 06, 2012 6:02 pm

Hi, you could try something like this Edited

Code: Select all

#include <wx/wxprec.h>

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

#include <vector>


class MyFrame : public wxFrame
{
public:
    MyFrame()
        : wxFrame(NULL, wxID_ANY, _("Test"))
    {
                
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        const size_t numCtrls = 10;
        wxTextCtrl* textCtrl;
        
        m_textCtrls.reserve(numCtrls);
        for (int i = 0; i < numCtrls; i++) {
            textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, 
                wxString::Format("Text control #%d", i+1));
            bSizer->Add(textCtrl, 1, wxEXPAND);   
            
            textCtrl->Bind(wxEVT_CONTEXT_MENU, &MyFrame::OnShowContextMenu, this);      
            
            m_textCtrls.push_back(textCtrl);
        }
        
        SetSizer(bSizer);
        Fit();
        Centre();        
                
        Bind(wxEVT_COMMAND_MENU_SELECTED, &MyFrame::OnContextMenuSelected, this, MENU_ID_CONTEXT_1, MENU_ID_CONTEXT_3);
    }	
private:
    typedef std::vector<wxTextCtrl*> TextCtrlPtrVector;
    TextCtrlPtrVector m_textCtrls;
        
    enum MenuIDs { MENU_ID_CONTEXT_1 = wxID_HIGHEST + 1, MENU_ID_CONTEXT_2, MENU_ID_CONTEXT_3 };

    void OnShowContextMenu(wxContextMenuEvent& event)
    {
        wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>(event.GetEventObject());
        wxCHECK_RET(textCtrl, "MyFrame:OnShowContextMenu: Invalid event object");
        
        wxMenu menu;

        menu.SetTitle(textCtrl->GetName());
        menu.Append(MENU_ID_CONTEXT_1, "Context Menu command 1");
        menu.Append(MENU_ID_CONTEXT_2, "Context Menu command 2");
        menu.Append(MENU_ID_CONTEXT_3, "Context Menu command 3");
        
        PopupMenu(&menu);        
    }

    void OnContextMenuSelected(wxCommandEvent& event)
    {
        wxMenu* menu = dynamic_cast<wxMenu*>(event.GetEventObject());                        
        wxCHECK_RET(menu, "MyFrame:OnContextMenuSelected: Invalid event object");
                
        wxString str;                
        switch (event.GetId()) {
            case MENU_ID_CONTEXT_1:
                str = "Context Menu command 1";
                break;
            case MENU_ID_CONTEXT_2:
                str = "Context Menu command 2";
                break;
            case MENU_ID_CONTEXT_3:
                str = "Context Menu command 3";
                break;
            default:
                wxFAIL_MSG("MyFrame:OnContextMenuSelected: Invalid command");
        }        
        
        wxString menuTitle = menu->GetTitle();
        size_t idx;
        
        for (idx = 0; idx < m_textCtrls.size(); idx++) {
            if (m_textCtrls[idx]->GetName() == menuTitle)
                break;
        }
        wxCHECK_RET(idx < m_textCtrls.size(), "MyFrame:OnContextMenuSelected: Invalid event object");
        
        *m_textCtrls[idx] << str;
    }
};

/**** MyApp ****/
class MyApp : public wxApp
{
public:	
	virtual bool OnInit()
	{
		if (!wxApp::OnInit())
			return false;       	

        MyFrame* frame = new MyFrame();
        frame ->Show();

        return true;
	}
};

IMPLEMENT_APP(MyApp)
This example uses (somewhat hackishly) wxMenu:[Set|Get]Title(), because if I understand correctly you wanted to have a specific menu label (= title) there for each control?

But if I did that myself, I would not probably use wxMenu but derived my own class from it and added some member variable indicating which control the menu command is for, set it in OnShowContextMenu() and retrieved in OnContextMenuSelected().

But maybe you want different menus for different edit controls? Anyway, that would not be difficult to implement as you should have all required information now. I leave it as an exercise. ;)

Post Reply