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 <tool/conditional_menu.h>
27 #include <tool/action_menu.h>
28 #include <menus_helpers.h>
29 #include <kiface_i.h>
30 
31 
33  ACTION_MENU( true, aTool )
34 {
35 }
36 
37 
39 {
41  clone->m_entries = m_entries;
42  return clone;
43 }
44 
45 
46 void CONDITIONAL_MENU::AddItem( const TOOL_ACTION& aAction, const SELECTION_CONDITION& aCondition,
47  int aOrder )
48 {
49  wxASSERT( aAction.GetId() > 0 ); // Check if action was previously registered in ACTION_MANAGER
50  addEntry( ENTRY( &aAction, aCondition, aOrder, false ) );
51 }
52 
53 
55  const SELECTION_CONDITION& aCondition, int aOrder )
56 {
57  wxASSERT( aAction.GetId() > 0 ); // Check if action was previously registered in ACTION_MANAGER
58  addEntry( ENTRY( &aAction, aCondition, aOrder, true ) );
59 }
60 
61 
62 void CONDITIONAL_MENU::AddItem( int aId, const wxString& aText, const wxString& aTooltip,
63  BITMAP_DEF aIcon, const SELECTION_CONDITION& aCondition,
64  int aOrder )
65 {
66  wxMenuItem item( nullptr, aId, aText, aTooltip, wxITEM_NORMAL );
67 
68  if( aIcon )
69  AddBitmapToMenuItem( &item, KiBitmap( aIcon ) );
70 
71  addEntry( ENTRY( item, aIcon, aCondition, aOrder, false ) );
72 }
73 
74 
75 void CONDITIONAL_MENU::AddCheckItem( int aId, const wxString& aText, const wxString& aTooltip,
76  BITMAP_DEF aIcon, const SELECTION_CONDITION& aCondition,
77  int aOrder )
78 {
79  wxMenuItem item( nullptr, aId, aText, aTooltip, wxITEM_CHECK );
80 
81  if( aIcon )
82  AddBitmapToMenuItem( &item, KiBitmap( aIcon ) );
83 
84  addEntry( ENTRY( item, aIcon, aCondition, aOrder, true ) );
85 }
86 
87 
89  int aOrder )
90 {
91  addEntry( ENTRY( aMenu, aCondition, aOrder ) );
92 }
93 
94 
96 {
98 }
99 
100 
102 
103 
105 {
107  UpdateAll();
108 
109  runOnSubmenus( [] ( ACTION_MENU* aMenu ) {
110  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
111 
112  if( conditionalMenu )
113  conditionalMenu->Resolve();
114  } );
115 }
116 
117 
119 {
120  Clear();
121 
122  // We try to avoid adding useless separators (when no menuitems between separators)
123  int menu_count = 0; // number of menus since the latest separator
124 
125  for( const ENTRY& entry : m_entries )
126  {
127  const SELECTION_CONDITION& cond = entry.Condition();
128  bool result;
129  wxMenuItem* menuItem = nullptr;
130 
131  try
132  {
133  result = cond( aSelection );
134  }
135  catch( std::exception& )
136  {
137  continue;
138  }
139 
140  if( !result )
141  continue;
142 
143  switch( entry.Type() )
144  {
145  case ENTRY::ACTION:
146  menuItem = Add( *entry.Action(), entry.IsCheckmarkEntry() );
147  menu_count++;
148  break;
149 
150  case ENTRY::MENU:
151  menuItem = Add( entry.Menu() );
152  menu_count++;
153  break;
154 
155  case ENTRY::WXITEM:
156  menuItem = new wxMenuItem( this,
157  entry.wxItem()->GetId(),
158  entry.wxItem()->GetItemLabel(),
159  entry.wxItem()->GetHelp(),
160  entry.wxItem()->GetKind() );
161 
162  if( entry.GetIcon() )
163  AddBitmapToMenuItem( menuItem, KiBitmap( entry.GetIcon() ) );
164 
165  // the wxMenuItem must be append only after the bitmap is set:
166  Append( menuItem );
167 
168  menu_count++;
169  break;
170 
171  case ENTRY::SEPARATOR:
172  if( menu_count )
173  menuItem = AppendSeparator();
174 
175  menu_count = 0;
176  break;
177 
178  default:
179  wxASSERT( false );
180  break;
181  }
182  }
183 
184  // Recursively call Evaluate on all the submenus that are CONDITIONAL_MENUs to ensure
185  // they are updated. This is also required on GTK to make sure the menus have the proper
186  // size when created.
188  [&aSelection]( ACTION_MENU* aMenu )
189  {
190  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
191 
192  if( conditionalMenu )
193  conditionalMenu->Evaluate( aSelection );
194  } );
195 }
196 
197 
199 {
200  if( aEntry.Order() < 0 ) // Any order, so give it any order number
201  aEntry.SetOrder( m_entries.size() );
202 
203  std::list<ENTRY>::iterator it = m_entries.begin();
204 
205  // Find the right spot for the entry
206  while( it != m_entries.end() && it->Order() <= aEntry.Order() )
207  ++it;
208 
209  m_entries.insert( it, aEntry );
210 }
211 
213 {
214  m_type = aEntry.m_type;
215  m_icon = aEntry.m_icon;
216 
217  switch( aEntry.m_type )
218  {
219  case ACTION:
220  m_data.action = aEntry.m_data.action;
221  break;
222  case MENU:
223  m_data.menu = aEntry.m_data.menu;
224  break;
225  case WXITEM:
226  // We own the wxItem, so we need to make a new one for the new object
227  m_data.wxItem = new wxMenuItem( nullptr,
228  aEntry.m_data.wxItem->GetId(),
229  aEntry.m_data.wxItem->GetItemLabel(),
230  aEntry.m_data.wxItem->GetHelp(),
231  aEntry.m_data.wxItem->GetKind() );
232  break;
233  case SEPARATOR:
234  break; //No data to copy
235  }
236  m_condition = aEntry.m_condition;
237  m_order = aEntry.m_order;
239 }
240 
242 {
243  if( WXITEM == m_type )
244  delete m_data.wxItem;
245 }
246 
ACTION_MENU * create() const override
Returns 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)
Adds a submenu to the menu.
wxMenuItem * Add(const wxString &aLabel, int aId, const BITMAP_OPAQUE *aIcon)
Adds a wxWidgets-style entry to the menu.
PNG memory record (file in memory).
Definition: bitmap_def.h:29
Helper class to organize menu entries.
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:43
const TOOL_ACTION * action
SELECTION g_resolveDummySelection
std::list< ENTRY > m_entries
List of all menu entries.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Adds a checked menu entry to run a TOOL_ACTION on selected items.
void UpdateAll()
Runs update handlers for the menu and its submenus.
SELECTION_CONDITION m_condition
Condition to be fulfilled to show the entry in menu.
void SetOrder(int aOrder)
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
int m_order
Order number, the higher the number the lower position it takes it is in the menu.
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:207
void Evaluate(SELECTION &aSelection)
Updates 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.
TOOL_INTERACTIVE * m_tool
Creator of the menu
Definition: action_menu.h:235
void Resolve()
Updates the initial contents so that wxWidgets doesn't get its knickers tied in a knot over the menu ...
void AddSeparator(int aOrder=ANY_ORDER)
Adds a separator to the menu.
void runOnSubmenus(std::function< void(ACTION_MENU *)> aFunction)
Runs a function on the menu and all its submenus.
int GetId() const
Returns the unique id of the TOOL_ACTION object.
Definition: tool_action.h:94
void addEntry(ENTRY aEntry)
Inserts the entry, preserving the requested order.
Represents a single user action.
Definition: tool_action.h:44
void Clear()
Removes all the entries from the menu (as well as its title).
const BITMAP_OPAQUE * m_icon
CONDITIONAL_MENU(TOOL_INTERACTIVE *aTool)
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Adds a menu entry to run a TOOL_ACTION on selected items.
ENTRY(const TOOL_ACTION *aAction, SELECTION_CONDITION aCondition, int aOrder, bool aCheckmark)
union CONDITIONAL_MENU::ENTRY::@32 m_data