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