30#include <wx/stdpaths.h>
33#include <wx/textdlg.h>
52#include <wx/dcclient.h>
53#include <wx/settings.h>
98 wxT(
"^.*\\.kicad_pro$" ),
101 wxT(
"^.*\\.kicad_sch$" ),
102 wxT(
"^[^$].*\\.brd$" ),
103 wxT(
"^[^$].*\\.kicad_pcb$" ),
104 wxT(
"^[^$].*\\.kicad_dru$" ),
105 wxT(
"^[^$].*\\.kicad_wks$" ),
106 wxT(
"^[^$].*\\.kicad_mod$" ),
110 wxT(
"^.*\\.kicad_sym$" ),
115 wxT(
"^.*\\.gbrjob$" ),
116 wxT(
"^.*\\.gb[alops]$" ),
117 wxT(
"^.*\\.gt[alops]$" ),
118 wxT(
"^.*\\.g[0-9]{1,2}$" ),
119 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
123 wxT(
"^.*\\.html$" ),
134 wxT(
"^.*\\.kicad_jobset" ),
180 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
181 wxNO_BORDER | wxTAB_TRAVERSAL )
184 m_TreeProject =
nullptr;
185 m_isRenaming =
false;
186 m_selectedItem =
nullptr;
187 m_watcherNeedReset =
false;
188 m_lastGitStatusUpdate = wxDateTime::Now();
189 m_gitLastError = GIT_ERROR_NONE;
192 Connect( wxEVT_FSWATCHER,
195 Bind( wxEVT_SYS_COLOUR_CHANGED,
205 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
214 Disconnect( wxEVT_FSWATCHER,
216 Unbind( wxEVT_SYS_COLOUR_CHANGED,
238 if( tree_data.size() != 1 )
241 wxString prj_filename = tree_data[0]->GetFileName();
255 wxString curr_dir = item_data->GetDir();
257 if( curr_dir.IsEmpty() )
263 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
266 if( !curr_dir.IsEmpty() )
267 curr_dir += wxFileName::GetPathSeparator();
285 wxString curr_dir = item_data->GetDir();
287 if( curr_dir.IsEmpty() )
290 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
292 if( new_dir.IsEmpty() )
295 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
297 if( !wxMkdir( full_dirname ) )
327 case TREE_FILE_TYPE::DRILL_NC:
return "nc";
328 case TREE_FILE_TYPE::DRILL_XNC:
return "xnc";
338 case TREE_FILE_TYPE::ROOT:
339 case TREE_FILE_TYPE::UNKNOWN:
340 case TREE_FILE_TYPE::MAX:
341 case TREE_FILE_TYPE::DIRECTORY:
break;
344 return wxEmptyString;
350 std::vector<wxString> projects;
351 wxString dir_filename;
352 bool haveFile = dir.GetFirst( &dir_filename );
356 wxFileName file( dir_filename );
360 projects.push_back( file.GetName() );
362 haveFile = dir.GetNext( &dir_filename );
371 git_repository* repo =
nullptr;
375 if( git_repository_discover( &repo_path, filename, 0, NULL ) )
378 printf(
"get_git_repository_for_file: %s\n", git_error_last()->message ); fflush( 0 );
383 if( git_repository_open( &repo, repo_path.ptr ) )
385 git_buf_free( &repo_path );
390 git_buf_free( &repo_path );
397 const wxTreeItemId& aParent,
398 std::vector<wxString>* aProjectNames,
402 wxFileName fn( aName );
405 return wxTreeItemId();
407 if( wxDirExists( aName ) )
409 type = TREE_FILE_TYPE::DIRECTORY;
415 bool addFile =
false;
417 for(
const wxString& m_filter :
m_filters )
419 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
420 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
422 if( reg.Matches( aName ) )
430 return wxTreeItemId();
432 for(
int i =
static_cast<int>( TREE_FILE_TYPE::LEGACY_PROJECT );
433 i < static_cast<int>( TREE_FILE_TYPE::MAX ); i++ )
437 if( ext == wxT(
"" ) )
440 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ),
441 wxRE_ICASE ) && reg.Matches( aName ) )
449 wxString file = wxFileNameFromPath( aName );
450 wxFileName currfile( file );
454 if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
455 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
457 return wxTreeItemId();
460 if( currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
461 || currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
466 return wxTreeItemId();
471 wxDir parentDir( parentTreeItem->
GetDir() );
472 std::vector<wxString> projects =
getProjects( parentDir );
475 return wxTreeItemId();
480 wxTreeItemIdValue cookie;
481 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
488 return itemData->GetId();
494 if( type == TREE_FILE_TYPE::LEGACY_PROJECT || type == TREE_FILE_TYPE::JSON_PROJECT
495 || type == TREE_FILE_TYPE::LEGACY_SCHEMATIC || type == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
507 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
511 case TREE_FILE_TYPE::LEGACY_PROJECT:
512 if( itemData->
GetType() == TREE_FILE_TYPE::JSON_PROJECT )
513 return wxTreeItemId();
517 case TREE_FILE_TYPE::LEGACY_SCHEMATIC:
518 if( itemData->
GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
519 return wxTreeItemId();
523 case TREE_FILE_TYPE::JSON_PROJECT:
524 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
529 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
530 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
546 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
553 wxString fileName = currfile.GetName().Lower();
554 wxString projName =
project.GetName().Lower();
556 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
560 bool subdir_populated =
false;
565 if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
571 std::vector<wxString> projects =
getProjects( dir );
572 wxString dir_filename;
573 bool haveFile = dir.GetFirst( &dir_filename );
578 subdir_populated = aRecurse;
584 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
587 haveFile = dir.GetNext( &dir_filename );
596 if( subdir_populated )
622 wxFileName fn = pro_dir;
623 bool prjReset =
false;
634 bool prjOpened = fn.FileExists();
646 if( conn_type ==
"https" )
648 else if( conn_type ==
"ssh" )
656 if( !prjOpened && !prjReset )
659 prjOpened = fn.FileExists();
666 m_root =
m_TreeProject->AddRoot( fn.GetFullName(),
static_cast<int>( TREE_FILE_TYPE::ROOT ),
667 static_cast<int>( TREE_FILE_TYPE::ROOT ) );
680 wxDir dir( pro_dir );
684 std::vector<wxString> projects =
getProjects( dir );
686 bool haveFile = dir.GetFirst( &filename );
690 if( filename != fn.GetFullName() )
692 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
699 haveFile = dir.GetNext( &filename );
723 git_status_options opts;
724 git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
726 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
727 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
728 | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
730 git_status_list* status_list =
nullptr;
731 int error = git_status_list_new( &status_list, repo, &opts );
733 if( error != GIT_OK )
736 bool has_changed_files = git_status_list_entrycount( status_list ) > 0;
737 git_status_list_free( status_list );
738 return has_changed_files;
744 wxTreeItemId curr_item = Event.GetItem();
751 bool can_switch_to_project =
true;
752 bool can_create_new_directory =
true;
753 bool can_open_this_directory =
true;
754 bool can_edit =
true;
755 bool can_rename =
true;
756 bool can_delete =
true;
757 bool run_jobs =
false;
761 bool vcs_can_init = !vcs_has_repo;
762 bool vcs_can_remove = vcs_has_repo;
765 bool vcs_can_pull = vcs_can_fetch;
766 bool vcs_can_switch = vcs_has_repo;
770#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
771 int major, minor, rev;
772 bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
775 bool libgit_init =
true;
778 vcs_menu &= libgit_init;
780 if( selection.size() == 0 )
784 if( selection.size() != 1 )
786 can_switch_to_project =
false;
787 can_create_new_directory =
false;
796 can_switch_to_project =
false;
802 can_delete = item->CanDelete();
803 can_rename = item->CanRename();
805 switch( item->GetType() )
807 case TREE_FILE_TYPE::JSON_PROJECT:
808 case TREE_FILE_TYPE::LEGACY_PROJECT:
813 can_switch_to_project =
false;
817 can_create_new_directory =
false;
818 can_open_this_directory =
false;
822 case TREE_FILE_TYPE::DIRECTORY:
823 can_switch_to_project =
false;
827 case TREE_FILE_TYPE::ZIP_ARCHIVE:
828 case TREE_FILE_TYPE::PDF:
830 can_switch_to_project =
false;
831 can_create_new_directory =
false;
832 can_open_this_directory =
false;
835 case TREE_FILE_TYPE::JOBSET_FILE:
840 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
841 case TREE_FILE_TYPE::SEXPR_PCB:
845 can_switch_to_project =
false;
846 can_create_new_directory =
false;
847 can_open_this_directory =
false;
857 if( can_switch_to_project )
860 _(
"Close all editors, and switch to the selected project" ),
861 KiBitmap( BITMAPS::open_project ) );
862 popup_menu.AppendSeparator();
865 if( can_create_new_directory )
868 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
871 if( can_open_this_directory )
873 if( selection.size() == 1 )
876 text =
_(
"Reveal in Finder" );
877 help_text =
_(
"Reveals the directory in a Finder window" );
879 text =
_(
"Open Directory in File Explorer" );
880 help_text =
_(
"Opens the directory in the default system file manager" );
886 text =
_(
"Reveal in Finder" );
887 help_text =
_(
"Reveals the directories in a Finder window" );
889 text =
_(
"Open Directories in File Explorer" );
890 help_text =
_(
"Opens the directories in the default system file manager" );
895 KiBitmap( BITMAPS::directory_browser ) );
900 if( selection.size() == 1 )
901 help_text =
_(
"Open the file in a Text Editor" );
903 help_text =
_(
"Open files in a Text Editor" );
909 if( run_jobs && selection.size() == 1 )
917 if( selection.size() == 1 )
919 text =
_(
"Rename File..." );
920 help_text =
_(
"Rename file" );
924 text =
_(
"Rename Files..." );
925 help_text =
_(
"Rename files" );
934 if( selection.size() == 1 )
935 help_text =
_(
"Delete the file and its content" );
937 help_text =
_(
"Delete the files and their contents" );
939 if( can_switch_to_project
940 || can_create_new_directory
941 || can_open_this_directory
945 popup_menu.AppendSeparator();
959 wxMenu* vcs_submenu =
new wxMenu();
960 wxMenu* branch_submenu =
new wxMenu();
961 wxMenuItem* vcs_menuitem =
nullptr;
964 _(
"Add Project to Version Control..." ),
965 _(
"Initialize a new repository" ) );
966 vcs_menuitem->Enable( vcs_can_init );
970 _(
"Commit changes to the local repository" ) );
971 vcs_menuitem->Enable( vcs_can_commit );
973 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
974 _(
"Push committed local changes to remote repository" ) );
975 vcs_menuitem->Enable( vcs_can_push );
977 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
978 _(
"Pull changes from remote repository into local" ) );
979 vcs_menuitem->Enable( vcs_can_pull );
981 vcs_submenu->AppendSeparator();
984 _(
"Commit changes to the local repository" ) );
985 vcs_menuitem->Enable( vcs_can_commit );
987 vcs_submenu->AppendSeparator();
996 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
998 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
1000 vcs_menuitem->Enable( vcs_can_switch );
1004 _(
"Switch to a different branch" ) );
1005 vcs_menuitem->Enable( vcs_can_switch );
1009 vcs_submenu->AppendSeparator();
1011 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Remove Version Control" ),
1012 _(
"Delete all version control files from the project directory." ) );
1013 vcs_menuitem->Enable( vcs_can_remove );
1015 popup_menu.AppendSeparator();
1016 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1019 if( popup_menu.GetMenuItemCount() > 0 )
1020 PopupMenu( &popup_menu );
1028 if( editorname.IsEmpty() )
1030 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1038 wxString fullFileName = item_data->GetFileName();
1040 if( !fullFileName.IsEmpty() )
1042 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1053 item_data->Delete();
1063 if( tree_data.size() != 1 )
1067 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1068 tree_data[0]->GetFileName() );
1069 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1071 if( dlg.ShowModal() != wxID_OK )
1074 buffer = dlg.GetValue();
1075 buffer.Trim(
true );
1076 buffer.Trim(
false );
1078 if( buffer.IsEmpty() )
1081 tree_data[0]->Rename( buffer,
true );
1090 if( tree_data.size() != 1 )
1127 wxTreeItemId itemId = Event.GetItem();
1133 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1137 wxTreeItemIdValue cookie;
1138 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1141 bool subdir_populated =
false;
1144 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1148 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1155 wxDir dir( fileName );
1157 if( dir.IsOpened() )
1159 std::vector<wxString> projects =
getProjects( dir );
1160 wxString dir_filename;
1161 bool haveFile = dir.GetFirst( &dir_filename );
1166 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1169 haveFile = dir.GetNext( &dir_filename );
1175 subdir_populated =
true;
1184 if( subdir_populated )
1192 wxArrayTreeItemIds selection;
1193 std::vector<PROJECT_TREE_ITEM*> data;
1197 for(
auto it = selection.begin(); it != selection.end(); it++ )
1203 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast "
1208 data.push_back( item );
1227 if( prj_dir == aSubDir )
1231 wxTreeItemIdValue cookie;
1232 wxTreeItemId root_id =
m_root;
1233 std::stack<wxTreeItemId> subdirs_id;
1235 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1239 if( ! child.IsOk() )
1241 if( subdirs_id.empty() )
1248 root_id = subdirs_id.top();
1259 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1269 subdirs_id.push( child );
1285 const wxFileName& pathModified =
event.GetPath();
1286 wxString subdir = pathModified.GetPath();
1287 wxString fn = pathModified.GetFullPath();
1289 switch( event.GetChangeType() )
1291 case wxFSW_EVENT_DELETE:
1292 case wxFSW_EVENT_CREATE:
1293 case wxFSW_EVENT_RENAME:
1297 case wxFSW_EVENT_MODIFY:
1300 case wxFSW_EVENT_ACCESS:
1307 if( !root_id.IsOk() )
1310 wxTreeItemIdValue cookie;
1311 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1313 switch( event.GetChangeType() )
1315 case wxFSW_EVENT_CREATE:
1317 wxTreeItemId newitem =
1331 case wxFSW_EVENT_DELETE:
1345 case wxFSW_EVENT_RENAME :
1347 const wxFileName& newpath =
event.GetNewPath();
1348 wxString newdir = newpath.GetPath();
1349 wxString newfn = newpath.GetFullPath();
1373 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1379 if( newitem.IsOk() )
1399#if defined( _WIN32 )
1432 fn.AssignDir( prj_dir );
1433 fn.DontFollowLink();
1447 TO_UTF8( fn.GetFullPath() ) );
1455 TO_UTF8( fn.GetFullPath() ) );
1460 if( m_TreeProject->IsEmpty() )
1464 wxTreeItemIdValue cookie;
1465 wxTreeItemId root_id = m_root;
1467 std::stack < wxTreeItemId > subdirs_id;
1469 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1470 int total_watch_count = 0;
1476 if( subdirs_id.empty() )
1482 root_id = subdirs_id.top();
1484 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1500 if( wxFileName::IsDirReadable(
path ) )
1502 fn.AssignDir(
path );
1503 m_watcher->Add( fn );
1504 total_watch_count++;
1507 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1508 subdirs_id.push( kid );
1512 kid = m_TreeProject->GetNextChild( root_id, cookie );
1519#if defined(DEBUG) && 1
1520 wxArrayString paths;
1521 m_watcher->GetWatchedPaths( &paths );
1524 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1558 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1559 wxPaintDC dc(
this );
1561 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1562 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1564 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1565 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1579 wxString dir = tree_data->
GetDir();
1583 wxLogError(
"Failed to initialize git project: project directory is empty." );
1588 git_repository* repo =
nullptr;
1589 int error = git_repository_open(&repo, dir.mb_str());
1594 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1597 _(
"The selected directory is already a git project." ) );
1598 git_repository_free( repo );
1604 error = git_repository_init( &repo, dir.mb_str(), 0 );
1608 git_repository_free( repo );
1614 git_error_last()->message );
1628 dlg.SetTitle(
_(
"Set default remote" ) );
1640 git_remote* remote =
nullptr;
1649 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1657 fullURL.append( wxS(
":" ) );
1661 fullURL.append( wxS(
"@" ) );
1672 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1673 fullURL.ToStdString().c_str(),
1674 "+refs/heads/*:refs/remotes/origin/*" );
1676 if( error != GIT_OK )
1682 git_error_last()->message );
1698 _(
"Fetching Remote" ),
1737 _(
"Fetching Remote" ),
1740 if( handler.
PerformPull() != PullResult::Success )
1764 _(
"Fetching Remote" ),
1767 if( handler.
PerformPush() != PushResult::Success )
1780 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1782 wxLogError(
"Failed to lookup HEAD reference" );
1787 git_commit* commit =
nullptr;
1788 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1790 wxLogError(
"Failed to lookup commit" );
1794 git_reference* branchRef =
nullptr;
1796 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1798 wxLogError(
"Failed to create branch" );
1799 git_commit_free( commit );
1803 git_commit_free( commit );
1804 git_reference_free( branchRef );
1822 if( retval == wxID_ADD )
1824 else if( retval != wxID_OK )
1828 git_reference* branchRef =
nullptr;
1830 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1831 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1833 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1834 branchName, giterr_last()->message );
1839 const char* branchRefName = git_reference_name( branchRef );
1841 git_object* branchObj =
nullptr;
1843 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1845 wxString errorMessage =
1846 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1848 git_reference_free( branchRef );
1854 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1856 wxString errorMessage =
1857 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1859 git_reference_free( branchRef );
1860 git_object_free( branchObj );
1865 if( git_repository_set_head( repo, branchRefName ) != 0 )
1867 wxString errorMessage = wxString::Format(
1868 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1870 git_reference_free( branchRef );
1871 git_object_free( branchObj );
1876 git_reference_free( branchRef );
1877 git_object_free( branchObj );
1886 || !
IsOK( wxGetTopLevelParent(
this ),
1887 _(
"Are you sure you want to remove git tracking from this project?" ) ) )
1893 git_repository_free( repo );
1898 fn.AppendDir(
".git" );
1908 std::stack<wxTreeItemId> items;
1911 while( !items.empty() )
1913 wxTreeItemId current = items.top();
1917 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1919 wxTreeItemIdValue cookie;
1920 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1922 while( child.IsOk() )
1924 items.push( child );
1941 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1958 git_reference* currentBranchReference =
nullptr;
1959 git_repository_head( ¤tBranchReference, repo );
1962 wxFileName rootFilename( rootItem->
GetFileName() );
1963 wxString repoWorkDir( git_repository_workdir( repo ) );
1966 if( currentBranchReference )
1968 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1969 wxString branchName = git_reference_shorthand( currentBranchReference );
1971 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1972 git_reference_free( currentBranchReference );
1977 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1983 std::map<wxString, wxTreeItemId> branchMap;
1985 std::stack<wxTreeItemId> items;
1988 while( !items.empty() )
1997 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
1999 branchMap[gitAbsPath] = kid;
2001 wxTreeItemIdValue cookie;
2002 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2004 while( child.IsOk() )
2006 items.push( child );
2012 wxFileName relative = rootFilename;
2013 relative.MakeRelativeTo( repoWorkDir );
2014 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2017 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2020 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
2022 git_status_options status_options;
2023 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2024 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2025 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
2026 status_options.pathspec = { (
char**) pathspec, 1 };
2028 git_index* index =
nullptr;
2030 if( git_repository_index( &index, repo ) != GIT_OK )
2033 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2037 git_status_list* status_list =
nullptr;
2039 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2041 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2042 git_index_free( index );
2048 size_t count = git_status_list_entrycount( status_list );
2050 for(
size_t ii = 0; ii < count; ++ii )
2052 const git_status_entry* entry = git_status_byindex( status_list, ii );
2053 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2054 : entry->index_to_workdir->old_file.path );
2056 wxString absPath = repoWorkDir;
2059 auto iter = branchMap.find( absPath );
2061 if( iter == branchMap.end() )
2067 if( entry->status == GIT_STATUS_CURRENT )
2073 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2080 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2087 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2096 if( localChanges.count(
path ) )
2103 else if( remoteChanges.count(
path ) )
2119 git_status_list_free( status_list );
2120 git_index_free( index );
2130 if( repo ==
nullptr )
2132 wxMessageBox(
"The selected directory is not a git project." );
2136 git_config*
config =
nullptr;
2137 git_repository_config( &
config, repo );
2140 wxString authorName;
2141 wxString authorEmail;
2144 git_config_entry* name_c =
nullptr;
2145 git_config_entry* email_c =
nullptr;
2146 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2148 if( authorNameError != 0 || name_c ==
nullptr )
2154 authorName = name_c->value;
2155 git_config_entry_free( name_c );
2159 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2161 if( authorEmailError != 0 || email_c ==
nullptr )
2167 authorEmail = email_c->value;
2168 git_config_entry_free( email_c );
2172 git_config_free(
config );
2175 git_status_options status_options;
2176 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2177 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2178 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2180 git_status_list* status_list =
nullptr;
2181 git_status_list_new( &status_list, repo, &status_options );
2183 std::map<wxString, int> modifiedFiles;
2185 size_t count = git_status_list_entrycount( status_list );
2187 std::set<wxString> selected_files;
2191 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2192 selected_files.emplace( item->GetFileName() );
2195 for(
size_t i = 0; i < count; ++i )
2197 const git_status_entry* entry = git_status_byindex( status_list, i );
2200 if( entry->status == GIT_STATUS_CURRENT
2201 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2206 wxFileName fn( entry->index_to_workdir->old_file.path );
2207 fn.MakeAbsolute( git_repository_workdir( repo ) );
2209 wxString filePath( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2213 modifiedFiles.emplace( filePath, entry->status );
2215 else if( selected_files.count( fn.GetFullPath() ) )
2217 modifiedFiles.emplace( filePath, entry->status );
2221 git_status_list_free( status_list );
2224 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2228 if( ret == wxID_OK )
2232 git_tree* tree =
nullptr;
2233 git_commit* parent =
nullptr;
2234 git_index* index =
nullptr;
2240 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2246 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2250 if( git_repository_index( &index, repo ) != 0 )
2252 wxMessageBox(
_(
"Failed to get repository index: %s" ), giterr_last()->message );
2256 for( wxString& file :files )
2258 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2260 wxMessageBox(
_(
"Failed to add file to index: %s" ), giterr_last()->message );
2261 git_index_free( index );
2266 if( git_index_write( index ) != 0 )
2268 wxMessageBox(
_(
"Failed to write index: %s" ), giterr_last()->message );
2269 git_index_free( index );
2273 if (git_index_write_tree( &tree_id, index ) != 0)
2275 wxMessageBox(
_(
"Failed to write tree: %s" ), giterr_last()->message );
2276 git_index_free( index );
2280 git_index_free( index );
2282 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2284 wxMessageBox(
_(
"Failed to lookup tree: %s" ), giterr_last()->message );
2288 git_reference* headRef =
nullptr;
2290 if( git_repository_head( &headRef, repo ) != 0 )
2292 wxMessageBox(
_(
"Failed to get HEAD reference: %s" ), giterr_last()->message );
2293 git_index_free( index );
2297 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2299 wxMessageBox(
_(
"Failed to get commit: %s" ), giterr_last()->message );
2300 git_reference_free( headRef );
2301 git_index_free( index );
2305 git_reference_free( headRef );
2311 git_signature* author =
nullptr;
2313 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2315 wxMessageBox(
_(
"Failed to create author signature: %s" ), giterr_last()->message );
2321#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
2322 && ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
2341 git_commit*
const parents[1] = { parent };
2344 const git_commit* parents[1] = { parent };
2347 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2350 wxMessageBox(
_(
"Failed to create commit: %s" ), giterr_last()->message );
2354 git_signature_free( author );
2355 git_commit_free( parent );
2356 git_tree_free( tree );
2377 if( git_repository_index( &index, repo ) != 0 )
2381 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2383 git_index_free( index );
2387 git_index_free( index );
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
BITMAP_STORE * GetBitmapStore()
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
wxString GetCommitMessage() const
wxString GetAuthorEmail() const
std::vector< wxString > GetSelectedFiles() const
wxString GetAuthorName() const
KIGIT_COMMON::GIT_CONN_TYPE GetRepoType() const
wxString GetBareRepoURL() const
Get the Bare Repo U R L object.
wxString GetRepoSSHPath() const
wxString GetRepoURL() const
wxString GetUsername() const
wxString GetPassword() const
wxString GetBranchName() const
void SetProgressReporter(std::unique_ptr< WX_PROGRESS_REPORTER > aProgressReporter)
void PerformRemoveFromIndex()
bool PerformResolveConflict()
The main KiCad project manager frame.
wxString m_FileWatcherInfo
PROJECT_TREE_PANE * m_leftWin
const wxString GetProjectFileName() const
void OnChangeWatchedPaths(wxCommandEvent &aEvent)
Called by sending a event with id = ID_INIT_WATCHED_PATHS rebuild the list of watched paths.
void LoadProject(const wxFileName &aProjectFileName)
std::vector< wxString > GetBranchNames() const
void SetConnType(GIT_CONN_TYPE aConnType)
wxString GetSSHKey() const
void SetSSHKey(const wxString &aSSHKey)
void SetUsername(const wxString &aUsername)
GIT_CONN_TYPE GetConnType() const
wxString GetPassword() const
std::pair< std::set< wxString >, std::set< wxString > > GetDifferentFiles() const
Return a pair of sets of files that differ locally from the remote repository The first set is files ...
bool HasPushAndPullRemote() const
wxString GetUsername() const
bool HasLocalCommits() const
void SetPassword(const wxString &aPassword)
wxString GetErrorString()
KISTATUSBAR is a wxStatusBar suitable for Kicad manager.
void SetEllipsedTextField(const wxString &aText, int aFieldId)
Set the text in a field using wxELLIPSIZE_MIDDLE option to adjust the text size to the field size (un...
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
virtual COMMON_SETTINGS * GetCommonSettings() const
virtual const wxString & GetTextEditor(bool aCanShowFileChooser=true)
Return the path to the preferred text editor application.
wxString m_GitRepoPassword
wxString m_GitRepoUsername
Handle one item (a file or a directory name) for the tree file.
void SetRootFile(bool aValue)
const wxString & GetFileName() const
void SetPopulated(bool aValue)
TREE_FILE_TYPE GetType() const
const wxString GetDir() const
void Activate(PROJECT_TREE_PANE *aTreePrjFrame)
PROJECT_TREE_PANE Window to display the tree files.
PROJECT_TREE_ITEM * m_selectedItem
void onGitFetch(wxCommandEvent &event)
Fetch the latest changes from the git repository.
void onGitInitializeProject(wxCommandEvent &event)
Initialize a new git repository in the current project directory.
void onGitSyncProject(wxCommandEvent &event)
Sync the current project with the git repository.
void onDeleteFile(wxCommandEvent &event)
Function onDeleteFile Delete the selected file or directory in the tree project.
void onGitRemoveVCS(wxCommandEvent &event)
Remove the git repository from the current project directory.
void EmptyTreePrj()
Delete all m_TreeProject entries.
void FileWatcherReset()
Reinit the watched paths Should be called after opening a new project to rebuild the list of watched ...
std::vector< PROJECT_TREE_ITEM * > GetSelectedData()
Function GetSelectedData return the item data from item currently selected (highlighted) Note this is...
void onOpenDirectory(wxCommandEvent &event)
Function onOpenDirectory Handles the right-click menu for opening a directory in the current system f...
void onGitResolveConflict(wxCommandEvent &event)
Resolve conflicts in the git repository.
void onFileSystemEvent(wxFileSystemWatcherEvent &event)
called when a file or directory is modified/created/deleted The tree project is modified when a file ...
wxFileSystemWatcher * m_watcher
wxDateTime m_lastGitStatusUpdate
void onRight(wxTreeEvent &Event)
Called on a right click on an item.
void onGitCommit(wxCommandEvent &event)
Commit the current project saved changes to the git repository.
void onSelect(wxTreeEvent &Event)
Called on a double click on an item.
PROJECT_TREE * m_TreeProject
KICAD_MANAGER_FRAME * m_Parent
void onExpand(wxTreeEvent &Event)
Called on a click on the + or - button of an item with children.
void onIdle(wxIdleEvent &aEvent)
Idle event handler, used process the selected items at a point in time when all other events have bee...
void onGitRevertLocal(wxCommandEvent &event)
Revert the local repository to the last commit.
void onGitPullProject(wxCommandEvent &event)
Pull the latest changes from the git repository.
static wxString GetFileExt(TREE_FILE_TYPE type)
friend class PROJECT_TREE_ITEM
void onGitRemoveFromIndex(wxCommandEvent &event)
Remove a file from the git index.
wxTreeItemId findSubdirTreeItem(const wxString &aSubDir)
Function findSubdirTreeItem searches for the item in tree project which is the node of the subdirecto...
void ReCreateTreePrj()
Create or modify the tree showing project file names.
void onThemeChanged(wxSysColourChangedEvent &aEvent)
void onRunSelectedJobsFile(wxCommandEvent &event)
Run a selected jobs file.
void onGitCompare(wxCommandEvent &event)
Compare the current project to a different branch in the git repository.
void shutdownFileWatcher()
Shutdown the file watcher.
wxTreeItemId addItemToProjectTree(const wxString &aName, const wxTreeItemId &aParent, std::vector< wxString > *aProjectNames, bool aRecurse)
Function addItemToProjectTree.
bool hasChangedFiles()
Returns true if the current project has any uncommitted changes.
void onOpenSelectedFileWithTextEditor(wxCommandEvent &event)
Function onOpenSelectedFileWithTextEditor Call the text editor to open the selected file in the tree ...
void onGitAddToIndex(wxCommandEvent &event)
Add a file to the git index.
void updateGitStatusIcons()
Updates the icons shown in the tree project to reflect the current git status.
PROJECT_TREE_ITEM * GetItemIdData(wxTreeItemId aId)
Function GetItemIdData return the item data corresponding to a wxTreeItemId identifier.
void onGitSwitchBranch(wxCommandEvent &event)
Switch to a different branch in the git repository.
void onCreateNewDirectory(wxCommandEvent &event)
Function onCreateNewDirectory Creates a new subdirectory inside the current kicad project directory t...
void onSwitchToSelectedProject(wxCommandEvent &event)
Switch to a other project selected from the tree project (by selecting an other .pro file inside the ...
void onPaint(wxPaintEvent &aEvent)
We don't have uniform borders so we have to draw them ourselves.
void onRenameFile(wxCommandEvent &event)
Function onRenameFile Rename the selected file or directory in the tree project.
void onGitPushProject(wxCommandEvent &event)
Push the current project changes to the git repository.
bool canFileBeAddedToVCS(const wxString &aFilePath)
Returns true if the file has already been added to the repository or false if it has not been added y...
std::vector< wxString > m_filters
PROJECT_TREE This is the class to show (as a tree) the files in the project directory.
git_repository * GetGitRepo() const
KIGIT_COMMON * GitCommon() const
void SetGitRepo(git_repository *aRepo)
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
This file is part of the common library.
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback, bool aFileForKicad)
Call the executable file aEditorName with the parameter aFileName.
bool RmDirRecursive(const wxString &aFileName, wxString *aErrors)
Removes the directory aDirName and all its contents including subdirectories and their files.
bool m_EnableGit
Enable git integration.
static const std::string LegacySchematicFileExtension
static const std::string HtmlFileExtension
static const wxString GerberFileExtensionsRegex
static const std::string NetlistFileExtension
static const std::string GerberJobFileExtension
static const std::string ReportFileExtension
static const std::string ProjectFileExtension
static const std::string FootprintPlaceFileExtension
static const std::string LegacyPcbFileExtension
static const std::string LegacyProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string LegacySymbolLibFileExtension
static const std::string KiCadSymbolLibFileExtension
static const std::string SpiceFileExtension
static const std::string PdfFileExtension
static const std::string TextFileExtension
static const std::string DrawingSheetFileExtension
static const std::string KiCadJobSetFileExtension
static const std::string FootprintAssignmentFileExtension
static const std::string SVGFileExtension
static const std::string DrillFileExtension
static const std::string DesignRulesFileExtension
static const std::string MarkdownFileExtension
static const std::string KiCadFootprintFileExtension
static const std::string ArchiveFileExtension
static const std::string KiCadPcbFileExtension
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
const wxChar *const traceGit
Flag to enable Git debugging output.
IDs used in KiCad main frame foe menuitems and tools.
@ ID_PROJECT_SWITCH_TO_OTHER
@ ID_GIT_REMOVE_FROM_INDEX
@ ID_GIT_RESOLVE_CONFLICT
@ ID_GIT_INITIALIZE_PROJECT
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
KICOMMON_API wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmapBundle &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
PGM_BASE & Pgm()
The global Program "get" accessor.
#define NAMELESS_PROJECT
default name for nameless projects
std::vector< wxString > getProjects(const wxDir &dir)
static git_repository * get_git_repository_for_file(const char *filename)
static const wxChar * s_allowedExtensionsToList[]
static int git_create_branch(git_repository *aRepo, wxString &aBranchName)
#define wxFileSystemWatcher
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.