KiCad PCB EDA Suite
conditional_menu.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2019 CERN
5  * Copyright (C) 2015-2019 KiCad Developers, see CHANGELOG.txt for contributors.
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <bitmaps.h>
27 #include <tool/conditional_menu.h>
28 #include <tool/action_menu.h>
29 #include <menus_helpers.h>
30 #include <kiface_base.h>
31 
32 
34  ACTION_MENU( true, aTool )
35 {
36 }
37 
38 
40 {
42  clone->m_entries = m_entries;
43  return clone;
44 }
45 
46 
47 void CONDITIONAL_MENU::AddItem( const TOOL_ACTION& aAction, const SELECTION_CONDITION& aCondition,
48  int aOrder )
49 {
50  wxASSERT( aAction.GetId() > 0 ); // Check if action was previously registered in ACTION_MANAGER
51  addEntry( ENTRY( &aAction, aCondition, aOrder, false ) );
52 }
53 
54 
56  const SELECTION_CONDITION& aCondition, int aOrder )
57 {
58  wxASSERT( aAction.GetId() > 0 ); // Check if action was previously registered in ACTION_MANAGER
59  addEntry( ENTRY( &aAction, aCondition, aOrder, true ) );
60 }
61 
62 
63 void CONDITIONAL_MENU::AddItem( int aId, const wxString& aText, const wxString& aTooltip,
64  BITMAPS aIcon, const SELECTION_CONDITION& aCondition,
65  int aOrder )
66 {
67  wxMenuItem item( nullptr, aId, aText, aTooltip, wxITEM_NORMAL );
68 
69  if( !!aIcon )
70  AddBitmapToMenuItem( &item, KiBitmap( aIcon ) );
71 
72  addEntry( ENTRY( item, aIcon, aCondition, aOrder, false ) );
73 }
74 
75 
76 void CONDITIONAL_MENU::AddCheckItem( int aId, const wxString& aText, const wxString& aTooltip,
77  BITMAPS aIcon, const SELECTION_CONDITION& aCondition,
78  int aOrder )
79 {
80  wxMenuItem item( nullptr, aId, aText, aTooltip, wxITEM_CHECK );
81 
82  if( !!aIcon )
83  AddBitmapToMenuItem( &item, KiBitmap( aIcon ) );
84 
85  addEntry( ENTRY( item, aIcon, aCondition, aOrder, true ) );
86 }
87 
88 
90  int aOrder )
91 {
92  addEntry( ENTRY( aMenu, aCondition, aOrder ) );
93 }
94 
95 
97 {
99 }
100 
101 
103 
104 
106 {
108  UpdateAll();
109 
110  runOnSubmenus( [] ( ACTION_MENU* aMenu ) {
111  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
112 
113  if( conditionalMenu )
114  conditionalMenu->Resolve();
115  } );
116 }
117 
118 
120 {
121  Clear();
122 
123  // We try to avoid adding useless separators (when no menuitems between separators)
124  int menu_count = 0; // number of menus since the latest separator
125 
126  for( const ENTRY& entry : m_entries )
127  {
128  const SELECTION_CONDITION& cond = entry.Condition();
129  bool result;
130  wxMenuItem* menuItem = nullptr;
131 
132  try
133  {
134  result = cond( aSelection );
135  }
136  catch( std::exception& )
137  {
138  continue;
139  }
140 
141  if( !result )
142  continue;
143 
144  switch( entry.Type() )
145  {
146  case ENTRY::ACTION:
147  menuItem = Add( *entry.Action(), entry.IsCheckmarkEntry() );
148  menu_count++;
149  break;
150 
151  case ENTRY::MENU:
152  menuItem = Add( entry.Menu() );
153  menu_count++;
154  break;
155 
156  case ENTRY::WXITEM:
157  menuItem = new wxMenuItem( this,
158  entry.wxItem()->GetId(),
159  entry.wxItem()->GetItemLabel(),
160  entry.wxItem()->GetHelp(),
161  entry.wxItem()->GetKind() );
162 
163  if( !!entry.GetIcon() )
164  AddBitmapToMenuItem( menuItem, KiBitmap( entry.GetIcon() ) );
165 
166  // the wxMenuItem must be append only after the bitmap is set:
167  Append( menuItem );
168 
169  menu_count++;
170  break;
171 
172  case ENTRY::SEPARATOR:
173  if( menu_count )
174  menuItem = AppendSeparator();
175 
176  menu_count = 0;
177  break;
178 
179  default:
180  wxASSERT( false );
181  break;
182  }
183  }
184 
185  // Recursively call Evaluate on all the submenus that are CONDITIONAL_MENUs to ensure
186  // they are updated. This is also required on GTK to make sure the menus have the proper
187  // size when created.
189  [&aSelection]( ACTION_MENU* aMenu )
190  {
191  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
192 
193  if( conditionalMenu )
194  conditionalMenu->Evaluate( aSelection );
195  } );
196 }
197 
198 
200 {
201  if( aEntry.Order() < 0 ) // Any order, so give it any order number
202  aEntry.SetOrder( m_entries.size() );
203 
204  std::list<ENTRY>::iterator it = m_entries.begin();
205 
206  // Find the right spot for the entry
207  while( it != m_entries.end() && it->Order() <= aEntry.Order() )
208  ++it;
209 
210  m_entries.insert( it, aEntry );
211 }
212 
214 {
215  m_type = aEntry.m_type;
216  m_icon = aEntry.m_icon;
217 
218  switch( aEntry.m_type )
219  {
220  case ACTION:
221  m_data.action = aEntry.m_data.action;
222  break;
223  case MENU:
224  m_data.menu = aEntry.m_data.menu;
225  break;
226  case WXITEM:
227  // We own the wxItem, so we need to make a new one for the new object
228  m_data.wxItem = new wxMenuItem( nullptr,
229  aEntry.m_data.wxItem->GetId(),
230  aEntry.m_data.wxItem->GetItemLabel(),
231  aEntry.m_data.wxItem->GetHelp(),
232  aEntry.m_data.wxItem->GetKind() );
233  break;
234  case SEPARATOR:
235  break; //No data to copy
236  }
237  m_condition = aEntry.m_condition;
238  m_order = aEntry.m_order;
240 }
241 
243 {
244  if( WXITEM == m_type )
245  delete m_data.wxItem;
246 }
247 
ACTION_MENU * create() const override
< Return an instance of this class. It has to be overridden in inheriting classes.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
< Helper class to organize menu entries. Inserts the entry, preserving the requested order.
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
const TOOL_ACTION * action
SELECTION g_resolveDummySelection
std::list< ENTRY > m_entries
union CONDITIONAL_MENU::ENTRY::@31 m_data
Condition to be fulfilled to show the entry in menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void UpdateAll()
Run update handlers for the menu and its submenus.
SELECTION_CONDITION m_condition
Order number, the higher the number the lower position it takes it is in the menu.
void SetOrder(int aOrder)
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:243
~ENTRY()
Possible entry types.
void Evaluate(SELECTION &aSelection)
Update the contents of the menu based on the supplied conditions.
std::function< bool(const SELECTION &)> SELECTION_CONDITION
< Functor type that checks a specific condition for selected items.
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
TOOL_INTERACTIVE * m_tool
Associates tool actions with menu item IDs. Non-owning.
Definition: action_menu.h:260
void Resolve()
Update the initial contents so that wxWidgets doesn't get its knickers tied in a knot over the menu b...
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void runOnSubmenus(std::function< void(ACTION_MENU *)> aFunction)
Check if any of submenus contains a TOOL_ACTION with a specific ID.
int GetId() const
Return the unique id of the TOOL_ACTION object.
Definition: tool_action.h:121
void addEntry(ENTRY aEntry)
List of all menu entries.
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:32
Represent a single user action.
Definition: tool_action.h:67
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
void Clear()
Remove all the entries from the menu (as well as its title).
CONDITIONAL_MENU(TOOL_INTERACTIVE *aTool)
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
ENTRY(const TOOL_ACTION *aAction, SELECTION_CONDITION aCondition, int aOrder, bool aCheckmark)