Event Handlers Not Getting Called? - wxWidgets - c++

I'm working on a program for my C++ programming class, using wxWidgets. I'm having a huge problem in that my event handlers (I assume) are not getting called, because when I click on the button to trigger the event, nothing happens. My question is: Can you help me find the problem and explain why they would not be getting called?
The event handlers OnAbout and OnQuit are working, just not OnCompute or OnClear. I'm really frustrated as I can't figure this out. Thanks a bunch in advance!
#include "wx/wx.h"
#include "time.h"
#include <string>
using std::string;
// create object of Time class
Time first;
class App: public wxApp
{
virtual bool OnInit();
};
class MainPanel : public wxPanel
{
public:
// Constructor for panel class
// Constructs my panel class
// Params - wxWindow pointer
// no return type
// pre-conditions: none
// post-conditions: none
MainPanel(wxWindow* parent);
// OnCompute is the event handler for the Compute button
// params - none
// preconditions - none
// postconditions - tasks will have been carried otu successfully
// returns void
void OnCompute(wxCommandEvent& WXUNUSED(event));
// OnClear is the event handler for the Clear button
// params - none
// preconditions - none
// postconditions - all text areas will be cleared of data
// returns void
void OnClear(wxCommandEvent& WXUNUSED(event));
// Destructor for panel class
// params none
// preconditions - none
// postconditions - none
// no return type
~MainPanel( );
private:
wxStaticText *startLabel;
wxStaticText *endLabel;
wxStaticText *pCLabel;
wxStaticText *newEndLabel;
wxTextCtrl *start;
wxTextCtrl *end;
wxTextCtrl *pC;
wxTextCtrl *newEnd;
wxButton *compute;
wxButton *clear;
DECLARE_EVENT_TABLE()
};
class MainFrame: public wxFrame
{
private:
wxPanel *mainPanel;
public:
MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
~MainFrame();
DECLARE_EVENT_TABLE()
};
enum
{
ID_Quit = 1,
ID_About,
BUTTON_COMPUTE = 100,
BUTTON_CLEAR = 200
};
IMPLEMENT_APP(App)
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_MENU(ID_Quit, MainFrame::OnQuit)
EVT_MENU(ID_About, MainFrame::OnAbout)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(MainPanel, wxPanel)
EVT_MENU(BUTTON_COMPUTE, MainPanel::OnCompute)
EVT_MENU(BUTTON_CLEAR, MainPanel::OnClear)
END_EVENT_TABLE()
bool App::OnInit()
{
MainFrame *frame = new MainFrame( _("Good Guys Delivery Time Calculator"), wxPoint(50, 50),
wxSize(450,340) );
frame->Show(true);
SetTopWindow(frame);
return true;
}
MainPanel::MainPanel(wxWindow* parent) : wxPanel(parent)
{
startLabel = new wxStaticText(this, -1, "Start Time:", wxPoint(75, 35));
start = new wxTextCtrl(this, -1, "", wxPoint(135, 35), wxSize(40, 21));
endLabel = new wxStaticText(this, -1, "End Time:", wxPoint(200, 35));
end = new wxTextCtrl(this, -1, "", wxPoint(260, 35), wxSize(40, 21));
pCLabel = new wxStaticText(this, -1, "Percent Change:", wxPoint(170, 85));
pC = new wxTextCtrl(this, -1, "", wxPoint(260, 85), wxSize(40, 21));
newEndLabel = new wxStaticText(this, -1, "New End Time:", wxPoint(180, 130));
newEnd = new wxTextCtrl(this, -1, "", wxPoint(260, 130), wxSize(40, 21));
compute = new wxButton(this, BUTTON_COMPUTE, "Compute", wxPoint(135, 185), wxSize(75, 35));
clear = new wxButton(this, BUTTON_CLEAR, "Clear", wxPoint(230, 185), wxSize(75, 35));
}
MainPanel::~MainPanel() {}
MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame( NULL, -1, title, pos, size )
{
mainPanel = new MainPanel(this);
wxMenu *menuFile = new wxMenu;
menuFile->Append( ID_About, _("&About...") );
menuFile->AppendSeparator();
menuFile->Append( ID_Quit, _("E&xit") );
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append( menuFile, _("&File") );
SetMenuBar( menuBar );
CreateStatusBar();
SetStatusText( _("Hi") );
}
MainFrame::~MainFrame() {}
void MainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(TRUE);
}
void MainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox( _("Alex Olson\nProject 11"),
_("About"),
wxOK | wxICON_INFORMATION, this);
}
void MainPanel::OnCompute(wxCommandEvent& WXUNUSED(event))
{
int startT;
int endT;
int newEndT;
double tD;
wxString startTString = start->GetValue();
wxString endTString = end->GetValue();
startT = wxAtoi(startTString);
endT = wxAtoi(endTString);
pC->GetValue().ToDouble(&tD);
first.SetStartTime(startT);
first.SetEndTime(endT);
first.SetTimeDiff(tD);
try {
first.ValidateData();
newEndT = first.ComputeEndTime();
*newEnd << newEndT;
}
catch (BaseException& e) {
wxMessageBox(_(e.GetMessage()),
_("Something Went Wrong!"),
wxOK | wxICON_INFORMATION, this);
}
}
void MainPanel::OnClear(wxCommandEvent& WXUNUSED(event))
{
start->Clear();
end->Clear();
pC->Clear();
newEnd->Clear();
}

