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