KiCad PCB EDA Suite
tree_project_frame.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-2017 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 
32 #include <stack>
33 
34 #include <wx/regex.h>
35 #include <wx/stdpaths.h>
36 #include <wx/string.h>
37 
38 #include <bitmaps.h>
39 #include <gestfich.h>
40 #include <menus_helpers.h>
42 
43 #include "class_treeproject_item.h"
44 #include "class_treeprojectfiles.h"
45 #include "pgm_kicad.h"
46 
47 #include "tree_project_frame.h"
48 
49 
50 /* Note about the tree project build process:
51  * Building the tree project can be *very* long if there are a lot of subdirectories
52  * in the working directory.
53  * Unfortunately, this happens easily if the project file *.pro is in the home directory
54  * So the tree project is built "on demand":
55  * First the tree is built from the current directory and shows files and subdirs.
56  * > First level subdirs trees are built (i.e subdirs contents are not read)
57  * > When expanding a subdir, each subdir contains is read,
58  * and the corresponding sub tree is populated on the fly.
59  */
60 
61 // list of files extensions listed in the tree project window
62 // *.sch files are always allowed, do not add here
63 // Add extensions in a compatible regex format to see others files types
64 static const wxChar* s_allowedExtensionsToList[] =
65 {
66  wxT( "^.*\\.pro$" ),
67  wxT( "^.*\\.pdf$" ),
68  wxT( "^[^$].*\\.brd$" ), // Legacy Pcbnew files
69  wxT( "^[^$].*\\.kicad_pcb$" ), // S format Pcbnew board files
70  wxT( "^[^$].*\\.kicad_wks$" ), // S format kicad page layout descr files
71  wxT( "^[^$].*\\.kicad_mod$" ), // S format kicad footprint files, currently not listed
72  wxT( "^.*\\.net$" ), // pcbnew netlist file
73  wxT( "^.*\\.cir$" ), // Spice netlist file
74  wxT( "^.*\\.lib$" ), // Schematic library file
75  wxT( "^.*\\.txt$" ),
76  wxT( "^.*\\.pho$" ), // Gerber file (Old Kicad extension)
77  wxT( "^.*\\.gbr$" ), // Gerber file
78  wxT( "^.*\\.gbrjob$" ), // Gerber job file
79  wxT( "^.*\\.gb[alops]$" ), // Gerber back (or bottom) layer file (deprecated Protel ext)
80  wxT( "^.*\\.gt[alops]$" ), // Gerber front (or top) layer file (deprecated Protel ext)
81  wxT( "^.*\\.g[0-9]{1,2}$" ), // Gerber inner layer file (deprecated Protel ext)
82  wxT( "^.*\\.odt$" ),
83  wxT( "^.*\\.htm$" ),
84  wxT( "^.*\\.html$" ),
85  wxT( "^.*\\.rpt$" ), // Report files
86  wxT( "^.*\\.csv$" ), // Report files in comma separated format
87  wxT( "^.*\\.pos$" ), // Footprint position files
88  wxT( "^.*\\.cmp$" ), // Cvpcb cmp/footprint link files
89  wxT( "^.*\\.drl$" ), // Excellon drill files
90  wxT( "^.*\\.svg$" ), // SVG print/plot files
91  NULL // end of list
92 };
93 
94 
95 /* TODO: Check if these file extension and wildcard definitions are used
96  * in any of the other KiCad programs and move them into the common
97  * library as required.
98  */
99 
100 // File extension definitions.
101 const wxChar TextFileExtension[] = wxT( "txt" );
102 
103 // Gerber file extension wildcard.
104 const wxString GerberFileExtensionWildCard( ".((gbr|gbrjob|(gb|gt)[alops])|pho)" );
105 
106 
115 BEGIN_EVENT_TABLE( TREE_PROJECT_FRAME, wxSashLayoutWindow )
116  EVT_TREE_ITEM_ACTIVATED( ID_PROJECT_TREE, TREE_PROJECT_FRAME::OnSelect )
117  EVT_TREE_ITEM_EXPANDED( ID_PROJECT_TREE, TREE_PROJECT_FRAME::OnExpand )
118  EVT_TREE_ITEM_RIGHT_CLICK( ID_PROJECT_TREE, TREE_PROJECT_FRAME::OnRight )
119  EVT_MENU( ID_PROJECT_TXTEDIT, TREE_PROJECT_FRAME::OnOpenSelectedFileWithTextEditor )
120  EVT_MENU( ID_PROJECT_NEWDIR, TREE_PROJECT_FRAME::OnCreateNewDirectory )
123 END_EVENT_TABLE()
124 
125 
127  wxSashLayoutWindow( parent, ID_LEFT_FRAME,
128  wxDefaultPosition, wxDefaultSize,
129  wxNO_BORDER | wxTAB_TRAVERSAL )
130 {
131  m_Parent = parent;
132  m_TreeProject = NULL;
133 
134  m_watcher = NULL;
135  Connect( wxEVT_FSWATCHER,
136  wxFileSystemWatcherEventHandler( TREE_PROJECT_FRAME::OnFileSystemEvent ) );
137 
138  /*
139  * Filtering is now inverted: the filters are actually used to _enable_ support
140  * for a given file type.
141  */
142 
143  // NOTE: sch filter must be first because of a test in AddFile() below
144  m_filters.push_back( wxT( "^.*\\.sch$" ) );
145 
146  for( int ii = 0; s_allowedExtensionsToList[ii] != NULL; ii++ )
147  m_filters.push_back( s_allowedExtensionsToList[ii] );
148 
149  m_filters.push_back( wxT( "^no KiCad files found" ) );
150 
151  ReCreateTreePrj();
152 }
153 
154 
156 {
157  if( m_watcher )
158  {
159  m_watcher->RemoveAll();
160  m_watcher->SetOwner( NULL );
161  delete m_watcher;
162  }
163 }
164 
165 
166 void TREE_PROJECT_FRAME::RemoveFilter( const wxString& filter )
167 {
168  for( unsigned int i = 0; i < m_filters.size(); i++ )
169  {
170  if( filter == m_filters[i] )
171  {
172  m_filters.erase( m_filters.begin() + i );
173  return;
174  }
175  }
176 }
177 
178 
179 void TREE_PROJECT_FRAME::OnCreateNewDirectory( wxCommandEvent& event )
180 {
181  // Get the root directory name:
182  TREEPROJECT_ITEM* treeData = GetSelectedData();
183 
184  if( !treeData )
185  return;
186 
187  TreeFileType rootType = treeData->GetType();
188  wxTreeItemId root;
189 
190  if( TREE_DIRECTORY == rootType )
191  {
192  root = m_TreeProject->GetSelection();
193  }
194  else
195  {
196  root = m_TreeProject->GetItemParent( m_TreeProject->GetSelection() );
197 
198  if( !root.IsOk() )
199  root = m_TreeProject->GetSelection();
200  }
201 
202  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
203 
204  // Ask for the new sub directory name
205  wxString curr_dir = treeData->GetDir();
206 
207  if( !curr_dir.IsEmpty() ) // A subdir is selected
208  {
209  // Make this subdir name relative to the current path.
210  // It will be more easy to read by the user, in the next dialog
211  wxFileName fn;
212  fn.AssignDir( curr_dir );
213  fn.MakeRelativeTo( prj_dir );
214  curr_dir = fn.GetPath();
215 
216  if( !curr_dir.IsEmpty() )
217  curr_dir += wxFileName::GetPathSeparator();
218  }
219 
220  wxString msg = wxString::Format( _( "Current project directory:\n%s" ), GetChars( prj_dir ) );
221  wxString subdir = wxGetTextFromUser( msg, _( "Create New Directory" ), curr_dir );
222 
223  if( subdir.IsEmpty() )
224  return;
225 
226  wxString full_dirname = prj_dir + wxFileName::GetPathSeparator() + subdir;
227 
228  if( wxMkdir( full_dirname ) )
229  {
230  // the new itel will be added by the file watcher
231  // AddItemToTreeProject( subdir, root );
232  }
233 }
234 
235 
237 {
238  wxString ext;
239 
240  switch( type )
241  {
242  case TREE_PROJECT:
243  ext = ProjectFileExtension;
244  break;
245 
246  case TREE_SCHEMA:
248  break;
249 
250  case TREE_LEGACY_PCB:
252  break;
253 
254  case TREE_SEXP_PCB:
255  ext = KiCadPcbFileExtension;
256  break;
257 
258  case TREE_GERBER:
260  break;
261 
262  case TREE_HTML:
263  ext = HtmlFileExtension;
264  break;
265 
266  case TREE_PDF:
267  ext = PdfFileExtension;
268  break;
269 
270  case TREE_TXT:
271  ext = TextFileExtension;
272  break;
273 
274  case TREE_NET:
275  ext = NetlistFileExtension;
276  break;
277 
278  case TREE_CMP_LINK:
280  break;
281 
282  case TREE_REPORT:
283  ext = ReportFileExtension;
284  break;
285 
286  case TREE_FP_PLACE:
288  break;
289 
290  case TREE_DRILL:
291  ext = DrillFileExtension;
292  break;
293 
294  case TREE_SVG:
295  ext = SVGFileExtension;
296  break;
297 
300  break;
301 
302  case TREE_FOOTPRINT_FILE:
304  break;
305 
308  break;
309 
310  default: // Eliminates unnecessary GCC warning.
311  break;
312  }
313 
314  return ext;
315 }
316 
317 
318 /*
319  * Return the wxFileDialog wildcard string for the selected file type.
320  */
322 {
323  wxString ext;
324 
325  switch( type )
326  {
327  case TREE_PROJECT:
328  ext = ProjectFileWildcard();
329  break;
330 
331  case TREE_SCHEMA:
332  ext = SchematicFileWildcard();
333  break;
334 
335  case TREE_LEGACY_PCB:
336  case TREE_SEXP_PCB:
337  ext = PcbFileWildcard();
338  break;
339 
340  case TREE_GERBER:
341  ext = GerberFileWildcard();
342  break;
343 
344  case TREE_HTML:
345  ext = HtmlFileWildcard();
346  break;
347 
348  case TREE_PDF:
349  ext = PdfFileWildcard();
350  break;
351 
352  case TREE_TXT:
353  ext = TextFileWildcard();
354  break;
355 
356  case TREE_NET:
357  ext = NetlistFileWildcard();
358  break;
359 
360  case TREE_CMP_LINK:
361  ext = ComponentFileWildcard();
362  break;
363 
364  case TREE_REPORT:
365  ext = ReportFileWildcard();
366  break;
367 
368  case TREE_FP_PLACE:
370  break;
371 
372  case TREE_DRILL:
373  ext = DrillFileWildcard();
374  break;
375 
376  case TREE_SVG:
377  ext = SVGFileWildcard();
378  break;
379 
382  break;
383 
384  case TREE_FOOTPRINT_FILE:
386  break;
387 
390  break;
391 
392  default: // Eliminates unnecessary GCC warning.
393  break;
394  }
395 
396  return ext;
397 }
398 
399 
400 bool TREE_PROJECT_FRAME::AddItemToTreeProject( const wxString& aName,
401  wxTreeItemId& aRoot, bool aRecurse )
402 {
403  wxTreeItemId cellule;
404 
405  // Check the file type
406  TreeFileType type = TREE_UNKNOWN;
407 
408  // Skip not visible files and dirs
409  wxFileName fn( aName );
410 
411  // Files/dirs names starting by "." are not visible files under unices.
412  // Skip them also under Windows
413  if( fn.GetName().StartsWith( wxT( "." ) ) )
414  return false;
415 
416  if( wxDirExists( aName ) )
417  {
418  type = TREE_DIRECTORY;
419  }
420  else
421  {
422  // Filter
423  wxRegEx reg;
424 
425  bool isSchematic = false;
426  bool addFile = false;
427 
428  for( unsigned i = 0; i < m_filters.size(); i++ )
429  {
430  wxCHECK2_MSG( reg.Compile( m_filters[i], wxRE_ICASE ), continue,
431  wxT( "Regular expression " ) + m_filters[i] +
432  wxT( " failed to compile." ) );
433 
434  if( reg.Matches( aName ) )
435  {
436  addFile = true;
437 
438  if( i==0 )
439  isSchematic = true;
440 
441  break;
442  }
443  }
444 
445  if( !addFile )
446  return false;
447 
448  // only show the schematic if it is a top level schematic. Eeschema
449  // cannot open a schematic and display it properly unless it starts
450  // at the top of the hierarchy. The schematic is top level only if
451  // there is a line in the header saying:
452  // "Sheet 1 "
453  // However if the file has the same name as the project, it is always
454  // shown, because it is expected the root sheet.
455  // (and to fix an issue (under XP but could exist under other OS),
456  // when a .sch file is created, the file
457  // create is sent to the wxFileSystemWatcher, but the file still has 0 byte
458  // so it cannot detected as root sheet
459  // This is an ugly fix.
460  if( isSchematic )
461  {
462  wxString fullFileName = aName.BeforeLast( '.' );
463  wxString rootName;
464  TREEPROJECT_ITEM* itemData = GetItemIdData( m_root );
465  if( itemData )
466  rootName = itemData->GetFileName().BeforeLast( '.' );
467 
468  if( fullFileName != rootName )
469  {
470  char line[128]; // small because we just need a few bytes from the start of a line
471  FILE* fp;
472 
473  fullFileName = aName;
474  fp = wxFopen( fullFileName, wxT( "rt" ) );
475 
476  if( fp == NULL )
477  return false;
478 
479  addFile = false;
480 
481  // check the first 100 lines for the "Sheet 1" string
482  for( int i = 0; i<100; ++i )
483  {
484  if( !fgets( line, sizeof(line), fp ) )
485  break;
486 
487  if( !strncmp( line, "Sheet 1 ", 8 ) )
488  {
489  addFile = true;
490  break;
491  }
492  }
493 
494  fclose( fp );
495 
496  if( !addFile )
497  return false; // it is a non-top-level schematic
498  }
499  }
500 
501  for( int i = TREE_PROJECT; i < TREE_MAX; i++ )
502  {
503  wxString ext = GetFileExt( (TreeFileType) i );
504 
505  if( ext == wxT( "" ) )
506  continue;
507 
508  reg.Compile( wxString::FromAscii( "^.*\\" ) + ext +
509  wxString::FromAscii( "$" ), wxRE_ICASE );
510 
511  if( reg.Matches( aName ) )
512  {
513  type = (TreeFileType) i;
514  break;
515  }
516  }
517  }
518 
519  // also check to see if it is already there.
520  wxTreeItemIdValue cookie;
521  wxTreeItemId kid = m_TreeProject->GetFirstChild( aRoot, cookie );
522 
523  while( kid.IsOk() )
524  {
525  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
526 
527  if( itemData )
528  {
529  if( itemData->GetFileName() == aName )
530  {
531  return true; // well, we would have added it, but it is already here!
532  }
533  }
534 
535  kid = m_TreeProject->GetNextChild( aRoot, cookie );
536  }
537 
538  // Append the item (only appending the filename not the full path):
539  wxString file = wxFileNameFromPath( aName );
540  cellule = m_TreeProject->AppendItem( aRoot, file );
541  TREEPROJECT_ITEM* data = new TREEPROJECT_ITEM( type, aName, m_TreeProject );
542 
543  m_TreeProject->SetItemData( cellule, data );
544  data->SetState( 0 );
545 
546  // Mark root files (files which have the same aName as the project)
547  wxFileName project( m_Parent->GetProjectFileName() );
548  wxFileName currfile( file );
549 
550  if( currfile.GetName().CmpNoCase( project.GetName() ) == 0 )
551  data->SetRootFile( true );
552  else
553  data->SetRootFile( false );
554 
555  // This section adds dirs and files found in the subdirs
556  // in this case AddFile is recursive, but for the first level only.
557  if( TREE_DIRECTORY == type && aRecurse )
558  {
559  wxDir dir( aName );
560 
561  if( dir.IsOpened() ) // protected dirs will not open properly.
562  {
563  wxString dir_filename;
564 
565  data->SetPopulated( true );
566 
567  if( dir.GetFirst( &dir_filename ) )
568  {
569  do // Add name in tree, but do not recurse
570  {
571  wxString path = aName + wxFileName::GetPathSeparator() + dir_filename;
572  AddItemToTreeProject( path, cellule, false );
573  } while( dir.GetNext( &dir_filename ) );
574  }
575  }
576 
577  // Sort filenames by alphabetic order
578  m_TreeProject->SortChildren( cellule );
579  }
580 
581  return true;
582 }
583 
584 
586 {
587  wxTreeItemId rootcellule;
588  bool prjOpened = false;
589  wxString pro_dir = m_Parent->GetProjectFileName();
590 
591  if( !m_TreeProject )
592  m_TreeProject = new TREEPROJECTFILES( this );
593  else
594  m_TreeProject->DeleteAllItems();
595 
596  if( !pro_dir ) // This is empty from TREE_PROJECT_FRAME constructor
597  return;
598 
599  wxFileName fn = pro_dir;
600 
601  if( !fn.IsOk() )
602  {
603  fn.Clear();
604  fn.SetPath( wxStandardPaths::Get().GetDocumentsDir() );
605  fn.SetName( NAMELESS_PROJECT );
606  fn.SetExt( ProjectFileExtension );
607  }
608 
609  prjOpened = fn.FileExists();
610 
611  // root tree:
612  m_root = rootcellule = m_TreeProject->AddRoot( fn.GetFullName(),
613  TREE_PROJECT - 1,
614  TREE_PROJECT - 1 );
615 
616  m_TreeProject->SetItemBold( rootcellule, true );
617 
618  m_TreeProject->SetItemData( rootcellule,
620  fn.GetFullPath(),
621  m_TreeProject ) );
622 
623  // Now adding all current files if available
624  if( prjOpened )
625  {
626  pro_dir = wxPathOnly( m_Parent->GetProjectFileName() );
627  wxDir dir( pro_dir );
628 
629  if( dir.IsOpened() ) // protected dirs will not open, see "man opendir()"
630  {
631  wxString filename;
632  bool cont = dir.GetFirst( &filename );
633 
634  while( cont )
635  {
636  if( filename != fn.GetFullName() )
637  {
638  wxString name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
639  AddItemToTreeProject( name, m_root );
640  }
641 
642  cont = dir.GetNext( &filename );
643  }
644  }
645  }
646  else
647  {
648  m_TreeProject->AppendItem( m_root, wxT( "Empty project" ) );
649  }
650 
651  m_TreeProject->Expand( rootcellule );
652 
653  // Sort filenames by alphabetic order
654  m_TreeProject->SortChildren( m_root );
655 }
656 
657 
658 void TREE_PROJECT_FRAME::OnRight( wxTreeEvent& Event )
659 {
660  int tree_id;
661  TREEPROJECT_ITEM* tree_data;
662  wxString fullFileName;
663  wxTreeItemId curr_item = Event.GetItem();
664 
665  // Ensure item is selected (Under Windows right click does not select the item)
666  m_TreeProject->SelectItem( curr_item );
667 
668  tree_data = GetSelectedData();
669 
670  if( !tree_data )
671  return;
672 
673  tree_id = tree_data->GetType();
674  fullFileName = tree_data->GetFileName();
675 
676  wxMenu popupMenu;
677 
678  switch( tree_id )
679  {
680  case TREE_PROJECT:
681  AddMenuItem( &popupMenu, ID_PROJECT_NEWDIR,
682  _( "New D&irectory" ),
683  _( "Create a New Directory" ),
684  KiBitmap( directory_xpm ) );
685  break;
686 
687  case TREE_DIRECTORY:
688  AddMenuItem( &popupMenu, ID_PROJECT_NEWDIR,
689  _( "New D&irectory" ),
690  _( "Create a New Directory" ),
691  KiBitmap( directory_xpm ) );
692  AddMenuItem( &popupMenu, ID_PROJECT_DELETE,
693  _( "&Delete Directory" ),
694  _( "Delete the Directory and its content" ),
695  KiBitmap( delete_xpm ) );
696  break;
697 
698  default:
699  AddMenuItem( &popupMenu, ID_PROJECT_TXTEDIT,
700  _( "&Edit in a text editor" ),
701  _( "Open the file in a Text Editor" ),
702  KiBitmap( editor_xpm ) );
703  AddMenuItem( &popupMenu, ID_PROJECT_RENAME,
704  _( "&Rename file" ),
705  _( "Rename file" ),
706  KiBitmap( right_xpm ) );
707  AddMenuItem( &popupMenu, ID_PROJECT_DELETE,
708  _( "&Delete File" ),
709  _( "Delete the Directory and its content" ),
710  KiBitmap( delete_xpm ) );
711  break;
712  }
713 
714  PopupMenu( &popupMenu );
715 }
716 
717 
719 {
720  TREEPROJECT_ITEM* tree_data = GetSelectedData();
721 
722  if( !tree_data )
723  return;
724 
725  if( tree_data->GetType() == TREE_DIRECTORY )
726  return;
727 
728  wxString fullFileName = tree_data->GetFileName();
729  AddDelimiterString( fullFileName );
730  wxString editorname = Pgm().GetEditorName();
731 
732  if( !editorname.IsEmpty() )
733  ExecuteFile( this, editorname, fullFileName );
734 }
735 
736 
737 void TREE_PROJECT_FRAME::OnDeleteFile( wxCommandEvent& )
738 {
739  TREEPROJECT_ITEM* tree_data = GetSelectedData();
740 
741  if( !tree_data )
742  return;
743 
744  tree_data->Delete();
745 }
746 
747 
748 void TREE_PROJECT_FRAME::OnRenameFile( wxCommandEvent& )
749 {
750  wxTreeItemId curr_item = m_TreeProject->GetSelection();
751  TREEPROJECT_ITEM* tree_data = GetSelectedData();
752 
753  if( !tree_data )
754  return;
755 
756  wxString buffer = m_TreeProject->GetItemText( curr_item );
757  wxString msg = wxString::Format(
758  _( "Change filename: '%s'" ),
759  GetChars( tree_data->GetFileName() ) );
760 
761  wxTextEntryDialog dlg( this, msg, _( "Change filename" ), buffer );
762 
763  if( dlg.ShowModal() != wxID_OK )
764  return; // canceled by user
765 
766  buffer = dlg.GetValue();
767  buffer.Trim( true );
768  buffer.Trim( false );
769 
770  if( buffer.IsEmpty() )
771  return; // empty file name not allowed
772 
773  if( tree_data->Rename( buffer, true ) )
774  m_TreeProject->SetItemText( curr_item, buffer );
775 }
776 
777 
778 void TREE_PROJECT_FRAME::OnSelect( wxTreeEvent& Event )
779 {
780  TREEPROJECT_ITEM* tree_data = GetSelectedData();
781 
782  if( !tree_data )
783  return;
784 
785  tree_data->Activate( this );
786 }
787 
788 
789 void TREE_PROJECT_FRAME::OnExpand( wxTreeEvent& Event )
790 {
791  wxTreeItemId itemId = Event.GetItem();
792  TREEPROJECT_ITEM* tree_data = GetItemIdData( itemId );
793 
794  if( !tree_data )
795  return;
796 
797  if( tree_data->GetType() != TREE_DIRECTORY )
798  return;
799 
800  // explore list of non populated subdirs, and populate them
801  wxTreeItemIdValue cookie;
802  wxTreeItemId kid = m_TreeProject->GetFirstChild( itemId, cookie );
803 
804  bool subdir_populated = false;
805 
806  for( ; kid.IsOk(); kid = m_TreeProject->GetNextChild( itemId, cookie ) )
807  {
808  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
809 
810  if( !itemData || itemData->GetType() != TREE_DIRECTORY )
811  continue;
812 
813  if( itemData->IsPopulated() )
814  continue;
815 
816  wxString fileName = itemData->GetFileName();
817  wxDir dir( fileName );
818 
819  if( dir.IsOpened() )
820  {
821  wxString dir_filename;
822 
823  if( dir.GetFirst( &dir_filename ) )
824  {
825  do // Add name to tree item, but do not recurse in subdirs:
826  {
827  wxString name = fileName + wxFileName::GetPathSeparator() + dir_filename;
828  AddItemToTreeProject( name, kid, false );
829  } while( dir.GetNext( &dir_filename ) );
830  }
831 
832  itemData->SetPopulated( true ); // set state to populated
833  subdir_populated = true;
834  }
835 
836  // Sort filenames by alphabetic order
837  m_TreeProject->SortChildren( kid );
838  }
839 
840  if( subdir_populated )
841  {
842  #ifndef __WINDOWS__
844  #endif
845  }
846 }
847 
848 
850 {
851  return dynamic_cast<TREEPROJECT_ITEM*>( m_TreeProject->GetItemData
852  ( m_TreeProject->GetSelection() ) );
853 }
854 
855 
857 {
858  return dynamic_cast<TREEPROJECT_ITEM*>( m_TreeProject->GetItemData( aId ) );
859 }
860 
861 
862 wxTreeItemId TREE_PROJECT_FRAME::findSubdirTreeItem( const wxString& aSubDir )
863 {
864  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
865 
866  // If the subdir is the current working directory, return m_root
867  // in main list:
868  if( prj_dir == aSubDir )
869  return m_root;
870 
871  // The subdir is in the main tree or in a subdir: Locate it
872  wxTreeItemIdValue cookie;
873  wxTreeItemId root_id = m_root;
874  std::stack < wxTreeItemId > subdirs_id;
875 
876  wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
877 
878  while( 1 )
879  {
880  if( ! kid.IsOk() )
881  {
882  if( subdirs_id.empty() ) // all items were explored
883  {
884  root_id = kid; // Not found: return an invalid wxTreeItemId
885  break;
886  }
887  else
888  {
889  root_id = subdirs_id.top();
890  subdirs_id.pop();
891  kid = m_TreeProject->GetFirstChild( root_id, cookie );
892 
893  if( ! kid.IsOk() )
894  continue;
895  }
896  }
897 
898  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
899 
900  if( itemData && ( itemData->GetType() == TREE_DIRECTORY ) )
901  {
902  if( itemData->GetFileName() == aSubDir ) // Found!
903  {
904  root_id = kid;
905  break;
906  }
907 
908  // kid is a subdir, push in list to explore it later
909  if( itemData->IsPopulated() )
910  subdirs_id.push( kid );
911  }
912 
913  kid = m_TreeProject->GetNextChild( root_id, cookie );
914  }
915 
916  return root_id;
917 }
918 
919 
920 void TREE_PROJECT_FRAME::OnFileSystemEvent( wxFileSystemWatcherEvent& event )
921 {
922  wxFileName pathModified = event.GetPath();
923  wxString subdir = pathModified.GetPath();
924  wxString fn = pathModified.GetFullPath();
925 
926  switch( event.GetChangeType() )
927  {
928  case wxFSW_EVENT_DELETE:
929  break;
930 
931  case wxFSW_EVENT_CREATE:
932  break;
933 
934  case wxFSW_EVENT_RENAME:
935  break;
936 
937  case wxFSW_EVENT_MODIFY:
938  case wxFSW_EVENT_ACCESS:
939  default:
940  return;
941  }
942 
943  wxTreeItemId root_id = findSubdirTreeItem( subdir );
944 
945  if( !root_id.IsOk() )
946  return;
947 
948  wxTreeItemIdValue cookie; // dummy variable needed by GetFirstChild()
949  wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
950 
951  switch( event.GetChangeType() )
952  {
953  case wxFSW_EVENT_CREATE:
954  AddItemToTreeProject( pathModified.GetFullPath(), root_id, false );
955  break;
956 
957  case wxFSW_EVENT_DELETE:
958  while( kid.IsOk() )
959  {
960  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
961 
962  if( itemData && itemData->GetFileName() == fn )
963  {
964  m_TreeProject->Delete( kid );
965  return;
966  }
967  kid = m_TreeProject->GetNextChild( root_id, cookie );
968  }
969  break;
970 
971  case wxFSW_EVENT_RENAME :
972  {
973  wxFileName newpath = event.GetNewPath();
974  wxString newfn = newpath.GetFullPath();
975 
976  while( kid.IsOk() )
977  {
978  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
979 
980  if( itemData && itemData->GetFileName() == fn )
981  {
982  m_TreeProject->Delete( kid );
983  break;
984  }
985 
986  kid = m_TreeProject->GetNextChild( root_id, cookie );
987  }
988 
989  AddItemToTreeProject( newfn, root_id, false );
990  }
991  break;
992  }
993 
994  // Sort filenames by alphabetic order
995  m_TreeProject->SortChildren( root_id );
996 }
997 
998 
1000 {
1001  // Prepare file watcher:
1002  if( m_watcher )
1003  {
1004  m_watcher->RemoveAll();
1005  }
1006  else
1007  {
1008  m_watcher = new wxFileSystemWatcher();
1009  m_watcher->SetOwner( this );
1010  }
1011 
1012  // Add directories which should be monitored.
1013  // under windows, we add the curr dir and all subdirs
1014  // under unix, we add only the curr dir and the populated subdirs
1015  // see http://docs.wxwidgets.org/trunk/classwx_file_system_watcher.htm
1016  // under unix, the file watcher needs more work to be efficient
1017  // moreover, under wxWidgets 2.9.4, AddTree does not work properly.
1018 
1019  // We can see wxString under a debugger, not a wxFileName
1020  wxString prj_dir = wxPathOnly( m_Parent->GetProjectFileName() );
1021  wxFileName fn;
1022  fn.AssignDir( prj_dir );
1023  fn.DontFollowLink();
1024 
1025 #ifdef __WINDOWS__
1026  m_watcher->AddTree( fn );
1027 #else
1028  m_watcher->Add( fn );
1029 
1030  // Add subdirs
1031  wxTreeItemIdValue cookie;
1032  wxTreeItemId root_id = m_root;
1033 
1034  std::stack < wxTreeItemId > subdirs_id;
1035 
1036  wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1037 
1038  while( 1 )
1039  {
1040  if( !kid.IsOk() )
1041  {
1042  if( subdirs_id.empty() ) // all items were explored
1043  break;
1044  else
1045  {
1046  root_id = subdirs_id.top();
1047  subdirs_id.pop();
1048  kid = m_TreeProject->GetFirstChild( root_id, cookie );
1049 
1050  if( !kid.IsOk() )
1051  continue;
1052  }
1053  }
1054 
1055  TREEPROJECT_ITEM* itemData = GetItemIdData( kid );
1056 
1057  if( itemData && itemData->GetType() == TREE_DIRECTORY )
1058  {
1059  // we can see wxString under a debugger, not a wxFileName
1060  wxString path = itemData->GetFileName();
1061 
1062  wxLogDebug( "%s: add '%s'\n", __func__, TO_UTF8( path ) );
1063 
1064  if( wxFileName::IsDirReadable( path ) ) // linux whines about watching protected dir
1065  {
1066  fn.AssignDir( path );
1067  m_watcher->Add( fn );
1068 
1069  // if kid is a subdir, push in list to explore it later
1070  if( itemData->IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1071  subdirs_id.push( kid );
1072  }
1073  }
1074 
1075  kid = m_TreeProject->GetNextChild( root_id, cookie );
1076  }
1077 #endif
1078 
1079 #if defined(DEBUG) && 1
1080  wxArrayString paths;
1081  m_watcher->GetWatchedPaths( &paths );
1082  wxLogDebug( "%s: watched paths:", __func__ );
1083 
1084  for( unsigned ii = 0; ii < paths.GetCount(); ii++ )
1085  wxLogDebug( " %s\n", TO_UTF8( paths[ii] ) );
1086 #endif
1087 }
1088 
1089 
1090 void KICAD_MANAGER_FRAME::OnChangeWatchedPaths( wxCommandEvent& aEvent )
1091 {
1093 }
wxString GerberFileWildcard()
Definition: kicad.h:59
Class TREEPROJECT_ITEM handles one item (a file or a directory name) for the tree file...
wxString PageLayoutDescrFileWildcard()
TreeFileType
Definition: kicad.h:52
wxString SchematicFileWildcard()
static wxString GetFileWildcard(TreeFileType type)
wxString SVGFileWildcard()
This file is part of the common library TODO brief description.
const wxString SVGFileExtension
wxString ComponentFileWildcard()
wxString PdfFileWildcard()
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:55
const wxString ProjectFileExtension
const wxString FootprintPlaceFileExtension
const wxString HtmlFileExtension
Class TREEPROJECTFILES This is the class to show (as a tree) the files in the project directory...
const wxString LegacyPcbFileExtension
const wxString KiCadFootprintFileExtension
wxString SchematicLibraryFileWildcard()
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
bool Delete(bool check=true)
void SetRootFile(bool aValue)
const wxString ReportFileExtension
TREEPROJECTFILES * m_TreeProject
wxTreeItemId findSubdirTreeItem(const wxString &aSubDir)
Function findSubdirTreeItem searches for the item in tree project which is the node of the subdirecto...
Definition: kicad.h:61
const wxString GetDir() const
const wxChar TextFileExtension[]
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Function KiBitmap constructs a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:36
void OnSelect(wxTreeEvent &Event)
Called on a double click on an item.
const wxString NetlistFileExtension
Definition: kicad.h:68
const wxString GerberFileExtensionWildCard(".((gbr|gbrjob|(gb|gt)[alops])|pho)")
void OnFileSystemEvent(wxFileSystemWatcherEvent &event)
called when a file or directory is modified/created/deleted The tree project is modified when a file ...
const wxString ComponentFileExtension
class TREE_PROJECT_FRAME Window to display the tree files
wxString ReportFileWildcard()
wxString NetlistFileWildcard()
wxString HtmlFileWildcard()
const wxString PageLayoutDescrFileExtension
friend class TREEPROJECT_ITEM
EVT_MENU(ID_SELECT_PREFERED_PDF_BROWSER_NAME, KICAD_MANAGER_FRAME::OnSelectPreferredPdfBrowser) EVT_MENU_RANGE(ID_LANGUAGE_CHOICE
TreeFileType GetType() const
KICAD_MANAGER_FRAME * m_Parent
void OnDeleteFile(wxCommandEvent &event)
Function OnDeleteFile Called via the popup menu, when right clicking on a file name or a directory na...
const wxString DrillFileExtension
The common library.
void OnChangeWatchedPaths(wxCommandEvent &aEvent)
Called by sending a event with id = ID_INIT_WATCHED_PATHS rebuild the list of wahtched paths...
void ReCreateTreePrj()
Create or modify the tree showing project file names.
void AddDelimiterString(wxString &string)
Function AddDelimiterString Add un " to the start and the end of string (if not already done)...
Definition: gestfich.cpp:44
void OnOpenSelectedFileWithTextEditor(wxCommandEvent &event)
Function OnOpenSelectedFileWithTextEditor Called via the popup menu, when right clicking on a file na...
Definition: kicad.h:72
void OnRight(wxTreeEvent &Event)
Called on a right click on an item.
wxString DrillFileWildcard()
void Activate(TREE_PROJECT_FRAME *prjframe)
void OnRenameFile(wxCommandEvent &event)
Function OnRenameFile Called via the popup menu, when right clicking on a file name or a directory na...
TREEPROJECT_ITEM * GetSelectedData()
Function GetSelectedData return the item data from item currently selected (highlighted) Note this is...
const wxString PdfFileExtension
void FileWatcherReset()
Reinit the watched paths Should be called after opening a new project to rebuild the list of watched ...
bool Rename(const wxString &name, bool check=true)
wxString ProjectFileWildcard()
const wxString GetProjectFileName()
Definition: mainframe.cpp:147
TREEPROJECT_ITEM * GetItemIdData(wxTreeItemId aId)
Function GetItemIdData return the item data corresponding to a wxTreeItemId identifier.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
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
wxFileSystemWatcher * m_watcher
wxString FootprintPlaceFileWildcard()
void RemoveFilter(const wxString &filter)
TREE_PROJECT_FRAME * m_LeftWin
Definition: kicad.h:304
wxString PcbFileWildcard()
void OnExpand(wxTreeEvent &Event)
Called on a click on the + or - button of an item with children.
wxString TextFileWildcard()
const char * name
const wxString & GetFileName() const
VTBL_ENTRY const wxString & GetEditorName(bool aCanShowFileChooser=true)
Return the preferred editor name.
Definition: pgm_base.cpp:338
Definition: kicad.h:60
const wxString KiCadPcbFileExtension
bool AddItemToTreeProject(const wxString &aName, wxTreeItemId &aRoot, bool aRecurse=true)
Function AddItemToTreeProject.
#define NAMELESS_PROJECT
default name for nameless projects
Definition: common.h:72
std::vector< wxString > m_filters
static wxString GetFileExt(TreeFileType type)
const wxString SchematicLibraryFileExtension
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:208
void OnCreateNewDirectory(wxCommandEvent &event)
Function OnCreateNewDirectory Creates a new subdirectory inside the current kicad project directory t...
static const wxChar * s_allowedExtensionsToList[]
The main KiCad project manager frame.
Definition: kicad.h:137
wxString KiCadFootprintLibFileWildcard()
void SetPopulated(bool aValue)
const wxString SchematicFileExtension