void Toolbar::updateAction( unsigned int& index, ItemPtr item, TBBUTTONINFO& info )
{
	unsigned int mask = BTNS_SEP | BTNS_CHECK | BTNS_GROUP | BTNS_CHECKGROUP | BTNS_DROPDOWN;
	if( ( info.fsStyle & mask ) != 0 || // not a push button
		info.idCommand != item->commandID() ) // not for me
	{
		
		TBBUTTON button;
		memset(	&button, 0, sizeof(button) );
		button.iBitmap = getImageIndex( item );
		button.idCommand = item->commandID();
		button.fsState = TBSTATE_ENABLED;
		button.fsStyle = BTNS_BUTTON;
		button.dwData = 0;
		button.iString = -1;

		sendMessage( TB_INSERTBUTTON, index, (LPARAM)&button );
	}
	TBBUTTONINFO buttonInfo = { sizeof( buttonInfo ), TBIF_TEXT | TBIF_BYINDEX | TBIF_STATE };
	std::string tooltip = item->description();	// auto tooltip
	if( item->shortcutKey().size() )
	{
		tooltip += " (";
		tooltip += item->shortcutKey();
		tooltip += ')';
	}
	buttonInfo.pszText = &tooltip[0];
	buttonInfo.fsState = item->update()	&& isEnabled() ?	TBSTATE_ENABLED : 0;
	if ( sendMessage( WM_CGUITOOLBAR_USE_WRAP, 0, 0 ) &&
		( info.fsState & TBSTATE_WRAP ) )
		buttonInfo.fsState |= TBSTATE_WRAP;

	if( forceChanged_ || info.pszText != tooltip || info.fsState != buttonInfo.fsState )
		sendMessage( TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo );
}
void Menu::updateAction( HMENU hmenu, int& index, ItemPtr item, const MENUITEMINFO& info )
{
    if( !info.dwTypeData || buildMenuText( item ) != (LPCTSTR)info.dwTypeData )
        ModifyMenu( hmenu, index, MF_BYPOSITION | MF_STRING, item->commandID(),
                    buildMenuText( item ).c_str() );
    EnableMenuItem( hmenu, index, MF_BYPOSITION | ( item->update() ? MF_ENABLED : MF_GRAYED ) );
}
void Menu::insertMenuItem( HMENU hmenu, int index, ItemPtr item )
{
    MENUITEMINFO info = { sizeof( info ), MIIM_STRING | MIIM_ID | MIIM_DATA, 0, 0,
                          item->commandID(), NULL, NULL, NULL, (DWORD)item.getObject(), "menu"
                        };
    InsertMenuItem( hmenu, index, MF_BYPOSITION, &info );
}
void Menu::updateChoice( HMENU hmenu, int& index, ItemPtr item, const MENUITEMINFO& info )
{
    static const unsigned int MAX_MENU_TEXT = 1024;
    char txtBuf[ MAX_MENU_TEXT + 1 ];
    unsigned int subItemIndex = 0;
    while( index < GetMenuItemCount( hmenu ) )
    {
        MENUITEMINFO info = { sizeof( info ),
                              MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE | MIIM_ID |
                              MIIM_STATE | MIIM_STRING | MIIM_SUBMENU
                            };
        info.cch = MAX_MENU_TEXT;
        info.dwTypeData = txtBuf;
        GetMenuItemInfo( hmenu, index, TRUE, &info );

        if( info.dwItemData != (DWORD)item.getObject() )
            break;

        if( subItemIndex < item->num() )
        {
            ItemPtr subItem = ( *item )[ subItemIndex ];
            if( !info.dwTypeData || buildMenuText( subItem ) != (LPCTSTR)info.dwTypeData )
                insertMenuItem( hmenu, index, item );
            ModifyMenu( hmenu, index, MF_BYPOSITION | MF_STRING, subItem->commandID(),
                        buildMenuText( subItem ).c_str() );
            EnableMenuItem( hmenu, index, MF_BYPOSITION | ( item->update() ? MF_ENABLED : MF_GRAYED ) );
            CheckMenuItem( hmenu, index, MF_BYPOSITION | ( subItem->update() ? MF_CHECKED : MF_UNCHECKED ) );
            ++index;
            ++subItemIndex;
        }
        else
            DeleteMenu( hmenu, index, MF_BYPOSITION );
    }
    while( subItemIndex < item->num() )
    {
        ItemPtr subItem = ( *item )[ subItemIndex ];
        insertMenuItem( hmenu, index, item );
        ModifyMenu( hmenu, index, MF_BYPOSITION | MF_STRING, subItem->commandID(),
                    buildMenuText( subItem ).c_str() );
        EnableMenuItem( hmenu, index, MF_BYPOSITION | ( item->update() ? MF_ENABLED : MF_GRAYED ) );
        CheckMenuItem( hmenu, index, MF_BYPOSITION | ( subItem->update() ? MF_CHECKED : MF_UNCHECKED ) );
        ++subItemIndex;
    }
    --index;
}
void Menu::updateToggle( HMENU hmenu, int& index, ItemPtr item, const MENUITEMINFO& info )
{
    if( !info.dwTypeData || buildMenuText( item ) != (LPCTSTR)info.dwTypeData )
        ModifyMenu( hmenu, index, MF_BYPOSITION | MF_STRING, item->commandID(),
                    buildMenuText( item ).c_str() );
    if( item->num() != 2 )// exactly 2
        throw 1;
    EnableMenuItem( hmenu, index, MF_BYPOSITION | ( item->update() ? MF_ENABLED : MF_GRAYED ) );
    CheckMenuItem( hmenu, index, MF_BYPOSITION | ( ( *item )[ 0 ]->update() ? MF_UNCHECKED : MF_CHECKED ) );
}
void Menu::updateGroup( HMENU hmenu, int& index, ItemPtr item, const MENUITEMINFO& info )
{
    HMENU sub = info.hSubMenu;
    if( !info.dwTypeData || buildMenuText( item ) != (LPCTSTR)info.dwTypeData )
        ModifyMenu( hmenu, index, MF_BYPOSITION | MF_STRING, item->commandID(),
                    buildMenuText( item ).c_str() );
    if( sub == NULL )
    {
        MENUITEMINFO info = { sizeof( info ), MIIM_SUBMENU };
        info.hSubMenu = ( sub = CreateMenu() );
        SetMenuItemInfo( hmenu, index, TRUE, &info );
    }
    EnableMenuItem( hmenu, index, MF_BYPOSITION | ( item->update() ? MF_ENABLED : MF_GRAYED ) );
    changed( sub, item );
}
void Toolbar::updateToggle( unsigned int& index, ItemPtr item, TBBUTTONINFO& info )
{
	if( ( info.fsStyle & BTNS_CHECK ) == 0 || // not a toggle button
		info.idCommand != item->commandID() ) // not for me
	{
		TBBUTTON button;
		memset(	&button, 0, sizeof(button) );
		button.iBitmap = getImageIndex( item );
		button.idCommand = item->commandID();
		button.fsState = TBSTATE_ENABLED;
		button.fsStyle = BTNS_CHECK;
		button.dwData = (DWORD)item.getObject();
		button.iString = -1;
		sendMessage( TB_INSERTBUTTON, index, (LPARAM)&button );
	}
	TBBUTTONINFO buttonInfo = { sizeof( buttonInfo ), TBIF_TEXT | TBIF_BYINDEX | TBIF_STATE };
	std::string tooltip = item->description();	// auto tooltip
	if( item->shortcutKey().size() )
	{
		tooltip += " (";
		tooltip += item->shortcutKey();
		tooltip += ')';
	}
	buttonInfo.pszText = &tooltip[0];
	LPARAM enabled = item->update()	&& isEnabled() ?	TBSTATE_ENABLED : 0;
	LPARAM checked = ( *item )[ 0 ]->update()	?	0 : TBSTATE_CHECKED;
	if( !enabled )
		checked = 0;
	buttonInfo.fsState = (BYTE)( enabled | checked );
	if ( sendMessage( WM_CGUITOOLBAR_USE_WRAP, 0, 0 ) &&
		( info.fsState & TBSTATE_WRAP ) )
		buttonInfo.fsState |= TBSTATE_WRAP;

	if( forceChanged_ || info.pszText != tooltip || info.fsState != buttonInfo.fsState )
		sendMessage( TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo );
}
void Menu::changed( ItemPtr item )
{
    std::stack<HMENU> menus;
    menus.push( hmenu_ );
    bool found = false;
    while( !menus.empty() )
    {
        HMENU hmenu = menus.top();
        menus.pop();
        MENUITEMINFO info = { sizeof( info ), MIIM_ID | MIIM_DATA };
        for( int i = 0; i < GetMenuItemCount( hmenu ); ++i )
            if( GetMenuItemInfo( hmenu, i, TRUE, &info ) &&
                    info.wID == item->commandID() &&
                    ( Item* )info.dwItemData == item )
                changed( hmenu, item );
    }
    if( !found )// we'll do a full refresh
        changed( hmenu_, rootItem() );
}
void Toolbar::updateExpandedChoice( unsigned int& index, ItemPtr item, TBBUTTONINFO& info )
{
	struct PatchDisplayName
	{
		std::string operator()( std::string name )
		{
			std::string::iterator iter = name.begin();
			while( iter != name.end() )
			{
				if( *iter == '&' )
					name.erase( iter );
				else
					++iter;
			}
			return name;
		}
	}
	PatchDisplayName;
	HWND combobox = (HWND)info.lParam;
	if( ( info.fsStyle & BTNS_SEP ) == 0 || // not a place holder
		info.lParam == 0 ) // do not contain a combo box
	{
		int width = item->exist( "width" )	?	atoi( (*item)[ "width" ].c_str() )	:
												150;
		combobox = CreateWindow( "COMBOBOX", "",
			CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE,
			0, 0, width, 1,
			toolbar_, NULL, GetModuleHandle( NULL ), NULL );
		SendMessage( combobox, WM_SETFONT,
			SendMessage( toolbar_, WM_GETFONT, 0, 0 ),
			FALSE );
		TBBUTTON button;
		memset(	&button, 0, sizeof(button) );
		button.iBitmap = width; // width of the combobox, MUST be configurable in future
		button.idCommand = 0;
		button.fsState = 0;
		button.fsStyle = BTNS_SEP;
		button.dwData = (DWORD)combobox;
		button.iString = -1;

		sendMessage( TB_INSERTBUTTON, index, (LPARAM)&button );
	}
	{// set the position & size
		RECT rectButton, rectWin;
		sendMessage( TB_GETITEMRECT, index, (LPARAM)&rectButton );
		GetWindowRect( combobox, &rectWin );
		ScreenToClient( toolbar_, (POINT*)&rectWin );
		MoveWindow( combobox, rectButton.left, rectButton.top,
			rectButton.right - rectButton.left, ( rectButton.bottom - rectButton.top ) * 20, TRUE );
	}
	int comboIndex = 0;
	unsigned int subitemIndex = 0;
	while( comboIndex < SendMessage( combobox, CB_GETCOUNT, 0, 0 ) &&
		subitemIndex < item->num() )
	{
		ItemPtr subitem = (*item)[ subitemIndex ];
		char* buffer = (char*)_alloca( SendMessage( combobox, CB_GETLBTEXTLEN, comboIndex, 0 ) + 1 );
		SendMessage( combobox, CB_GETLBTEXT, comboIndex, (LPARAM)buffer );
		if( SendMessage( combobox, CB_GETITEMDATA, comboIndex, 0 ) != subitem->commandID() ||
			buffer != subitem->displayName() )
		{
			SendMessage( combobox, CB_INSERTSTRING, comboIndex,
				(LPARAM)PatchDisplayName( subitem->displayName() ).c_str() );
			SendMessage( combobox, CB_SETITEMDATA, comboIndex, subitem->commandID() );
		}
		if( subitem->update() && !SendMessage( combobox, CB_GETDROPPEDSTATE, 0, 0 ) )
			SendMessage( combobox, CB_SETCURSEL, comboIndex, 0 );
		++comboIndex;
		++subitemIndex;
	}
	while( comboIndex < SendMessage( combobox, CB_GETCOUNT, 0, 0 ) )
		SendMessage( combobox, CB_DELETESTRING, comboIndex, 0 );
	while( subitemIndex < item->num() )
	{
		ItemPtr subitem = (*item)[ subitemIndex ];
		SendMessage( combobox, CB_INSERTSTRING, comboIndex,
			(LPARAM)PatchDisplayName( subitem->displayName() ).c_str() );
		SendMessage( combobox, CB_SETITEMDATA, comboIndex, subitem->commandID() );
		if( subitem->update()  && !SendMessage( combobox, CB_GETDROPPEDSTATE, 0, 0 ) )
			SendMessage( combobox, CB_SETCURSEL, comboIndex, 0 );
		++comboIndex;
		++subitemIndex;
	}
	EnableWindow( combobox, item->update() && item->num() && isEnabled() );
}
void Toolbar::updateChoice( unsigned int& index, ItemPtr item, TBBUTTONINFO& info )
{
	unsigned int i = 0;
	while( index < (unsigned int)sendMessage( TB_BUTTONCOUNT, 0, 0 ) &&
		i < item->num() )
	{
		static const unsigned int MAX_MENU_TEXT = 1024;
		char txtBuf[ MAX_MENU_TEXT + 1 ];

		TBBUTTONINFO info = { sizeof( info ), TBIF_BYINDEX | TBIF_COMMAND | TBIF_IMAGE
			| TBIF_LPARAM | TBIF_SIZE | TBIF_STATE | TBIF_STYLE | TBIF_TEXT };
		info.pszText = txtBuf;
		info.cchText = MAX_MENU_TEXT;

		sendMessage( TB_GETBUTTONINFO, index, (LPARAM)&info );

		ItemPtr subItem = (*item)[ i ];

		if( ( info.fsStyle & BTNS_CHECK ) == 0 || // not a toggle button
			info.idCommand != subItem->commandID() ) // not for me
		{
			TBBUTTON button;
			memset(	&button, 0, sizeof(button) );
			button.iBitmap = getImageIndex( subItem );
			button.idCommand = subItem->commandID();
			button.fsState = TBSTATE_ENABLED;
			button.fsStyle = BTNS_CHECK;
			button.dwData = 0;
			button.iString = -1;
			sendMessage( TB_INSERTBUTTON, index, (LPARAM)&button );
		}
		TBBUTTONINFO buttonInfo = { sizeof( buttonInfo ), TBIF_TEXT | TBIF_BYINDEX | TBIF_STATE };
		std::string tooltip = subItem->description();	// auto tooltip
		if( subItem->shortcutKey().size() || item->shortcutKey().size() )
		{
			if (subItem->shortcutKey().size() && item->shortcutKey().size())
			{
				tooltip = 
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM0", 
						subItem->description(),
						subItem->shortcutKey(),
						item->shortcutKey()
					);
			}
			else if (subItem->shortcutKey().size())
			{
				tooltip =
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM1",
						subItem->description(),
						subItem->shortcutKey()
					);
			}
			else if (item->shortcutKey().size())
			{
				tooltip =
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM2",
						subItem->description(),
						item->shortcutKey()
					);
			}
		}
		buttonInfo.pszText = &tooltip[0];
		LPARAM enabled = item->update()	&& isEnabled() ?	TBSTATE_ENABLED : 0;
		LPARAM checked = subItem->update()	?	TBSTATE_CHECKED : 0;
		if( !enabled )
			checked = 0;
		buttonInfo.fsState = (BYTE)( enabled | checked );
		if ( sendMessage( WM_CGUITOOLBAR_USE_WRAP, 0, 0 ) &&
			( info.fsState & TBSTATE_WRAP ) )
			buttonInfo.fsState |= TBSTATE_WRAP;

		if( forceChanged_ || info.pszText != tooltip || info.fsState != buttonInfo.fsState )
			sendMessage( TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo );
		++i;
		++index;
	}
	while( i < item->num() )
	{
		ItemPtr subItem = (*item)[ i ];

		TBBUTTON button;
		memset(	&button, 0, sizeof(button) );
		button.iBitmap = getImageIndex( subItem );
		button.idCommand = subItem->commandID();
		button.fsState = TBSTATE_ENABLED;
		button.fsStyle = BTNS_CHECK;
		button.dwData = 0;
		button.iString = -1;
		sendMessage( TB_INSERTBUTTON, index, (LPARAM)&button );

		TBBUTTONINFO buttonInfo = { sizeof( buttonInfo ), TBIF_TEXT | TBIF_BYINDEX | TBIF_STATE };
		std::string tooltip = subItem->description();	// auto tooltip
		if( subItem->shortcutKey().size() || item->shortcutKey().size() )
		{
			if (subItem->shortcutKey().size() && item->shortcutKey().size())
			{
				tooltip = 
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM0", 
						subItem->description(),
						subItem->shortcutKey(),
						item->shortcutKey()
					);
			}
			else if (subItem->shortcutKey().size())
			{
				tooltip =
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM1",
						subItem->description(),
						subItem->shortcutKey()
					);
			}
			else if (item->shortcutKey().size())
			{
				tooltip =
					L
					(
						"GUIMANAGER/GUI_TOOLBAR/TOOLTIP_FORM2",
						subItem->description(),
						item->shortcutKey()
					);
			}
		}
		buttonInfo.pszText = &tooltip[0];
		LPARAM enabled = item->update()	&& isEnabled() ?	TBSTATE_ENABLED : 0;
		LPARAM checked = subItem->update()	?	TBSTATE_CHECKED : 0;
		if( !enabled )
			checked = 0;
		buttonInfo.fsState = (BYTE)( enabled | checked );
		if ( sendMessage( WM_CGUITOOLBAR_USE_WRAP, 0, 0 ) &&
			( info.fsState & TBSTATE_WRAP ) )
			buttonInfo.fsState |= TBSTATE_WRAP;

		if( forceChanged_ || info.pszText != tooltip || info.fsState != buttonInfo.fsState )
			sendMessage( TB_SETBUTTONINFO, index, (LPARAM)&buttonInfo );
		++i;
		++index;
	}
	--index;
}