EVT_MENU(BUTTON_COMPUTE,
MainPanel::OnCompute)
EVT_MENU(BUTTON_CLEAR,
MainPanel::OnClear)
In the above statements use EVT_BUTTON instead of EVT_MENU.

I think I see it. In the event table for MainPanel instead of EVT_MENU use EVT_BUTTON.

Related

wxWidgets - setting an image as background

I am trying to set an image as the background but I have no idea how to do it.
It has been hours. The first lines of code I copied from somebody and they seemed alright but it still doesn't work.
The error that I have appears in a separate window and says it cannot load/find the image.
#include "MainFrame.h"
#include <wx/wx.h>
#include "FrameTwo.h"
#include "wx/custombgwin.h"
#include <wx/dcclient.h>
enum IDs {
BUTTON_ID=2
};
wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MainFrame::OnLoginClicked)
wxEND_EVENT_TABLE()
MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title) {
wxPanel* panel = new wxPanel(this);
wxClientDC dc(panel);
wxMemoryDC mdc;
int w, h;
w = 600;
h = 600;
dc.GetSize(&w, &h);
wxImage img(wxT("C:\\Users\\ALEX\\Desktop\\background_uno.jpeg"), wxBITMAP_TYPE_JPEG);
wxBitmap cat(img.Scale(w, h, wxIMAGE_QUALITY_HIGH));
mdc.SelectObject(cat);
dc.Blit(0, 0, cat.GetWidth(), cat.GetHeight(), &mdc, 0, 0, wxCOPY, 0);
mdc.SelectObject(wxNullBitmap);
//panel->SetBackgroundColour(wxColour(255, 102, 102)); //light orange-red
//panel->SetBackgroundColour(wxColour(155, 202, 62)); //fresh green
wxButton* login = new wxButton(panel, BUTTON_ID, "Log in", wxPoint(340, 350), wxSize(100, 35));
CreateStatusBar(); //creates a bar in the bottom of the frame
//wxBoxSizer* sizer = new wxBoxSizer(wxALIGN_CENTER_VERTICAL);
//sizer -> Add(login, 1, wxEXPAND | wxALL, 10);
wxButton* exit = new wxButton(panel, wxID_OK, "Exit", wxPoint(340, 390), wxSize(100, 35));
exit->Bind(wxEVT_BUTTON, &MainFrame::OnExitClicked, this);
panel->SetFont(panel->GetFont().Scale(2.5));
wxStaticText* staticText = new wxStaticText(panel, wxID_ANY, "UNO DELUXE", wxPoint(300, 40));
panel->SetFont(panel->GetFont().Scale(0.5));
wxStaticText* username = new wxStaticText(panel, wxID_ANY, "Enter username: ", wxPoint(180, 250));
wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, " ", wxPoint(300, 250), wxSize(250, -1));
wxStaticText* password = new wxStaticText(panel, wxID_ANY, "Enter password: ", wxPoint(180, 290));
wxTextCtrl* textCtrl2 = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(300, 290), wxSize(250, -1), wxTE_PASSWORD);
}
I can't help you with the program not being able to find the image - the image needs to exist either in the application's working folder or on an absolute path.
You can get around this by including the image in the executable. Unfortunately there is no consistent cross-platform way to do this - windows and mac have separate ways of doing this but GTK has no way at all. Here are some suggestions from the wiki for some possible workarounds.
Regardless of the method used to get the image into the application, you should not draw it with a client DC. Instead you should handle the paint event for the panel and draw the image in the handler for that event. Here's an example:
#include <wx/wx.h>
class ImageBgFrame: public wxFrame
{
public:
ImageBgFrame(wxFrame *frame, const wxString& title);
private:
void OnImagePanelPaint(wxPaintEvent&);
void OnExit(wxCommandEvent&);
void CreateScaledBg();
wxPanel* m_imagePanel;
wxImage m_image;
wxBitmap m_scaledBg;
};
ImageBgFrame::ImageBgFrame(wxFrame *frame, const wxString& title)
:wxFrame(frame, wxID_ANY, title)
{
::wxInitAllImageHandlers();
// Try to load the image.
m_image = wxImage("Thinking-of-getting-a-cat.png", wxBITMAP_TYPE_PNG);
if ( !m_image.IsOk() )
{
return;
}
// Create the controls.
m_imagePanel = new wxPanel(this, wxID_ANY);
CreateScaledBg();
wxButton* exit = new wxButton(m_imagePanel, wxID_OK, "Exit");
// Arrang the controls with a sizer.
wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
szr->Add(exit,wxSizerFlags().Border(wxALL));
m_imagePanel->SetSizer(szr);
Layout();
// Bind Event Handlers
m_imagePanel->Bind(wxEVT_PAINT, &ImageBgFrame::OnImagePanelPaint, this);
exit->Bind(wxEVT_BUTTON, &ImageBgFrame::OnExit, this);
}
void ImageBgFrame::OnImagePanelPaint(wxPaintEvent&)
{
if ( m_imagePanel->GetSize() != m_scaledBg.GetSize() )
{
CreateScaledBg();
}
wxPaintDC dc(m_imagePanel);
dc.DrawBitmap(m_scaledBg,0,0);
}
void ImageBgFrame::OnExit(wxCommandEvent&)
{
Close();
}
void ImageBgFrame::CreateScaledBg()
{
wxSize sz = m_imagePanel->GetSize();
m_scaledBg = wxBitmap(m_image.Scale(sz.GetWidth(), sz.GetHeight(),
wxIMAGE_QUALITY_NORMAL));
}
class ImageBgApp : public wxApp
{
public:
bool OnInit()
{
ImageBgFrame* frame = new ImageBgFrame(NULL, "Image BG");
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(ImageBgApp);
This assumes that the image "Thinking-of-getting-a-cat.png" is in the application's working folder. On GTK it looks like this:
I've also made a few other changes to the code you gave:
I'm using Bind to connect the event handlers instead of static event tables.
I'm using default sizes for the controls instead of setting the size explicitly and laying out the controls in a sizer instead of setting explicit location.
Both of these changes are a more modern way of doing things.

wxButton covering entire client area C++

I have made an application using wxWidgets 3.1.5 in C++ and everything is working fine except a test button that I have on my main window.
Here's a pic:
The menubar, menus and their functions work perfectly but the button covers the entire client area.
Here's the code:
main.h
#pragma once
#include <wx\wx.h>
#include "mainFrame.h"
class main : public wxApp
{
private:
mainFrame* frame;
public:
virtual bool OnInit();
};
main.cpp
#include "main.h"
wxIMPLEMENT_APP(main);
bool main::OnInit()
{
frame = new mainFrame("Kill Me", wxPoint(15, 10), wxSize(640, 480));
frame->Show();
return true;
}
mainFrame.h
#pragma once
#include "About.h"
using str = std::string;
class mainFrame : public wxFrame
{
public:
mainFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
~mainFrame();
private:
About* abtF = NULL;
wxButton* hewwo = NULL;
wxMenuBar* mbar = NULL;
wxMenu* sett = NULL;
wxMenu* quitApp = NULL;
wxMenu* abt = NULL;
void onHewwo(wxCommandEvent& evt);
void onSett(wxCommandEvent& evt);
void quit(wxCommandEvent& evt);
void about(wxCommandEvent& evt);
wxDECLARE_EVENT_TABLE();
};
enum {
ID_SETT = 1,
ID_BTN = 2
};
mainFrame.cpp
#include "mainFrame.h"
wxBEGIN_EVENT_TABLE(mainFrame, wxFrame)
EVT_BUTTON(ID_BTN, onHewwo)
EVT_MENU(ID_SETT, onSett)
EVT_MENU(wxID_EXIT, quit)
EVT_MENU(wxID_ABOUT, about)
wxEND_EVENT_TABLE()
mainFrame::mainFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
:
wxFrame(nullptr, wxID_ANY, title, pos, size) {
hewwo = new wxButton(this, ID_BTN, "Hewwo World", wxPoint(15, 15), wxSize(70, 20));
sett = new wxMenu();
sett->AppendSeparator();
sett->Append(ID_SETT, "&Settings");
quitApp = new wxMenu();
quitApp->AppendSeparator();
quitApp->Append(wxID_EXIT, "&Quit this crap");
abt = new wxMenu();
abt->AppendSeparator();
abt->Append(wxID_ABOUT, "&About");
mbar = new wxMenuBar();
mbar->Append(sett, "&Settings");
mbar->Append(abt, "&About");
mbar->Append(quitApp, "&Quit");
SetMenuBar(mbar);
}
void mainFrame::onHewwo(wxCommandEvent& evt) {
wxMessageBox("Hewwo", "Hewwo", wxOK | wxICON_INFORMATION, this);
}
void mainFrame::onSett(wxCommandEvent& evt) {
wxMessageBox("Settings", "Settings", wxOK | wxICON_INFORMATION, this); // Just a test
}
void mainFrame::about(wxCommandEvent& evt) {
abtF = new About(wxPoint(10, 10), wxSize(480, 320));
abtF->Show();
}
void mainFrame::quit(wxCommandEvent& evt) {
Close(true);
}
mainFrame::~mainFrame() {
delete abtF;
}
I'm using Visual Studio 2019.
(I followed OneLoneCoder's (javidx9) youtube video on wxWidgets)
That is how a wxFrame with only one child behaves.
If you don't want that, use a wxSizer to layout your button (position, align, expand etc).
Reference:
if the frame has exactly one child window, not counting the status and toolbar, this child is resized to take the entire frame client area. If two or more windows are present, they should be laid out explicitly either by manually handling wxEVT_SIZE or using sizers
wxFrame docs -> Default event processing -> wxEVT_SIZE

Custom wxPanel problems

I'm writing a C++ wxWidgets application. I want my custom wxButton's that have a EXPANDABLE flag to open up a panel on top of them when I right click. Said panel is also supposed to automatically close when the cursor leaves it, or when the user clicks on one of the buttons it contains.
I created a custom wxPanel class for this job, but I'm having some trouble. The panel is successfully created when I right click on the button, but:
The panel is not removed when the cursor leaves it
There's an awkward graphical glitch when I move the cursor that cause the other interface elements to get on top of it (?)
Here's my code:
expandPanel.h
#pragma once
#include "wx/wx.h"
#include "ikeButton.h"
class expandPanel : public wxPanel
{
wxBoxSizer* sizer;
int type;
public:
expandPanel(wxWindow* parent, wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxTAB_TRAVERSAL,
const wxString & name = wxPanelNameStr);
~expandPanel();
void paintEvent(wxPaintEvent& evt);
void paintNow();
void Render(wxDC& dc);
void mouseLeftWindow(wxMouseEvent& evt);
DECLARE_EVENT_TABLE()
};
expandPanel.cpp
#include "expandPanel.h"
BEGIN_EVENT_TABLE(expandPanel, wxPanel)
EVT_LEAVE_WINDOW(expandPanel::mouseLeftWindow)
EVT_PAINT(expandPanel::paintEvent)
END_EVENT_TABLE()
expandPanel::expandPanel(wxWindow* parent, wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
wxPanel::Create(parent, id, pos, size, style, name);
sizer = new wxBoxSizer(wxHORIZONTAL);
SetSizer(sizer);
}
expandPanel::~expandPanel()
{
}
void expandPanel::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
Render(dc);
}
void expandPanel::paintNow()
{
wxClientDC dc(this);
Render(dc);
}
void expandPanel::Render(wxDC& dc)
{
dc.SetBrush(wxColour(80, 83, 111));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(0, 0, this->GetSize().GetWidth(), this->GetSize().GetHeight());
}
void expandPanel::mouseLeftWindow(wxMouseEvent& evt)
{
this->Close();
}
(I'm fairly new to this framework)
Ok, I tried switching to wxPopupTransientWindow, but the buttons appear on a different location on screen and sometimes disappear when I move the cursor, but the popup placed in the correct position do not disappear in any way. I feel like I'm doing this completely wrong.
expandMenu.h
#pragma once
#include "wx/wx.h"
#include "wx/popupwin.h"
#include "ikeButton.h"
class expandMenu : public wxPopupTransientWindow
{
wxBoxSizer* sizer;
public:
expandMenu(wxWindow* parent, wxPoint pos, wxSize size, int flags = wxBORDER_NONE);
~expandMenu();
void AddButton(ikeButton* btn);
};
expandMenu.cpp
#include "expandMenu.h"
expandMenu::expandMenu(wxWindow* parent, wxPoint pos, wxSize size, int flags)
: wxPopupTransientWindow(parent, flags)
{
this->SetPosition(pos);
this->SetSize(size);
this->SetBackgroundColour(wxColour(90, 93, 121));
sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Layout();
SetSizer(sizer);
}
expandMenu::~expandMenu()
{
}
void expandMenu::AddButton(ikeButton* btn)
{
unsigned int w = this->GetSize().GetWidth(), h = this->GetSize().GetHeight();
if (sizer->GetItemCount() > 0)
this->SetSize(wxSize(w, h + h / sizer->GetItemCount() ));
sizer->Add(btn, 0, wxEXPAND);
sizer->Layout();
}
Then I generate them like this:
void Main::OnExpandableRightClick(wxMouseEvent& evt)
{
expandMenu* menu = new expandMenu(this, wxGetMousePosition(), wxSize(50, 50));
for (int i = 0; i < 3; i++) menu->AddButton(new ikeButton(this, wxID_ANY, "TEST " + std::to_string(i), OP));
menu->Popup();
evt.Skip();
}
(This is a test scenario to see if everything is working)

Is it possible to catch global mouse events

I have a simple frame which is containing some buttons. My aim is that, after clicking the GetMousePosition button, getting the first mouse click position. I try to capture mouse click, even if I click outside of the running application.
This is a desktop application running on Windows. I tried some mouse events that wxwidgets provides, but I couldn't handle the next click event. I tried to find a solution with following code, but if there is some different solution, I can throw that code in the trash.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON_GetPos, MyFrame::OnButtonClick)
EVT_MOUSE_EVENTS(MyFrame::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(MyFrame::OnMouseCaptureLost)
END_EVENT_TABLE()
//some more code
void MyFrame::OnButtonClick(wxCommandEvent & WXUNUSED(event))
{
//Start Capturing for next mouse left-click
if (!HasCapture())
CaptureMouse();
}
void MyFrame::OnMouseEvent(wxMouseEvent &event)
{
if (event.LeftDown()) {
//GetMousePosition
if (HasCapture())
ReleaseMouse();
}
}
void MyFrame::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
{
}
I expect to get the mouse position, in the first left click after the button is pressed.
The code you posted looks like it should work. If there's a problem, it might be in the code you omitted. Anyway, here's a small example application showing the behavior you want. The underlying logic of this example is the same as the code you posted, except this example uses dynamic binding instead of event tables.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);
protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnLeftDown(wxMouseEvent&);
void CleanUp();
private:
wxTextCtrl* m_textCtrl;
};
class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};
MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();
button->Bind(wxEVT_BUTTON,&MyFrame::OnButtonClick,this);
}
void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");
Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}
}
void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
Unbind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Unbind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}
void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}
void MyFrame::OnLeftDown(wxMouseEvent& event)
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
wxIMPLEMENT_APP(MyApp);
I hope this helps.
edit: Here's a version using event tables as well:
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#define BUTTON_ID 101
class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);
protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnMouseEvent(wxMouseEvent&);
void CleanUp();
private:
wxTextCtrl* m_textCtrl;
wxDECLARE_EVENT_TABLE();
};
class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MyFrame::OnButtonClick)
EVT_MOUSE_EVENTS(MyFrame::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(MyFrame::OnMouseCapLost)
wxEND_EVENT_TABLE()
MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, BUTTON_ID, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();
}
void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");
}
}
void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
}
void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}
void MyFrame::OnMouseEvent(wxMouseEvent& event)
{
if( HasCapture() && event.LeftIsDown() )
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}
}
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
wxIMPLEMENT_APP(MyApp);

