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( isContextMenu )
34 {
35  m_tool = aTool;
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  BITMAP_DEF aIcon, const SELECTION_CONDITION& aCondition,
65  int aOrder )
66 {
67  wxMenuItem* item = new wxMenuItem( 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  BITMAP_DEF aIcon, const SELECTION_CONDITION& aCondition,
78  int aOrder )
79 {
80  wxMenuItem* item = new wxMenuItem( 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 
102 void CONDITIONAL_MENU::AddClose( wxString aAppname )
103 {
104  AddItem( wxID_CLOSE, _( "Close\tCTRL+W" ), wxString::Format( "Close %s", aAppname ), exit_xpm,
106 }
107 
108 
109 void CONDITIONAL_MENU::AddQuitOrClose( KIFACE_I* aKiface, wxString aAppname )
110 {
111  if( !aKiface || aKiface->IsSingle() ) // not when under a project mgr
112  {
113  // Don't use ACTIONS::quit; wxWidgets moves this on OSX and expects to find it via
114  // wxID_EXIT
115  AddItem( wxID_EXIT, _( "Quit" ), wxString::Format( "Quit %s", aAppname ), exit_xpm,
117  }
118  else
119  {
120  AddClose( aAppname );
121  }
122 }
123 
124 
126 
127 
129 {
131  UpdateAll();
132 
133  runOnSubmenus( [] ( ACTION_MENU* aMenu ) {
134  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
135 
136  if( conditionalMenu )
137  conditionalMenu->Resolve();
138  } );
139 }
140 
141 
143 {
144  Clear();
145 
146  // We try to avoid adding useless separators (when no menuitems between separators)
147  int menu_count = 0; // number of menus since the latest separator
148 
149  for( const ENTRY& entry : m_entries )
150  {
151  const SELECTION_CONDITION& cond = entry.Condition();
152  bool result;
153  wxMenuItem* menuItem = nullptr;
154 
155  try
156  {
157  result = cond( aSelection );
158  }
159  catch( std::exception& )
160  {
161  continue;
162  }
163 
164  if( m_isContextMenu && !result )
165  continue;
166 
167  switch( entry.Type() )
168  {
169  case ENTRY::ACTION:
170  menuItem = Add( *entry.Action(), entry.IsCheckmarkEntry() );
171  menu_count++;
172  break;
173 
174  case ENTRY::MENU:
175  menuItem = Add( entry.Menu() );
176  menu_count++;
177  break;
178 
179  case ENTRY::WXITEM:
180 #ifdef __WXMAC__
181  // Instantiate the Preferences item only on the first Resolve(); after that
182  // wxWidgets will have moved it to the Application menu
183  if( entry.wxItem()->GetId() == wxID_PREFERENCES )
184  {
185  if( &aSelection != &g_resolveDummySelection )
186  continue;
187  }
188 #endif
189  menuItem = new wxMenuItem( this,
190  entry.wxItem()->GetId(),
191  entry.wxItem()->GetItemLabel(),
192  entry.wxItem()->GetHelp(),
193  entry.wxItem()->GetKind() );
194 
195  if( entry.GetIcon() )
196  AddBitmapToMenuItem( menuItem, KiBitmap( entry.GetIcon() ) );
197 
198  // the wxMenuItem must be append only after the bitmap is set:
199  Append( menuItem );
200 
201  menu_count++;
202  break;
203 
204  case ENTRY::SEPARATOR:
205  if( menu_count )
206  menuItem = AppendSeparator();
207 
208  menu_count = 0;
209  break;
210 
211  default:
212  wxASSERT( false );
213  break;
214  }
215 
216  if( menuItem )
217  {
218  if( entry.IsCheckmarkEntry() )
219  menuItem->Check( result );
220  else
221  menuItem->Enable( result );
222  }
223  }
224 
225  // Recursively call Evaluate on all the submenus that are CONDITIONAL_MENUs to ensure
226  // they are updated. This is also required on GTK to make sure the menus have the proper
227  // size when created.
229  [&aSelection]( ACTION_MENU* aMenu )
230  {
231  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( aMenu );
232 
233  if( conditionalMenu )
234  conditionalMenu->Evaluate( aSelection );
235  } );
236 }
237 
238 
240 {
241  if( aEntry.Order() < 0 ) // Any order, so give it any order number
242  aEntry.SetOrder( m_entries.size() );
243 
244  std::list<ENTRY>::iterator it = m_entries.begin();
245 
246  // Find the right spot for the entry
247  while( it != m_entries.end() && it->Order() <= aEntry.Order() )
248  ++it;
249 
250  m_entries.insert( it, aEntry );
251 }
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)
Function ShowAlways The default condition function (always returns true).
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Function AddMenu()
wxMenuItem * Add(const wxString &aLabel, int aId, const BITMAP_OPAQUE *aIcon)
Function Add() Adds a wxWidgets-style entry to the menu.
Class KIFACE_I is a KIFACE (I)mplementation, with some features useful for DSOs which implement a KIF...
Definition: kiface_i.h:37
Helper class to organize menu entries.
Class ACTION_MENU.
Definition: action_menu.h:43
SELECTION g_resolveDummySelection
std::list< ENTRY > m_entries
List of all menu entries.
CONDITIONAL_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool)
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddCheckItem()
void UpdateAll()
Function UpdateAll() Runs update handlers for the menu and its submenus.
void SetOrder(int aOrder)
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:79
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:223
bool IsSingle() const
Function IsSingle is this KIFACE_I running under single_top?
Definition: kiface_i.h:115
bool m_isContextMenu
Definition: action_menu.h:215
void Evaluate(SELECTION &aSelection)
Function Evaluate()
std::function< bool(const SELECTION &)> SELECTION_CONDITION
Functor type that checks a specific condition for selected items.
#define _(s)
TOOL_INTERACTIVE * m_tool
Creator of the menu
Definition: action_menu.h:227
void Resolve()
Function Resolve()
void AddSeparator(int aOrder=ANY_ORDER)
Function AddSeparator()
void runOnSubmenus(std::function< void(ACTION_MENU *)> aFunction)
Runs a function on the menu and all its submenus.
void AddQuitOrClose(KIFACE_I *aKiface, wxString aAppname="")
Functions AddQuitOrClose()
void AddClose(wxString aAppname="")
Function AddClose()
int GetId() const
Function GetId() Returns the unique id of the TOOL_ACTION object.
Definition: tool_action.h:100
void addEntry(ENTRY aEntry)
Inserts the entry, preserving the requested order.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
Class TOOL_ACTION.
Definition: tool_action.h:46
void Clear()
Function Clear() Removes all the entries from the menu (as well as its title).
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddItem()