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