KiCad PCB EDA Suite
project_tree_pane.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) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
6  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
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 <stack>
27 
28 #include <wx/regex.h>
29 #include <wx/stdpaths.h>
30 #include <wx/string.h>
31 #include <wx/msgdlg.h>
32 #include <wx/textdlg.h>
33 
34 #include <bitmaps.h>
35 #include <bitmap_store.h>
36 #include <gestfich.h>
37 #include <macros.h>
38 #include <menus_helpers.h>
39 #include <trace_helpers.h>
41 #include <kiplatform/environment.h>
42 #include <core/kicad_algo.h>
43 #include <paths.h>
44 
45 #include "project_tree_item.h"
46 #include "project_tree.h"
47 #include "pgm_kicad.h"
48 #include "kicad_id.h"
49 #include "kicad_manager_frame.h"
50 
51 #include "project_tree_pane.h"
52 
53 
54 /* Note about the project tree build process:
55  * Building the project tree can be *very* long if there are a lot of subdirectories in the
56  * working directory. Unfortunately, this happens easily if the project file *.pro is in the
57  * user's home directory.
58  * So the tree project is built "on demand":
59  * First the tree is built from the current directory and shows files and subdirs.
60  * > First level subdirs trees are built (i.e subdirs contents are not read)
61  * > When expanding a subdir, each subdir contains is read, and the corresponding sub tree is
62  * populated on the fly.
63  */
64 
65 // list of files extensions listed in the tree project window
66 // Add extensions in a compatible regex format to see others files types
67 static const wxChar* s_allowedExtensionsToList[] = {
68  wxT( "^.*\\.pro$" ),
69  wxT( "^.*\\.kicad_pro$" ),
70  wxT( "^.*\\.pdf$" ),
71  wxT( "^.*\\.sch$" ), // Legacy Eeschema files
72  wxT( "^.*\\.kicad_sch$" ), // S-expr Eeschema files
73  wxT( "^[^$].*\\.brd$" ), // Legacy Pcbnew files
74  wxT( "^[^$].*\\.kicad_pcb$" ), // S format Pcbnew board files
75  wxT( "^[^$].*\\.kicad_dru$" ), // Design rule files
76  wxT( "^[^$].*\\.kicad_wks$" ), // S format kicad drawing sheet files
77  wxT( "^[^$].*\\.kicad_mod$" ), // S format kicad footprint files, currently not listed
78  wxT( "^.*\\.net$" ), // pcbnew netlist file
79  wxT( "^.*\\.cir$" ), // Spice netlist file
80  wxT( "^.*\\.lib$" ), // Legacy schematic library file
81  wxT( "^.*\\.kicad_sym$" ), // S-expr symbol libraries
82  wxT( "^.*\\.txt$" ),
83  wxT( "^.*\\.pho$" ), // Gerber file (Old Kicad extension)
84  wxT( "^.*\\.gbr$" ), // Gerber file
85  wxT( "^.*\\.gbrjob$" ), // Gerber job file
86  wxT( "^.*\\.gb[alops]$" ), // Gerber back (or bottom) layer file (deprecated Protel ext)
87  wxT( "^.*\\.gt[alops]$" ), // Gerber front (or top) layer file (deprecated Protel ext)
88  wxT( "^.*\\.g[0-9]{1,2}$" ), // Gerber inner layer file (deprecated Protel ext)
89  wxT( "^.*\\.odt$" ),
90  wxT( "^.*\\.htm$" ),
91  wxT( "^.*\\.html$" ),
92  wxT( "^.*\\.rpt$" ), // Report files
93  wxT( "^.*\\.csv$" ), // Report files in comma separated format
94  wxT( "^.*\\.pos$" ), // Footprint position files
95  wxT( "^.*\\.cmp$" ), // CvPcb cmp/footprint link files
96  wxT( "^.*\\.drl$" ), // Excellon drill files
97  wxT( "^.*\\.nc$" ), // Excellon NC drill files (alternate file ext)
98  wxT( "^.*\\.xnc$" ), // Excellon NC drill files (alternate file ext)
99  wxT( "^.*\\.svg$" ), // SVG print/plot files
100  wxT( "^.*\\.ps$" ), // PostScript plot files
101  nullptr // end of list
102 };
103 
104 
105 /* TODO: Check if these file extension and wildcard definitions are used
106  * in any of the other KiCad programs and move them into the common
107  * library as required.
108  */
109 
110 // Gerber file extension wildcard.
111 const wxString GerberFileExtensionWildCard( ".((gbr|gbrjob|(gb|gt)[alops])|pho)" );
112 
113 
121 BEGIN_EVENT_TABLE( PROJECT_TREE_PANE, wxSashLayoutWindow )
122  EVT_TREE_ITEM_ACTIVATED( ID_PROJECT_TREE, PROJECT_TREE_PANE::onSelect )
123  EVT_TREE_ITEM_EXPANDED( ID_PROJECT_TREE, PROJECT_TREE_PANE::onExpand )
124  EVT_TREE_ITEM_RIGHT_CLICK( ID_PROJECT_TREE, PROJECT_TREE_PANE::onRight )
132  EVT_IDLE( PROJECT_TREE_PANE::onIdle )
133 END_EVENT_TABLE()
134 
135 
137  wxSashLayoutWindow( parent, ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
138  wxNO_BORDER | wxTAB_TRAVERSAL )
139 {
140  m_Parent = parent;
141  m_TreeProject = nullptr;
142  m_isRenaming = false;
143  m_selectedItem = nullptr;
144  m_watcherNeedReset = false;
145 
146  m_watcher = nullptr;
147  Connect( wxEVT_FSWATCHER,
148  wxFileSystemWatcherEventHandler( PROJECT_TREE_PANE::onFileSystemEvent ) );
149 
150  Bind( wxEVT_SYS_COLOUR_CHANGED,
151  wxSysColourChangedEventHandler( PROJECT_TREE_PANE::onThemeChanged ), this );
152 
153  /*
154  * Filtering is now inverted: the filters are actually used to _enable_ support
155  * for a given file type.
156  */
157  for( int ii = 0; s_allowedExtensionsToList[ii] != nullptr; ii++ )
158  m_filters.emplace_back( s_allowedExtensionsToList[ii] );
159 
160  m_filters.emplace_back( wxT( "^no KiCad files found" ) );
161 
162  ReCreateTreePrj();
163 }
164 
165 
167 {
169 }
170 
171 
173 {
174  if( m_watcher )
175  {
176  m_watcher->RemoveAll();
177  m_watcher->SetOwner( nullptr );
178  delete m_watcher;
179  m_watcher = nullptr;
180  }
181 }
182 
183 
184 void PROJECT_TREE_PANE::onSwitchToSelectedProject( wxCommandEvent& event )
185 {
186  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
187 
188  if( tree_data.size() != 1 )
189  return;
190 
191  wxString prj_filename = tree_data[0]->GetFileName();
192 
193  m_Parent->LoadProject( prj_filename );
194 }
195 
196 
197 void PROJECT_TREE_PANE::onOpenDirectory( wxCommandEvent& event )
198 {
199  // Get the root directory name:
200  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
201 
202  for( PROJECT_TREE_ITEM* item_data : tree_data )
203  {
204  // Ask for the new sub directory name
205  wxString curr_dir = item_data->GetDir();
206 
207  if( curr_dir.IsEmpty() )
208  {
209  // Use project path if the tree view path was empty.
210  curr_dir = wxPathOnly( m_Parent->GetProjectFileName() );
211 
212  // As a last resort use the user's documents folder.
213  if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
215 
216  if( !curr_dir.IsEmpty() )
217  curr_dir += wxFileName::GetPathSeparator();
218  }
219 
220 #ifdef __WXMAC__
221  wxString msg;
222 
223  // Quote in case there are spaces in the path.
224  msg.Printf( "open \"%s\"", curr_dir );
225 
226  system( msg.c_str() );
227 #else
228  #if !wxCHECK_VERSION( 3, 1, 0 )
229  // Quote in case there are spaces in the path.
230  // Not needed on 3.1.4, but needed in 3.0 versions
231  // Moreover, on Linux, on 3.1.4 wx version, adding quotes breaks
232  // wxLaunchDefaultApplication
233  QuoteString( curr_dir );
234  #endif
235 
236  wxLaunchDefaultApplication( curr_dir );
237 #endif
238  }
239 }
240 
241 
242 void PROJECT_TREE_PANE::onCreateNewDirectory( wxCommandEvent& event )
243 {
244  // Get the root directory name:
245  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
246 
247  for( PROJECT_TREE_ITEM* item_data : tree_data )
248  {
249  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
250 
251  // Ask for the new sub directory name
252  wxString curr_dir = item_data->GetDir();
253 
254  if( curr_dir.IsEmpty() )
255  curr_dir = prj_dir;
256 
257  wxString new_dir = wxGetTextFromUser( _( "Directory name:" ), _( "Create New Directory" ) );
258 
259  if( new_dir.IsEmpty() )
260  return;
261 
262  wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
263 
264  wxMkdir( full_dirname );
265  addItemToProjectTree( full_dirname, item_data->GetId(), nullptr, false );
266  }
267 }
268 
269 
271 {
272  switch( type )
273  {
290  case TREE_FILE_TYPE::DRILL_NC: return "nc";
291  case TREE_FILE_TYPE::DRILL_XNC: return "xnc";
298  default: return wxEmptyString;
299  }
300 }
301 
302 
303 std::vector<wxString> getProjects( const wxDir& dir )
304 {
305  std::vector<wxString> projects;
306  wxString dir_filename;
307  bool haveFile = dir.GetFirst( &dir_filename );
308 
309  while( haveFile )
310  {
311  wxFileName file( dir_filename );
312 
313  if( file.GetExt() == LegacyProjectFileExtension || file.GetExt() == ProjectFileExtension )
314  projects.push_back( file.GetName() );
315 
316  haveFile = dir.GetNext( &dir_filename );
317  }
318 
319  return projects;
320 }
321 
322 
323 wxTreeItemId PROJECT_TREE_PANE::addItemToProjectTree( const wxString& aName,
324  const wxTreeItemId& aParent,
325  std::vector<wxString>* aProjectNames,
326  bool aRecurse )
327 {
329  wxFileName fn( aName );
330 
331  // Files/dirs names starting by "." are not visible files under unices.
332  // Skip them also under Windows
333  if( fn.GetName().StartsWith( wxT( "." ) ) )
334  return wxTreeItemId();
335 
336  if( wxDirExists( aName ) )
337  {
339  }
340  else
341  {
342  // Filter
343  wxRegEx reg;
344  bool addFile = false;
345 
346  for( const wxString& m_filter : m_filters )
347  {
348  wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ), continue,
349  wxString::Format( "Regex %s failed to compile.", m_filter ) );
350 
351  if( reg.Matches( aName ) )
352  {
353  addFile = true;
354  break;
355  }
356  }
357 
358  if( !addFile )
359  return wxTreeItemId();
360 
361  for( int i = static_cast<int>( TREE_FILE_TYPE::LEGACY_PROJECT );
362  i < static_cast<int>( TREE_FILE_TYPE::MAX ); i++ )
363  {
364  wxString ext = GetFileExt( (TREE_FILE_TYPE) i );
365 
366  if( ext == wxT( "" ) )
367  continue;
368 
369  // For gerber files, the official ext is gbr
370  if( i == static_cast<int>( TREE_FILE_TYPE::GERBER ) )
371  ext = "gbr";
372 
373  reg.Compile( wxString::FromAscii( "^.*\\." ) + ext + wxString::FromAscii( "$" ),
374  wxRE_ICASE );
375 
376  if( reg.Matches( aName ) )
377  {
378  type = (TREE_FILE_TYPE) i;
379  break;
380  }
381  }
382  }
383 
384  wxString file = wxFileNameFromPath( aName );
385  wxFileName currfile( file );
386  wxFileName project( m_Parent->GetProjectFileName() );
387 
388  // Ignore legacy projects with the same name as the current project
389  if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
390  && ( currfile.GetName().CmpNoCase( project.GetName() ) == 0 ) )
391  {
392  return wxTreeItemId();
393  }
394 
395  if( currfile.GetExt() == GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
396  || currfile.GetExt() == GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
397  {
398  if( aProjectNames )
399  {
400  if( !alg::contains( *aProjectNames, currfile.GetName() ) )
401  return wxTreeItemId();
402  }
403  else
404  {
405  PROJECT_TREE_ITEM* parentTreeItem = GetItemIdData( aParent );
406  wxDir parentDir( parentTreeItem->GetDir() );
407  std::vector<wxString> projects = getProjects( parentDir );
408 
409  if( !alg::contains( projects, currfile.GetName() ) )
410  return wxTreeItemId();
411  }
412  }
413 
414  // also check to see if it is already there.
415  wxTreeItemIdValue cookie;
416  wxTreeItemId kid = m_TreeProject->GetFirstChild( aParent, cookie );
417 
418  while( kid.IsOk() )
419  {
420  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
421 
422  if( itemData && itemData->GetFileName() == aName )
423  return itemData->GetId(); // well, we would have added it, but it is already here!
424 
425  kid = m_TreeProject->GetNextChild( aParent, cookie );
426  }
427 
428  // Only show current files if both legacy and current files are present
431  {
432  kid = m_TreeProject->GetFirstChild( aParent, cookie );
433 
434  while( kid.IsOk() )
435  {
436  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
437 
438  if( itemData )
439  {
440  wxFileName fname( itemData->GetFileName() );
441 
442  if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
443  {
444  switch( type )
445  {
447  if( itemData->GetType() == TREE_FILE_TYPE::JSON_PROJECT )
448  return wxTreeItemId();
449 
450  break;
451 
453  if( itemData->GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
454  return wxTreeItemId();
455 
456  break;
457 
459  if( itemData->GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
460  m_TreeProject->Delete( kid );
461 
462  break;
463 
465  if( itemData->GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
466  m_TreeProject->Delete( kid );
467 
468  break;
469 
470  default:
471  break;
472  }
473  }
474  }
475 
476  kid = m_TreeProject->GetNextChild( aParent, cookie );
477  }
478  }
479 
480  // Append the item (only appending the filename not the full path):
481  wxTreeItemId newItemId = m_TreeProject->AppendItem( aParent, file );
482  PROJECT_TREE_ITEM* data = new PROJECT_TREE_ITEM( type, aName, m_TreeProject );
483 
484  m_TreeProject->SetItemData( newItemId, data );
485  data->SetState( 0 );
486 
487  // Mark root files (files which have the same aName as the project)
488  wxString fileName = currfile.GetName().Lower();
489  wxString projName = project.GetName().Lower();
490  data->SetRootFile( fileName == projName || fileName.StartsWith( projName + "-" ) );
491 
492 #ifndef __WINDOWS__
493  bool subdir_populated = false;
494 #endif
495 
496  // This section adds dirs and files found in the subdirs
497  // in this case AddFile is recursive, but for the first level only.
498  if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
499  {
500  wxDir dir( aName );
501 
502  if( dir.IsOpened() ) // protected dirs will not open properly.
503  {
504  std::vector<wxString> projects = getProjects( dir );
505  wxString dir_filename;
506  bool haveFile = dir.GetFirst( &dir_filename );
507 
508  data->SetPopulated( true );
509 
510 #ifndef __WINDOWS__
511  subdir_populated = aRecurse;
512 #endif
513 
514  while( haveFile )
515  {
516  // Add name in tree, but do not recurse
517  wxString path = aName + wxFileName::GetPathSeparator() + dir_filename;
518  addItemToProjectTree( path, newItemId, &projects, false );
519 
520  haveFile = dir.GetNext( &dir_filename );
521  }
522  }
523 
524  // Sort filenames by alphabetic order
525  m_TreeProject->SortChildren( newItemId );
526  }
527 
528 #ifndef __WINDOWS__
529  if( subdir_populated )
530  m_watcherNeedReset = true;
531 #endif
532 
533  return newItemId;
534 }
535 
536 
538 {
539  wxString pro_dir = m_Parent->GetProjectFileName();
540 
541  if( !m_TreeProject )
542  m_TreeProject = new PROJECT_TREE( this );
543  else
544  m_TreeProject->DeleteAllItems();
545 
546  if( !pro_dir ) // This is empty from PROJECT_TREE_PANE constructor
547  return;
548 
549  wxFileName fn = pro_dir;
550  bool prjReset = false;
551 
552  if( !fn.IsOk() )
553  {
554  fn.Clear();
555  fn.SetPath( PATHS::GetDefaultUserProjectsPath() );
556  fn.SetName( NAMELESS_PROJECT );
557  fn.SetExt( ProjectFileExtension );
558  prjReset = true;
559  }
560 
561  bool prjOpened = fn.FileExists();
562 
563  // We may have opened a legacy project, in which case GetProjectFileName will return the
564  // name of the migrated (new format) file, which may not have been saved to disk yet.
565  if( !prjOpened && !prjReset )
566  {
567  fn.SetExt( LegacyProjectFileExtension );
568  prjOpened = fn.FileExists();
569 
570  // Set the ext back so that in the tree view we see the (not-yet-saved) new file
571  fn.SetExt( ProjectFileExtension );
572  }
573 
574  // root tree:
575  m_root = m_TreeProject->AddRoot( fn.GetFullName(), static_cast<int>( TREE_FILE_TYPE::ROOT ),
576  static_cast<int>( TREE_FILE_TYPE::ROOT ) );
577  m_TreeProject->SetItemBold( m_root, true );
578 
579  // The main project file is now a JSON file
581  fn.GetFullPath(), m_TreeProject ) );
582 
583  // Now adding all current files if available
584  if( prjOpened )
585  {
586  pro_dir = wxPathOnly( m_Parent->GetProjectFileName() );
587  wxDir dir( pro_dir );
588 
589  if( dir.IsOpened() ) // protected dirs will not open, see "man opendir()"
590  {
591  std::vector<wxString> projects = getProjects( dir );
592  wxString filename;
593  bool haveFile = dir.GetFirst( &filename );
594 
595  while( haveFile )
596  {
597  if( filename != fn.GetFullName() )
598  {
599  wxString name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
600  // Add items living in the project directory, and populate the item
601  // if it is a directory (sub directories will be not populated)
602  addItemToProjectTree( name, m_root, &projects, true );
603  }
604 
605  haveFile = dir.GetNext( &filename );
606  }
607  }
608  }
609  else
610  {
611  m_TreeProject->AppendItem( m_root, wxT( "Empty project" ) );
612  }
613 
614  m_TreeProject->Expand( m_root );
615 
616  // Sort filenames by alphabetic order
617  m_TreeProject->SortChildren( m_root );
618 }
619 
620 
621 void PROJECT_TREE_PANE::onRight( wxTreeEvent& Event )
622 {
623  wxTreeItemId curr_item = Event.GetItem();
624 
625  // Ensure item is selected (Under Windows right click does not select the item)
626  m_TreeProject->SelectItem( curr_item );
627 
628  std::vector<PROJECT_TREE_ITEM*> selection = GetSelectedData();
629 
630  bool can_switch_to_project = true;
631  bool can_create_new_directory = true;
632  bool can_open_this_directory = true;
633  bool can_edit = true;
634  bool can_rename = true;
635  bool can_delete = true;
636  bool can_print = true;
637 
638  if( selection.size() == 0 )
639  return;
640 
641  // Remove things that don't make sense for multiple selections
642  if( selection.size() != 1 )
643  {
644  can_switch_to_project = false;
645  can_create_new_directory = false;
646  can_rename = false;
647  can_print = false;
648  }
649 
650  for( PROJECT_TREE_ITEM* item : selection )
651  {
652  // Check for empty project
653  if( !item )
654  {
655  can_switch_to_project = false;
656  can_edit = false;
657  can_rename = false;
658  can_print = false;
659  continue;
660  }
661 
662  wxString full_file_name = item->GetFileName();
663 
664  switch( item->GetType() )
665  {
668  can_rename = false;
669  can_print = false;
670 
671  if( item->GetId() == m_TreeProject->GetRootItem() )
672  {
673  can_switch_to_project = false;
674  can_delete = false;
675  }
676  else
677  {
678  can_create_new_directory = false;
679  can_open_this_directory = false;
680  }
681  break;
682 
684  can_switch_to_project = false;
685  can_edit = false;
686  can_rename = false;
687  can_print = false;
688  break;
689 
690  default:
691  can_switch_to_project = false;
692  can_create_new_directory = false;
693  can_open_this_directory = false;
694 
695  if( !CanPrintFile( full_file_name ) )
696  can_print = false;
697 
698  break;
699  }
700  }
701 
702  wxMenu popup_menu;
703  wxString text;
704  wxString help_text;
705 
706  if( can_switch_to_project )
707  {
709  _( "Switch to this Project" ),
710  _( "Close all editors, and switch to the selected project" ),
712  popup_menu.AppendSeparator();
713  }
714 
715  if( can_create_new_directory )
716  {
717  AddMenuItem( &popup_menu, ID_PROJECT_NEWDIR, _( "New Directory..." ),
718  _( "Create a New Directory" ), KiBitmap( BITMAPS::directory ) );
719  }
720 
721  if( can_open_this_directory )
722  {
723  if( selection.size() == 1 )
724  {
725 #ifdef __APPLE__
726  text = _( "Reveal in Finder" );
727  help_text = _( "Reveals the directory in a Finder window" );
728 #else
729  text = _( "Open Directory in File Explorer" );
730  help_text = _( "Opens the directory in the default system file manager" );
731 #endif
732  }
733  else
734  {
735 #ifdef __APPLE__
736  text = _( "Reveal in Finder" );
737  help_text = _( "Reveals the directories in a Finder window" );
738 #else
739  text = _( "Open Directories in File Explorer" );
740  help_text = _( "Opens the directories in the default system file manager" );
741 #endif
742  }
743 
744  AddMenuItem( &popup_menu, ID_PROJECT_OPEN_DIR, text, help_text,
746  }
747 
748  if( can_edit )
749  {
750  if( selection.size() == 1 )
751  help_text = _( "Open the file in a Text Editor" );
752  else
753  help_text = _( "Open files in a Text Editor" );
754 
755  AddMenuItem( &popup_menu, ID_PROJECT_TXTEDIT, _( "Edit in a Text Editor" ),
756  help_text, KiBitmap( BITMAPS::editor ) );
757  }
758 
759  if( can_rename )
760  {
761  if( selection.size() == 1 )
762  {
763  text = _( "Rename File..." );
764  help_text = _( "Rename file" );
765  }
766  else
767  {
768  text = _( "Rename Files..." );
769  help_text = _( "Rename files" );
770  }
771 
772  AddMenuItem( &popup_menu, ID_PROJECT_RENAME, text, help_text, KiBitmap( BITMAPS::right ) );
773  }
774 
775  if( can_delete )
776  {
777  if( selection.size() == 1 )
778  help_text = _( "Delete the file and its content" );
779  else
780  help_text = _( "Delete the files and their contents" );
781 
782  if( can_switch_to_project
783  || can_create_new_directory
784  || can_open_this_directory
785  || can_edit
786  || can_rename )
787  {
788  popup_menu.AppendSeparator();
789  }
790 
791 #ifdef __WINDOWS__
792  AddMenuItem( &popup_menu, ID_PROJECT_DELETE, _( "Delete" ), help_text,
794 #else
795  AddMenuItem( &popup_menu, ID_PROJECT_DELETE, _( "Move to Trash" ), help_text,
797 #endif
798  }
799 
800  if( can_print )
801  {
802  popup_menu.AppendSeparator();
803  AddMenuItem( &popup_menu, ID_PROJECT_PRINT,
804 
805 #ifdef __APPLE__
806  _( "Print..." ),
807 #else
808  _( "Print" ),
809 #endif
810  _( "Print the contents of the file" ), KiBitmap( BITMAPS::print_button ) );
811  }
812 
813  if( popup_menu.GetMenuItemCount() > 0 )
814  PopupMenu( &popup_menu );
815 }
816 
817 
819 {
820  wxString editorname = Pgm().GetTextEditor();
821 
822  if( editorname.IsEmpty() )
823  {
824  wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
825  return;
826  }
827 
828  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
829 
830  wxString files;
831 
832  for( PROJECT_TREE_ITEM* item_data : tree_data )
833  {
834  wxString fullFileName = item_data->GetFileName();
835  QuoteString( fullFileName );
836 
837  if( !files.IsEmpty() )
838  files += " ";
839 
840  files += fullFileName;
841  }
842 
843  ExecuteFile( editorname, files );
844 }
845 
846 
847 void PROJECT_TREE_PANE::onDeleteFile( wxCommandEvent& event )
848 {
849  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
850 
851  for( PROJECT_TREE_ITEM* item_data : tree_data )
852  item_data->Delete();
853 }
854 
855 
856 void PROJECT_TREE_PANE::onPrintFile( wxCommandEvent& event )
857 {
858  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
859 
860  for( PROJECT_TREE_ITEM* item_data : tree_data )
861  item_data->Print();
862 }
863 
864 
865 void PROJECT_TREE_PANE::onRenameFile( wxCommandEvent& event )
866 {
867  wxTreeItemId curr_item = m_TreeProject->GetFocusedItem();
868  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
869 
870  // XXX: Unnecessary?
871  if( tree_data.size() != 1 )
872  return;
873 
874  wxString buffer = m_TreeProject->GetItemText( curr_item );
875  wxString msg = wxString::Format( _( "Change filename: \"%s\"" ),
876  tree_data[0]->GetFileName() );
877  wxTextEntryDialog dlg( this, msg, _( "Change filename" ), buffer );
878 
879  if( dlg.ShowModal() != wxID_OK )
880  return; // canceled by user
881 
882  buffer = dlg.GetValue();
883  buffer.Trim( true );
884  buffer.Trim( false );
885 
886  if( buffer.IsEmpty() )
887  return; // empty file name not allowed
888 
889  tree_data[0]->Rename( buffer, true );
890  m_isRenaming = true;
891 }
892 
893 
894 void PROJECT_TREE_PANE::onSelect( wxTreeEvent& Event )
895 {
896  std::vector<PROJECT_TREE_ITEM*> tree_data = GetSelectedData();
897 
898  if( tree_data.size() != 1 )
899  return;
900 
901  // Bookmark the selected item but don't try and activate it until later. If we do it now,
902  // there will be more events at least on Windows in this frame that will steal focus from
903  // any newly launched windows
904  m_selectedItem = tree_data[0];
905 }
906 
907 
908 void PROJECT_TREE_PANE::onIdle( wxIdleEvent& aEvent )
909 {
910  // Idle executes once all other events finished processing. This makes it ideal to launch
911  // a new window without starting Focus wars.
912  if( m_watcherNeedReset )
913  {
914  m_selectedItem = nullptr;
916  }
917 
918  if( m_selectedItem != nullptr )
919  {
920  // Activate launches a window which may run the event loop on top of us and cause OnIdle
921  // to get called again, so be sure to block off the activation condition first.
923  m_selectedItem = nullptr;
924 
925  item->Activate( this );
926  }
927 }
928 
929 
930 void PROJECT_TREE_PANE::onExpand( wxTreeEvent& Event )
931 {
932  wxTreeItemId itemId = Event.GetItem();
933  PROJECT_TREE_ITEM* tree_data = GetItemIdData( itemId );
934 
935  if( !tree_data )
936  return;
937 
938  if( tree_data->GetType() != TREE_FILE_TYPE::DIRECTORY )
939  return;
940 
941  // explore list of non populated subdirs, and populate them
942  wxTreeItemIdValue cookie;
943  wxTreeItemId kid = m_TreeProject->GetFirstChild( itemId, cookie );
944 
945 #ifndef __WINDOWS__
946  bool subdir_populated = false;
947 #endif
948 
949  for( ; kid.IsOk(); kid = m_TreeProject->GetNextChild( itemId, cookie ) )
950  {
951  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
952 
953  if( !itemData || itemData->GetType() != TREE_FILE_TYPE::DIRECTORY )
954  continue;
955 
956  if( itemData->IsPopulated() )
957  continue;
958 
959  wxString fileName = itemData->GetFileName();
960  wxDir dir( fileName );
961 
962  if( dir.IsOpened() )
963  {
964  std::vector<wxString> projects = getProjects( dir );
965  wxString dir_filename;
966  bool haveFile = dir.GetFirst( &dir_filename );
967 
968  while( haveFile )
969  {
970  // Add name to tree item, but do not recurse in subdirs:
971  wxString name = fileName + wxFileName::GetPathSeparator() + dir_filename;
972  addItemToProjectTree( name, kid, &projects, false );
973 
974  haveFile = dir.GetNext( &dir_filename );
975  }
976 
977  itemData->SetPopulated( true ); // set state to populated
978 
979 #ifndef __WINDOWS__
980  subdir_populated = true;
981 #endif
982  }
983 
984  // Sort filenames by alphabetic order
985  m_TreeProject->SortChildren( kid );
986  }
987 
988 #ifndef __WINDOWS__
989  if( subdir_populated )
990  m_watcherNeedReset = true;
991 #endif
992 }
993 
994 
995 std::vector<PROJECT_TREE_ITEM*> PROJECT_TREE_PANE::GetSelectedData()
996 {
997  wxArrayTreeItemIds selection;
998  std::vector<PROJECT_TREE_ITEM*> data;
999 
1000  m_TreeProject->GetSelections( selection );
1001 
1002  for( auto it = selection.begin(); it != selection.end(); it++ )
1003  data.push_back( GetItemIdData( *it ) );
1004 
1005  return data;
1006 }
1007 
1008 
1010 {
1011  return dynamic_cast<PROJECT_TREE_ITEM*>( m_TreeProject->GetItemData( aId ) );
1012 }
1013 
1014 
1015 wxTreeItemId PROJECT_TREE_PANE::findSubdirTreeItem( const wxString& aSubDir )
1016 {
1017  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
1018 
1019  // If the subdir is the current working directory, return m_root
1020  // in main list:
1021  if( prj_dir == aSubDir )
1022  return m_root;
1023 
1024  // The subdir is in the main tree or in a subdir: Locate it
1025  wxTreeItemIdValue cookie;
1026  wxTreeItemId root_id = m_root;
1027  std::stack<wxTreeItemId> subdirs_id;
1028 
1029  wxTreeItemId child = m_TreeProject->GetFirstChild( root_id, cookie );
1030 
1031  while( true )
1032  {
1033  if( ! child.IsOk() )
1034  {
1035  if( subdirs_id.empty() ) // all items were explored
1036  {
1037  root_id = child; // Not found: return an invalid wxTreeItemId
1038  break;
1039  }
1040  else
1041  {
1042  root_id = subdirs_id.top();
1043  subdirs_id.pop();
1044  child = m_TreeProject->GetFirstChild( root_id, cookie );
1045 
1046  if( !child.IsOk() )
1047  continue;
1048  }
1049  }
1050 
1051  PROJECT_TREE_ITEM* itemData = GetItemIdData( child );
1052 
1053  if( itemData && ( itemData->GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1054  {
1055  if( itemData->GetFileName() == aSubDir ) // Found!
1056  {
1057  root_id = child;
1058  break;
1059  }
1060 
1061  // child is a subdir, push in list to explore it later
1062  if( itemData->IsPopulated() )
1063  subdirs_id.push( child );
1064  }
1065 
1066  child = m_TreeProject->GetNextChild( root_id, cookie );
1067  }
1068 
1069  return root_id;
1070 }
1071 
1072 
1073 void PROJECT_TREE_PANE::onFileSystemEvent( wxFileSystemWatcherEvent& event )
1074 {
1075  // No need to process events when we're shutting down
1076  if( !m_watcher )
1077  return;
1078 
1079  const wxFileName& pathModified = event.GetPath();
1080  wxString subdir = pathModified.GetPath();
1081  wxString fn = pathModified.GetFullPath();
1082 
1083  switch( event.GetChangeType() )
1084  {
1085  case wxFSW_EVENT_DELETE:
1086  case wxFSW_EVENT_CREATE:
1087  case wxFSW_EVENT_RENAME:
1088  break;
1089 
1090  case wxFSW_EVENT_MODIFY:
1091  case wxFSW_EVENT_ACCESS:
1092  default:
1093  return;
1094  }
1095 
1096  wxTreeItemId root_id = findSubdirTreeItem( subdir );
1097 
1098  if( !root_id.IsOk() )
1099  return;
1100 
1101  wxTreeItemIdValue cookie; // dummy variable needed by GetFirstChild()
1102  wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1103 
1104  switch( event.GetChangeType() )
1105  {
1106  case wxFSW_EVENT_CREATE:
1107  {
1108  wxTreeItemId newitem =
1109  addItemToProjectTree( pathModified.GetFullPath(), root_id, nullptr, true );
1110 
1111  // If we are in the process of renaming a file, select the new one
1112  // This is needed for MSW and OSX, since we don't get RENAME events from them, just a
1113  // pair of DELETE and CREATE events.
1114  if( m_isRenaming && newitem.IsOk() )
1115  {
1116  m_TreeProject->SelectItem( newitem );
1117  m_isRenaming = false;
1118  }
1119  }
1120  break;
1121 
1122  case wxFSW_EVENT_DELETE:
1123  while( kid.IsOk() )
1124  {
1125  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
1126 
1127  if( itemData && itemData->GetFileName() == fn )
1128  {
1129  m_TreeProject->Delete( kid );
1130  return;
1131  }
1132  kid = m_TreeProject->GetNextChild( root_id, cookie );
1133  }
1134  break;
1135 
1136  case wxFSW_EVENT_RENAME :
1137  {
1138  const wxFileName& newpath = event.GetNewPath();
1139  wxString newdir = newpath.GetPath();
1140  wxString newfn = newpath.GetFullPath();
1141 
1142  while( kid.IsOk() )
1143  {
1144  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
1145 
1146  if( itemData && itemData->GetFileName() == fn )
1147  {
1148  m_TreeProject->Delete( kid );
1149  break;
1150  }
1151 
1152  kid = m_TreeProject->GetNextChild( root_id, cookie );
1153  }
1154 
1155  // Add the new item only if it is not the current project file (root item).
1156  // Remember: this code is called by a wxFileSystemWatcherEvent event, and not always
1157  // called after an actual file rename, and the cleanup code does not explore the
1158  // root item, because it cannot be renamed by the user. Also, ensure the new file
1159  // actually exists on the file system before it is readded. On Linux, moving a file
1160  // to the trash can cause the same path to be returned in both the old and new paths
1161  // of the event, even though the file isn't there anymore.
1162  PROJECT_TREE_ITEM* rootData = GetItemIdData( root_id );
1163 
1164  if( newpath.Exists() && ( newfn != rootData->GetFileName() ) )
1165  {
1166  wxTreeItemId newroot_id = findSubdirTreeItem( newdir );
1167  wxTreeItemId newitem = addItemToProjectTree( newfn, newroot_id, nullptr, true );
1168 
1169  // If the item exists, select it
1170  if( newitem.IsOk() )
1171  m_TreeProject->SelectItem( newitem );
1172  }
1173 
1174  m_isRenaming = false;
1175  }
1176  break;
1177  }
1178 
1179  // Sort filenames by alphabetic order
1180  m_TreeProject->SortChildren( root_id );
1181 }
1182 
1183 
1185 {
1186  m_watcherNeedReset = false;
1187 
1188  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
1189 
1190 #if defined( _WIN32 )
1191  if( KIPLATFORM::ENV::IsNetworkPath( prj_dir ) )
1192  {
1193  // Due to a combination of a bug in SAMBA sending bad change event IDs and wxWidgets
1194  // choosing to fault on an invalid event ID instead of sanely ignoring them we need to
1195  // avoid spawning a filewatcher. Unfortunately this punishes corporate environments with
1196  // Windows Server shares :/
1197  m_Parent->SetStatusText( _( "Network path: not monitoring folder changes" ), 1 );
1198  return;
1199  }
1200  else
1201  {
1202  m_Parent->SetStatusText( _( "Local path: monitoring folder changes" ), 1 );
1203  }
1204 #endif
1205 
1206  // Prepare file watcher:
1207  if( m_watcher )
1208  {
1209  m_watcher->RemoveAll();
1210  }
1211  else
1212  {
1213  m_watcher = new wxFileSystemWatcher();
1214  m_watcher->SetOwner( this );
1215  }
1216 
1217  // We can see wxString under a debugger, not a wxFileName
1218  wxFileName fn;
1219  fn.AssignDir( prj_dir );
1220  fn.DontFollowLink();
1221 
1222  // Add directories which should be monitored.
1223  // under windows, we add the curr dir and all subdirs
1224  // under unix, we add only the curr dir and the populated subdirs
1225  // see http://docs.wxwidgets.org/trunk/classwx_file_system_watcher.htm
1226  // under unix, the file watcher needs more work to be efficient
1227  // moreover, under wxWidgets 2.9.4, AddTree does not work properly.
1228 #ifdef __WINDOWS__
1229  m_watcher->AddTree( fn );
1230 #else
1231  m_watcher->Add( fn );
1232 
1233  if( m_TreeProject->IsEmpty() )
1234  return;
1235 
1236  // Add subdirs
1237  wxTreeItemIdValue cookie;
1238  wxTreeItemId root_id = m_root;
1239 
1240  std::stack < wxTreeItemId > subdirs_id;
1241 
1242  wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1243 
1244  while( true )
1245  {
1246  if( !kid.IsOk() )
1247  {
1248  if( subdirs_id.empty() ) // all items were explored
1249  {
1250  break;
1251  }
1252  else
1253  {
1254  root_id = subdirs_id.top();
1255  subdirs_id.pop();
1256  kid = m_TreeProject->GetFirstChild( root_id, cookie );
1257 
1258  if( !kid.IsOk() )
1259  continue;
1260  }
1261  }
1262 
1263  PROJECT_TREE_ITEM* itemData = GetItemIdData( kid );
1264 
1265  if( itemData && itemData->GetType() == TREE_FILE_TYPE::DIRECTORY )
1266  {
1267  // we can see wxString under a debugger, not a wxFileName
1268  const wxString& path = itemData->GetFileName();
1269 
1270  wxLogTrace( tracePathsAndFiles, "%s: add '%s'\n", __func__, TO_UTF8( path ) );
1271 
1272  if( wxFileName::IsDirReadable( path ) ) // linux whines about watching protected dir
1273  {
1274  fn.AssignDir( path );
1275  m_watcher->Add( fn );
1276 
1277  // if kid is a subdir, push in list to explore it later
1278  if( itemData->IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1279  subdirs_id.push( kid );
1280  }
1281  }
1282 
1283  kid = m_TreeProject->GetNextChild( root_id, cookie );
1284  }
1285 #endif
1286 
1287 #if defined(DEBUG) && 1
1288  wxArrayString paths;
1289  m_watcher->GetWatchedPaths( &paths );
1290  wxLogTrace( tracePathsAndFiles, "%s: watched paths:", __func__ );
1291 
1292  for( unsigned ii = 0; ii < paths.GetCount(); ii++ )
1293  wxLogTrace( tracePathsAndFiles, " %s\n", TO_UTF8( paths[ii] ) );
1294 #endif
1295 }
1296 
1297 
1299 {
1300  // Make sure we don't try to inspect the tree after we've deleted its items.
1302 
1303  m_TreeProject->DeleteAllItems();
1304 }
1305 
1306 
1307 void PROJECT_TREE_PANE::onThemeChanged( wxSysColourChangedEvent &aEvent )
1308 {
1311  m_TreeProject->Refresh();
1312 
1313  aEvent.Skip();
1314 }
1315 
1316 
1317 void KICAD_MANAGER_FRAME::OnChangeWatchedPaths( wxCommandEvent& aEvent )
1318 {
1320 }
1321 
1322 
const std::string NetlistFileExtension
const wxString & GetFileName() const
void onRight(wxTreeEvent &Event)
Called on a right click on an item.
void onCreateNewDirectory(wxCommandEvent &event)
Function onCreateNewDirectory Creates a new subdirectory inside the current kicad project directory t...
IDs used in KiCad main frame foe menuitems and tools.
const wxString GetProjectFileName() const
bool IsPopulated() const
const std::string KiCadFootprintFileExtension
void Activate(PROJECT_TREE_PANE *aTreePrjFrame)
const std::string ProjectFileExtension
TREE_FILE_TYPE GetType() const
const std::string LegacyPcbFileExtension
const std::string LegacySymbolLibFileExtension
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
Definition: bitmap.cpp:257
void onRenameFile(wxCommandEvent &event)
Function onRenameFile Rename the selected file or directory in the tree project.
wxTreeItemId findSubdirTreeItem(const wxString &aSubDir)
Function findSubdirTreeItem searches for the item in tree project which is the node of the subdirecto...
std::vector< wxString > m_filters
const std::string DesignRulesFileExtension
TREE_FILE_TYPE
void SetPopulated(bool aValue)
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
void onDeleteFile(wxCommandEvent &event)
Function onDeleteFile Delete the selected file or directory in the tree project.
void ReCreateTreePrj()
Create or modify the tree showing project file names.
const std::string KiCadPcbFileExtension
void onOpenSelectedFileWithTextEditor(wxCommandEvent &event)
Function onOpenSelectedFileWithTextEditor Call the text editor to open the selected file in the tree ...
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
PROJECT_TREE_ITEM * m_selectedItem
PROJECT_TREE_PANE * m_leftWin
void onSwitchToSelectedProject(wxCommandEvent &event)
Switch to a other project selected from the tree project (by selecting an other .pro file inside the ...
void SetState(int state)
void onExpand(wxTreeEvent &Event)
Called on a click on the + or - button of an item with children.
This file contains miscellaneous commonly used macros and functions.
PROJECT_TREE_PANE Window to display the tree files.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
const std::string HtmlFileExtension
const std::string FootprintAssignmentFileExtension
void QuoteString(wxString &string)
Add un " to the start and the end of string (if not already done).
Definition: gestfich.cpp:42
PROJECT_TREE * m_TreeProject
int ExecuteFile(const wxString &ExecFile, const wxString &param, wxProcess *callback)
Call the executable file ExecFile with the command line parameters param.
Definition: gestfich.cpp:115
const wxString GerberFileExtensionWildCard(".((gbr|gbrjob|(gb|gt)[alops])|pho)")
const std::string GerberJobFileExtension
Definition of file extensions used in Kicad.
friend class PROJECT_TREE_ITEM
void OnChangeWatchedPaths(wxCommandEvent &aEvent)
Called by sending a event with id = ID_INIT_WATCHED_PATHS rebuild the list of watched paths.
#define _(s)
wxLogTrace helper definitions.
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
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
const std::string LegacyProjectFileExtension
const std::string PdfFileExtension
const std::string TextFileExtension
void LoadIcons()
const std::string LegacySchematicFileExtension
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void onIdle(wxIdleEvent &aEvent)
Idle event handler, used process the selected items at a point in time when all other events have bee...
static const wxChar * s_allowedExtensionsToList[]
bool IsNetworkPath(const wxString &aPath)
Determines if a given path is a network shared file apth On Windows for example, any form of path is ...
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition: paths.cpp:139
const wxString GetDir() const
const std::string DrawingSheetFileExtension
const char * name
Definition: DXF_plotter.cpp:56
void onThemeChanged(wxSysColourChangedEvent &aEvent)
const std::string ReportFileExtension
void FileWatcherReset()
Reinit the watched paths Should be called after opening a new project to rebuild the list of watched ...
void onOpenDirectory(wxCommandEvent &event)
Function onOpenDirectory Handles the right-click menu for opening a directory in the current system f...
std::vector< PROJECT_TREE_ITEM * > GetSelectedData()
Function GetSelectedData return the item data from item currently selected (highlighted) Note this is...
void EmptyTreePrj()
Delete all m_TreeProject entries.
#define NAMELESS_PROJECT
default name for nameless projects
Definition: project.h:41
const std::string KiCadSchematicFileExtension
static wxString GetFileExt(TREE_FILE_TYPE type)
const std::string SVGFileExtension
wxFileSystemWatcher * m_watcher
Handle one item (a file or a directory name) for the tree file.
const std::string FootprintPlaceFileExtension
void onFileSystemEvent(wxFileSystemWatcherEvent &event)
called when a file or directory is modified/created/deleted The tree project is modified when a file ...
void SetRootFile(bool aValue)
void LoadProject(const wxFileName &aProjectFileName)
wxTreeItemId addItemToProjectTree(const wxString &aName, const wxTreeItemId &aParent, std::vector< wxString > *aProjectNames, bool aRecurse)
Function addItemToProjectTree.
void shutdownFileWatcher()
Shutdown the file watcher.
KICAD_MANAGER_FRAME * m_Parent
void onSelect(wxTreeEvent &Event)
Called on a double click on an item.
The main KiCad project manager frame.
bool CanPrintFile(const wxString &file)
Definition: gestfich.cpp:328
PROJECT_TREE_ITEM * GetItemIdData(wxTreeItemId aId)
Function GetItemIdData return the item data corresponding to a wxTreeItemId identifier.
std::vector< wxString > getProjects(const wxDir &dir)
const std::string DrillFileExtension
const std::string KiCadSymbolLibFileExtension
PROJECT_TREE This is the class to show (as a tree) the files in the project directory.
Definition: project_tree.h:38
BITMAP_STORE * GetBitmapStore()
Definition: bitmap.cpp:93
void onPrintFile(wxCommandEvent &event)
Function onDeleteFile Print the selected file or directory in the tree project.