How to drag and drop between two (or more) wxListCtrl (wxGTK)?

I'm trying to organize a wxListCtrl-derived control (xList) which support DnD between items (LC_REPORT view). So, I catch BEGIN_DRAG event
Connect(wxEVT_COMMAND_LIST_BEGIN_DRAG,
(wxObjectEventFunction)&xList::OnBeginDrag
);
and OnBeginDrag function is designed in a way to catch mouse motion and mouse left button up events for each instance of xList (list):
list->Connect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
list->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
(and OnEndDrag disconnect them all). When I have single xList instance (one panel) it works perfectly, but when I have two it looks like motion and left-up events are caught only for the panel I start dragging from: I can DnD inside single panel, but when I drag mouse from one panel to another it still acts like xList::OnMoveDrag is still working for first panel. What I am missing to?
Is wxEVT_MOTION handled for each widget separately? If so, why program acts like this. If not, why it is always handled for widget I start dragging from, not the last connected one?
Here is a sample code (as far simple as I could reach) to show what's happening:
#include <wx/wx.h>
#include <vector>
class xList;
// class to store group of xList to DnD between
class DnDxList
{public:
void BeginDrag ();
void EndDrag ();
void AddList (xList* l) {list.push_back (l); }; // register new xList object
private:
std::vector<xList*> list;
};
class xList: public wxListCtrl
{public:
xList (DnDxList& dnd,
wxWindow *parent,
wxWindowID winid = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxLC_ICON,
const wxValidator &validator = wxDefaultValidator,
const wxString &name = wxListCtrlNameStr
);
virtual ~xList () {};
void OnBeginDrag(wxListEvent& event);
void OnEndDrag(wxMouseEvent& event);
void OnMoveDrag(wxMouseEvent& event);
DnDxList& dndsource; // keep reference to common DnDxList object
};
void DnDxList::BeginDrag () // connect motion and left-up events for all lists in group
{for (std::vector<xList*>::const_iterator i = list.begin(); i != list.end(); i++)
{(*i)->Connect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
(*i)->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
}
};
void DnDxList::EndDrag () // disconnect all
{for (std::vector<xList*>::const_iterator i = list.begin(); i != list.end(); i++)
{(*i)->Disconnect(wxEVT_MOTION,
wxMouseEventHandler(xList::OnMoveDrag)
);
(*i)->Disconnect(wxEVT_LEFT_UP,
wxMouseEventHandler(xList::OnEndDrag)
);
}
}
xList::xList (DnDxList& dnd,
wxWindow *parent,
wxWindowID winid,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator &validator,
const wxString &name
): wxListCtrl (parent, winid, pos, size, style, validator, name),
dndsource (dnd)
{Connect(wxEVT_COMMAND_LIST_BEGIN_DRAG,
(wxObjectEventFunction)&xList::OnBeginDrag
);
dndsource.AddList (this);
};
void xList::OnBeginDrag(wxListEvent& event) // begin drag
{SetCursor(wxCursor(wxCURSOR_HAND));
dndsource.BeginDrag();
}
void xList::OnMoveDrag(wxMouseEvent& event)
{std::cout << "Movie: " << this << std::endl; // to show the object for which the move event is called for
}
void xList::OnEndDrag(wxMouseEvent& event)
{std::cout << "End: " << this << std::endl;
dndsource.EndDrag();
SetCursor(wxCursor(*wxSTANDARD_CURSOR));
}
class xFrame: public wxFrame
{
public:
xFrame (const wxString& title,
const wxPoint& pos,
const wxSize& size
);
~xFrame () { }
private:
void OnExit(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
DnDxList* dndxlist;
xList* lp;
xList* rp;
wxPanel* panel;
};
BEGIN_EVENT_TABLE(xFrame, wxFrame)
EVT_MENU(wxID_EXIT, xFrame::OnExit)
END_EVENT_TABLE()
xFrame::xFrame(const wxString& title,
const wxPoint& pos,
const wxSize& size
): wxFrame(NULL, wxID_ANY, title, pos, size)
{
panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
// create common DnDxList
dndxlist = new DnDxList();
// create two panels
lp = new xList (*dndxlist,
panel,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL
);
rp = new xList (*dndxlist,
panel,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL
);
// some contents
lp->InsertColumn(0, _("A"));
lp->InsertColumn(1, _("B"));
lp->InsertColumn(2, _("C"));
lp->InsertColumn(3, _("D"));
lp->SetColumnWidth(0, 100);
lp->SetColumnWidth(1, 100);
lp->SetColumnWidth(2, 100);
lp->SetColumnWidth(3, 100);
for (long i = 0; i < 100; i++)
{lp->InsertItem(i, 1);
for (int j = 0; j < 4; j++)
{wxString s;
s << _("lp [") << i << _(", ") << j << _("]");
lp->SetItem (i, j, s);
}
}
rp->InsertColumn(0, _("A"));
rp->InsertColumn(1, _("B"));
rp->InsertColumn(2, _("C"));
rp->InsertColumn(3, _("D"));
rp->SetColumnWidth(0, 100);
rp->SetColumnWidth(1, 100);
rp->SetColumnWidth(2, 100);
rp->SetColumnWidth(3, 100);
for (long i = 0; i < 100; i++)
{rp->InsertItem(i, 1);
for (int j = 0; j < 4; j++)
{wxString s;
s << _("rp [") << i << _(", ") << j << _("]");
rp->SetItem (i, j, s);
}
}
sizer->Add(lp,1, wxEXPAND | wxALL, 10);
sizer->Add(rp,1, wxEXPAND | wxALL, 10);
panel->SetSizer(sizer);
}
void xFrame::OnExit(wxCommandEvent& event)
{
Close( true );
}
class xApp: public wxApp
{
public:
virtual bool OnInit();
};
IMPLEMENT_APP(xApp);
bool xApp::OnInit()
{
xFrame *frame = new xFrame(_("Frame"), wxPoint(50, 50), wxSize(450, 340) );
frame->Show(true);
return true;
}
Looking into console output one can find that mouse motion event and mouse left-up event always calls methods for the same object, where dragging starts from, not the object mouse is actually on.
wxListCtrl-specific drag events are indeed only meant to be used for dragging the items inside the same control. To drag and drop stuff between different controls, or even between different applications, you need to use the general wxDragSource and wxDropTarget classes.