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