action_manager.cpp 5.42 KB
Newer Older
Maciej Suminski's avatar
Maciej Suminski committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2013 CERN
 * @author Maciej Suminski <maciej.suminski@cern.ch>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <tool/action_manager.h>
#include <tool/tool_manager.h>
#include <tool/tool_event.h>
#include <tool/tool_action.h>
29
#include <boost/foreach.hpp>
30
#include <cassert>
Maciej Suminski's avatar
Maciej Suminski committed
31 32 33 34 35 36 37

ACTION_MANAGER::ACTION_MANAGER( TOOL_MANAGER* aToolManager ) :
    m_toolMgr( aToolManager )
{
}


38 39 40 41 42 43 44
ACTION_MANAGER::~ACTION_MANAGER()
{
    while( !m_actionIdIndex.empty() )
        UnregisterAction( m_actionIdIndex.begin()->second );
}


Maciej Suminski's avatar
Maciej Suminski committed
45 46
void ACTION_MANAGER::RegisterAction( TOOL_ACTION* aAction )
{
47 48 49 50
    // TOOL_ACTIONs are supposed to be named [appName.]toolName.actionName (with dots between)
    // action name without specifying at least toolName is not valid
    assert( aAction->GetName().find( '.', 0 ) != std::string::npos );

51
    // TOOL_ACTIONs must have unique names & ids
52 53
    assert( m_actionNameIndex.find( aAction->m_name ) == m_actionNameIndex.end() );
    assert( m_actionIdIndex.find( aAction->m_id ) == m_actionIdIndex.end() );
54

55 56
    if( aAction->m_id == -1 )
        aAction->m_id = MakeActionId( aAction->m_name );
Maciej Suminski's avatar
Maciej Suminski committed
57 58 59 60 61

    m_actionNameIndex[aAction->m_name] = aAction;
    m_actionIdIndex[aAction->m_id] = aAction;

    if( aAction->HasHotKey() )
62
        m_actionHotKeys[aAction->m_currentHotKey].push_back( aAction );
Maciej Suminski's avatar
Maciej Suminski committed
63 64 65 66 67
}


void ACTION_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
{
68 69 70
    m_actionNameIndex.erase( aAction->m_name );
    m_actionIdIndex.erase( aAction->m_id );

Maciej Suminski's avatar
Maciej Suminski committed
71
    if( aAction->HasHotKey() )
72 73 74 75 76 77 78 79 80
    {
        std::list<TOOL_ACTION*>& actions = m_actionHotKeys[aAction->m_currentHotKey];
        std::list<TOOL_ACTION*>::iterator action = std::find( actions.begin(), actions.end(), aAction );

        if( action != actions.end() )
            actions.erase( action );
        else
            assert( false );
    }
Maciej Suminski's avatar
Maciej Suminski committed
81 82 83 84 85
}


int ACTION_MANAGER::MakeActionId( const std::string& aActionName )
{
86
    static int currentActionId = 1;
87

Maciej Suminski's avatar
Maciej Suminski committed
88 89 90 91 92 93 94 95 96
    return currentActionId++;
}


bool ACTION_MANAGER::RunAction( const std::string& aActionName ) const
{
    std::map<std::string, TOOL_ACTION*>::const_iterator it = m_actionNameIndex.find( aActionName );

    if( it == m_actionNameIndex.end() )
97
        return false; // no action with given name found
Maciej Suminski's avatar
Maciej Suminski committed
98

99
    RunAction( it->second );
Maciej Suminski's avatar
Maciej Suminski committed
100 101 102 103 104

    return true;
}


105 106 107 108 109 110 111 112
void ACTION_MANAGER::RunAction( const TOOL_ACTION* aAction ) const
{
    TOOL_EVENT event = aAction->MakeEvent();

    m_toolMgr->ProcessEvent( event );
}


Maciej Suminski's avatar
Maciej Suminski committed
113 114
bool ACTION_MANAGER::RunHotKey( int aHotKey ) const
{
115 116
    int key = std::toupper( aHotKey & ~MD_MODIFIER_MASK );
    int mod = aHotKey & MD_MODIFIER_MASK;
Maciej Suminski's avatar
Maciej Suminski committed
117

118 119 120 121 122 123
    HOTKEY_LIST::const_iterator it = m_actionHotKeys.find( key | mod );

    // If no luck, try without modifier, to handle keys that require a modifier
    // e.g. to get ? you need to press Shift+/ without US keyboard layout
    // Hardcoding ? as Shift+/ is a bad idea, as on another layout you may need to press a
    // different combination
Maciej Suminski's avatar
Maciej Suminski committed
124
    if( it == m_actionHotKeys.end() )
125 126 127 128 129 130
    {
        it = m_actionHotKeys.find( key );

        if( it == m_actionHotKeys.end() )
            return false; // no appropriate action found for the hotkey
    }
Maciej Suminski's avatar
Maciej Suminski committed
131

132
    const std::list<TOOL_ACTION*>& actions = it->second;
Maciej Suminski's avatar
Maciej Suminski committed
133

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    // Choose the action that has the highest priority on the active tools stack
    // If there is none, run the global action associated with the hot key
    int highestPriority = -1, priority = -1;
    const TOOL_ACTION* context = NULL;  // pointer to context action of the highest priority tool
    const TOOL_ACTION* global = NULL;   // pointer to global action, if there is no context action

    BOOST_FOREACH( const TOOL_ACTION* action, actions )
    {
        if( action->GetScope() == AS_GLOBAL )
        {
            // Store the global action for the hot key in case there was no possible
            // context actions to run
            assert( global == NULL );       // there should be only one global action per hot key
            global = action;

            continue;
        }

        TOOL_BASE* tool = m_toolMgr->FindTool( action->GetToolName() );

        if( tool )
        {
            priority = m_toolMgr->GetPriority( tool->GetId() );

            if( priority >= 0 && priority > highestPriority )
            {
                highestPriority = priority;
                context = action;
            }
        }
    }
Maciej Suminski's avatar
Maciej Suminski committed
165

166 167
    if( !global && !context )   // currently there is no valid action to run
        return false;
Maciej Suminski's avatar
Maciej Suminski committed
168

169 170 171 172 173 174
    if( context )
        RunAction( context );
    else if( global )
        RunAction( global );

    return true;
175
}