Monitoring for Windows clipboard changes with wxWidgets
Many applications have cut/copy/paste icons in their menus and on their toolbars. Any such application needs to enable and disable these items at the appropriate time. For menus, they can monitor the "menu opening" event (EVT_MENU_OPEN
in wxWidgets) and then enable/disable items lazily just before they are displayed. For toolbars, this lazy trick cannot be used, as toolbars are displayed permanently. Handling the cut and copy toolbar items is application specific, but the application programmer should be able to work out when there is something available to be put onto the clipboard. Handling the paste toolbar icon requires a different strategy. wxWidgets doesn't seem to provide a "clipboard data changed" event, hence you have to fall back to whatever your platform provides. On Windows, this boils down to the SetClipboardViewer
function. For convenience, I've wrapped up the required logic into a class called ClipboardWatcher
, which others might find useful.
// Released into the public domain
template <typename T>
class ClipboardWatcher : public T
{
public:
ClipboardWatcher() :
m_hClipboardChainNext(NULL) {}
template <typename A1>
ClipboardWatcher(A1 a1) : T(a1),
m_hClipboardChainNext(NULL) {}
template <typename A1, typename A2>
ClipboardWatcher(A1 a1, A2 a2) : T(a1, a2),
m_hClipboardChainNext(NULL) {}
template <typename A1, typename A2, typename A3>
ClipboardWatcher(A1 a1, A2 a2, A3 a3) : T(a1, a2, a3),
m_hClipboardChainNext(NULL) {}
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
switch(nMsg)
{
case WM_CHANGECBCHAIN:
if((HWND)wParam == m_hClipboardChainNext)
m_hClipboardChainNext = (HWND)lParam;
else if(m_hClipboardChainNext != NULL)
SendMessage(m_hClipboardChainNext, nMsg, wParam, lParam);
return 0;
case WM_DRAWCLIPBOARD:
OnClipboardChange();
if(m_hClipboardChainNext != NULL)
SendMessage(m_hClipboardChainNext, nMsg, wParam, lParam);
return 0;
case WM_DESTROY:
if(m_hClipboardChainNext)
ChangeClipboardChain(GetHwnd(), m_hClipboardChainNext);
break;
}
return T::MSWWindowProc(nMsg, wParam, lParam);
}
protected:
void BeginWatchingClipboard()
{
m_hClipboardChainNext = SetClipboardViewer(GetHWND());
}
virtual void OnClipboardChange() = 0;
private:
HWND m_hClipboardChainNext;
};
Typical usage looks something like this:
class frmMain : public ClipboardWatcher<wxFrame> // instead of just : public wxFrame
{
public:
frmMain() : ClipboardWatcher((wxWindow*)NULL, wxID_ANY, wxTheApp->GetAppDisplayName())
{
// etc.
BeginWatchingClipboard();
}
virtual void OnClipboardChange()
{
document_window_t pDoc = getActiveDocument();
m_pStdToolbar->EnableTool(wxID_PASTE, pDoc && pDoc->canPaste());
m_pStdToolbar->Refresh(false);
}
// etc.
};