1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 Ian McInerney <Ian.S.McInerney@ieee.org>
5  * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <confirm.h>
22 #include <cstdint>
23 #include <functional>
24 #include <kiface_base.h>
25 #include <kiway_express.h>
26 #include <lib_id.h>
27 #include <tool/actions.h>
28 #include <tool/tool_manager.h>
29 
30 #include <cvpcb_mainframe.h>
31 #include <dialogs/dialog_config_equfiles.h>
32 #include <display_footprints_frame.h>
33 #include <listboxes.h>
34 #include <tools/cvpcb_actions.h>
35 #include <tools/cvpcb_control.h>
36 #include <wx/settings.h>
37 
38 using namespace std::placeholders;
39 
40 
CVPCB_CONTROL()41 CVPCB_CONTROL::CVPCB_CONTROL() :
42         TOOL_INTERACTIVE( "cvpcb.Control" ),
43         m_frame( nullptr )
44 {
45 }
46 
47 
Reset(RESET_REASON aReason)48 void CVPCB_CONTROL::Reset( RESET_REASON aReason )
49 {
50     m_frame = getEditFrame<CVPCB_MAINFRAME>();
51 }
52 
53 
Main(const TOOL_EVENT & aEvent)54 int CVPCB_CONTROL::Main( const TOOL_EVENT& aEvent )
55 {
56     // Main loop: keep receiving events
57     while( TOOL_EVENT* evt = Wait() )
58     {
59         bool handled = false;
60 
61         // The escape key maps to the cancel event, which is used to close the window
62         if( evt->IsCancel() )
63         {
64             m_frame->Close( false );
65             handled = true;
66         }
67         else if( evt->IsKeyPressed() )
68         {
69             switch( evt->KeyCode() )
70             {
71             // The right arrow moves focus to the focusable object to the right
72             case WXK_RIGHT:
73                 m_toolMgr->RunAction( CVPCB_ACTIONS::changeFocusRight );
74                 handled = true;
75                 break;
76 
77             // The left arrow moves focus to the focusable object to the left
78             case WXK_LEFT:
79                 m_toolMgr->RunAction( CVPCB_ACTIONS::changeFocusLeft );
80                 handled = true;
81                 break;
82 
83             default:
84                 // Let every other key continue processing to the controls of the window
85                 break;
86             }
87         }
88 
89         if( !handled )
90             evt->SetPassEvent();
91     }
92 
93     return 0;
94 }
95 
96 
ChangeFocus(const TOOL_EVENT & aEvent)97 int CVPCB_CONTROL::ChangeFocus( const TOOL_EVENT& aEvent )
98 {
99     int tmp = aEvent.Parameter<intptr_t>();
100     CVPCB_MAINFRAME::FOCUS_DIR dir =
101             static_cast<CVPCB_MAINFRAME::FOCUS_DIR>( tmp );
102 
103     switch( dir )
104     {
105     case CVPCB_MAINFRAME::CHANGE_FOCUS_RIGHT:
106         switch( m_frame->GetFocusedControl() )
107         {
108         case CVPCB_MAINFRAME::CONTROL_LIBRARY:
109             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_COMPONENT );
110             break;
111 
112         case CVPCB_MAINFRAME::CONTROL_COMPONENT:
113             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_FOOTPRINT );
114             break;
115 
116         case CVPCB_MAINFRAME::CONTROL_FOOTPRINT:
117             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_LIBRARY );
118             break;
119 
120         case CVPCB_MAINFRAME::CONTROL_NONE:
121         default:
122             break;
123         }
124 
125         break;
126 
127     case CVPCB_MAINFRAME::CHANGE_FOCUS_LEFT:
128         switch( m_frame->GetFocusedControl() )
129         {
130         case CVPCB_MAINFRAME::CONTROL_LIBRARY:
131             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_FOOTPRINT );
132             break;
133 
134         case CVPCB_MAINFRAME::CONTROL_COMPONENT:
135             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_LIBRARY );
136             break;
137 
138         case CVPCB_MAINFRAME::CONTROL_FOOTPRINT:
139             m_frame->SetFocusedControl( CVPCB_MAINFRAME::CONTROL_COMPONENT );
140             break;
141 
142         case CVPCB_MAINFRAME::CONTROL_NONE:
143         default:
144             break;
145         }
146 
147         break;
148 
149     default:
150         break;
151     }
152 
153     return 0;
154 }
155 
156 
ShowFootprintViewer(const TOOL_EVENT & aEvent)157 int CVPCB_CONTROL::ShowFootprintViewer( const TOOL_EVENT& aEvent )
158 {
159 
160     DISPLAY_FOOTPRINTS_FRAME* fpframe = m_frame->GetFootprintViewerFrame();
161 
162     if( !fpframe )
163     {
164         fpframe = (DISPLAY_FOOTPRINTS_FRAME*) m_frame->Kiway().Player(
165                 FRAME_CVPCB_DISPLAY, true, m_frame );
166         fpframe->Show( true );
167     }
168     else
169     {
170         if( fpframe->IsIconized() )
171             fpframe->Iconize( false );
172 
173         // The display footprint window might be buried under some other
174         // windows, so CreateScreenCmp() on an existing window would not
175         // show any difference, leaving the user confused.
176         // So we want to put it to front, second after our CVPCB_MAINFRAME.
177         // We do this by a little dance of bringing it to front then the main
178         // frame back.
179         wxWindow* focus = m_frame->FindFocus();
180 
181         fpframe->Raise(); // Make sure that is visible.
182         m_frame->Raise(); // .. but still we want the focus.
183 
184         if( focus )
185             focus->SetFocus();
186     }
187 
188     fpframe->InitDisplay();
189 
190     return 0;
191 }
192 
193 
ToggleFootprintFilter(const TOOL_EVENT & aEvent)194 int CVPCB_CONTROL::ToggleFootprintFilter( const TOOL_EVENT& aEvent )
195 {
196     m_frame->SetFootprintFilter(
197             static_cast<FOOTPRINTS_LISTBOX::FP_FILTER_T>( aEvent.Parameter<intptr_t>() ),
198             CVPCB_MAINFRAME::FILTER_TOGGLE );
199 
200     return 0;
201 }
202 
203 
ShowEquFileTable(const TOOL_EVENT & aEvent)204 int CVPCB_CONTROL::ShowEquFileTable( const TOOL_EVENT& aEvent )
205 {
206     DIALOG_CONFIG_EQUFILES dlg( m_frame );
207     dlg.ShowModal();
208 
209     return 0;
210 }
211 
212 
SaveAssociations(const TOOL_EVENT & aEvent)213 int CVPCB_CONTROL::SaveAssociations( const TOOL_EVENT& aEvent )
214 {
215     m_frame->SaveFootprintAssociation( true );
216     return 0;
217 }
218 
219 
ToNA(const TOOL_EVENT & aEvent)220 int CVPCB_CONTROL::ToNA( const TOOL_EVENT& aEvent )
221 {
222     int tmp = aEvent.Parameter<intptr_t>();
223     CVPCB_MAINFRAME::ITEM_DIR dir =
224             static_cast<CVPCB_MAINFRAME::ITEM_DIR>( tmp );
225 
226     std::vector<unsigned int> naComp = m_frame->GetComponentIndices( CVPCB_MAINFRAME::NA_COMPONENTS );
227     std::vector<unsigned int> tempSel = m_frame->GetComponentIndices( CVPCB_MAINFRAME::SEL_COMPONENTS );
228 
229     // No unassociated components
230     if( naComp.empty() )
231         return 0;
232 
233     // Extract the current selection
234     unsigned int curSel = -1;
235     unsigned int newSel = -1;
236     switch( dir )
237     {
238     case CVPCB_MAINFRAME::ITEM_NEXT:
239         if( !tempSel.empty() )
240             newSel = tempSel.front();
241 
242         // Find the next index in the component list
243         for( unsigned int i : naComp )
244         {
245             if( i > newSel )
246             {
247                 newSel = i;
248                 break;
249             }
250         }
251 
252         break;
253 
254     case CVPCB_MAINFRAME::ITEM_PREV:
255         if( !tempSel.empty() )
256         {
257             newSel = tempSel.front();
258             curSel = newSel - 1;        // Break one before the current selection
259         }
260 
261         break;
262 
263     default:
264         wxASSERT_MSG( false, "Invalid direction" );
265     }
266 
267     // Find the next index in the component list
268     for( unsigned int i : naComp )
269     {
270         if( i >= curSel )
271         {
272             newSel = i;
273             break;
274         }
275     }
276 
277     // Set the component selection
278     m_frame->SetSelectedComponent( newSel );
279 
280     return 0;
281 }
282 
283 
UpdateMenu(const TOOL_EVENT & aEvent)284 int CVPCB_CONTROL::UpdateMenu( const TOOL_EVENT& aEvent )
285 {
286     ACTION_MENU*      actionMenu = aEvent.Parameter<ACTION_MENU*>();
287     CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
288     SELECTION         dummySel;
289 
290     if( conditionalMenu )
291         conditionalMenu->Evaluate( dummySel );
292 
293     if( actionMenu )
294         actionMenu->UpdateAll();
295 
296     return 0;
297 }
298 
299 
setTransitions()300 void CVPCB_CONTROL::setTransitions()
301 {
302     // Control actions
303     Go( &CVPCB_CONTROL::UpdateMenu,            ACTIONS::updateMenu.MakeEvent() );
304     Go( &CVPCB_CONTROL::Main,                  CVPCB_ACTIONS::controlActivate.MakeEvent() );
305     Go( &CVPCB_CONTROL::ChangeFocus,           CVPCB_ACTIONS::changeFocusRight.MakeEvent() );
306     Go( &CVPCB_CONTROL::ChangeFocus,           CVPCB_ACTIONS::changeFocusLeft.MakeEvent() );
307 
308     // Run the footprint viewer
309     Go( &CVPCB_CONTROL::ShowFootprintViewer,   CVPCB_ACTIONS::showFootprintViewer.MakeEvent() );
310 
311     // Management actions
312     Go( &CVPCB_CONTROL::ShowEquFileTable,      CVPCB_ACTIONS::showEquFileTable.MakeEvent() );
313     Go( &CVPCB_CONTROL::SaveAssociations,      CVPCB_ACTIONS::saveAssociations.MakeEvent() );
314 
315     // Navigation actions
316     Go( &CVPCB_CONTROL::ToNA,                  CVPCB_ACTIONS::gotoNextNA.MakeEvent() );
317     Go( &CVPCB_CONTROL::ToNA,                  CVPCB_ACTIONS::gotoPreviousNA.MakeEvent() );
318 
319     // Filter the footprints
320     Go( &CVPCB_CONTROL::ToggleFootprintFilter, CVPCB_ACTIONS::FilterFPbyFPFilters.MakeEvent() );
321     Go( &CVPCB_CONTROL::ToggleFootprintFilter, CVPCB_ACTIONS::FilterFPbyLibrary.MakeEvent() );
322     Go( &CVPCB_CONTROL::ToggleFootprintFilter, CVPCB_ACTIONS::filterFPbyPin.MakeEvent() );
323 }
324