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