KiCad PCB EDA Suite
kicad_manager_control.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) 2019 CERN
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 <gestfich.h>
23 #include <executable_names.h>
24 #include <pgm_base.h>
25 #include <kiway.h>
26 #include <kicad_manager_frame.h>
27 #include <confirm.h>
28 #include <bitmaps.h>
29 #include <tool/selection.h>
30 #include <tool/tool_event.h>
31 #include <tool/tool_manager.h>
35 
37 class DIR_CHECKBOX : public wxPanel
38 {
39 public:
40  DIR_CHECKBOX( wxWindow* aParent )
41  : wxPanel( aParent )
42  {
43  m_cbCreateDir = new wxCheckBox( this, wxID_ANY,
44  _( "Create a new directory for the project" ) );
45  m_cbCreateDir->SetValue( true );
46 
47  wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL );
48  sizer->Add( m_cbCreateDir, 0, wxALL, 8 );
49 
50  SetSizerAndFit( sizer );
51  }
52 
53  bool CreateNewDir() const
54  {
55  return m_cbCreateDir->GetValue();
56  }
57 
58  static wxWindow* Create( wxWindow* aParent )
59  {
60  return new DIR_CHECKBOX( aParent );
61  }
62 
63 protected:
64  wxCheckBox* m_cbCreateDir;
65 };
66 
67 
69  TOOL_INTERACTIVE( "kicad.Control" ),
70  m_frame( nullptr )
71 {
72 }
73 
74 
76 {
77  m_frame = getEditFrame<KICAD_MANAGER_FRAME>();
78 }
79 
80 
82 {
83  wxString default_dir = m_frame->GetMruPath();
84  wxFileDialog dlg( m_frame, _( "Create New Project" ), default_dir, wxEmptyString,
85  ProjectFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
86 
87  // Add a "Create a new directory" checkbox
88  dlg.SetExtraControlCreator( &DIR_CHECKBOX::Create );
89 
90  if( dlg.ShowModal() == wxID_CANCEL )
91  return -1;
92 
93  wxFileName pro( dlg.GetPath() );
94 
95  // wxFileName automatically extracts an extension. But if it isn't
96  // a .pro extension, we should keep it as part of the filename
97  if( !pro.GetExt().IsEmpty()
98  && pro.GetExt().ToStdString() != ProjectFileExtension )
99  pro.SetName( pro.GetName() + wxT( "." ) + pro.GetExt() );
100 
101  pro.SetExt( ProjectFileExtension ); // enforce extension
102 
103  if( !pro.IsAbsolute() )
104  pro.MakeAbsolute();
105 
106  // Append a new directory with the same name of the project file.
107  if( static_cast<DIR_CHECKBOX*>( dlg.GetExtraControl() )->CreateNewDir() )
108  pro.AppendDir( pro.GetName() );
109 
110  // Check if the project directory is empty if it already exists.
111  wxDir directory( pro.GetPath() );
112 
113  if( !pro.DirExists() )
114  {
115  if( !pro.Mkdir() )
116  {
117  wxString msg;
118  msg.Printf( _( "Directory \"%s\" could not be created.\n\n"
119  "Please make sure you have write permissions and try again." ),
120  pro.GetPath() );
122  return -1;
123  }
124  }
125  else if( directory.HasFiles() )
126  {
127  wxString msg = _( "The selected directory is not empty. It is recommended that you "
128  "create projects in their own empty directory.\n\nDo you "
129  "want to continue?" );
130 
131  if( !IsOK( m_frame, msg ) )
132  return -1;
133  }
134 
135  m_frame->CreateNewProject( pro );
136  m_frame->LoadProject( pro );
137 
138  return 0;
139 }
140 
141 
143 {
145 
146  wxFileName templatePath;
147  wxString envStr;
148 
149  // KiCad system template path.
150  ENV_VAR_MAP_CITER it = Pgm().GetLocalEnvVariables().find( "KICAD_TEMPLATE_DIR" );
151 
152  if( it != Pgm().GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
153  {
154  templatePath.AssignDir( it->second.GetValue() );
155  ps->AddTemplatesPage( _( "System Templates" ), templatePath );
156  }
157 
158  // User template path.
159  it = Pgm().GetLocalEnvVariables().find( "KICAD_USER_TEMPLATE_DIR" );
160 
161  if( it != Pgm().GetLocalEnvVariables().end() && it->second.GetValue() != wxEmptyString )
162  {
163  templatePath.AssignDir( it->second.GetValue() );
164  ps->AddTemplatesPage( _( "User Templates" ), templatePath );
165  }
166 
167  // Show the project template selector dialog
168  if( ps->ShowModal() != wxID_OK )
169  return -1;
170 
171  if( !ps->GetSelectedTemplate() )
172  {
173  wxMessageBox( _( "No project template was selected. Cannot generate new project." ),
174  _( "Error" ), wxOK | wxICON_ERROR, m_frame );
175 
176  return -1;
177  }
178 
179  // Get project destination folder and project file name.
180  wxString default_dir = wxFileName( Prj().GetProjectFullName() ).GetPathWithSep();
181  wxString title = _( "New Project Folder" );
182  wxFileDialog dlg( m_frame, title, default_dir, wxEmptyString, ProjectFileWildcard(),
183  wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
184 
185  // Add a "Create a new directory" checkbox
186  dlg.SetExtraControlCreator( &DIR_CHECKBOX::Create );
187 
188  if( dlg.ShowModal() == wxID_CANCEL )
189  return -1;
190 
191  wxFileName fn( dlg.GetPath() );
192 
193  // wxFileName automatically extracts an extension. But if it isn't
194  // a .pro extension, we should keep it as part of the filename
195  if( !fn.GetExt().IsEmpty()
196  && fn.GetExt().ToStdString() != ProjectFileExtension )
197  fn.SetName( fn.GetName() + wxT( "." ) + fn.GetExt() );
198 
199  fn.SetExt( ProjectFileExtension ); // enforce extension
200 
201  if( !fn.IsAbsolute() )
202  fn.MakeAbsolute();
203 
204  // Append a new directory with the same name of the project file.
205  if( static_cast<DIR_CHECKBOX*>( dlg.GetExtraControl() )->CreateNewDir() )
206  fn.AppendDir( fn.GetName() );
207 
208  // Check if the project directory is empty if it already exists.
209  wxDir directory( fn.GetPath() );
210 
211  if( !fn.DirExists() )
212  {
213  if( !fn.Mkdir() )
214  {
215  wxString msg;
216  msg.Printf( _( "Directory \"%s\" could not be created.\n\n"
217  "Please make sure you have write permissions and try again." ),
218  fn.GetPath() );
220  return -1;
221  }
222  }
223 
224  if( !fn.IsDirWritable() )
225  {
226  wxString msg;
227 
228  msg.Printf( _( "Cannot write to folder \"%s\"." ), fn.GetPath() );
229  wxMessageDialog msgDlg( m_frame, msg, _( "Error!" ), wxICON_ERROR | wxOK | wxCENTER );
230  msgDlg.SetExtendedMessage( _( "Please check your access permissions to this folder "
231  "and try again." ) );
232  msgDlg.ShowModal();
233  return -1;
234  }
235 
236  m_frame->ClearMsg();
237 
238  // Make sure we are not overwriting anything in the destination folder.
239  std::vector< wxFileName > destFiles;
240 
241  if( ps->GetSelectedTemplate()->GetDestinationFiles( fn, destFiles ) )
242  {
243  std::vector< wxFileName > overwrittenFiles;
244 
245  for( const auto& file : destFiles )
246  {
247  if( file.FileExists() )
248  overwrittenFiles.push_back( file );
249  }
250 
251  if( !overwrittenFiles.empty() )
252  {
253  wxString extendedMsg = _( "Overwriting files:" ) + "\n";
254 
255  for( const auto& file : overwrittenFiles )
256  extendedMsg += "\n" + file.GetFullName();
257 
258  KIDIALOG msgDlg( m_frame, _( "Similar files already exist in the destination folder." ),
259  _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
260  msgDlg.SetExtendedMessage( extendedMsg );
261  msgDlg.SetOKLabel( _( "Overwrite" ) );
262  msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
263 
264  if( msgDlg.ShowModal() == wxID_CANCEL )
265  return -1;
266  }
267  }
268 
269  wxString errorMsg;
270 
271  // The selected template widget contains the template we're attempting to use to
272  // create a project
273  if( !ps->GetSelectedTemplate()->CreateProject( fn, &errorMsg ) )
274  {
275  wxMessageDialog createDlg( m_frame,
276  _( "A problem occurred creating new project from template!" ),
277  _( "Template Error" ),
278  wxOK | wxICON_ERROR );
279 
280  if( !errorMsg.empty() )
281  createDlg.SetExtendedMessage( errorMsg );
282 
283  createDlg.ShowModal();
284  return -1;
285  }
286 
287  m_frame->CreateNewProject( fn.GetFullPath() );
288  m_frame->LoadProject( fn );
289  return 0;
290 }
291 
292 
294 {
295  wxString default_dir = m_frame->GetMruPath();
296  wxFileDialog dlg( m_frame, _( "Open Existing Project" ), default_dir, wxEmptyString,
297  ProjectFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
298 
299  if( dlg.ShowModal() == wxID_CANCEL )
300  return -1;
301 
302  wxFileName pro( dlg.GetPath() );
303  pro.SetExt( ProjectFileExtension ); // enforce extension
304 
305  if( !pro.IsAbsolute() )
306  pro.MakeAbsolute();
307 
308  if( !pro.FileExists() )
309  return -1;
310 
311  m_frame->LoadProject( pro );
312  return 0;
313 }
314 
315 
317 {
319  return 0;
320 }
321 
322 
324 {
325  ACTION_MENU* actionMenu = aEvent.Parameter<ACTION_MENU*>();
326  CONDITIONAL_MENU* conditionalMenu = dynamic_cast<CONDITIONAL_MENU*>( actionMenu );
327  SELECTION dummySel;
328 
329  if( conditionalMenu )
330  conditionalMenu->Evaluate( dummySel );
331 
332  if( actionMenu )
333  actionMenu->UpdateAll();
334 
335  return 0;
336 }
337 
338 
340 {
341  FRAME_T playerType = aEvent.Parameter<FRAME_T>();
342  KIWAY_PLAYER* player;
343 
344  try
345  {
346  player = m_frame->Kiway().Player( playerType, true );
347  }
348  catch( const IO_ERROR& err )
349  {
350  wxMessageBox( _( "Application failed to load:\n" ) + err.What(), _( "KiCad Error" ),
351  wxOK | wxICON_ERROR, m_frame );
352  return -1;
353  }
354 
355  if( !player->IsVisible() ) // A hidden frame might not have the document loaded.
356  {
357  wxString filepath;
358 
359  if( playerType == FRAME_SCH )
360  {
361  filepath = m_frame->SchFileName();
362  }
363  else if( playerType == FRAME_PCB_EDITOR )
364  {
365  wxFileName kicad_board( m_frame->PcbFileName() );
366  wxFileName legacy_board( m_frame->PcbLegacyFileName() );
367 
368  if( !legacy_board.FileExists() || kicad_board.FileExists() )
369  filepath = kicad_board.GetFullPath();
370  else
371  filepath = legacy_board.GetFullPath();
372  }
373 
374  if( !filepath.IsEmpty() )
375  {
376  if( !player->OpenProjectFiles( std::vector<wxString>( 1, filepath ) ) )
377  return -1;
378  }
379 
380  player->Show( true );
381  }
382 
383  // Needed on Windows, other platforms do not use it, but it creates no issue
384  if( player->IsIconized() )
385  player->Iconize( false );
386 
387  player->Raise();
388 
389  // Raising the window does not set the focus on Linux. This should work on
390  // any platform.
391  if( wxWindow::FindFocus() != player )
392  player->SetFocus();
393 
394  return 0;
395 }
396 
397 
398 class TERMINATE_HANDLER : public wxProcess
399 {
400 private:
401  wxString m_appName;
402 
403 public:
404  TERMINATE_HANDLER( const wxString& appName ) :
405  m_appName( appName )
406  { }
407 
408  void OnTerminate( int pid, int status ) override
409  {
410  wxString msg = wxString::Format( _( "%s closed [pid=%d]\n" ),
411  m_appName,
412  pid );
413 
414  wxWindow* window = wxWindow::FindWindowByName( KICAD_MANAGER_FRAME_NAME );
415 
416  if( window ) // Should always happen.
417  {
418  // Be sure the kicad frame manager is found
419  // This dynamic cast is not really mandatory, but ...
420  KICAD_MANAGER_FRAME* frame = dynamic_cast<KICAD_MANAGER_FRAME*>( window );
421 
422  if( frame )
423  frame->PrintMsg( msg );
424  }
425 
426  delete this;
427  }
428 };
429 
430 
432 {
433  wxString execFile;
434  wxString params;
435 
437  execFile = GERBVIEW_EXE;
438  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::convertImage ) )
439  execFile = BITMAPCONVERTER_EXE;
440  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::showCalculator ) )
441  execFile = PCB_CALCULATOR_EXE;
442  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editWorksheet ) )
443  execFile = PL_EDITOR_EXE;
444  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::openTextEditor ) )
445  execFile = Pgm().GetEditorName();
446  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editOtherSch ) )
447  execFile = EESCHEMA_EXE;
448  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::editOtherPCB ) )
449  execFile = PCBNEW_EXE;
450  else
451  wxFAIL_MSG( "Execute(): unexpected request" );
452 
453  if( execFile.IsEmpty() )
454  return 0;
455 
456  if( aEvent.Parameter<wxString*>() )
457  params = *aEvent.Parameter<wxString*>();
458  else if( aEvent.IsAction( &KICAD_MANAGER_ACTIONS::viewGerbers ) )
459  params = m_frame->Prj().GetProjectPath();
460 
461  if( !params.empty() )
462  AddDelimiterString( params );
463 
464  TERMINATE_HANDLER* callback = new TERMINATE_HANDLER( execFile );
465 
466  long pid = ExecuteFile( m_frame, execFile, params, callback );
467 
468  if( pid > 0 )
469  {
470  wxString msg = wxString::Format( _( "%s %s opened [pid=%ld]\n" ),
471  execFile,
472  params,
473  pid );
474  m_frame->PrintMsg( msg );
475 
476 #ifdef __WXMAC__
477  msg.Printf( "osascript -e 'activate application \"%s\"' ", execFile );
478  system( msg.c_str() );
479 #endif
480  }
481  else
482  {
483  delete callback;
484  }
485 
486  return 0;
487 }
488 
489 
491 {
495 
498 
508 
511 }
static TOOL_ACTION newProject
KiCad executable names.
Class KIWAY_PLAYER is a wxFrame capable of the OpenProjectFiles function, meaning it can load a porti...
Definition: kiway_player.h:59
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox
Definition: confirm.cpp:53
int ShowPlayer(const TOOL_EVENT &aEvent)
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
const wxString PL_EDITOR_EXE
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:249
This file is part of the common library TODO brief description.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:44
Class ACTION_MENU.
Definition: action_menu.h:43
This file is part of the common library.
Helper widget to select whether a new directory should be created for a project
const wxString PCBNEW_EXE
const std::string ProjectFileExtension
PROJECT_TEMPLATE * GetSelectedTemplate()
FRAME_T
Enum FRAME_T is the set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition: frame_type.h:34
PROJECT & Prj()
Definition: kicad.cpp:297
static TOOL_ACTION showCalculator
static TOOL_ACTION viewGerbers
static TOOL_ACTION newFromTemplate
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
void UpdateAll()
Function UpdateAll() Runs update handlers for the menu and its submenus.
void OnTerminate(int pid, int status) override
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Function Go()
void setTransitions() override
Sets up handlers for various events.
const wxString GERBVIEW_EXE
static TOOL_ACTION convertImage
bool IsAction(const TOOL_ACTION *aAction) const
Function IsAction() Tests if the event contains an action issued upon activation of the given TOOL_AC...
Definition: tool_event.cpp:63
int Refresh(const TOOL_EVENT &aEvent)
static TOOL_ACTION editOtherPCB
wxString GetMruPath() const
const wxString SchFileName()
const wxString EESCHEMA_EXE
static TOOL_ACTION editSchematic
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:321
int OpenProject(const TOOL_EVENT &aEvent)
void PrintMsg(const wxString &aText)
Displays aText in the text panel.
TERMINATE_HANDLER(const wxString &appName)
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
KICAD_MANAGER_FRAME * m_frame
Pointer to the currently used edit/draw frame.
T Parameter() const
Function Parameter() Returns a non-standard parameter assigned to the event.
Definition: tool_event.h:427
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
Definition: pgm_base.h:148
Class TOOL_EVENT.
Definition: tool_event.h:168
DIR_CHECKBOX(wxWindow *aParent)
Definition of file extensions used in Kicad.
const wxString PCB_CALCULATOR_EXE
static TOOL_ACTION editFootprints
void AddDelimiterString(wxString &string)
Function AddDelimiterString Add un " to the start and the end of string (if not already done).
Definition: gestfich.cpp:42
static TOOL_ACTION updateMenu
Definition: actions.h:158
static TOOL_ACTION editWorksheet
VTBL_ENTRY const ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.h:325
#define _(s)
string & err
Definition: json11.cpp:598
int NewProject(const TOOL_EVENT &aEvent)
wxCheckBox * m_cbCreateDir
wxString ProjectFileWildcard()
void Reset(RESET_REASON aReason) override
Function Reset() Brings the tool to a known, initial state.
static TOOL_ACTION editOtherSch
size_t GetDestinationFiles(const wxFileName &aNewProjectPath, std::vector< wxFileName > &aDestFiles)
Fetch the list of destination files to be copied when the new project is created.
static TOOL_ACTION editPCB
int UpdateMenu(const TOOL_EVENT &aEvent)
see class PGM_BASE
bool CreateProject(wxFileName &aNewProjectPath, wxString *aErrorMsg=nullptr)
Copies and renames all template files to create a new project.
const wxString PcbLegacyFileName()
static TOOL_ACTION openProject
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
#define KICAD_MANAGER_FRAME_NAME
static TOOL_ACTION zoomRedraw
Definition: actions.h:87
static wxWindow * Create(wxWindow *aParent)
const wxString PcbFileName()
int NewFromTemplate(const TOOL_EVENT &aEvent)
void AddTemplatesPage(const wxString &aTitle, wxFileName &aPath)
Add a new page with aTitle, populated with templates from aPath.
RESET_REASON
Determines the reason of reset for a tool
Definition: tool_base.h:79
int ShowModal() override
Definition: confirm.cpp:95
VTBL_ENTRY const wxString & GetEditorName(bool aCanShowFileChooser=true)
Return the preferred editor name.
Definition: pgm_base.cpp:179
void LoadProject(const wxFileName &aProjectFileName)
const wxString BITMAPCONVERTER_EXE
void CreateNewProject(const wxFileName &aProjectFileName)
Creates a new project by setting up and initial project, schematic, and board files.
static TOOL_ACTION editSymbols
int ExecuteFile(wxWindow *frame, const wxString &ExecFile, const wxString &param, wxProcess *callback)
Function ExecuteFile calls the executable file ExecFile with the command line parameters param.
Definition: gestfich.cpp:178
void ClearMsg()
Erase the text panel.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:281
The main KiCad project manager frame.
int Execute(const TOOL_EVENT &aEvent)
static TOOL_ACTION openTextEditor
bool CreateNewDir() const