You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
6.6 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013 CERN
  5. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <tool/tool_event.h>
  25. #include <tool/tool_manager.h>
  26. #include <tool/tool_interactive.h>
  27. #include <tool/context_menu.h>
  28. #include <boost/bind.hpp>
  29. #include <cassert>
  30. CONTEXT_MENU::CONTEXT_MENU() :
  31. m_titleSet( false ), m_selected( -1 ), m_tool( NULL )
  32. {
  33. setCustomEventHandler( boost::bind( &CONTEXT_MENU::handleCustomEvent, this, _1 ) );
  34. setupEvents();
  35. }
  36. CONTEXT_MENU::CONTEXT_MENU( const CONTEXT_MENU& aMenu ) :
  37. m_titleSet( aMenu.m_titleSet ), m_selected( -1 ), m_tool( aMenu.m_tool ),
  38. m_toolActions( aMenu.m_toolActions ), m_customHandler( aMenu.m_customHandler )
  39. {
  40. // Copy all the menu entries
  41. for( unsigned i = 0; i < aMenu.GetMenuItemCount(); ++i )
  42. {
  43. wxMenuItem* item = aMenu.FindItemByPosition( i );
  44. if( item->IsSubMenu() )
  45. {
  46. #ifdef DEBUG
  47. // Submenus of a CONTEXT_MENU are supposed to be CONTEXT_MENUs as well
  48. assert( dynamic_cast<CONTEXT_MENU*>( item->GetSubMenu() ) );
  49. #endif
  50. CONTEXT_MENU* menu = new CONTEXT_MENU( static_cast<const CONTEXT_MENU&>( *item->GetSubMenu() ) );
  51. AppendSubMenu( menu, item->GetItemLabel(), wxEmptyString );
  52. }
  53. else
  54. {
  55. wxMenuItem* newItem = new wxMenuItem( this, item->GetId(), item->GetItemLabel(),
  56. wxEmptyString, item->GetKind() );
  57. Append( newItem );
  58. copyItem( item, newItem );
  59. }
  60. }
  61. setupEvents();
  62. }
  63. void CONTEXT_MENU::setupEvents()
  64. {
  65. Connect( wxEVT_MENU_HIGHLIGHT, wxEventHandler( CONTEXT_MENU::onMenuEvent ),
  66. NULL, this );
  67. Connect( wxEVT_COMMAND_MENU_SELECTED, wxEventHandler( CONTEXT_MENU::onMenuEvent ),
  68. NULL, this );
  69. // Workaround for the case when mouse cursor never reaches menu (it hangs up tools using menu)
  70. wxMenuEvent menuEvent( wxEVT_MENU_HIGHLIGHT, -1, this );
  71. AddPendingEvent( menuEvent );
  72. }
  73. void CONTEXT_MENU::SetTitle( const wxString& aTitle )
  74. {
  75. // TODO handle an empty string (remove title and separator)
  76. // Unfortunately wxMenu::SetTitle() does nothing.. (at least wxGTK)
  77. if( m_titleSet )
  78. {
  79. FindItemByPosition( 0 )->SetItemLabel( aTitle );
  80. }
  81. else
  82. {
  83. InsertSeparator( 0 );
  84. Insert( 0, new wxMenuItem( this, -1, aTitle, wxEmptyString, wxITEM_NORMAL ) );
  85. m_titleSet = true;
  86. }
  87. }
  88. void CONTEXT_MENU::Add( const wxString& aLabel, int aId )
  89. {
  90. #ifdef DEBUG
  91. if( FindItem( aId ) != NULL )
  92. wxLogWarning( wxT( "Adding more than one menu entry with the same ID may result in"
  93. "undefined behaviour" ) );
  94. #endif
  95. Append( new wxMenuItem( this, aId, aLabel, wxEmptyString, wxITEM_NORMAL ) );
  96. }
  97. void CONTEXT_MENU::Add( const TOOL_ACTION& aAction )
  98. {
  99. /// ID numbers for tool actions need to have a value higher than m_actionId
  100. int id = m_actionId + aAction.GetId();
  101. wxMenuItem* item = new wxMenuItem( this, id,
  102. wxString( aAction.GetMenuItem().c_str(), wxConvUTF8 ),
  103. wxString( aAction.GetDescription().c_str(), wxConvUTF8 ), wxITEM_NORMAL );
  104. if( aAction.HasHotKey() )
  105. {
  106. int key = aAction.GetHotKey() & ~MD_MODIFIER_MASK;
  107. int mod = aAction.GetHotKey() & MD_MODIFIER_MASK;
  108. int flags = wxACCEL_NORMAL;
  109. switch( mod )
  110. {
  111. case MD_ALT: flags = wxACCEL_ALT; break;
  112. case MD_CTRL: flags = wxACCEL_CTRL; break;
  113. case MD_SHIFT: flags = wxACCEL_SHIFT; break;
  114. }
  115. wxAcceleratorEntry accel( flags, key, id, item );
  116. item->SetAccel( &accel );
  117. }
  118. Append( item );
  119. m_toolActions[id] = &aAction;
  120. }
  121. void CONTEXT_MENU::Clear()
  122. {
  123. m_titleSet = false;
  124. // Remove all the entries from context menu
  125. for( unsigned i = 0; i < GetMenuItemCount(); ++i )
  126. Destroy( FindItemByPosition( 0 ) );
  127. m_toolActions.clear();
  128. }
  129. void CONTEXT_MENU::onMenuEvent( wxEvent& aEvent )
  130. {
  131. TOOL_EVENT evt;
  132. wxEventType type = aEvent.GetEventType();
  133. // When the currently chosen item in the menu is changed, an update event is issued.
  134. // For example, the selection tool can use this to dynamically highlight the current item
  135. // from selection clarification popup.
  136. if( type == wxEVT_MENU_HIGHLIGHT )
  137. evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_UPDATE, aEvent.GetId() );
  138. // One of menu entries was selected..
  139. else if( type == wxEVT_COMMAND_MENU_SELECTED )
  140. {
  141. // Store the selected position
  142. m_selected = aEvent.GetId();
  143. // Check if there is a TOOL_ACTION for the given ID
  144. if( m_toolActions.count( aEvent.GetId() ) == 1 )
  145. {
  146. evt = m_toolActions[aEvent.GetId()]->MakeEvent();
  147. }
  148. else
  149. {
  150. OPT_TOOL_EVENT custom = m_customHandler( aEvent );
  151. if( custom )
  152. evt = *custom;
  153. else
  154. {
  155. // Handling non-action menu entries (e.g. items in clarification list)
  156. evt = TOOL_EVENT( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, aEvent.GetId() );
  157. }
  158. }
  159. }
  160. // forward the action/update event to the TOOL_MANAGER
  161. TOOL_MANAGER::Instance().ProcessEvent( evt );
  162. }
  163. void CONTEXT_MENU::copyItem( const wxMenuItem* aSource, wxMenuItem* aDest ) const
  164. {
  165. assert( !aSource->IsSubMenu() ); // it does not transfer submenus
  166. aDest->SetKind( aSource->GetKind() );
  167. aDest->SetHelp( aSource->GetHelp() );
  168. aDest->Enable( aSource->IsEnabled() );
  169. if( aSource->IsCheckable() )
  170. aDest->Check( aSource->IsChecked() );
  171. if( aSource->GetKind() == wxITEM_NORMAL )
  172. aDest->SetBitmap( aSource->GetBitmap() );
  173. }