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