void WIDGET_HOTKEY_LIST::UpdateFromClientData()
{
    for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
    {
        WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( i );

        if( hkdata )
        {
            const auto& changed_hk = hkdata->GetChangedHotkey();
            const EDA_HOTKEY& hk = changed_hk.GetCurrentValue();

            wxString key_text = KeyNameFromKeyCode( hk.m_KeyCode );

            // mark unsaved changes
            if( changed_hk.HasUnsavedChange() )
                key_text += " *";

            SetItemText( i, 0, wxGetTranslation( hk.m_InfoMsg ) );
            SetItemText( i, 1, key_text);
        }
    }

    // Trigger a resize in case column widths have changed
    wxSizeEvent dummy_evt;
    TWO_COLUMN_TREE_LIST::OnSize( dummy_evt );
}
bool WIDGET_HOTKEY_LIST::TransferDataFromControl()
{
    for( size_t sec_index = 0; sec_index < m_sections.size(); ++sec_index )
    {
        EDA_HOTKEY_CONFIG* section = m_sections[sec_index].m_section;

        for( EDA_HOTKEY** info_ptr = section->m_HK_InfoList; *info_ptr; ++info_ptr )
        {
            EDA_HOTKEY* info = *info_ptr;

            for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) )
            {
                WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item );

                if( !hkdata )
                    continue;

                EDA_HOTKEY& hk = hkdata->GetHotkey();

                if( hk.m_Idcommand == info->m_Idcommand )
                {
                    info->m_KeyCode = hk.m_KeyCode;
                    break;
                }
            }
        }
    }

    return true;
}
void WIDGET_HOTKEY_LIST::ResetItem( wxTreeListItem aItem )
{
    WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( aItem );
    EDA_HOTKEY* hk = &hkdata->GetHotkey();

    for( size_t sec_index = 0; sec_index < m_sections.size(); ++sec_index )
    {
        wxString& section_tag = *( m_sections[sec_index].m_section->m_SectionTag );

        if( section_tag != hkdata->GetSectionTag() )
            continue;

        HOTKEY_LIST& each_list = m_hotkeys[sec_index];
        HOTKEY_LIST::iterator hk_it;

        for( hk_it = each_list.begin(); hk_it != each_list.end(); ++hk_it )
        {
            if( hk_it->m_Idcommand == hk->m_Idcommand )
            {
                hk->m_KeyCode = hk_it->m_KeyCode;
                break;
            }
        }
    }

    UpdateFromClientData();
}
void WIDGET_HOTKEY_LIST::ResetItemToDefault( wxTreeListItem aItem )
{
    WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( aItem );
    EDA_HOTKEY* hk = &hkdata->GetHotkey();
    hk->ResetKeyCodeToDefault();
    UpdateFromClientData();
}
WIDGET_HOTKEY_CLIENT_DATA* WIDGET_HOTKEY_LIST::getExpectedHkClientData( wxTreeListItem aItem )
{
    const auto hkdata = GetHKClientData( aItem );

    // This probably means a hotkey-only action is being attempted on
    // a row that is not a hotkey (like a section heading)
    wxASSERT_MSG( hkdata != nullptr, "No hotkey data found for list item" );

    return hkdata;
}
bool WIDGET_HOTKEY_LIST::CheckKeyConflicts( long aKey, const wxString& aSectionTag,
        EDA_HOTKEY** aConfKey, EDA_HOTKEY_CONFIG** aConfSect )
{
    EDA_HOTKEY* conflicting_key = NULL;
    struct EDA_HOTKEY_CONFIG* conflicting_section = NULL;

    for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) )
    {
        WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item );

        if( !hkdata )
            continue;

        EDA_HOTKEY& hk = hkdata->GetHotkey();
        wxString tag = hkdata->GetSectionTag();

        if( aSectionTag != g_CommonSectionTag
            && tag != g_CommonSectionTag
            && tag != aSectionTag )
        {
            // This key and its conflict candidate are in orthogonal sections, so skip.
            continue;
        }

        if( aKey == hk.m_KeyCode )
        {
            conflicting_key = &hk;

            // Find the section
            HOTKEY_SECTIONS::iterator it;

            for( it = m_sections.begin(); it != m_sections.end(); ++it )
            {
                if( *it->m_section->m_SectionTag == tag )
                {
                    conflicting_section = it->m_section;
                    break;
                }
            }
        }
    }

    // Write the outparams
    if( aConfKey )
        *aConfKey = conflicting_key;

    if( aConfSect )
        *aConfSect = conflicting_section;

    return conflicting_key == NULL;
}
void WIDGET_HOTKEY_LIST::UpdateFromClientData()
{
    for( wxTreeListItem i = GetFirstItem(); i.IsOk(); i = GetNextItem( i ) )
    {
        WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( i );

        if( hkdata )
        {
            EDA_HOTKEY& hk = hkdata->GetHotkey();

            SetItemText( i, 0, wxGetTranslation( hk.m_InfoMsg ) );
            SetItemText( i, 1, KeyNameFromKeyCode( hk.m_KeyCode ) );
        }
    }
}
bool WIDGET_HOTKEY_LIST::TransferDefaultsToControl()
{
    Freeze();

    for( wxTreeListItem item = GetFirstItem(); item.IsOk(); item = GetNextItem( item ) )
    {
        WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( item );
        if( hkdata == NULL)
            continue;

        hkdata->GetHotkey().ResetKeyCodeToDefault();
    }

    UpdateFromClientData();
    Thaw();

    return true;
}
void WIDGET_HOTKEY_LIST::EditItem( wxTreeListItem aItem )
{
    WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( aItem );

    if( !hkdata )
    {
        // Activated item was not a hotkey row
        return;
    }

    wxString    name = GetItemText( aItem, 0 );
    wxString    current_key = GetItemText( aItem, 1 );

    wxKeyEvent key_event = HK_PROMPT_DIALOG::PromptForKey( GetParent(), name, current_key );
    long key = MapKeypressToKeycode( key_event );

    if( hkdata && key )
    {
        // See if this key code is handled in hotkeys names list
        bool exists;
        KeyNameFromKeyCode( key, &exists );

        if( exists && hkdata->GetHotkey().m_KeyCode != key )
        {
            wxString tag = hkdata->GetSectionTag();
            bool canUpdate = ResolveKeyConflicts( key, tag );

            if( canUpdate )
            {
                hkdata->GetHotkey().m_KeyCode = key;
            }
        }

        UpdateFromClientData();

        // Trigger a resize in case column widths have changed
        wxSizeEvent dummy_evt;
        OnSize( dummy_evt );
    }
}
void WIDGET_HOTKEY_LIST::OnContextMenu( wxTreeListEvent& aEvent )
{
    // Save the active event for use in OnMenu
    m_context_menu_item = aEvent.GetItem();

    wxMenu menu;

    WIDGET_HOTKEY_CLIENT_DATA* hkdata = GetHKClientData( m_context_menu_item );

    // Some actions only apply if the row is hotkey data
    if( hkdata )
    {
        menu.Append( ID_EDIT, _( "Edit..." ) );
        menu.Append( ID_RESET, _( "Undo Changes" ) );
        menu.Append( ID_DEFAULT, _( "Restore Default" ) );
        menu.Append( wxID_SEPARATOR );
    }

    menu.Append( ID_RESET_ALL, _( "Undo All Changes" ) );
    menu.Append( ID_DEFAULT_ALL, _( "Restore All to Default" ) );

    PopupMenu( &menu );
}
WIDGET_HOTKEY_CLIENT_DATA* WIDGET_HOTKEY_LIST::GetSelHKClientData()
{
    return GetHKClientData( GetSelection() );
}