Motivation
WTL's CUpdateUI
goes against the policy of ATL. BEGIN_MSG_MAP
is a set of function calls, but WTL's BEGIN_UPDATE_UI_MAP
is a set of data. It makes your program uncontrollable especially in the case of MDI applications. See the WTL sample, MDIDocVw. It is impossible to understand. Therefore, CUpdateUI
should be replaced.
So, Ketchup.CmdUI provides a port of MFC Update Command UI mechanism, which is the only good thing MFC has left.
Requirements
Quick start
- Include the header:
#include "ketchup/cmd_ui.hpp"
- Define
CmdUI
handlers: void OnUpdateViewStatusBar(ketchup::cmd_ui& ui)
{
ui.set_check(::IsWindowVisible(m_hWndStatusBar));
}
- Add the handlers to message map:
BEGIN_MSG_MAP(CMDIFrame)
KETCHUP_CMD_UI_HANDLER(ID_VIEW_STATUS_BAR,
OnUpdateViewStatusBar)
END_MSG_MAP()
- Add CmdUI generators:
virtual BOOL OnIdle()
{
ketchup::update_toolbar_cmd_ui(m_hWnd, m_ToolBar);
return FALSE;
}
BEGIN_MSG_MAP(CMDIFrame)
KETCHUP_UPDATE_MENU_CMD_UI()
END_MSG_MAP()
Ketchup.CmdUI is an event-driven architecture. You can determine the state of UI using the code instead of data. To begin with, CUpdateUI
is inconsistent with Win32.
Basic concepts
CmdUI
A CmdUI
is an object that is derived from ketchup::cmd_ui
.
CmdUI
generator
A CmdUI
generator is anything that generates a CmdUI
and sends it to the window that handles it.
- Dependent
CmdUI
A dependent CmdUI
is any CmdUI
that doesn't have the ID for itself. is_dependent
represents the dependency.
ketchup::cmd_ui
This class is a port of MFC's CCmdUI:
struct cmd_ui
{
UINT get_id() const;
void set_handled(bool on);
bool is_handled() const;
virtual void enable(bool on);
virtual void set_check(int state);
virtual void set_radio(bool on);
virtual void set_text(LPCTSTR pszText);
virtual void set_default(bool on);
virtual bool is_dependent();
};
The semantics of the members is the same as that of CCmdUI, but the default arguments are rejected.
Predefined CmdUI generators
KETCHUP_UPDATE_MENU_CMD_UI
This is an entry of BEGIN_MSG_MAP
that makes a CmdUI
from a menu item.
ketchup::update_toolbar_cmd_ui
This makes a CmdUI
from a toolbar button item.
Nested pop-up menu
A CmdUI
generated from a pop-up menu is a model of the dependent CmdUI
. A pop-up menu cannot have an ID for itself, so it depends on the first menu item. See the MFC's document Nested Pop-up Menus. MFC's syntax is somewhat awkward. The ketchup::cmd_ui
provides a member called is_dependent
:
void OnUpdatePopupSubmenu(ketchup::cmd_ui& ui)
{
if (ui.is_dependent())
ui.enable(m_popup_enabled);
}
Note that all the top-level menu items like the File, Edit and etc. can be made dependent if you use the chevron menu of WTL::CCommandBarCtrl
.
CmdUI chain
The KETCHUP_CHAIN_CLIENT_CMD_UI
and KETCHUP_CHAIN_MDI_CHILD_CMD_UI
are provided. The roles are the same as those of CHAIN_CLIENT_COMMANDS
or CHAIN_MDI_CHILD_COMMANDS
of WTL.
Automatic disabling
Each MDI child has a different set of commands. The MDI framework provides dynamic menus, but what would you do with the toolbar? What would you do if you want to share menus among the MDI children? So KETCHUP_ENABLE_CMD_UI_IF_HANDLED_BY_MDI_CHILD
chains and disables CmdUI
automatically:
typedef ketchup::idset<
ID_BLACK, ID_RED, ID_GREEN, ID_BLUE, ID_WHITE,
ID_CUSTOM, ID_SPEED_SLOW, ID_SPEED_FAST
> child_cmd_ids;
BEGIN_MSG_MAP(CMDIFrame)
KETCHUP_ENABLE_CMD_UI_IF_HANDLED_BY_MDI_CHILD(child_cmd_ids)
END_MSG_MAP()
MFC can disable CCmdUI in response to whether or not WM_COMMAND
is handled, but WTL cannot. So Ketchup.CmdUI disables CmdUI
s in response to whether or not CmdUI
is handled, that is to say, whether an MDI child has KETCHUP_CMD_UI_HANDLER
entry or not. Note that you should specify the target set of commands using ketchup::idset
.
References